Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
sdk-api/rust-cpi couvre la mécanique bas niveau de l’invocation de chaque programme Raydium. Cette page est la contrepartie haut niveau : pourquoi vous composeriez Raydium dans votre propre programme, quel modèle convient à votre cas d’usage, et tout le code de liaison dont vous avez besoin de bout en bout.

Quand CPI est le bon outil

Un programme personnalisé a du sens quand l’échange doit se produire de manière atomique avec d’autres changements d’état on-chain que seul votre programme peut effectuer. Cas courants :
  • Programmes de séquestre / ordres limités — l’utilisateur dépose un mint dans votre séquestre, votre programme surveille une condition de prix, et quand elle se déclenche, votre programme échange atomiquement via Raydium et crédite le compte de l’utilisateur.
  • Proxies agrégateurs — une seule instruction qui achemine un swap via Raydium + un ou plusieurs autres DEX, avec tous les sauts sous une seule vérification de slippage détenue par votre programme.
  • Coffres auto-composés — déposez des tokens LP ou des enjeux de ferme dans votre coffre, le coffre récolte les récompenses selon un calendrier, réapprovisionne la liquidité, émet des tokens de part.
  • Coffres de stratégie — positions LP à effet de levier qui se rééquilibrent en swappant via CLMM ; liquidateurs qui ferment les positions et swappent les garanties en une seule transaction.
  • Plateformes de lancement de tokens avec acquisition personnalisée — votre programme détient les tokens d’acquisition et les libère dans un pool Raydium selon un calendrier.
Si vous voulez simplement envoyer un swap depuis du code hors chaîne, CPI est excessif — utilisez le SDK. CPI ne justifie sa complexité que quand l’atomicité avec votre propre état est l’exigence.

Modèles de composition

Modèle 1 : Proxy mince

Votre programme expose une seule instruction qui valide une certaine politique (par exemple, paires de mints autorisées, réduction de frais pour les utilisateurs vérifiés) puis transfère à Raydium.
L’état vit dans les ATA de l’utilisateur. Votre programme ne possède aucun token. Empreinte de confiance minimale.

Modèle 2 : Séquestre

Votre programme possède un PDA qui détient le mint d’entrée de l’utilisateur. Au déclenchement, le PDA signe un CPI vers Raydium pour swapper son propre solde.
Détail critique : le PDA signe via CpiContext::new_with_signer. Voir Graines de signataire.

Modèle 3 : Multi-hop composé

Votre programme émet plusieurs CPI en une seule instruction, en appliquant une seule limite de slippage sur tous. Les instructions de swap Raydium ont chacune leur propre minimum_amount_out, mais vous les définissez à 0 (ou un plancher très lâche) et appliquez un minimum strict final vous-même après le dernier hop.
Cela vous donne une seule porte de reversion pour tout l’itinéraire. N’utilisez ce modèle que si vous faites confiance à chaque hop pour être sûr en slippage ; sinon, laissez chaque hop appliquer son propre min.

Modèle 4 : Coffre / stratégie

Votre programme détient des tokens LP ou des enjeux de ferme dans un PDA. Un gardien (ou l’utilisateur) appelle compound(), qui :
  1. Récolte les récompenses de la ferme.
  2. Échange les récompenses pour les tokens du pool (CPI dans CPMM ou CLMM).
  3. Redépose les produits dans le LP (un autre CPI).
  4. Enjeu du nouveau LP (un autre CPI).
Tout en une seule transaction pour que la NAV du coffre se déplace atomiquement. Le budget de calcul est généralement 600k–1M CU ; les tables de recherche d’adresses sont obligatoires.

Construction de la liste des comptes

La struct Accounts du programme appelant reflète l’ordre des comptes du programme Raydium, mais la plupart des comptes côté Raydium sont UncheckedAccount car Raydium les valide lui-même. Vous n’ajoutez des contraintes que sur les comptes que vous possédez :
L’asymétrie — validation stricte sur vos comptes, UncheckedAccount sur ceux de Raydium — n’est pas de la paresse. Le destinataire valide les siens ; double-valider à l’appelant brûle juste du CU et risque de se désynchroniser quand Raydium expédie un nouveau champ de disposition de struct.

