首先介紹 signature vs. selector:
setNumber(uint256)
0x3fb5c1cb
假設在 solidity 有一個 function 是:
function transfer(address to, uint256 amount) public virtual returns (bool) { ... }
那它的 function signature 為 transfer(address,uint256)
。沒有空格,也沒有 function 開頭。
對 function sginature 做 keccak-256 hash 後可以得到 32 bytes 如下:
0xa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b
而 transfer(address,uint256)
的 function selector 是 hash 後的前四個 bytes,也就是:0xa9059cbb
。
--
ABI encoding 是以太坊上的編碼格式,當我們要呼叫合約時,要先把函式名稱與參數編譯成 calldata 後,才能用來呼叫合約。
要使用一個合約呼叫另一個合約上的 function,寫法如下:
targetContractAddress.call(abi.encodeWithSignature("setNumber(uint256)", 32);
abi.encodeWithSignature
會回傳 calldata,長得像這樣:
0x3fb5c1cb0000000000000000000000000000000000000000000000000000000000000018
--
calldata 可以用來呼叫合約,也可能是一筆鏈上交易的 input data,它需經過 ABI encoding 後得到。calldata 的前四個 bytes 也就是 function selector,而之後所接的可能是 arguments, offset, length 等等。
假設我們對以下函式名稱與參數進行 ABI encoding:
transfer(uint256[],address)
transfer([5769, 14894, 7854], 0x1b7e1b7ea98232c77f9efc75c4a7c7ea2c4d79f1)
cast calldata
cast calldata "transfer(uint256[],address)" "[5769, 14894, 7854]" 0x1b7e1b7ea98232c77f9efc75c4a7c7ea2c4d79f1
得到的 calldata 如下:
0x8229ffb6 // function selector
0000000000000000000000000000000000000000000000000000000000000040 // offset
0000000000000000000000001b7e1b7ea98232c77f9efc75c4a7c7ea2c4d79f1 // address
0000000000000000000000000000000000000000000000000000000000000003 // length
0000000000000000000000000000000000000000000000000000000000001689
0000000000000000000000000000000000000000000000000000000000003a2e
0000000000000000000000000000000000000000000000000000000000001eae
offset: 用來標示 dynamic data 的位置,是以 offset 提供的位置起算。
--
Fixed-sized data types in solidity:
Dynamic data types:
使用 dynamic data 的 calldata 較為複雜,因為會有 offset,可以參考這篇文章,從 Working with dynamic calldata 的標題開始,會帶你拆解 calldata 看懂 offset。
https://www.rareskills.io/post/abi-encoding
建議搭配文末留言提供的 ABI CallData Visualizer,它能夠更清楚呈現 calldata 的樣貌。
PS: calldata 的長度會影響交易的 gas 成本,計算方式如下:
因為 zero byte 比較便宜,有些人會刻意將合約的地址創造成前面有很多個零,當這個地址作為參數放在 calldata 時就會比較便宜。