// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; contract Coin { // 关键字“public”让这些变量可以从外部读取 address public minter; mapping (address => uint) public balances;复制
//外部读取方法 // 轻客户端可以通过事件针对变化作出高效的反应 event Sent(address from, address to, uint amount); // 这是构造函数,只有当合约创建时运行 constructor() { minter = msg.sender; } function mint(address receiver, uint amount) public { require(msg.sender == minter); balances[receiver] += amount; } // Errors allow you to provide information about // why an operation failed. They are returned // to the caller of the function. error InsufficientBalance(uint requested, uint available); function send(address receiver, uint amount) public { if (amount > balances[msg.sender]) revert InsufficientBalance({ requested: amount, available: balances[msg.sender] }); balances[msg.sender] -= amount; balances[receiver] += amount; emit Sent(msg.sender, receiver, amount); } }复制
address public minter;
这一行声明了一个可以被公开访问的 address
类型的状态变量。 address
类型是一个160位的值,且不允许任何算数操作。这种类型适合存储合约地址或外部人员的密钥对。关键字 public
自动生成一个函数,允许你在这个合约之外访问这个状态变量的当前值。如果没有这个关键字,其他的合约没有办法访问这个变量。由编译器生成的函数的代码大致如下所示(暂时忽略 external 和 view):
function minter() external view returns (address) { return minter; }复制
当然,加一个和上面完全一样的函数是行不通的,因为我们会有同名的一个函数和一个变量,这里,主要是希望你能明白——编译器已经帮你实现了。
下一行, mapping (address => uint) public balances;
也创建一个公共状态变量,但它是一个更复杂的数据类型。 该类型将address映射为无符号整数。 Mappings 可以看作是一个 哈希表 它会执行虚拟初始化,以使所有可能存在的键都映射到一个字节表示为全零的值。 但是,这种类比并不太恰当,因为它既不能获得映射的所有键的列表,也不能获得所有值的列表。 因此,要么记住你添加到mapping中的数据(使用列表或更高级的数据类型会更好),要么在不需要键列表或值列表的上下文中使用它,就如本例。 而由 public
关键字创建的getter函数 getter function 则是更复杂一些的情况, 它大致如下所示:
function balances(address account) external view returns (uint) { return balances[account]; }复制
正如你所看到的,你可以通过该函数轻松地查询到账户的余额。
event Sent(address from, address to, uint amount);
这行声明了一个所谓的“事件(event)”,它会在 send
函数的最后一行被发出。用户界面(当然也包括服务器应用程序)可以监听区块链上正在发送的事件,而不会花费太多成本。一旦它被发出,监听该事件的listener都将收到通知。而所有的事件都包含了 from
, to
和 amount
三个参数,可方便追踪交易。 为了监听这个事件,你可以使用如下JavaScript代码(假设 Coin 是已经通过 web3.js 创建好的合约对象 ):