问题描述
我正在尝试找出解决此问题的方法,以处理交易确认订单并将值设置为 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 gas
,amount
将始终失败并显示 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,因为他正在向您支付这些退款。