Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
CPI (« cross-program invocation ») est le mécanisme par lequel un programme Solana en appelle un autre. La plupart des programmes Raydium fournissent des crates CPI wrapper Anchor qui font ressembler le site d’appel à un appel de fonction typée, avec des structs de comptes ayant des noms de champs validés et des helpers cpi::<ix>(). Cette page documente le motif général une fois, puis les différences par programme. Pour du TypeScript exécutable, consultez la page code-demos de chaque chapitre produit.

Quel motif s’applique à quel programme

Si vous intégrez CPMM, CLMM ou LaunchLab, lisez d’abord le motif général, puis passez à la section de votre programme pour la liste des comptes et les différences. Farm v6 et AMM v4 sont suffisamment différents pour justifier la lecture de leurs sections de manière autonome.

Dépendances Cargo

La clé de dépendance doit correspondre exactement au [package] name du dépôt cible, traits d’union inclus. Cargo ne traite pas raydium_cp_swap comme équivalent à raydium-cp-swap lors de la résolution d’une dépendance git.
branch = "master" suit la dernière source publiée ; épinglez à un rev = "<commit>" spécifique si vous avez besoin d’une construction reproductible. Ceci est recommandé une fois que vous avez dépassé le prototypage, car un changement de disposition de compte en amont sur master cassera votre construction sans avertissement. Le flag de feature cpi fait que les crates se compilent en juste la surface CPI (structs de comptes + invocateurs) plutôt que le programme complet, donc votre binaire reste petit. anchor-lang / anchor-spl doivent correspondre à ce que la crate cible épingle, et à partir de 2026-09 les deux crates Raydium publiques ne s’accordent pas :
Vous ne pouvez pas dépendre des deux crates d’un seul programme en ce moment. Chacun épingle Anchor avec =, donc Cargo devrait lier deux copies incompatibles des traits d’Anchor dans un seul binaire, et la construction échoue. Si votre programme fait des CPI dans CPMM et CLMM, vous devez soit le diviser en deux programmes, soit abandonner la crate CPI typée pour l’un d’eux et encoder manuellement cette instruction (le motif montré pour AMM v4 fonctionne pour n’importe quel programme). Revérifiez les deux fichiers Cargo.toml avant de commencer — ceci devrait se résoudre quand CLMM passera à Anchor 1.x.
Anchor 1.0 a changé deux choses que chaque site d’appel CPI touche. Si vous déplacez une intégration fonctionnelle hors de 0.3x :
  • CpiContext::new prend un Pubkey, pas un AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) devient CpiContext::new(*ctx.accounts.cpmm_program.key, accts). Idem pour new_with_signer. Le champ struct est maintenant program_id: Pubkey.
  • Context a une durée de vie, pas quatre. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> devient Context<'info, MyProxySwap<'info>>.
Du côté client, anchor-client’s RequestBuilder::instructions() retourne maintenant Vec<Instruction> plutôt que Result<Vec<Instruction>> (supprimez le ?), et CommitmentConfig a quitté solana-sdk — prenez-le de anchor_client à la place. spl-associated-token-account 8.0 réexporte ses helpers de la nouvelle crate spl-associated-token-account-interface. get_associated_token_address et ID sont toujours accessibles à la racine de la crate (spl_associated_token_account::{get_associated_token_address, ID}), mais les helpers d’adresse sont dépréciés là — préférez dépendre de spl-associated-token-account-interface directement et importer spl_associated_token_account_interface::address::get_associated_token_address et spl_associated_token_account_interface::program::ID. Notez que ::address et ::program sont des modules de la crate interface ; spl_associated_token_account::address::… ne se résout pas.
Pour des exemples CPI fonctionnels qui câblent les structs de comptes de bout en bout, voir raydium-io/raydium-cpi-example (couvre AMM v4, CPMM et CLMM). Sa branche la plus récente est anchor-0.31.0 — il n’y a pas encore de branche Anchor 1.x, donc traitez ce dépôt comme la référence pour le câblage des structs de comptes, pas pour les pins de version que cette page impose.

Le motif Anchor CPI général

Cette section parcourt CPMM de bout en bout comme l’exemple travaillé : struct Accounts, CpiContext, cpi::<ix>(). CLMM suit la forme identique, avec une liste de comptes différente et une exigence de comptes restants. LaunchLab suit la même mécanique mais sa liste de comptes porte plusieurs comptes sans équivalent CPMM/CLMM (global_config, platform_config, event_authority, program), donc traitez-le comme le même motif, pas la même forme. Consultez la section propre à chaque programme plutôt que d’assumer que la liste de comptes de cette procédure pas à pas se transfère directement.

Construction de la liste des comptes

