简体   繁体   English

动态调用 solidity function,基于它的 bytes4 function 选择器

[英]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 . myDynamicFuncpublic的, myFuncA + myFuncB也是public的。
  • All 3 functions are implemented in the same smart contract.所有 3 个功能都在同一个智能合约中实现。

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选项:

  • Using 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 使用)
  • To use 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... elserequire() ),并且在那个时候更昂贵。

(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方法可用于地址类型, transfersend方法可用于支付地址类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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