Meteora DLMM: the concentrated liquidity layer behind Solana token launches
How Meteora's Dynamic Liquidity Market Maker works — bin model, volatility accumulator fees, liquidity shapes (uniform/curve/bid-ask), Alpha Vault anti-snipe, and SDK code examples.
Meteora DLMM is the concentrated liquidity layer that powers the majority of Solana token launches in 2026. When a project migrates from Pump.fun or seeds initial liquidity, the default destination is almost always a Meteora DLMM pool. Understanding why requires understanding what makes DLMM different from a standard constant-product AMM.
This article covers the bin model, how volatility-adaptive fees work, the three liquidity shapes LPs choose between, Alpha Vault's anti-snipe mechanism, and end-to-end TypeScript examples using the official SDK.
The bin model
A traditional AMM (Uniswap v2 / Raydium v4) places liquidity across the entire price range from zero to infinity — most of it nowhere near the current price. Concentrated liquidity protocols (Uniswap v3, Orca Whirlpools) improved on this by letting LPs specify a price range, but they still use a continuous curve within that range, which means computing the exact output requires integration over the curve.
DLMM uses a different abstraction: bins. Each bin covers a discrete price range — for example, bin 1000 might cover SOL prices from $142.00 to $142.14. Liquidity deposited into a bin is only active when the current price is inside that bin. The active bin is the only one executing swaps at any given moment; all other bins are inert.
This has two practical consequences for builders:
- Zero slippage within a bin. Because a bin covers a fixed price range and all liquidity in it trades at the bin's price, small swaps that don't exhaust the active bin are executed at a fixed rate — no curve-based price impact.
- Discrete price steps. When a swap exhausts the active bin, the price jumps to the next bin. The step size is set by the pool's bin step parameter (in basis points). A 10 bps bin step means each bin covers a 0.1% price range. Tighter bin steps mean more precision; wider steps mean larger price jumps per bin crossed.
Bin step and pool parameters
Every DLMM pool is created with a fixed bin step. Common values on Meteora:
- 1–5 bps — stablecoin pairs (USDC/USDT). Tiny price steps; most volume stays in a handful of bins.
- 10–25 bps — major liquid tokens (SOL/USDC). Standard for high-volume pairs.
- 50–100 bps — volatile or low-cap tokens. Wider steps tolerate larger price swings per bin, reducing the number of bins that need to be crossed during volatile periods.
The bin step is immutable after pool creation. Choosing it correctly matters: too tight on a volatile token means LPs quickly end up concentrated in bins far from the current price; too wide on a stable pair increases effective spread unnecessarily.
The volatility accumulator and dynamic fees
Static AMM fees (0.3%, 0.25%, etc.) are a blunt instrument. In calm markets the fee is often too high relative to impermanent loss risk; in volatile markets it's often too low to compensate LPs for the rapid price movement eating their position.
Meteora DLMM solves this with a volatility accumulator. The protocol tracks how many bin crossings have occurred in recent swaps. Each crossing increments the accumulator; the accumulator decays over time. The swap fee is then computed as:
effective_fee = base_fee + variable_fee
variable_fee = variable_fee_control × (volatility_accumulator × bin_step)²In practice: when the price is stable and staying in one bin, the variable fee approaches zero and LPs collect near the base fee. When the price is rapidly crossing many bins (high volatility), the accumulator spikes and the fee rises sharply — exactly when LPs need the most compensation for impermanent loss.
This is a meaningful improvement over fixed-fee pools for LPs on volatile tokens. It doesn't eliminate impermanent loss, but it makes the economics better-calibrated to actual market conditions.
Liquidity shapes
When adding liquidity to a DLMM pool, LPs choose how to distribute tokens across bins. Meteora exposes three built-in shapes, each appropriate for a different market view:
Uniform
Equal liquidity in every bin across the chosen range. Equivalent to a Uniswap v3 position with a flat distribution. Good for: range orders, market-making where you expect the price to oscillate across the entire range without strong trend prediction.
Curve (bell / normal)
Most liquidity concentrated near the current price, tapering off toward the edges of the range. This is the most capital-efficient choice when you believe the price will stay near its current level. Good for: liquid pairs (SOL/USDC) where the price is mean-reverting within a band. This shape maximises fee capture per dollar of capital when your range assumption is correct, and suffers the most when the price trends outside the range.
Bid-Ask
Inverted bell: most liquidity at the outer edges of the range, least near the current price. This is a directional bet: you expect the price to move significantly in one direction and want to accumulate the out-of-range token. Common use case: seeding one side of a new token launch (load all liquidity at prices above current, so as the price rises buyers deplete your token and you accumulate the base). Used extensively in Alpha Vault launch mechanics.
Installing the SDK
npm install @meteora-ag/dlmm @solana/web3.js @solana/spl-tokenCreating a DLMM pool
Pool creation requires choosing the two token mints, the bin step, and the initial active price. Once created, the pool is permissionless — anyone can add or remove liquidity.
import DLMM from "@meteora-ag/dlmm";
import { Connection, PublicKey, Keypair } from "@solana/web3.js";
import BN from "bn.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Token mints
const TOKEN_X = new PublicKey("So11111111111111111111111111111111111111112"); // SOL
const TOKEN_Y = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); // USDC
// Create a new DLMM pool: 10 bps bin step, active price = 142.5 USDC/SOL
const binStep = 10; // 0.1% per bin
const activePrice = 142.5;
const { tx, dlmmPool } = await DLMM.createCustomizablePermissionlessLbPair(
connection,
new BN(binStep),
TOKEN_X,
TOKEN_Y,
// Initial active bin price — SDK computes the bin ID from this
new BN(Math.round(activePrice * 1e6)), // price in Y decimals per X
{ cluster: "mainnet-beta" }
);
// Sign and send the creation transaction
tx.sign([wallet]);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Pool created:", dlmmPool.toBase58());Loading a pool and querying state
import DLMM from "@meteora-ag/dlmm";
import { Connection, PublicKey } from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Known pool address (e.g. SOL/USDC 10bps)
const POOL_ADDRESS = new PublicKey("5rCf1DM8LjKTw4YqhnoLcngyZYeNnQqztScTogYHAS6");
const dlmmPool = await DLMM.create(connection, POOL_ADDRESS);
// Pool state
console.log("Token X mint:", dlmmPool.tokenX.publicKey.toBase58());
console.log("Token Y mint:", dlmmPool.tokenY.publicKey.toBase58());
console.log("Bin step (bps):", dlmmPool.lbPair.binStep);
console.log("Active bin ID:", dlmmPool.lbPair.activeId);
// Get active bin price
const activeBin = await dlmmPool.getActiveBin();
console.log("Active bin price:", activeBin.pricePerToken, "Y per X");
console.log("Active bin reserve X:", activeBin.xAmount.toString());
console.log("Active bin reserve Y:", activeBin.yAmount.toString());Adding liquidity with a curve shape
Adding liquidity requires specifying the range (number of bins on each side of the active bin), the distribution strategy, and the token amounts. The SDK computes the optimal split between X and Y for each bin given the strategy.
import DLMM, { StrategyType } from "@meteora-ag/dlmm";
import BN from "bn.js";
// Load the pool (already created above)
const dlmmPool = await DLMM.create(connection, POOL_ADDRESS);
const activeBin = await dlmmPool.getActiveBin();
// Range: 10 bins on each side of the active bin
const BINS_EACH_SIDE = 10;
const minBinId = activeBin.binId - BINS_EACH_SIDE;
const maxBinId = activeBin.binId + BINS_EACH_SIDE;
// Amounts: 1 SOL + 142.5 USDC (matching current price)
const totalXAmount = new BN(1_000_000_000); // 1 SOL in lamports
const totalYAmount = new BN(142_500_000); // 142.5 USDC (6 decimals)
// Build position with Curve distribution
const createPositionTx = await dlmmPool.initializePositionAndAddLiquidityByStrategy({
positionPubKey: positionKeypair.publicKey,
user: wallet.publicKey,
totalXAmount,
totalYAmount,
strategy: {
maxBinId,
minBinId,
strategyType: StrategyType.SpotBalanced, // Curve (bell-shaped)
},
});
// Add the position keypair as a signer
createPositionTx.sign([wallet, positionKeypair]);
const sig = await connection.sendRawTransaction(createPositionTx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Liquidity added:", sig);Strategy types reference
import { StrategyType } from "@meteora-ag/dlmm";
// Available strategy types:
StrategyType.SpotImBalanced // Uniform distribution across all bins
StrategyType.SpotBalanced // Curve / bell-shaped (most LP near current price)
StrategyType.BidAsk // Inverted bell (most LP at outer edges)
// You can also pass raw bin-by-bin liquidity distribution for custom shapes:
const customBinLiquidityDist = [
{ binId: activeBin.binId - 2, xAmountBpsOfTotal: new BN(1000), yAmountBpsOfTotal: new BN(1000) },
{ binId: activeBin.binId - 1, xAmountBpsOfTotal: new BN(2000), yAmountBpsOfTotal: new BN(2000) },
{ binId: activeBin.binId, xAmountBpsOfTotal: new BN(4000), yAmountBpsOfTotal: new BN(4000) },
{ binId: activeBin.binId + 1, xAmountBpsOfTotal: new BN(2000), yAmountBpsOfTotal: new BN(2000) },
{ binId: activeBin.binId + 2, xAmountBpsOfTotal: new BN(1000), yAmountBpsOfTotal: new BN(1000) },
];Removing liquidity
// List all positions for the wallet in this pool
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(
wallet.publicKey
);
for (const position of userPositions) {
console.log("Position:", position.publicKey.toBase58());
console.log(
"Bins:",
position.positionData.positionBinData.map((b) => b.binId)
);
// Withdraw 100% of liquidity from this position
// BPS_TO_REMOVE = 10000 = 100%
const removeTxs = await dlmmPool.removeLiquidity({
position: position.publicKey,
user: wallet.publicKey,
binIds: position.positionData.positionBinData.map((b) => b.binId),
liquiditiesBpsToRemove: position.positionData.positionBinData.map(
() => new BN(10_000) // 100% per bin
),
shouldClaimAndClose: true, // close position account and claim fees
});
// removeTxs may be an array if position spans many bins (chunked)
for (const tx of Array.isArray(removeTxs) ? removeTxs : [removeTxs]) {
tx.sign([wallet]);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Removed liquidity:", sig);
}
}Swapping through a DLMM pool
Swaps can be routed through the pool directly or via Jupiter (which includes DLMM pools in its routing graph). Direct swaps are useful for programmatic trading or when you specifically want to interact with a single pool.
import BN from "bn.js";
// Quote: swap 1 SOL for USDC
const swapAmount = new BN(1_000_000_000); // 1 SOL in lamports
const swapXtoY = true; // true = sell X (SOL), get Y (USDC)
const binArraysPubkey = await dlmmPool.getBinArrayForSwap(swapXtoY);
const swapQuote = await dlmmPool.swapQuote(
swapAmount,
swapXtoY,
new BN(10), // max slippage bps
binArraysPubkey
);
console.log("Expected out:", swapQuote.outAmount.toString(), "USDC lamports");
console.log(
"Price impact:",
swapQuote.priceImpact.toFixed(4),
"%"
);
console.log("Bins consumed:", swapQuote.binArraysPubkey.length);
// Execute the swap
const swapTx = await dlmmPool.swap({
inToken: dlmmPool.tokenX.publicKey,
binArraysPubkey: swapQuote.binArraysPubkey,
inAmount: swapAmount,
lbPair: dlmmPool.pubkey,
user: wallet.publicKey,
minOutAmount: swapQuote.minOutAmount,
outToken: dlmmPool.tokenY.publicKey,
});
swapTx.sign([wallet]);
const sig = await connection.sendRawTransaction(swapTx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Swap complete:", sig);Claiming fees
Fees accumulate in each bin as swaps cross it. LPs claim fees separately from removing liquidity — a position can claim fees while remaining open.
// Claim accumulated fees for all positions
const { userPositions } = await dlmmPool.getPositionsByUserAndLbPair(
wallet.publicKey
);
for (const position of userPositions) {
const claimFeeTx = await dlmmPool.claimAllFees(
wallet.publicKey,
[{ publicKey: position.publicKey, positionData: position.positionData }]
);
for (const tx of Array.isArray(claimFeeTx) ? claimFeeTx : [claimFeeTx]) {
tx.sign([wallet]);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Fees claimed:", sig);
}
}Alpha Vault: anti-snipe for launches
Alpha Vault is a separate Meteora program layered on top of DLMM pools specifically for token launches. The problem it solves: when a new token launches with a DLMM pool, bots front-run the initial liquidity by purchasing in the same block or the first few seconds, capturing the lowest bins and immediately selling into retail buyers.
Alpha Vault addresses this with a pro-rata purchase cap during a defined pre-launch window. Here's the mechanism:
- A project configures Alpha Vault with: the DLMM pool address, a deposit cap (max USDC/SOL that can be deposited into the vault), and a vesting schedule.
- During the deposit window (before launch), participants deposit SOL or USDC into the vault. Each wallet is limited to a per-wallet cap, preventing any single actor from dominating.
- At launch, the vault uses all collected deposits to buy from the DLMM pool atomically in a single transaction. Because the purchase is aggregated across all depositors and executed as one tx, individual bots can't front-run it — the bot is the vault.
- Purchased tokens vest over the configured schedule. Early exit (before full vesting) incurs a fee that redistributes to remaining depositors.
From a builder perspective: if you're launching a token on Meteora, Alpha Vault is configured at pool creation time via the Meteora Launch platform UI. The SDK also exposes the vault program for custom integrations.
Alpha Vault SDK integration
npm install @meteora-ag/alpha-vaultimport AlphaVault from "@meteora-ag/alpha-vault";
import { Connection, PublicKey } from "@solana/web3.js";
import BN from "bn.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Load the vault for a launch pool
const VAULT_ADDRESS = new PublicKey("YOUR_VAULT_ADDRESS");
const alphaVault = await AlphaVault.create(connection, VAULT_ADDRESS);
// Check vault state
console.log("Deposit cap:", alphaVault.vault.maxBuyingCap.toString());
console.log("Total deposited:", alphaVault.vault.totalDeposit.toString());
console.log("Deposit open:", alphaVault.vault.depositingPoint.toString());
console.log("Deposit close:", alphaVault.vault.startVestingPoint.toString());
// Deposit into the vault (during deposit window)
const depositAmount = new BN(100_000_000); // 100 USDC (6 decimals)
const depositTx = await alphaVault.deposit(depositAmount, wallet.publicKey);
depositTx.sign([wallet]);
const sig = await connection.sendRawTransaction(depositTx.serialize());
await connection.confirmTransaction(sig, "confirmed");
console.log("Deposited:", sig);
// After launch: claim vested tokens
const claimTx = await alphaVault.claimToken(wallet.publicKey);
claimTx.sign([wallet]);
const claimSig = await connection.sendRawTransaction(claimTx.serialize());
await connection.confirmTransaction(claimSig, "confirmed");
console.log("Tokens claimed:", claimSig);Reading pool metrics
For analytics or monitoring, the SDK exposes bin-level data that lets you reconstruct the full liquidity distribution and track volume per bin.
// Fetch bin arrays around the active bin
const activeBin = await dlmmPool.getActiveBin();
const RANGE = 20; // bins each side
const binArrays = await dlmmPool.getBinsBetweenLowerAndUpperBound(
activeBin.binId - RANGE,
activeBin.binId + RANGE
);
// Print liquidity per bin
for (const bin of binArrays.bins) {
const price = dlmmPool.fromPricePerLamport(Number(bin.pricePerToken));
console.log(
`Bin ${bin.binId} | Price: ${price.toFixed(4)} | ` +
`X: ${bin.xAmount.toString()} | Y: ${bin.yAmount.toString()}`
);
}
// Compute total pool TVL
const totalX = binArrays.bins.reduce(
(acc, b) => acc.add(b.xAmount), new BN(0)
);
const totalY = binArrays.bins.reduce(
(acc, b) => acc.add(b.yAmount), new BN(0)
);
console.log("Total SOL in pool:", totalX.toString(), "lamports");
console.log("Total USDC in pool:", totalY.toString(), "micro-USDC");Fetching all Meteora DLMM pools
Meteora exposes a public API for pool discovery — useful for building aggregators, analytics dashboards, or automated LP strategies.
// Fetch all DLMM pools from Meteora's API
const pools = await fetch(
"https://dlmm-api.meteora.ag/pair/all"
).then((r) => r.json());
// Filter for SOL/USDC pools
const SOL_MINT = "So11111111111111111111111111111111111111112";
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const solUsdcPools = pools.filter(
(p: any) =>
(p.mint_x === SOL_MINT && p.mint_y === USDC_MINT) ||
(p.mint_x === USDC_MINT && p.mint_y === SOL_MINT)
);
// Sort by 24h volume
solUsdcPools.sort((a: any, b: any) => b.trade_volume_24h - a.trade_volume_24h);
for (const pool of solUsdcPools.slice(0, 5)) {
console.log(
`Pool ${pool.address.slice(0, 8)}… | ` +
`bin step: ${pool.bin_step} bps | ` +
`24h vol: $${Math.round(pool.trade_volume_24h).toLocaleString()} | ` +
`TVL: $${Math.round(pool.liquidity).toLocaleString()}`
);
}DLMM vs other Solana AMMs
Choosing the right pool for a launch or LP strategy depends on the token's expected behaviour:
- DLMM (Meteora) — best for new launches and volatile tokens. Bin model simplifies concentrated LP; dynamic fees protect LPs during volatile periods; Alpha Vault enables fair launches. Per-bin fee accounting means LPs can see exactly where fees accumulated.
- CLMM (Raydium / Orca Whirlpools) — continuous concentrated liquidity. Better tooling for backtesting strategies; deeper integration with Jupiter routing for established pairs. More complex LP math (requires integrating over a curve to compute position value).
- CPMM (Raydium v4 / constant product) — simplest model, widest compatibility. No concentration, so capital efficiency is lower but impermanent loss risk is spread across the full range. Good for long-tail tokens where you don't want to manage an active bin.
- Stable pools (Meteora / Curve-style) — optimised for pegged assets (stablecoin pairs, LST pairs like mSOL/SOL). Extremely low price impact near the peg; not suitable for directionally traded pairs.
Common pitfalls
- Choosing too tight a bin step for a volatile token. If the bin step is 5 bps and the token moves 20% in a day, your position is out-of-range within hours. Use wider bin steps (50–100 bps) and a wider range for high-volatility tokens.
- Not claiming fees before position changes. Unclaimed fees can accumulate in bins that are no longer active. Claim before adjusting or removing to avoid leaving fees behind.
- Chunked transactions for large positions. The SDK returns an array of transactions for operations spanning many bins. Always check whether the return value is a single transaction or an array and handle both cases.
- Bin array initialisation cost. The first time a bin is used in a pool, its bin array account must be initialised on-chain (rent). This adds a one-time cost of ~0.07 SOL per new bin array (covering 70 bins). Budget for this in pool bootstrapping.
Where to go from here
The Meteora docs cover pool creation, Alpha Vault configuration, and the full parameter reference. The meteora-profit-analysis repo has scripts for backtesting LP strategies against historical bin data. For real-time pool analytics, the DLMM API provides pool state, volume, and fee data without requiring RPC calls. The Meteora Discord has a dedicated #dev channel where the team responds to integration questions.
Keep reading
Jupiter routes every swap through a live graph of Solana's AMMs, splits orders across pools, and executes via versioned transactions with embedded ALTs. Under that is the JLP vault backing perpetuals, an on-chain DCA program, a limit-order crank, and a community-gated token launchpad. Here's the full picture for builders integrating it.
OpenFinance collapses many brokers and chains behind one API key for AI agents: crypto spot and perps, US equities, Polymarket, and DeFi. Solana is one of its three spot & DeFi chains. The notable design choice is non-custodial — the MCP server previews and builds transactions, but the agent signs and submits them. A look at the surface and the trust boundary.
Restaking lets your staked SOL pull double duty: secure Solana, then secure other services for extra yield. Jito coined NCNs, Solayer split endogenous vs exogenous AVS, Fragmetric built it on Token-2022. The model, the three players, and the honest question — where's the real slashing risk and demand?
Get new articles in your inbox
Technical deep-dives on Solana tooling, infrastructure, and ecosystem. No noise.