如何使用 web3 与 UUPS 可升级合约交互?

Posted

技术标签:

【中文标题】如何使用 web3 与 UUPS 可升级合约交互?【英文标题】:How to interact with UUPS upgradeable contract using web3? 【发布时间】:2021-12-19 04:12:53 【问题描述】:

我已经在 Ropsten 测试网上部署了一个 ERC20 代币,有两个版本。

V1 是一个简单的未经代理的 ERC20 代币,如下所示:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable 
    constructor() ERC20("MyToken", "MTK") 

    function mint(address to, uint256 amount) public onlyOwner 
        _mint(to, amount);
    

我可以使用 web3 与这个合约交互:

const Web3 = require('web3');
const MyToken = require('./build/contracts/MyToken.json');
const HDWalletProvider = require('@truffle/hdwallet-provider');

const provider = new HDWalletProvider(process.env.ACCOUNT_SECRET, process.env.INFURA_URL);
const web3 = new Web3(provider);
const contract = new web3.eth.Contract(MyToken.abi, process.env.CONTRACT_ADDRESS);

例如,这是一个检索合约所有者的调用:

await contract.methods.owner().call();

另一方面,V2 是一个 UUPS 可升级合约,如下所示:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract MyToken is Initializable, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable 
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer 

    function initialize() initializer public 
        __ERC20_init("MyToken", "MTK");
        __Ownable_init();
        __UUPSUpgradeable_init();
    

    function mint(address to, uint256 amount) public onlyOwner 
        _mint(to, amount);
    

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyOwner
        override
    

为了使用相同的 web3 nodejs 代码与 V2 交互,我尝试将 build/abi 以及 process.env.CONTRACT_ADDRESS 从 V1 的地址更新到 V2 的地址。但是,每当我使用相同的代码检索所有者时,它总是返回零地址。

我认为呼叫应该被代理或其他什么,但我不知道如何并且我找不到关于此的资源(文档/教程)。不胜感激。

编辑:

更多信息

合约 V1 和 V2 是从 wizard.openzeppelin.com 生成的。没有任何修改。

V2 通过了获取所有者、符号和名称松露测试。

【问题讨论】:

【参考方案1】:

我不太确定关于 constructor() 中的 initializer 修饰符的最佳做法是什么。我的猜测是 OpenZeppelin 建议将它与构造函数一起使用,以防您还在构造函数中设置其他变量而不使用其他 init 函数。

但是,您的实现的效果是它只是将 initialized 变量设置为 true 而不执行*** initialize() 函数 - 实际上没有设置 owner 变量(以及其他,例如namesymbol)。

我能够通过删除 initializer 修饰符来执行快速修复(因为它已经使用 initialize() 函数,并从构造函数中调用 initialize()。请检查是否没有任何副作用它。

// removed the modifier
// added the call to `initialize()`
constructor() 
    initialize();


// stays the same
function initialize() initializer public 

【讨论】:

感谢您的回答!但是,(我的错)我可能应该补充一点,V1 和 V2 是从wizard.openzeppelin.com 生成的。绝对没有任何修改。关于constructor() initializer ,这里是reference,为什么它被添加为安全标准。所以我有点犹豫要修改那部分代码。此外,我应该提到 V2 通过了获取所有者、符号、名称等松露测试。 @L.Lei 我明白了。我能够重现您的问题(通过从向导生成“错误”源代码)。从this discussion 看来,可升级的 OZ 合约通常在 Remix 中不起作用。当我有时间时,我会尝试更深入地研究这个。 是的,我使用 OZ upgrades plugins(链接中提到的 nodejs 包)和 Truffle 来部署和测试我的 V2,而不是 remix,因为它是 UUPS 代理的(我还想指出在您链接的帖子中,该操作使用透明代理)。到目前为止,根据文档,他们工作得很好。我真的不知道如何使用 web3 与像 V2 这样的 UUPS 可升级合同进行交互。另外,非常感谢您的帮助,谢谢!

以上是关于如何使用 web3 与 UUPS 可升级合约交互?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Flutter 与 ERC721 智能合约交互?

Web3.js的基本使用(与以太坊智能合约的交互)

Web3 开发系列教程—创建你的第一个智能合约与智能合约交互

Web3.js1.0与智能合约交互

(合约升级)

Web3 开发系列教程—创建你的第一个智能合约将你的智能合约发布到 Etherscan