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

Goerli部署合约调用时Web3.js执行回滚问题修复咨询

问题修复方案

核心问题分析

你的交易回滚主要源自两个关键问题:

  • value参数精度丢失:用1e15这种浮点数表示wei单位金额,JavaScript的浮点数精度限制会导致实际传入合约的金额不符合预期,触发计算错误。
  • gas估算不足:tx.estimateGas()仅估算合约方法调用的基础gas,未覆盖转账操作的额外gas消耗,容易因gas不足触发回滚。

此外,你的Solidity合约缺少对接收者数组长度的校验,若传入非2个地址会直接报错,建议补充安全校验。

修复步骤

1. 修正value参数写法

避免使用浮点数,改用整数或Web3工具函数转换为精确的wei值:

  • 直接写wei整数:1000000000000000(对应0.001 ETH)
  • 用工具函数转换:web3.utils.toWei("0.001", "ether"),自动生成正确的wei整数

2. 优化gas设置

给估算的gas增加10%缓冲,避免因gas不足导致回滚:

gas: Math.floor((await tx.estimateGas()) * 1.1),

3. 补充Solidity合约安全校验

在splitEther方法开头添加数组长度校验,防止传入错误数量的接收者:

require(recipients.length == 2, "Must provide exactly 2 recipients");

修复后的完整代码

JavaScript代码

const Web3 = require("web3");
const fs = require("fs");
const { abi } = JSON.parse(fs.readFileSync("EtherSplitter.json"));

async function main() {
  const network = process.env.ETHEREUM_NETWORK;
  const web3 = new Web3(
    new Web3.providers.HttpProvider(
      `https://${network}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`
    )
  );

  const signer = web3.eth.accounts.privateKeyToAccount(
    process.env.SIGNER_PRIVATE_KEY
  );
  web3.eth.accounts.wallet.add(signer);

  const contract = new web3.eth.Contract(
    abi,
    process.env.ETHERSPLITTER_CONTRACT
  );

  const tx = contract.methods.splitEther([
    "0xdF8b511C3682b093736318A67Fcc2FEC6772D1a6",
    "0x1E2eBeBB3348B1FeFC29239c20Df1c78668180Cc"
  ]);
  
  const receipt = await tx
    .send({
      from: signer.address,
      // 改用工具函数保证精度
      value: web3.utils.toWei("0.001", "ether"),
      // 给估算gas加缓冲
      gas: Math.floor((await tx.estimateGas()) * 1.1),
    })
    .once("transactionHash", (txhash) => {
      console.log(`正在挖矿交易...`);
      console.log(`https://${network}.etherscan.io/tx/${txhash}`);
    });

  console.log(`交易已打包到区块 ${receipt.blockNumber}`);
}

require("dotenv").config();
main();

修复后的Solidity合约

contract EtherSplitter {
    function splitEther(address payable[] memory recipients) public payable {
        // 新增校验:必须传入2个接收地址
        require(recipients.length == 2, "Must provide exactly 2 recipients");
        uint fee = 10;
        
        recipients[0].transfer(msg.value * fee / 100);
        recipients[1].transfer(msg.value * (100 - fee) / 100);
    }

    receive() external payable {}
}

额外排查建议

  • 确认签名账户在Goerli测试网有足够ETH,覆盖转账金额+gas费用
  • 若接收地址是合约,确保其receive/fallback方法消耗gas不超过2300(transfer方法的gas限制),否则改用call{value: ...}("")替代transfer

内容的提问来源于stack exchange,提问作者Ronan McGovern

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:24:14