Chaque CPI Raydium nécessite un struct Accounts dans le programme appelant. Ses champs sont tous les comptes dont votre instruction a besoin, avec des validateurs au niveau des champs ; leur ordre de déclaration n’a pas besoin de correspondre à l’ordre des comptes d’instruction propre à Raydium, puisque votre propre client généré par IDL les adresse par nom, pas par position :
La plupart des comptes côté Raydium sont UncheckedAccount car le callee (Raydium) possède la validation. Votre programme appelant valide strictement uniquement les comptes que vous possédez, comme les ATA utilisateur et vos propres PDA. Le commentaire doc /// CHECK: supprime l’avertissement d’Anchor concernant les vérifications manquantes. L’exception côté Raydium est cpmm_program lui-même : c’est le programme invoqué plutôt qu’un compte de données que Raydium valide en interne, donc il est typé Program<T> et obtient la vérification d’adresse automatique d’Anchor au lieu d’une vérification manuelle /// CHECK:. Cette forme largement UncheckedAccount, où Raydium valide ses propres comptes, est la même pour CLMM et LaunchLab. Cet exemple suppose que les deux mints sont du Token SPL classique ; si l’un ou l’autre peut être un mint Token-2022, ajoutez un champ token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> et passez-le comme input_token_program/output_token_program de ce côté dans l’appel CPI ci-dessous au lieu de token_program.

Construction de l’appel CPI

Anchor génère un helper par instruction, ainsi qu’un struct de comptes CPI (cpi::accounts::Swap, aliasé CpmmSwap ci-dessous). Contrairement à votre propre struct MyProxySwap ci-dessus, les noms et l’ordre des champs de celui-ci sont fixés par l’IDL propre de raydium-cp-swap et doivent correspondre exactement :
cpi::swap_base_input est généré à partir de l’IDL ; sa liste d’arguments reflète la liste d’arguments de l’instruction Anchor. Chaque programme Raydium basé sur Anchor confirmé (CPMM, CLMM, LaunchLab) génère ses helpers cpi::<ix>() de la même manière, le nom de la fonction correspondant au nom de l’instruction en snake_case. Que cela s’étende à Farm v6 n’est pas confirmé ; voir sa section.

Graines de signataire (CPI signé par PDA)

Quand votre programme signe le CPI au nom d’un PDA (courant pour les coffres, les séquences, etc.), utilisez CpiContext::new_with_signer :
Les graines de signataire doivent correspondre à la dérivation du PDA. Pour tout compte passé comme authority (ou rôle de signataire similaire), le runtime Solana vérifie que le PDA signe via ces graines.

Comptes restants

Certaines instructions Raydium prennent des comptes restants, une liste de longueur variable ajoutée après les comptes fixes. Les helpers CPI d’Anchor ne vérifient pas les comptes restants ; passez-les via .with_remaining_accounts(...) :
L’ordre compte toujours, car le programme récepteur itère les comptes restants dans l’ordre que vous les passez. Deux ordres confirmés :
  • CLMM SwapV2 : tableaux de ticks, ordonnés directionnellement.
  • Farm v6 : paires (reward_vault, user_reward_ata), mais seulement à partir du deuxième flux de récompense en avant ; voir Farm v6 pour ce que le décodage d’une vraie transaction montre.

Application du motif : CLMM

SwapV2 suit le motif général ci-dessus avec une liste de comptes différente et une exigence de comptes restants pour les tableaux de ticks. Le module #[program] de la crate est nommé raydium_clmm, qui est aussi son chemin Rust use.
Le struct de comptes CPI est nommé SwapSingleV2, pas SwapV2. SwapV2 est le nom de l’instruction on-chain.
Calculez la liste des tableaux de ticks de la même manière que le SDK le fait, via un devis par rapport à l’état actuel du pool, plutôt que de deviner un nombre fixe ; un swap qui dépasse les tableaux que vous avez passés revient avec TickArrayNotFound (voir products/clmm/instructions pour la table de comptes complète et la liste des erreurs). Passez-les dans la direction de la marche des prix : premier tableau dans la direction du swap en premier.

Application du motif : LaunchLab

