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 de Raydium fournissent des crates CPI wrapper Anchor qui font ressembler le site d’appel à un appel de fonction typée, avec des structs de compte 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 une lecture autonome de leurs sections.

Dépendances Cargo

La clé de dépendance doit correspondre exactement au [package] name du repo cible, tirets 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 source publiée la plus récente ; épinglez à un rev = "<commit>" spécifique si vous avez besoin d’une build 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 build sans avertissement. Le flag de feature cpi fait compiler les crates à juste la surface CPI (structs de compte + invokers) plutôt que le programme complet, donc votre binaire reste petit. anchor-lang / anchor-spl doivent correspondre à ce que la crate cible épingle :
Prenez les deux crates de la même ligne Anchor. Les deux branches master épinglent =1.0.2, donc un programme peut faire un CPI dans CPMM et CLMM à partir d’une seule crate. Mélanger les lignes casse la build : par exemple, raydium-cp-swap sur master avec raydium-clmm épinglé à une rev d’avant sa mise à niveau Anchor 1.0. Cargo devrait lier deux copies incompatibles des traits d’Anchor dans un seul binaire. Si vous êtes bloqué sur une paire mixte, divisez le programme en deux, ou abandonnez la crate CPI typée pour un côté et encodez manuellement cette instruction (le motif montré pour AMM v4 fonctionne pour n’importe quel programme).
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). Même chose 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, RequestBuilder::instructions() d’anchor-client retourne maintenant Vec<Instruction> plutôt que Result<Vec<Instruction>> (supprimez le ?), et CommitmentConfig a été déplacé hors de solana-sdk — prenez-le d’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 directement de spl-associated-token-account-interface 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 compte 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 repo comme la référence pour le câblage des structs de compte, pas pour les épingles de version que cette page impose.

Le motif CPI Anchor général

Cette section parcourt CPMM de bout en bout comme exemple travaillé : struct Accounts, CpiContext, cpi::<ix>(). CLMM suit la même forme exacte, avec une liste de comptes différente et une exigence de remaining-accounts. LaunchLab suit la même mécanique mais sa liste de comptes contient 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 de supposer que la liste de comptes de cette procédure s’applique directement.

Construction de la liste de comptes

