Solidity 调用其他合约

本文介绍了智能合约之间的相互调用,并通过代码示例展示了如何实现合约间的通信,解释了 call 函数的使用、ABI编码、以及函数返回值的处理。

到目前为止,我们所做的一切都是直接调用智能合约。但智能合约之间也能够互相通信,这不仅是可能的,实际上也是可取的。

让我们来看一个简单的示例。


contract ExampleContract {
    function askTheMeaningOfLife(address source)
        public
        returns (uint256) {
            (bool ok, bytes memory result) = source.call(
                abi.encodeWithSignature("meaningOfLifeAndAllExistence()")
            );
            require(ok, "call failed");

            return abi.decode(result, (uint256));
    }
}

contract AnotherContract {
    function meaningOfLifeAndAllExistence()
        public
        pure
        returns (uint256) {
            return 42;
    }
}

你可以通过以下方式查看它的实际效果。

https://static.wixstatic.com/media/61a666_ad5ca9f4df6844fc8f3c4d7463fdd53e~mv2.png

因为我们已经回顾了元组、abi 编码bytes memory,这里唯一令人惊讶的部分是call,以及askTheMeaningOfLife() 不是一个 view 函数。

为什么 askTheMeaningOfLife() 不是 view 函数?如果你尝试用 view 修饰符编译它,它将无法编译。

View 函数是只读的。当你调用任意智能合约的函数时,你无法知道它是否是只读的。因此,Solidity 不允许你将调用其他智能合约的函数指定为 view。

此外,尽管我们可以看到AnotherContract中的meaningOfLifeAndAllExistence返回了一个 uint256,但在一般情况下是无法知道的。它可能返回一个字符串。

函数总是返回 abi 编码的字节。Remix 如何知道将字符串格式化为字符串并将数字格式化为数字?在背后,它执行了我们在这里所做的abi.decode操作。

元组中的bool ok部分是什么?调用其他智能合约的函数可能会失败,例如如果函数 revert。为了知道外部调用是否 revert,返回了一个布尔值。在这个实现中,调用函数 askTheMeaningOfLifeAndAllExistence 也会 revert,但这并不是必须的。

这里有一个有趣的问题。如果你调用一个不存在的智能合约会发生什么?

尝试用 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db 作为参数调用 askTheMeaningOfLife(address source)。

不要偷懒,在上面的代码中试试看!

它会 revert,但这并不是因为地址不存在,而是因为你试图解码空数据。如果我们注释掉解码部分,那么当我们调用一个不存在的地址时,函数将不再 revert。

当你在 Remix 中打开交易下拉菜单时,你会看到 revert 的解释。

https://static.wixstatic.com/media/61a666_07808a80849d4e6c8938a4459a811a87~mv2.png


contract ExampleContract {
    function askTheMeaningOfLife(address source)
        public
        returns (uint256) {
            (bool ok, bytes memory result) = source.call(
                abi.encodeWithSignature("meaningOfLifeAndAllExistence()")
            );
            require(ok, "call failed");

            //return abi.decode(result, (uint256));
            return 0;
    }
}

如果另一个合约需要参数怎么办?以下是如何处理的代码。


contract ExampleContract {
    function callAdd(address source, uint256 x, uint256 y)
        public
        returns (uint256) {
            (bool ok, bytes memory result) = source.call(
                abi.encodeWithSignature("add(uint256,uint256)", x, y)
            );
            require(ok, "call failed");

            uint256 sum = abi.decode(result, (uint256));
            return sum;
    }
}

contract Calc {
    function add(uint256 x, uint256 y)
        public
        returns (uint256) {
            return x + y;
    }
}

要注意不要在 "add(uint256,uint256)" 中加入空格。

准备好将其付诸实践了吗?

练习问题

CrossContract

想了解更多?

查看 Solidity 训练营以了解更多关于智能合约开发和代币标准的内容。

  • 原文链接: rareskills.io/learn-soli...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/