LaunchLab est basé sur Anchor et IDL-publié : raydium_launchpad/raydium_launchpad.json dans le dépôt public raydium-idl. L’identifiant de métadonnées interne de cet IDL est raydium_launchpad, un nom technique pour le programme sous-jacent, pas un nom alternatif pour le produit. Contrairement à CPMM et CLMM, cependant, la source du programme elle-même n’est pas disponible publiquement (voir reference/program-addresses). Il n’y a pas de dépendance git = "..." vers laquelle pointer Cargo, et pas de source pour confirmer quel serait le chemin Rust use d’une vraie crate. Générez les liaisons à partir de l’IDL publié en utilisant la macro declare_program! d’Anchor. Enregistrez le JSON IDL sous idls/raydium_launchpad.json dans votre crate (Cargo cherche un répertoire idls/ relatif à CARGO_MANIFEST_DIR), puis declare_program!(raydium_launchpad); génère les structs raydium_launchpad::cpi::accounts::<Ix> et les fonctions cpi::<ix>() directement à partir de l’IDL, aucune source de programme requise. Le nom du struct de comptes généré est toujours le nom de l’instruction en PascalCase (buy_exact_in → BuyExactIn), et les noms de champs correspondent exactement aux noms de comptes de l’IDL, la même liste de comptes déjà utilisée dans MyProxyBuy ci-dessous. La forme CPI suit le motif général. La liste des comptes et les arguments ci-dessous proviennent de l’instruction buy_exact_in de l’IDL on-chain, pas de products/launchlab/instructions.mdx :
Après la graduation, le programme cible est CPMM ou AMM v4 selon pool_state.migrate_type, que products/launchlab/accounts.mdx dit être défini au moment de Initialize. Votre liste de comptes CPI doit être préparée pour l’un ou l’autre, ou vous devez d’abord lire migrate_type hors de PoolState et vous brancher.

Propagation d’erreur

Chaque programme Raydium basé sur Anchor retourne son propre enum d’erreur ; Anchor les enveloppe, donc votre programme appelant les voit comme Err(ProgramError::Custom(code)). Pour gérer les erreurs spécifiques :
Échangez le type d’erreur pertinent pour le programme que vous appelez (raydium_clmm::error::ErrorCode pour CLMM, et ainsi de suite). Les numéros de code d’erreur sont stables selon la politique IDL (sdk-api/anchor-idl), donc vous pouvez tester contre des codes spécifiques en comparant la valeur numérique. Tables d’erreurs complètes : CPMM, CLMM, AMM v4, Farm v6 et LaunchLab.

Budget de calcul dans les CPI composés

Chaque frame CPI a une surcharge, et la consommation CU propre du callee s’empile sur la vôtre, donc une transaction qui appelle Raydium de l’intérieur de votre programme a besoin d’un budget de calcul explicite plutôt que de compter sur la limite par défaut de 200k CU.
Mesuré, pas estimé. Un swap_base_input CPMM sur mainnet consomme ~23 000 CU dans le programme CPMM lui-même — échantillonné 2026-09-09 sur huit swaps en direct sur un pool à haut volume (22 721–23 052), lu à partir de la ligne de log Program CPMMoo8… consumed N of M compute units. Pour comparaison : swap AMM v4 ~26 000 ; swap CLMM ~41 000 ; swap_v2 CLMM ~48 000 (43 838–52 887), augmentant avec chaque croisement de tick.Une révision antérieure de cette page rapportait ~47 700 CU pour un CPI proxy-swap. Ce chiffre était la transaction entière (computeUnitsConsumed), qui inclut votre propre programme, le frame CPI et toute configuration d’ATA — pas le coût du callee. Les deux sont utiles, mais ce ne sont pas le même nombre, donc comparez comme avec comme. Mesurez votre propre transaction plutôt que de budgéter hors de l’un ou l’autre.
Les CPI CLMM et LaunchLab coûtent plus cher (CLMM en particulier marche des tableaux de ticks supplémentaires via remaining_accounts, ajoutant du CU par tableau), mais seul le chiffre CPMM ci-dessus est une valeur mesurée. Définissez toujours une limite ComputeBudgetProgram::set_compute_unit_limit(...) explicite dimensionnée à partir de votre propre mesure, pas un nombre copié de la documentation, car la limite par défaut de 200k CU s’épuisera silencieusement et les coûts par instruction changent à mesure que les programmes sont mis à niveau.

AMM v4 : construction manuelle d’instruction

AMM v4 est antérieur à Anchor et n’a pas de crate CPI, ce qui en fait le seul programme de ce document qui ne suit pas le motif général ci-dessus. Construisez l’Instruction à la main :
Voir products/amm-v4/code-demos pour la liste complète des comptes.

Farm v6

