如何通过Factory合约调用ERC721的mint函数并修改其状态变量?
解决Factory合约调用ERC721 mint时的状态更新与msg.sender问题
核心问题解析
- 直接通过Factory调用ERC721的
mint函数时,msg.sender会是Factory合约地址,而非发起调用的用户地址。 delegatecall无法更新ERC721的状态:因为delegatecall会以调用者(Factory)的存储上下文执行目标合约代码,所有状态修改都会写入Factory的存储,而非ERC721合约,这是delegatecall的固有特性。
可行实现方案
方案1:修改ERC721的mint逻辑,允许Factory调用并传递用户地址
调整ERC721合约的mint函数,添加权限验证(仅允许Factory调用),并接收要 mint 的目标用户地址:
// ERC721合约示例 contract MyERC721 is ERC721 { address public factory; uint256 public nextTokenId; constructor(address _factory) ERC721("MyNFT", "MNFT") { factory = _factory; } modifier onlyFactory() { require(msg.sender == factory, "Only factory can call"); _; } function mint(address to) external onlyFactory { _safeMint(to, nextTokenId); nextTokenId++; } }
Factory合约中调用该mint函数时,将发起调用的用户地址(msg.sender)传递进去:
// Factory合约示例 contract NFTFactory { MyERC721 public nftContract; constructor(address _nftContract) { nftContract = MyERC721(_nftContract); } function createNFT() external { // 调用ERC721的mint,将NFT mint给调用Factory的用户 nftContract.mint(msg.sender); } }
此方案中,ERC721的状态会被直接更新,因为是通过常规合约调用操作目标合约的存储。
方案2:使用EIP-2771元交易实现用户身份传递(无需修改mint逻辑的场景)
如果无法修改ERC721的mint函数(例如mint仅允许msg.sender给自己 mint),可通过EIP-2771元交易让Factory作为中继,传递用户的签名身份:
- ERC721合约实现EIP-2771的信任转发逻辑:
contract MyERC721WithMeta is ERC721 { address public trustedForwarder; uint256 public nextTokenId; constructor(address _forwarder) ERC721("MyNFT", "MNFT") { trustedForwarder = _forwarder; } modifier onlyTrustedForwarder() { require(msg.sender == trustedForwarder, "Not trusted forwarder"); _; } function mint() external onlyTrustedForwarder { address user = _msgSender(); _safeMint(user, nextTokenId); nextTokenId++; } // 获取实际发起交易的用户地址 function _msgSender() internal view returns (address) { if (msg.sender == trustedForwarder && msg.data.length >= 20) { assembly { return(add(msg.data, 0x14), 0x20) } } return msg.sender; } }
- Factory作为中继转发用户的交易请求,传递用户签名与地址,ERC721合约验证后会以实际用户身份执行
mint,状态更新正常写入ERC721合约。
内容的提问来源于stack exchange,提问作者Naveen-Arali
相关产品推荐
相关产品推荐

