Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
L’ID du programme et les seeds des PDAs pour CPMM sont listés de manière canonique dans reference/program-addresses. Cette page se concentre sur ce que chaque compte fait et les invariants qu’il maintient, non sur les adresses codées en dur.

Les six comptes d’un pool CPMM

Chaque pool CPMM est entièrement décrit par six adresses dérivées du programme (PDAs) sous le programme CPMM, plus un compte AmmConfig partagé qu’il référence. Une fois que vous avez les deux mints, vous pouvez dériver tout de manière déterministe sans toucher au réseau. Et la config partagée : Et un compte optionnel par créateur :

Dériver un pool à partir de rien d’autre que deux mints

Triez toujours les mints avant de dériver le PDA du pool. La seed hache les deux mints dans l’ordre des octets, pas dans l’ordre de l’utilisateur. Deux pools avec (A, B) et (B, A) entreraient en collision on-chain — le tri est comment le programme rend le mapping canonique.
L’ID du pool n’est pas toujours le PDA canonique. Initialize accepte une keypair signataire arbitraire comme pool_state en plus du PDA ci-dessus. Si le compte passé ne correspond pas au PDA canonique, le programme exige qu’il soit un signataire — c’est-à-dire que le créateur passe une nouvelle keypair qu’il signe. C’est la défense contre le front-running : tout tiers qui court pour saisir le PDA canonique peut être contourné par le créateur légitime en utilisant une keypair aléatoire à la place. Les PDAs en aval (lpMint, vault0, vault1, observation) sont toujours dérivés de poolState.key(), donc ils restent uniques à l’adresse utilisée. Quand vous indexez les pools, découvrez toujours l’ID du pool à partir de l’état on-chain (par exemple, les comptes PoolState sous le programme CPMM), pas en dérivant le PDA canonique — ce dernier manquera les pools avec keypair aléatoire.

Layouts des comptes

Les définitions Rust complètes se trouvent dans la source raydium-cp-swap. Les champs ci-dessous sont ceux que vous lirez lors d’une intégration.

PoolState

Ce qu’il faut vraiment lire :
  • lp_supply — le total LP interne du pool. Il n’est pas égal à la supply du mint LP : il est exactement 100 unités de base plus élevé, car les 100 unités verrouillées sont comptées ici mais jamais mintées. Toutes les mathématiques de part LP (dépôt, retrait) divisent par lp_supply, donc utilisez ce champ et ne substituez pas la supply on-chain du mint.
  • protocol_fees_token{0,1}, fund_fees_token{0,1} — frais accumulés non encore collectés. Ceux-ci n’affectent pas la tarification des swaps ; ils restent dans les vaults jusqu’à ce que CollectProtocolFee / CollectFundFee soit appelé. protocol_fees_token{0,1} reçoit également la part du protocole des frais de créateur quand un frais de créateur est collecté, donc il augmente en dehors des swaps — voir products/cpmm/fees.
  • status — un masque de bits contrôlant si Swap, Deposit, Withdraw sont autorisés. Mis à jour par l’admin via UpdatePoolStatus. Le SDK vérifie cela avant de construire une transaction ; si vous faites du CPI directement, vérifiez-le vous-même.
  • token0_program / token1_program — le programme de token dans lequel faire du CPI pour chaque vault. L’un peut être le SPL Token classique et l’autre Token-2022 ; ils sont indépendants.
  • open_time — un timestamp Unix. Les swaps avant cette heure échouent. Les dépôts sont autorisés avant open_time pour que le pool puisse être amorcé.
  • creator_fee_on / enable_creator_fee — ensemble contrôlent si le frais de créateur optionnel est actif pour ce pool et de quel côté du swap il est collecté. enable_creator_fee == false annule entièrement le chemin des frais de créateur. Quand activé, creator_fee_on sélectionne : 0 = prendre le frais du token qui est l’entrée du swap (BothToken) ; 1 = prendre le frais de token_0 uniquement (ignorer sur les swaps token_1 → token_0) ; 2 = prendre le frais de token_1 uniquement. Défini à la création du pool via InitializeWithPermission ; ne peut pas changer plus tard.
  • creator_fees_token_{0,1} — frais de créateur accumulés, collectés par CollectCreatorFee ou CollectCreatorFeePermissionless. Les deux chemins remettent les compteurs à zéro, mais depuis la mise à jour creator-fee-share du 2026-09-19 seulement une partie du solde quitte le pool : la part du protocole est ajoutée à protocol_fees_token_{0,1} et le reste est transféré au créateur. Le chemin sans permission fixe les destinataires aux ATAs canoniques de pool_creator. PoolState lui-même n’a pas changé — il n’y a pas de compteur séparé pour le montant partagé.

