继承和事件是Solidity中实现代码复用和链下通知的重要特性。本文详细介绍Solidity的继承机制、事件系统,以及如何在实际开发中使用这些特性构建可维护的智能合约。

Inheritance and Events


一、什么是继承

一、1 基本概念

继承是Solidity中实现代码复用和模块化的重要机制,允许一个合约继承另一个合约的功能。Solidity支持多重继承,子合约可以继承多个父合约。

二、2 继承的特点

1
代码复用:
  • 继承父合约的功能
  • 减少重复代码
  • 提高可维护性
  • 模块化设计
1
多重继承:
  • 可以继承多个合约
  • 使用C3线性化
  • 解决钻石问题
  • 灵活组合功能
1
函数覆盖:
  • 可以覆盖父合约函数
  • 使用virtual和override
  • 保持接口一致
  • 扩展功能

二、如何实现继承

三、1 单继承

1
基础继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
pragma solidity ^0.8.0;

// 可拥有合约:管理合约所有者
contract Ownable {
address public owner;

// 构造函数:设置部署者为所有者
constructor() {
owner = msg.sender;
}

// 只有所有者可以调用的修改器
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}

// 转移所有权
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid address");
owner = newOwner;
}

// 继承Ownable合约
contract MyContract is Ownable {
uint256 public value;

// 只有所有者可以设置值
function setValue(uint256 _value) public onlyOwner {
value = _value;
}

四、2 多重继承

1
2
3
4
多个父合约:
// 可暂停合约
contract Pausable {
bool public paused;
1
2
3
// 未暂停时才能调用
modifier whenNotPaused() {
require(!paused, "Paused");
    _;
}
1
2
3
// 暂停合约
function pause() public {
paused = true;
}
1
2
3
// 恢复合约
function unpause() public {
paused = false;
}
1
// 可拥有合约
    _;
}

}
1
2
3
4
5
// 同时继承Ownable和Pausable
contract MyContract is Ownable, Pausable {
// 需要owner且未暂停才能调用
function sensitiveFunction() public onlyOwner whenNotPaused {
// 敏感操作
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

### 五、3 函数覆盖

virtual和override:
contract Base {
// 声明为virtual,允许子合约覆盖
function foo() public virtual returns (string memory) {
return "Base";
}

contract Derived is Base {
// 使用override覆盖父合约函数
function foo() public pure override returns (string memory) {
return "Derived";
}
`多重覆盖:`solidity
contract A {
function test() public virtual returns (string memory) {
return "A";
}

contract B {
return "B";
}

contract C is A, B {
// 明确指定覆盖哪些父合约的函数
function test() public pure override(A, B) returns (string memory) {
return "C";
}

三、什么是事件

六、1 基本概念

事件(Event)是Solidity中用于记录日志和通知链下应用的机制。事件数据存储在交易日志中,可以被前端应用监听和处理。

七、2 事件的特点

1
链下通知:
  • 前端可以监听
  • 实时获取状态变化
  • 无需轮询查询
  • 提高用户体验
1
Gas效率:
  • 比存储更便宜
  • 适合记录历史
  • 不占用存储槽
  • 优化Gas消耗
1
不可篡改:
  • 记录在区块链
  • 永久保存
  • 可验证
  • 审计追踪

四、如何定义和使用事件

八、1 事件定义

1
简单事件:
1
2
3
4
5
contract EventExample {
// 值改变事件
event ValueChanged(uint256 oldValue, uint256 newValue);
// 转账事件:indexed参数可以用于过滤
event Transfer(address indexed from, address indexed to, uint256 value);
1
2
3
4
5
// 设置值并触发事件
function setValue(uint256 _value) public {
uint256 oldValue = value;
// 触发事件
emit ValueChanged(oldValue, _value);
}

索引参数:solidity

1
event Transfer(
address indexed from,    // indexed:可以用于过滤
address indexed to,      // indexed:可以用于过滤
uint256 value            // 非indexed:完整数据

);

1
2
// indexed参数最多3个
// indexed参数可以用于前端过滤查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

### 九、2 事件使用

触发事件:
contract Token {
// 转账事件
// 授权事件
event Approval(address indexed owner, address indexed spender, uint256 value);

mapping(address => uint256) public balances;

// 转账函数
function transfer(address to, uint256 amount) public returns (bool) {
balances[msg.sender] -= amount;
balances[to] += amount;
// 触发转账事件
emit Transfer(msg.sender, to, amount);
return true;
}

// 授权函数
function approve(address spender, uint256 amount) public returns (bool) {
// 触发授权事件
emit Approval(msg.sender, spender, amount);
}

十、3 监听事件

1
使用Web3.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 监听转账事件
contract.events.Transfer({
filter: { from: userAddress }, // 过滤条件
fromBlock: 0 // 从区块0开始
}, function(error, event) {
console.log('Transfer:', event.returnValues);
})
.on('data', function(event) {
console.log('From:', event.returnValues.from);
console.log('To:', event.returnValues.to);
console.log('Value:', event.returnValues.value);
});
`使用ethers.js:```javascript
// 监听所有转账事件
contract.on("Transfer", (from, to, value, event) => {
console.log(`Transfer: ${from} -> ${to}, ${value}`);
});

// 过滤事件:只监听从特定地址发出的转账
const filter = contract.filters.Transfer(userAddress, null);
contract.on(filter, (from, to, value) => {
console.log(`Received from ${from}: ${value}`);
});

五、应用场景

十一、1 标准接口

1
2
3
4
ERC20事件:
contract ERC20 {
// 标准转账事件
// 标准授权事件
1
// 转账逻辑
}

ERC721事件:solidity

1
2
3
4
5
contract ERC721 {
// NFT转账事件:tokenId也是indexed
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
// NFT授权事件
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
1
2
function transferFrom(address from, address to, uint256 tokenId) public {
emit Transfer(from, to, tokenId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

### 十二、2 状态追踪

订单状态:
contract OrderSystem {
enum OrderStatus { Created, Paid, Shipped, Delivered }

// 订单状态改变事件
event OrderStatusChanged(
uint256 indexed orderId,
OrderStatus oldStatus,
OrderStatus newStatus
);

mapping(uint256 => OrderStatus) public orders;

// 更新订单状态
function updateStatus(uint256 orderId, OrderStatus newStatus) public {
OrderStatus oldStatus = orders[orderId];
orders[orderId] = newStatus;
// 触发状态改变事件
emit OrderStatusChanged(orderId, oldStatus, newStatus);
}

十三、3 审计日志

1
2
3
4
操作记录:
contract Auditable {
// 管理员操作事件
event AdminAction(
    address indexed admin,
    string action,
    uint256 timestamp
);
1
2
// 用户操作事件
event UserAction(
    address indexed user,
    bytes data
);
1
2
3
4
5
// 管理员函数
function adminFunction() public {
// 记录管理员操作
emit AdminAction(msg.sender, "adminFunction", block.timestamp);
// 执行操作
}
1
2
3
4
// 用户函数
function userFunction(bytes memory data) public {
// 记录用户操作
emit UserAction(msg.sender, "userFunction", data);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

## 六、继承最佳实践

### 十四、1 设计模式

可拥有合约:
// 所有权转移事件
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

emit OwnershipTransferred(address(0), owner);
}

_;
}

emit OwnershipTransferred(owner, newOwner);
}
`可暂停合约:`solidity
contract Pausable is Ownable {
// 暂停事件
event Paused(address account);
// 恢复事件
event Unpaused(address account);


_;
}

// 暂停合约:只有owner且未暂停时才能调用
function pause() public onlyOwner whenNotPaused {
emit Paused(msg.sender);
}

// 恢复合约:只有owner且已暂停时才能调用
function unpause() public onlyOwner {
require(paused, "Not paused");
emit Unpaused(msg.sender);
}

十五、2 库的使用

1
2
3
4
5
6
7
可重用库:
library SafeMath {
// 安全加法:检查溢出
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
1
2
3
contract MyContract {
// 为uint256类型使用SafeMath库
using SafeMath for uint256;
1
2
3
function calculate(uint256 a, uint256 b) public pure returns (uint256) {
// 使用库函数进行安全加法
return a.add(b);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

### 十六、3 接口定义

标准接口:
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}

contract MyToken is IERC20 {
// 实现接口中的transfer函数
function transfer(address to, uint256 amount) external override returns (bool) {
// 实现逻辑
}

// 实现接口中的balanceOf函数
function balanceOf(address account) external view override returns (uint256) {
return 0;
}

七、事件最佳实践

十七、1 事件设计

1
2
3
完整信息:
// 订单创建事件:包含所有重要信息
event OrderCreated(
uint256 indexed orderId,        // 订单ID:可过滤
address indexed buyer,          // 买家:可过滤
address indexed seller,         // 卖家:可过滤
uint256 amount,                 // 金额:完整数据
uint256 timestamp               // 时间戳:完整数据

);

1
2
// 包含所有重要信息,便于链下处理
// 最多3个indexed参数
address indexed from,    // 可以过滤发送者
address indexed to,      // 可以过滤接收者
uint256 value            // 非索引,完整数据

);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

### 十八、2 Gas优化

替代存储:
// 不推荐:存储历史数据(消耗Gas)
mapping(uint256 => uint256) public history;

// 推荐:使用事件记录历史(更省Gas)
event ValueChanged(uint256 indexed index, uint256 oldValue, uint256 newValue);

function setValue(uint256 index, uint256 value) public {
uint256 oldValue = values[index];
values[index] = value;
// 使用事件记录,比存储更便宜
emit ValueChanged(index, oldValue, value);
}

八、常见问题

十九、1 继承顺序

1
问题:
  • 多重继承的顺序
  • C3线性化规则
1
解决:
  • 理解继承顺序
  • 测试验证
  • 使用override明确
  • 查看编译器警告

二十、2 事件过滤

  • 如何高效过滤事件

  • 索引参数限制

  • 使用indexed参数

  • 合理设计事件结构

  • 链下索引服务

  • 考虑Gas成本

九、总结

继承和事件是Solidity中实现代码复用和链下通信的重要机制。关键要点:

1
继承机制:
  • 代码复用
  • 多重继承
  • 函数覆盖
1
事件系统:
  • 链下通知
  • Gas高效
  • 不可篡改
1
最佳实践:
  • 合理设计继承结构
  • 使用标准模式
  • 优化事件设计
  • 提高代码质量

通过合理使用继承和事件,可以构建更模块化、更易维护、更高效的智能合约系统。

本文标题: Solidity继承和事件

发布时间: 2024年11月25日 00:00

最后更新: 2025年12月30日 08:54

原始链接: https://haoxiang.eu.org/eb8ae9b2/

版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!

× 喜欢就赞赏一下呗!
打赏二维码