Halaman ini diterjemahkan secara otomatis oleh AI. Versi bahasa Inggris adalah acuan resmi.Lihat versi bahasa Inggris →
Representasi sqrt-price
CLMM menyimpan harga sebagaisqrt_price_x64 — akar kuadrat dari harga token1-per-token0, sebagai bilangan fixed-point Q64.64:
di mana p = token1_amount / token0_amount. Bekerja dalam sqrt alih-alih p melinearkan matematika swap (delta jumlah token menjadi linear dalam Δsqrt_price), dan fixed-point x64 mempertahankan presisi melalui swap multi-tick.
Konversi tick ↔ sqrt-price diperhitungkan sebelumnya melalui pendekatan log bit-by-bit:
diimplementasikan sebagai eksponensial berbasis lookup dalam tick_math::get_sqrt_price_at_tick.
Likuiditas sebagai unit kanonik
Di dalam rentang[sqrt_a, sqrt_b] (dengan sqrt_a < sqrt_b) sebuah posisi dengan likuiditas L memetakan ke jumlah token sebagai berikut. Misalkan sqrt_c = sqrt_price_x64 adalah harga saat ini pool.
Ketiga identitas berasal dari invarian
x = L / sqrt_p, y = L · sqrt_p yang dipenuhi likuiditas terkonsentrasi dalam sebuah rentang.
Integrator biasanya menginginkan kebalikannya: diberikan deposit amount0 / amount1, hitung L maksimum yang sesuai dalam rentang. SDK LiquidityMath.getLiquidityFromTokenAmounts melakukan ini. Rumus untuk kasus dalam-rentang:
Sisi mana pun yang mengikat menentukan rasio yang benar-benar dikonsumsi; sisi lain mungkin memiliki sisa.
Langkah swap satu tick
Swap berlangsung dalam langkah-langkah. Setiap langkah baik (a) mengonsumsi semua input yang tersedia dalam rentang tick saat ini tanpa melewati tick, atau (b) memindahkan harga tepat ke tick yang diinisialisasi berikutnya. Diberikan keadaan saat ini(sqrt_c, L) dan swap turun (token0 masuk, token1 keluar, sqrt_price menurun — harga dikutip sebagai token1/token0, jadi menambah token0 menurunkannya), tick yang diinisialisasi berikutnya di bawah berada di sqrt_t < sqrt_c. Di dalam micro-interval ini hubungan antara input dan harga adalah:
dan
Swap token1-in adalah citra cermin: sqrt_price naik menuju tick berikutnya di atas, dan kedua rumus menukar titik akhir mana yang dikurangi. Program mengenkode arah sebagai zero_for_one (true ketika mint input adalah token_mint_0), dan swap zero_for_one menjepit menuju MIN_SQRT_PRICE_X64.
Program melakukan salah satu dari dua hal:
-
Apakah seluruh input sesuai? Jika input yang tersisa (setelah biaya) kurang dari
Δamount0untuk mencapaisqrt_t, selesaikansqrt_c'baru dengan tepat: (untuk swap exact-inputtoken0 → token1). Swap selesai dalam langkah ini tanpa melewati tick. -
Input melebihi
Δamount0? Atursqrt_c' = sqrt_t, lewati tick (terapkanliquidity_net), kurangi input yang tersisa denganΔamount0, tambah output denganΔamount1, dan ulangi.
token1 → token0, harga turun), rumusnya memiliki sqrt_c dan sqrt_t ditukar dan inversi di slot lain.
Implementasi Rust lengkap berada di raydium-clmm/programs/amm/src/libraries/swap_math.rs. Logika di sana cocok dengan SwapMath.computeSwapStep Uniswap v3 satu-ke-satu.
Biaya pada setiap langkah
Biaya perdagangan diambil dari jumlah input dalam setiap langkah, konvensi yang sama dengan CPMM:L_i yang tetap dalam-rentang di seluruh swap ini akan kemudian membaca kembali L_i · Δfee_growth_global / 2^{64} token yang dihutangkan.
Porsi protokol dan dana terakumulasi masing-masing ke PoolState.protocol_fees_token_{0,1} dan PoolState.fund_fees_token_{0,1}, identik dengan CPMM. Mereka disapu oleh CollectProtocolFee / CollectFundFee.
Pertumbuhan biaya di luar dan di dalam
Bagian rumit dari akuntansi biaya CLMM: posisi memperoleh biaya hanya saat harga pool berada di dalam rentangnya. Pool melacak biaya kumulatif secara global; posisi perlu mengetahui biaya kumulatif saat berada di dalam rentang spesifiknya. Solusinya adalah akumulator berbasis tick. Setiap tick menyimpan:- Jika harga pool berada di atas tick ini (
tick_current >= this_tick),fee_growth_outside = fee_growth_global. (Semua yang diperoleh sejauh ini adalah “di luar” — yaitu, di bawah — tick ini, relatif terhadap harga saat ini.) - Jika tidak
fee_growth_outside = 0.
fee_growth_outside tick itu:
Invarian yang dipertahankan ini: untuk tick t apa pun, fee_growth_outside(t) sama dengan biaya yang terakumulasi saat tick_current berada di sisi berlawanan dari t.
Pertumbuhan biaya di dalam rentang [tick_lower, tick_upper] kemudian diturunkan:
Apa yang disimpan posisi dan apa yang dibacanya
PersonalPositionState menyimpan fee_growth_inside_0_last_x64 dan fee_growth_inside_1_last_x64: nilai fee_growth_inside pada waktu terakhir posisi disentuh.
Pada sentuhan berikutnya (tambah, kurangi, kumpulkan), program:
- Menghitung
fee_growth_inside_{0,1}_x64saat ini menggunakan rumus di atas. - Menghitung
Δ = fee_growth_inside_now − fee_growth_inside_last(pengurangan modular pada u128). - Menambahkan
Δ × position.liquidity / 2^{64}ketokens_fees_owed_{0,1}. - Memperbarui
fee_growth_inside_lastke nilai baru.
CollectFees / DecreaseLiquidity, terhadap tokens_fees_owed.
Reward
Setiap dari hingga 3 aliran reward pool menggunakan mesin pertumbuhan-dalam yang sama, dalam akumulatornya sendirireward_growth_global_x64. Pada waktu emisi:
— emisi berskala terbalik dengan likuiditas aktif, jadi pool yang lebih padat membayar setiap posisi secara proporsional lebih sedikit per detik, tetapi di atas lebih banyak posisi total. Reward per-posisi yang dihutangkan adalah
dan dibayarkan oleh DecreaseLiquidity / DecreaseLiquidityV2 — tidak ada instruksi kumpul-reward mandiri. Lihat products/clmm/fees.
Contoh kerja: swap exact-input
Misalkan:tick_spacing = 60sqrt_price_x64 = 1 × 2^{64}— harga = 1.0, jaditick_current = 0.- Likuiditas aktif
L = 1_000_000 × 2^{64}. - Tick yang diinisialisasi berikutnya di bawah:
t = −60(sqrt_price_b ≈0.997005 × 2^{64}). Swap token0-in bergerak turun, jadi tick di atas tidak relevan di sini. - Tingkat biaya perdagangan: 500 (0,05%).
SwapBaseInput exact-input 1.000 token0.
Langkah 1 — biaya:
999 < 3004.4, jadi seluruh input sesuai tanpa melewati tick.
Langkah 3 — harga baru:
sqrt_c' mendarat sedikit di bawah sqrt_c, yang merupakan arah yang benar: swap token0 → token1 menambah token0 ke pool dan dengan demikian menurunkan harga token1/token0.
Langkah 4 — jumlah keluar:
trade_fee_rate × protocol_fee_rate / 1e6 (dan serupa untuk dana); porsi LP mengalir ke fee_growth_global_0_x64.
Pencocokan limit order selama swap
Ketika langkah swap melewati tick yang memegang limit order terbuka, order tersebut mengonsumsi input swap sebelum kurva LP melakukannya, pada harga tepat tick. Pencocokan adalah FIFO dalam tick menurut kohortorder_phase.
Status per-kohort pada TickState
orders_amount dan mewarisi order_phase berikutnya; mereka tidak dapat diisi sampai kohort sebelumnya sepenuhnya dikonsumsi.
Langkah pencocokan
Pseudo-code untuk pencocokan yang terjadi pada setiap penyeberangan tick selama swap:SettleLimitOrder (atau DecreaseLimitOrder). Pool hanya melacak berapa banyak kohort yang sekarang terisi melalui unfilled_ratio_x64. Setiap LimitOrderState menyimpan snapshot (order_phase, unfilled_ratio_x64) miliknya sendiri pada waktu pembukaan, jadi penyelesaian berkurang menjadi:
Interaksi dengan kurva LP
Dalam langkah swap, pencocokan limit order terjadi pada tick (zeroΔsqrt_price); konsumsi kurva LP terjadi antara tick. Urutannya adalah:
- Lewati tick
t_cross(terapkan perubahan LPliquidity_netterlebih dahulu, karena ini adalah cara Uniswap-V3 melakukannya). - Isi limit order apa pun yang duduk di
t_cross. - Lanjutkan di sepanjang kurva LP ke tick yang diinisialisasi berikutnya atau ke kelelahan
swap_input.
Derivasi biaya dinamis
PoolState.dynamic_fee_info membawa keadaan volatilitas. Setiap langkah swap menghitung tingkat biaya per-langkah sebagai:
di mana:
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_accadalah akumulator per-swap setelah aturan pembaruan di bawahtick_spacingberasal dariPoolState.tick_spacing
Pembaruan akumulator
Dua aturan diterapkan setiap swap, secara berurutan: Peluruhan. Lantai referensi meluruh berdasarkan waktu sejak pembaruan terakhir: Akumulasi. Akumulator baru adalah referensi ditambah jarak tick yang ditempuh sejak indeks referensi sebelumnya:tick_spacing_index_reference () dalam unit tick-spacing, bukan tick mentah: .
Mengapa parabolik dalam jarak tick
Mengkuadratkan akumulator berarti biaya naik sebagai kuadrat seberapa jauh harga telah berjalan dari titik referensinya. Secara empiris ini cocok dengan penskalaan varians harga di bawah tekanan random-walk: eksursi tick 2× menyiratkan volatilitas tersirat 4×, jadi mengenakan surcharge 4×. Parameterdynamic_fee_control mengkalibrasi tingkat absolut.
Jendela filter_period mencegah osilasi sub-detik kecil (misalnya, bot MEV sandwiching) dari menginflasi akumulator. Jendela decay_period mencegah lonjakan masa lalu tunggal dari mengenakan biaya tanpa batas setelah pasar telah tenang.
Ketangguhan numerik
- Semua produk perantara melalui aritmetika berbentuk
u128atauu256. CLMM menggunakan pembantuU128Sqrtdan polaFullMath::mulDivyang langsung diportir dari Uniswap v3. - Pembulatan pembagian dipilih per-langkah untuk menegakkan invarian
k' ≥ ksecara lokal.SwapBaseInputmembulatkan output ke bawah;SwapBaseOutputmembulatkan input ke atas. - Penyeberangan tick yang menjatuhkan
PoolState.liquidityke nol diizinkan (harga dapat melintasi “lubang likuiditas”) tetapi swap hanya maju ke tick yang diinisialisasi berikutnya tanpa mengonsumsi input, tidak mengenakan biaya. - Penjaga overflow:
sqrt_price_x64disimpan dalam rentang inklusif[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]yang sesuai dengan[MIN_TICK, MAX_TICK]. Swap yang akan mendorong melampaui batas mana pun kembali denganSqrtPriceLimitOverflow.
Ke mana selanjutnya
products/clmm/ticks-and-positionsuntuk bagaimana peta tick berpartisipasi dalam jalan.products/clmm/feesuntuk sisi biaya/reward dari matematika secara detail.algorithms/clmm-mathuntuk derivasi di balikL = sqrt(x · y)dan rumus rentang-vs-likuiditas.
raydium-io/raydium-clmm—libraries/swap_math.rs,libraries/tick_math.rs- Whitepaper “Uniswap v3 Core”, §6–7