Chaque CPI Raydium nécessite une 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 de Raydium lui-même, 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 parce que l’appelé (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 de documentation /// 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 reçoit la vérification d’adresse automatique d’Anchor au lieu d’une vérification manuelle /// CHECK:. Cette forme principalement UncheckedAccount, où Raydium valide ses propres comptes, est la même pour CLMM et LaunchLab. Cet exemple suppose que les deux mints sont des Token SPL classiques ; 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’une struct de comptes CPI (cpi::accounts::Swap, aliasée CpmmSwap ci-dessous). Contrairement à votre propre struct MyProxySwap ci-dessus, les noms et l’ordre des champs de celle-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. L’extension à Farm v6 n’est pas confirmée ; consultez sa section.

Graines de signataire (CPI signé par PDA)

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

Remaining accounts

Certaines instructions Raydium prennent des remaining accounts, une liste de longueur variable ajoutée après les comptes fixes. Les helpers CPI d’Anchor ne font pas de vérification de type sur les remaining accounts ; passez-les via .with_remaining_accounts(...) :
L’ordre compte toujours, puisque le programme récepteur itère les remaining accounts dans l’ordre où vous les passez. Deux ordres confirmés :
  • CLMM SwapV2 : tick arrays, ordonnés directionnellement.
  • Farm v6 : paires (reward_vault, user_reward_ata), mais uniquement à partir du deuxième flux de récompense ; consultez Farm v6 pour voir ce qu’un 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 remaining-accounts pour les tick arrays. Le module #[program] de la crate est nommé raydium_clmm, qui est aussi son chemin Rust use.
La struct de comptes CPI est nommée SwapSingleV2, pas SwapV2. SwapV2 est le nom de l’instruction on-chain.
Calculez la liste des tick-arrays 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 arrays que vous avez passés revient avec TickArrayNotFound (consultez 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 array dans la direction du swap en premier.

Application du modèle : LaunchLab

LaunchLab est basé sur Anchor et publié via IDL : 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, et non un nom alternatif du produit. Contrairement à CPMM et CLMM, le code source du programme lui-même n’est pas publiquement disponible (voir reference/program-addresses). Il n’y a pas de dépendance git = "..." pour pointer Cargo, et aucune source pour confirmer quel serait le vrai chemin Rust use d’une crate. Générez les bindings à 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, sans source de programme requise. Le nom de la struct de comptes générée 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 modèle général. La liste de comptes et les arguments ci-dessous proviennent de l’instruction buy_exact_in de l’IDL on-chain, et non 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 indique ê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 depuis PoolState et créer une branche.

Propagation des erreurs

Chaque programme Raydium basé sur Anchor retourne sa propre énumération d’erreur ; Anchor les enveloppe, donc votre programme appelant les voit comme Err(ProgramError::Custom(code)). Pour gérer des erreurs spécifiques :
Remplacez par le type d’erreur pertinent du programme que vous appelez (raydium_clmm::error::ErrorCode pour CLMM, etc.). Les numéros de code d’erreur sont stables selon la politique IDL (sdk-api/anchor-idl), vous pouvez donc tester des codes spécifiques en comparant avec la valeur numérique. Tableaux d’erreurs complets : CPMM, CLMM, AMM v4, Farm v6, et LaunchLab.

Budget de calcul dans les CPIs composés

Chaque frame CPI a une surcharge, et la consommation CU propre de l’appelé s’ajoute à la vôtre, donc une transaction qui appelle Raydium depuis l’intérieur de votre programme a besoin d’un budget de calcul explicite plutôt que de s’appuyer sur la limite par défaut de 200k CU.
Mesuré, non estimé. Un swap_base_input CPMM sur mainnet consomme ~23 000 CU dans le programme CPMM lui-même — échantillonné le 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 ; CLMM swap ~41 000 ; CLMM swap_v2 ~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 ATA — pas le coût de l’appelé. Les deux sont utiles, mais ce ne sont pas le même nombre, donc comparez des choses comparables. Mesurez votre propre transaction plutôt que de budgéter d’après l’un ou l’autre.
Les CPIs CLMM et LaunchLab coûtent plus cher (CLMM en particulier parcourt 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 instruction ComputeBudgetProgram::set_compute_unit_limit(...) explicite dimensionnée à partir de votre propre mesure, et non d’un nombre copié depuis 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 à jour.

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 modèle 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 Rust 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 Program Metadata program (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 de Rust CPI 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 layouts TypeScript du SDK (raydium-sdk-V2’s farm module), en décodant les transactions réelles directement (voir ci-dessous), ou en vidant et désassemblant le programme déployé. Pour la forme d’instruction sans argument cohérente avec un appel harvest ou claim, l’ordre réel des comptes est un préfixe fixe (token_program, le compte d’état de la farm, une PDA d’autorité de coffre, le premier coffre de récompense de cette PDA, une 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 ne font pas du tout partie de remaining_accounts.

Tester un flux CPI

Le développement local nécessite que les programmes Raydium soient disponibles dans votre validateur de test. Trois options :
  1. anchor test avec clonage de programme. Récupère le bytecode déployé sur mainnet dans votre validateur local ; consultez Cloner des programmes dans un validateur local ci-dessous pour la configuration Anchor.toml et deux points qui peuvent poser problème lors des tests de création de pool.
  2. Devnet. Raydium déploie la plupart des programmes sur devnet, mais avec des ID de programme différents de mainnet pour chaque programme (CPMM, CLMM, AMM v4, Stable AMM et LaunchLab ont chacun une adresse devnet distincte ; consultez le tableau Devnet dans reference/program-addresses). Farm v3/v5/v6 ne sont pas publiés de manière fiable sur devnet ; l’API en direct (https://api-v3-devnet.raydium.io/main/info) affiche l’état actuel. Si vous utilisez les constantes DEVNET_PROGRAM_ID fournies avec 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 tester 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 exécutez anchor deploy sur un validateur local. Cela ajoute une surcharge au cycle de test, mais vous permet de modifier le programme appelé pour le débogage.
Exécutez avec anchor test, ou anchor build d’abord puis anchor test --skip-build après si vous itérez sur le fichier de test sans modifier le programme.

Cloner des programmes dans un validateur local

Cela fonctionne par ID de programme, que la source du programme soit publique ou non, 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 ne suffit pas si votre test crée aussi un pool (plutôt que de faire un swap contre un pool existant). L’instruction initialize de CPMM valide ses comptes amm_config et create_pool_fee par rapport aux données réelles on-chain, donc vous devez les cloner aussi, sinon initialize échoue purement et simplement. Pour CPMM spécifiquement : clonez l’AmmConfig de la tranche de frais que vous voulez (récupérez son adresse depuis GET https://api-v3.raydium.io/main/cpmm-config, l’index 0 est la tranche 0,25 %) et le compte de token du destinataire des 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 swappable à 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 écart réel ≥1 seconde avant que le pool accepte les swaps. Un test qui crée un pool et fait un swap contre lui sans délai va rencontrer NotApproved. Un court await (1–2s) entre la création du pool et le premier swap suffit. C’est spécifique aux tests ; un humain exécutant deux commandes manuelles séparées ne remarquerait normalement pas cela, puisque la saisie et le démarrage du processus consomment déjà plus d’une seconde.

Ressources

Sources :