You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OpenZeppelin可升级合约中实现多重继承?

多重继承下实现OpenZeppelin可升级合约的正确方案

针对你继承ERC20BurnableUpgradeable和[draft-]ERC20PermitUpgradeable的场景,结合Solidity 0.8.9、Hardhat、OpenZeppelin 4.7.3的环境,正确操作如下:

1. 合约代码编写规范

由于ERC20BurnableUpgradeable和ERC20PermitUpgradeable都继承自ERC20Upgradeable,它们的普通初始化函数(__ERC20Burnable_init、__ERC20Permit_init)都会自动调用__ERC20_init,直接调用这两个函数会导致基础ERC20逻辑被重复初始化,引发问题。正确的做法是手动控制初始化流程:

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

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, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PermitUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(string memory name, string memory symbol) initializer public {
        // 仅调用一次基础ERC20的初始化逻辑
        __ERC20_init(name, symbol);
        // 调用Burnable扩展的无父依赖初始化函数
        __ERC20Burnable_init_unchained();
        // 调用Permit扩展的无父依赖初始化函数(需传入代币名称)
        __ERC20Permit_init_unchained(name);
    }
}

代码说明

  • 继承Initializable是可升级合约的必要前提,构造函数中调用_disableInitializers()是为了禁止通过构造函数初始化合约(可升级合约必须通过代理调用initialize函数)。
  • 使用_unchained后缀的初始化函数,这类函数不会自动调用父合约的初始化逻辑,避免重复执行__ERC20_init。
  • 手动先完成基础ERC20的初始化,再依次初始化各个扩展功能,确保所有逻辑被正确初始化且无重复。

2. 部署流程

使用Hardhat的OpenZeppelin Upgrades插件部署时,必须使用deployProxy函数(而非普通deploy),它会自动创建代理合约并调用initialize函数:

const { ethers, upgrades } = require("hardhat");

async function main() {
  const MyToken = await ethers.getContractFactory("MyToken");
  // 传入initialize函数所需的参数:代币名称、符号
  const myToken = await upgrades.deployProxy(MyToken, ["MyToken", "MTK"]);
  await myToken.deployed();
  console.log("可升级代币合约部署地址:", myToken.address);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

3. 通用规则

  • 所有多重继承的可升级合约,禁止在初始化函数中调用多个父合约的普通init函数(无_unchained后缀的),避免重复初始化父类逻辑。
  • 统一调用最基础父合约的init函数一次,再依次调用各个扩展合约的_unchained版本初始化函数,确保每个扩展的独立逻辑被正确初始化。

内容的提问来源于stack exchange,提问作者Can Poyrazoğlu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:42:28