在開發智能合約的過程,由於想要能夠達到多筆撮合的功能,我們實作了一個函式能夠輸入多筆訂單資料。在隨著我們加入了越來越多的功能,同時為了確保合約公正性,而需要越來越的參數,例如:礦工手續費、taker 手續費、maker 手續費和支付手續費方式等,我們遇到了變數過多而無法編譯的情況,而大量的資料所消耗的 gas 也成為一個問題。
我們在撮合的函式中,最後每個訂單會有這些資料:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 uint256 amountSell, uint256 amountBuy, address tokenSell, address tokenBuy, address user, uint256 nonce, uint256 gasFee, uint256 takerFee, uint256 makerFee, uint256 joyPrice, bool isBuy, uint8 v, byte32 r, byte32 s
於是我們試著想辦法減少變數的數量,在某次我看著 Etherscan 某交易顯示如下的參數資料時
1 2 3 4 5 6 7 8 9 10 11 12 13 Function: trade(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s, uint256 amount) *** MethodID: 0x0a19b14a [0]:0000000000000000000000000000000000000000000000000000000000000000 [1]:000000000000000000000000000000000000000000000000006a94d74f430000 [2]:000000000000000000000000a92f038e486768447291ec7277fff094421cbe1c [3]:0000000000000000000000000000000000000000000000000000000005f5e100 [4]:000000000000000000000000000000000000000000000000000000000024cd39 [5]:00000000000000000000000000000000000000000000000000000000e053cefa [6]:000000000000000000000000a11654ff00ed063c77ae35be6c1a95b91ad9586e [7]:000000000000000000000000000000000000000000000000000000000000001c [8]:caa3a70dd8ab2ea89736d7c12c6a8508f59b68590016ed99b40af0bcc2de8dee [9]:26e2347abfba108444811ae5e6ead79c7bd0434cf680aa3102596f1ab855c571 [10]:000000000000000000000000000000000000000000000000000221b262dd8000
我發現所有的參數長度都是 256 bits,無論你變數的型別是 byte32
、address
還是 uint8
。所以大部分的參數左邊都有大量的 ”0”,是未使用的位元資料,很自然的我就想到了利用這些”空間”。
資料合併 首先 uin256
和 byte32
的型別會佔滿整個變數空間,金額數量的 amount 和簽章的 r, s 都是不能使用的變數,所以我選擇了 address
欄位的變數來使用。另外也將不需要太多位元數的型別做了調整:
1 2 3 4 5 nonce -> 40 bits takerFee -> 16 bits makerFee -> 16 bits uint256 joyPrice -> 28 bits isBuy -> 4 bits (實際上 1 bit 即可, 為了文件容易表示)
一個原本是 address
的變數,例如:
1 000000000000000000000000a92f038e486768447291ec7277fff094421cbe1c
就可以塞入上面的資料變成
上面的資料對應如下
1 2 3 4 5 nonce: 0181bfeb takerFee: 0014 makerFee: 000a joyPrice: 0000000 isBuy: 1
所以原本的資料就可以減少為
1 2 3 4 5 6 7 8 9 uint256 amountSell, uint256 amountBuy, uint256 tokenSellWithData, address tokenBuy, address user, uint256 gasFee, uint8 v, byte32 r, byte32 s
當然 v 可以也在塞到另一個 address
的變數中,這邊就不再贅述。
編號 到此已經省下不少的變數,但是我又有了新的想法:token 的 address
應該是有限個,不如用資料庫的自動遞增 (Auto increment) 的方式來編號?於是我們進一步的把 token 和 user 都做了編號,最後資料變成:
最右邊的三個分別就表示了tokenSell, tokenBuy 和 user,而橘色的 1 則表示簽章的 v。最終我們的一筆訂單資料壓縮為:
1 2 3 4 5 6 uint256 amountSell, uint256 amountBuy, uint256 data, uint256 gasFee, byte32 r, byte32 s
從原本的 14 個變數,省為 6 個變數。
延伸閱讀 上一篇 Solidity 智能合約 Gas 優化技巧 - 函式名稱 下一篇 Solidity 智能合約 Gas 優化技巧 - 變數順序