Solidity 合约组成结构
在 Web3 世界里游荡,你应该已经与非常多的合约交互过了。你可能会好奇合约都由什么基本结构组成的呢? 我们在编写合约的时候又是如何把这些基本结构结合在一起的呢? 这一节我们概略性地介绍一下合约的七大组成结构,和它们互相间的关系。
合约的七大组成结构有:
一个典型的合约
为了方便介绍每个组成结构都是什么, 长什么样的, 我们先看一个例子. 下面是一个Owner合约, 它的唯一功能就是记录谁是这个合约 Owner。你可以获取,改变或者移除 Owner。
Owner合约
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.17;
contract Owner {
// 结构体
struct Identity {
address addr;
string name;
}
// 枚举
enum State {
HasOwner,
NoOwner
}
// 事件
event OwnerSet(address indexed oldOwnerAddr, address indexed newOwnerAddr);
event OwnerRemoved(address indexed oldOwnerAddr);
// 函数修饰器
modifier isOwner() {
require(msg.sender == owner.addr, "Caller is not owner");
_;
}
// 状态变量
Identity private owner;
State private state;
// 下面的都是函数
// 构造函数
constructor(string memory name) {
owner.addr = msg.sender;
owner.name = name;
state = State.HasOwner;
emit OwnerSet(address(0), owner.addr);
}
// 普通函数
function changeOwner(address addr, string calldata name) public isOwner {
owner.addr = msg.sender;
owner.name = name;
emit OwnerSet(owner.addr, addr);
}
// 普通函数
function removeOwner() public isOwner {
emit OwnerRemoved(owner.addr);
delete owner;
state = State.NoOwner;
}
// 普通函数
function getOwner() external view returns (address, string memory) {
return (owner.addr, owner.name);
}
// 普通函数
function getState() external view returns (State) {
return state;
}
}
上面的 Solidity 合约实现了一个简单的 owner
管理功能,可以用来设置、管理和删除合约的 owner
。它定义了一系列结构体、枚举、事件、函数修饰器、状态变量和函数。我们逐个拆解来看看每一个组成结构。
结构体
结构体是一种数据类型,用于表示多个字段的集合。结构体可以被用来定义复杂的数据类型。
struct Identity { // 结构体
address addr;
string name;
}
Identity
:它是一个结构体,包含了owner
的地址和姓名两个字段。
枚举
枚举是一种数据类型,用于定义一组名称和整数值之间的对应关系。枚举可以让代码更具可读性和可维护性。
enum State { // 枚举
HasOwner,
NoOwner
}
State
:它是一个枚举,定义了两个状态HasOwner
和NoOwner
。
事件
事件是一种特殊的函数,可以用来记录合约执行过程中发生的重要事件。可以认为事件就是 Solidity 的 log。你可以通过已连接的客户端来访问这些事件。
// 事件
event OwnerSet(address indexed oldOwnerAddr, address indexed newOwnerAddr);
event OwnerRemoved(address indexed oldOwnerAddr);
OwnerSet
:当owner
被设置成新owner时触发OwnerRemoved
:当owner
被删除时触发
函数修饰器
函数修饰器可以被用来修饰函数的行为。
modifier isOwner() { // 函数修饰器
require(msg.sender == owner.addr, "Caller is not owner");
_;
}
isOwner
:它是一个函数修饰器,只允许合约的owner
调用被它修饰的函数。
状态变量
状态变量是用于存储合约状态的变量。状态变量会被永久保存在区块链上,并且可以在合约执行期间被读写。
Identity private owner; // 状态变量
State private state; // 状态变量
owner
:它是一个Identity
类型的变量,表示合约的 ownerstate
:它是一个State
类型的变量,表示合约的当前状态
函数
函数是一组逻辑块,定义了合约可以执行的操作。它可以接受参数,并根据参数计算返回结果。函数可以被其他合约调用。
// 构造函数
constructor(string memory name) {
owner.addr = msg.sender;
owner.name = name;
state = State.HasOwner;
emit OwnerSet(address(0), owner.addr);
}
// 普通函数
function changeOwner(address addr, string calldata name) public isOwner {
owner.addr = msg.sender;
owner.name = name;
emit OwnerSet(owner.addr, addr);
}
// 普通函数
function removeOwner() public isOwner {
emit OwnerRemoved(owner.addr);
delete owner;
state = State.NoOwner;
}
// 普通函数
function getOwner() external view returns (address, string memory) {
return (owner.addr, owner.name);
}
// 普通函数
function getState() external view returns (State) {
return state;
}
合约一共定义了5个函数,它们分别为:
constructor(string memory name)
:合约构造函数,在合约部署时自动执行。它将当前调用者设置为owner
,并设置合约的状态为HasOwner
changeOwner(address addr, string calldata name)
:修改owner
removeOwner()
:删除owner
getOwner()
:返回owner
的地址和名称getState()
:返回合约的状态
函数主要的操作包括:
- 在合约部署时,调用构造函数
constructor(string memory name)
设置owner
。 - 调用函数
changeOwner(address addr, string calldata name)
修改owner
。 - 调用函数
removeOwner()
删除owner
。 - 所有重要的操作都需要使用函数修饰器
isOwner
进行保护,只有owner
才能执行。 - 这个合约还提供了一些辅助函数,如
getOwner
和getState
,用来查询owner
的信息和合约的状态 - 此外,这个合约还定义了两个事件:
OwnerSet
和OwnerRemoved
,分别在owner
被设置或删除时触发。这些事件可以被外部监听,从而做出相应的反应。
小结
- 状态变量: 储存在合约中的变量。在本例中,定义了私有状态变量
owner
和state
。 - 函数: 合约中定义的可执行代码块。在本例中,有构造函数
constructor
和普通函数changeOwner
、removeOwner
、getOwner
和getState
。 - 函数修饰器: 在函数定义之前使用的一个声明,可以对函数的行为进行修饰。在本例中,使用了
isOwner
修饰器。 - 事件: 合约中定义的日志记录,可以通过事件来跟踪合约的执行。在本例中,定义了事件
OwnerSet
和OwnerRemoved
。 - Error: 合约中定义的错误信息。
- 结构体: 合约中定义的自定义数据类型。在本例中,定义了结构体
Identity
。 - 枚举: 合约中定义的自定义常量类型。在本例中,定义了枚举
State
。