Box

Box

3BoxTech Founder / Web3Box Founder/ solidity dev./Several years of experience in Solidity and full-stack development.
twitter

Web3企業級工程-初級篇:4. 部署完整版UniswapV3

前言#

文接上回,我們已經部署了 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,將合約編譯一下。可以不出所料的出現一個錯誤。

image

這是因為我們的編譯器版本不對,因為 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',
      },
    ],
  },
};

修改完成後,我們再次編譯。

image

修改 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出品

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。