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.
Diese Seite wurde mit KI automatisch übersetzt. Maßgeblich ist stets die englische Version.Englische Version ansehen →
L = sqrt(x · y) wichtig ist — siehe algorithms/clmm-math. Diese Seite setzt voraus, dass Sie diese gelesen haben.
Sqrt-Preis-Darstellung
CLMM speichert den Preis alssqrt_price_x64 — die Quadratwurzel des Token1-pro-Token0-Preises, als Q64.64 Fixed-Point-Zahl:
wobei p = token1_amount / token0_amount. Das Arbeiten mit sqrt statt p linearisiert die Tausch-Mathematik (Token-Betrags-Deltas werden linear in Δsqrt_price), und die x64 Fixed-Point-Arithmetik erhält die Präzision über viele Ticks.
Tick ↔ Sqrt-Preis-Umrechnung wird über eine Bit-für-Bit-Log-Approximation vorberechnet:
implementiert als lookup-basierte 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) wird eine Position mit Liquidität L folgendermaßen auf Token-Beträge abgebildet. Sei sqrt_c = sqrt_price_x64 der aktuelle Preis des Pools.
| Fall | amount0 | amount1 |
|---|---|---|
sqrt_c <= sqrt_a (Pool-Preis unter Bereich) | L · (sqrt_b - sqrt_a) / (sqrt_a · sqrt_b) | 0 |
sqrt_a < sqrt_c < sqrt_b (im Bereich) | L · (sqrt_b - sqrt_c) / (sqrt_c · sqrt_b) | L · (sqrt_c - sqrt_a) |
sqrt_c >= sqrt_b (Pool-Preis über Bereich) | 0 | L · (sqrt_b - sqrt_a) |
x = L / sqrt_p, y = L · sqrt_p, die konzentrierte Liquidität innerhalb eines Bereichs erfüllt.
Integratoren möchten normalerweise das Inverse: Bei einer Einzahlung von amount0 / amount1 das maximale L berechnen, das in den Bereich passt. Die SDK-Methode LiquidityMath.getLiquidityFromTokenAmounts macht dies. Die Formel für den In-Range-Fall:
Welche Seite bindend ist, bestimmt das tatsächlich verbrauchte Verhältnis; die andere Seite kann Reste haben.
Einzelner Tick-Tausch-Schritt
Ein Tausch wird in Schritten durchgeführt. Jeder Schritt entweder (a) verbraucht die gesamte verfügbare Eingabe innerhalb des aktuellen Tick-Bereichs, ohne einen Tick zu überschreiten, oder (b) bewegt den Preis exakt zum nächsten initialisierten Tick. Gegeben den aktuellen Zustand(sqrt_c, L) und einen Tausch nach oben (Token0 rein, Token1 raus, sqrt_price steigt), ist die Distanz zum nächsten initialisierten Tick sqrt_t. Innerhalb dieses Mikro-Intervalls ist die Beziehung zwischen Eingabe und Preis:
und
Das Programm macht eines von zwei Dingen:
-
Passt die gesamte Eingabe? Wenn die verbleibende Eingabe (nach Gebühren) kleiner ist als
Δamount0umsqrt_tzu erreichen, lösen Sie für das neuesqrt_c'exakt auf: (für einen exaktentoken0 → token1Tausch). Der Tausch endet in diesem Schritt, ohne einen Tick zu überschreiten. -
Eingabe übersteigt
Δamount0? Setzen Siesqrt_c' = sqrt_t, überschreiten Sie den Tick (wenden Sieliquidity_netan), dekrementieren Sie die verbleibende Eingabe umΔamount0, inkrementieren Sie die Ausgabe umΔamount1, und wiederholen Sie.
token1 → token0, Preis geht hinunter) haben die Formeln sqrt_c und sqrt_t vertauscht und die Inversion im anderen Slot.
Die vollständige Rust-Implementierung befindet sich 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 vom Eingabe-Betrag in jedem Schritt abgezogen, gleiche Konvention wie CPMM:L_i, die während dieses Tauschs im Bereich geblieben ist, später L_i · Δfee_growth_global / 2^{64} fällige Token zurückliest.
Die Protocol- und Fund-Anteile sammeln sich in PoolState.protocol_fees_token_{0,1} bzw. PoolState.fund_fees_token_{0,1} an, identisch mit CPMM. Sie werden von CollectProtocolFee / CollectFundFee eingezogen.
Gebühren-Wachstum außerhalb und innerhalb
Der knifflige Teil der CLMM Gebühren-Abrechnung: eine Position verdient Gebühren nur, während der Pool-Preis innerhalb seines Bereichs ist. Der Pool verfolgt kumulative Gebühren global; die Position muss die kumulativen Gebühren während sie in seinem spezifischen Bereich ist kennen. Die Lösung ist ein Tick-basierter Akkumulator. Jeder Tick speichert:- Wenn der Pool-Preis über diesem Tick liegt (
tick_current >= this_tick), istfee_growth_outside = fee_growth_global. (Alles bisher Verdiente ist „außerhalb” — d. h., unter — diesem Tick, relativ zum aktuellen Preis.) - Andernfalls
fee_growth_outside = 0.
fee_growth_outside dieses Ticks um:
Die Invariante, die dies bewahrt: für jeden Tick t, fee_growth_outside(t) gleich die Gebühren, die sich ansammelten, während tick_current auf der gegenüberliegenden 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
EinePersonalPositionState speichert fee_growth_inside_0_last_x64 und fee_growth_inside_1_last_x64: die fee_growth_inside Werte zum letzten Mal, als die Position berührt wurde.
Beim nächsten Anfassen (erhöhen, verringern, einziehen), macht das Programm:
- Berechnet die aktuelle
fee_growth_inside_{0,1}_x64unter Verwendung der obigen Formel. - Berechnet
Δ = fee_growth_inside_now − fee_growth_inside_last(modulare 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
Jedes der bis zu 3 Belohnungs-Streams des Pools verwendet die gleiche Growth-Inside-Mechanik in seinem eigenenreward_growth_global_x64 Akkumulator. Bei Emissionszeit:
— Emissionen skalieren umgekehrt mit aktiver Liquidität, so dass ein dichterer Pool jeder Position proportional weniger pro Sekunde zahlt, aber über mehr Positionen insgesamt. Die pro Position geschuldete Belohnung ist
und wird via CollectReward eingefordert. Siehe products/clmm/fees.
Durchgerechnetes Beispiel: Exakter-Input-Tausch
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 oben:
t = 60(sqrt_price_b ≈1.003004 × 2^{64}). - Handelsgebühren-Satz: 500 (0,05%).
SwapBaseInput exakte Eingabe 1.000 Token0.
Schritt 1 — Gebühren:
999 < 2995.5, also passt die gesamte Eingabe ohne Tick-Überschreitung.
Schritt 3 — neuer Preis:
sqrt_c' leicht unter sqrt_c. Beachten Sie, dass die obige Formel für einen token1 → token0 Tausch ist. Das Beispiel hier ist token0 → token1, das den Preis nach oben treibt, nicht nach unten — also verwenden wir die entsprechende Form für token0 rein:
token0 → token1: sqrt_c steigt zusammen mit dem Preis.)
Schritt 4 — Betrag raus:
trade_fee_rate × protocol_fee_rate / 1e6 (und ähnlich für Fund); der LP-Anteil fließt in fee_growth_global_0_x64.
Limit-Order-Matching während Tausch
Wenn ein Tausch-Schritt einen Tick überschreitet, der offene Limit Orders hält, verbrauchen diese Orders Tausch-Eingabe vor der LP-Kurve, zum exakten Preis des Ticks. Das Matching ist FIFO innerhalb des Ticks nachorder_phase Kohorte.
Pro-Kohorte-Status 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-Überschreitung während eines Tauschs geschieht:SettleLimitOrder (oder DecreaseLimitOrder) aufruft. Der Pool verfolgt einfach, wie viel von der Kohorte nun gefüllt ist via unfilled_ratio_x64. Jedes LimitOrderState speichert seine eigene (order_phase, unfilled_ratio_x64) Momentaufnahme bei der Eröffnung, also reduziert sich die Abrechnung auf:
Interaktion mit der LP-Kurve
In einem Tausch-Schritt geschieht Limit-Order-Matching am Tick (nullΔsqrt_price); LP-Kurven-Verbrauch geschieht zwischen Ticks. Die Reihenfolge ist daher:
- Überschreiten Sie Tick
t_cross(wenden Sie LPliquidity_netÄnderung zuerst an, da dies ist, wie Uniswap-V3 es macht). - Füllen Sie alle Limit Orders, die auf
t_crosssitzen. - Fahren Sie entlang der LP-Kurve zum nächsten initialisierten Tick oder zur
swap_inputErschöpfung fort.
Dynamische Gebühren-Ableitung
PoolState.dynamic_fee_info trägt den Volatilitätszustand. Jeder Tausch-Schritt berechnet den Pro-Schritt-Gebührensatz als:
wobei:
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_accist der Pro-Tausch-Akkumulator nach der Aktualisierungsregel untentick_spacingist ausPoolState.tick_spacing
Akkumulator-Aktualisierung
Zwei Regeln werden bei jedem Tausch angewendet, in dieser Reihenfolge: Zerfall. Die Referenz-Bodensatz verfällt basierend auf der Zeit seit letzter Aktualisierung: Akkumulieren. Der neue Akkumulator ist die Referenz plus Tick-Distanz durchlaufen seit dem vorherigen Referenz-Index:tick_spacing_index_reference () ist in Tick-Spacing-Einheiten, nicht rohen Ticks: .
Warum parabolisch in Tick-Distanz
Das Quadrieren des Akkumulators bedeutet, die Gebühr steigt als das Quadrat wie weit der Preis von seinem Referenzpunkt weggelaufen ist. Empirisch stimmt dies mit der Varianz-Skalierung des Preises unter Random-Walk-Druck überein: eine 2× Tick-Exkursion impliziert 4× die implizierte Volatilität, also berechnet 4× den Aufschlag. Derdynamic_fee_control Parameter kalibriert das absolute Niveau.
Das filter_period Fenster verhindert, dass winzige Sub-Sekunden-Oszillationen (z. B. MEV-Bots-Sandwiching) den Akkumulator aufblasen. Das decay_period Fenster verhindert, dass ein einzelner vergangener Spike Gebühren auf unbestimmte Zeit nach der Markt-Beruhigung berechnet.
Numerische Robustheit
- Alle Zwischenprodukte durchlaufen
u128oderu256-ähnliche Arithmetik. CLMM verwendetU128SqrtHilfsmittel undFullMath::mulDivMuster direkt aus Uniswap v3 portiert. - Divisions-Rundung wird pro Schritt gewählt, um die Invariante
k' ≥ klokal durchzusetzen.SwapBaseInputrundet Ausgabe hinunter;SwapBaseOutputrundet Eingabe hinauf. - Tick-Überschreitungen, die
PoolState.liquidityauf Null fallen lassen, sind erlaubt (der Preis kann ein „Liquiditätsloch” durchlaufen), aber der Tausch rückt einfach zum nächsten initialisierten Tick vor, ohne Eingabe zu verbrauchen, ohne Gebühren zu berechnen. - Overflow-Schutz:
sqrt_price_x64wird im inklusiven Bereich[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]entsprechend[MIN_TICK, MAX_TICK]gehalten. Ein Tausch, der über beide Grenzen hinaus gehen würde, wird mitSqrtPriceLimitOverflowrückgängig gemacht.
Nächste Schritte
products/clmm/ticks-and-positionswie die Tick-Karte bei dem Durchgang teilnimmt.products/clmm/feesfür die Gebühren-/Belohnungs-Seite der Mathematik im Detail.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


