Что делает этот скрипт. Загружает CPMM pool с RPC, рассчитывает swap с проскальзыванием 0.5%, собирает транзакцию, подписывает её вашим keypair и отправляет. От начала до конца — около 30 строк.
Подготовка
Убедитесь, что вы прочитали Quick start prerequisites и установили RPC_URL, KEYPAIR и необходимые зависимости.
Скрипт
Сохраните как swap.mjs:
// swap.mjs
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import { Raydium, TxVersion, CurveCalculator } from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";
import fs from "node:fs";
// ── Config from env ──────────────────────────────────────────────
const RPC_URL = process.env.RPC_URL ?? "https://api.mainnet-beta.solana.com";
const KEYPAIR = process.env.KEYPAIR ?? `${process.env.HOME}/.config/solana/id.json`;
const POOL_ID = process.env.POOL_ID; // required, base58
const INPUT_MINT = process.env.INPUT_MINT; // required, base58
const AMOUNT_RAW = process.env.AMOUNT_RAW; // required, integer in raw units (e.g. 1_000_000_000 for 1 SOL)
if (!POOL_ID || !INPUT_MINT || !AMOUNT_RAW) {
console.error("Set POOL_ID, INPUT_MINT, AMOUNT_RAW env vars.");
process.exit(1);
}
// ── Setup ────────────────────────────────────────────────────────
const connection = new Connection(RPC_URL, "confirmed");
const owner = Keypair.fromSecretKey(
new Uint8Array(JSON.parse(fs.readFileSync(KEYPAIR, "utf8"))),
);
const raydium = await Raydium.load({
owner,
connection,
cluster: "mainnet",
disableFeatureCheck: true,
blockhashCommitment: "finalized",
});
// ── Load pool ────────────────────────────────────────────────────
const { poolInfo, poolKeys, rpcData } =
await raydium.cpmm.getPoolInfoFromRpc(new PublicKey(POOL_ID));
const baseIn = poolInfo.mintA.address === INPUT_MINT;
// ── Quote ────────────────────────────────────────────────────────
const inputAmount = new BN(AMOUNT_RAW);
const swapResult = CurveCalculator.swap(
inputAmount,
baseIn ? rpcData.baseReserve : rpcData.quoteReserve,
baseIn ? rpcData.quoteReserve : rpcData.baseReserve,
rpcData.configInfo.tradeFeeRate,
);
console.log(`Quote: ${inputAmount.toString()} -> ${swapResult.destinationAmountSwapped.toString()}`);
console.log(`Fee: ${swapResult.tradeFee.toString()}`);
// ── Build and execute ────────────────────────────────────────────
const { execute } = await raydium.cpmm.swap({
poolInfo,
poolKeys,
inputAmount,
swapResult,
slippage: 0.005, // 0.5%
baseIn,
txVersion: TxVersion.V0,
computeBudgetConfig: {
units: 250_000,
microLamports: 50_000,
},
});
const { txId } = await execute({ sendAndConfirm: true });
console.log(`Swap landed: https://solscan.io/tx/${txId}`);
Запуск
Выберите любой CPMM pool, для которого у вас есть ликвидность. Пример с каноническим SOL/USDC CPMM pool:
export POOL_ID="<вставьте ID CPMM pool — найдите в UI Raydium на вкладке Pools>"
export INPUT_MINT="So11111111111111111111111111111111111111112"
export AMOUNT_RAW="10000000" # 0.01 SOL
node swap.mjs
Ожидаемый вывод:
Quote: 10000000 -> 1640000
Fee: 25000
Swap landed: https://solscan.io/tx/4Z9...
Что произошло
Raydium.load инициализировал SDK — загрузил глобальный конфиг и установил контекст вашего кошелька.
getPoolInfoFromRpc получил актуальное состояние pool прямо с RPC (не из API кеша). Для свопов с высокой стоимостью вы всегда хотите свежее состояние.
CurveCalculator.swap рассчитал выход по constant-product за вычетом комиссии pool. Эта же математика запускается на-цепи, поэтому вы можете сравнивать расчеты off- и on-chain.
raydium.cpmm.swap собрал транзакцию в формате V0 (с поддержкой lookup-таблиц адресов) и добавил явную конфигурацию compute-бюджета. Комиссия compute-бюджета помогает транзакции попасть в загруженные периоды.
execute({ sendAndConfirm: true }) подписал, отправил и дождался подтверждения.
Частые ошибки
Pool not found — Неправильный POOL_ID или вы используете неправильный кластер (ID pool’а mainnet’а для devnet RPC и т.д.).
Insufficient funds for transaction — В вашем кошельке недостаточно SOL для input swap + комиссии + аренды ATA.
Slippage tolerance exceeded — Цена в pool изменилась между расчетом и выполнением. Повторите попытку или увеличьте параметр slippage, или используйте computeAmountOut SDK, который всегда заново загружает резервы.
Token account not initialized — ATA выходного токена не существовал и инструкция неявного создания упала по какой-то причине; проверьте баланс SOL в кошельке и попробуйте снова.
Дальше