Preface#
Continuing from the last time, we have deployed the core contracts of Uniswap, and next we will deploy the full version of UniswapV3. The full version of UniswapV3 includes the following contracts:
-
UniswapV3Factory (Pool creation contract)
-
UniswapV3SwapRouter (Wrapper trading contract)
-
NonfungiblePositionManager (NFT management contract)
-
NonfungibleTokenPositionDescriptor (NFT position descriptor contract)
-
NFTDescriptor (NFT descriptor contract)
-
WETH9 (Wrapper ETH)
In the previous chapter, we completed the deployment of the first two contracts, so in this chapter, we need to start deploying the subsequent contracts. The deployment methods are all the same. However, there is one small point to note: NonfungibleTokenPositionDescriptor
needs to link to the Library, so after deploying NFTDescriptor
, it should be linked as a Library to NonfungibleTokenPositionDescriptor
. Next, we will continue to improve our deployment script to deploy all these contracts.
Improving the Deployment Script#
We continue to improve our deployment script to deploy the contracts from the previous chapter and those that need to be deployed in this chapter. Since the deployment method is the same as in the previous chapter, we only need to add some code in 00_deploy_univ3.ts
. However, we also need to add a WETH9 contract. In the previous chapter, we directly used the zero address for the WETH9 position, which is not acceptable now. So we first open the URL https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol
, copy the code inside, and then create a new WETH9.sol
file under the contracts folder and paste the code into it. Then we run yarn hardhat compile
to compile the contract. As expected, an error will occur.
This is because our compiler version is incorrect; the WETH9 contract is compiled with the version >=0.4.22 <0.6
, while our default version is ^0.8.0
, so we need to modify the compiler version. However, thanks to Hardhat's powerful features, we can set multiple compiler versions, allowing us to compile our contracts under multiple compiler versions. We open hardhat.config.ts
, and in the solidity
configuration item, we add a compilers
configuration item, setting it as an array that includes the compiler versions we need.
const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: '0.8.0',
},
{
version: '0.4.22',
},
],
},
};
After the modification, we compile again.
Modifying the Deploy File#
We open 00_deploy_univ3.ts
and add some code in the main
function.
import { utils } from 'ethers'
import { DeployFunction } from "hardhat-deploy/types";
import {
abi as FACTORY_ABI,
bytecode as FACTORY_BYTECODE,
} from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'
import {
abi as SWAP_ROUTER_ABI,
bytecode as SWAP_ROUTER_BYTECODE,
} from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json'
import { HardhatRuntimeEnvironment } from "hardhat/types";
import {
abi as NFTDescriptor_ABI, bytecode as NFTDescriptor_BYTECODE
} from '@uniswap/v3-periphery/artifacts/contracts/libraries/NFTDescriptor.sol/NFTDescriptor.json'
import {
abi as NFTPositionManager_ABI, bytecode as NFTPositionManager_BYTECODE
} from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import {
abi as NFTPositionDescriptor_ABI, bytecode as NFTPositionDescriptor_BYTECODE
} from '@uniswap/v3-periphery/artifacts/contracts/NonfungibleTokenPositionDescriptor.sol/NonfungibleTokenPositionDescriptor.json'
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, ethers } = hre
const [deployer] = await ethers.getSigners()
const factory = await deployments.deploy("UniV3Factory", {
from: deployer.address,
contract: {
bytecode: FACTORY_BYTECODE,
abi: FACTORY_ABI
},
})
const WETH9 = await deployments.deploy("WETH9", {
from: deployer.address
})
await deployments.deploy("UniV3SwapRouter", {
from: deployer.address,
contract: {
abi: SWAP_ROUTER_ABI,
bytecode: SWAP_ROUTER_BYTECODE
},
args: [factory.address, WETH9.address]
})
const NFTDescriptorlibrary = await deployments.deploy('NFTDescriptorLibrary', {
from: deployer.address,
contract: {
abi: NFTDescriptor_ABI,
bytecode: NFTDescriptor_BYTECODE
}
})
const linkedBytecode = linkLibrary(NFTPositionDescriptor_BYTECODE,
{
['contracts/libraries/NFTDescriptor.sol:NFTDescriptor']: NFTDescriptorlibrary.address
}
)
const positionDescriptor = await deployments.deploy('NFTPositionDescriptor', {
from: deployer.address,
contract: {
abi: NFTPositionDescriptor_ABI,
bytecode: linkedBytecode
},
args: [
WETH9.address,
// 'ETH' as a bytes32 string
'0x4554480000000000000000000000000000000000000000000000000000000000'
]
})
await deployments.deploy('NFTPositionManager', {
from: deployer.address,
contract: {
abi: NFTPositionManager_ABI,
bytecode: NFTPositionManager_BYTECODE
},
args: [factory.address, WETH9.address, positionDescriptor.address]
})
}
function linkLibrary(bytecode: string, libraries: {
[name: string]: string
} = {}): string {
let linkedBytecode = bytecode
for (const [name, address] of Object.entries(libraries)) {
const placeholder = `__\$${utils.solidityKeccak256(['string'], [name]).slice(2, 36)}\$__`
const formattedAddress = utils.getAddress(address).toLowerCase().replace('0x', '')
if (linkedBytecode.indexOf(placeholder) === -1) {
throw new Error(`Unable to find placeholder for library ${name}`)
}
while (linkedBytecode.indexOf(placeholder) !== -1) {
linkedBytecode = linkedBytecode.replace(placeholder, formattedAddress)
}
}
return linkedBytecode
}
export default func;
In fact, much of the above is repetitive code, and we need to focus on two parts. The first part is the linkLibrary
function.
function linkLibrary(bytecode: string, libraries: {
[name: string]: string
} = {}): string {
let linkedBytecode = bytecode
for (const [name, address] of Object.entries(libraries)) {
const placeholder = `__\$${utils.solidityKeccak256(['string'], [name]).slice(2, 36)}\$__`
const formattedAddress = utils.getAddress(address).toLowerCase().replace('0x', '')
if (linkedBytecode.indexOf(placeholder) === -1) {
throw new Error(`Unable to find placeholder for library ${name}`)
}
while (linkedBytecode.indexOf(placeholder) !== -1) {
linkedBytecode = linkedBytecode.replace(placeholder, formattedAddress)
}
}
return linkedBytecode
}
Its function is to link the library
in the file to the Bytecode, but generally, we do not adopt this method. Because when deploying from the source code, we can use tools to simplify this process. The library
is an extremely effective way to reduce contract bytecode, and we will specifically explain this issue in subsequent articles. The second part is the deployment of NFTDescriptorlibrary
.
const linkedBytecode = linkLibrary(NFTTokenPositionDescriptor_BYTECODE,
{
['contracts/libraries/NFTDescriptor.sol:NFTDescriptor']: NFTDescriptorlibrary.address
}
)
const positionDescriptor = await deployments.deploy('NFTPositionDescriptor', {
from: deployer.address,
contract: {
abi: NFTPositionDescriptor_ABI,
bytecode: linkedBytecode
},
args: [
WETH9.address,
// 'ETH' as a bytes32 string
'0x4554480000000000000000000000000000000000000000000000000000000000'
]
})
Here, we are using the linkLibrary
function to link the address of NFTDescriptor
to the Bytecode of NFTTokenPositionDescriptor
.
Deploying Contracts#
At this point, we can verify whether our deployment script has any issues. We run yarn hardhat deploy
, and if everything goes smoothly, we can see the following output.
Nothing to compile
No need to generate any newer typings.
✨ Done in 1.45s.
In simple terms, there is no special output; if an error occurs here, it indicates that our deployment script has a problem. Some may wonder why I didn't fill in any parameters; where did my contract get deployed? In fact, if we directly execute the deploy script, it will default to deploying to a local network. We can also start a local network using yarn hardhat node
. However, since we did not start a local network, a local network will be automatically started for deployment, and after the deployment is completed, this network will automatically shut down. Therefore, we can hardly see any feedback input, but no feedback is actually the best feedback.
Conclusion#
In this chapter, we completed the full deployment of UniswapV3. With this deployment, we can conduct some tests in subsequent chapters. Of course, capable friends can also carry out some preliminary contract development work on this. However, I do not recommend using this method very much because when the contract reports an error, you cannot debug it down to the line accurately. However, we will address this issue in the following chapters. In the next chapter, we will deploy a front end for Uniswap for testing.
The article was first published on: ee.web3box.dev
Author: https://twitter.com/BoxMrChen
Produced by SafeHouseDAO