AmmConfig

Trois choses à faire attention :
  1. trade_fee_rate et creator_fee_rate sont des fractions du volume, tous deux dénominés en unités de 1/1_000_000. 2500 signifie 0,25% du volume du trade. protocol_fee_rate et fund_fee_rate sont des fractions des frais de trading (pas du volume), dans le même dénominateur 1/1_000_000. Le frais de créateur n’est pas une fraction des frais de trading — c’est son propre taux indépendant. L’arithmétique complète est dans products/cpmm/fees.
  2. index est un u16, donc la seed hash utilise 2 octets big-endian. Une erreur d’un dans l’ordre des octets est un bug d’intégration courant.
  3. AmmConfig est immuable au niveau du pool. Un pool pointe vers un AmmConfig à la création et ne change jamais. Les changements de frais se propagent car le pool lit la config à chaque swap — mais le pool ne peut pas être déplacé entre les tiers de frais.
Une note sur les frais de créateur : le taux lui-même (creator_fee_rate) vit sur AmmConfig et est partagé entre le tier de frais. Si un pool particulier le facture réellement (enable_creator_fee) et de quel côté du swap il atterrit (creator_fee_on) vivent sur PoolState. Le frais de créateur est indépendant du frais de trading — c’est son propre taux, accumulé à ses propres compteurs (creator_fees_token_{0,1}), et ne réduit jamais les parts LP / protocole / fonds des frais de trading. La collecte se fait via CollectCreatorFee ou le CollectCreatorFeePermissionless contraint par destination, et les deux chemins donnent une part du solde accumulé au protocole en sortant — au creator_fee_share_rate, ou au taux sur un PDA CreatorFeeShare quand il existe pour cette paire (creator, amm_config). Voir products/cpmm/fees pour la mécanique complète.

Permission

Un petit compte de contrôle d’accès utilisé par InitializeWithPermission. Le programme CPMM supporte un chemin de création de pool avec permission pour que d’autres programmes (par ex. LaunchLab lors de la graduation d’un token vers CPMM) puissent prouver qu’ils sont autorisés à créer un pool contre un AmmConfig donné.
Le PDA Permission est créé via CreatePermissionPda par soit l’admin CPMM soit une autorité dédiée de créateur de PDA de permission. Depuis la mise à jour 2026-09, ClosePermissionPda accepte les deux mêmes signataires ; avant c’était admin-only. Les utilisateurs finaux n’interagissent pas directement avec ce compte — c’est de la plomberie pour les flux cross-program. Voir security/admin-and-multisig pour la limite de rôle et reference/program-addresses pour les adresses canoniques.

CreatorFeeShare

Un compte optionnel qui remplace la part du protocole des frais de créateur pour une paire (créateur de pool, AmmConfig). Ajouté par la mise à jour creator-fee-share du 2026-09-19.
Comment il se comporte :
  • Il est optionnel, mais le compte n’est jamais optionnel dans l’instruction. CollectCreatorFee et CollectCreatorFeePermissionless déclarent tous deux creator_fee_share avec la contrainte de seed ci-dessus et le prennent à chaque appel. Le programme vérifie ensuite si le compte est vide ou possédé par un tiers ; si c’est le cas, il revient à AmmConfig.creator_fee_share_rate. Donc un client doit toujours dériver et passer l’adresse, que le compte existe ou non.
  • share_rate est plafonné à FEE_RATE_DENOMINATOR_VALUE (1_000_000) à la création, et à nouveau quand le split s’exécute. 1_000_000 route l’intégralité des frais de créateur au protocole ; 0 n’en route aucun.
  • Créé et fermé par l’admin ou une autorité dédiée via CreateCreatorFeeShare / CloseCreatorFeeShare. Le fermer retourne le loyer au signataire et fait revenir la paire à la valeur par défaut de la config ; le créateur du pool n’est pas un signataire sur l’un ou l’autre chemin.
  • Il est indexé sur le créateur, pas sur le pool. Un compte gouverne chaque pool que ce créateur a sur ce AmmConfig. Un créateur avec des pools sur deux tiers de frais a besoin de deux comptes pour être couvert sur les deux.
L’arithmétique du split qu’il pilote est dans products/cpmm/fees.

