事件
事件是定制的数据结构,由智能合约在执行期间发出。 它们为智能合约提供了一种与外部世界沟通的方式,即记录合约中所发生的特定事情的信息。
事件在智能合约的创建中起着至关重要的作用。以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::Event
trait。
此外,这些变体的成员必须实现Serde
trait(c.f. Appendix C: Serializing with Serde),因为键/数据是通过序列化过程添加到事件中的。
starknet::Event
trait的自动实现将为我们的 Event
枚举的每个变体成员实现 append_keys_and_data
函数。生成的实现将根据变量名(StoredName
)附加一个单独的键,然后递归调用Event
trait的实现中的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 });