Get started with Cleopetra’s LP APIs in minutes. This guide walks you through finding the best pools, creating positions, and managing fees with copy-paste code snippets.
npm install @solana/web3.js bs58
import { Connection, Keypair, VersionedTransaction, LAMPORTS_PER_SOL, } from "@solana/web3.js"; import base58 from "bs58"; const API_KEY = "your-cleo-api-key"; const PRIVATE_KEY = "your-private-key-here"; // string const API_URL = "https://api.cleopetra.fun"; const RPC_URL = "your-solana-rpc-url"; const connection = new Connection(RPC_URL); const keypair = Keypair.fromSecretKey(base58.decode(PRIVATE_KEY)); const sendRequest = async (route: string, params: any) => { const response = await fetch(`${API_URL}${route}`, { method: route.includes('positions') || route.includes('pools') ? 'GET' : 'POST', headers: { Authorization: `Bearer ${API_KEY}`, Accept: "application/json", "Content-Type": "application/json", }, body: route.includes('positions') || route.includes('pools') ? undefined : JSON.stringify(params), }); if (!response.ok) { throw new Error(`API Error: ${response.status}`); } return await response.json(); };
const sendTransaction = async ( base64Txns: string[], positions: string[] = [], positionNftMintKp?: string ) => { const signatures: string[] = []; for (const [i, base64Txn] of base64Txns.entries()) { try { const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); const transaction = VersionedTransaction.deserialize( Buffer.from(base64Txn, "base64") ); // Update txn with latest blockhash transaction.message.recentBlockhash = blockhash; const signers: Keypair[] = [keypair]; // Add position keypair if provided if (positions && positions.length > 0) { for (const position of positions) { if (position) { const positionKp = Keypair.fromSecretKey( Buffer.from(position, "base64") ); signers.push(positionKp); } } } // Add NFT mint keypair for DAMMv2 if (positionNftMintKp) { const nftKp = Keypair.fromSecretKey( Buffer.from(positionNftMintKp, "base64") ); signers.push(nftKp); } // Sign and send transaction.sign(signers); const signature = await connection.sendTransaction(transaction); await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight, }, "confirmed"); console.log(`Transaction ${i + 1} confirmed:`, signature); signatures.push(signature); } catch (error) { console.error(`Failed to send transaction ${i + 1}:`, error); } } return signatures; };
const discoverPools = async () => { const tokenMint = "9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump"; // Fartcoin const queryParams = new URLSearchParams({ token: tokenMint, programs: "dlmm,dammv2", order_by: "desc", sort_by: "volume" }); const pools = await sendRequest(`/pools/token?${queryParams}`, null); console.log("Available pools:", pools.slice(0, 3)); // Choose the top DLMM pool const topDlmmPool = pools.find(pool => pool.type === "Dlmm"); if (!topDlmmPool) { throw new Error("No DLMM pool found for this token"); } console.log("Selected pool:", { address: topDlmmPool.address, tvl: topDlmmPool.tvl, volume24h: topDlmmPool.volume_24h, apr: topDlmmPool.apr }); return topDlmmPool; };
const createPosition = async (pool: any) => { // Calculate price range (5% above and below current price) const currentPrice = pool.price; const minPrice = currentPrice * 0.95; const maxPrice = currentPrice * 1.05; const params = { user: keypair.publicKey.toString(), mode: "zap", // Automatically converts SOL to required tokens pool: pool.address, strategy: "spot", amount_sol: (1 * LAMPORTS_PER_SOL).toString(), // 1 SOL min_price: minPrice, max_price: maxPrice, slippage: 500, // 5% slippage }; console.log("Creating position with params:", params); const result = await sendRequest("/dlmm/initialize", params); console.log("Position creation result:", result); if (result?.transactions) { const signatures = await sendTransaction( result.transactions, result.positions ); console.log("Position created successfully!"); return signatures[0]; } throw new Error("Failed to create position"); };
const fetchPositions = async () => { const queryParams = new URLSearchParams({ user: keypair.publicKey.toString(), programs: "dlmm,dammv2", limit: "10", page: "1" }); const result = await sendRequest(`/positions?${queryParams}`, null); console.log(`Found ${result.total} positions:`, result.positions); if (result.positions.length === 0) { throw new Error("No positions found for this wallet"); } return result.positions; };
const claimFees = async (positions: any[]) => { const firstPosition = positions[0]; console.log("Claiming fees for position:", firstPosition.position_address); console.log("Unclaimed fees (USD):", firstPosition.total_unclaimed_fees_usd); if (firstPosition.total_unclaimed_fees_usd === 0) { console.log("No fees to claim for this position"); return; } const params = { user: keypair.publicKey.toString(), mode: "zap", // Convert claimed tokens to SOL if needed position: firstPosition.position_address, slippage: 500, }; const result = await sendRequest("/dlmm/claim", params); console.log("Claim result:", result); if (result?.transactions) { const signatures = await sendTransaction(result.transactions); console.log("Fees claimed successfully!"); return signatures; } throw new Error("Failed to claim fees"); };
const main = async () => { try { console.log("🔍 Step 1: Discovering pools..."); const selectedPool = await discoverPools(); console.log("\n💰 Step 2: Creating DLMM position..."); await createPosition(selectedPool); // Wait a moment for the position to be indexed console.log("\n⏳ Waiting for position indexing..."); await new Promise(resolve => setTimeout(resolve, 5000)); console.log("\n📊 Step 3: Fetching positions..."); const positions = await fetchPositions(); console.log("\n🎯 Step 4: Claiming LP fees..."); await claimFees(positions); console.log("\n✅ Workflow completed successfully!"); } catch (error) { console.error("❌ Workflow failed:", error); } }; main();