Stellar Smart Contract Events Explained for Soroban Developers
Events are your primary observability tool for Soroban smart contracts. When your contract does something important — a transfer, a state change, an error condition — it should emit an event. This guide covers how to emit events in your contract, query them via the RPC, and use them for monitoring and debugging.
What Are Soroban Events
Soroban events are structured data emitted during contract execution. They are stored in the ledger metadata and queryable via the Stellar RPC getEvents method.
Each event has:
contract (from your code), system (from the runtime), or diagnosticEmitting Events in Rust
In your Soroban contract:
use soroban_sdk::{contractimpl, Env, Symbol, Address, symbol_short};
#[contractimpl]
impl MyContract {
pub fn transfer(env: Env, from: Address, to: Address, amount: i128) {
// ... transfer logic ...
// Emit event
env.events().publish(
(symbol_short!("transfer"), from.clone(), to.clone()),
amount,
);
}
}The topics tuple is what consumers use to filter events. Put the event type first, then the key identifiers.
Querying Events via RPC
Use the getEvents method to query contract events:
{
"jsonrpc": "2.0",
"id": 1,
"method": "getEvents",
"params": {
"startLedger": 61000000,
"filters": [{
"type": "contract",
"contractIds": ["CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"],
"topics": [["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*"]]
}],
"pagination": {
"limit": 100
}
}
}Filter Options
| Filter | Description |
|---|---|
| `type` | `contract`, `system`, or `diagnostic` |
| `contractIds` | Array of contract IDs to filter by |
| `topics` | Array of topic filters (`*` for wildcard) |
| `startLedger` | Start of the ledger range |
Topic Matching
Topics are XDR-encoded. Use wildcards for flexible matching:
["*"] — Match any first topic["transfer", "*", "*"] — Match transfer events from any account to any account["transfer", "GABC...", "*"] — Match transfers from a specific accountEvent Types to Emit
Standard Events
| Event | Topics | Data | When |
|---|---|---|---|
| transfer | (transfer, from, to) | amount | Asset moved |
| approve | (approve, owner, spender) | amount | Allowance set |
| mint | (mint, to) | amount | New tokens created |
| burn | (burn, from) | amount | Tokens destroyed |
Custom Events
| Event | Topics | Data | When |
|---|---|---|---|
| config_change | (config, key) | new_value | Settings updated |
| error | (error, code) | message | Error condition |
| deposit | (deposit, user) | amount, asset | Funds deposited |
| withdrawal | (withdrawal, user) | amount, asset | Funds withdrawn |
Monitoring Events in Production
Polling Pattern
async function pollEvents(contractId, startLedger, onEvent) {
let cursor = startLedger;
setInterval(async () => {
const res = await fetch(RPC_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0', id: 1,
method: 'getEvents',
params: {
startLedger: cursor,
filters: [{ type: 'contract', contractIds: [contractId], topics: [['*']] }],
pagination: { limit: 100 },
},
}),
});
const data = await res.json();
const events = data.result?.events || [];
for (const event of events) {
onEvent(event);
cursor = event.ledger + 1;
}
}, 5000);
}What to Alert On
| Condition | Alert Level |
|---|---|
| No events in 30 minutes (normally active contract) | Warning |
| Error events | Critical |
| Unexpected event topic | Warning |
| Large transfer event (whale movement) | Info |
| Event volume spike (2x normal) | Info |
Debugging with Events
Events are invaluable for debugging production issues:
Tools
LumenQuery provides tools for working with Soroban events:
*Monitor your Soroban contract events with LumenQuery. Decoded event streams, storage viewer, and call history — start free.*