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

Solidity多铸币NFT合约付费safeMintNft函数执行失败排查

问题:付费铸币函数safeMintNft执行失败

问题背景

开发了一个Solidity NFT集合合约,包含两种铸币方式:需Soulbound NFT白名单的铸币,以及MATIC/ETH付费的公开销售铸币。当前Soulbound铸币功能正常,但付费铸币的safeMintNft函数无法正常工作。

合约核心代码(safeMintNft函数)

function safeMintNft() public payable whenNotPaused {
    require(
        nftCost > 0, "NFT cost is not set"
    );

    require(
        keccak256(bytes(whitelistedAddresses[msg.sender].discordUser)) !=
            keccak256(""),
        "Address is not whitelisted"
    );

    require(
        keccak256(bytes(whitelistedAddresses[msg.sender].user_type)) == keccak256(bytes("buyer")),
        "Only buyers can mint the NFT"
    );
    uint256 minterBalance = address(msg.sender).balance;
    require(
        minterBalance >= nftCost,
        "not enough tokens to pay for this NFT"
    );

    
    uint256 tokenId = _tokenIdCounter.current();
    _tokenIdCounter.increment();
    tokenIdAddresses[tokenId] = msg.sender;

    address payable contractAddress = payable(address(this));
    contractAddress.transfer(nftCost);

    _safeMint(msg.sender, tokenId);
}

测试代码(Chai)

describe("User has enough eth to mint", async function() {
     beforeEach(async function() {
         await this.nft.setNftCost(ethers.utils.parseEther("0.1"));
      });
      it("should be able to mint", async function() {
         const tx = await this.nft.connect(this.normalUser).safeMintNft();
         const waitedTx = await tx.wait();
         expect(waitedTx.status).to.equal(1);
      });
});

报错信息

Error: Transaction reverted: function call failed to execute
at MelkExp.safeMintNft (contracts/MelkExp.sol:107)

报错行指向代码中的contractAddress.transfer(nftCost);


问题原因与解决思路

核心原因

  1. 无效的转账逻辑:尝试让合约给自己转ETH,这是完全没必要的操作,Solidity中合约无法向自身转账,直接触发revert。
  2. 未验证用户实际发送的ETH金额:函数声明了payable,但仅检查了用户余额,未验证msg.value(用户实际发送的ETH)是否等于nftCost,且测试代码调用时也未附带ETH,导致后续逻辑出错。

解决步骤

  1. 移除多余的转账代码:直接删除以下两行,合约会自动接收用户发送的ETH:
    address payable contractAddress = payable(address(this));
    contractAddress.transfer(nftCost);
    
  2. 添加msg.value验证:在余额检查后新增一行,确保用户发送的ETH金额正确:
    require(msg.value == nftCost, "Incorrect ETH amount sent");
    
  3. 修正测试代码:调用safeMintNft时必须附带指定金额的ETH:
    const tx = await this.nft.connect(this.normalUser).safeMintNft({ value: ethers.utils.parseEther("0.1") });
    

额外优化建议

  • 避免使用transfer/send:这类函数有2300gas的限制,后续若需给其他地址转ETH,推荐使用call{value: amount}("")。
  • 优化白名单检查逻辑:直接存储布尔值标记是否为白名单买家,避免重复执行keccak256计算,节省gas消耗。

内容的提问来源于stack exchange,提问作者Lorenzo Battistela

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:27:30