import { Keypair, Transaction, VersionedTransaction } from "@solana/web3.js";
import bs58 from "bs58";
const API_BASE = "https://api.lpagent.io/open-api/v1";
const API_KEY = "your-api-key-here";
const wallet = Keypair.fromSecretKey(bs58.decode("your-base58-private-key"));
const OWNER = wallet.publicKey.toBase58();
async function apiCall(method: string, path: string, body?: object) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: { "Content-Type": "application/json", "x-api-key": API_KEY },
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) throw new Error(`API error: ${await res.text()}`);
return res.json();
}
function signTx(base64Tx: string): string {
const buffer = Buffer.from(base64Tx, "base64");
try {
const tx = VersionedTransaction.deserialize(buffer);
tx.sign([wallet]);
return Buffer.from(tx.serialize()).toString("base64");
} catch {
const tx = Transaction.from(buffer);
tx.partialSign(wallet);
return tx.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
}
}
// ===================== ZAP-IN =====================
async function zapIn(poolAddress: string, amountSOL: number) {
// Get pool info for active bin
const info = await apiCall("GET", `/pools/${poolAddress}/info`);
const activeBin = info.data.liquidityViz?.activeBin;
const fromBinId = activeBin.binId - 34;
const toBinId = activeBin.binId + 34;
// Generate zap-in transactions
const addTx = await apiCall("POST", `/pools/${poolAddress}/add-tx`, {
stratergy: "Spot",
inputSOL: amountSOL,
percentX: 0.5,
fromBinId,
toBinId,
owner: OWNER,
slippage_bps: 500,
mode: "zap-in",
});
// Sign and land via Jito
const signedSwapTxs = addTx.data.swapTxsWithJito.map(signTx);
const signedAddTxs = addTx.data.addLiquidityTxsWithJito.map(signTx);
const result = await apiCall("POST", "/pools/landing-add-tx", {
lastValidBlockHeight: addTx.data.lastValidBlockHeight,
swapTxsWithJito: signedSwapTxs,
addLiquidityTxsWithJito: signedAddTxs,
meta: addTx.data.meta,
});
console.log(`Zap-In done: https://solscan.io/tx/${result.data.signature}`);
return addTx.data.meta.positionPubKey;
}
// ===================== ZAP-OUT =====================
async function zapOut(positionId: string, bps: number = 10000) {
// Generate zap-out transactions
const decreaseTx = await apiCall("POST", "/position/decrease-tx", {
position_id: positionId,
bps,
owner: OWNER,
slippage_bps: 500,
output: "both",
});
// Sign and land via Jito
const signedCloseTxs = decreaseTx.data.closeTxsWithJito.map(signTx);
const signedSwapTxs = decreaseTx.data.swapTxsWithJito.map(signTx);
const result = await apiCall("POST", "/position/landing-decrease-tx", {
lastValidBlockHeight: decreaseTx.data.lastValidBlockHeight,
closeTxs: [],
swapTxs: [],
closeTxsWithJito: signedCloseTxs,
swapTxsWithJito: signedSwapTxs,
});
console.log(`Zap-Out done: https://solscan.io/tx/${result.data.signature}`);
}
// ===================== MAIN =====================
async function main() {
// Discover a pool
const discover = await apiCall("GET", "/pools/discover?" + new URLSearchParams({
chain: "SOL", sortBy: "vol_24h", sortOrder: "desc", pageSize: "1",
}));
const pool = discover.data[0];
console.log(`Pool: ${pool.token0_symbol}/${pool.token1_symbol}`);
// Zap-In: add 0.1 SOL of liquidity
const positionKey = await zapIn(pool.pool, 0.1);
console.log(`Position created: ${positionKey}`);
// Check position status
const positions = await apiCall("GET", `/lp-positions/opening?owner=${OWNER}`);
const position = positions.data.find((p: any) => p.pool === pool.pool);
if (position) {
console.log(`Position value: $${position.currentValue}, PnL: ${position.pnl.percent}%`);
// Zap-Out: withdraw 100%
await zapOut(position.id, 10000);
}
}
main().catch(console.error);