Skip to main content
Diese Seite wurde mit KI automatisch übersetzt. Maßgeblich ist stets die englische Version.Englische Version ansehen →
Diese Seite fasst die Herleitungen hinter CLMM zusammen. Für die On-Chain-Implementierung siehe products/clmm/math (das diese Seite zitiert) und products/clmm/ticks-and-positions (das das Tick-Gitter motiviert).

Warum sqrt-price statt price

CLMMs der Uniswap-v3-Familie stellen den Preis als Quadratwurzel dar, gespeichert in einem Fixed-Point-Format Q64.64:
Drei Gründe:
  1. Lineare Liquiditätsmathematik. Die Menge von Token0 oder Token1 in einem Preisbereich ist eine lineare Funktion von sqrt_price, nicht von price. Die Speicherung von sqrt_price ermöglicht es dem Swap-Schritt, diese linearen Formeln auszuwerten, ohne eine Quadratwurzel zu berechnen.
  2. Overflow-Kontrolle. sqrt_price · L passt in u256 für alle vernünftigen Parameter; price · L kann viel früher überlasten.
  3. Tick-Mathematik ist einheitlich. Da Ticks als 1.0001^i definiert sind, ist sqrt(price) = (√1.0001)^i ≈ 1.00005^i auch eine exakte geometrische Leiter. (√1.0001 = 1.00004999875…, daher driftet die Behandlung als exakt 1.00005 um bis zu ~0,6% bei MAX_TICK.) Jeder Tick-Übergang entspricht einer kleinen Multiplikation im sqrt_price_x64-Raum.
Preis und sqrt-price sind eins-zu-eins; die Umrechnung ist price = (sqrt_price_x64 / 2^64)^2.

Tick-Gitter

Preise werden auf ein Gitter diskretisiert:
tick_i ist ein i32. Der aktive Bereich ist [MIN_TICK, MAX_TICK] = [−443636, 443636], was einen Preisbereich von ungefähr [2^−64, 2^64] ergibt — etwa 5,4e-20 bis 1,8e19. Der tick_spacing jedes Pools wird durch seinen Fee-Tier festgelegt: kleinere Abstände für enge Paare (z. B. Stablecoin-0,01%-Tier verwendet Abstand 1), größere Abstände für volatile Paare (0,25%-Tier verwendet 60, 1%-Tier verwendet 120). Positionen müssen tick_lower und tick_upper an tick_spacing ausgerichtet haben. Die aktiven Ticks eines Pools (diejenigen mit Liquidität, die dort beginnt oder endet) sind die einzigen Ticks, die der Swap-Schritt berücksichtigt.

Liquidität-zu-Betrag

Für eine Position mit Liquidität L und Preisbereich [sqrt_lo, sqrt_hi] (alle sqrt_price-Werte): Herleitung: Differenzieren Sie die CPMM-Invariante lokal. Innerhalb eines einzelnen Tick-Bereichs verhält sich die Position wie ein CPMM mit virtuellen Reserven (x_v, y_v), die so gewählt sind, dass der aktuelle (sqrt_p, L) des Pools mit L = sqrt(x_v · y_v) konsistent ist. Die Integration von sqrt_p zur Bereichsgrenze ergibt die obigen Beträge. Inverse Formeln (verwendet beim Prägen einer Position für einen gegebenen amount0 oder amount1):

Single-Tick-Swap-Schritt

Innerhalb eines einzelnen Tick-Bereichs verhält sich der Pool wie ein CPMM. Gegeben aktuelles sqrt_p und Ziel sqrt_target:

Exact-Input-Schritt

Gegeben Δin_remaining:
Der 0→1-Swap senkt sqrt_p (der Preis sinkt, wenn wir Token0 verkaufen). Ein 1→0-Swap hebt ihn an. Die Formeln sind symmetrisch mit sqrt_p und sqrt_target vertauscht.

Exact-Output-Schritt

Gleiche Struktur, lösen Sie stattdessen für Δin.

Multi-Tick-Swap-Schleife

Ein Swap iteriert über Ticks, bis die Eingabe erschöpft ist oder das Preislimit erreicht wird:
Jeder single_step verwendet die aktuelle L des Pools. L ändert sich nur beim Überqueren eines initialisierten Ticks. Die Liquidität zwischen Ticks ist konstant, was die Schritt-Mathematik geschlossen macht. liquidity_net bei einem Tick ist die vorzeichenbehaftete Summe der Positionsliquiditäten, die bei diesem Tick beginnen, minus denen, die dort enden. Das Überqueren nach oben addiert liquidity_net; das Überqueren nach unten subtrahiert es. Wenn der Pool Limit-Orders bei einem Tick offen hat, verbraucht der Cross-Tick-Schritt auch opportunistisch einen Teil der Swap-Eingabe, um diese Orders zu erfüllen (FIFO über Kohorten). Der Matching-Algorithmus und der dynamische Fee-Zuschlag, der möglicherweise zusätzlich zum Basis-Schritt anfällt, sind in products/clmm/math dokumentiert; sie ändern die geschlossenen Single-Step-Formeln oben nicht.

