The engine that powers the Internet Capital Market on Solana.
pnpm add @saros-finance/dlmm-sdkyarn add @saros-finance/dlmm-sdknpm install @saros-finance/dlmm-sdkSarosDLMM- Static factory class for protocol-level operations (creating pairs, discovering pools)SarosDLMMPair- Pair instance class with operations for a specific pair/pool
import { Connection, Keypair } from '@solana/web3.js';
import { SarosDLMM, MODE } from '@saros-finance/dlmm-sdk';
const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');
const wallet = "WALLET_KEYPAIR"
const sdk = new SarosDLMM({
mode: MODE.MAINNET, // or MODE.DEVNET
connection
});import { PublicKey } from '@solana/web3.js';
const { transaction, pair, activeBin } = await sdk.createPair({
tokenX: {
mintAddress: new PublicKey('TokenX_MINT_ADDRESS'),
decimals: 9,
},
tokenY: {
mintAddress: new PublicKey('TokenY_MINT_ADDRESS'),
decimals: 6,
},
binStep: 1, // Price step between bins (1 = 0.01% steps)
ratePrice: 0.000002, // Initial price of tokenX in terms of tokenY
payer: wallet.publicKey,
});
// Sign and send the transaction
const signature = await connection.sendTransaction(transaction, [wallet]);
await connection.confirmTransaction(signature);
console.log(`Pool created: ${pair.toString()}`);
console.log(`Active bin: ${activeBin}`);const pairAddress = new PublicKey('PAIR_ADDRESS');
const pair = await sdk.getPair(pairAddress);
// Get pool metadata (price, fees, tokens, etc.)
const { tokenX, tokenY, binStep, baseFee, dynamicFee, activePrice } = pair.getPairMetadata();import { Keypair } from '@solana/web3.js';
// Generate a unique position mint
const positionKeypair = Keypair.generate();
// Create position spanning bins from -5 to +5 around the active bin
const createTx = await pair.createPosition({
binRange: [-5, 5],
payer: wallet.publicKey,
positionMint: positionKeypair.publicKey,
});
const sig = await connection.sendTransaction(createTx, [wallet, positionKeypair]);
await connection.confirmTransaction(sig);import { LiquidityShape } from '@saros-finance/dlmm-sdk';
const addTx = await pair.addLiquidityByShape({
positionMint: positionKeypair.publicKey,
payer: wallet.publicKey,
amountTokenX: 1_000_000_000n, // Amount in token's smallest unit
amountTokenY: 1_000_000n,
liquidityShape: LiquidityShape.Spot, // Uniform distribution
binRange: [-5, 5],
});
const sig = await connection.sendTransaction(addTx, [wallet]);
await connection.confirmTransaction(sig);Liquidity Shapes:
LiquidityShape.Spot- Uniform distribution across the rangeLiquidityShape.Curve- Bell curve, concentrated at active binLiquidityShape.BidAsk- Concentrated at range edges
// Get all user positions for this pair
const positions = await pair.getUserPositions({
payer: wallet.publicKey,
});
// Get reserves for each position
for (const position of positions) {
console.log(`Position: ${position.positionMint.toString()}`);
console.log(`Bin range: ${position.lowerBinId} to ${position.upperBinId}`);
const reserves = await pair.getPositionReserves(position.position);
reserves.forEach((bin) => {
if (bin.reserveX > 0n || bin.reserveY > 0n) {
console.log({
binId: bin.binId,
reserveX: bin.reserveX,
reserveY: bin.reserveY,
liquidityShare: bin.liquidityShare,
});
}
});
}import { RemoveLiquidityType } from '@saros-finance/dlmm-sdk';
const { setupTransaction, transactions, cleanupTransaction, closedPositions } =
await pair.removeLiquidity({
positionMints: [positionKeypair.publicKey],
payer: wallet.publicKey,
type: RemoveLiquidityType.All, // All, TokenX, or TokenY
});
// Execute transactions in order
if (setupTransaction) {
await connection.sendTransaction(setupTransaction, [wallet]);
}
for (const tx of transactions) {
await connection.sendTransaction(tx, [wallet]);
}
if (cleanupTransaction) {
await connection.sendTransaction(cleanupTransaction, [wallet]);
}
console.log(`Closed positions: ${closedPositions.length}`);const quote = await pair.getQuote({
amount: 1_000_000n,
options: {
swapForY: true, // true = X→Y, false = Y→X
isExactInput: true, // true = exact input, false = exact output
},
slippage: 1, // 1% slippage tolerance
});
console.log({
amountIn: quote.amountIn,
amountOut: quote.amountOut,
minTokenOut: quote.minTokenOut, // Use this for slippage protection
priceImpact: quote.priceImpact,
});const swapTx = await pair.swap({
tokenIn: pair.getPairMetadata().tokenX.mintAddress,
tokenOut: pair.getPairMetadata().tokenY.mintAddress,
amount: 1_000_000n,
options: {
swapForY: true,
isExactInput: true,
},
minTokenOut: quote.minTokenOut, // Slippage protection from quote
payer: wallet.publicKey,
});
const sig = await connection.sendTransaction(swapTx, [wallet]);
await connection.confirmTransaction(sig);Swap Modes:
- Exact Input: Spend exactly
amountof tokenIn, receive variable tokenOut - Exact Output: Receive exactly
amountof tokenOut, spend variable tokenIn
// Get all pool addresses
const allPools = await sdk.getAllPairAddresses();
// Find pools for a specific token
const usdcPools = await sdk.findPairs(
new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v') // USDC
);
// Find pools for a specific token pair
const solUsdcPools = await sdk.findPairs(
new PublicKey('So11111111111111111111111111111111111111112'), // SOL
new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v') // USDC
);
// Fetch multiple pairs at once
const pairs = await sdk.getPairs([
new PublicKey('PAIR_1'),
new PublicKey('PAIR_2'),
]);If a pool has rewards enabled, you can claim accumulated rewards from your positions:
// Check if the pool has rewards
const hookInfo = await pair.getHookAccount();
if (!hookInfo || !hookInfo.rewardTokenMint) {
throw new Error('This pool has no reward token');
}
// Claim rewards for a position
const claimTx = await pair.claimReward({
payer: wallet.publicKey,
positionMint: positionKeypair.publicKey,
rewardTokenMint: hookInfo.rewardTokenMint,
});
const sig = await connection.sendTransaction(claimTx, [wallet]);
await connection.confirmTransaction(sig);