简体   繁体   中英

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. 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 .
  • All 3 functions are implemented in the same smart contract.

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:

  • Using call will return a bytes object, which then should you convert to appropiate type, in this case to an integer. (extra gas usage)
  • To use 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM