Documentation Index
Fetch the complete documentation index at: https://docs.meteora.ag/llms.txt
Use this file to discover all available pages before exploring further.
These examples are based on the current ts-client source in the DLMM SDK repository. They focus on transaction construction patterns. For the full method catalog, see the SDK Reference.
Setup
import DLMM, { StrategyType } from "@meteora-ag/dlmm";
import { Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js";
import BN from "bn.js";
const connection = new Connection(process.env.RPC_URL!, "confirmed");
const user = Keypair.fromSecretKey(/* your signer bytes */);
const lbPair = new PublicKey("...");
const pool = await DLMM.create(connection, lbPair, {
cluster: "mainnet-beta",
});
Use cluster: "devnet" for devnet. The SDK defaults to the public DLMM program ID; only pass a custom programId when Meteora has provided a compatible deployment or test harness.
Quote And Swap
The swap flow is:
- Load the pool.
- Fetch bin arrays in the swap direction.
- Quote using current pool state.
- Build the transaction with the quote’s bin arrays and minimum output.
- Sign and send.
const swapYtoX = true;
const inAmount = new BN(5_000_000);
const slippageBps = new BN(50);
const binArrays = await pool.getBinArrayForSwap(swapYtoX, 4);
const quote = pool.swapQuote(
inAmount,
swapYtoX,
slippageBps,
binArrays,
false,
);
const [inToken, outToken] = swapYtoX
? [pool.tokenY.mint.address, pool.tokenX.mint.address]
: [pool.tokenX.mint.address, pool.tokenY.mint.address];
const tx = await pool.swap({
inToken,
outToken,
inAmount: quote.consumedInAmount,
minOutAmount: quote.minOutAmount,
lbPair: pool.pubkey,
user: user.publicKey,
binArraysPubkey: quote.binArraysPubkey,
});
const signature = await sendAndConfirmTransaction(connection, tx, [user]);
Use quote.consumedInAmount when partial fill is enabled. It may be lower than your requested input amount if the pool cannot consume the full input under the quote constraints.
Create A Position And Add Liquidity
For most LP flows, initialize the position and add liquidity in one transaction. The SDK initializes required bin arrays and token accounts when needed.
const activeBin = await pool.getActiveBin();
const position = Keypair.generate();
const minBinId = activeBin.binId - 10;
const maxBinId = activeBin.binId + 10;
const tx = await pool.initializePositionAndAddLiquidityByStrategy({
positionPubKey: position.publicKey,
user: user.publicKey,
totalXAmount: new BN(1_000_000),
totalYAmount: new BN(1_000_000),
strategy: {
minBinId,
maxBinId,
strategyType: StrategyType.Spot,
},
});
const signature = await sendAndConfirmTransaction(connection, tx, [
user,
position,
]);
Use StrategyType.Spot, StrategyType.BidAsk, or StrategyType.Curve depending on the distribution you want. The product-level behavior of these strategies is covered in DLMM Strategies and Use Cases.
Add Liquidity To An Existing Position
const tx = await pool.addLiquidityByStrategy({
positionPubKey: position.publicKey,
user: user.publicKey,
totalXAmount: new BN(500_000),
totalYAmount: new BN(500_000),
strategy: {
minBinId,
maxBinId,
strategyType: StrategyType.Spot,
},
});
await sendAndConfirmTransaction(connection, tx, [user]);
For large ranges, use addLiquidityByStrategyChunkable or the multiple-position helpers documented in the SDK Reference.
Fetch User Positions
const { userPositions } = await pool.getPositionsByUserAndLbPair(
user.publicKey,
);
for (const position of userPositions) {
console.log(position.publicKey.toBase58());
console.log(position.positionData.lowerBinId);
console.log(position.positionData.upperBinId);
}
Use DLMM.getAllLbPairPositionsByUser(connection, user, opt) when you need all DLMM positions for a wallet across pools.
Remove Liquidity, Claim, And Close
removeLiquidity returns one or more transactions because wide positions may need chunking.
const txs = await pool.removeLiquidity({
position: position.publicKey,
user: user.publicKey,
fromBinId: minBinId,
toBinId: maxBinId,
bps: new BN(10_000),
shouldClaimAndClose: true,
});
for (const tx of txs) {
await sendAndConfirmTransaction(connection, tx, [user]);
}
bps: 10_000 removes 100% of liquidity. Set shouldClaimAndClose only when the position should claim accrued fees/rewards and close after the remove flow.
Claim Fees And Rewards
const txs = await pool.claimAllRewardsByPosition({
owner: user.publicKey,
position: position.publicKey,
});
for (const tx of txs) {
await sendAndConfirmTransaction(connection, tx, [user]);
}
Use claimSwapFee, claimAllSwapFee, claimLMReward, claimAllLMRewards, claimAllRewards, or claimAllRewardsByPosition depending on whether you are claiming one position, one reward index, or a wallet-wide set of positions.
Place A Limit Order
Limit orders deposit token X on the ask side and token Y on the bid side. The SDK creates missing bin arrays, initializes bitmap extension accounts when needed, wraps SOL when needed, and builds the place_limit_order instruction.
const limitOrder = Keypair.generate();
await pool.refetchStates();
const activeId = pool.lbPair.activeId;
const isAskSide = true;
const binIds = [activeId + 1, activeId + 20];
const amountPerBin = new BN(1_000_000);
const tx = await pool.placeLimitOrder({
owner: user.publicKey,
sender: user.publicKey,
payer: user.publicKey,
limitOrder: limitOrder.publicKey,
params: {
isAskSide,
relativeBin: null,
bins: binIds.map((id) => ({ id, amount: amountPerBin })),
},
});
await sendAndConfirmTransaction(connection, tx, [user, limitOrder]);
Before placing a limit order into existing bins, check whether the bins already contain pending limit order liquidity on the opposite side:
const { bins } = await pool.getBinsBetweenLowerAndUpperBound(
Math.min(...binIds),
Math.max(...binIds),
);
Cancel by reading non-empty order bins and passing those bin IDs:
const { limitOrderData } = await pool.getLimitOrder(limitOrder.publicKey);
const binIdsToCancel = limitOrderData.limitOrderBinData
.filter((bin) => !bin.empty)
.map((bin) => bin.binId);
const cancelTx = await pool.cancelLimitOrder({
limitOrderPubkey: limitOrder.publicKey,
owner: user.publicKey,
rentReceiver: user.publicKey,
binIds: binIdsToCancel,
});
await sendAndConfirmTransaction(connection, cancelTx, [user]);
Create A Permissionless V2 Pool
Use createLbPair2 for new permissionless pools. It reads PresetParameter2, supports SPL Token and Token-2022, and derives the pool PDA from the preset and token pair.
import { NATIVE_MINT } from "@solana/spl-token";
import { DLMM } from "@meteora-ag/dlmm";
import { PublicKey, sendAndConfirmTransaction } from "@solana/web3.js";
import BN from "bn.js";
const tokenX = new PublicKey("...");
const tokenY = NATIVE_MINT;
const presetParameter2 = new PublicKey("...");
const presetState = await DLMM.getAllPresetParameters(connection, {
cluster: "devnet",
});
const binStep = presetState.presetParameter2.find((preset) =>
preset.publicKey.equals(presetParameter2),
)?.account.binStep;
if (!binStep) {
throw new Error("PresetParameter2 not found");
}
const activeId = new BN(DLMM.getBinIdFromPrice(0.000008, binStep, true));
const tx = await DLMM.createLbPair2(
connection,
user.publicKey,
tokenX,
tokenY,
presetParameter2,
activeId,
{ cluster: "devnet" },
);
await sendAndConfirmTransaction(connection, tx, [user]);
Use createCustomizablePermissionlessLbPair2 when the creator must configure activation behavior, base fee parameters, ConcreteFunctionType, or CollectFeeMode.