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

Forge测试预估Gas与主网真实交易差异原因及精准估算方案

Forge测试Gas预估与主网真实消耗差异问题

我在使用forge test时发现,预估的Gas消耗量远低于以太坊主网真实交易的Gas消耗。以某笔主网交易为例,原交易消耗122532 Gas,但forge测试预估仅为106309,差距明显。

我尝试通过以下步骤复刻该交易:

  • 从该交易所在区块的前一个区块进行fork
  • 使用vm.prank从同一发起者地址0xf5213a6a2f0890321712520b8048D9886c1A9900发送交易
  • 调用目标地址0xE592427A0AEce92De3Edee1F18E0157C05861564(UniswapV3 Router)并传入相同交易数据

测试代码如下:

contract EstimateGas is Test {
    
    function setUp() public {
        vm.createSelectFork("https://eth.drpc.org", 19400684);
    }

    function testEstimateGas() public {
        vm.prank(0xf5213a6a2f0890321712520b8048D9886c1A9900);
        bytes memory data = hex"414bf389000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000d1d2eb1b1e90b638588728b4130137d262c87cae0000000000000000000000000000000000000000000000000000000000000bb8000000000000000000000000f5213a6a2f0890321712520b8048d9886c1a990000000000000000000000000000000000000000000000000000000000065ecea7e00000000000000000000000000000000000000000000000058c3de02f05a8000000000000000000000000000000000000000000000000000000027c593b519ff0000000000000000000000000000000000000000000000000000000000000000";
        address router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
        (bool success,) = router.call(data);
        require(success, "fail");
    }
}

我使用的forge版本为forge 0.2.0 (42da942 2024-03-17T00:17:57.841914000Z)。

请问造成该差异的原因是什么?如何在forge测试中精准估算Gas?


问题解答

一、差异产生的核心原因

1. 状态访问的Gas计算差异

主网交易中,合约访问冷状态(即该状态数据近期未被其他交易访问过)时,会收取更高的Gas费用(冷存储访问成本为2100 Gas,热存储仅为100 Gas)。而在fork测试环境中,所有状态数据默认都是热状态——因为fork操作会一次性加载相关状态到本地,导致forge计算时使用了热存储的Gas费率,从而低估实际消耗。

2. 交易固有开销未完全模拟

以太坊主网交易的基础Gas开销包含:

  • 21000 Gas的基础交易费
  • 每字节交易数据的Gas费用(非零字节4 Gas,零字节1 Gas)
    forge测试中直接调用合约的方式,不会完全模拟主网交易的这些固有开销,尤其是交易数据的字节成本,会导致预估结果偏低。

3. EVM版本与优化差异

主网节点使用的EVM版本可能与forge测试环境的EVM存在细微差异,部分操作码的Gas成本在不同EVM版本中略有调整;另外,forge默认会启用一些编译优化选项,而主网合约的执行环境没有这些优化,也会导致Gas计算偏差。

4. 链上环境的细微差异

即使fork了前一个区块,主网交易执行时的区块上下文(比如区块哈希、时间戳等)与测试环境可能存在细微差别,部分合约逻辑会依赖这些上下文变量,进而影响实际Gas消耗。

二、精准估算Gas的解决方案

1. 模拟冷状态访问

在测试中手动标记状态为冷状态,使用vm.accesses(address account, bytes32[] storageSlots)方法,指定需要设置为冷访问的存储槽,让forge按照冷存储的Gas费率计算:

// 在调用合约前,标记相关存储槽为冷状态
vm.accesses(router, getRelevantStorageSlots());

(注:需要自行获取合约中被访问的存储槽,可通过forge inspect工具分析合约存储布局)

2. 完整模拟主网交易流程

不要直接调用合约,而是使用vm.sendTransaction方法模拟完整的主网交易,该方法会包含基础交易费和数据字节成本:

function testEstimateGas() public {
    vm.sendTransaction({
        from: 0xf5213a6a2f0890321712520b8048D9886c1A9900,
        to: router,
        data: data,
        gasPrice: tx.gasPrice
    });
}

通过这种方式,forge会计算完整的交易Gas开销,结果更贴近主网真实值。

3. 关闭编译优化

在foundry.toml中关闭编译优化,确保测试环境与主网合约执行环境一致:

optimizer = false
optimizer_runs = 0

4. 匹配主网EVM版本

在foundry.toml中指定与主网一致的EVM版本,比如以太坊上海升级后的版本:

evm_version = "shanghai"

5. 集成RPC节点的Gas估算(辅助验证)

可以通过调用以太坊节点的eth_estimateGas RPC方法,传入完整的交易参数,获取更精准的预估结果,在测试中可以集成该逻辑:

function testEstimateGasViaRPC() public {
    uint256 estimatedGas = vm.rpcUint(
        "eth_estimateGas",
        abi.encode(
            address(0xf5213a6a2f0890321712520b8048D9886c1A9900),
            router,
            data
        )
    );
    console.log("RPC Estimated Gas:", estimatedGas);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:05:02