Skip to main content
Cette page est traduite automatiquement par IA. La version anglaise fait foi.Voir la version anglaise →

Pourquoi sqrt-price et non price

Les CLMMs de la famille Uniswap-v3 représentent le prix par sa racine carrée, stockée dans un Q64.64 en virgule fixe :
Trois raisons :
  1. Mathématiques linéaires de la liquidité. La quantité de token0 ou token1 dans une plage de prix s’avère être une fonction linéaire de sqrt_price, pas de price. Stocker sqrt_price permet à l’étape de swap d’évaluer ces formules linéaires sans calculer une racine carrée.
  2. Contrôle du débordement. sqrt_price · L tient dans u256 pour tous les paramètres raisonnables ; price · L peut déborder beaucoup plus tôt.
  3. Les ticks ont une mathématique uniforme. Parce que les ticks sont définis comme 1.0001^i, sqrt(price) = 1.00005^i est aussi une puissance exacte de l’échelle 1.00005. Chaque franchissement de tick se traduit par une petite multiplication dans l’espace sqrt_price_x64.
Le prix et le sqrt-price sont en correspondance biunivoque ; la conversion est price = (sqrt_price_x64 / 2^64)^2.

Treillis de ticks

Les prix sont discrétisés sur une grille :
tick_i est un i32. La plage active est [MIN_TICK, MAX_TICK] = [−443636, 443636], donnant une plage de prix d’environ [2^−128, 2^128]. Le tick_spacing de chaque pool est défini par son tier de frais : espacement plus petit pour les paires serrées (par exemple, le tier stablecoin 0,01% utilise l’espacement 1), espacement plus grand pour les paires volatiles (le tier 0,25% utilise 60, le tier 1% utilise 120). Les positions doivent avoir tick_lower et tick_upper alignés sur tick_spacing. Les ticks actifs d’un pool (ceux ayant une liquidité commençant ou se terminant là) sont les seuls ticks dont l’étape de swap se préoccupe.

Liquidité-to-amount

Pour une position avec liquidité L et plage de prix [sqrt_lo, sqrt_hi] (toutes les valeurs sqrt_price) : Dérivation : différencier l’invariant CPMM localement. À l’intérieur de n’importe quelle plage de tick unique, la position se comporte comme un CPMM avec réserves virtuelles (x_v, y_v) choisies de sorte que le (sqrt_p, L) actuel du pool soit cohérent avec L = sqrt(x_v · y_v). L’intégration de sqrt_p à la limite de la plage donne les montants ci-dessus. Formules inverses (utilisées lors de la création d’une position pour un amount0 ou amount1 donné) :

Étape de swap sur un seul tick

À l’intérieur d’une seule plage de tick, le pool se comporte comme un CPMM. Étant donné le sqrt_p actuel et le sqrt_target cible :

Étape d’entrée exacte

Donné Δin_remaining :
Le swap 0→1 abaisse sqrt_p (le prix baisse alors qu’on vend du token0). Un swap 1→0 le relève. Les formules sont symétriques avec sqrt_p et sqrt_target échangés.

Étape de sortie exacte

Même structure, résoudre pour Δin à la place.

Boucle de swap multi-ticks

Un swap itère sur les ticks jusqu’à ce que l’entrée soit épuisée ou la limite de prix atteinte :
Chaque single_step utilise le L actuel du pool. L change uniquement lors du franchissement d’un tick initialisé. La liquidité entre les ticks est constante, ce qui est ce qui rend la mathématique des étapes en forme fermée. liquidity_net à un tick est la somme signée des liquidités de position qui commencent à ce tick moins celles qui s’y terminent. Traverser vers le haut ajoute liquidity_net ; traverser vers le bas la soustrait. Quand le pool a des ordres à limite ouverts à un tick, l’étape de franchissement de tick consomme aussi opportunément une partie de l’entrée de swap pour remplir ces ordres (FIFO par cohorte). L’algorithme d’appariement et la surcharge de frais dynamiques qui peuvent s’appliquer au-dessus de l’étape de base sont documentés dans products/clmm/math ; ils ne changent pas les formules de single-step en forme fermée ci-dessus.

Accumulateurs de croissance des frais

CLMM suit les frais par unité de liquidité active, par côté, globalement et par tick :
À chaque single_step :
(Le fee_growth_global de l’autre côté ne bouge pas à cette étape, puisqu’aucun token de ce côté n’a été payé en entrée.) Lors du franchissement d’un tick, le programme bascule fee_growth_outside :
« Outside » (dehors) est relatif à tick_current. Quand tick_current est au-dessus du tick, dehors signifie « en dessous ». Quand tick_current est en dessous, dehors signifie « au-dessus ». La bascule échange l’interprétation.

fee_growth_inside pour une position

Étant donné une position [tick_lower, tick_upper] et le tick_current actuel :
Les frais non collectés d’une position pour le côté de token s sont :
Cette mise à jour s’exécute à chaque interaction avec la position (IncreaseLiquidity, DecreaseLiquidity, CollectFees).

Exemple travaillé — franchissement d’un tick

Pool (simplifié) :
  • sqrt_p_x64 = 2^64 · 1.0 = 2^64 (price = 1.0)
  • L = 1_000_000
  • tick_current = 0
  • Tick initialisé suivant en dessous : tick = −60, sqrt_price = 1.0001^(−30) ≈ 0.99700, liquidity_net = −400_000 (ce tick termine une position, donc une traversée vers le bas supprime 400k)
  • Taux de frais : 0,25%
Swap : Δin = 10_000 token0, direction = 0→1. Étape 1 — jusqu’à sqrt_target = 0.99700 · 2^64 :
3 009 < 10 000, donc on remplit cette étape complètement :
Étape 2 — avec nouveau L = 600_000 : Le prochain tick initialisé (disons tick = −120) est à sqrt = 0.99402. Recalculer amount_in_to_target :
Toujours moins que Δin_remaining. Franchir encore. Continuer jusqu’à ce que Δin_remaining atteigne zéro. La séquence complète de Δout s’accumule à la sortie de swap finale.

Initialisation et gardiens de débordement

  • MIN_SQRT_PRICE_X64 et MAX_SQRT_PRICE_X64 correspondent à tick = ±443636. Tout swap qui pousserait sqrt_p en dehors de cette plage revert.
  • Le paramètre sqrt_price_limit de l’utilisateur doit se trouver dans le même intervalle ; le programme vérifie.
  • Les produits de L · Δsqrt sont calculés en u256 puis décalés vers u128 pour éviter le débordement.

Différences par rapport à Uniswap v3

  • Oracle. L’ObservationState de Raydium stocke le buffer en anneau (block_timestamp, tick_cumulative, seconds_per_liquidity_cumulative) ; format de transmission légèrement différent d’Uniswap mais la même mathématique TWAP.
  • Token-2022. Le CLMM de Raydium supporte les mints Token-2022 ; la variante avec frais de transfert nécessite des ajustements supplémentaires des montants pré/post-swap. Voir algorithms/token-2022-transfer-fees.
  • Bitmap de ticks. Raydium empile le bitmap de tick initialisé dans [u64; 16] par pool pour un find_next_initialized_tick rapide ; Uniswap utilise un mapping on-chain par mot. Le compromis est le loyer vs le coût de recherche.
  • Slots de récompense. Raydium supporte 3 flux de récompense par pool avec des compteurs reward_growth_global_x64 séparés ; même structure que l’accumulateur de croissance des frais.

Pointeurs

Sources :
  • Livre blanc d’Uniswap v3 (dérivation canonique de la mathématique sqrt-price).
  • Code source du programme CLMM de Raydium.