Utilisez le SDK TS si c’est une option pour votre intégration. raydium.farm.deposit(...) (voir products/farm-staking/code-demos) est exercé par des démos réelles et ne dépend pas de l’existence d’une crate Anchor pour ce programme.
Farm v6 n’offre pas de chemin CPI Anchor. Il n’y a pas de crate raydium_farm_v6 sur crates.io, pas de dépôt source public, et pas d’IDL on-chain — le programme n’a ni un compte anchor:idl hérité ni une entrée dans le programme Program Metadata (voir sdk-api/anchor-idl). Traitez-le comme un programme non-Anchor et construisez ses instructions à la main, comme ci-dessous.
Si vous avez besoin du CPI Rust malgré tout, par exemple en composant à partir d’un autre programme on-chain, construisez l’Instruction à la main, de la même manière que AMM v4 : dérivez la vraie liste de comptes et les discriminateurs d’instruction indépendamment, par exemple en décodant les dispositions TypeScript du SDK (raydium-sdk-V2’s farm module), en décodant les vraies transactions directement (voir ci-dessous), ou en vidant et en désassemblant le programme déployé. Pour la forme d’instruction sans argument cohérente avec un appel de récolte ou de réclamation, l’ordre de compte réel est un préfixe fixe (token_program, le compte d’état de la ferme, un PDA d’autorité de coffre, le premier coffre de récompense de ce PDA, un deuxième PDA, l’appelant, et l’ATA de l’appelant pour ce premier mint de récompense), suivi de paires (reward_vault_i, user_reward_ata_i) dans remaining_accounts pour chaque flux de récompense après le premier. La convention d’appairage est réelle, mais elle ne commence qu’au deuxième flux de récompense : le coffre et l’ATA du premier flux sont des comptes fixes, pas adjacents l’un à l’autre, et pas du tout partie de remaining_accounts.

Test d’un flux CPI

Le dev local nécessite que les programmes Raydium soient disponibles dans votre validateur de test. Trois options :
  1. anchor test avec clonage de programme. Tire le bytecode déployé mainnet dans votre validateur local ; voir Clonage de programmes dans un validateur local ci-dessous pour la config Anchor.toml et deux choses qui trébuchent spécifiquement les tests de création de pool.
  2. Devnet. Raydium déploie la plupart des programmes sur devnet, mais à des ID de programme différents que mainnet pour chaque programme (CPMM, CLMM, AMM v4, Stable AMM et LaunchLab ont chacun une adresse devnet distincte ; voir le tableau Devnet dans reference/program-addresses). Farm v3/v5/v6 ne sont pas fiablement publiés sur devnet ; l’API en direct (https://api-v3-devnet.raydium.io/main/info) a l’image actuelle. Si vous utilisez les constantes DEVNET_PROGRAM_ID groupées de raydium_clmm (ou l’équivalent pour d’autres crates), ne supposez pas qu’un ID mainnet fonctionne aussi sur devnet. Exécutez anchor test --provider.cluster devnet pour frapper le code en direct une fois que vous avez les bonnes adresses.
  3. Déploiement local. Clonez les dépôts Raydium (CPMM, CLMM ; la source de LaunchLab n’est pas disponible pour cette option) et anchor deploy sur un validateur local. Ajoute une surcharge de cycle de test mais vous permet de modifier le callee pour le débogage.
Exécutez avec anchor test, ou anchor build d’abord et anchor test --skip-build après si vous itérez sur le fichier de test sans changer le programme.

Clonage de programmes dans un validateur local

Cela fonctionne par ID de programme indépendamment de la disponibilité publique de la source du programme, donc LaunchLab se clone de la même manière que CPMM et CLMM même si sa source n’est pas disponible. reference/program-addresses est la source de vérité pour chaque adresse ici.
Cloner le programme n’est pas suffisant si votre test crée aussi un pool (plutôt que d’échanger contre un qui existe déjà). L’instruction initialize de CPMM valide ses comptes amm_config et create_pool_fee par rapport aux vraies données on-chain, donc vous devez aussi cloner ceux-ci, ou initialize échoue carrément. Pour CPMM spécifiquement : clonez le AmmConfig de niveau de frais que vous voulez (récupérez son adresse de GET https://api-v3.raydium.io/main/cpmm-config, l’index 0 est le niveau 0,25 %) et le compte de token récepteur de frais, validé par adresse exacte, pas créé à la volée, donc il doit déjà exister.
Un pool que votre test vient de créer n’est pas échangeable à l’instant même. L’instruction initialize de CPMM remplace silencieusement un open_time demandé qui n’est pas strictement dans le futur (if open_time <= block_timestamp { open_time = block_timestamp + 1 }), donc même startTime: 0 (« ouvrir immédiatement », selon le SDK) laisse un vrai écart ≥1 seconde avant que le pool n’accepte les swaps. Un test qui crée un pool et échange contre lui sans délai frappera NotApproved. Un court await (1–2s) entre la création du pool et le premier swap est suffisant. Ceci est spécifique aux tests ; un humain exécutant deux commandes manuelles séparées ne remarquerait normalement pas, car la dactylographie et le démarrage du processus mangent déjà plus d’une seconde.

Pointeurs

Sources :