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. Tous les exemples ciblent @raydium-io/raydium-sdk-v2@0.2.42-alpha sur Solana mainnet-beta, vérifié en 2026-04. Le SDK dispatche v3 / v5 / v6 en interne selon le propriétaire du programme de la farm ; les exemples ci-dessous supposent une farm v6. Consultez reference/program-addresses pour les trois identifiants de programme.

Installation

Les exemples ici reflètent les fichiers dans raydium-sdk-V2-demo/src/farm. L’initialisation suit le config.ts.template du dépôt de démonstration :
import { Connection, Keypair, clusterApiUrl, PublicKey } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";
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",
  disableFeatureCheck: true,
  blockhashCommitment: "finalized",
});
export const txVersion = TxVersion.V0;

Récupérer une farm par ID

const farmId = new PublicKey("<FARM_ID>");

const farm = await raydium.farm.getFarmById({ farmId });
console.log("Staking mint:", farm.symbolMint.toBase58());
console.log("Total staked:", farm.totalStaked.toString());
console.log("Rewards:");
for (const r of farm.rewardInfos) {
  console.log(
    "  -", r.mint.toBase58(),
    "rate(1e-" + r.decimals + "/s):", r.perSecond.toString(),
    "open:", new Date(r.openTime * 1000).toISOString(),
    "end: ", new Date(r.endTime * 1000).toISOString(),
  );
}
getFarmById récupère FarmState hors-chaîne, décode selon la version du programme, et normalise le taux d’émission à virgule fixe en un simple Decimal par seconde.

Mettre en stake des tokens LP

Source : src/farm/stake.ts
const amount = new BN(1_000_000_000);   // 1 LP (en supposant 9 décimales)

