Solidity学习笔记-函数相关-01

特殊函数

receive()

一个合约只能有一个receive函数,该函数不能有参数和返回值,需设置为external,payable;
当本合约收到ether且未指定调用任何函数(calldata 为空)时执行,receive函数被触发;传进来的ether全部转给合约。

fallback()

一个合约只能有一个receive函数,该函数不能有参数和返回值,需设置为external;
可设置为payable;
当本合约的其他函数不匹配调用,或调用者未提供任何信息,且没有receive函数,fallback函数被触发;

0.5.x之前使用 function() external payable { } 来实现。后面发现有问题才拆分上面两个。比如0.5.x的一个实现。

pragma solidity ^0.5.0;

contract Charity {
    mapping(address => uint256) public donations;

    function processDonation(address user) external payable {
        donations[user] += msg.value;
    }
}

contract Receiver {
    event ValueReceived(address user, uint256 amount);

    function() external payable {
        emit ValueReceived(msg.sender, msg.value);
    }
}

contract CharitySplitter {
    function donate(Charity charity) external payable {
        charity.processDonation.value(msg.value)(msg.sender);
    }
}
const goodCharity = await Charity.new();
const receiver = await Receiver.new();
const badCharity = await Charity.at(receiver.address);
const charitySplitter = await CharitySplitter.new();
// 成功捐款10wei
await charitySplitter.donate(goodCharity, { value: 10 });

// 这里虽然传入了一个错误的地址,但是触发了Receiver的fallback函数,没有看到任何异常
await charitySplitter.donate(badCharity, { value: 10 });

为了修复这个问题。可以使用下面的实现。

pragma solidity ^0.6.0;
contract Charity {
    mapping (address => uint256) public donations;

    function processDonation(address user) external payable {
        donations[user] += msg.value;
    }
}
contract Receiver {
    event ValueReceived(address user, uint amount);

    receive() external payable {
        emit ValueReceived(msg.sender, msg.value);
    }
}

contract CharitySplitter {
    function donate(Charity charity) external payable {
        charity.processDonation{value:msg.value}(msg.sender);
    }
}

可见性和 getter 函数

由于 Solidity 有两种函数调用(内部调用不会产生实际的 EVM 调用或称为“消息调用”,而外部调用则会产生一个 EVM 调用), 函数和状态变量有四种可见性类型。 函数可以指定为 external ,public ,internal 或者 private。 对于状态变量,不能设置为 external ,默认是 internal 。

external

外部函数作为合约接口的一部分,意味着我们可以从其他合约和交易中调用。 一个外部函数 f 不能从内部调用(即 f 不起作用,但 this.f() 可以)。 当收到大量数据的时候,外部函数有时候会更有效率,因为数据不会从calldata复制到内存.

public

public 函数是合约接口的一部分,可以在内部或通过消息调用。对于 public 状态变量, 会自动生成一个 getter 函数(见下面)。

internal

这些函数和状态变量只能是内部访问(即从当前合约内部或从它派生的合约访问),不使用 this 调用。

private

private 函数和状态变量仅在当前定义它们的合约中使用,并且不能被派生合约使用。

Constant 和 Immutable 状态变量

状态变量声明为 constant (常量)或者 immutable (不可变量),在这两种情况下,合约一旦部署之后,变量将不在修改。

对于 constant 常量, 他的值在编译器确定,而对于 immutable, 它的值在部署时确定。

也可以在文件级别定义 constant 变量(注:0.7.2 之后加入的特性)。

编译器不会为这些变量预留存储位,它们的每次出现都会被替换为相应的常量表达式(它可能被优化器计算为实际的某个值)。

与常规状态变量相比,常量和不可变量的gas成本要低得多。 对于常量,赋值给它的表达式将复制到所有访问该常量的位置,并且每次都会对其进行重新求值。 这样可以进行本地优化。

不可变变量在构造时进行一次求值,并将其值复制到代码中访问它们的所有位置。 对于这些值,将保留32个字节,即使它们适合较少的字节也是如此。 因此,常量有时可能比不可变量更便宜。

状态可变性(mutability)

View 视图函数

可以将函数声明为 view 类型,这种情况下要保证不修改状态。
下面的语句被认为是修改状态:

  • 修改状态变量。
  • 产生事件。
  • 创建其它合约。
  • 使用 selfdestruct。
  • 通过调用发送以太币。
  • 调用任何没有标记为 view 或者 pure 的函数。
  • 使用低级调用。
  • 使用包含特定操作码的内联汇编。

Pure 纯函数

函数可以声明为 pure ,在这种情况下,承诺不读取也不修改状态。
除了上面解释的状态修改语句列表之外,以下被认为是读取状态:

  • 读取状态变量。
  • 访问 address(this).balance 或者 <address>.balance。
  • 访问 block,tx, msg 中任意成员 (除 msg.sig 和 msg.data 之外)。
  • 调用任何未标记为 pure 的函数。
  • 使用包含某些操作码的内联汇编。

payable

用payable声明的函数可以接受发送给合约的以太币,如果未指定,该函数将自动拒绝所有发送给它的以太币。

暂无评论

发送评论 编辑评论


				
上一篇
下一篇