In a smart contract, let's say I have a function which wants to invoke another function dynamically, based on some internal logic. Here it obtains the function selector as a bytes4
variable.
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? See: (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
. Notes
I have written up an answer expanding on @kj-crypto
's suggestion in the comments. If there is another way to accomplish the above without using address(this).call(...)
, I'm all ears!
Regarding option 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
, you need to pack the selector and the parameters (extra gas usage)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.
Expanding on @kj-crypto
's comment above:
Do you mean sth like
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.
(2) - The return type will default to bytes memory
, as this is the return type of 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. For example using 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.
(4) - Overall, the gas costs of the original function appear to be less than, and thus advantageous, over this suggested form. 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 ).
selector
is bytes4 type and has no method to call a function or invoke a 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
(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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.