const { execute } = await raydium.farm.deposit({
  farmInfo: farm,
  amount,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Deposit tx:", txId);
Le SDK gère le pré-règlement de toute récompense en attente, donc si ce portefeuille a déjà des stakes dans cette farm, l’instruction versera les récompenses accumulées aux ATAs de l’utilisateur dans la même transaction.

Récolte uniquement (harvest)

Source : src/farm/harvest.ts
const { execute } = await raydium.farm.harvestAllRewards({
  farmInfoList: [farm],
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
harvestAllRewards accepte une liste — pour les UIs montrant une vue de portefeuille, regroupez l’appel. Chaque farm est récoltée dans une instruction séparée au sein d’une transaction (soumis à la limite de 1232 octets ; pour plus de ~6 farms, divisez en plusieurs transactions). Pour une seule farm sur v6, vous pouvez aussi utiliser le chemin Harvest explicite :
const { execute } = await raydium.farm.withdraw({
  farmInfo: farm,
  amount: new BN(0),        // 0 = chemin harvest-only, même sur v6
  txVersion: TxVersion.V0,
});
await execute({ sendAndConfirm: true });
Sur v3 et v5, l’idiome amount: 0 est requis ; le SDK le dispatche correctement.

Retirer les stakes

Source : src/farm/unstake.ts
const amount = new BN(500_000_000);   // 0.5 LP

const { execute } = await raydium.farm.withdraw({
  farmInfo: farm,
  amount,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });

Créer une farm v6

Source : src/farm/createAmmFarm.ts et editAmmFarm.ts
import { CurveCalculator } from "@raydium-io/raydium-sdk-v2"; // optionnel, pour la planification du APR

const stakingMint = new PublicKey("<LP_MINT>");       // par exemple, un mint LP CPMM
const rewardMint  = new PublicKey("<REWARD_MINT>");   // par exemple, le token de votre projet

const now      = Math.floor(Date.now() / 1000);
const openTime = now + 60 * 60;                       // démarrer dans 1h
const duration = 60 * 60 * 24 * 30;                   // 30 jours
const endTime  = openTime + duration;

const totalBudget = new BN(1_000_000).mul(new BN(10).pow(new BN(9))); // 1M tokens de récompense (9 déc)
const perSecond   = totalBudget.div(new BN(duration));                // entier, le SDK élève à Q64.64

const { execute, extInfo } = await raydium.farm.create({
  programId: /* ID du programme v6, depuis reference/program-addresses */,
  poolInfo: { lpMint: { address: stakingMint, decimals: 9, programId: /* SPL Token */ } as any },
  rewardInfos: [
    {
      mint:           rewardMint,
      openTime:       new BN(openTime),
      endTime:        new BN(endTime),
      perSecond,
      rewardSender:   owner.publicKey,    // qui paie le budget initial
      mintProgramId:  /* SPL Token ou Token-2022 */,
    },
  ],
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Farm:", extInfo.farmId.toBase58(), "createTx:", txId);
Points clés :
  • perSecond est le taux d’émission entier par seconde. Le SDK le compresse en Q64.64 avant l’envoi. Pour un taux fractionnaire, ajustez l’échelle et la durée.
  • Le budget complet (perSecond × duration) doit être présent dans votre ATA de récompense — create le déplace dans le coffre de récompenses atomiquement.
  • Vous pouvez mettre en place jusqu’à 5 récompenses en un seul appel create. La liste des comptes augmente de (reward_mint, reward_vault, sender_ata, token_program) par flux supplémentaire ; restez conscient de la limite de taille de transaction de 1232 octets. Pour 4+ récompenses, créez avec 1–2 et utilisez AddReward dans les transactions de suivi.

Augmenter un flux de récompense existant

const additionalDays = 30;
const extraSeconds   = 60 * 60 * 24 * additionalDays;
const extraBudget    = perSecond.mul(new BN(extraSeconds));

const { execute } = await raydium.farm.setRewards({
  farmInfo: farm,
  rewardInfos: [
    {
      rewardMint:  rewardMint,
      newEndTime:  new BN(farm.rewardInfos[0].endTime + extraSeconds),
      newPerSecond: perSecond,                   // conserver le même taux
      payer:        owner.publicKey,
    },
  ],
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
setRewards prolonge end_time et transfère le budget delta. L’instruction ne peut pas raccourcir un flux, ne peut pas diminuer per_second sur un flux actif, et ne peut pas changer le mint de récompense. Pour changer les mints, attendez end_time et utilisez AddReward sur un emplacement libéré (s’il y en a un), ou créez une nouvelle farm.

Redémarrer un flux terminé

const { execute } = await raydium.farm.restartRewards({
  farmInfo: farm,
  newRewardInfo: {
    rewardMint:   rewardMint,
    openTime:     new BN(Math.floor(Date.now() / 1000) + 60 * 60),
    endTime:      new BN(Math.floor(Date.now() / 1000) + 60 * 60 + 60 * 60 * 24 * 14),
    perSecond:    new BN("1000000000"),
    payer:        owner.publicKey,
  },
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
Valide uniquement quand l’état reward_state == 2 (terminé) de l’emplacement cible. L’appelant doit être le reward_sender de l’emplacement (v6) ou le propriétaire de la farm (v5).

CPI Rust

Contrairement à AMM v4, le programme farm v6 est livré avec une caisse Anchor (raydium_farm_v6) publiée aux côtés des sources frontend et SDK. Un esquisse minimal de Deposit :
use anchor_lang::prelude::*;
use raydium_farm_v6::{self, cpi::accounts::Deposit as RaydiumDeposit};

#[derive(Accounts)]
pub struct ProxyFarmDeposit<'info> {
    /// CHECK:
    pub farm_program: UncheckedAccount<'info>,
    #[account(mut)] pub user: Signer<'info>,
    #[account(mut)] /// CHECK:
    pub user_ledger: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub farm_state: UncheckedAccount<'info>,
    /// CHECK:
    pub farm_authority: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub staking_vault: UncheckedAccount<'info>,
    #[account(mut)] /// CHECK:
    pub user_staking_ata: UncheckedAccount<'info>,
    // Suivi dans remaining_accounts par des paires (reward_vault_i, user_reward_ata_i)
    // pour chaque flux de récompense actif sur la farm.
    pub token_program: UncheckedAccount<'info>,
    pub system_program: Program<'info, System>,
}

pub fn proxy_deposit<'info>(
    ctx: Context<'_, '_, '_, 'info, ProxyFarmDeposit<'info>>,
    amount: u64,
) -> Result<()> {
    let cpi_ctx = CpiContext::new(
        ctx.accounts.farm_program.to_account_info(),
        RaydiumDeposit {
            user:             ctx.accounts.user.to_account_info(),
            user_ledger:      ctx.accounts.user_ledger.to_account_info(),
            farm_state:       ctx.accounts.farm_state.to_account_info(),
            farm_authority:   ctx.accounts.farm_authority.to_account_info(),
            staking_vault:    ctx.accounts.staking_vault.to_account_info(),
            user_staking_ata: ctx.accounts.user_staking_ata.to_account_info(),
            token_program:    ctx.accounts.token_program.to_account_info(),
            system_program:   ctx.accounts.system_program.to_account_info(),
        },
    ).with_remaining_accounts(ctx.remaining_accounts.to_vec());

    raydium_farm_v6::cpi::deposit(cpi_ctx, amount)
}
La tranche remaining_accounts doit correspondre exactement aux emplacements de récompense actifs de la farm, 1 pour 1 (paires de reward_vault_i, user_reward_ata_i dans l’ordre des index). Omettre ou mélanger ces éléments produit une mauvaise comptabilité silencieuse — le programme transférera le mauvais montant.

Pièges

  • Oublier de récolter avant de retirer. Inoffensif — Withdraw règle d’abord les récompenses en attente. Mais si votre UI montre « récolter » séparément de « retirer », l’utilisateur peut penser qu’il y a encore quelque chose à récolter après un Withdraw. Ce n’est pas le cas ; tout ce qui s’est accumulé jusqu’à ce point a été versé.
  • total_staked = 0 pendant les émissions. Les émissions accumulées alors que rien n’était en stake sont perdues (la formule de mise à jour reward_per_share divise par 0 et le programme saute la mise à jour). Pour les programmes avec open_time programmé, exécutez un « seed stake » à open_time pour éviter cela.
  • Frais de transfert Token-2022. Sur les farms v6 avec mints de récompense Token-2022, le frais de transfert s’applique lors de l’émission (coffre → utilisateur). Tenez compte de cela dans les devis de APR.
  • Petit per_second sur v5. Le taux u64 de v5 signifie que tout per_second < 1 unité token par seconde (sur les mints avec ≥9 décimales, c’est souvent le taux souhaité) ne peut pas être exprimé — le taux du flux s’arrondit à 0 et la farm n’émet rien. Utilisez v6.

Prochaines étapes

Sources :