L’appel CPI lui-même

Graines de signataire PDA

Le CPI ne réussit que si le PDA passé comme authority correspond à la dérivation que l’appelant prétend. Les deux doivent s’accorder sur :
  1. La séquence d’octets de graine (ici [b"escrow", user.key().as_ref()]).
  2. Le bump.
  3. L’ID du programme appelant (votre programme, pas celui de Raydium).
Notez ce avec quoi le PDA doit correspondre. L’emplacement authority de CPMM est son propre PDA de coffre — un compte fixe à l’échelle du programme qu’il dérive et signe avec lui-même, et que votre programme ne contrôle ni ne substitue. Le compte avec lequel vos graines PDA doivent s’aligner est payer : la vérification se produit à l’intérieur du helper transfer_from_user_to_pool_vault de CPMM lui-même, qui exige que le compte passé comme payer soit le propriétaire de input_token_account. Bug courant : passer user comme payer tandis que escrow_input_ata est détenue par le PDA du séquestre. Le programme SPL Token rejette avec owner mismatch. Faites toujours de payer le propriétaire de l’ATA — et signez-le avec new_with_signer quand ce propriétaire est un PDA.

Comptes restants

Plusieurs instructions Raydium prennent une liste de longueur variable de comptes ajoutés après les comptes fixes — comptes restants.
  • CLMM SwapV2 : 1–8 comptes TickArrayState pour les tableaux de ticks que le swap peut traverser, dans la direction du swap.
  • Farm v6 Deposit / Harvest / Withdraw : paires (reward_vault, user_reward_ata), une paire par emplacement de récompense actif.
  • Mints de crochet de transfert Token-2022 : le programme de crochet de transfert plus tous les comptes dont le crochet a besoin.
Les helpers CPI d’Anchor ne vérifient pas les comptes restants. Passez-les :
L’ordre compte. Pour CLMM :
Pour la récolte de farm v6 :
Votre programme appelant doit passer les comptes restants qu’il reçoit du client inchangés. N’essayez pas de les filtrer ou de les réorganiser.

Budget de calcul pour les appels composés

Un CPI coûte ~1 500 CU pour le cadre d’appel lui-même ; l’utilisation propre du CU de l’appelé s’empile par-dessus. Les chiffres de l’appelé ci-dessous sont mesurés à partir de transactions mainnet en direct sur des pools à haut volume le 2026-09-09, lus à partir de la ligne de journal Program <id> consumed N of M compute units pour la propre invocation du programme Raydium (ils incluent donc ses CPI de programme de token internes) : Ajoutez ~1 500 pour chaque cadre CPI et la surcharge de votre propre programme par-dessus. Le coût du swap CLMM s’échelonne avec les traversées de ticks, donc traitez son chiffre comme un plancher. Les mints Token-2022 ajoutent le coût de gestion des extensions du transfert lui-même ; mesurez-le pour vos propres mints plutôt que d’appliquer un multiplicateur plat.
Les révisions antérieures de cette page contenaient des estimations 5–7× plus élevées (un swap CPMM ~150 000 CU, ~180 000 pour CLMM). Celles-ci n’ont jamais été mesurées. Budgétisez à partir de votre propre lecture computeUnitsConsumed, pas à partir d’un nombre documenté — et notez qu’une transaction complète coûte plus que l’instruction Raydium seule une fois que la création d’ATA, l’enveloppe wSOL et les instructions de budget de calcul sont comptées.
Définissez toujours une limite explicite ComputeBudgetProgram::set_compute_unit_limit :
Le plafond par défaut de 200k CU s’épuisera silencieusement bien avant qu’un appel composé ne se termine.

Propagation des erreurs

