事件(Events)

事件(event)是用来追踪链上行为的主要方式。

English Version

Events are the main way to track actions on chain.

/// 在共享对象示例的基础上加入事件(Event)
/// Extended example of a shared object. Now with addition of events!
module examples::donuts_with_events {
    use sui::transfer;
    use sui::sui::SUI;
    use sui::coin::{Self, Coin};
    use sui::object::{Self, ID, UID};
    use sui::balance::{Self, Balance};
    use sui::tx_context::{Self, TxContext};

    // 使用事件是只需要添加`sui::event`依赖
    // This is the only dependency you need for events.
    use sui::event;

    /// 错误码 0:对应所付费用低于甜甜圈价格   
    /// For when Coin balance is too low.
    const ENotEnough: u64 = 0;

    /// 商店所有者权限凭证:获取利润
    /// Capability that grants an owner the right to collect profits.
    struct ShopOwnerCap has key { id: UID }

    /// 一个可被购买的甜甜圈对象
    /// A purchasable Donut. For simplicity's sake we ignore implementation.
    struct Donut has key { id: UID }

    struct DonutShop has key {
        id: UID,
        price: u64,
        balance: Balance<SUI>
    }

    // ====== Events ======

    /// 当用户购买甜甜圈时触发本事件
    /// For when someone has purchased a donut.
    struct DonutBought has copy, drop {
        id: ID
    }

    /// 当店主提取利润时触发本事件
    /// For when DonutShop owner has collected profits.
    struct ProfitsCollected has copy, drop {
        amount: u64
    }

    // ====== Functions ======

    fun init(ctx: &mut TxContext) {
        transfer::transfer(ShopOwnerCap {
            id: object::new(ctx)
        }, tx_context::sender(ctx));

        transfer::share_object(DonutShop {
            id: object::new(ctx),
            price: 1000,
            balance: balance::zero()
        })
    }

    /// 购买一个甜甜圈
    /// Buy a donut.
    public entry fun buy_donut(
        shop: &mut DonutShop, payment: &mut Coin<SUI>, ctx: &mut TxContext
    ) {
        assert!(coin::value(payment) >= shop.price, ENotEnough);

        let coin_balance = coin::balance_mut(payment);
        let paid = balance::split(coin_balance, shop.price);
        let id = object::new(ctx);

        balance::join(&mut shop.balance, paid);

        // 生成包含新甜甜圈ID的`DonutBought`事件
        // Emit the event using future object's ID.
        event::emit(DonutBought { id: object::uid_to_inner(&id) });
        transfer::transfer(Donut { id }, tx_context::sender(ctx))
    }

    /// 吃掉甜甜圈 :)
    /// Consume donut and get nothing...
    public entry fun eat_donut(d: Donut) {
        let Donut { id } = d;
        object::delete(id);
    }

    /// 收集利润,需要`ShopOwnerCap`凭证
    /// Take coin from `DonutShop` and transfer it to tx sender.
    /// Requires authorization with `ShopOwnerCap`.
    public entry fun collect_profits(
        _: &ShopOwnerCap, shop: &mut DonutShop, ctx: &mut TxContext
    ) {
        let amount = balance::value(&shop.balance);
        let profits = coin::take(&mut shop.balance, amount, ctx);

        // 生成包含转账金额的`ProfitsCollected`事件
        // simply create new type instance and emit it
        event::emit(ProfitsCollected { amount });

        transfer::public_transfer(profits, tx_context::sender(ctx))
    }
}