基于 bytes4 函数选择器动态调用 solidity 函数

Call solidity function dynamically, based on its bytes4 function selector

在智能合约中,假设我有一个函数想要调用 另一个动态功能,基于一些内部逻辑。 这里它获取函数选择器作为 bytes4 变量。

之后可以使用分支逻辑来调用 目标函数之一。 参见:(A)

但是,是否可以避免这种情况并直接调用函数选择器? 参见:(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)
}


详情

注释

我在评论中写了 扩展 @kj-crypto 的建议。 如果有 另一种方法 来完成上述 而无需 使用 address(this).call(...),我洗耳恭听!

关于选项B:

  • 使用 call 将 return 一个字节对象,然后您应该将其转换为适当的类型,在本例中为整数。 (额外的气体使用量)
  • 要使用 call,您需要打包选择器和参数(额外的 gas 使用量)

只要你在同一个合约中使用一个函数,就没有必要使用它的 abi 规范,因为你已经知道函数在哪里,它是如何定义的,你可以毫不费力地调用它。

正在扩展 以上:

Do you mean sth like 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) - 如果您需要函数是 pure,不幸的是,它不能,因为 address(this).call(...) 可能会修改状态。

(2) - return 类型将默认为 bytes memory,因为这是 address(this).call(...) 的 return 类型。您可以转换它,但这会增加代码的复杂性,这与最初的动机背道而驰。

(3) - 要正确 处理address(this).call(...),需要对元组中的bool returned 做一些事情。例如使用 require()。这也违背了最初的动机,因为它只是将分支逻辑从一种形式转移到另一种形式(if ... elserequire()),并且在那个时候更昂贵。

(4) - 总的来说,原始函数的 gas 成本似乎低于建议的形式,因此更有优势。请注意,这尚未通过实验验证,如果有人想试一试,这里是 (full solidity file).