Skip to main content

Using Events

Move code can interact with objects stored on Sui. Some applications might want to track an object's activity and trigger certain workflows within the application based on that activity, such as application updates based on how many times an NFT is minted or the amount of SUI a smart contract generates.

Move supports activity monitoring through emitting events. Emitting events consists of 3 steps:

  1. Defining an event struct in your Move module that captures the data you want to track.

  2. Using the emit function to emit events when relevant actions occur.

  3. Processing events using either a custom indexer or by polling the network.

Defining event struct

Event structs have the copy and drop abilities. In the event, define the fields you need to capture relevant data about the action.

public struct TestEvent has copy, drop {
message: ascii::String,
value: u64,
}

Using the emit function

Use the sui::event::emit function to emit an event when the action you want to monitor occurs:

public struct TestEvent has copy, drop {
message: ascii::String,
value: u64,
}

Processing events

After your Move code emits events, you need to process them. There are 2 approaches:

  • Custom indexer: Stream checkpoints and filter events continuously for real-time processing.

  • Polling: Query the Sui network periodically for emitted events. This approach requires a database to store retrieved data.

Event object structure

When you process events, each event object contains the following attributes:

  • id: JSON object containing the transaction digest ID and event sequence.

  • packageId: The object ID of the package that emits the event.

  • transactionModule: The module that performs the transaction.

  • sender: The Sui network address that triggered the event.

  • type: The type of event being emitted.

  • parsedJson: JSON object describing the event.

  • bcs: Binary canonical serialization value.

  • timestampMs: Unix epoch timestamp in milliseconds.

public fun lock<T: key + store>(obj: T, ctx: &mut TxContext): (Locked<T>, Key) {
let key = Key { id: object::new(ctx) };
let mut lock = Locked {
id: object::new(ctx),
key: object::id(&key),
};

event::emit(LockCreated {
lock_id: object::id(&lock),
key_id: object::id(&key),
creator: ctx.sender(),
item_id: object::id(&obj),
});

dof::add(&mut lock.id, LockedObjectKey {}, obj);

(lock, key)
}

Using custom indexers

Learn more about custom indexers.

Querying events with RPC

The Sui RPC provides a queryEvents method to query on-chain packages and return available events. As an example, the following curl command queries the DeepBookV3 package on Mainnet for a specific type of event:

$ curl -X POST https://fullnode.mainnet.sui.io:443 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "suix_queryEvents",
"params": [
{
"MoveModule": {
"package": "0x158f2027f60c89bb91526d9bf08831d27f5a0fcb0f74e6698b9f0e1fb2be5d05",
"module": "deepbook_utils",
"type": "0xdee9::clob_v2::DepositAsset<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>"
}
},
null,
3,
false
]
}'
Click to open

Successful response

