| name | meteora-dlmm |
| description | Interact with Meteora DLMM on Solana. Swap tokens and provide concentrated liquidity. |
Meteora DLMM Skill
Requires Phantom MCP. Before any action, call get_connection_status. If the tool is not available in your session, show the user this setup and stop:
{ "mcpServers": { "phantom": { "command": "npx", "args": ["-y", "@phantom/mcp-server@latest"] } } }
Add this to your client MCP config (Claude Code example path: ~/.claude/claude_desktop_config.json), then reload your client and sign in with Google or Apple.
Enables AI agents to interact with Meteora DLMM (program: LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo).
Setup (once per session)
mkdir -p /tmp/phantom-idl-fetch && cd /tmp/phantom-idl-fetch && echo '{"type":"module"}' > package.json && npm install @coral-xyz/anchor@0.29.0 @coral-xyz/anchor-new@npm:@coral-xyz/anchor@latest @solana/web3.js @solana/spl-token --save-quiet && echo "Ready"
Available Actions
Swap
Swap tokens in a Meteora DLMM pool.
Uses buy_token directly — Phantom routes through DLMM automatically.
What to ask the user for:
sellTokenMint: Solana address — token mint to sell (or use sellTokenIsNative: true for SOL)
buyTokenMint: Solana address — token mint to buy (or use buyTokenIsNative: true for SOL)
amount: number — amount to sell in base units (lamports for SOL)
slippageTolerance: number — max slippage as a percentage, 0–100 (default: 0.5 = 0.5%)
To execute:
- Call
get_connection_status — if not authenticated, call phantom_login first.
- Call
get_wallet_addresses to get the user's Solana address.
- Call
get_token_balances to confirm sufficient balance.
- Call
buy_token directly:
buy_token({
sellTokenMint: "<SELL_TOKEN_MINT>",
buyTokenMint: "<BUY_TOKEN_MINT>",
amount: <AMOUNT_IN_BASE_UNITS>,
slippageTolerance: 0.5,
execute: true
})
Add Liquidity By Strategy
Deposits tokens into a DLMM position using a distribution strategy.
What to ask the user for:
lb_pair: Solana address — the DLMM pool
position: Solana address — existing position account
amount_x / amount_y: numbers — token amounts to deposit in lamports
strategy_type: spotBalanced, curveBalanced, or bidAskBalanced
min_bin_id / max_bin_id: numbers — bin range matching the position
user_token_x / user_token_y: Solana addresses — user's token accounts
Run the pool helper to get reserve_x/y, token_x/y_mint, bin_array_lower/upper, active_id:
import anchor from "@coral-xyz/anchor-new";
const { Program, AnchorProvider, BN } = anchor;
import { Connection, PublicKey, clusterApiUrl } from "@solana/web3.js";
const PROGRAM_ID = new PublicKey("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo");
const LB_PAIR = new PublicKey(process.argv[2]);
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
const provider = new AnchorProvider(connection, { publicKey: PublicKey.default }, {});
const idl = await Program.fetchIdl(PROGRAM_ID, provider);
const program = new Program(idl, provider);
const pool = await program.account.lbPair.fetch(LB_PAIR);
const BIN_ARRAY_SIZE = 70;
const lowerIdx = Math.floor(pool.activeId / BIN_ARRAY_SIZE);
const [binArrayLower] = PublicKey.findProgramAddressSync([Buffer.from("bin_array"), LB_PAIR.toBuffer(), Buffer.from(new BN(lowerIdx).toArray("le", 8))], PROGRAM_ID);
const [binArrayUpper] = PublicKey.findProgramAddressSync([Buffer.from("bin_array"), LB_PAIR.toBuffer(), Buffer.from(new BN(lowerIdx + 1).toArray("le", 8))], PROGRAM_ID);
console.log(JSON.stringify({ reserveX: pool.reserveX.toBase58(), reserveY: pool.reserveY.toBase58(), tokenXMint: pool.tokenXMint.toBase58(), tokenYMint: pool.tokenYMint.toBase58(), activeId: pool.activeId, binArrayLower: binArrayLower.toBase58(), binArrayUpper: binArrayUpper.toBase58() }, null, 2));
To execute:
- Call
get_connection_status — if not authenticated, call phantom_login first.
- Call
get_wallet_addresses and get_token_balances.
- Run the pool helper, then build the transaction:
import anchor from "@coral-xyz/anchor-new";
const { Program, AnchorProvider, BN } = anchor;
import { Connection, PublicKey, clusterApiUrl } from "@solana/web3.js";
const [,, userWallet, position, lbPair, amountX, amountY, activeId, minBinId, maxBinId, strategyType, userTokenX, userTokenY, reserveX, reserveY, tokenXMint, tokenYMint, binArrayLower, binArrayUpper] = process.argv;
const PROGRAM_ID = new PublicKey("LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo");
const USER = new PublicKey(userWallet);
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
const provider = new AnchorProvider(connection, { publicKey: USER }, {});
const idl = await Program.fetchIdl(PROGRAM_ID, provider);
const program = new Program(idl, provider);
const [eventAuthority] = PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], PROGRAM_ID);
const tx = await program.methods.addLiquidityByStrategy({
amountX: new BN(amountX), amountY: new BN(amountY), activeId: parseInt(activeId),
maxActiveBinSlippage: 10,
strategyParameters: { minBinId: parseInt(minBinId), maxBinId: parseInt(maxBinId), strategyType: { [strategyType]: {} }, parameters: Buffer.alloc(64) },
}).accounts({
position: new PublicKey(position), lbPair: new PublicKey(lbPair), binArrayBitmapExtension: null,
userTokenX: new PublicKey(userTokenX), userTokenY: new PublicKey(userTokenY),
reserveX: new PublicKey(reserveX), reserveY: new PublicKey(reserveY),
tokenXMint: new PublicKey(tokenXMint), tokenYMint: new PublicKey(tokenYMint),
binArrayLower: new PublicKey(binArrayLower), binArrayUpper: new PublicKey(binArrayUpper),
sender: USER,
tokenXProgram: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
tokenYProgram: new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
eventAuthority, program: PROGRAM_ID,
}).transaction();
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.feePayer = USER;
console.log(tx.serialize({ requireAllSignatures: false }).toString("base64"));
- Send (first call simulates, second call with
confirmed: true sends):
send_solana_transaction({ transaction: "<base64>" })
Review the simulation preview, then:
send_solana_transaction({ transaction: "<base64>", confirmed: true })
Adding Payments with CASH
Gate any action behind a CASH payment using pay_api_access before executing the instruction.