{ 跳至内容 }

Solidity 0.6.x 特性:使用不可变变量节省存储成本

作者:Daniel Kirchner 发布于 2020年5月13日

解释

随着0.6.5 版本,Solidity 引入了用于状态变量的 immutable 关键字。不可变状态变量只能在合约创建期间赋值,但在已部署合约的生命周期内保持不变。不可变变量的最大优势在于读取它们比读取普通状态变量便宜得多,因为不可变变量不会存储在存储中,而是它们的取值将直接插入到运行时代码中。

如何使用不可变变量

可以使用 immutable 关键字声明不可变状态变量。在合约创建期间无法读取它们,但必须在构造函数中精确写入一次或在其声明中直接初始化。运行时代码只能读取不可变变量,但不能写入它们。以下是一个 immutable 的示例

pragma solidity >=0.6.5;
contract SimpleDonationManager {
    uint256 immutable minDonation = 42;
    uint256 immutable maxDonation = calcMaxDonation();
    address immutable owner;
    constructor() public {
        owner = msg.sender;
        // Error: Immutables cannot be read in constructor.
        // assert(minDonation <= maxDonation);
        // Error: Immutables can only be assigned once.
        // owner = address(0);
    }
    function withdraw(uint256 amount) public {
        require(msg.sender == owner); // Requires no sload!
        msg.sender.transfer(amount);
    }
    receive() external payable {
        require(msg.value >= minDonation && msg.value <= maxDonation);
    }
    function calcMaxDonation() internal pure returns (uint256 max) {
        max = 10000 * 42;
        // Error: Cannot read from Immutables in construction context.
        // max = 10000 * minDonation;
    }
    // Error: cannot write to Immutables from runtime context.
    // function changeOwner(address newOwner) external {
    //     owner = newOwner;
    // }
}

不可变变量的工作原理

虽然普通状态变量存储在存储中的固定偏移量处,但不可变变量并非如此。相反,不可变变量直接存储在已部署的字节码中,即运行时代码将包含每个不可变变量读取的占位符,而创建代码将不可变变量的值插入这些占位符中。实际上,这意味着在运行时读取不可变变量将减少到一个简单的 PUSH32 操作码。

虽然这种机制将来可能会发生变化,但目前在合约创建期间,会为不可变变量的值保留一个专用的内存区域。然后,构造函数代码可以将预期的不可变变量值存储到该内存区域中。在构造函数之后执行并返回合约运行时代码的编译器生成的创建代码部分将读回不可变变量的值并将它们插入运行时字节码中的所有出现位置。

限制和未来计划

到目前为止,仅支持值类型的不可变变量。但是,此限制将来可能会放宽。这很可能通过使用与值类型不可变变量相同的机制将偏移量存储到运行时代码中,并使用 codecopy 将引用类型不可变变量的内容读取到内存中来实现。可能还允许在创建上下文中读取不可变变量,如果编译器可以确保在读取之前已经分配了不可变变量。

上一篇

下一篇

参与进来

GitHub

推特

Mastodon

矩阵

了解更多

博客文档使用案例贡献关于论坛

2024Solidity 团队

安全策略

行为准则