Meteora DLMM Bot Infrastructure: RPC, Yellowstone gRPC, and Safe Execution
A practical TypeScript architecture for monitoring Meteora DLMM pools, maintaining local state, reconciling stream updates, and submitting bounded transactions on Solana.
Rrpc edge Team·Jul 22, 2026·13 min read
Frequently asked questions
Can I build a Meteora DLMM bot with Yellowstone gRPC?
Yes. Use JSON-RPC and the official Meteora DLMM SDK to discover and bootstrap a pool, then use Yellowstone gRPC to receive relevant account or transaction updates. Keep a periodic RPC reconciliation loop because a live stream is not a complete state store by itself.
Which Meteora DLMM accounts should a bot monitor?
Start with the LB pair account and the bin array accounts that cover the price range relevant to your position or decision. The exact set changes with the active bin and your strategy range, so derive it from current pool state and refresh the subscription when that range changes.
Should a DLMM bot subscribe to accounts or transactions?
Use account updates when the decision depends on current pool state such as the active bin and liquidity distribution. Use transaction updates when you need to classify swaps or instructions. Many production systems use both, with account state as the decision input and transactions as context and telemetry.
How should a DLMM bot recover after a gRPC disconnect?
Reconnect with bounded exponential backoff, request replay from a known slot when the provider supports from_slot, deduplicate replayed updates, and refresh authoritative pool state through RPC before enabling execution again.
Does faster infrastructure make a DLMM strategy profitable?
No. Infrastructure can reduce observation and submission delay, but it cannot create a profitable strategy or remove inventory, fee, slippage, adverse selection, smart contract, or transaction failure risk. Validate the strategy separately and enforce explicit execution limits.
A DLMM automation loop may rebalance a position, monitor a price range, classify swaps, or prepare
some other bounded action. Whatever the strategy, the infrastructure has the same responsibilities:
Discover the pool and load a coherent starting snapshot.
Observe the accounts that can change the decision.
Apply updates in slot order and reject duplicates.
Detect disconnects, dead slots, and local state gaps.
Reconcile the local view against an authoritative RPC read.
Build, simulate, sign, submit, and verify an action under explicit limits.
The strategy decides whether to act. The infrastructure decides whether the state is trustworthy
enough to permit that action.
A low-latency stream is useful only when the bot can prove what it has seen, what it may have missed,
and which snapshot its next transaction is based on.
This guide uses the official
Meteora DLMM TypeScript SDK for pool access and the official
Yellowstone gRPC interface for streaming concepts.
Read those projects as the source of truth for current package versions, protobuf fields, and
program behavior.
Install the SDK and its Solana dependencies using the versions listed in the
official repository:
install.sh
npm install @meteora-ag/dlmm @solana/web3.js
Create the pool client from a known LB pair address, then read the initial pool state. Keep the RPC
endpoint and the gRPC endpoint in the same region when possible, so the snapshot and live stream are
less likely to describe materially different tips of the chain.
bootstrap-pool.ts
import DLMM from "@meteora-ag/dlmm";import { Connection, PublicKey } from "@solana/web3.js";const connection = new Connection(process.env.SOLANA_RPC_URL!, { commitment: "confirmed", wsEndpoint: process.env.SOLANA_WS_URL,});const lbPair = new PublicKey(process.env.METEORA_LB_PAIR!);const pool = await DLMM.create(connection, lbPair);const activeBin = await pool.getActiveBin();const bootstrap = { lbPair: lbPair.toBase58(), activeBinId: activeBin.binId,
This is a bootstrap, not a permanent cache. Save the slot associated with the snapshot if your RPC
method exposes it. Your stream consumer needs a boundary that answers a basic question: which
updates happened after the state I loaded?
At startup, also resolve the bin array accounts that cover the range your system cares about.
DLMM distributes liquidity across discrete bins, so watching only the pair account may tell you that
the active bin moved without giving you every liquidity value needed for a decision. Derive the
relevant bin arrays from current pool state and the strategy range, then update that watch set when
the active range moves.
Yellowstone supports account filters by public key, owner, data size, and memory comparison. It also
supports transaction filters using account_include, account_exclude, and account_required.
The exact semantics are documented in the
Yellowstone repository.
For a DLMM state loop, begin with explicit account keys:
the LB pair account;
the bin array accounts around the active range;
position accounts owned by the bot, if position state is part of the decision;
token accounts needed to enforce inventory limits.
Avoid subscribing to the entire program unless you genuinely need every pool. A broad subscription
increases bandwidth, decoding work, memory pressure, and the chance that useful updates wait behind
irrelevant ones in your own process.
The request below uses the official Yellowstone client shape. watchedAccounts should come from the
Meteora bootstrap and range selection step, not from a hardcoded production list.
The official protobuf uses snake case on the wire while generated TypeScript clients commonly use
camel case. Confirm field names against the installed client version before deploying.
Account writes are the right default when the action depends on current pool state. Add a filtered
transaction stream when you need to classify swaps, attribute state changes, or measure which
instructions preceded a move.
Use the Meteora program ID in account_include, then narrow the result in your process to the LB pair
and instruction variants you support. Set vote and failed explicitly. A transaction stream adds
context, but it should not silently become the source of truth for balances or current bin state.
Do not call the strategy directly from the gRPC event handler. The handler should validate, record,
and reduce each update into a local model. The strategy reads only a model marked healthy.
pool-state-reducer.ts
type AccountUpdate = { pubkey: string; slot: bigint; writeVersion: bigint; data: Uint8Array;};type Version = { slot: bigint; writeVersion: bigint };const versions = new Map<string, Version>();function shouldApply(update: AccountUpdate): boolean { const previous = versions.get(update.pubkey); if (!previous) return
The pair of account key, slot, and write version gives the reducer an idempotency boundary. A replay
or reconnect may deliver data the process has already seen. Dropping an exact duplicate is correct.
Dropping a newer write because it shares a slot is not.
Keep the raw account bytes or a hash long enough to diagnose a bad decode. Parser failures should
quarantine the affected pool and alert an operator. They should never fall through to a default
price or empty liquidity value.
A production stream will disconnect eventually. Plan the recovery path before enabling execution.
Yellowstone includes a ping field for keeping long-lived subscriptions active behind load
balancers. Its current protobuf also includes from_slot for replay where the server supports a
retained range. Neither feature removes the need for a fresh state check.
Use this recovery sequence:
Mark the local model unhealthy as soon as the stream closes or stops advancing.
Disable new execution, but continue verifying already submitted signatures.
Reconnect with bounded exponential backoff and jitter.
Request replay from the last safely applied slot when supported.
Deduplicate replayed account writes in the reducer.
Fetch a fresh SDK or RPC snapshot for the pair and relevant bin arrays.
Compare the snapshot with the reduced state.
Re-enable execution only after the watch set and state agree.
Slot notifications also expose status changes. A slot observed at processed can later be marked
dead. Keep processed data useful for responsiveness, but reconcile consequential decisions at the
commitment level your risk policy requires. The distinctions are covered in
Solana Commitment Levels: Processed vs Confirmed vs Finalized.
The values above are examples, not universal defaults. Choose thresholds from the cadence and
failure budget of your application, then test them during deliberate disconnects and delayed RPC
responses.
Need RPC, Yellowstone gRPC, and a sender for one Solana bot?
rpc edge provides the read, streaming, and transaction submission paths. Bring the pool, region, expected filters, and workload so the plan can be sized around the actual control loop.
The official Meteora SDK can construct pool instructions and transactions. Keep construction behind
a policy boundary. Before signing, check:
the pool and token mints match the configured allowlist;
the quote or active bin is no older than the strategy permits;
inventory after the action remains inside a hard limit;
minimum received, maximum spent, and price impact bounds are explicit;
the recent blockhash and fee policy are fresh;
simulation succeeds against an appropriate commitment;
only the intended instructions and accounts are present.
Sign locally. Never put a private key in a stream filter, URL, log line, or support request.
After submission, distinguish these states:
submission-lifecycle.txt
accepted by sender != received by leaderreceived by leader != executedexecuted != confirmedconfirmed != finalized
An accepted response means the sender accepted the bytes and began its configured route policy. It
does not prove inclusion. Track the signature, inspect the execution error, refresh affected
accounts, and make retries idempotent. See
Why Solana Transactions Fail and
Landing Transactions on Solana for the full send and
verification path.
Do not retry a state-dependent DLMM instruction blindly. The active bin, balances, blockhash, and
valid price boundary may have changed since the first attempt. A retry should return to the policy
gate and rebuild from fresh state.
accepted count, landed count, execution errors, expiry, confirmation time
Outcome
inventory drift, realized fees and costs, slippage, strategy-specific risk measures
Keep provider latency separate from local decode, decision, signing, network, and confirmation time.
Otherwise an optimization in one stage can hide a regression in another. A fuller telemetry model
is available in Observability for a Solana Trading Stack.
A Meteora DLMM bot is a stateful Solana application. Bootstrap through RPC and the official Meteora
SDK. Stream the smallest relevant account set through Yellowstone gRPC. Reduce updates idempotently,
detect gaps, reconcile on a schedule, and close the execution gate whenever state is uncertain.
Then build and simulate against fresh state, sign locally, submit through a measured path, and verify
the result independently.
That architecture does not promise a profitable strategy. It gives the strategy a controlled,
observable, and recoverable way to interact with a live DLMM pool.