{
"jsonrpc": "2.0",
"result": {
"data": [
{
"id": {
"txDigest": "8NB8sXb4m9PJhCyLB7eVH4onqQWoFFzVUrqPoYUhcQe2",
"eventSeq": "0"
},
"packageId": "0x158f2027f60c89bb91526d9bf08831d27f5a0fcb0f74e6698b9f0e1fb2be5d05",
"transactionModule": "deepbook_utils",
"sender": "0x8b35e67a519fffa11a9c74f169228ff1aa085f3a3d57710af08baab8c02211b9",
"type": "0xdee9::clob_v2::WithdrawAsset<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
"parsedJson": {
"owner": "0x704c8c0d8052be7b5ca7174222a8980fb2ad3cd640f4482f931deb6436902627",
"pool_id": "0x7f526b1263c4b91b43c9e646419b5696f424de28dda3c1e6658cc0a54558baa7",
"quantity": "6956"
},
"bcs": "2szz6igTRuGmD7YATo8BEg81VLaei4od62wehadwMXYJv63UzJE16USL9pHFYBAGbwNkDYLCk53d45eFj3tEZK1vDGqtXcqH5US",
"timestampMs": "1691757698019"
},
{
"id": {
"txDigest": "8NB8sXb4m9PJhCyLB7eVH4onqQWoFFzVUrqPoYUhcQe2",
"eventSeq": "1"
},
"packageId": "0x158f2027f60c89bb91526d9bf08831d27f5a0fcb0f74e6698b9f0e1fb2be5d05",
"transactionModule": "deepbook_utils",
"sender": "0x8b35e67a519fffa11a9c74f169228ff1aa085f3a3d57710af08baab8c02211b9",
"type": "0xdee9::clob_v2::OrderFilled<0x2::sui::SUI, 0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
"parsedJson": {
"base_asset_quantity_filled": "0",
"base_asset_quantity_remaining": "1532800000000",
"is_bid": false,
"maker_address": "0x78a1ff467e9c15b56caa0dedfcfbdfe47c0c385f28b05fdc120b2de188cc8736",
"maker_client_order_id": "1691757243084",
"maker_rebates": "0",
"order_id": "9223372036854839628",
"original_quantity": "1614700000000",
"pool_id": "0x7f526b1263c4b91b43c9e646419b5696f424de28dda3c1e6658cc0a54558baa7",
"price": "605100",
"taker_address": "0x704c8c0d8052be7b5ca7174222a8980fb2ad3cd640f4482f931deb6436902627",
"taker_client_order_id": "20082022",
"taker_commission": "0"
},
"bcs": "DcVGz85dWTLU4S33N7VYrhgbkm79ENhHVnp5kBfENEWEeMxHQuvsczg94teh6WHdYtwPqdEsPWdvSJ7ne5qiMxxn3kBm36KLyuuzHV1QdzF45GN8ZU1MDGU4XppiaqcMeRpPPiW8JpUDyeQoobKEV8fMqcyYpDq6KWtZ1WMoGvEDxFKDgFvW9Q7bt1JAzQehRkEKEDZ6dTwfiHw92QuFqczmZ5MKJLYzeysUsSw",
"timestampMs": "1691757698019"
},
{
"id": {
"txDigest": "8b3byDuRojHXqmSz16PsyzfdXJEY5nZBGTM23gMsMAY8",
"eventSeq": "0"
},
"packageId": "0x158f2027f60c89bb91526d9bf08831d27f5a0fcb0f74e6698b9f0e1fb2be5d05",
"transactionModule": "deepbook_utils",
"sender": "0x8b35e67a519fffa11a9c74f169228ff1aa085f3a3d57710af08baab8c02211b9",
"type": "0xdee9::clob_v2::OrderFilled<0x2::sui::SUI, 0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
"parsedJson": {
"base_asset_quantity_filled": "700000000",
"base_asset_quantity_remaining": "0",
"is_bid": false,
"maker_address": "0x03b86e93d80b27763ee1fc2c37e285465dff835769de9462d9ad4ebcf46ac6df",
"maker_client_order_id": "20082022",
"maker_rebates": "634",
"order_id": "9223372036854839643",
"original_quantity": "1000000000",
"pool_id": "0x7f526b1263c4b91b43c9e646419b5696f424de28dda3c1e6658cc0a54558baa7",
"price": "604100",
"taker_address": "0x704c8c0d8052be7b5ca7174222a8980fb2ad3cd640f4482f931deb6436902627",
"taker_client_order_id": "20082022",
"taker_commission": "1058"
},
"bcs": "DcVGz85dWTLU4S33N7VYrhgbkm79ENhHVnp5kBfENEWEjN45pa9U3AkNhxfTRZbaHTQLugLBXttE32hpJKRsbrZGdryXMPmNA8EpHJnVcnYMXZmWXkNXvY1XjEYnAKU4BnhyJ9BQuxRJDXLA4DEu5uWEpWjLPD2ZHuxqHCn7GpUxvxJjHkKjr9jVVfeR6sN2uRhUXkThEDjCekrqaqwidkyXNmTzmZG4fre3eoZ",
"timestampMs": "1691758372427"
}
],
"nextCursor": {
"txDigest": "8b3byDuRojHXqmSz16PsyzfdXJEY5nZBGTM23gMsMAY8",
"eventSeq": "0"
},
"hasNextPage": true
},
"id": 1
}

You can use the getTransaction method with include: { events: true } to retrieve events for a specific transaction:

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

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

async function getEventsForTransaction(digest: string) {
const result = await client.getTransaction({
digest,
include: { events: true },
});

if (result.$kind === 'Transaction') {
return result.Transaction.events ?? [];
}
return [];
}

Querying events with GraphQL

⚙️Early-Stage Feature

This content describes an alpha/beta feature or service. These early stage features and services are in active development, so details are likely to change.

You can use GraphQL to query events instead of JSON RPC.

