Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
Cette page décrit la structure et le rôle de chaque compte. Les seeds sont canoniques et listés dans reference/program-addresses. Un pool CLMM est plus lourd en comptes qu’un pool CPMM car la liquidité est stockée de manière clairsemée sur la plage de ticks ; comprendre cette clairsemé est l’essentiel de cette page.

Inventaire des comptes

Un pool CLMM actif est décrit par les familles de comptes suivantes. Tous sont possédés par le programme CLMM sauf les deux mints et leurs vaults.

PoolState

L’état actif du pool, lu à chaque swap et à chaque changement de position.
Champs que vous allez réellement toucher :
  • sqrt_price_x64 et tick_current sont l’état des prix du pool. Ils sont mis à jour ensemble à chaque swap. tick_current est le plancher de log_{1.0001}(price).
  • liquidity est la liquidité active — la somme des valeurs L pour toutes les positions dont la plage contient tick_current. Elle change chaque fois qu’un swap franchit un tick et chaque fois qu’une position est ouverte/fermée/redimensionnée.
  • fee_growth_global_{0,1}_x64 sont les frais cumulatifs gagnés par unité de liquidité sur tout l’historique du pool. Les positions lisent ceci pour calculer ce qui leur est dû.
  • tick_spacing est verrouillé à AmmConfig à l’initialisation et ne change jamais. Il détermine quels indices de ticks sont autorisés à être des points de terminaison de position.
  • tick_array_bitmap est un bitmap inline couvrant la plage « proche du spot » — ±1 024 tableaux de ticks. Pour les pools dont les positions s’étendent loin, le suivi du débordement vit dans le compte TickArrayBitmapExtension séparé.
  • fee_on est fixé à la création du pool. 0 (FromInput) reproduit le comportement classique d’Uniswap-V3. 1 et 2 acheminent les frais de swap vers un seul côté du carnet — voir products/clmm/fees pour les compromis.
  • seed_index est [0, 0] pour chaque pool créé via CreatePool / CreateCustomizablePool (un pool canonique par paire). Une valeur non-zéro signifie que le pool a été créé via CreatePermissionedPool et l’index fait partie des seeds du PDA du pool, permettant à plusieurs pools de coexister pour le même (config, mint0, mint1). Pour redériver l’adresse d’un tel pool, vous devez connaître son seed_index.
  • dynamic_fee_info porte l’état de volatilité pour la surcharge de frais dynamiques. Quand activé, chaque swap recalcule un dynamic_fee_component en plus de AmmConfig.trade_fee_rate. La structure est documentée sous DynamicFeeInfo ci-dessous ; les pools sans frais dynamiques laissent la structure entière à zéro.

AmmConfig

Un ensemble typique de tiers de frais CLMM publiés (confirmez par rapport à GET https://api-v3.raydium.io/main/clmm-config) : protocol_fee_rate et fund_fee_rate sont des fractions des frais commerciaux ; même convention que CPMM. Voir products/clmm/fees.

TickArrayState

CLMM ne stocke pas un seul enregistrement par tick. Ce serait des milliards de comptes. À la place, il groupe TICK_ARRAY_SIZE ticks adjacents initialisés ou non (généralement 60 ou 88 selon la version du programme) dans un TickArrayState qui est créé paresseusement à la première utilisation.
Les quatre champs d’ordre limite sont zéro sur tout tick qui n’a jamais été utilisé pour un ordre limite. Quand des ordres sont ouverts sur un tick, le programme les suit comme une séquence de cohortes :
  • order_phase est l’id de cohorte. Il s’incrémente chaque fois qu’une cohorte passe de « tout non rempli » à « partiellement rempli ».
  • orders_amount est le total du token d’entrée de la cohorte actuelle (la plus nouvelle).
  • part_filled_orders_remaining suit la cohorte précédente qui est actuellement remplie par les swaps en cours.
  • unfilled_ratio_x64 est un multiplicateur Q64.64 porté sur la cohorte : quand un swap remplit X% de la cohorte, le ratio est multiplié par (1 − X). Chaque ordre ouvert stocke son propre snapshot (order_phase, unfilled_ratio_x64) au moment de l’ouverture, donc les mathématiques de règlement se réduisent à comparer les snapshots.
Règles :
  • Un tick de point de terminaison de position t doit satisfaire t % tick_spacing == 0. Le programme rejette les positions hors espacement.
  • Le tableau du tick est situé à floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing).
  • Un tableau de ticks est initialisé paresseusement : la première position ou le premier swap qui touche un tableau non initialisé le crée, en payant le loyer.
  • Un tableau de ticks est jamais fermé par le programme. Une fois alloué, il persiste pour la durée de vie du pool, même après que chaque tick à l’intérieur revienne à liquidity_gross == 0. Les positions et swaps ultérieurs réutilisent le compte existant sans loyer supplémentaire. Il n’y a pas de chemin de nettoyage entraîné par ClosePosition pour les tableaux de ticks.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (inline) couvre la plage « proche du spot » — ±1 024 tableaux de ticks. En dehors de cette plage (pour les valeurs de ticks extrêmes), le programme maintient un compte d’extension :
Si la plage de votre position est « normale », vous ne pensez jamais au compte d’extension. Les positions de plage complète (par exemple, (MIN_TICK, MAX_TICK)) le nécessitent ; le SDK le résout pour vous.

Positions

Une position CLMM est un ensemble de trois comptes plus un mint :

Mint du NFT de position

