Solidity删除存储未获Gas退款,如何验证Gas退款功能?
如何通过Solidity合约验证Gas退款功能?
我编写了一个简单的Solidity合约来验证删除存储时的Gas退款机制,合约代码如下:
pragma solidity >=0.7.0 <0.9.0; contract Storage { bool[] private data = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true ]; event Count(uint i); function removeRange(bool isDelete) public returns(bool[] memory) { for (uint j = 0; j < data.length; j++) { emit Count(j); if(!isDelete) { continue; } delete data[j]; } return data; } }
我在方法中添加了计数器来执行耗Gas的操作。当不执行删除操作时,Remix日志返回的Gas消耗为:
gas 151499 gas
transaction cost 131738 gas
execution cost 131738 gas
当调用执行存储删除的函数时,不仅没有获得Gas退款,Gas消耗反而更高:
gas 186013 gas
transaction cost 152150 gas
execution cost 152150 gas
问题原因
你当前的合约无法触发Gas退款的核心原因:
bool类型仅占存储槽的1位,delete data[j]只是将该位设为默认值false,并没有释放整个存储槽。Ethereum的Gas退款仅针对释放完整存储槽的操作——即把曾经写入非默认值的存储槽重置为默认值时才会触发。- 执行
delete本身有基础Gas开销(约5000 Gas/操作),这个开销暂时盖过了单一位修改带来的微小退款,导致总消耗上升。
正确验证方案
1. 优化合约设计
使用占满完整存储槽的类型(如uint256),并分开基准操作和删除操作,让退款效果更清晰:
pragma solidity >=0.7.0 <0.9.0; contract GasRefundValidator { // 每个元素占1个完整存储槽的数组 uint256[] private storageSlots; // 初始化写入非默认值,为后续退款做准备 constructor() { for (uint256 i = 0; i < 50; i++) { storageSlots.push(i + 1); // 写入非0值 } } // 仅执行循环(基准消耗,无存储操作) function baselineLoop() public { for (uint256 j = 0; j < storageSlots.length; j++) { // 空循环,模拟遍历开销 } } // 循环+删除存储(触发退款) function loopWithDelete() public { for (uint256 j = 0; j < storageSlots.length; j++) { delete storageSlots[j]; } } // 批量清空数组(更高效的存储释放) function clearEntireArray() public { delete storageSlots; // 一次性释放所有数组元素的存储槽 } }
2. 验证步骤
- 部署合约后,先调用
baselineLoop(),记录execution cost(记为baseCost)。 - 再调用
loopWithDelete(),记录execution cost(记为deleteCost)。 - 对比两者:单个
delete的基础开销约5000 Gas,而释放存储槽的退款约48000 Gas,所以每个delete的净收益约43000 Gas。批量操作后,deleteCost会远低于baseCost + 50*5000的理论值,证明退款生效。 - 额外测试
clearEntireArray():它的Gas消耗会远低于循环删除,因为EVM对数组清空有优化,一次性释放所有存储槽,退款效果更显著。
3. 关键注意事项
- 退款上限:单交易的Gas退款不能超过总消耗的20%,若交易总消耗过低,退款会被限制。
- 存储槽状态:只有存储槽之前写入过非默认值时,重置为默认值才会触发退款。
- 关注execution cost:Remix中的
gas包含交易固定开销(如签名验证),execution cost才是合约执行的实际消耗,用于对比更准确。
内容的提问来源于stack exchange,提问作者Ritzy Dev
相关产品推荐
相关产品推荐

