Skip to content

Events

Events are emitted while a transaction executes. Each Move module defines its own event types and chooses when to emit them.

Aptos Move supports two forms of events:

  • Module events — the current mechanism, shipped in framework release 1.7. A global stream keyed by struct type.
  • EventHandle events — the original Diem-style streams, stored inside a resource. They are deprecated. Because historical ledger data cannot be rewritten, they will remain queryable.

Module events are global streams identified by a struct type. Mark a normal Move struct that has drop and store with #[event]:

/// 0xcafe::my_module_name
/// An example module event struct denoting a coin transfer.
#[event]
struct TransferEvent has drop, store {
sender: address,
receiver: address,
amount: u64
}

Create and emit the event:

let event = TransferEvent {
sender: @0xcafe,
receiver: @0xface,
amount: 100
};
0x1::event::emit(event);

Example module events appear on the Events tab of any user transaction in Explorer. The screenshot below shows three module events of type 0x66c34778730acbb120cefa57a3d98fd21e0c8b3a51e9baee530088b2e444e94c::event::MyEvent. For REST compatibility, module events still include Account Address, Creation Number, and Sequence Number, all set to 0.

Module event example

Events live in a per-transaction event accumulator (a Merkle tree). That tree is independent of the state tree, so the MoveVM cannot read events during production execution. Tests can inspect them with two helpers in 0x1::event:

/// Return all emitted module events with type T as a vector.
#[test_only]
public native fun emitted_events<T: drop + store>(): vector<T>;
/// Return true iff `msg` was emitted.
#[test_only]
public fun was_event_emitted<T: drop + store>(msg: &T): bool {
emitted_events<T>().contains(msg)
}

Query module events and remaining EventHandle events through:

Aptos inherited Libra/Diem event streams keyed by an EventHandle: a globally unique GUID plus a per-handle counter, stored inside a resource. Each event in that stream has a sequence number from the handle.

Legacy Coin transfers emitted withdraw and deposit events on sender and receiver CoinStore resources. After the Fungible Asset migration, new accounts typically do not have CoinStore<0x1::aptos_coin::AptosCoin>, so that handle path 404s. Query remaining EventHandle streams with Get events by event handle. The REST shape looks like:

GET /v1/accounts/{address}/events/0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>/withdraw_events

[
{
"key": "0x0000000000000000caa60eb4a01756955ab9b2d1caca52ed",
"sequence_number": "0",
"type": "0x1::coin::WithdrawEvent",
"data": {
"amount": "1000"
}
}
]

Each registered EventHandle has a unique key. That key can be queried directly with GET /v1/events/{key}.

Each entry has a sequence_number starting at 0, a type, and data. Several events can share similar types, especially with generics. Include in data whatever a client needs to understand the resource before and after the emitting transaction.

EventHandle events are deprecated. Emit a module event wherever you currently emit an EventHandle event. After indexers and clients consume the module events, you can stop emitting the legacy ones.

EventHandle streams cannot be deleted. Contracts that cannot upgrade can keep using them.