Sui
Sui is a Layer 1 blockchain also utilizing the Move language, but with a distinct object-centric model.
Core Concepts
Sui integration utilizes Move-based concepts similar to Aptos but adapted for Sui's unique architecture, especially its object model.
Processors
Sui integration offers several processor types, listed in order of common usage:
User-Defined Processors
Generate type-safe processors from your smart contract ABIs using Sentio's code generation tools. These provide the best developer experience with full type safety and auto-completion.
// Example: Generated from your custom DEX contract
import { dex } from './types/sui/0xYourPackage.dex'
dex.bind({
network: SuiNetwork.MAIN_NET,
startCheckpoint: 1000000n
}).onEventSwapEvent(async (evt, ctx) => {
// Fully typed event data
const { amount_in, amount_out, user } = evt.data_decoded
// Process swap...
})Built-in System Processors
Sentio SDK pre-generates type-safe processors from Sui's core system modules, providing ready-to-use bindings for system-level monitoring:
// SUI coin module
import { sui } from '@sentio/sdk/sui/builtin/0x2'
// Staking system
import { staking_pool } from '@sentio/sdk/sui/builtin/0x3'
// Validator system
import { validator_set } from '@sentio/sdk/sui/builtin/0x3'These processors are generated by Sentio SDK from Sui's system packages and provide fully-typed interfaces for monitoring token transfers, staking operations, validator activities, and other system functions.
SuiObjectProcessor
Monitors a specific object by ID and its dynamic fields.
- Use Case: Track state of a specific pool, NFT, or singleton object
- Binding:
SuiObjectProcessor.bind({ objectId: '0x...', network: SuiNetwork.MAIN_NET, startCheckpoint: 1000000n })
SuiObjectTypeProcessor
Monitors ALL objects of a specific Move type across the network.
- Use Case: Track all NFTs in a collection, all pools of a DEX
- Binding:
SuiObjectTypeProcessor.bind({ objectType: '0x...::nft::Token', network: SuiNetwork.MAIN_NET }) - Supports generic type
Twith@typemove/suifor type-safe decoding
SuiAddressProcessor
Monitors all objects owned by an address and transactions sent to it.
- Use Case: Portfolio tracking, wallet monitoring, treasury management
- Binding:
SuiAddressProcessor.bind({ address: '0x...', network: SuiNetwork.MAIN_NET, startCheckpoint: 1000000n })
SuiModulesProcessor
Binds to a package address for manual event/call processing.
- Note: Consider using code generation for type-safe processors instead
- Binding:
SuiModulesProcessor.bind({ address: '0x...', network: SuiNetwork.MAIN_NET }) - Advanced Use: Rarely needed as user-defined processors handle most use cases better
SuiGlobalProcessor
Processes all transactions network-wide with optional filtering.
- Warning: Can be resource-intensive; use specific processors when possible
- Binding:
SuiGlobalProcessor.bind({ network: SuiNetwork.MAIN_NET }) - Advanced Use: Only for chain-wide analytics requiring global transaction monitoring
Handlers
Handlers vary depending on the processor type:
-
SuiModulesProcessor/SuiGlobalProcessor:onMoveEvent(handler(event, ctx), filter): Triggered when a specific Move event matching thefilter(type string) is emitted.onEntryFunctionCall(handler(call, ctx), filter): Triggered when an entry function matching thefilter(e.g.,0x...::module::function_name) is called.onTransactionBlock(handler(tx, ctx), filter?): Triggered for transaction blocks matching the filter (e.g., involving specific addresses).onObjectChange(handler(changes, ctx), typeFilter)(SuiGlobalProcessoronly): Triggered when objects matching thetypeFilterare changed (created, mutated, deleted) within any transaction block.
-
SuiAddressProcessor/SuiObjectProcessor/SuiObjectTypeProcessor:onTimeInterval(handler(objects | self, dynamicFields, ctx), intervalMinutes?, backfillIntervalMinutes?, type?, fetchConfig?): Periodically fetches and processes objects based on time intervals.SuiAddressProcessor:handler(objects: SuiMoveObject[], ctx: SuiAddressContext)SuiObjectProcessor:handler(self: SuiMoveObject, dynamicFields: SuiMoveObject[], ctx: SuiObjectContext)SuiObjectTypeProcessor:handler(self: TypedSuiMoveObject<T>, dynamicFields: SuiMoveObject[], ctx: SuiObjectContext)
onCheckpointInterval(handler(...), interval?, backfillInterval?, type?, fetchConfig?): Similar toonTimeIntervalbut based on checkpoint intervals.onTransactionBlock(handler(tx, ctx), filter?)(SuiAddressProcessoronly): Handles transaction blocks sent to the bound address.onObjectChange(handler(changes, ctx))(SuiObjectTypeProcessoronly): Processes changes specific to the bound object type.
Context (ctx)
ctx)Handlers receive a context object specific to the processor and handler type (SuiContext, SuiAddressContext, SuiObjectContext, SuiObjectChangeContext) providing:
- Chain information:
network,checkpoint. - Source details:
address(package or account),moduleName,objectId. - Transaction/Event details:
transaction,eventIndex,timestamp. - Helper methods: Sui
client(ctx.client) for interacting with the RPC,coderfor decoding Move data. - Standard SDK outputs:
ctx.meter.Counter('...'),ctx.eventLogger.emit('...'),ctx.exporter.sui_Object(...).
Fetch Configuration (fetchConfig)
fetchConfig)- Transaction-based handlers (
onMoveEvent,onEntryFunctionCall,onTransactionBlock) supportMoveFetchConfig(similar to Aptos) to includeresourceChanges,allEvents, orinputs.
interface MoveFetchConfig {
allEvents: boolean; // Fetch all events for the transaction
includeFailedTransaction?: boolean; // Include failed transactions
inputs: boolean; // Fetch transaction input arguments
resourceChanges: boolean; // Fetch resource changes
resourceConfig?: ResourceConfig; // Specific configuration for resource fetching
supportMultisigFunc?: boolean; // Support for multisig functions
}- Interval-based handlers (
onTimeInterval,onCheckpointInterval) supportMoveAccountFetchConfig(mainlyowned: true/false).
interface MoveAccountFetchConfig {
owned: boolean;
}Getting Started Example (Processing Events)
First, create a Sui processor project: yarn sentio create -c sui <project-name>. You can refer to the CLI Reference to learn details.
import { SuiModulesProcessor, SuiContext, SuiNetwork } from "@sentio/sdk/sui";
import { SuiEvent } from '@mysten/sui/client';
const PACKAGE_ADDRESS = "0xdee9...package_id"; // Replace with your package ID
// Define the expected structure of the event based on your Move contract
interface MyCustomEvent {
field1: string;
amount: bigint;
user: string;
}
SuiModulesProcessor.bind({
address: PACKAGE_ADDRESS,
network: SuiNetwork.MAIN_NET,
startCheckpoint: 25000000n
})
.onMoveEvent(async (event: SuiEvent, ctx: SuiContext) => {
// Check if the event type matches what we expect
if (event.type === `${PACKAGE_ADDRESS}::my_module::MyCustomEvent`) {
// Type cast the parsedJson for easier access, assuming it matches MyCustomEvent
const decodedEvent = event.parsedJson as MyCustomEvent;
ctx.meter.Counter("my_custom_event_cnt").add(1);
ctx.meter.Counter("my_custom_event_amount_total").add(decodedEvent.amount);
ctx.eventLogger.emit("MyCustomEventFired", {
distinctId: decodedEvent.user,
field1Value: decodedEvent.field1,
eventAmount: decodedEvent.amount.toString(), // Convert bigint to string for logging
suiTxDigest: ctx.transaction?.digest
});
}
}, { type: `${PACKAGE_ADDRESS}::my_module::MyCustomEvent` }); // Filter specifically for this event type
import { SuiObjectTypeProcessor, SuiNetwork, TypedSuiMoveObject } from "@sentio/sdk/sui";
import { TypeDescriptor } from "@typemove/move";
// Define the expected structure of your Move object based on your contract
interface MyCustomObject {
id: { id: string };
field1: string;
amount: bigint;
owner: string;
metadata?: {
name: string;
description: string;
};
}
const OBJECT_TYPE = "0x1234...::my_module::MyCustomObject"; // Replace with your object type
SuiObjectTypeProcessor.bind({
objectType: new TypeDescriptor(OBJECT_TYPE),
network: SuiNetwork.MAIN_NET,
startCheckpoint: 25000000n
})
.onTimeInterval(async (self: TypedSuiMoveObject<MyCustomObject>, dynamicFields, ctx) => {
// Access decoded object fields here
const fields = self.data_decoded;
// Access generic type arguments if your object has them
const typeArg0 = self.type_arguments[0];
const typeArg1 = self.type_arguments[1];
ctx.meter.Counter("my_custom_object_cnt").add(1);
ctx.meter.Counter("my_custom_object_amount_total").add(fields.amount);
ctx.eventLogger.emit("MyCustomObjectSnapshot", {
distinctId: "system", // Use system for timed intervals
objectId: fields.id?.id || "unknown",
field1Value: fields.field1,
objectAmount: fields.amount.toString(), // Convert bigint to string for logging
owner: fields.owner,
metadata: JSON.stringify(fields.metadata || {}),
typeArgument0: typeArg0 ? `0x${typeArg0}` : "none",
typeArgument1: typeArg1 ? `0x${typeArg1}` : "none",
checkpoint: ctx.checkpoint.toString()
});
}, undefined, 60000); // Run every 60 secondsUpdated 15 days ago