Un mint SPL Token ou Token-2022 avec supply 1. Le NFT de position dans le portefeuille du propriétaire est un ATA contenant ce jeton unique. Le programme lie l’autorisation au détenteur actuel du solde ATA du NFT, pas à une Pubkey stockée dans l’état. Les nouveaux mints de NFT de position définissent pool_state comme leur autorité de gel avant de frapper le jeton unique et de supprimer l’autorité de mint. Définir une autorité de gel ne gèle pas en soi le compte du NFT. Le compte reste dégelé et transférable sauf si les deux conditions sont remplies : l’appelant utilise OpenPositionV2 ou OpenPositionWithToken22Nft, et au moins l’autorité de gel d’un mint de vault sous-jacent apparaît sur la liste des émetteurs restreints de CLMM. Ce n’est que alors que CLMM gèle le compte du NFT après la frappe. Cela ne change aucun octet de PersonalPositionState ou PoolState.

PersonalPositionState

Un par position ouverte. Clé basée sur le mint du NFT.

ProtocolPositionState (déprécié)

Les versions CLMM plus anciennes stockaient la comptabilité agrégée par (pool, tick_lower, tick_upper) dans un PDA ProtocolPositionState. Les versions plus récentes ne créent ni ne lisent plus ce compte. L’emplacement apparaît toujours sur les listes de comptes OpenPosition / IncreaseLiquidity / DecreaseLiquidity comme un UncheckedAccount pour la compatibilité ABI, mais le programme n’y écrit pas. Les comptes existants on-chain sont vestigiaux ; l’admin peut appeler CloseProtocolPosition pour récupérer le loyer pour eux.La comptabilité agrégée de plage est maintenant dérivée directement des deux ticks de point de terminaison (liquidity_gross, liquidity_net, et les fee_growth_outside_* / reward_growths_outside_x64 par tick) dans TickArrayState. La formule de croissance des frais internes fee_growth_inside = global − outside_lower − outside_upper continue de fonctionner sans un compte de position agrégé.

Observation

Le buffer d’observation de CLMM stocke un tick cumulatif, pas un prix cumulatif. Les consommateurs externes calculent le prix géométrique moyen sur un intervalle à partir de (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) puis price = 1.0001 ** tick. Voir algorithms/clmm-math.

DynamicFeeConfig et DynamicFeeInfo

Les paramètres de frais dynamiques vivent à deux endroits. Le modèle réutilisable — DynamicFeeConfig — est géré par l’admin et partagé entre les pools qui s’inscrivent. L’état d’exécution par pool — DynamicFeeInfo — est intégré dans PoolState et mis à jour par chaque swap.

DynamicFeeConfig

Seed PDA : ["dynamic_fee_config", index.to_be_bytes()]. Créé via create_dynamic_fee_config (gated admin) et modifié via update_dynamic_fee_config. Un pool créé avec enable_dynamic_fee = true capture les cinq paramètres de calibrage de la config (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator) dans son propre DynamicFeeInfo à la création ; les modifications ultérieures à DynamicFeeConfig n’affectent pas rétroactivement les pools existants.

DynamicFeeInfo (intégré dans PoolState)

Les quatre champs du bas sont l’état ; les cinq du haut sont la calibrage copiée de DynamicFeeConfig. Les mathématiques des frais et les règles de décroissance sont documentées sous products/clmm/math et products/clmm/fees. Constantes utilisées par la formule :

LimitOrderState

Un compte par ordre limite ouvert.
Cycle de vie :
  1. Ouverture — l’utilisateur appelle open_limit_order, dépose total_amount du token d’entrée, l’ordre est lié à une cohorte TickState.
  2. (optionnel) Augmentation / Diminutionincrease_limit_order ajoute à total_amount ; decrease_limit_order retourne les tokens non remplis (et toute sortie réglée jusqu’à ce point).
  3. Règlement — quand la cohorte est entièrement ou partiellement remplie, le propriétaire ou le gardien opérationnel appelle settle_limit_order pour pousser les tokens de sortie vers l’ATA du propriétaire.
  4. Fermeture — une fois unfilled_amount == 0, le compte est fermable. Le loyer retourne toujours à owner.
Seed PDA : [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. Le PDA d’ordre est donc unique par (owner, nonce_index, order_nonce).

LimitOrderNonce

Compteur par (wallet, nonce_index) qui permet à un seul utilisateur d’exécuter plusieurs pipelines parallèles d’ordres limites sans collisionner sur les PDAs.
Seed PDA : [user_wallet.as_ref(), &[nonce_index]]. La plupart des clients utilisent nonce_index = 0 et laissent order_nonce porter la cardinalité.

Permission

Un compte de capacité dont l’existence est la subvention : si un PDA Permission est dérivé pour une autorité donnée, cette autorité peut appeler CreatePermissionedPool. Il ne stocke rien au-delà de l’autorité pour laquelle il a été créé.
Seed PDA : ["permission", authority.as_ref()]. Créé par un admin via CreatePermissionPda et supprimé via ClosePermissionPda (le loyer est remboursé à l’appelant). Les deux instructions admin acceptent soit l’admin du programme soit une clé permission_pda_admin dédiée. Fermer le PDA révoque la subvention — l’autorité ne peut plus créer de pools supplémentaires, mais les pools qu’elle a déjà créés ne sont pas affectés.

Dérivation des comptes clés

Les chaînes de seed exactes doivent toujours être vérifiées par rapport à l’IDL on-chain et reference/program-addresses.

Référence rapide du cycle de vie

Les comptes TickArrayState sont jamais fermés par le programme — ils persistent pour la durée de vie du pool. Une fois qu’un tableau de ticks a été initialisé, il reste on-chain même quand chaque tick à l’intérieur revient à liquidity_gross == 0. Réutiliser un tableau de ticks existant est gratuit ; seule la première position qui touche un tableau jamais initialisé paie son loyer.

Quoi lire où

Sources :