Saltar al contenido principal

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

Esta página fue traducida automáticamente por IA. La versión en inglés es la fuente autorizada.Ver versión en inglés →
Banner de versión. Todas las demostraciones se orientan a @raydium-io/raydium-sdk-v2@0.2.42-alpha contra la red principal de Solana (mainnet-beta), verificado 2026-04. ID del programa: 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 (ver reference/program-addresses).
La creación de nuevos pools no se muestra aquí. La interfaz de Raydium ya no ofrece creación de pools AMM v4 — los nuevos pares por defecto van a CPMM. El programa AMM v4 en sí todavía acepta Initialize2 en cadena; simplemente no es la ruta recomendada. Las demostraciones a continuación cubren las operaciones de pool activo que todo integrador necesita: swap, depósito, retiro.

Configuración

import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import fs from "node:fs";

const connection = new Connection(process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
const raydium = await Raydium.load({ owner, connection, cluster: "mainnet" });

Obtener un pool por ID

import { PublicKey } from "@solana/web3.js";

const poolId = new PublicKey("<AMM_V4_POOL_ID>");

// Extrae el objeto del pool normalizado por el SDK. Para AMM v4, esto incluye las cuentas
// de OpenBook que los constructores de instrucciones necesitarán.
const data = await raydium.liquidity.getPoolInfoFromRpc({ poolId });
const { poolInfo, poolKeys, poolRpcData } = data;

console.log("Par:", poolInfo.mintA.symbol, "/", poolInfo.mintB.symbol);
console.log("Versión:", poolInfo.version);       // 4 para AMM v4
console.log("Mercado:", poolKeys.marketId.toBase58());
poolKeys es la estructura que consumen los constructores de instrucciones. Contiene todas las cuentas AMM v4 y OpenBook en el orden que el programa espera.

Swap (entrada en base)

import BN from "bn.js";

const amountIn = new BN(1_000_000);            // 1 USDC (cotización de 6 decimales)
const inputMint = new PublicKey(poolInfo.mintB.address);  // USDC
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountOut({
  poolInfo,
  amountIn,
  mintIn: inputMint,
  mintOut: new PublicKey(poolInfo.mintA.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn,
  amountOut: computed.minAmountOut,
  fixedSide: "in",
  inputMint,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Tx de swap:", txId);
El SDK añade automáticamente cada cuenta de OpenBook. No intentes sustituirlas manualmente — el programa valida cada posición.

Swap (salida en base)

const amountOut = new BN(1_000_000_000);       // 1 SOL (base de 9 decimales)
const slippage  = 0.005;

const computed = raydium.liquidity.computeAmountIn({
  poolInfo,
  amountOut,
  mintOut: new PublicKey(poolInfo.mintA.address),
  mintIn: new PublicKey(poolInfo.mintB.address),
  slippage,
});

const { execute } = await raydium.liquidity.swap({
  poolInfo,
  poolKeys,
  amountIn: computed.maxAmountIn,
  amountOut,
  fixedSide: "out",
  inputMint: new PublicKey(poolInfo.mintB.address),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });

Agregar liquidez

const amountA = new BN(100_000_000);           // 0.1 SOL

const { anotherAmount, maxAnotherAmount } = raydium.liquidity.computePairAmount({
  poolInfo,
  amount: amountA,
  baseIn: true,
  slippage: 0.01,
});

const { execute } = await raydium.liquidity.addLiquidity({
  poolInfo,
  poolKeys,
  amountInA: amountA,
  amountInB: maxAnotherAmount,
  fixedSide: "a",
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
fixedSide: "a" le indica al SDK que suministraste el amountInA exacto y que amountInB debe ser como máximo maxAnotherAmount. La liquidez en libro del pool se liquida antes del cálculo prorrateado para que la ratio de depósito coincida con las reservas más recientes.

Retirar liquidez

const lpAmount = new BN(50_000);               // LP a quemar

const { execute } = await raydium.liquidity.removeLiquidity({
  poolInfo,
  poolKeys,
  lpAmount,
  baseAmountMin: new BN(0),
  quoteAmountMin: new BN(0),
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
Los mínimos de slippage protegen contra cambios en el estado del pool entre tu precotización y el momento de la ejecución.

Sintonización de unidad de cálculo / tarifa de prioridad

Los swaps de AMM v4 son intensivos en cálculo porque cada instrucción valida el estado completo de OpenBook. Un swap típico usa 180k–250k CU dependiendo de cuántas órdenes abiertas necesiten liquidarse en el camino. Siempre especifica un límite de unidad de cálculo:
import { ComputeBudgetProgram } from "@solana/web3.js";

const { execute, innerTransactions } = await raydium.liquidity.swap({
  /* ...params... */
  computeBudgetConfig: {
    units: 400_000,
    microLamports: 50_000,       // tarifa de prioridad
  },
});
Si omites computeBudgetConfig, el SDK aún puede usar su propio valor por defecto; inspecciona innerTransactions para confirmar. Ver integration-guides/priority-fee-tuning.

CPI directo de Rust

Si debes hacer CPI en AMM v4 desde tu propio programa Anchor, necesitarás modelar la lista de cuentas de SwapBaseIn exactamente. Un boceto mínimo:
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program::invoke_signed;
use anchor_lang::solana_program::instruction::Instruction;

const AMM_V4_PROGRAM_ID: Pubkey = pubkey!("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");

#[derive(Accounts)]
pub struct ProxyAmmV4Swap<'info> {
    /// CHECK:
    pub token_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm:          UncheckedAccount<'info>,
    /// CHECK:
    pub amm_authority: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_open_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub amm_target_orders: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_coin_token_account: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub pool_pc_token_account: UncheckedAccount<'info>,
    /// CHECK:
    pub market_program: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_bids: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_asks: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_event_queue: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_coin_vault: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub market_pc_vault: UncheckedAccount<'info>,
    /// CHECK:
    pub market_vault_signer: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_source: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_dest: UncheckedAccount<'info>,
    pub user_owner: Signer<'info>,
}

pub fn proxy_swap(
    ctx: Context<ProxyAmmV4Swap>,
    amount_in: u64,
    minimum_amount_out: u64,
) -> Result<()> {
    // El discriminador de instrucción para SwapBaseIn es 9 en AMM v4.
    let mut data = vec![9u8];
    data.extend_from_slice(&amount_in.to_le_bytes());
    data.extend_from_slice(&minimum_amount_out.to_le_bytes());

    let ix = Instruction {
        program_id: AMM_V4_PROGRAM_ID,
        accounts: vec![
            AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
            AccountMeta::new(ctx.accounts.amm.key(), false),
            AccountMeta::new_readonly(ctx.accounts.amm_authority.key(), false),
            AccountMeta::new(ctx.accounts.amm_open_orders.key(), false),
            AccountMeta::new(ctx.accounts.amm_target_orders.key(), false),
            AccountMeta::new(ctx.accounts.pool_coin_token_account.key(), false),
            AccountMeta::new(ctx.accounts.pool_pc_token_account.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_program.key(), false),
            AccountMeta::new(ctx.accounts.market.key(), false),
            AccountMeta::new(ctx.accounts.market_bids.key(), false),
            AccountMeta::new(ctx.accounts.market_asks.key(), false),
            AccountMeta::new(ctx.accounts.market_event_queue.key(), false),
            AccountMeta::new(ctx.accounts.market_coin_vault.key(), false),
            AccountMeta::new(ctx.accounts.market_pc_vault.key(), false),
            AccountMeta::new_readonly(ctx.accounts.market_vault_signer.key(), false),
            AccountMeta::new(ctx.accounts.user_source.key(), false),
            AccountMeta::new(ctx.accounts.user_dest.key(), false),
            AccountMeta::new_readonly(ctx.accounts.user_owner.key(), true),
        ],
        data,
    };
    invoke_signed(&ix, &ctx.accounts.to_account_infos(), &[])?;
    Ok(())
}
AMM v4 no incluye una caja Anchor para CPI. El boceto anterior utiliza una Instruction construida manualmente.

Trampas comunes

  • Falta una cuenta de OpenBook. Las 8 cuentas del lado de OpenBook son obligatorias en cada swap, depósito y retiro; el SDK se encarga de esto, pero las instrucciones construidas manualmente a menudo no.
  • Leer saldos brutos de bóvedas. No refleja montos en custodia en libro o PnL acumulado. Usa la cotización del SDK o api-v3.raydium.io/pools/info/ids.
  • Cola de eventos de OpenBook llena. Un pool puede revertir swaps con SerumOrderError cuando la cola de eventos de su mercado necesita ser procesada. El procesamiento es permissionless (MonitorStep en las cuentas OpenBook del mercado).
  • Mints de Token-2022. No soportados. Un pool AMM v4 no puede crearse contra un mint de Token-2022; cualquier par de Token-2022 debe estar en CPMM o CLMM.

Por dónde continuar

Fuentes: