DEV Community

tom14cat14
tom14cat14

Posted on Originally published at lunarshadowlabs.com AI-assisted

Decoding Solana DEX trades from Yellowstone gRPC: 8 things you have to handle

Yellowstone (Geyser) gRPC gives you raw Solana transactions about as fast as a node sees them. That part is easy to set up. The hard part comes after: turning a raw transaction into "this wallet bought this token on this pool for this much."

We run a decoder like this across 12 DEX venues. Here is the list of problems we had to solve, so you can budget for them if you build your own.

1. One decoder per program

Every venue logs trades differently. Some use Anchor emit_cpi events, some print their trades to the program logs, and sometimes the only reliable source is the balance changes. Twelve venues means twelve decoders, and any of them can break when the program is upgraded and the event layout changes.

2. Inner instructions

A lot of volume arrives through routers and aggregators, so the actual swap is an inner instruction, not a top-level one. If you only look at top-level instructions you will miss a large share of trades. We also record the outer program (we call it via) so you can tell routed flow from direct flow.

3. Duplicate events

Some programs emit two events for one swap. Meteora DLMM, for example, emits a current event plus a legacy twin. If you count both, your volume is doubled. You need to pick one.

4. Truncated logs

The runtime truncates long logs, so on busy transactions the event you are looking for can simply be missing. For the venues that report trades in their logs, our fallback is to rebuild the trade from the user's own token transfers inside the swap (or from the pool's balance changes) and flag the result as derived, so consumers know it was reconstructed.

5. Base vs quote orientation

Pools list their two tokens in arbitrary order. If you don't normalize, "buy" and "price" mean opposite things on different pools. We orient every pool the same way: the first of USDC, USDT or SOL present is the quote token. After that, is_buy and price mean the same thing on every venue.

6. Token-2022 extensions

Transfer-fee extensions change what a user actually receives, and other mint extensions (permanent delegate, transfer hook and so on) matter for risk. You need to read the mint account, not just the transaction, and cache it.

7. Big integers

Raw token amounts regularly exceed JavaScript's safe integer range (2^53). Send them as strings with the decimals alongside, or your JS consumers will silently lose precision.

8. Reconnects and gaps

gRPC streams drop. When you reconnect you need to know what you missed. We put a sequence number on every event so a client can resume from where it left off, and send an explicit gap message when something could not be replayed. Without that, a reconnect silently loses trades.

What the output looks like

After all of that, a PumpSwap trade and a Raydium CLMM trade come out in the same flat shape. Trimmed example:

{
  "kind": "swap",
  "venue": "pumpswap",
  "commitment": "processed",
  "slot": 454291681,
  "via": "DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH",
  "pool": "6tv1HzMFM93HHPZds7kyWZFuUEgVAnNkAgT1d5Dg7Gw5",
  "user": "jMrze5nuUXTTWraMGWDnqzLED5JWvQygLByH86hpwJ1",
  "base_mint": "7XSrFStYppauv9dvrPBqMXqJarQJo4QFSDsUp7Dmpump",
  "quote_mint": "So11111111111111111111111111111111111111112",
  "is_buy": true,
  "base_amount": "328124895560",
  "quote_amount": "210414078",
  "base_decimals": 6,
  "quote_decimals": 9,
  "price": 6.412621561094693e-07,
  "derived": false
}
Enter fullscreen mode Exit fullscreen mode

When to just parse raw gRPC yourself

Building this yourself is the right call if:

  • you need programs a decoded feed doesn't cover, or non-DEX activity
  • you need the full transaction or account data, not just the decoded event
  • you need confirmed or finalized commitment (ours, for example, publishes at processed, before confirmation)
  • you already run Yellowstone and have decoders you trust

If you mostly want DEX trades, launches, migrations and pool events across many venues, and you'd rather not maintain decoders every time a program upgrades, a decoded feed is less work. For reference, the full multi-venue stream we see is about 480 messages per second (about 0.65 MB/s), so server-side filtering by venue, mint, pool or wallet matters.


We build sol-feed, a decoded Solana DEX WebSocket feed, which is where this list comes from. The original guide, with a Python client example, is here. Market data only.

Top comments (0)