Skip to content

Latest commit

 

History

History

16_Events

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
title tags
16. 事件
cairo
starknet
event
emit

WTF Cairo极简教程: 16. 事件

我最近在学cairo-lang,巩固一下细节,也写一个WTF Cairo极简教程,供小白们使用。教程基于cairo 2.2.0版本。

推特:@0xAA_Science@WTFAcademy_

WTF Academy 社群:Discord微信群官网 wtf.academy

所有代码和教程开源在 github: github.com/WTFAcademy/WTF-Cairo


在本章中,我们将探索 Cairo 中的事件(events)。在释放时,事件会将传递给它们的参数存储在 Starknet 交易日志中。

事件

类似于 Solidity,Cairo 中的事件是存储在 Starknet 上的交易日志。事件在函数调用时被释放,并可以被外部的链下应用访问。

以下是Starknet 的事件例子。这是ERC20合约中的一个 Transfer 事件,它包含三个参数:'from','to',和 'value'。

事件具有以下几个特点:

  1. 将数据存储在事件中比存储在存储变量中更具成本效益。
  2. 事件不能直接从合约内部读取。
  3. 诸如 starknet.js 的应用程序可以通过 RPC 接口订阅这些事件,并在前端触发响应。

为了更好地说明 Cairo 中的事件,我们扩展了上一章中的 Owner 合约示例。具体来说,我们添加了一个 ChangeOwner 事件,每次更改所有者时都会释放这个事件。

#[starknet::contract]
mod owner_event{
    // 导入与合约地址相关的库
    use starknet::ContractAddress;
    use starknet::get_caller_address;

    /// 当所有者更改时发出的事件
    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        ChangeOwner: ChangeOwner,
    }

    #[derive(Drop, starknet::Event)]
    struct ChangeOwner {
        #[key]
        old_owner: ContractAddress, // 旧的所有者地址
        new_owner: ContractAddress  // 新的所有者地址
    }

    // 存储变量
    #[storage]
    struct Storage{
        owner: ContractAddress,  
        balance: felt252,
    }

    // 在部署期间设置所有者地址
    #[constructor]
    fn constructor(ref self: ContractState, balance_: felt252) {
        self.owner.write(get_caller_address());
        self.balance.write(balance_);
    }

    // 读取所有者地址
    #[external(v0)]
    fn read_owner(self: @ContractState) -> ContractAddress{
        self.owner.read()
    }

    #[external(v0)]
    fn balance_read(self: @ContractState) -> felt252 {
        self.balance.read()
    }

    // 更改所有者地址并发出ChangeOwner事件
    // 任何人都可以调用
    #[external(v0)]
    fn change_owner(ref self: ContractState, new_owner: ContractAddress){
        let old_owner = self.owner.read();
        self.owner.write(new_owner);
        // 通过调用事件函数发出事件
        self.emit(ChangeOwner {old_owner: old_owner, new_owner: new_owner});
    }
}

定义事件

在 Cairo 中,所有事件都必须在Event枚举中定义。该枚举必须使用#[event]#[derive(Drop, starknet::Event)]属性,每个事件变体成员必须是一个与变体同名的结构体。然后,你需要定义事件结构体,它也需要使用#[derive(Drop, starknet::Event)]属性,并将你想要记录的参数添加为成员。在下面的例子中,我们定义了一个 ChangeOwner 事件,它有两个参数:旧所有者和新所有者的地址。

/// 当所有者更改时发出的事件
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
    ChangeOwner: ChangeOwner,
}

#[derive(Drop, starknet::Event)]
struct ChangeOwner {
    #[key]
    old_owner: ContractAddress, // 旧的所有者地址
    new_owner: ContractAddress  // 新的所有者地址
}

释放事件

要释放事件,你需要使用self.emit()方法,并把要记录的数据作为参数。在下面的例子中,ChangeOwner 事件在 change_owner() 函数中的 owner 更改后释放。

// 更改所有者地址并发出ChangeOwner事件
// 任何人都可以调用
#[external(v0)]
fn change_owner(ref self: ContractState, new_owner: ContractAddress){
    let old_owner = self.owner.read();
    self.owner.write(new_owner);
    // 通过调用事件函数发出事件
    self.emit(ChangeOwner {old_owner: old_owner, new_owner: new_owner});
}

读取释放的事件

可以使用 Starknet.js 库读取释放的事件,这是一个用于 Starknet 的 JavaScript 库,类似于以太坊的 Ethers.js 。有关更多信息,请参阅 Starknet.js 文档

总结

在本章中,我们介绍了 Cairo 中的事件,它们提供了一种高效的存储数据和跟踪合约中状态变化的方法。它们可以用于开发响应式的去中心化应用。