為什麼 Etherscan 的 opcode view 和 opcode disassembler 顯示 USDT 合約的 opcode 不同?
我們有 USDT 智能合約,位於
0xdac17f958d2ee523a2206206994597c13d831ec7
. 我正在嘗試反編譯智能合約(至少將其視為操作碼)。我看到的是它以60606040526000
(etherscan 上的 ABI)開頭[0] PUSH1 0x60 -> 6060 [2] PUSH1 0x40 -> 6040 [4] MSTORE -> 52 [5] PUSH1 0x00 -> 6000
所以說 opcode tool,但如果你
Switch to opcode view
在合約頁面上 ,你會看到以下操作碼:PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x04 CALLDATASIZE LT PUSH2 0x0196 JUMPI PUSH1 0x00 CALLDATALOAD
哪些不是
# from opcode tool [1] PUSH1 0x60 [3] PUSH1 0x40 [4] MSTORE [6] PUSH1 0x00 [7] DUP1 [9] PUSH1 0x14 [12] PUSH2 0x0100 [13] EXP [14] DUP2 [15] SLOAD [16] DUP2
問題是為什麼這些不同?什麼是正確的?我猜第二個變體是正確的,因為docs,但是 etherscan 會出錯嗎?順便說一句,如果你知道任何關於編譯的智能合約結構的文件,很高興分享,這樣我和每個人都能更好地理解它。
您的“操作碼工具”連結指向一個完全不同的地址 (
0x9e1b57fc92eba6434251a8458811c32690f32c45
)。如果您檢查原始地址的操作碼,您會發現它們是相同的:PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x04 CALLDATASIZE LT PUSH2 0x0196 JUMPI PUSH1 0x00 CALLDATALOAD PUSH29 0x0100000000000000000000000000000000000000000000000000000000 SWAP1 DIV ...
[1] PUSH1 0x60 [3] PUSH1 0x40 [4] MSTORE [6] PUSH1 0x04 [7] CALLDATASIZE [8] LT [11] PUSH2 0x0196 [12] JUMPI [14] PUSH1 0x00 [15] CALLDATALOAD [45] PUSH29 0x0100000000000000000000000000000000000000000000000000000000 [46] SWAP1 [47] DIV ...
順便說一句,如果你知道任何關於編譯的智能合約結構的文件,很高興分享,這樣我和每個人都能更好地理解它。
查看來自 OpenZeppelin 的多部分文章系列:解構 Solidity 合約 — 第 I 部分:簡介。
請注意,這只是描述了 Solidity 編譯器生成的字節碼。目前 EVM 不強制執行任何結構,因此不同的編譯器可以以不同的方式執行。EVM 所做的只是在位置 0 處開始執行二進制 blob,然後去任何跳轉的地方。二進製文件的某些部分可能永遠不會被執行——例如,您可以將隨機垃圾附加到任何有效的字節碼中,並且它將保持有效(並且該部分不會被執行,因為不能跳轉到它)。solc 使用這個事實來創建程式碼/數據部分(子程序集/子對象)並在最後添加元數據雜湊。例如,要部署的執行時程式碼是一個子程序集。如果你的合約部署了其他合約
new
,這些合約中的每一個的字節碼也有一個單獨的子程序集。這種自由形式的結構有其缺點,並且將來會發生變化。請參閱EIP-3540:EVM 對象格式 (EOF) v1,這是一個新標準,將使其更加嚴格。