React+Solidity开发DApp调用ERC20 transfer报-32000执行回滚错误
报错核心原因
- 调用方法使用错误:你写的
await contract.transfer.call(account, 50)是只读模拟调用,不会发起真实的链上交易,且本地模拟时如果未指定发送地址,msg.sender不是你当前连接的钱包地址,直接触发合约里require(numTokens <= balances[msg.sender])的余额校验,交易回滚抛出execution reverted错误。 - 概念理解偏差:
transfer()是ERC20代币转账函数,本身不需要payable修饰符,只有需要接收链原生币(Rinkeby测试网ETH)的函数才需要加payable,你之前对函数属性的认知是错的。 - 数值精度问题:ERC20代币默认采用18位精度,直接传整数50实际只对应0.00000000000000005个代币,如果你要转50个完整单位的代币,数值需要做精度转换,这个是后续会触发逻辑异常的隐藏问题。
修复步骤
1. 修正前端调用逻辑
完全去掉.call调用,改用发起交易的方法,根据你用的库选对应写法:
web3.js 版本
// 获取当前连接的钱包地址 const accounts = await web3.eth.getAccounts() const currentAccount = accounts[0] // 做精度转换,18位精度和ETH单位规则一致,直接用toWei转换即可 const transferValue = web3.utils.toWei('50', 'ether') // 发起真实链上交易,指定发送方为当前钱包地址 await contract.methods.transfer(接收方钱包地址, transferValue).send({ from: currentAccount })
ethers.js 版本
// 获取当前钱包签名者 const signer = await provider.getSigner() // 精度转换,第二个参数填你合约设置的decimals值,默认是18 const transferValue = ethers.utils.parseUnits('50', 18) // 连接签名者后发起交易 await contract.connect(signer).transfer(接收方钱包地址, transferValue)
2. 合约侧校验
- 确认你已经给测试用的钱包地址mint了足够数量的测试代币,余额不足的话哪怕调用方式正确,还是会触发require校验回滚。
- 不要给
transfer函数添加payable修饰符,否则用户调用时误转的ETH会永久锁在合约里,没有对应提币逻辑就无法取出。
内容的提问来源于stack exchange,提问作者KylianMbappe
相关产品推荐
相关产品推荐

