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

如何实现可复制部署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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:35:53