在EVM中,每个存储插槽是256位。每存储一次数据都会调用‘SSTORE’指令,当前版本下,如果是新增需要消耗20000gas,如果是更新则消耗5000gas。
1、EVM中由于每个存储插槽是256位,不够256位的存储,EVM会自动填充,因此如果不够256位还要消耗额外的gas。所以,在开发中,把
uint8(<256) => uint256
2、将外部函数参数进行拼接打包,让原本分散的参数调用,打包成一个参数进行调用。然后在函数内部进行拆解。
比如
function test(uint128 a, uint128 b) public {}
修改为:
function test(uintc256 c) public {
//….这里是拆解c的代码
}
这里推荐使用移位的方法进行拆解,如下:
//假设c 是由“a(128位)b(128位)”组成 function unPack(uint256 c) public pure returns(uint256 a, uint256 b) { uint256 b = uint256(c & ((1<<128)-1)); c>>128; uint256 a = uint256(c & ((1<<128)-1)); return a, b; }
3、使用constant声明常量、使用immutable声明只进行一次赋值的变量(也就是编译才进行赋值)。
4、结构体中通过SOLC编译器自动打包。SOLC编译器默认按照顺序进行打包写入插槽中,如果打包以后超过256位则舍弃该变量,重开新插槽对该变量进行打包存储。因此结构体中的变量顺序很重要,可以通过调整顺序节省gas。例如:
struct Data {
uint64 a;
uint128 c;
uint256 d;
uint64 b;
}
//ac打包占用192位,在打包c的时候超过256了,因此ac占用一个插槽,不足256的部分补0;
//d打包占用满了256位,因此b也需要占用1个插槽。
//所以,总共需要3个插槽,执行三次sstore 指令
struct Data {
uint64 a;
uint64 b;
uint128 c;
uint256 d;
}
//转换顺序以后,abc打包占满256位,d进行单独打包且占满256位。因此,需要2个插槽,执行两次sstore命令,且不需要自动补0。节省了大量gas。
5、无状态合约。
顾名思义,无状态合约就是不存储变量,没有插槽的存储和更新,因此消耗的gas是极少的。那么该问了,无状态合约,怎么存储数据呢?买个关子,接下来重开一篇文章介绍《无状态合约》。