如何正确部署可升级ERC20合约?部署异常问题排查
正确部署UUPS可升级ERC20合约的解决方案
问题根源
你当前的脚本用普通deploy函数直接部署了实现合约,而非可升级合约架构要求的代理合约。可升级合约的核心逻辑是用户通过代理合约与实现合约交互,代理负责将调用转发至实现合约;直接部署实现合约不仅无法支持后续升级,还会导致合约状态交互异常,出现name调用无返回的问题。
你的合约代码本身是合规的(继承UUPSUpgradeable、实现_authorizeUpgrade、构造器禁用初始化),只需修改部署脚本即可。
步骤1:安装依赖插件
首先安装OpenZeppelin的Hardhat升级插件:
npm install @openzeppelin/hardhat-upgrades @nomiclabs/hardhat-ethers ethers
然后在hardhat.config.js中引入插件:
require("@openzeppelin/hardhat-upgrades");
步骤2:修改部署脚本
替换原有逻辑,使用upgrades.deployProxy完成代理+实现合约的部署,并自动执行初始化:
const { getNamedAccounts, ethers, upgrades, network } = hre; const { deployer } = await getNamedAccounts(); // 部署UUPS代理合约,自动调用initialize初始化 const governanceToken = await upgrades.deployProxy( await ethers.getContractFactory("GovernanceToken"), [], // initialize无参数,传空数组 { initializer: "initialize", // 指定初始化函数 kind: "uups", // 匹配合约的UUPS升级模式 from: deployer, } ); await governanceToken.deployed(); console.log(`GovernanceToken代理合约部署地址: ${governanceToken.address}`); // 验证实现合约(注意:需验证实现合约而非代理地址) if (!developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) { const implementationAddr = await upgrades.erc1967.getImplementationAddress(governanceToken.address); await hre.run("verify:verify", { address: implementationAddr, contract: "contracts/GovernanceToken.sol:GovernanceToken", constructorArguments: [], }); } // 验证合约功能 const name = await governanceToken.name(); console.log(`代币名称: ${name}`); const symbol = await governanceToken.symbol(); console.log(`代币符号: ${symbol}`); const totalSupply = await governanceToken.totalSupply(); console.log(`总供应量: ${ethers.utils.formatUnits(totalSupply, 18)}`);
关键说明
upgrades.deployProxy会自动完成三件事:部署实现合约、部署UUPS代理合约、通过代理调用initialize,确保初始化逻辑正确执行。- 链上验证时,需验证实现合约地址而非代理地址——代理合约是OpenZeppelin标准合约,无需验证;可通过
upgrades.erc1967.getImplementationAddress获取实现合约地址。 - 后续如需升级合约,只需调用
upgrades.upgradeProxy传入代理地址和新的实现合约工厂即可,无需重新部署代理。
内容的提问来源于stack exchange,提问作者Ritzy Dev
相关产品推荐
相关产品推荐

