这篇文章介绍 Ethereum 开发者的一些实用工具:Foundry 除了测试之外的功能及 VSCode 的 Solidity Visual Developer 插件
这篇文章介绍 Ethereum 开发者的一些实用工具:Foundry 除了测试之外的功能及 VSCode 的 Solidity Visual Developer 插件
Photo by Dan Cristian Pădureț on Unsplash
Foundry 除了提供合约开发、测试和部署,其实还有许多其他实用的功能。
如果是 Linux 或 macOS,先安裝 foundryup,接着直接用 foundryup 指令就可以安裝。未來要升级 foundry 也只需要执行 foundryup 就好,非常简单直觉。
// Install foundryup
curl -L https://foundry.paradigm.xyz | bash
// Install or update Foundry
foundryup
详细可以参考 Foundry book 的 Installation 页面。
Foundry 安装完后会有 forge、cast 及 anvil 三个指令,每个指令都有许多 option,这时有 Auto Completion 会非常省事。例如产生 bash 用的 Auto Completion script:
mkdir -p $HOME/.local/share/bash-completion/completions
forge completions bash > $HOME/.local/share/bash-completion/completions/forge
cast completions bash > $HOME/.local/share/bash-completion/completions/cast
anvil completions bash > $HOME/.local/share/bash-completion/completions/anvil
exec bash
其他 Shell script 的产生方式可以参考 Foundry book 的 Shell Autocompletion 页面。
其实就和 Ganache 及 hardhat node 一样:跑起一個节点。可以是全新的一个本地节点,也可以是背后连上 Forked State(例如 Forked Mainnet、Forked Goerli)的节点。如果你平常都是习惯用来跑一个短暂的全新测试节点就直接下 anvil 即可。
如果你会想在一个 Forked State 內实验或测试的话,多加上 --fork-url 和 --fork-block-number 的 option。另外可以留意 --compute-units-per-second 这个 option,它会控制每秒请求的计算量,避免取 Forked State 太频繁导致遇到像是 Alchemy Rate Limit 的问题。
更详细的节点设置可以参考 Foundry book 的 Anvil Reference 页面。
cast 是用来取链上资料非常好用的工具(也有送交易的工具但个人觉得不太实用)。因为有很多功能,这里我只会列出我常用及觉得实用的功能。
注:要读取链的状态记得要在 foundry.toml 档里附上 endpoint url(eth_rpc_url=$URL)或透过 --rpc-url $URL 的方式。
首先是抓取交易资讯:
注:cast run --debug 的 debug 介面可能沒有那么好读,如果不需要看到执行过程中的 memory/stack/storage等细节时可以直接用 ethtx.info 或 Tenderly ,好读很多。
cast tx $TX_HASH
cast receipt $TX_HASH
cast run $TX_HASH --quick --debug
接着是从 Etherscan 抓取合约资讯:
// Download WETH9 contract file to tmp folder
cast etherscan-source -d tmp 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 --etherscan-api-key $API_KEY
注:如果想直接在浏览器开启 VSCode 环境来浏览、编辑合约的话,可以使用 deth.net。还可以透过书签一键开启,非常方便,请参考这则 twitter。
接着是 encode/decode ABI 的功能:
cast 4b/4bd/4be:反查 function selector、calldata 或是 event signature。在手上只有一笔交易或一个 event 的 raw data 时,可以透过这些工具来尝试找出它要执行什么函式或 emit 了什么 event
cast calldata:用 function signature 及参数组出 calldata。在为多签检查或组 calldata 的时候很方便 注:因为 function selector 只有 4 bytes,如果多个 function signature 都是同一个 function selector 的话,则反查回傳所有可能 function signature
// Decode function selector of "transfer(address,uint256)"
cast 4b 0xa9059cbb
// Decode calldata of a transfer
cast 4bd 0xa9059cbb000000000000000000000000f8ed47951b8eb0997d9f038fb1fcea46b171ea2f0000000000000000000000000000000000000000000000000000000078ca2e7e
// Decode event signature of "Transfer(address,address,uint256)"
cast 4be 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
// Encode calldata for a transfer
cast calldata "transfer(address,uint256)" 0xf8ed47951B8eB0997D9F038FB1FCea46B171Ea2f 2026516094
最后是一些转换或计算的小工具:
cast —-from-utf8/--to-ascii:在 hex 或字串之間转换
cast sig:计算 function selector
cast keccak:计算 keccak hash
cast compute-address:计算 CREATE(不是 CREATE2)的地址
cast --from-utf8 "pls return my money"
// 0x706c732072657475726e206d79206d6f6e6579
cast --to-ascii 0x706c732072657475726e206d79206d6f6e6579
// "pls return my money"
cast sig "transfer(address,uint256)"
// 0xa9059cbb
cast keccak "transfer(address,uint256)"
// 0xa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b
cast compute-address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --nonce 123
// Computed Address: 0xB57130C323EC5A53F89b73c9cA6f9Fc3c11744f6
更多 cast 功能可以参考 Foundry book 的 cast Commands 页面。你可以从中按照你的需求找到对你有帮助的常用指令。
forge 的 inspect 指令则是用来挖出一个合约更进阶的资讯。它不是去拉链上的资料,而是作用在本地端的档案(例如 MyContract),所以如果你需要分析(链上的)其他合约,你需要先把它下载下来。
forge inspect MyContract ...
注:在分析前 forge 会先编译合约,会花一些时间。
首先是列出合约基本资料的功能:
接下來是比较进阶的使用:irOptimized(或 iro),主要是你想再优化你合約的 gas 消耗时可以采用的方式。
forge inspect MyContract irOptimized
它会呈现你的合约编译成(优化过的)Yul 后的长相。Yul 就像 assembly,它介于 Solidity 和 EVM opcode 之间,它可以让你看到你写的 Solidity function 背后实际上还做了哪些事情,如此你就可以开始刪去一些不需要的检查。
这个工具是从这两则 twitter(link1, link2)发现到的,这边直接引用里面的范例和图片来搭配说明。以一个简单的将 number 变数递增的 Counter 合约为例,里面有一个 setNumber 函式用来设置 number 的值及一个 increment 函式用来把 number 加 1。右边则是执行 forge inspect Counter irOptimized 后所输出 Yul 版本的 Counter 合约:
source: https://twitter.com/w1nt3r_eth/status/1579486967963693057 可以看到一个简单的 Solidity 合约编译完后变得复杂许多。接下来以 increment 函式为例:
source: https://twitter.com/w1nt3r_eth/status/1579486967963693057 可以发现一个number++里面其实包含了很多检查,例如 not payable 和 overflow。但其实我们知道它只会以一次加 1 的方递增所以几乎不可能会 overflow(加上 unchecked),然后如果这个函式是可以接受 ether 的话,那就可以再省掉 not payable 的检查(加上 payable):
source: https://twitter.com/w1nt3r_eth/status/1579486967963693057 可以发现省去了许多执行步骤。
比起原地把 Solidity 程式码替换成assembly 程式码这个比较冒险的优化方式,这是一个新的管道让你能重新完整地检视你的合約,並且可以看出修改前后的对比。但要注意还是以安全为优先,不要为了省一点的 gas 而拿掉你沒有把握的程式码或检查。
其他 forge inspect 功能可以参考 Foundry book 的 forge inspect 页面。
最后是介绍一个在 review 合约时很实用的 VSCode 插件:Solidity Visual Developer
注:以下介绍会是个人使用心得,所以会有特色或功能是沒有介绍到的。可以自己下载来试用看看,看能不能发现适合你的用途的功能。
Solidity Visual Developer - Visual Studio Marketplace 🌐 📩 🔥 ] Advanced Solidity Language Support Code Augmentation Source Exploration Visual Security Linting An extension… marketplace.visualstudio.com https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor
里面最常看到的就是它为你合约的变数套上框框并能连结到宣告变数的地方,以及函式的参数套上底色方便识别(虽然有时候颜色太多会看得有点眼花)。另外有 variable shadowing 的警告提醒,例如你的合约继承了 Ownable 合约但某个函式宣告了一个 owner 参数,这时候这个 owner 参数就会被套上显眼的红色框框来提醒你:
owner 变数被 owner 参数 shadow 了
keywords 例如 block.timestamp、tx.origin、msg.data 或是 external 等等关键字都有提供 hover时显示安全提醒:
block.timestamp 的安全提醒
external 的安全提醒 如果你在 review 合约需要做笔记时,可以利用 @audit 及 @audit-ok 这两个 tag,它会出现像 Bookmarks 那样的图示方便你回到笔记过的程式码段落:
@audit 及 @audit-ok 分別显示红色及绿色的书签提示 它还有提供一些分析整個合约的功能,让你能得到不同合约间的继承关系(inheritance)和函式之间的关系(graph),这边直接使用官方的图:
https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor
https://marketplace.visualstudio.com/items?itemName=tintinweb.solidity-visual-auditor 还有其他工具像是 flatten:
Solidity Visual Developer 这个插件对 review 或审计合约算是非常有幫助,但写合约时帮助比较少。虽然对一般人来说里面大部分的工具其实平常几乎都用不太到,不过至少知道有哪些工具可以使用之后,未来有天当你需要 review 合约的时候它们就可以派上用场。
Thanks to Cyan Ho
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!