Fee-Growth-Akkumulatoren

CLMM verfolgt Gebühren pro Einheit aktiver Liquidität, pro Seite, global und pro Tick:
Bei jedem single_step:
(Die fee_growth_global der anderen Seite bewegt sich bei diesem Schritt nicht, da kein Token auf dieser Seite als Eingabe bezahlt wurde.) Beim Überqueren eines Ticks flippt das Programm fee_growth_outside:
„Außen” ist relativ zu tick_current. Wenn tick_current über dem Tick liegt, bedeutet außen „darunter”. Wenn tick_current darunter liegt, bedeutet außen „darüber”. Der Flip tauscht die Interpretation.

fee_growth_inside für eine Position

Gegeben eine Position [tick_lower, tick_upper] und das aktuelle tick_current:
Die nicht eingezogenen Gebühren einer Position für Token-Seite s sind:
Dieses Update läuft bei jeder Interaktion mit der Position (IncreaseLiquidity, DecreaseLiquidity, CollectFees).

Durchgerechnetes Beispiel — einen Tick überqueren

Pool (vereinfacht):
  • sqrt_p_x64 = 2^64 · 1.0 = 2^64 (Preis = 1,0)
  • L = 1_000_000
  • tick_current = 0
  • Nächster initialisierter Tick darunter: tick = −60, sqrt_price = 1.0001^(−30) ≈ 0,99700, liquidity_net = −400_000 (dieser Tick beendet eine Position, daher entfernt ein Abwärts-Übergang 400k)
  • Fee-Rate: 0,25%
Swap: Δin = 10_000 Token0, Richtung = 0→1. Schritt 1 — bis sqrt_target = 0,99700 · 2^64:
3.009 < 10.000, daher füllen wir diesen Schritt vollständig:
Schritt 2 — mit neuem L = 600_000: Der nächste initialisierte Tick (sagen wir tick = −120) liegt bei sqrt = 0,99402. Berechnen Sie amount_in_to_target neu:
Immer noch weniger als Δin_remaining. Überqueren Sie erneut. Fahren Sie fort, bis Δin_remaining null erreicht. Die vollständige Abfolge von Δout akkumuliert zur endgültigen Swap-Ausgabe.

Initialisierung und Overflow-Schutz

  • MIN_SQRT_PRICE_X64 und MAX_SQRT_PRICE_X64 entsprechen tick = ±443636. Jeder Swap, der sqrt_p außerhalb dieses Bereichs drücken würde, wird rückgängig gemacht.
  • Der sqrt_price_limit-Parameter des Benutzers muss im gleichen Intervall liegen; das Programm prüft dies.
  • Produkte von L · Δsqrt werden in u256 berechnet und dann auf u128 verschoben, um Overflow zu vermeiden.

Unterschiede zu Uniswap v3

  • Oracle. Raydiums ObservationState-Ringpuffer speichert nur (block_timestamp: u32, tick_cumulative: i64) pro Slot plus [u64; 4] reserviertes Padding — es gibt kein seconds_per_liquidity_cumulative-Feld, daher sind Uniswaps liquiditätsgewichtete Ableitungen nicht on-chain verfügbar.
  • Token-2022. Raydium CLMM unterstützt Token-2022-Mints; die Transfer-Fee-Variante erfordert zusätzliche Pre/Post-Swap-Anpassungen. Siehe algorithms/token-2022-transfer-fees.
  • Tick-Bitmap. Raydium packt die initialisierte-Tick-Bitmap in [u64; 16] pro Pool für schnelle find_next_initialized_tick; Uniswap verwendet eine Pro-Wort-On-Chain-Zuordnung. Der Tradeoff ist Miete vs. Lookup-Kosten.
  • Reward-Slots. Raydium unterstützt 3 Pro-Pool-Reward-Streams mit separaten reward_growth_global_x64-Zählern; gleiche Struktur wie der Fee-Growth-Akkumulator.

Verweise

Quellen:
  • Uniswap v3 Whitepaper (kanonische Herleitung der sqrt-price-Mathematik).
  • Raydium CLMM Programmquelle.