继承和事件是Solidity中实现代码复用和链下通知的重要特性。本文详细介绍Solidity的继承机制、事件系统,以及如何在实际开发中使用这些特性构建可维护的智能合约。
一、什么是继承 一、1 基本概念 继承是Solidity中实现代码复用和模块化的重要机制,允许一个合约继承另一个合约的功能。Solidity支持多重继承,子合约可以继承多个父合约。
二、2 继承的特点 继承父合约的功能 减少重复代码 提高可维护性 模块化设计 可以继承多个合约 使用C3线性化 解决钻石问题 灵活组合功能 可以覆盖父合约函数 使用virtual和override 保持接口一致 扩展功能 二、如何实现继承 三、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 2 3 4 5 contract MyContract is Ownable, Pausable { 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 事件的特点 前端可以监听 实时获取状态变化 无需轮询查询 提高用户体验 比存储更便宜 适合记录历史 不占用存储槽 优化Gas消耗 四、如何定义和使用事件 八、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
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 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 }, 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:` `` javascriptcontract.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 { // 标准转账事件 // 标准授权事件
}
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 继承顺序 理解继承顺序 测试验证 使用override明确 查看编译器警告 二十、2 事件过滤 如何高效过滤事件
索引参数限制
使用indexed参数
合理设计事件结构
链下索引服务
考虑Gas成本
九、总结 继承和事件是Solidity中实现代码复用和链下通信的重要机制。关键要点:
合理设计继承结构 使用标准模式 优化事件设计 提高代码质量 通过合理使用继承和事件,可以构建更模块化、更易维护、更高效的智能合约系统。
本文标题: Solidity继承和事件
发布时间: 2024年11月25日 00:00
最后更新: 2025年12月30日 08:54
原始链接: https://haoxiang.eu.org/eb8ae9b2/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0 许可协议,转载请注明出处!