getPrice函数在用户无免费额度时触发RPC错误,求修复方案
问题修复方案
错误根源
触发execution reverted的核心原因是整数乘法溢出:
- 若使用Solidity 0.8.0+版本,编译器默认开启整数运算溢出检查,当
price.mul(_quantity)的结果超出uint256的最大值(2^256-1)时,会直接触发revert。 - 若使用0.8.0以下版本,虽无默认溢出检查,但乘积超出范围会导致数值绕回,后续逻辑可能引发异常(你当前场景更偏向0.8+版本的溢出检查触发)。
你的getPrice函数逻辑本身没问题,但运算过程的溢出导致了执行失败。
具体修复步骤
1. Solidity合约修复
方案A:使用SafeMath库(兼容所有Solidity版本)
引入OpenZeppelin的SafeMath,用安全运算避免溢出:
import "@openzeppelin/contracts/utils/math/SafeMath.sol"; contract YourNFT { using SafeMath for uint256; uint256 public constant maxFree = 1; uint256 public constant price = 2200000000000000; // 0.0022 ETH(以wei为单位) // 记录用户已铸造数量 mapping(address => uint256) public mintedCounts; function getPrice(address _sender, uint256 _quantity) public view returns (uint256) { uint256 _userFreeAmount = maxFree.sub(mintedCounts[_sender]); if (_userFreeAmount <= 0) { return price.mul(_quantity); } else if (_quantity.sub(_userFreeAmount) <= 0) { return 0; } else { return price.mul(_quantity.sub(_userFreeAmount)); } } // 补充publicMint函数(确保mintedCounts正确更新) function publicMint(uint256 _quantity) external payable { // 此处需校验msg.value与getPrice返回值一致,以及其他铸造逻辑 mintedCounts[msg.sender] += _quantity; // ... 其他铸造逻辑(如NFT mint、转账等) } }
方案B:Solidity 0.8+版本简化写法
0.8+版本自带溢出检查,可直接用原生运算符;若确定单次铸造数量不会过大,也可手动跳过检查提升效率:
contract YourNFT { uint256 public constant maxFree = 1; uint256 public constant price = 0.0022 ether; // 直接用ether后缀更直观 mapping(address => uint256) public mintedCounts; function getPrice(address _sender, uint256 _quantity) public view returns (uint256) { uint256 _userFreeAmount = maxFree - mintedCounts[_sender]; if (_userFreeAmount <= 0) { // 若限制单次最大铸造数量(如100),可放心用unchecked unchecked { return price * _quantity; } } else if (_quantity <= _userFreeAmount) { return 0; } else { unchecked { return price * (_quantity - _userFreeAmount); } } } }
2. 前端辅助防御
在调用合约前限制单次铸造数量,从源头避免溢出场景:
const mint = async () => { let myProvider = web3.currentProvider; let networkCheck = myProvider.networkVersion == null ? myProvider.chainId : myProvider.networkVersion; if (networkCheck == CHAINID) { if (contract && account) { const quantity = parseInt($('.supply').text()); // 新增:限制单次最大铸造数量,比如100个 if (quantity > 100) { alert("单次最多铸造100个NFT"); return; } try { const price = await contract.methods.getPrice(account, quantity).call(); await contract.methods.publicMint(quantity).send({ from: account, value: price }); $.statusSection.init("minting success."); } catch (error) { alert(error.name); alert(error.message); } } } else { await switchNetwork(); location.reload(); } }
额外校验点
- 确认
price的单位为wei:0.0022 ETH等于2200000000000000wei,若直接写0.0022未加ether后缀,会导致数值错误。 - 确保
mintedCounts在publicMint函数中正确更新,否则_userFreeAmount的计算会出错。
内容的提问来源于stack exchange,提问作者ZO eth
相关产品推荐
相关产品推荐

