我正在尝试将具有多重继承的现有不可升级合约转换为可升级合约。我正在学习 https://docs.openzeppelin.com/contracts/4.x/upgradeable 上的教程,我在文档中发现的唯一内容如下:
初始化函数不像编译器那样被线性化 构造函数。因此,每个 __{ContractName}_init 函数 嵌入对所有父初始化器的线性化调用。作为一个 因此,调用其中两个 init 函数可能 两次初始化同一个合约。
每个合约都有__{ContractName}_init_unchained函数 是初始化函数减去对父初始化器的调用, 并且可以用来避免双重初始化问题,但是做 不建议手动操作。我们希望能够实现 在升级插件的未来版本中对此进行安全检查。
我不知道从这里做什么。它谈到了一个问题,告诉了一个解决方法,但也告诉我们不推荐手动操作,并且还告诉它会在未来的升级插件中进行安全检查。
那么我应该做什么?它说了我不应该做什么,但没有提到我应该做什么。我错过了什么吗?
如何使用 OpenZeppelin 合约同时拥有多重继承和可升级性? (我正在扩展
ERC20BurnableUpgradeable
和 [draft-]ERC20PermitUpgradeable
,并使用 Solidity 0.8.9、Hardhat、OpenZeppelin 4.7.3,如果有帮助的话)
您可以使用OpenZeppelin提供的
Initializable
和ERC20Upgradeable
合约实现OpenZeppelin可升级合约的多重继承。这是一个例子:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract MyToken is Initializable, OwnableUpgradeable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PermitUpgradeable {
function initialize(string memory name, string memory symbol, uint256 initialSupply) public initializer {
__ERC20_init(name, symbol);
__ERC20Burnable_init();
__ERC20Permit_init(name);
__Ownable_init();
transferOwnership(msg.sender);
_mint(msg.sender, initialSupply);
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function burn(address from, uint256 amount) public onlyOwner {
_burn(from, amount);
}
}
在此示例中,
MyToken
扩展了 OwnableUpgradeable
、ERC20Upgradeable
、ERC20BurnableUpgradeable
和 ERC20PermitUpgradeable
以提供所有权、ERC20 功能、可销毁功能和许可功能。
要初始化合约,您需要使用适当的参数调用
initialize
函数,这也将初始化所有父合约。
Initializable
合约通过使用处理所有父合约初始化的非线性初始化模式来解决您提到的双重初始化问题。
请注意,在将可升级合约部署到生产环境之前,您应该始终彻底测试它们,尤其是在处理多重继承时。
希望对您有所帮助!如果您还有其他问题,请告诉我。