https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#layout-of-state-variables-in-storage

https://blog.csdn.net/feifeilb/article/details/100715532

https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts

 

存储间隙是在基础合约中保留存储槽的约定,允许该合约的未来版本用完这些槽而不影响子合约的存储布局。

要创建存储间隙,请在基础合约中声明一个具有初始槽数的固定大小数组。这可以是一个数组,uint256以便每个元素保留一个 32 字节的槽。为数组使用名称gap或以 开头的名称,以便 OpenZeppelin Upgrades 能够识别间隙

为了让 EVM 对此进行优化,请确保您尝试对存储变量和struct成员进行排序,以便它们可以紧密打包。例如,按照而不是 的 顺序声明您的存储变量,因为前者只会占用两个存储槽,而后者会占用三个。uint128, uint128, uint256 | uint128, uint256, uint128

将同一组的数据类型放到一个32字节的插槽中,并在代码中逐个声明它们。将数据类型分组很重要,因为EVM按照给定顺序逐个保存变量。这只适用于状态变量和结构体内部。数组只有一种数据类型,不需要顺序。
将尽量多的变量存储在一个插槽中,只要它们的存储总小于等于32字节大小。例如,一个bool变量占用一个字节,uint8也是一个字节,uint16是两个字节,uint32是四个字节,依次类推。字节数据类型的存储大小更容易,例如,byte4就是四个字节。所以32个uint8变量的存储空间和1个uint256变量相同。只有当变量依次声明才有效,因为如果必须在变量间保存一个大数据类型,则需要新插槽。

solidity数据类型分类
  值类型:布尔类型(bool)、整型(int)、地址类型(address)、定长字节数组(bytes)、枚举类型(enum)、函数类型(function);

  引用类型:字符串(string)、数组(array)、结构体(structs)、映射(mapping)、不定长字节数组(bytes)

uint256 – 32 bytes 占用1个存储槽
address – 20 bytes 占用1个存储槽
uint128 – 16 bytes 两个连续占用1个存储槽

值类型 bytes1, bytes2, bytes3, …bytes32 包含从 1 到最多 32 的字节序列。