[英]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.