Click to open

Event connection

{
events(
filter: {type: "0x3164fcf73eb6b41ff3d2129346141bd68469964c2d95a5b1533e8d16e6ea6e13::Market::ChangePriceEvent<0x2::sui::SUI>"}
) {
nodes {
transactionModule {
name
package {
digest
}
}
sender {
address
}
timestamp
contents {
type {
repr
}
json
}
eventBcs
}
}
}

Querying events in Rust

The Sui by Example repo on GitHub contains a code sample that demonstrates how to query events using the query_events function. The package that PACKAGE_ID_CONST points to exists on Mainnet, so you can test this code using Cargo. To do so, clone the sui-by-example repo locally and follow the Example 05 directions.

use sui_sdk::{rpc_types::EventFilter, types::Identifier, SuiClientBuilder};

const PACKAGE_ID_CONST: &str = "0x279525274aa623ef31a25ad90e3b99f27c8dbbad636a6454918855c81d625abc";

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let sui_mainnet = SuiClientBuilder::default()
.build("https://fullnode.mainnet.sui.io:443")
.await?;

let events = sui_mainnet
.event_api()
.query_events(
EventFilter::MoveModule {
package: PACKAGE_ID_CONST.parse()?,
module: Identifier::new("dev_trophy")?,
},
None,
None,
false,
)
.await?;

for event in events.data {
println!("Event: {:?}", event.parsed_json);
}

Ok(())
}

Filtering event queries

To filter the events returned from your queries, use the following data structures.

RPC

QueryDescriptionJSON-RPC parameter example
AllAll events{"All": []}
AnyEvents emitted from any of the given filter{"Any": SuiEventFilter[]}
TransactionEvents emitted from the specified transaction{"Transaction":"DGUe2TXiJdN3FI6MH1FwghYbiHw+NKu8Nh579zdFtUk="}
MoveModuleEvents emitted from the specified Move module{"MoveModule":{"package":"<PACKAGE-ID>", "module":"nft"}}
MoveEventModuleEvents emitted, defined on the specified Move module{"MoveEventModule": {"package": "<DEFINING-PACKAGE-ID>", "module": "nft"}}
MoveEventTypeMove struct name of the event{"MoveEventType":"::nft::MintNFTEvent"}
SenderQuery by sender address{"Sender":"0x008e9c621f4fdb210b873aab59a1e5bf32ddb1d33ee85eb069b348c234465106"}
TimeRangeReturn events emitted in [start_time, end_time] interval{"TimeRange":{"startTime":1669039504014, "endTime":1669039604014}}

GraphQL

To filter events queried using GraphQL, use one of the following workflows.

Click to open

Filter events by sender using a GraphQL query

query ByTxSender {
events(
first: 1
filter: {
sender: "0xdff57c401e125a7e0e06606380560b459a179aacd08ed396d0162d57dbbdadfb"
}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
transactionModule {
name
}
contents {
type {
repr
}
json
}
sender {
address
}
timestamp
eventBcs
}
}
}

The TypeScript SDK also can be used to interact with the Sui GraphQL service and filter events.

Click to open

Filter events using the TypeScript SDK

import { SuiGraphQLClient } from '@mysten/sui/graphql';
import { graphql } from '@mysten/sui/graphql/schema';

const gqlClient = new SuiGraphQLClient({
url: 'https://graphql.mainnet.sui.io/graphql',
network: 'mainnet',
});

const queryEventsByType = graphql(`
query EventsByType($eventType: String!, $first: Int) {
events(filter: { eventType: $eventType }, first: $first) {
nodes {
sendingModule {
name
package { address }
}
type { repr }
sender { address }
contents { json }
timestamp
}
pageInfo {
hasNextPage
endCursor
}
}
}
`);

async function getEventsByType(eventType: string, first: number = 10) {
const result = await gqlClient.query({
query: queryEventsByType,
variables: { eventType, first },
});

return result.data?.events?.nodes ?? [];
}
Custom Indexing Framework

The sui-indexer-alt-framework is a powerful Rust framework for building high-performance, custom blockchain indexers on Sui. It provides customizable, production-ready components for data ingestion, processing, and storage.

Events

The Move Book shows how to emit events in Move.

Trustless Swap

An app that performs atomic swaps on Sui. Atomic swaps are similar to escrows but without requiring a trusted third party.