Diese Seite wurde mit KI automatisch übersetzt. Maßgeblich ist stets die englische Version.Englische Version ansehen →
Sqrt-Preis-Darstellung
CLMM speichert den Preis alssqrt_price_x64 — die Quadratwurzel des Token1-pro-Token0-Preises, als Q64.64-Festkommazahl:
wobei p = token1_amount / token0_amount. Die Arbeit mit sqrt statt p linearisiert die Swap-Mathematik (Token-Mengen-Deltas werden linear in Δsqrt_price), und die x64-Festkommadarstellung erhält die Präzision über viele-Tick-Swaps hinweg.
Die Tick ↔ Sqrt-Preis-Konvertierung wird über eine Bit-für-Bit-Logarithmus-Approximation vorberechnet:
implementiert als nachschlagbasierte Exponentiation in tick_math::get_sqrt_price_at_tick.
Liquidität als kanonische Einheit
Innerhalb eines Bereichs[sqrt_a, sqrt_b] (mit sqrt_a < sqrt_b) ordnet sich eine Position mit Liquidität L wie folgt Token-Mengen zu. Sei sqrt_c = sqrt_price_x64 der aktuelle Preis des Pools.
Alle drei Identitäten folgen aus der Invariante
x = L / sqrt_p, y = L · sqrt_p, die konzentrierte Liquidität innerhalb eines Bereichs erfüllt.
Integratoren wünschen sich typischerweise die Umkehrung: Gegeben eine Einzahlung von amount0 / amount1, berechne das maximale L, das in den Bereich passt. Das SDK’s LiquidityMath.getLiquidityFromTokenAmounts macht dies. Die Formel für den In-Bereich-Fall:
Welche Seite bindet, bestimmt das tatsächlich verbrauchte Verhältnis; die andere Seite kann Reste haben.
Single-Tick-Swap-Schritt
Ein Swap verläuft in Schritten. Jeder Schritt entweder (a) verbraucht die gesamte verfügbare Eingabe innerhalb des aktuellen Tick-Bereichs, ohne einen Tick zu kreuzen, oder (b) bewegt den Preis genau zum nächsten initialisierten Tick. Gegeben der aktuelle Zustand(sqrt_c, L) und ein Swap nach unten (Token0 rein, Token1 raus, sqrt_price sinkt — Preis wird als token1/token0 notiert, also senkt das Hinzufügen von Token0 ihn), sitzt der nächste initialisierte Tick darunter bei sqrt_t < sqrt_c. Innerhalb dieses Mikro-Intervalls ist die Beziehung zwischen Eingabe und Preis:
und
Ein Token1-Eingabe-Swap ist das Spiegelbild: sqrt_price steigt zum nächsten Tick oben, und die beiden Formeln tauschen, welcher Endpunkt subtrahiert wird. Das Programm kodiert die Richtung als zero_for_one (true, wenn die Eingabe-Mint token_mint_0 ist), und ein zero_for_one-Swap klemmt gegen MIN_SQRT_PRICE_X64.
Das Programm macht eines von zwei Dingen:
-
Passt die gesamte Eingabe? Wenn die verbleibende Eingabe (nach Gebühr) kleiner als
Δamount0ist, umsqrt_tzu erreichen, löse für das neuesqrt_c'exakt: (für einen exakten-Eingabetoken0 → token1Swap). Der Swap endet in diesem Schritt, ohne einen Tick zu kreuzen. -
Eingabe übersteigt
Δamount0? Setzesqrt_c' = sqrt_t, kreuze den Tick (wendeliquidity_netan), dekrementiere verbleibende Eingabe umΔamount0, inkrementiere Ausgabe umΔamount1, und wiederhole.
token1 → token0, Preis geht nach unten), haben die Formeln sqrt_c und sqrt_t getauscht und die Inversion im anderen Slot.
Die vollständige Rust-Implementierung lebt in raydium-clmm/programs/amm/src/libraries/swap_math.rs. Die Logik dort entspricht Uniswap v3’s SwapMath.computeSwapStep eins-zu-eins.
Gebühren bei jedem Schritt
Handelsgebühren werden von der Eingabe-Menge in jedem Schritt abgezogen, gleiche Konvention wie CPMM:L_i, die über diesen Swap im Bereich blieb, später L_i · Δfee_growth_global / 2^{64} geschuldete Token zurückliest.
Die Protokoll- und Fund-Anteile sammeln sich in PoolState.protocol_fees_token_{0,1} und PoolState.fund_fees_token_{0,1} an, identisch mit CPMM. Sie werden durch CollectProtocolFee / CollectFundFee eingezogen.
Gebühren-Wachstum außerhalb und innerhalb
Der knifflige Teil der CLMM-Gebühren-Buchhaltung: Eine Position verdient Gebühren nur, während der Pool-Preis innerhalb ihres Bereichs ist. Der Pool verfolgt kumulative Gebühren global; die Position muss die kumulativen Gebühren während sie in ihrem spezifischen Bereich ist kennen. Die Lösung ist ein Tick-basierter Akkumulator. Jeder Tick speichert:- Wenn der Pool-Preis über diesem Tick ist (
tick_current >= this_tick), istfee_growth_outside = fee_growth_global. (Alles bisher Verdiente ist „außerhalb” — d.h., unterhalb — dieses Ticks, relativ zum aktuellen Preis.) - Sonst
fee_growth_outside = 0.
fee_growth_outside dieses Ticks:
Die Invariante, die dies bewahrt: Für jeden Tick t ist fee_growth_outside(t) gleich den Gebühren, die aufgelaufen sind, während tick_current auf der entgegengesetzten Seite von t war.
Gebühren-Wachstum innerhalb eines Bereichs [tick_lower, tick_upper] wird dann abgeleitet:
Was eine Position speichert und was sie liest
EinPersonalPositionState speichert fee_growth_inside_0_last_x64 und fee_growth_inside_1_last_x64: die fee_growth_inside-Werte beim letzten Mal, als die Position berührt wurde.
Bei jeder nachfolgenden Berührung (Erhöhung, Verringerung, Einzug) macht das Programm:
- Berechnet den aktuellen
fee_growth_inside_{0,1}_x64mit der obigen Formel. - Berechnet
Δ = fee_growth_inside_now − fee_growth_inside_last(Modular-Subtraktion auf u128). - Addiert
Δ × position.liquidity / 2^{64}zutokens_fees_owed_{0,1}. - Aktualisiert
fee_growth_inside_lastauf den neuen Wert.
CollectFees / DecreaseLiquidity, gegen tokens_fees_owed.
Belohnungen
Jeder der bis zu 3 Belohnungsströme des Pools verwendet die gleiche Growth-Inside-Maschinerie, in seinem eigenenreward_growth_global_x64-Akkumulator. Zur Emissionszeit:
— Emissionen skalieren umgekehrt mit aktiver Liquidität, sodass ein dichterer Pool jeder Position proportional weniger pro Sekunde zahlt, aber über mehr Positionen insgesamt. Die pro-Position geschuldete Belohnung ist
und wird durch DecreaseLiquidity / DecreaseLiquidityV2 ausgezahlt — es gibt keine eigenständige Collect-Reward-Anweisung. Siehe /de/products/clmm/fees.
Durchgerechnetes Beispiel: Exakte-Eingabe-Swap
Angenommen:tick_spacing = 60sqrt_price_x64 = 1 × 2^{64}— Preis = 1,0, alsotick_current = 0.- Aktive Liquidität
L = 1_000_000 × 2^{64}. - Nächster initialisierter Tick darunter:
t = −60(sqrt_price_b ≈0.997005 × 2^{64}). Ein Token0-Eingabe-Swap bewegt sich nach unten, also ist der Tick oben hier irrelevant. - Handelsgebührsatz: 500 (0,05%).
SwapBaseInput exakte Eingabe 1.000 Token0.
Schritt 1 — Gebühren:
999 < 3004.4, also passt die gesamte Eingabe ohne Tick-Kreuzen.
Schritt 3 — neuer Preis:
sqrt_c' landet leicht unter sqrt_c, was die richtige Richtung ist: Ein token0 → token1 Swap fügt Token0 zum Pool hinzu und senkt daher den token1/token0-Preis.
Schritt 4 — Ausgabemenge:
trade_fee_rate × protocol_fee_rate / 1e6 (und ähnlich für Fund) aufgeteilt; der LP-Anteil fließt in fee_growth_global_0_x64.
Limit-Order-Matching während Swap
Wenn ein Swap-Schritt einen Tick kreuzt, der offene Limit-Orders hält, verbrauchen diese Orders Swap-Eingabe vor der LP-Kurve, zum exakten Preis des Ticks. Das Matching ist FIFO innerhalb des Ticks nachorder_phase-Kohorte.
Pro-Kohorte-Zustand auf TickState
orders_amount bei und erben die nächste order_phase; sie können nicht füllen, bis die vorherige Kohorte vollständig verbraucht ist.
Matching-Schritt
Pseudo-Code für das Matching, das bei jedem Tick-Kreuzen während eines Swaps passiert:SettleLimitOrder (oder DecreaseLimitOrder) aufruft. Der Pool verfolgt einfach, wie viel der Kohorte jetzt gefüllt ist, über unfilled_ratio_x64. Jeder LimitOrderState speichert seinen eigenen (order_phase, unfilled_ratio_x64)-Snapshot zur Öffnungszeit, sodass die Abwicklung sich reduziert auf:
Interaktion mit der LP-Kurve
In einem Swap-Schritt passiert Limit-Order-Matching am Tick (NullΔsqrt_price); LP-Kurven-Verbrauch passiert zwischen Ticks. Die Reihenfolge ist daher:
- Kreuze Tick
t_cross(wende LPliquidity_net-Änderung zuerst an, da dies wie Uniswap-V3 es macht). - Fülle alle Limit-Orders, die bei
t_crosssitzen. - Fahre entlang der LP-Kurve zum nächsten initialisierten Tick oder bis
swap_input-Erschöpfung fort.
Dynamische Gebühren-Ableitung
PoolState.dynamic_fee_info trägt den Volatilitätszustand. Jeder Swap-Schritt berechnet den Pro-Schritt-Gebührensatz als:
wobei:
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_accist der Pro-Swap-Akkumulator nach der Aktualisierungsregel untentick_spacingist vonPoolState.tick_spacing
Akkumulator-Aktualisierung
Zwei Regeln werden bei jedem Swap angewendet, in Reihenfolge: Verfall. Der Referenz-Boden verfällt basierend auf der Zeit seit der letzten Aktualisierung: Akkumulieren. Der neue Akkumulator ist die Referenz plus Tick-Distanz, die seit dem vorherigen Referenz-Index durchquert wurde:tick_spacing_index_reference () ist in Tick-Spacing-Einheiten, nicht rohen Ticks: .
Warum parabolisch in Tick-Distanz
Das Quadrieren des Akkumulators bedeutet, dass die Gebühr als das Quadrat steigt, wie weit der Preis von seinem Referenzpunkt entfernt gelaufen ist. Empirisch entspricht dies der Varianz-Skalierung des Preises unter Random-Walk-Druck: Eine 2×-Tick-Ausflug impliziert 4× die implizite Volatilität, also berechnet 4× den Zuschlag. Derdynamic_fee_control-Parameter kalibriert das absolute Niveau.
Das filter_period-Fenster verhindert, dass winzige Sub-Sekunden-Oszillationen (z.B. MEV-Bots, die Sandwiching betreiben) den Akkumulator aufblähen. Das decay_period-Fenster verhindert, dass ein einzelner vergangener Spike Gebühren auf unbestimmte Zeit berechnet, nachdem der Markt sich beruhigt hat.
Numerische Robustheit
- Alle Zwischenprodukte gehen durch
u128- oderu256-förmige Arithmetik. CLMM verwendetU128Sqrt-Helfer undFullMath::mulDiv-Muster, direkt von Uniswap v3 portiert. - Divisions-Rundung wird pro-Schritt gewählt, um die Invariante
k' ≥ klokal durchzusetzen.SwapBaseInputrundet Ausgabe ab;SwapBaseOutputrundet Eingabe auf. - Tick-Kreuze, die
PoolState.liquidityauf Null senken, sind erlaubt (der Preis kann ein „Liquiditätsloch” durchqueren), aber der Swap rückt einfach zum nächsten initialisierten Tick vor, ohne Eingabe zu verbrauchen, und berechnet keine Gebühr. - Overflow-Schutz:
sqrt_price_x64wird im inklusiven Bereich[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]gehalten, entsprechend[MIN_TICK, MAX_TICK]. Ein Swap, der eine Grenze überschreiten würde, wird mitSqrtPriceLimitOverflowzurückgewiesen.
Wo es weitergeht
/de/products/clmm/ticks-and-positionsfür wie die Tick-Karte am Spaziergang teilnimmt./de/products/clmm/feesfür die Gebühren-/Belohnungs-Seite der Mathematik im Detail./de/algorithms/clmm-mathfür die Ableitungen hinterL = sqrt(x · y)und den Bereichs-vs-Liquiditäts-Formeln.
raydium-io/raydium-clmm—libraries/swap_math.rs,libraries/tick_math.rs- “Uniswap v3 Core” Whitepaper, §6–7

