[英]Call solidity function dynamically, based on its bytes4 function selector
In a smart contract, let's say I have a function which wants to invoke another function dynamically, based on some internal logic.在智能合约中,假设我有一个 function,它想根据一些内部逻辑动态调用另一个 function。 Here it obtains the function selector as a
bytes4
variable.在这里它获得 function 选择器作为
bytes4
变量。
After which it is possible to use branching logic to invoke one of the target functions.之后可以使用分支逻辑来调用目标函数之一。 See: (A)
见:(一)
However, is it possible to avoid that and invoke the function selector directly?但是,是否可以避免这种情况并直接调用 function 选择器? See: (B)
参见: (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)
}
Details细节
myDynamicFunc
is public
and myFuncA
+ myFuncB
are also public
. myDynamicFunc
是public
的, myFuncA
+ myFuncB
也是public
的。 Notes笔记
I have written up an answer expanding on @kj-crypto
's suggestion in the comments.我写了一个答案,扩展了
@kj-crypto
在评论中的建议。 If there is another way to accomplish the above without using address(this).call(...)
, I'm all ears!如果有另一种方法可以在不使用
address(this).call(...)
的情况下完成上述操作,我洗耳恭听!
Regarding option B:关于B选项:
call
will return a bytes object, which then should you convert to appropiate type, in this case to an integer. (extra gas usage)call
将返回一个字节 object,然后您应该将其转换为适当的类型,在本例中为 integer。(额外的 gas 使用)call
, you need to pack the selector and the parameters (extra gas usage)call
,您需要打包选择器和参数(额外的 gas 使用) As long as you are using a function in the same contract, there is no point to use its abi specification, because you already now where the function is, how is it defined and you can call it without any hassle.只要你在同一个合约中使用 function,就没有必要使用它的 abi 规范,因为你现在已经知道 function 在哪里,它是如何定义的,你可以毫不费力地调用它。
Expanding on @kj-crypto
's comment above:扩展
@kj-crypto
上面的评论:
Do you mean sth like
address(this).call(abi.encodePacked(selector, <func-args>))
?你的意思是像
address(this).call(abi.encodePacked(selector, <func-args>))
吗?
... and created this implementation: ...并创建了这个实现:
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;
}
To summarise, the answer is: "Yes it is possible, but no it isn't that great an idea."总而言之,答案是:“是的,这是可能的,但不,这不是一个好主意。”
Reasons:原因:
(1) - If you need the function to be pure
, it cannot be, unfortunately, because address(this).call(...)
potentially modifies state. (1) - 如果您需要 function 是
pure
的,不幸的是,它不能是纯的,因为address(this).call(...)
可能会修改 state。
(2) - The return type will default to bytes memory
, as this is the return type of address(this).call(...)
. (2) - 返回类型将默认为
bytes memory
,因为这是address(this).call(...)
的返回类型。 You can cast it, but this adds additional complexity to the code, which is against the grain of the original motivation.您可以转换它,但这会增加代码的复杂性,这与最初的动机背道而驰。
(3) - To properly handle address(this).call(...)
, need to do something with the bool
returned in the tuple. (3) - 要正确处理
address(this).call(...)
,需要对元组中返回的bool
做一些事情。 For example using require()
.例如使用
require()
。 This also against the grain of the original motivation, as it simply shifts the branching logic from one form to another ( if... else
to require()
), and a more expensive one at that.这也违背了最初的动机,因为它只是将分支逻辑从一种形式转移到另一种形式(
if... else
到require()
),并且在那个时候更昂贵。
(4) - Overall, the gas costs of the original function appear to be less than, and thus advantageous, over this suggested form. (4) - 总的来说,原始 function 的 gas 成本似乎低于建议的形式,因此更有优势。 Note that this has not been verified with experimentation, and if anyone would like to give it a go, here's the ( full solidity file ).
请注意,这尚未通过实验验证,如果有人想给它一个 go,这里是( 完整的 solidity 文件)。
selector
is bytes4 type and has no method to call a function or invoke a function. selector
是 bytes4 类型,没有方法调用 function 或调用 function。
bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
or it is return data value of:或者它是返回数据值:
nonPayableAddress.call(abi.encodeWithSignature("transfer(address,uint256)", 0xaddress, amount))
the only thing is available to call another contract's function using selector
is唯一可以使用
selector
调用另一个合约的 function 的是
(bool success, bytes memory data) = contractAddress.call(
abi.encodeWithSelector(SELECTOR, to, value)
);
call, delegateCall, callcode
methods are available for address, transfer
and send
methods are available for payable address type. call, delegateCall, callcode
方法可用于地址类型, transfer
和send
方法可用于支付地址类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.