Vaults et Token-2022

vault0 et vault1 sont possédés par le PDA d’autorité CPMM, et leur propriétaire de programme de token (token_program) est soit SPL Token soit Token-2022, déterminé à la création du pool par le programme du mint. Le pool gère les deux cas de manière transparente — vous passez le bon ID de programme de token pour chaque côté dans les comptes d’instruction Swap / Deposit / Withdraw. CPMM applique une stricte liste blanche d’extensions à la création du pool (is_supported_mint dans utils/token.rs). Un mint Token-2022 ne peut être utilisé dans un pool CPMM que si chaque extension qu’il porte est sur cette liste :
  • TransferFeeConfig. Appliqué par le mint à chaque transfert. Le pool est du côté récepteur pour les dépôts SwapBaseInput et du côté envoyeur pour les retraits. Le programme calcule le montant net atterrissant dans le vault et définit la courbe en conséquence. Voir algorithms/token-2022-transfer-fees.
  • MetadataPointer et TokenMetadata. Métadonnées standard on-mint. Aucun effet sur les mathématiques de swap.
  • InterestBearingConfig. Le montant UI du mint accumule des intérêts. Le vault stocke les montants bruts ; la courbe opère uniquement sur les montants bruts. Les UIs qui affichent l’APR doivent appeler les helpers Token-2022 pour rendre le montant UI.
  • ScaledUiAmount. Extension de mise à l’échelle d’affichage UI. Même traitement que InterestBearingConfig — la courbe utilise les montants bruts.
Toute autre extension — PermanentDelegate, TransferHook, DefaultAccountState, NonTransferable, ConfidentialTransfer, Group/GroupMember, MintCloseAuthority, etc. — cause le rejet de Initialize avec NotSupportMint. L’une exception est un registre par mint : si un PDA SupportMintAssociated existe à la seed [b"support_mint", mint], le mint est admis indépendamment de son ensemble d’extensions. Ce PDA est créé et supprimé par l’admin (ou une autorité dédiée de support-mint) via CreateSupportMintAssociated / CloseSupportMintAssociated, donc l’intégration d’un mint spécifique n’a plus besoin d’une mise à jour du programme.
Changé en 2026-09. CPMM portait auparavant aussi une MINT_WHITELIST codée en dur à quatre adresses qui court-circuitait la vérification d’extension. Ce tableau a été supprimé ; le registre PDA est maintenant le seul contournement. Tout mint qui dépendait de la liste codée en dur a besoin d’un PDA SupportMintAssociated avant qu’un nouveau pool puisse être créé pour lui — les pools existants ne sont pas affectés, car la vérification s’exécute uniquement à la création du pool.
La liste des extensions vérifiées vit dans la source CP-Swap sous programs/cp-swap/src/utils/token.rs et peut changer avec les futures mises à jour du programme. Voir reference/token-2022-support pour la matrice cross-program.

Observation

Le compte d’observation est un ring buffer d’entrées ObservationState, chacune stockant un block_timestamp et un prix cumulatif. À chaque swap, le programme ajoute une nouvelle observation si suffisamment de temps s’est écoulé depuis la dernière. Les TWAPs sont calculés en lisant deux observations et en divisant Δcumulative / Δtime.
Le ring buffer est dimensionné pour 100 observations. Chaque observation fait 40 octets (8 + 16 + 16), donc le tableau seul fait 4 000 octets ; ObservationState::LEN est exactement 4 075 octets (8 + 1 + 2 + 32 + 4,000 + 8 × 4). Deux règles pour les consommateurs :
  • N’utilisez pas une seule observation comme prix. C’est un cumulatif, pas un prix spot. Utilisez deux d’entre elles pour calculer un TWAP.
  • Choisissez des observations au moins un bloc à part. Les swaps dans le même bloc peuvent ne pas produire une nouvelle observation ; relire dos à dos peut retourner le même enregistrement.
Plus de mathématiques dans products/clmm/accounts.

Cycle de vie des comptes

Les pools CPMM et leurs PDAs ne sont jamais fermés. Permission, SupportMintAssociated et CreatorFeeShare sont les exceptions — ce sont des enregistrements gérés par l’admin autonomes, pas l’état du pool, et chacun a une instruction de fermeture explicite. Même à liquidité zéro, le poolState reste. C’est délibéré : réamorcer le même pool plus tard préserve son buffer d’observations historiques et sa dérivation PDA reste stable.

Où lire quoi

Sources :