Solidity:使用 Ethers.js 的 Solidity 存储变量
以太坊和智能合约状态
以太坊虚拟机(EVM)上的数据使用Modified Merkle Patricia Trie数据结构进行组织。区块链上的每个区块引用4个树:[全局]状态树、存储树、交易树和接收树。状态树包含EOA(外部拥有的帐户)数据作为地址到ETH余额的映射,而智能合约数据存储在指向状态树的存储树中。
存储树中的智能合约数据表示合约的持久状态,可以通过更新全局状态的交易进行更改。在一个Solidity的智能合约中,动态变量被存在持久化的存储中。内存中初始化的任何变量都是临时的,将在执行下一次外部函数调用之前被删除。此外,无法修改的常量变量不使用存储空间,因此,使用更少的gas。
智能合约存储布局
以太坊虚拟机(EVM)中的智能合约都有自己的永久存储空间,该存储空间在键-值对的映射中包含32字节的插槽(键和值都是32字节)。
32 字节的固定大小变量
固定大小的32字节变量,如字符串、uint256和int256,会按照它们在智能合约中列出的顺序分配一个单独的存储槽。在StorageLayoutOne合约中,常量变量hello没有存储槽,因为它不能被修改。变量numOne、goodbye和num分别使用存储槽0x0、0x1和0x2。
contract StorageLayoutOne { string constant hello = "hello world"; // no storage
uint256 numOne = 1; // slot 0x0
string goodbye = "goodbye world"; // slot 0x1
int256 num; // slot 0x2}
固定大小变量< 32字节
如果可能,小于32字节的固定大小的变量将被字节打包到单个存储槽中。在StorageLayoutTwo合约中,变量lock、byteX、bytesY和bytesZ都将被打包到slot 0x0(1+1+4+16 = 22字节)中。下一个变量bytesA存储在0x1槽中,因为它不能放入前一个变量中。最后,变量bytesB和bytesC被打包到槽0x2中。
contract StorageLayoutTwo { bool lock; // slot 0x0
byte byteX; // slot 0x0
bytes4 bytesY; // slot 0x0
bytes16 bytesZ; // slot 0x0 bytes28 bytesA; // slot 0x1 bytes16 bytesB; // slot 0x2
bytes16 bytesC; // slot 0x2}
需要注意的是,EVM 在 32 字节上运行,因此使用小于 32 字节的变量可能会由于额外的转换操作而导致更高的 gas 成本。但是,字节打包允许EVM编译器在同一个存储槽内组合对变量的多个读和写操作,来抵消这一点。因此,以最有效的方式将小于32字节的变量组合在一起,以降低总体gas成本是很重要的。
动态大小的变量
使用 keccak-256 散列算法将可能超过 32 字节的动态大小的变量(例如动态数组和映射)散列到抗碰撞存储位置,该算法伪随机地选择 2²⁵⁶ 存储槽范围内的位置。如果你想知道