Parse Meteora DLMM Transactions on Solana with Yellowstone gRPC
Stream and decode Meteora DLMM pool transactions in real-time. Learn to parse swaps, add/remove liquidity events using gRPC in TypeScript.
Introduction
Meteora's Dynamic Liquidity Market Maker (DLMM) is one of Solana's most innovative DEX protocols, offering concentrated liquidity with dynamic fees. For developers building analytics tools, arbitrage bots, or liquidity management systems, parsing DLMM transactions is a core requirement.
This guide covers how to stream Meteora DLMM transactions via Yellowstone gRPC and decode the various instruction types.
Understanding Meteora DLMM
Unlike traditional AMMs with uniform liquidity distribution, Meteora DLMM uses a bin-based system:
- Liquidity is organized into discrete price bins
- Each bin has a specific price range
- Fees adjust dynamically based on market volatility
- LPs can concentrate liquidity around the current price
This architecture means transaction parsing is more nuanced than simple AMM swaps.
Setting Up the gRPC Stream
import { Client } from '@triton-one/yellowstone-grpc';
const METEORA_DLMM_PROGRAM = 'LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo';
const client = new Client(ENDPOINT, TOKEN);
const stream = await client.subscribe();
await stream.write({
transactions: {
meteora: {
accountInclude: [METEORA_DLMM_PROGRAM],
accountExclude: [],
accountRequired: [],
},
},
commitment: 'confirmed',
});
Parsing DLMM Instructions
Meteora DLMM has several key instruction types:
Swap
The most common instruction — token swaps through DLMM pools.
Add Liquidity
LPs adding tokens to specific bins. Includes parameters for bin range and distribution strategy.
Remove Liquidity
LPs withdrawing tokens from bins. Important for tracking liquidity changes.
Initialize Position
Creating a new LP position in a pool.
import { BorshCoder } from '@coral-xyz/anchor';
import meteoraIdl from './meteora_dlmm_idl.json';
const coder = new BorshCoder(meteoraIdl);
function parseMeteoraDLMMTx(tx: TransactionUpdate) {
const message = tx.transaction?.message;
if (!message) return null;
const events = [];
for (const ix of message.instructions) {
try {
const decoded = coder.instruction.decode(Buffer.from(ix.data));
if (!decoded) continue;
switch (decoded.name) {
case 'swap':
events.push({
type: 'swap',
amountIn: decoded.data.amountIn.toString(),
minAmountOut: decoded.data.minAmountOut.toString(),
pool: message.accountKeys[ix.accounts[0]],
});
break;
case 'addLiquidity':
events.push({
type: 'add_liquidity',
amounts: decoded.data.liquidityParameter,
pool: message.accountKeys[ix.accounts[0]],
});
break;
}
} catch { /* not a Meteora instruction */ }
}
return events;
}
Detecting Buy and Sell Events
To classify swaps as buys or sells, compare the input token against the pool's base token:
function classifyDLMMSwap(swap: DLMMSwap, pool: DLMMPool): 'buy' | 'sell' {
// If swapping SOL/USDC for the token, it's a buy
// If swapping the token for SOL/USDC, it's a sell
return swap.swapForY ? 'buy' : 'sell';
}
Working with Bin Data
DLMM bin data provides rich information about liquidity distribution:
// Fetch bin arrays for price analysis
function calculatePriceFromBin(binId: number, binStep: number): number {
return Math.pow(1 + binStep / 10000, binId - 8388608);
}
Production Considerations
- Multiple pool types — Meteora has DLMM, DAMM v2, and DBC pools. Each has a different program ID and IDL.
- Bin precision — Prices derived from bins have specific decimal precision depending on the token pair.
- Event ordering — A single transaction can contain multiple Meteora instructions. Process them in order.
- Account data — For full context, you may need to fetch the pool account data to know the token mints.
Leveraging Solana Tracker
Solana Tracker's Data API provides pre-indexed Meteora pool data including TVL, volume, and price history — saving you from building your own indexer. For real-time needs, combine the Data API with gRPC streaming for comprehensive coverage.
View the full source code on GitHub.