Les programmes Raydium retournent des erreurs Anchor avec des codes d’erreur stables. Votre programme appelant les voit comme Err(ProgramError::Custom(code)). Propagez par défaut :
Ou interceptez pour des codes spécifiques :
Notez le terme ERROR_CODE_OFFSET : les variantes #[error_code] sont émises à partir de 6000, donc comparer contre le discriminant d’énumération nu ne correspond jamais. (Il n’y a pas de helper is_err dans anchor-lang ou dans raydium_cp_swap — les révisions antérieures de cette page en utilisaient un qui n’existe pas.) Le mappage code d’erreur-à-signification est stable selon la politique IDL (sdk-api/anchor-idl) ; les nouveaux codes s’ajoutent à la fin, les codes existants ne changent jamais de signification.

Exemple complet travaillé : séquestre d’ordre limité

Flux :
  1. open_order — l’utilisateur dépose amount_in de input_mint dans le PDA du séquestre ; enregistre le min_amount_out cible et l’expiration.
  2. execute_order — n’importe qui (gardien) appelle avec les comptes du pool actuels. Le programme vérifie que le devis actuel ≥ min_amount_out, puis CPI le swap Raydium et garde la sortie en séquestre.
  3. claim — l’utilisateur retire le mint de sortie du séquestre.
Le gardien paie les frais de transaction (il reçoit des frais de gardien ailleurs — non montré). Le PDA order signe le CPI comme payer, car il possède l’ATA d’entrée du séquestre ; ExecuteOrder a donc aussi besoin d’un champ pool_authority: UncheckedAccount<'info> pour le propre PDA de coffre de CPMM. La vérification de slippage côté Raydium et la vérification de delta du séquestre appliquent le plancher — ceinture et bretelles.

Test

Tirer les programmes Raydium dans un validateur local pour les tests d’intégration (depuis Anchor.toml) :
Clonez aussi les comptes d’état du pool pour que vos tests puissent réellement exécuter des swaps ; anchor test les récupère depuis mainnet au démarrage. Voir sdk-api/rust-cpi.

Pièges spécifiques à la composition

Réentrance

Solana n’a pas de vraie réentrance — un CPI ne peut pas rappeler le programme d’origine dans la même invocation. Mais vous pouvez toujours vous construire dans une réentrance logique : un CPI qui lit votre état, puis votre code le relit en supposant que le CPI ne l’a pas changé. Pour Raydium, les CPI ne touchent pas votre état, donc c’est moins une préoccupation que par exemple les contextes de prêt flash. Mais si vous composez Raydium avec un protocole de prêt, soyez conscient.

Dérive de mutabilité des comptes

Si votre programme passe un compte comme mut mais que Raydium s’attend à ce qu’il soit en lecture seule (ou vice versa), le runtime rejette l’invocation avec InvalidAccountData. Vérifiez toujours la mutabilité attendue de l’instruction de Raydium dans l’IDL ; raydium_cp_swap::cpi::accounts::Swap définit la mutabilité de chaque compte pour vous, à partir des marqueurs #[account(mut)] sur la propre struct Swap de CPMM — les champs dérivés sont tous des AccountInfo<'info> simples, donc c’est l’impl ToAccountMetas dérivé, pas les types de champs, qui porte les drapeaux.

Champ du programme Token-2022

Les mints d’entrée et de sortie peuvent être sous différents programmes de token — un SPL Token, un Token-2022. Le CPI a des champs input_token_program et output_token_program séparés pour cette raison. Vérifiez toujours le champ owner de chaque mint et routez le programme correct dans chaque emplacement.

Transactions versionnées

Une tx composée qui fait 2+ CPI Raydium plus une création d’ATA rentre rarement dans une transaction héritée (v0-sans-LUT). Utilisez V0 avec des tables de recherche d’adresses ; tirez les LUT publiques de Raydium via raydium.getRaydiumLutAddresses().

Pointeurs

Sources :