内存优化:错误消息
分析
在 Solidity 代码中执行回滚操作时,通常使用 require 或 revert 语句来回滚执行并返回错误信息。大多数情况下,可以使用汇编代码回滚并返回错误信息来进一步优化。以下是示例:
/// calling restrictedAction(2) with a non-owner address: 24042
contract SolidityRevert {
address owner;
uint256 specialNumber = 1;
constructor() {
owner = msg.sender;
}
function calls(uint256 num) external {
require(owner == msg.sender, "caller is not owner");
specialNumber = num;
}
}
/// calling restrictedAction(2) with a non-owner address: 23734
contract AssemblyRevert {
address owner;
uint256 specialNumber = 1;
constructor() {
owner = msg.sender;
}
function calls(uint256 num) external {
assembly {
if sub(caller(), sload(owner.slot)) {
mstore(0x00, 0x20) // store offset to where length of revert message is stored
mstore(0x20, 0x13) // store length (19)
mstore(0x40, 0x63616c6c6572206973206e6f74206f776e657200000000000000000000000000) // store hex representation of message
revert(0x00, 0x60) // revert with data
}
}
specialNumber = num;
}
}从上面的例子中我们可以看到,使用汇编语言进行同样的错误信息恢复比使用 Solidity 语言恢复节省了超过 300 Gas。这部分 Gas 节省来自于内存扩展成本以及 Solidity 编译器在后台执行的额外类型检查。
测试
文件位置:
src/assembly/AssemblyErr.sol
test/assembly/AssemblyErr.t.sol测试结果:
forge test --gas-report --mt test_revert_ -vvv --optimize
总结
-
更高效:可以直接操作存储槽、内存、calldata,省去 Solidity 运行时的各种边界检查和中间变量消耗,节省 gas。
-
自定义编码:允许你用自己的方式打包数据(如本例中自己决定如何编码字符串和长度)。
-
极限优化:可以省略一些安全检查和冗余操作,做到极致紧凑。
对应源码
本文配套代码固定到提交
8960383;Gas 数据会随编译器、优化器参数和 EVM 版本变化。