JavaScript 不可用。

我们检测到浏览器禁用了 JavaScript。请启用 JavaScript 或改用支持的浏览器来继续访问

Hardhat+ OpenZeppelin UUPS 开发可升级合约的部署和验证

作者:Anban Chu

发表日期:2023年09月01日

所属目录:OpenZeppelin

标签:

升级合约

import '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';

contract XXX is UUPSUpgradeable, OwnableUpgradeable {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    // ======================== State Variables ========================
    // --------------- pool
    // --------------- user

    // ======================== Events ========================
    event UnknownError(address caller, bytes data);

    // ======================== Constructor ========================
    constructor() {
        _disableInitializers();
    }
    
    function initialize() external initializer {
        __Ownable_init();
    }

    // 设置指定管理员
    // function initialize(address owner_) external initializer {
    //     __Ownable_init(owner_);
    // }

    function _authorizeUpgrade(address) internal override onlyOwner {}

    // ======================== Functions: Limit ========================


    // ======================== Functions: Main ========================


    // ======================== Functions: View ========================


    // ======================== Functions: Private ========================
    function _transfer(address _from, address _to, uint256 _amount) private {
        IERC20Upgradeable(address(WETH)).safeTransferFrom(_from, address(this), _amount);
        WETH.withdraw(_amount);
        (bool success, ) = payable(_to).call{ value: _amount }('');
        require(success, 'transfer failed');
    }    
    
    // ======================== Receive ========================
    receive() external payable {}

    fallback() external {
        emit UnknownError(msg.sender, msg.data);
    }
}

其中 initialize_authorizeUpgrade 是固定格式。

或者如下写法

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";


contract Xxx is ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    constructor() {
        _disableInitializers();
    }

    function initialize(
        address owner_,
        string memory name_,
        string memory symbol_
    ) public initializer {
        _baseUrl = "https://xxx.com/file/";

        __ERC721_init(name_, symbol_);
        __Ownable_init(owner_);
    }
    function _authorizeUpgrade(address) internal override onlyOwner {}
}

合约的部署

const XXXX = await ethers.getContractFactory("XXXX");
const xxxx = await upgrades.deployProxy(XXXX);
await xxxx.waitForDeployment();

hardhat.config.ts 文件 :

import { HardhatUserConfig } from 'hardhat/config'
import '@nomicfoundation/hardhat-toolbox'
require('@openzeppelin/hardhat-upgrades');
require('dotenv').config()


// require("@nomiclabs/hardhat-etherscan");
require("hardhat-deploy");
require("solidity-coverage");
require("hardhat-gas-reporter");

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
// const REPORT_GAS = process.env.REPORT_GAS || false
const BSCTESTNET_RPC_URL = process.env.RPC_URL_BSC_TEST || "https://data"
const BSCSCAN_API_KEY = process.env.BSCSCAN_API_KEY || "Your etherscan API key"
const PRIVATE_KEY = process.env.PK_ACCOUNT_1 || "0x"
const PRIVATE_KEY_PROD = process.env.PK_ACCOUNT_BSC || "0x"

const config: HardhatUserConfig = {
  networks: {
    hardhat: {
      allowUnlimitedContractSize: true,
    },
    bsc_test: {
      url: BSCTESTNET_RPC_URL,
      accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
      timeout: 600000,
      gasPrice: 1000000000 * 15,
      blockGasLimit: 0x1fffffffffffff,
      throwOnTransactionFailures: true,
      throwOnCallFailures: true,
      allowUnlimitedContractSize: true,
    },
    bsc: {
      url: process.env.RPC_URL_BSC,
      accounts: PRIVATE_KEY_PROD !== undefined ? [PRIVATE_KEY_PROD] : [],
      timeout: 600000,
    },
  },
  etherscan: {
    // npx hardhat verify --network <NETWORK> <CONTRACT_ADDRESS> <CONSTRUCTOR_PARAMETERS>
    apiKey: {
      bsc: BSCSCAN_API_KEY,
      bscTestnet: BSCSCAN_API_KEY,
    },
  },
  solidity: {
    version: '0.8.23',
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
      viaIR: true,
    },
  },
  // 单元测试的超时时间
  mocha: {
    timeout: 100000000
  },
}

export default config

一切特殊定制 deploy.ts :

const { ethers, upgrades } = require("hardhat");
import hre from 'hardhat'

async function main() {
  const [owner] = await ethers.getSigners()
  console.log('============ BASIC  INFO ============')
  console.log('Current Network   :', hre.network.name)
  console.log('Network Url       :', hre.network.config.url || 'hardhat temp network')
  console.log('Depoly Address    :', owner.address)

  console.log(`\n ____________ DEPLOY XXX ____________`)
  const ownerAds = owner.address;
  const name = 'Xxx';
  const symbol = 'XXX';

  const Xxx = await ethers.getContractFactory("Xxx");
  const xxx = await upgrades.deployProxy(Xxx,
    [ownerAds, name, symbol],
    {
      initializer: "initialize",
      unsafeAllow: ['constructor'],
      kind: 'uups'
    }
  );
  await xxx.waitForDeployment()
  console.log(`Prox   : ${xxx.target}`)
  console.log("ImplementationAddress", await upgrades.erc1967.getImplementationAddress(xxx.target)
  );
  // console.log("AdminAddress", await upgrades.erc1967.getAdminAddress(xxx.target));

  console.log('\n============ End ============\n')
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error)
  process.exitCode = 1
})

