[英]Call solidity function dynamically, based on its bytes4 function selector
在智能合約中,假設我有一個 function,它想根據一些內部邏輯動態調用另一個 function。 在這里它獲得 function 選擇器作為bytes4
變量。
之后可以使用分支邏輯來調用目標函數之一。 見:(一)
但是,是否可以避免這種情況並直接調用 function 選擇器? 參見: (B)
function myDynamicFunc(uint256 someParam) public {
bytes4 selector = /* ... some internal logic ... */
if (selector == this.myFuncA.selector) {
myFuncA(someParam);
} else if (selector == this.myFuncB.selector) {
myFuncB(someParam);
}
// (A) instead of something like this ^ branching logic (which works)
selector.invoke(someParam);
// (B) can something like this ^ instead by calling the selector directly instead (does not work)
}
細節
myDynamicFunc
是public
的, myFuncA
+ myFuncB
也是public
的。筆記
我寫了一個答案,擴展了@kj-crypto
在評論中的建議。 如果有另一種方法可以在不使用address(this).call(...)
的情況下完成上述操作,我洗耳恭聽!
關於B選項:
call
將返回一個字節 object,然后您應該將其轉換為適當的類型,在本例中為 integer。(額外的 gas 使用)call
,您需要打包選擇器和參數(額外的 gas 使用)只要你在同一個合約中使用 function,就沒有必要使用它的 abi 規范,因為你現在已經知道 function 在哪里,它是如何定義的,你可以毫不費力地調用它。
你的意思是像
address(this).call(abi.encodePacked(selector, <func-args>))
嗎?
...並創建了這個實現:
function myDynamicFunc(uint256 someParam)
public
// pure // --> (1)
returns (bytes memory result) // --> (2)
{
bytes4 selector =
/* ... some internal logic ... */
this.myFuncA.selector;
(bool success, bytes memory resultBytes) =
address(this).call(abi.encodePacked(selector, someParam));
require(success, "failed to call selector"); // --> 3
result = resultBytes;
}
總而言之,答案是:“是的,這是可能的,但不,這不是一個好主意。”
原因:
(1) - 如果您需要 function 是pure
的,不幸的是,它不能是純的,因為address(this).call(...)
可能會修改 state。
(2) - 返回類型將默認為bytes memory
,因為這是address(this).call(...)
的返回類型。 您可以轉換它,但這會增加代碼的復雜性,這與最初的動機背道而馳。
(3) - 要正確處理address(this).call(...)
,需要對元組中返回的bool
做一些事情。 例如使用require()
。 這也違背了最初的動機,因為它只是將分支邏輯從一種形式轉移到另一種形式( if... else
到require()
),並且在那個時候更昂貴。
(4) - 總的來說,原始 function 的 gas 成本似乎低於建議的形式,因此更有優勢。 請注意,這尚未通過實驗驗證,如果有人想給它一個 go,這里是( 完整的 solidity 文件)。
selector
是 bytes4 類型,沒有方法調用 function 或調用 function。
bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
或者它是返回數據值:
nonPayableAddress.call(abi.encodeWithSignature("transfer(address,uint256)", 0xaddress, amount))
唯一可以使用selector
調用另一個合約的 function 的是
(bool success, bytes memory data) = contractAddress.call(
abi.encodeWithSelector(SELECTOR, to, value)
);
call, delegateCall, callcode
方法可用於地址類型, transfer
和send
方法可用於支付地址類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.