Documentation Index
Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
Use this file to discover all available pages before exploring further.
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →
Représentation du prix en racine carrée
Le CLMM stocke le prix sous la formesqrt_price_x64 — la racine carrée du prix token1 par token0, comme nombre en virgule fixe Q64.64 :
où p = token1_amount / token0_amount. En travaillant en sqrt plutôt qu’en p, on linéarise les mathématiques du swap (les variations de montants de tokens deviennent linéaires en Δsqrt_price), et la virgule fixe x64 maintient la précision à travers de nombreux ticks.
La conversion tick ↔ sqrt-price est précalculée via une approximation logarithmique bit-by-bit :
implémentée comme une exponentiation basée sur recherche en table dans tick_math::get_sqrt_price_at_tick.
La liquidité comme unité canonique
À l’intérieur d’une plage[sqrt_a, sqrt_b] (avec sqrt_a < sqrt_b), une position de liquidité L correspond à des montants de tokens comme suit. Soit sqrt_c = sqrt_price_x64 le prix courant du pool.
| Cas | amount0 | amount1 |
|---|---|---|
sqrt_c <= sqrt_a (prix du pool en dessous de la plage) | L · (sqrt_b - sqrt_a) / (sqrt_a · sqrt_b) | 0 |
sqrt_a < sqrt_c < sqrt_b (dans la plage) | L · (sqrt_b - sqrt_c) / (sqrt_c · sqrt_b) | L · (sqrt_c - sqrt_a) |
sqrt_c >= sqrt_b (prix du pool au-dessus de la plage) | 0 | L · (sqrt_b - sqrt_a) |
x = L / sqrt_p, y = L · sqrt_p que la liquidité concentrée respecte dans une plage.
Les intégrateurs veulent généralement l’inverse : étant donné un dépôt de amount0 / amount1, calculer le L maximal qui rentre dans la plage. La méthode LiquidityMath.getLiquidityFromTokenAmounts du SDK le fait. La formule pour le cas dans la plage :
Celui des deux côtés qui se relie en premier détermine le ratio réellement consommé ; l’autre côté peut avoir un excédent.
Étape de swap à tick unique
Un swap se déroule par étapes. Chaque étape soit (a) consomme toute l’entrée disponible dans la plage de tick courante sans franchir un tick, soit (b) déplace le prix exactement jusqu’au prochain tick initialisé. Étant donné l’état courant(sqrt_c, L) et un swap montant (token0 in, token1 out, sqrt_price augmente), la distance jusqu’au prochain tick initialisé est sqrt_t. À l’intérieur de ce micro-intervalle, la relation entre l’entrée et le prix est :
et
Le programme fait l’une de deux choses :
-
L’entrée complète rentre-t-elle ? Si l’entrée restante (après frais) est inférieure à
Δamount0pour atteindresqrt_t, résoudre pour le nouveausqrt_c'exactement : (pour un swap exact-inputtoken0 → token1). Le swap se termine à cette étape sans franchir de tick. -
L’entrée dépasse
Δamount0? Définirsqrt_c' = sqrt_t, franchir le tick (appliquerliquidity_net), décrémenter l’entrée restante deΔamount0, incrémenter la sortie deΔamount1, et répéter.
token1 → token0, le prix baisse), les formules ont sqrt_c et sqrt_t échangés et l’inversion dans l’autre position.
L’implémentation Rust complète se trouve dans raydium-clmm/programs/amm/src/libraries/swap_math.rs. La logique correspond mot pour mot à SwapMath.computeSwapStep d’Uniswap v3.
Frais à chaque étape
Les frais commerciaux sont prélevés sur le montant d’entrée à chaque étape, même convention que le CPMM :L_i qui est restée dans la plage durant ce swap lira plus tard L_i · Δfee_growth_global / 2^{64} tokens dus.
Les portions protocole et fonds s’accumulent respectivement dans PoolState.protocol_fees_token_{0,1} et PoolState.fund_fees_token_{0,1}, identique au CPMM. Elles sont collectées par CollectProtocolFee / CollectFundFee.
Frais croissants en dehors et à l’intérieur
La partie délicate de la comptabilité des frais du CLMM : une position gagne des frais uniquement lorsque le prix du pool se trouve à l’intérieur de sa plage. Le pool suit les frais cumulatifs globalement ; la position a besoin de connaître les frais cumulatifs à l’intérieur de sa plage spécifique. La solution est un accumulateur basé sur les ticks. Chaque tick stocke :- Si le prix du pool est au-dessus de ce tick (
tick_current >= this_tick),fee_growth_outside = fee_growth_global. (Tout ce qui a été gagné jusqu’à présent est « à l’extérieur » — c’est-à-dire au-dessous — de ce tick, par rapport au prix courant.) - Sinon
fee_growth_outside = 0.
fee_growth_outside de ce tick :
L’invariant que cela préserve : pour tout tick t, fee_growth_outside(t) égale les frais qui se sont accumulés pendant que tick_current était de l’autre côté de t.
La croissance des frais à l’intérieur d’une plage [tick_lower, tick_upper] est alors dérivée :
Ce qu’une position stocke et ce qu’elle lit
UnPersonalPositionState stocke fee_growth_inside_0_last_x64 et fee_growth_inside_1_last_x64 : les valeurs fee_growth_inside à la dernière fois où la position a été touchée.
À tout toucher ultérieur (augmentation, diminution, collecte), le programme :
- Calcule le
fee_growth_inside_{0,1}_x64courant en utilisant la formule ci-dessus. - Calcule
Δ = fee_growth_inside_now − fee_growth_inside_last(soustraction modulaire sur u128). - Ajoute
Δ × position.liquidity / 2^{64}àtokens_fees_owed_{0,1}. - Met à jour
fee_growth_inside_lastà la nouvelle valeur.
CollectFees / DecreaseLiquidity, contre tokens_fees_owed.
Récompenses
Chacun des flux de récompense jusqu’à 3 du pool utilise la même machinerie croissance-dans, dans son propre accumulateurreward_growth_global_x64. Au moment de l’émission :
— les émissions se mettent à l’échelle inversement avec la liquidité active, donc un pool plus dense paie chaque position proportionnellement moins par seconde, mais sur plus de positions au total. La récompense par position due est
et est réclamée via CollectReward. Voir products/clmm/fees.
Exemple détaillé : swap exact-input
Supposons :tick_spacing = 60sqrt_price_x64 = 1 × 2^{64}— prix = 1.0, donctick_current = 0.- Liquidité active
L = 1_000_000 × 2^{64}. - Prochain tick initialisé au-dessus :
t = 60(sqrt_price_b ≈1.003004 × 2^{64}). - Taux de frais commerciaux : 500 (0.05%).
SwapBaseInput exact-input 1 000 token0.
Étape 1 — frais :
999 < 2995.5, donc l’entrée complète rentre sans franchir le tick.
Étape 3 — nouveau prix :
sqrt_c' légèrement en dessous de sqrt_c. Note que la formule ci-dessus est pour un swap token1 → token0. L’exemple ici est token0 → token1, qui pousse le prix vers le haut, pas vers le bas — donc on utilise la forme correspondante pour token0 in :
token0 → token1 : sqrt_c monte avec le prix.)
Étape 4 — montant sortant :
trade_fee_rate × protocol_fee_rate / 1e6 (et similaire pour fonds) ; la portion LP s’écoule dans fee_growth_global_0_x64.
Correspondance des ordres à limite lors du swap
Quand une étape de swap franchit un tick qui contient des ordres à limite ouverts, ces ordres consomment l’entrée du swap avant que la courbe LP le fasse, au prix exact du tick. La correspondance est FIFO au sein du tick par cohorteorder_phase.
État par cohorte sur TickState
orders_amount et héritent de la prochaine order_phase ; ils ne peuvent pas se remplir jusqu’à ce que la cohorte précédente soit entièrement consommée.
Étape de correspondance
Pseudo-code pour la correspondance qui se produit à chaque franchissement de tick lors d’un swap :SettleLimitOrder (ou DecreaseLimitOrder). Le pool suit simplement combien de la cohorte est maintenant remplie via unfilled_ratio_x64. Chaque LimitOrderState stocke son propre snapshot (order_phase, unfilled_ratio_x64) au moment de l’ouverture, donc le règlement se réduit à :
Interaction avec la courbe LP
Dans une étape de swap, la correspondance des ordres à limite se produit au tick (zéroΔsqrt_price) ; la consommation de la courbe LP se produit entre les ticks. L’ordre est donc :
- Franchir le tick
t_cross(appliquer d’abord le changement LPliquidity_net, puisque c’est ainsi qu’Uniswap-V3 le fait). - Remplir tous les ordres à limite assis à
t_cross. - Continuer le long de la courbe LP jusqu’au prochain tick initialisé ou jusqu’à épuisement de
swap_input.
Dérivation des frais dynamiques
PoolState.dynamic_fee_info porte l’état de volatilité. Chaque étape de swap calcule le taux de frais par étape comme :
où :
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_accest l’accumulateur par-swap après la règle de mise à jour ci-dessoustick_spacingest dePoolState.tick_spacing
Mise à jour de l’accumulateur
Deux règles sont appliquées à chaque swap, dans l’ordre : Décroissance. L’étage de référence décroît en fonction du temps depuis la dernière mise à jour : Accumulation. Le nouvel accumulateur est la référence plus la distance de tick traversée depuis l’indice de référence précédent :tick_spacing_index_reference () est en unités de tick-spacing, pas en ticks bruts : .
Pourquoi parabolique en distance de tick
Mettre au carré l’accumulateur signifie que les frais augmentent comme le carré de la distance que le prix a parcourue loin de son point de référence. Empiriquement, cela correspond à la mise à l’échelle de la variance du prix sous pression de marche aléatoire : une excursion de tick de 2× implique 4× la volatilité implicite, donc charge 4× la surcharge. Le paramètredynamic_fee_control calibre le niveau absolu.
La fenêtre filter_period empêche les minuscules oscillations sub-seconde (par exemple, les bots MEV sandwich) de gonfler l’accumulateur. La fenêtre decay_period empêche un pic passé unique de charger des frais indéfiniment après que le marché se soit calmé.
Robustesse numérique
- Tous les produits intermédiaires passent par l’arithmétique de forme
u128ouu256. CLMM utilise les aidesU128Sqrtet les motifsFullMath::mulDivdirectement portés d’Uniswap v3. - L’arrondi de la division est choisi par étape pour appliquer l’invariant
k' ≥ klocalement.SwapBaseInputarrondit la sortie vers le bas ;SwapBaseOutputarrondit l’entrée vers le haut. - Les franchissements de tick qui abaissent
PoolState.liquidityà zéro sont autorisés (le prix peut traverser un « trou de liquidité ») mais le swap avance simplement vers le prochain tick initialisé sans consommer d’entrée, sans charger de frais. - Garde de débordement :
sqrt_price_x64est maintenu dans la plage inclusive[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]correspondant à[MIN_TICK, MAX_TICK]. Un swap qui pousserait au-delà d’une limite s’annule avecSqrtPriceLimitOverflow.
Où aller ensuite
products/clmm/ticks-and-positionspour comment la carte de ticks participe à la marche.products/clmm/feespour le côté frais/récompense des mathématiques en détail.algorithms/clmm-mathpour les dérivations derrièreL = sqrt(x · y)et les formules plage-vs-liquidité.
raydium-io/raydium-clmm—libraries/swap_math.rs,libraries/tick_math.rs- « Uniswap v3 Core » whitepaper, §6–7