升级部署

async function main() {
    const [owner] = await ethers.getSigners()
    console.log('============ UPGRADE  INFO ============')
    console.log('Current Network   :', hre.network.name)
    console.log('UUPS    Proxy     :', proxyAddress)
    console.log(`\n ____________ UPGRADE CONTRAT ____________`)
    const Contract = await hre.ethers.getContractFactory("Claim");
    await upgrades.forceImport(proxyAddress, Contract);

    const V2 = await ethers.getContractFactory("ClaimV2")
    const proxy = await upgrades.upgradeProxy(proxyAddress, V2,
    {
      initializer: "initialize",
      unsafeAllow: ['constructor'],
      kind: 'uups'
    })
    console.log(`Proxy             : ${proxy.target}`, "(For FE)")
    const receipt = await proxy.deployTransaction.wait(2);
    console.log(`Implementation    : ${await upgrades.erc1967.getImplementationAddress(proxy.target)}`);

    console.log('\n============ End ============\n')
}

注意:如果不使用 forceImport 可能会报错: To register a previously deployed proxy for upgrading, use the forceImport function. 可以使用 forceImport 倒入一下。

部署时候如果有unsafe操作

比如:

Error: Contract `contracts/Xxxx.sol:Xxxx` is not upgrade safe

contracts/Xxxx.sol:53: Contract `Xxxx` has a constructor
    Define an initializer instead
    https://zpl.in/upgrades/error-001

具体的允许,可以在 upgrades.deployProxy 的 unsafeAllow 中修改。

https://docs.openzeppelin.com/upgrades-plugins/1.x/api-hardhat-upgrades#common-options

合约开源

npx hardhat verify --network bsc_test 0x9899999

合约测试

const { time, loadFixture } = require('@nomicfoundation/hardhat-toolbox/network-helpers')
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs')
const { expect } = require('chai')
const { ethers, upgrades } = require("hardhat");

describe('Deposit', function () {
  async function deployFixture() {
    const [owner, user1, user2, user3, user4, user5, user6, user7, user8, user9, user10, treasury] = await ethers.getSigners()

    const Deposit = await ethers.getContractFactory('Deposit')
    const deposit = await upgrades.deployProxy(Deposit)
    await deposit.waitForDeployment()

    // WETH
    const WETH = await ethers.getContractFactory('WETH')
    const weth = await WETH.deploy()
    await weth.waitForDeployment()
    // console.log('金库地址', treasury.address)

    console.log(`\n ____________ DEPLOY XXX ____________`)
    const ownerAddress = owner.address;
    const name = 'xxx';
    const symbol = 'XXX';

    const Xxx = await ethers.getContractFactory("Xxx");
    const xxx = await upgrades.deployProxy(Xxx,
        [ownerAddress, name, symbol],
        {
            initializer: "initialize",
            unsafeAllow: ['constructor'],
            kind: 'uups'
        }
    );
    await xxx.waitForDeployment()
    console.log(`Prox   : ${xxx.target}`)
    console.log("ImplementationAddress", await upgrades.erc1967.getImplementationAddress(xxx.target)
    );
    // console.log("AdminAddress", await upgrades.erc1967.getAdminAddress(xxx.target));

    // // AddressManager
    const AddressManager = await ethers.getContractFactory('AddressManager')
    const addressManager = await upgrades.deployProxy(AddressManager)
    await addressManager.waitForDeployment()
    await addressManager.addAddress(treasury.address)
    await addressManager.setWETH(weth.target)

    // ETH资金:
    await user3.sendTransaction({ to: treasury.address, value: ethers.parseEther('99') })
    const balance = await ethers.provider.getBalance(treasury.target);

    // 代币转移
    await weth.connect(treasury).deposit({ value: ethers.parseEther('39996') })
    await weth.connect(treasury).approve(deposit.target, ethers.parseEther('1000000'))
    expect(await weth.balanceOf(treasury.address)).to.equal(ethers.parseEther('39996'))

    // set address
    await deposit.setTreasury(treasury.target)
    await deposit.setHistoricalOrders(historicalOrders.target)
    await deposit.setAddressManager(addressManager.target)
    await deposit.setWETH(weth.target)

    return { owner, user1, user2, deposit, addressManager, weth }
  }

  describe('部署检测', function () {
    it('基础变量检测', async function () {
      const { owner, user1, deposit, addressManager, weth } = await loadFixture(deployFixture)
      expect(await deposit.T()).to.equal("T")
    })
  })
})

参考





以上就是本篇文章的全部内容了,希望对你有帮助。

>> 本站不提供评论服务,技术交流请在 Twitter 上找我 @anbang_account