Passer au contenu 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.

Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
Banneau de version. Toutes les démos ciblent @raydium-io/raydium-sdk-v2@0.2.42-alpha sur Solana mainnet-beta, vérifiées en 2026-04. ID du programme : 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8 (voir reference/program-addresses).
La création de nouveaux pools n’est pas montrée ici. L’interface Raydium ne propose plus la création de pools AMM v4 — les nouvelles paires utilisent par défaut CPMM. Le programme AMM v4 lui-même accepte toujours Initialize2 on-chain ; ce n’est simplement pas la voie recommandée. Les démos ci-dessous couvrent les opérations sur pools actifs que tout intégrateur doit maîtriser : swap, dépôt, retrait.

Configuration

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" });

Récupérer un pool par ID

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

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

// Récupère l'objet pool normalisé par le SDK. Pour AMM v4, cela inclut les
// comptes OpenBook que les constructeurs d'instructions utiliseront.
const data = await raydium.liquidity.getPoolInfoFromRpc({ poolId });
const { poolInfo, poolKeys, poolRpcData } = data;

console.log("Pair:", poolInfo.mintA.symbol, "/", poolInfo.mintB.symbol);
console.log("Version:", poolInfo.version);       // 4 pour AMM v4
console.log("Market:", poolKeys.marketId.toBase58());
poolKeys est la structure que les constructeurs d’instructions consomment. Elle contient chaque compte AMM v4 et OpenBook dans l’ordre attendu par le programme.

Swap (base entrante)

import BN from "bn.js";

const amountIn = new BN(1_000_000);            // 1 USDC (quote à 6 décimales)
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("Swap tx:", txId);
Le SDK ajoute automatiquement chaque compte OpenBook. N’essayez pas de les substituer à la main — le programme valide chaque emplacement.

Swap (base sortante)

const amountOut = new BN(1_000_000_000);       // 1 SOL (base à 9 décimales)
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 });

Ajouter de la liquidité

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" indique au SDK que vous avez fourni l’amountInA exact et que amountInB doit être au maximum maxAnotherAmount. La liquidité on-book du pool est réglée avant le calcul pro-rata, de sorte que le ratio de dépôt correspond aux réserves les plus fraîches.

Retirer de la liquidité

const lpAmount = new BN(50_000);               // LP à brûler

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

await execute({ sendAndConfirm: true });
Les minimums de slippage protègent contre le changement d’état du pool entre votre pré-citation et le temps d’atterrissage.

Ajustement des frais de priorité et des unités de calcul

Les swaps AMM v4 sont gourmands en calcul car chaque instruction valide l’état complet d’OpenBook. Un swap typique utilise 180 k–250 k CU selon le nombre de commandes ouvertes à régler en cours de route. Toujours fournir une limite d’unités de calcul :
import { ComputeBudgetProgram } from "@solana/web3.js";

const { execute, innerTransactions } = await raydium.liquidity.swap({
  /* ...params... */
  computeBudgetConfig: {
    units: 400_000,
    microLamports: 50_000,       // frais de priorité
  },
});
Si vous omettez computeBudgetConfig, le SDK peut toujours utiliser sa propre valeur par défaut ; inspectez innerTransactions pour confirmer. Voir integration-guides/priority-fee-tuning.

CPI Rust direct

Si vous devez faire un CPI dans AMM v4 à partir de votre propre programme Anchor, vous devrez modéliser la liste de comptes de SwapBaseIn verbatim. Un esquisse minimal :
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<()> {
    // Le discriminateur d'instruction pour SwapBaseIn est 9 sur 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 ne fournit pas de caisse Anchor pour le CPI. L’esquisse ci-dessus utilise une Instruction construite manuellement.

Pièges

  • Compte OpenBook manquant. Les 8 comptes du côté OpenBook sont requis sur chaque swap, dépôt et retrait ; le SDK gère cela, les instructions construites à la main souvent non.
  • Lecture des soldes bruts des coffres. Ne reflète pas les montants séquestré on-book ni les pertes et profits accumulés. Utilisez la citation du SDK ou api-v3.raydium.io/pools/info/ids.
  • File d’attente des événements OpenBook pleine. Un pool peut rejeter les swaps avec SerumOrderError quand la file d’attente des événements de son marché a besoin d’être remontée. Remonter est permissionless (MonitorStep sur les comptes OpenBook du marché).
  • Mints Token-2022. Non pris en charge. Un pool AMM v4 ne peut pas être créé par rapport à un mint Token-2022 ; toute paire Token-2022 doit être sur CPMM ou CLMM.

Prochaines étapes

Sources :