函数调用
内部函数调用
当前合约的函数可以直接(“内部”)调用,也可以递归调用,如以下无意义的示例所示:
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.22 <0.9.0; // This will report a warning contract C { function g(uint a) public pure returns (uint ret) { return a + f(); } function f() internal pure returns (uint ret) { return g(7) + f(); } }
这些函数调用被转换为 EVM 内的简单跳转。这具有不清除当前内存的效果,即将内存引用传递给内部调用的函数非常有效。只能在内部调用同一合约实例的函数。
您仍然应该避免过度递归,因为每个内部函数调用至少使用一个堆栈槽并且只有 1024 个可用槽。
外部函数调用
也可以使用this.g(8);
andc.g(2);
表示法调用函数,其中 c
是合约实例,g
是属于 的函数c
。通过任何一种方式调用函数g
都会导致它被“外部”调用,使用消息调用而不是直接通过跳转。请注意,函数调用this
不能在构造函数中使用,因为实际的合约尚未创建。
其他合约的函数必须在外部调用。对于外部调用,所有函数参数都必须复制到内存中。
笔记
从一个合约到另一个合约的函数调用不会创建自己的交易,它是作为整个交易的一部分的消息调用。
在调用其他合约的函数时,您可以通过特殊选项指定调用时发送的 Wei 或 Gas 数量。请注意,不鼓励明确指定 gas 值,因为操作码的 gas 成本可能会在未来发生变化。您发送给合约的任何 Wei 都会添加到该合约的总余额中:{value: 10, gas: 10000}
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.2 <0.9.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } } contract Consumer { InfoFeed feed; function setFeed(InfoFeed addr) public { feed = addr; } function callFeed() public { feed.info{value: 10, gas: 800}(); } }
您需要将修饰符payable
与info
函数一起使用,否则该value
选项将不可用。
警告
请注意,仅在本地设置 函数调用的发送量和发送量,最后的括号执行实际调用。so 不调用函数并且和设置丢失,只 执行函数调用。feed.info{value: 10, gas: 800}
value
gas
feed.info{value: 10, gas: 800}
value
gas
feed.info{value: 10, gas: 800}()
由于 EVM 认为对不存在的合约的调用总是成功的,Solidity 使用extcodesize
操作码检查即将被调用的合约是否确实存在(它包含代码),如果不存在则引发异常. 如果返回数据将在调用后被解码,则跳过此检查,因此 ABI 解码器将捕获不存在合约的情况。
请注意,在对地址而不是合约实例进行操作的低级调用的情况下,不会执行此检查。
笔记
对预编译合约使用高级调用时要小心 ,因为即使它们执行代码并且可以返回数据,编译器也会根据上述逻辑认为它们不存在。
如果被调用的合约本身抛出异常或耗尽gas,函数调用也会导致异常。
警告
与另一个合约的任何交互都会带来潜在的危险,特别是如果事先不知道合约的源代码。当前的合约将控制权移交给被调用的合约,这可能会做任何事情。即使被调用的合约继承自已知的父合约,也只需要继承的合约有正确的接口即可。然而,合同的执行可能是完全任意的,因此会造成危险。此外,请做好准备,以防它在第一次调用返回之前调用系统的其他合约,甚至返回调用合约。这意味着被调用合约可以通过其函数改变调用合约的状态变量。以某种方式编写函数,例如,
笔记
在 Solidity 0.6.2 之前,指定值和气体的推荐方法是使用f.value(x).gas(g)()
. 这在 Solidity 0.6.2 中已被弃用,并且自 Solidity 0.7.0 起不再可行。
命名调用和匿名函数参数
函数调用参数可以按任何顺序按名称给出,如果它们被包含在下面的示例中。参数列表的名称必须与函数声明中的参数列表一致,但可以是任意顺序。{ }
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.0 <0.9.0; contract C { mapping(uint => uint) data; function f() public { set({value: 2, key: 3}); } function set(uint key, uint value) public { data[key] = value; } }
省略的函数参数名称
未使用的参数(尤其是返回参数)的名称可以省略。这些参数仍将存在于堆栈中,但无法访问。
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.22 <0.9.0; contract C { // omitted name for parameter function func(uint k, uint) public pure returns(uint) { return k; } }