suideveloper
All questions
Sui Move

How do I emit and read events in Sui Move?

November 5, 2026

Emit an event in Move by calling sui::event::emit with a struct that has copy and drop and is defined in the same module. Events become part of the transaction's result and are indexed by full nodes. Move code can never read them back, so you read them offchain: per transaction, or by querying and subscribing with gRPC or GraphQL. JSON-RPC's suix_queryEvents is no longer served on public mainnet full nodes.

Emitting an event

Move
module book::market;

use sui::coin::Coin;
use sui::event;
use sui::sui::SUI;

public struct Item has key, store { id: UID }

/// Event: past tense, one type per action.
public struct ItemPurchased has copy, drop {
    item: ID,
    price: u64,
}

public fun purchase(seller: address, coin: Coin<SUI>, ctx: &mut TxContext): Item {
    let item = Item { id: object::new(ctx) };
    event::emit(ItemPurchased { item: object::id(&item), price: coin.value() });
    transfer::public_transfer(coin, seller);
    item
}

Rules and behaviour:

  • The type needs copy + drop, and the Sui verifier requires it to be defined in the emitting module. You can't emit a primitive or another module's type, so an ItemPurchased event from your package can only come from your code.
  • Events are only recorded if the transaction succeeds. An abort after emit discards them.
  • Each event automatically carries the sender, the transaction digest, the checkpoint timestamp and its full type (package, module, name). You don't need a sender field.
  • Emitting costs no storage fees, but per-transaction limits apply: up to 1,024 events, each at most 250 KiB (from sui-protocol-config).
  • Generic events work, for example PoolCreated<phantom T>, and the type parameter becomes part of the event type you filter on.

The framework also has event::emit_authenticated, which writes the event to a per-package event stream that light clients can verify. Plain emit is what most apps use.

Reading events from a transaction

With @mysten/sui v2, ask for events when you execute:

TypeScript
import { SuiGrpcClient } from '@mysten/sui/grpc'

const client = new SuiGrpcClient({
  network: 'testnet',
  baseUrl: 'https://fullnode.testnet.sui.io:443',
})

const result = await client.signAndExecuteTransaction({
  signer,
  transaction: tx,
  include: { events: true },
})

if (result.$kind === 'Transaction') {
  for (const ev of result.Transaction.events) {
    console.log(ev.eventType, ev.json)
  }
}

Each event has packageId, module, sender, eventType, bcs and json. The SDK notes the json shape can differ between transports; decode bcs if you need it exact.

Querying past events

listEvents works on the gRPC and GraphQL clients and filters by exactly one of eventType, emitModule or sender:

TypeScript
const page = await client.listEvents({
  filter: { eventType: `${PACKAGE_ID}::market::ItemPurchased` },
  limit: 50,
})

for (const ev of page.events) {
  console.log(ev.transactionDigest, ev.checkpoint, ev.json)
}
// Next page: listEvents({ filter, after: page.endCursor })

A generic event name without type arguments (0xpkg::mod::Event) matches every instantiation on gRPC and GraphQL. Use the original package ID in the type: event types keep the ID of the package version that first defined them, even after upgrades.

Live updates and indexing

For real-time feeds, the Sui docs point to the gRPC SubscriptionService.SubscribeEvents stream, which starts at the chain tip. To combine history and live data, open the subscription first, backfill with ListEvents up to its first watermark, then consume the stream. GraphQL's events query is the other option. For heavy analytics, build a custom indexer instead of polling.

Testing events

sui::event has test-only helpers:

Move
#[test]
fun emits_purchase() {
    event::emit(ItemPurchased { item: object::id_from_address(@0x1), price: 100 });
    assert!(event::num_events() == 1);
    let evs = event::events_by_type<ItemPurchased>();
    assert!(evs[0].price == 100);
}

See unit tests in Sui Move. You can also emit test events from the playground on testnet.

Sources

More on sui move