事件

事件是定制的数据结构,由智能合约在执行期间发出。 它们为智能合约提供了一种与外部世界沟通的方式,即记录合约中所发生的特定事情的信息。

事件在智能合约的创建中起着至关重要的作用。以Starknet上铸造的Non-Fungible Tokens(NFTs)为例。所有这些都被索引并存储在数据库中,然后通过使用这些事件显示给用户。忘记在你的NFT合约中编写一个事件可能会导致糟糕的用户体验。这是因为用户可能看不到他们的NFT出现在他们的钱包里(钱包使用这些索引器来显示用户的NFT)。

界定事件

合约中所有不同的事件都定义在 Event枚举中,它实现了starknet::Event trait,作为枚举变量。该trait在核心库中定义如下:

trait Event<T> {
    fn append_keys_and_data(self: T, ref keys: Array<felt252>, ref data: Array<felt252>);
    fn deserialize(ref keys: Span<felt252>, ref data: Span<felt252>) -> Option<T>;
}

属性 #[derive(starknet::Event)]会使得编译器为上述trait生成一个实现、 在我们的例子中,它是下面的枚举:

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        StoredName: StoredName,
    }

    #[derive(Drop, starknet::Event)]
    struct StoredName {
        #[key]
        user: ContractAddress,
        name: felt252
    }

每个事件变体成员必须是一个与变体同名的结构体,并且每个变体本身需要实现starknet::Eventtrait。 此外,这些变体的成员必须实现Serdetrait(c.f. Appendix C: Serializing with Serde),因为键/数据是通过序列化过程添加到事件中的。

starknet::Eventtrait的自动实现将为我们的 Event枚举的每个变体成员实现 append_keys_and_data函数。生成的实现将根据变量名(StoredName)附加一个单独的键,然后递归调用Eventtrait的实现中的append_keys_and_data函数。

在我们的合约中,我们定义了一个名为 StoredName 的事件,该事件将调用者的合约地址和存储在合约中的名称发送出去,其中 user 字段序列化为键,而 name 字段序列化为数据。 要索引一个事件的键,只需用#[key]注释它,如user键的示例所示。

当使用 self.emit(StoredName { user: user, name: name }) 发布事件时,与名称 StoredName 相对应的键,特别是 sn_keccak(StoredName) 会被添加到键列表中。由于使用了 #[key]属性,user 被序列化为 key,而地址被序列化为数据。一切处理完毕后,我们将得到以下键和数据:keys = [sn_keccak("StoredName"),user]data = [address]

发射事件

定义事件后,我们可以使用self.emit来emit它们,语法如下:

            self.emit(StoredName { user: user, name: name });
Last change: 2023-09-06, commit: 5bf14bf