Developers

Stellar Events: Track All the Things

Author

Simon Chow

Publishing date

Have you ever wondered what happens under the hood when a transaction or smart contract executes? Not the Wasm bytecode or instruction, but things like:

  • Were tokens minted?
  • Were any transferred?
  • Were some burned or clawed back?

Good news. CAP-67 introduces a single, structured event stream to get this exact information for every token-value movement on Stellar, classic and smart contract alike.

State of the World

Today, token movements on Stellar are tracked using different methods: non-smart contract operations rely on computing differences in ledger state, while Soroban smart contracts emit their own event logs. This split forces anyone who wants the full picture to juggle multiple data sources with overly complex logic to merge these data streams.

Let’s walk through a “simple” USDC example. Imagine you need to trace every movement of USDC for an account. They execute a straightforward payment, a multi-step path payment, and a DeFi Protocol swap, and you want to know the account holdings at each stage:

  1. Direct payment Parse the Payment operation, then diff ledger entries to see that Account A lost 100 USDC and Account B gained 100 USDC.
  2. Path payment Track the multi‐step path: A → B (USDC→XLM), then B → C (XLM→EURC), pulling reserves from the built-in Stellar network liquidity pool that converted USDC to EURC.
  3. DeFi Protocol swap Inspect ContractEvents and ContractData to manually compute pool-share movements of USDC.

To get the full picture you must:

  • Scrape ledger entry diffs for non-smart contract ops, then
  • Pull all contract events for USDC, and
  • Correlate them by account and asset

That’s three separate pipelines that you have to stitch together. Yuck!

CAP-67

All token value movements now flow through one stream of events:

  • Transfer - Tokens moved from one account to another
  • Mint - New tokens created and assigned to an account
  • Burn - Tokens burned (permanently destroyed)
  • Clawback - Tokens reclaimed by the issuer
  • Fee - XLM fee charged for network operations

You no longer need custom scraping logic to track token movements. All you need to do is aggregate the above events for the token you want to track.

For example, calculating total circulating supply of a token is as easy as:

SUM(token mint events) - SUM(token burn events)

-- Total circulating supply of USDC
SELECT
  SUM(CASE WHEN topic = 'mint' THEN $value.amount ELSE 0 END) - 
  SUM(CASE WHEN topic = 'burn' THEN $value.amount ELSE 0 END) 
  AS circulating_supply
FROM stellar_events
WHERE asset = 'USDC'

Event Format Examples

Here are concrete examples of the events you’ll see. The exact event format for operations can be found in CAP-67

For transfer events, the order of the addresses denotes the sender and receiver. The first address in the topic represents the source account, from, that is sending the asset. The second address is the destination, or to. Stellar assets details will be found in the optional 4th topic, string.

Transfer with SAC (Stellar Asset Contract)

ContractEventXDR
{
  "ext": "v0",
  "contract_id": "CA...6LF5",
  "type_": "contract",
  "body": {
    "v0": {
      "topics": [
        {
          "symbol": "transfer"
        },
        {
          "address": "GC...F33H"
        },
        {
          "address": "CA...NIBU"
        },
        {
          "string": "USDC:GA..AGER"
        }
      ],
      "data": {
        "i128": "10000000"
      }
    }
  }
}

Transfer with a liquidity pool

CAP-67 introduces a new SCAddressType for liquidity pool and claimable balances. Liquidity pool addresses will start with “L,” and claimable balances will follow the same convention with “B”. In this example, the from address is a liquidity pool using the new SCAddressType:

ContractEventXDR
{
  "ext": "v0",
  "contract_id": "CA...6LF5",
  "type_": "contract",
  "body": {
    "v0": {
      "topics": [
        {
          "symbol": "transfer"
        },
        {
          "address": "LC...F33H"
        },
        {
          "address": "CA...NIBU"
        },
        {
          "string": "USDC:GA..AGER"
        }
      ],
      "data": {
        "i128": "10000000"
      }
    }
  }
}

Transfer with a contract token

Note that the “asset” is omitted. Custom contracts don’t natively have the concept of an asset_code:asset_issuer. The “asset” is represented by the contract_id

ContractEventXDR
{
  "ext": "v0",
  "contract_id": "CA...6LF5",
  "type_": "contract",
  "body": {
    "v0": {
      "topics": [
        {
          "symbol": "transfer"
        },
        {
          "address": "GC...F33H"
        },
        {
          "address": "CA...NIBU"
        }
      ],
      "data": {
        "i128": "10000000"
      }
    }
  }
}

Every value movement–whether it’s simple transfers between Stellar accounts, smart contract events, liquidity-pool deposits and withdrawals, or claimable balance actions–flows through this unified format. You’ll never miss a step in a token’s journey again.

Why is this Important?

At the end of the day, nothing really changes for the final end user. A payment is still a payment.

This change is important because it is now infinitely simpler for backend developers and users to track token value movement holistically on the network regardless of where the operation originated.

A unified event stream makes it so:

  • Developers can build more reliable applications that are easily auditable
  • Wallets and explorers can display meaningful on-chain activity for their specific users
  • Indexers and analytics tools can provide easy access to any token value movement activity at any point in time in history
  • These events create a clearly defined and easy to use standard format for the whole ecosystem to use

It just makes everything we love easier to do. Visibility, interoperability, security, and trust. For full P23 release details, check out the Protocol 23 Announcement and corresponding Upgrade Guide.