如何在纯yul代码中使用setimmutable和loadimmutable?

问题描述 投票:0回答:1

我想学习如何在 Yul 中使用 setimmutable 和 loadimmutable 来设置不可变变量,这些变量在合约中检索时具有 Gas 效率。

如何使用 setimmutable 和 loadimmutable 的示例代码,使用对象表示法会很棒!

solidity
1个回答
0
投票

因此,本质上不可变在常规 Solidity 和纯 Yul 中的工作方式是编译器在运行时字节码(部署的合约代码)中为我们使用不可变变量的所有时间创建 32 字节占位符

每当在运行时字节码中使用不可变变量时,编译器都会自动创建占位符。如果我们在构造函数中定义不可变变量,但从不使用该不可变变量,则不会创建占位符,并且不可变值将简单地被推入堆栈,然后在构造函数执行期间再次弹出。

在 Solidity 中,我们只需通过以下方式即可定义和使用不可变变量:

contract A {
    uint256 immutable internal x = 9;

    function returnImmutable() external pure returns (uint256) {
        return x;
    }
}

因此,在部署时,运行时字节码将在

returnImmutable()
函数部分内有 1 个占位符。运行时字节码将使用
CODECOPY
操作码从 ROM 复制到内存。然后,32 字节的 0 占位符将在内存中被
0x0000000000000000000000000000000000000000000000000000000000000009
覆盖,然后从内存中返回最终的运行时字节码以部署合约。

对于 Yul,我们遵循相同的流程,但现在我们有:

object "A" {
    // constructor
    code {
        datacopy(0, dataoffset("RuntimeCode"), datasize("RuntimeCode")) // CODECOPY
        setimmutable(0, "x", 9)  // uint256 immutable internal x = 9;
        return(0, datasize("RuntimeCode"))
    }

    // Deployed code
    object "RuntimeCode" {
        code {
            if iszero(iszero(callvalue())) {    // Revert if ether sent
                revert(0, 0)
            }
            let selector := shr(0xe0, calldataload(0))
            switch selector
            case 0x335173c0 {  // 'returnImmutable()' func sig
                let value := loadimmutable("x")
                mstore(0, value)
                return(0, 0x20)
            }
            default {
                revert(0, 0)
            }
        }
    }

}

这个 Yul 基本上等同于上面的 Solidity,其中

loadimmutable("x")
告诉编译器在
0x335173c0
函数中创建 1 个占位符。在构造函数中
setimmutable(0, "x", 9)
告诉编译器用值
9
覆盖占位符。请注意,偏移量(
setimmutable
中的第一个参数)应该是
0
,因为这是相对于占位符的偏移量。因此,如果您的偏移量为
0x20
,则
9
的值将被放置在占位符 + 1 个单词处 -> 因此将创建一个额外的单词来存储
9
,而不是覆盖原始占位符

您可以将此 Yul 粘贴到 evm.codes 中来部署合约,然后使用 calldata

0x335173c0
复制运行时字节码以证明上述内容按预期工作。

© www.soinside.com 2019 - 2024. All rights reserved.