今天,Fuel Labs 介绍了 Yul+,它为 Yul 增加了各种 QoL 功能,Yul 是 Ethereum 虚拟机的一种低级中间语言。
Yul 是由 Solidity 开发者编写的一种不可思议的小众语言,作为进一步优化的编译目标。它的特点是一个简单而实用的低级语法。它允许开发者比 Solidity 更接近原始的 EVM,并由此带来了对 Gas 使用的大幅改善的承诺。
Fuel Labs 已经在很大程度上用 Yul 实现了其最初的开放性测试的 OP 合约,但我们注意到,即使增加极少的基本语言,我们的代码也会变得更加清晰和高效。
Yul+可以被看作是 Yul 的一个实验性升级,Yul 可能会在以后的时间里把它的一些功能原生地整合起来。
一些 Yul 的基础知识
一个带有构造函数和运行时的基本 Yul 合约:
object "EmptyContract" {
code {
// Your constructor code
datacopy(0, dataoffset("Runtime"), datasize("Runtime"))
return(0, datasize("Runtime"))
}
object "Runtime" {
code {
// Your runtime code
}
}
}
处理请求数据
// copy calldata to memory
// this copies 36 bytes of transaction calldata to memory position 0
calldatacopy(0, 0, 36)
内存管理
// store and read memory
// store 0xaa at memory position 100
mstore(100, 0xaa)
// load 32 byte chunk from memory position 100 and assign to someVar
let someVar := mload(100)
哈希
// hash memory position 0 to 0+32, assign result to someHash
let someHash := keccak256(0, 32)
状态存储
// store value 0xaa in state storage slot 3
sstore(3, 0xaa)
// get value from state storage 3 and assign to someVar
let someVar := sload(3)
函数和条件语句
// Functions and conditions
function someMethod(someVar, someOther) -> someResult {
if eq(someVar, someOther) {
someResult := 0x45
}
}
// Loops
for { let i := 0 } lt(i, 100) { i := add(i, 1) } {
// some loop code
}
// Switches
switch someVar
case 0 {
// when someVar == 0
}
case 1 {
// when someVar == 1
}
default {
// default
}
Yul+ 特点
- 所有现有的 Yul 语言功能
- 枚举
- 常量
- 以太坊标准 ABI 签名生成 ( sig"function …" )
- 布尔值(真,假)
- 默认情况下的安全数学(即加法、减法、乘法的溢出保护)
- 注入的方法(mslice 和 require)
- 内存结构 ( mstruct )
用法
枚举、常量和布尔值
enum Colors (
Red, // 0
Blue, // 1
Green // 2
)
// Constant someConst will equal 1
const someColor := Colors.Blue
// Constant someBool will equal 0x1
const someBool := true
以太坊标准 ABI 签名生成方法 sigs 和主题:
// someVar will equal 4 byte method signature 0x6057361d
let someVar := sig"function store(uint256 val)"
// someTopic will equal 32 byte topic hash 0x69404ebde4a368ae324ed310becfefc3edfe9e5ebca74464e37ffffd8309a3c1
let someTopic := topic"event Store(uint256 val)"
默认情况下,所有计算现在都是安全的,如果需要,可以在编译器中将其禁用。
let someVar := add(3, sub(4, 2))
// will compile to this, with safeAdd, safeSub methods injected
let someVar := safeAdd(3, safeSub(4, 2))
为方便起见,我们添加了一个内存片 mslice,并要求如果为真:
mstore(300, 0xaabbccdd) // note, mstore left pads zeros by 28 bytes
let someVal := mslice(328, 3) // will return 0xaabbcc
require(gt(someVal, 0)) // someVal > 0 or revert(0, 0) nicely
最后,我们启用内存结构。这些用于描述内存中已经存在的结构,例如调用数据、哈希数据或任何具有写入内存的结构的数据。
它提供了广泛的定位、偏移、散列、索引和组织功能,以通过按需注入的简洁高效的预制函数更好地处理内存。我们仍然继续使用注入函数的函数符号,这不会破坏现有的 Yul 语法风格。
// Let’s assume we assign some calldata to memory position 0
// this describes an abstract memory construction:
mstruct SomeCalldata(
signature: 4,
value: 32,
)
let methodSig := SomeCalldata.signature(0) // slices out sig
let someVal := SomeCalldata.value(0) // slices out value
// we also get some nice indexing and offset features
SomeCalldata.value.position(0) // equals 4 (i.e. 0 + 4)
// Index ordering values as well
SomeCalldata.signature.index() // equals 0
SomeCalldata.value.index() // equal 1
// Keccak hashing
SomeCalldata.value.keccak256(0) // equals 32 byte hash of value
// Calculate entire size of calldata structure
SomeCalldata.size(0) // equals 36 (i.e. 4 + 32)
示例:Yul+ SimpleStore 合约
object “SimpleStore” {
code {
datacopy(0, dataoffset(“Runtime”), datasize(“Runtime”))
return(0, datasize(“Runtime”))
}
object “Runtime” {
code {
calldatacopy(0, 0, 36) // copy calldata into memory
mstruct Calldata( // mstruct describes calldata
sig: 4,
val: 32
)
switch Calldata.sig(0) // get signature at positive zero
case sig”function store(uint256 val)” { // store method
sstore(0, Calldata.val(0))
}
case sig”function get() returns (uint256)” { // get method
mstore(100, sload(0))
return (100, 32)
}
}
}
}
在线调试
Yul+ - Low-Level Ethereum Devepment
总结
总之,Fuel Labs 团队希望通过创建更多低级替代方案来扩展以太坊虚拟机的可能性,我们每天都使用这些替代方案来为生态系统构建高性能的 optimistic rollup 可扩展性。