Solidity gas 估计 - 设置为 0 时总是没有 gas

问题描述

我正在尝试找出解决此问题的方法,以处理交易确认订单并将值设置为 0

pragma solidity ^0.5.17;

contract Test {
    uint256 amount;

    constructor() public {}

    function join() public {
        amount += 100;
    }

    function leave() public {
        amount -= 100;
    }
}

鉴于这些交易(在 ropsten 上测试):

tx 1) 呼叫加入 确认 amount == 100

tx 2) Call Join (gas price 1) 待定 amount == 100 should tx3 get mined first

tx 3) Call Leave (gas price 100) 待定 amount == 0

但是,只要 tx 2 重新设置为 out of gasamount 将始终失败并显示 0 错误。如果该值高于 0,则不会发生这种情况。我的理解是将值设置为其 0 状态而不是正整数会花费更多的气体,并且气体估计没有考虑到这一点。我已经尝试过 delete,希望这能提供 gas 退款以补偿过低的 gas 限制,但它仍然失败。

有没有一种优雅的方法来处理这种情况?我能想到的唯一方法是高估所有 join 交易的 gas,这有其明显的缺点,或者永远不要将 amount 设置回 0

解决方法

你的观察是正确的。

通过将其设置为 0,您将删除存储空间,并获得 gas 退款。这就是为什么它需要更少的气体。但是gas退款有上限,所以你不能用它来存储ETH。

在一个 slot 中存储一个值需要 20,000 gas(来源:https://github.com/ethereum/go-ethereum/blob/d13c59fef0926af0ef0cff0a2e793f95d46442f0/params/protocol_params.go#L41

从存储槽加载一个值需要 2,200 gas(来源:https://github.com/ethereum/go-ethereum/blob/d13c59fef0926af0ef0cff0a2e793f95d46442f0/params/protocol_params.go#L89

这就是为什么您会看到不同的 gas 消耗值。

只需将您的 gasLimit 设置为一些凭经验得出的粗略估计值。

跟踪您的交易,您将看到所有的 gas 消耗值。

现在,gas 退款的工作方式是,在状态转换函数期间,您需要为合约运行提供全部 gas。在此运行期间,所有 gas 退款都累积在 StateDB(临时状态对象)中。在状态转换功能结束时,您将获得合同将要进行的所有存储释放的退款。这就是为什么你必须设置 Etherscan 显示的更高的 gas 限制,因为假设你的合约需要 15,000 gas 才能运行,在存储释放后(比如 5,000 gas),Etherscan 会显示交易需要 10,000 gas。这是不正确的,因为您在最后获得了gas 退款,但在开始时您需要全部gas(15,000)。 Gas 退款由矿工赞助,他的账户将获得更少的 ETH,因为他正在向您支付这些退款。