繁体   English   中英

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

[英]Call solidity function dynamically, based on its bytes4 function selector

在智能合约中,假设我有一个 function,它想根据一些内部逻辑动态调用另一个 function。 在这里它获得 function 选择器作为bytes4变量。

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

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


细节

  • myDynamicFuncpublic的, myFuncA + myFuncB也是public的。
  • 所有 3 个功能都在同一个智能合约中实现。

笔记

我写了一个答案,扩展了@kj-crypto在评论中的建议。 如果有另一种方法可以在使用address(this).call(...)的情况下完成上述操作,我洗耳恭听!

关于B选项:

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

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

扩展@kj-crypto上面的评论

你的意思是像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) - 如果您需要 function 是pure的,不幸的是,它不能是纯的,因为address(this).call(...)可能会修改 state。

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

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

(4) - 总的来说,原始 function 的 gas 成本似乎低于建议的形式,因此更有优势。 请注意,这尚未通过实验验证,如果有人想给它一个 go,这里是( 完整的 solidity 文件)。

selector是 bytes4 类型,没有方法调用 function 或调用 function。

bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));

或者它是返回数据值:

  nonPayableAddress.call(abi.encodeWithSignature("transfer(address,uint256)", 0xaddress, amount))

唯一可以使用selector调用另一个合约的 function 的是

(bool success, bytes memory data) = contractAddress.call(
        abi.encodeWithSelector(SELECTOR, to, value)
    );

call, delegateCall, callcode方法可用于地址类型, transfersend方法可用于支付地址类型。

暂无
暂无

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

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