Addresses
正確的 ABI 有效負載打包以呼叫另一個合約的地址
假設我有以下兩個契約,
Dest
並且Proxy
:pragma solidity ^0.5.0; contract Dest { function foo(string memory name, string memory symbol, uint256 decimals, uint256 totalSupply, address sender, uint256 value) public { /* * Logic code Here */ } function bar(address a, address b, address c, address d, uint256 e, address f, address sender, uint256 value) public { /* * Logic code Here */ } } contract Proxy { address private _owner; address private _dest; constructor(address dest) public { _owner = msg.sender; _dest = dest; } function getDest() public view returns(address) { return _dest; } function setDest(address dest) public { require(msg.sender == _owner); _dest = dest; } // Invoke takes msg.sender and msg.value and concatenates both to payload before to call the correct method function invoke(string memory method, bytes memory data) public payable { bytes memory payload = abi.encodePacked(bytes4(keccak256(bytes(method)))), data, abi.encode(msg.sender), abi.encode(msg.value)); (bool ok, bytes memory response) = _dest.call(payload); } }
合約中的
foo
和bar
方法都作為最終輸入參數。Dest``address sender, uint256 value
邏輯是使用者將呼叫傳遞完整的方法簽名和除最後兩個之外的
Proxy
所有 abi 編碼參數。代理按順序將方法簽名、部分呼叫參數打包在一起,
msg.sender
並msg.value
在一個唯一的打包有效負載中,然後使用打包有效負載作為輸入bytes
呼叫該call
方法。Dest
address
問題是當
foo
方法被呼叫時,sender
輸入參數的值不是原來的msg.sender
address
,而是包含一些不一致的值。
bar
但是當呼叫該方法時一切正常。我懷疑這個問題與
foo
方法的輸入參數的頭部有長度可變的字元串有關,而bar
只有固定長度的參數。關於如何建構通用解決方案的任何建議?
通常,您不能只連接部分 ABI 編碼,因為 ABI 編碼將可變長度參數拆分為固定和可變部分。對於函式
Foo
,正確的編碼是:selector (4 bytes) name offset (32 bytes) symbol offset (32 bytes) decimals (32 bytes) totalSupply (32 bytes) address (32 bytes) value (32 bytes) name length (32 bytes) name body (? bytes) symbol length (32 bytes) symbol body (? bytes)
當使用者
Foo
對除最後兩個之外的所有參數進行編碼時,編碼可能如下所示:selector (4 bytes) name offset (32 bytes) symbol offset (32 bytes) decimals (32 bytes) totalSupply (32 bytes) name length (32 bytes) name body (? bytes) symbol length (32 bytes) symbol body (? bytes)
當您附加
msg.sender
andmsg.value
時,您會得到:selector (4 bytes) name offset (32 bytes) symbol offset (32 bytes) decimals (32 bytes) totalSupply (32 bytes) address (32 bytes) value (32 bytes) name length (32 bytes) name body (? bytes) symbol length (32 bytes) symbol body (? bytes) msg.sender (32 bytes) msg.value (32 bytes)
所以結果顯然是不正確的。
正確的解決方案是首先使用 將所有參數解碼為單獨的局部變數
abi.decode
,然後使用msg.sender
和msg.value
using重新打包回來abi.encodeWithSignature
。像這樣的東西:string memory name; string memory symbol; uint256 decimals; uint256 totalSupply; (name, symbol, decimals, totalSupply) = abi.decode ( data, (string, string, uint256, uint256)); bytes memory payload = abi.encodeWithSignature ( method, name, symbol, decimals, totalSupply, msg.sender, msg.value);