如何实现可复制部署UUPS代理的工厂合约?代码问题排查
问题描述
我已部署UUPS ERC721逻辑合约(地址为0x1234)及代理合约,希望开发一个工厂合约,通过复制链上已部署的代理来生成多个可升级代理,且这些代理均使用该逻辑合约。但编写的代码未达到预期效果,代理未完成初始化/部署。该ERC721合约为可升级类型,使用initialize而非构造函数,初始化需传入ownerAddress、name和symbol参数。
问题代码
pragma solidity 0.8.15; import "./GenericERC721.sol"; import "./cloneFactory.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract GenericFactory is Ownable, CloneFactory { address public proxyAddress; address public logicAddress; address[] public addressBook; event GenericERC721created(address deployedProxyAddress); constructor(address _proxyAddress, address _logicAddress) { proxyAddress = _proxyAddress; logicAddress =_logicAddress; } function createProxy( address _owner, string calldata _name, string calldata _symbol ) public onlyOwner { address clone = createClone(proxyAddress); ERC1967Proxy(clone).constructor(logicAddress, abi.encodeWithSignature("initialize(address,string,string)", _owner, _name, _symbol)); addressBook.push(clone); emit GenericERC721created(clone); } }
问题分析与解决方案
核心问题
- 错误调用
constructor:克隆生成的代理合约已完成部署,constructor仅在合约部署阶段执行一次,无法在克隆后通过外部调用触发。 - 代理初始化流程错误:未按ERC1967标准设置代理的逻辑合约地址,也未通过代理正确调用逻辑合约的
initialize函数。
修正后的代码
pragma solidity 0.8.15; import "./GenericERC721.sol"; import "./cloneFactory.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; contract GenericFactory is Ownable, CloneFactory { address public proxyAddress; address public logicAddress; address[] public addressBook; event GenericERC721created(address deployedProxyAddress); constructor(address _proxyAddress, address _logicAddress) { proxyAddress = _proxyAddress; logicAddress = _logicAddress; } function createProxy( address _owner, string calldata _name, string calldata _symbol ) public onlyOwner { // 克隆已部署的代理合约 address clone = createClone(proxyAddress); // 1. 按ERC1967标准设置代理的逻辑合约地址 ERC1967Utils.changeImplementation(clone, logicAddress); // 2. 通过代理调用逻辑合约的initialize函数完成初始化 GenericERC721(clone).initialize(_owner, _name, _symbol); addressBook.push(clone); emit GenericERC721created(clone); } }
关键修改点
- 使用
ERC1967Utils.changeImplementation设置代理的逻辑合约地址,符合ERC1967代理存储标准。 - 直接通过代理地址调用
initialize函数,代理会自动将调用转发给逻辑合约,确保初始化逻辑执行。 - 移除无效的
constructor调用,改用标准的代理初始化流程。
替代方案(直接部署新代理)
如果克隆已有代理存在兼容问题,也可以直接在工厂中部署新的ERC1967Proxy实例,示例如下:
// 替换原createClone逻辑,直接部署新代理 ERC1967Proxy newProxy = new ERC1967Proxy( logicAddress, abi.encodeWithSelector(GenericERC721.initialize.selector, _owner, _name, _symbol) ); address clone = address(newProxy);
内容的提问来源于stack exchange,提问作者Mueed
相关产品推荐
相关产品推荐

