我想学习如何在 Yul 中使用 setimmutable 和 loadimmutable 来设置不可变变量,这些变量在合约中检索时具有 Gas 效率。
如何使用 setimmutable 和 loadimmutable 的示例代码,使用对象表示法会很棒!
因此,本质上不可变在常规 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
复制运行时字节码以证明上述内容按预期工作。