前言#
文接上回,我們已經部署了 Uniswap 的核心合約,接下來我們將部署完整版的 UniswapV3。完整版的 UniswapV3 包含了以下合約:
-
UniswapV3Factory (Pool 生成合約)
-
UniswapV3SwapRouter (包裝類交易合約)
-
NonfungiblePositionManager (NFT 管理合約)
-
NonfungibleTokenPositionDescriptor (NFT 倉位描述合約)
-
NFTDescriptor (NFT 描述合約)
-
WETH9 (WrapperETH)
我們上一章節已經部署完成了前兩個合約,那麼我們這一章節就需要開始部署後續合約。其實部署方法都是一樣的。只不過有一點點需要注意的地方是,NonfungibleTokenPositionDescriptor
需要進行 Library 鏈接,將NFTDescriptor
部署後作為 Library 鏈接到NonfungibleTokenPositionDescriptor
中。接下來我們會繼續完善我們的部署腳本,將這些合約都部署出來。
完善部署腳本#
我們繼續完善我們的部署腳本,將上一章節中部署的合約和這一章節中需要部署的合約都部署出來。因為部署方式和上一章節中的部署方式一樣,所以我們只需要在00_deploy_univ3.ts
中添加一些代碼即可。
不過我們還需要添加一個 WETH9 的合約。我們上一章節中,在填寫 WETH9 的位置直接使用了 0 地址代替,現在可不行了。所以我們先打開網址https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol
,複製裡面的代碼,然後在 contracts 文件夾下新建一個WETH9.sol
文件,將代碼粘貼進去。然後我們運行一下yarn hardhat compile
,將合約編譯一下。可以不出所料的出現一個錯誤。
這是因為我們的編譯器版本不對,因為 WETH9 的合約是用>=0.4.22 <0.6
的版本進行編譯,而我們默認的是^0.8.0
的版本,所以我們需要修改編譯器的版本。不過得益於 Hardhat 的強大功能,我們可以設定多個版本的編譯器,這樣我們就能在多個版本的編譯器下編譯我們的合約了。
我們打開hardhat.config.ts
,在solidity
的配置項中添加一個compilers
的配置項,將其設置為一個數組,數組中包含我們需要的編譯器版本。
const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: '0.8.0',
},
{
version: '0.4.22',
},
],
},
};
修改完成後,我們再次編譯。
修改 deploy 文件#
我們打開00_deploy_univ3.ts
,在main
函數中添加一些代碼。
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;
其實上面很多都是些重複的代碼,我們需要關注兩段內容。第一段是這個linkLibrary
函數。
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
}
他的作用是將文件中library
的鏈接到 Bytecode 裡面去,但是一般情況下,我們不會採用這種方式。因為從源碼部署的時候,我們可以借助工具來簡化這個過程。而library
是一種極為有效的減少合約字節碼的方式,在後續的文章中,我們會專門講解這個問題。第二段是這個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'
]
})
其實這裡就是借助linkLibrary
函數,將NFTDescriptor
的地址鏈接到NFTTokenPositionDescriptor
的 Bytecode 中。
部署合約#
到此,我們可以驗證一下我們的部署腳本是否有問題。我們運行yarn hardhat deploy
,如果一切順利,我們可以看到如下的輸出。
Nothing to compile
No need to generate any newer typings.
✨ Done in 1.45s.
簡單來說就是沒有什麼特別的輸出,如果這裡出現了報錯,那麼就證明我們的部署腳本出現了問題。有人可能會有疑問,我這裡什麼參數都沒有填,我這合約究竟部署到了什麼地方去了?其實,如果我們直接執行 deploy 腳本,那麼他會默認部署到一個本地的網絡中。我們也可以通過yarn hardhat node
來啟動一個本地的網絡。但是,由於我們沒有啟動一個本地網絡,所以在 deploy 時會自動啟動一個本地網絡用於部署,當部署完成後又會自動的關閉這個網絡。所以我們基本看不到任何反饋輸入,不過,沒有反饋其實就是最好的反饋了。
结语#
這一個章節我們把 UniswapV3 進行了完整部署,有了這個部署,我們就可以在後續的章節中進行一些測試了。當然,有能力的朋友也可以在這上面進行一些初步的合約開發工作。不過我並不是很推薦使用這種方式,因為用這種方式,當合約報錯時,你是無法進行精確到行的 Debug 的。不過這個問題我們将在後面的章節進行解決。在下一章,我們將會部署一個 Uniswap 的前端進行測試。
文章首發於: ee.web3box.dev
作者:https://twitter.com/BoxMrChen
由SafeHouseDAO出品