title: 01. Hello Huff tags: huff template evm bytecode WTF Huff极简入门: 01. Hello Huff 我最近在重新学Huff,巩固一下细节,也写一个“Huff极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。 推特:@0xAAScience 社区:Discord|微信群|官网 wtf.academy 所有代码和教程开源在github: github.com/AmazingAng/WTF-Huff 这一讲,我们将介绍Huff语言,并使用Foundry运行模版合约。 Huff 大家可能很熟悉Solidity,但是几乎没听说过Huff。
title: 01. Hello Huff tags: - huff - template - evm - bytecode
我最近在重新学Huff,巩固一下细节,也写一个“Huff极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
所有代码和教程开源在github: github.com/AmazingAng/WTF-Huff
这一讲,我们将介绍Huff语言,并使用Foundry运行模版合约。
大家可能很熟悉Solidity,但是几乎没听说过Huff。Huff是一个低级的、为Ethereum智能合约设计的编程语言,它允许开发者编写高度优化的EVM字节码。Huff的两大特点:
难学: Huff不像Solidity抽象了EVM的底层工作原理,而是让开发者直接操作EVM的堆栈、内存、和存储。
高效: Huff像是给EVM Opcodes套了一层壳,几乎是直接在字节码层面编写合约,gas优化到极致。
因此,你在学习Huff前需要熟悉Solidity和EVM的工作原理。推荐的先修课程:
下面我们通过一个简单的合约SimpleStore.huff来学习Huff合约的结构。
/* 接口 */ #define function setValue(uint256) nonpayable returns () #define function getValue() view returns (uint256) /* 存储槽位 */ #define constant VALUE_LOCATION = FREE_STORAGE_POINTER() /* 方法 */ #define macro SET_VALUE() = takes (0) returns (0) { 0x04 calldataload // [value] [VALUE_LOCATION] // [ptr, value] sstore // [] stop // [] } #define macro GET_VALUE() = takes (0) returns (0) { // 从存储中加载值 [VALUE_LOCATION] // [ptr] sload // [value] // 将值存入内存 0x00 mstore // 返回值 0x20 0x00 return } // 合约的主入口,判断调用的是哪个函数 #define macro MAIN() = takes (0) returns (0) { // 通过selector判断要调用哪个函数 0x00 calldataload 0xE0 shr dup1 __FUNC_SIG(setValue) eq set jumpi dup1 __FUNC_SIG(getValue) eq get jumpi // 如果没有匹配的函数,就revert 0x00 0x00 revert set: SET_VALUE() get: GET_VALUE() }
下面,我们分段来学习这个huff合约。
首先,你需要定义合约的接口,像Solidity的接口一样,需要使用#define关键字。
/* 接口 */ #define function setValue(uint256) nonpayable returns () #define function getValue() view returns (uint256)
接下来,你需要声明存储槽位(storage slot),就像在Solidity合约中声明状态变量一样。FREE_STORAGE_POINTER()指向合约中未使用的存储槽(free storage)。
/* 存储槽位 */ #define constant VALUE_LOCATION = FREE_STORAGE_POINTER()
最后一部分要写合约中的方法(函数),本合约有3个方法:
SET_VALUE(): 改变VALUE_LOCATION存储的值。它先使用calldataload从calldata中读取变量的新值,然后使用sstore将新值存储到VALUE_LOCATION。GET_VALUE(): 读取VALUE_LOCATION存储的值。它利用sload将VALUE_LOCATION存储的值推入堆栈,然后利用mstore将值存到内存,最后使用return返回。MAIN(): 主宏,定义了合约的主入口。当对合约进行外部调用时,会运行这段代码来确定应该调用哪个函数。他先用0x00 calldataload 0xE0 shr读取calldata中的函数选择器,然后查看它是否与SET_VALUE()或GET_VALUE()匹配。如果匹配,就调用相应的函数;否则,回滚交易。/* 方法 */ #define macro SET_VALUE() = takes (0) returns (0) { 0x04 calldataload // [value] [VALUE_LOCATION] // [ptr, value] sstore // [] stop // [] } #define macro GET_VALUE() = takes (0) returns (0) { // 从存储中加载值 [VALUE_LOCATION] // [ptr] sload // [value] // 将值存入内存 0x00 mstore // 返回值 0x20 0x00 return } // 合约的主入口,判断调用的是哪个函数 #define macro MAIN() = takes (0) returns (0) { // 通过selector判断要调用哪个函数 0x00 calldataload 0xE0 shr dup1 __FUNC_SIG(setValue) eq set jumpi dup1 __FUNC_SIG(getValue) eq get jumpi // 如果没有匹配的函数,就revert 0x00 0x00 revert set: SET_VALUE() get: GET_VALUE() }
下面,我们介绍如何使用Foundry的插件foundry-huff运行模版项目。
首先,你需要在本地安装以下内容:
git --version,则说明您已正确安装。forge,cast和anvilforge --version并获取类似forge 0.2.0 (92f8951 2022-08-06T00:09:32.96582Z)的输出,您可以检测是否已正确安装。foundryup。huffc --version并获取类似huffc 0.3.0的输出,则说明您已正确安装。运行:
git clone https://github.com/WTFAcademy/WTF-Huff cd WTF-Huff
克隆并进入您的仓库后,您需要安装必要的依赖项。为此,只需运行:
forge install
要构建并测试您的合约,您可以运行:
forge build forge test

huffc打印huff合约的字节码:huffc src/SimpleStore.huff -b
控制台输会输出合约的字节码(creation code):
602e8060093d393df35f3560e01c8063552410771461001e5780632096525514610025575f5ffd5b6004355f55005b5f545f5260205ff3
如果想获取runtime code,可以使用huffc -r。
有关如何使用Foundry的更多信息,请查看Foundry Github Repository和foundry-huff library repository。
lib ├─ forge-std — https://github.com/foundry-rs/forge-std ├─ foundry-huff — https://github.com/huff-language/foundry-huff scripts ├─ Deploy.s.sol — 部署脚本 src ├─ SimpleStore — Huff中的简单存储合约 test └─ SimpleStore.t — SimpleStore测试
这一讲,我们介绍了Huff语言,学习了Huff合约结构,并运行了一个模版项目。Huff是一个低级的、为以太坊智能合约设计的编程语言,学习它不仅可以让你写出更优化的合约,还可以让你深入理解EVM。