为什么 Hedera 上的 eth_estimateGas 返回意外高的值?

bgu*_*uiz 4 rpc solidity hedera-hashgraph hedera

我知道这eth_estimateGas并不准确,\n但目前,我得到的实际gasUsed值\n大约是 \xc2\xa0 返回值的 6% eth_estimateGas

\n

在以下示例中,\n我使用完全相同的输入调用相同的智能合约两次,只有 1 个差异:

\n
    \n
  • 在第一次调用中,gasLimit = eth_estimateGas
  • \n
  • 在第二次调用中,gasLimit = eth_estimateGas * 0.064\n
      \n
    • 该值仅占估计值的一小部分
    • \n
    • 这个值是通过反复试验获得的......而不是通过任何计算
    • \n
    \n
  • \n
\n
    // with exact estimated amount of gas\n    const estimatedGas2 = (await expendGasSc.estimateGas.updateState(1_000_000_123n)).toBigInt();\n    console.log(\'estimatedGas2\', estimatedGas2);\n    const gasLimit2 = estimatedGas2 * 1n;\n    console.log(\'gasLimit2\', gasLimit2);\n    const txResponse2 = await (await expendGasSc\n        .updateState(\n            1_000_000_123n,\n            { gasLimit: gasLimit2 },\n        ))\n        .wait();\n    const gasUsed2 = txResponse2.gasUsed.toBigInt();\n    console.log(\'gasUsed2\', gasUsed2);\n\n    // with small fraction of estimated amount of gas\n    const estimatedGas4 = (await expendGasSc.estimateGas.updateState(1_000_000_123n)).toBigInt();\n    console.log(\'estimatedGas4\', estimatedGas4);\n    const gasLimit4 = estimatedGas4 * 64n / 1000n; // <---  6.4% \n    console.log(\'gasLimit4\', gasLimit4);\n    const txResponse4 = await (await expendGasSc\n        .updateState(\n            1_000_000_123n,\n            { gasLimit: gasLimit4 },\n        ))\n        .wait();\n    console.log(\'txResponse4\', txResponse4);\n    const gasUsed4 = txResponse4.gasUsed.toBigInt();\n    console.log(\'gasUsed4\', gasUsed4);\n\n
Run Code Online (Sandbox Code Playgroud)\n

结果如下:

\n
    \n
  • 什么时候gasLimit是40万,gasUsed什么时候是32万\n
      \n
    • 这恰好是指定的 80% gasLimit,\表明 HIP-185 的天然气超额保留惩罚*可能已经生效。
    • \n
    • 请参阅我之前相关问题的答案以了解上下文。
    • \n
    \n
  • \n
  • gasLimit是 25,600 时,gasUsed是 23,816\n
      \n
    • 这大于指定值的 80% gasLimit,\表明 HIP-185 的 Gas 超额保留惩罚尚未生效
    • \n
    \n
  • \n
\n
estimatedGas2 400000n\ngasLimit2 400000n\ngasUsed2 320000n\nestimatedGas4 400000n\ngasLimit4 25600n\ngasUsed4 23816n\n
Run Code Online (Sandbox Code Playgroud)\n

因此,我期望eth_estimateGas返回一个\n比 400,000 更接近 23,816 的值。\n与实际相比,为什么它会返回如此意外高的估计值\n?

\n
\n

这是智能合约:

\n
// SPDX-License-Identifier: GPL-3.0\npragma solidity 0.8.18;\n\ncontract ExpendSomeGasDemo {\n    uint256 public state;\n\n    function updateState(\n        uint256 newState\n    )\n        public\n        returns (uint256 updatedState)\n    {\n        state = newState;\n        updatedState = newState;\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,此合约部署在 Hedera 测试网上:\n0x9C58D0159495F7a8853A24574f2B8F348a72424c

\n

请注意,上面的 Javascript 示例使用 ethers.js。

\n

请注意,这个问题是我上一个问题的后续问题:\n Hedera 上几乎相同的交易中的价值存在巨大差异- 为什么?gasUsed

\n

小智 6

您正在eth_estimateGas通过 Hedera JSON-RPC 中继进行查询,该中继目前使用静态值代替实际的 Gas 估算,这就是估算一致的原因。

\n

请注意,当HIP-584 的实现\n发布时,这很快就会被实际的 Gas 估计算法取代。\n\n\xe2\x80\x8b

\n

具体来说,您可以查看packages/relay/src/lib/constants.ts,\n 来查看定义为常量的与 Gas 成本相关的值:\n\xe2\x80\x8b

\n
{\n    // ...\n    TX_BASE_COST: 21_000,\n    TX_HOLLOW_ACCOUNT_CREATION_GAS: 587_000,\n    TX_DEFAULT_GAS_DEFAULT: 400_000,\n    TX_CREATE_EXTRA: 32_000,\n    TX_DATA_ZERO_COST: 4,\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x8b\n这些由函数estimateGaspackages/relay/src/lib/eth.ts其返回值中使用。

\n

请注意,目前没有运行实际的 Gas 估计算法。\n这一行 - return this.defaultGas;,映射到TX_DEFAULT_GAS_DEFAULT上面的 -\这也是为什么您每次执行eth_estimateGas智能合约调用时都会得到相同的值 400,000\n。\n这里\下面是代码片段\n\xe2\x80\x8b

\n
  async estimateGas(transaction: any, _blockParam: string | null, requestId?: string) {\n    const requestIdPrefix = formatRequestIdMessage(requestId);\n    this.logger.trace(`${requestIdPrefix} estimateGas(transaction=${JSON.stringify(transaction)}, _blockParam=${_blockParam})`);\n    // this checks whether this is a transfer transaction and not a contract function execution\n    if (transaction && transaction.to && (!transaction.data || transaction.data === \'0x\')) {\n      const value = Number(transaction.value);\n      if (value > 0) {\n        const accountCacheKey = `${constants.CACHE_KEY.ACCOUNT}_${transaction.to}`;\n        let toAccount: object | null = this.cache.get(accountCacheKey);\n        if (!toAccount) {\n          toAccount = await this.mirrorNodeClient.getAccount(transaction.to, requestId);\n        }\n\xe2\x80\x8b\n        // when account exists return default base gas, otherwise return the minimum amount of gas to create an account entity\n        if (toAccount) {\n          this.logger.trace(`${requestIdPrefix} caching ${accountCacheKey}:${JSON.stringify(toAccount)} for ${constants.CACHE_TTL.ONE_HOUR} ms`);\n          this.cache.set(accountCacheKey, toAccount);\n\xe2\x80\x8b\n          return EthImpl.gasTxBaseCost;\n        }\n\xe2\x80\x8b\n        return EthImpl.gasTxHollowAccountCreation;\n      }\n\xe2\x80\x8b\n      return predefined.INVALID_PARAMETER(0, `Invalid \'value\' field in transaction param. Value must be greater than 0`);\n    } else {\n      return this.defaultGas;\n    }\n  }\n\xe2\x80\x8b\n
Run Code Online (Sandbox Code Playgroud)\n