soroban-cli
soroban-cli copied to clipboard
Add self-describing events formats
In protocol 23 contracts that use the new event definitions will include information in the contract spec about the shape of their events.
This prototype exists that show how by combining the event contract spec information with raw XDR events, a self-describing format for events can be generated:
- https://github.com/leighmcculloch/exp-sep48-derived-events-example
I propose we incorporate this ability, to convert events from their raw format into a self-describing JSON format, be something we implement in the CLI and offer it as the default rendering of events when using stellar events and stellar contract invoke.
As part of this work it would be worth thinking of the derived output as a format that should eventually become standardised, and so it may be worth as part of development writing a SEP that captures it.
Note: The act of marrying the event spec with a raw event will be something that SDKs that generate bindings for events will also need to do.
Current Output Today
The output formats for events today contain no context for what the data being displayed means. Parameter values are shown, but not parameter names.
stellar events
Plain format
Event 0002480158929850368-0000000000 [CONTRACT]:
Ledger: 577457 (closed at 2025-07-22T04:25:38Z)
Contract: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
Topics:
Symbol(ScSymbol(StringM(transfer)))
Address(Account(AccountId(PublicKeyTypeEd25519(Uint256(6dd03abefa152019c5acd5bd7b158891ee664dc70a0805d3a67436fbbcc4c242)))))
Address(Account(AccountId(PublicKeyTypeEd25519(Uint256(4a041fb650130ffda6f0726fccd535cefbf2505a13f091a960aa2cca997c9121)))))
String(ScString(StringM(native)))
Value: I128(Int128Parts { hi: 0, lo: 99999999900 })
JSON format
{
"type": "contract",
"ledger": 577462,
"ledgerClosedAt": "2025-07-22T04:26:03Z",
"id": "0002480180404682752-0000000002",
"contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"topic": [
"AAAADwAAAANmZWUA",
"AAAAEgAAAAAAAAAAvTfezXr4MS4ZsNWiaBTpG+RhNJ4lPwoF2fQk4ZxSI78="
],
"value": "AAAACgAAAAAAAAAAAAAAAAAXcn0="
}
stellar contract invoke
📅 CDHXZ22IGK5O7BXHLTPIHWZVDIG3W2W2J57N2FZ4Z6UTHIPEZXY4DIYQ - Success - Event: [{"symbol":"hello"},{"address":"CDHXZ22IGK5O7BXHLTPIHWZVDIG3W2W2J57N2FZ4Z6UTHIPEZXY4DIYQ"}] = {"map":[{"key":{"symbol":"name"},"val":{"string":"me"}}]}
Proposed Output
Event Definition
An approve event as defined in SEP-41.
#[contractevent]
pub struct Approve {
#[topic]
pub from: Address,
#[topic]
pub spender: Address,
pub amount: i128,
pub live_until_ledger: u32,
}
Raw Event (XDR-JSON)
{
"ext": "v0",
"contract_id": "CBUZJXHZ6PBS2YR3SEJZ3CIGMQBYP6367D3KQAR2NB3U2I5AOWLC4DU2",
"type_": "contract",
"body": {
"v0": {
"topics": [
{ "symbol": "approve" },
{ "address": "GBMBVAHBE6D4AJXJJVTBQTVU4G7SN4FEIJOL5YTOHZ4WCUMKQ52ANL2B" },
{ "address": "GCAN5IE4PWMWSMFZCYQRCJM73MCPG5JD2R7WF5HIFHHHDBWSDJFIWX7X" }
],
"data": {
"vec": [
{ "i128": "10000" },
{ "u32": 1998742 }
]
}
}
}
}
Contract Spec (SEP-48, XDR-JSON)
{
"event_v0": {
"name": "approve",
"prefix_topics": ["approve"],
"params": [
{ "name": "from", "type_":"address", "location": "topic_list" },
{ "name": "spender", "type_":"address", "location": "topic_list" },
{ "name": "amount", "type_":"i128", "location": "data" },
{ "name": "live_until_ledger", "type_":"u32", "location": "data" }
],
"data_format": "vec"
}
}
Output
And generating output, something like the following formats:
Multi-Line
Event 0002480158929850368-0000000000 [CONTRACT]:
Ledger: 577457 (closed at 2025-07-22T04:25:38Z)
Contract: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC
Event: approve
Params:
from: { "address": "GBMBVAHBE6D4AJXJJVTBQTVU4G7SN4FEIJOL5YTOHZ4WCUMKQ52ANL2B" }
spender: { "address": "GCAN5IE4PWMWSMFZCYQRCJM73MCPG5JD2R7WF5HIFHHHDBWSDJFIWX7X" }
amount: { "i128": "10000" }
live_until_ledger: { "u32": 1998742 }
Single-Line
📅 CDHXZ22IGK5O7BXHLTPIHWZVDIG3W2W2J57N2FZ4Z6UTHIPEZXY4DIYQ - Success - Event: hello, from: { "address": "GBMBVAHBE6D4AJXJJVTBQTVU4G7SN4FEIJOL5YTOHZ4WCUMKQ52ANL2B" }, spender: { "address": "GCAN5IE4PWMWSMFZCYQRCJM73MCPG5JD2R7WF5HIFHHHDBWSDJFIWX7X" }, amount: { "i128": "10000" }, live_until_ledger: { "u32": 1998742 }
JSON
{
"contract_id": "CBUZJXHZ6PBS2YR3SEJZ3CIGMQBYP6367D3KQAR2NB3U2I5AOWLC4DU2",
"event_type": "approve",
"params": {
"from": { "address": "GBMBVAHBE6D4AJXJJVTBQTVU4G7SN4FEIJOL5YTOHZ4WCUMKQ52ANL2B" },
"spender": { "address": "GCAN5IE4PWMWSMFZCYQRCJM73MCPG5JD2R7WF5HIFHHHDBWSDJFIWX7X" },
"amount": { "i128": "10000" },
"live_until_ledger": { "u32": 1998742 }
}
}
That would be a nice DX improvement 👍 Also the stellar events could have a bit more settings to filter events (by contract/address a minima).