Trang này được dịch tự động bằng AI. Phiên bản tiếng Anh là bản chính thức.Xem bản tiếng Anh →
Biểu diễn sqrt-price
CLMM lưu trữ giá dưới dạngsqrt_price_x64 — căn bậc hai của giá token1-trên-token0, được biểu diễn dưới dạng số điểm cố định Q64.64:
trong đó p = token1_amount / token0_amount. Làm việc với sqrt thay vì p tuyến tính hóa toán học swap (các delta lượng token trở thành tuyến tính trong Δsqrt_price), và điểm cố định x64 duy trì độ chính xác qua các swap nhiều tick.
Chuyển đổi Tick ↔ sqrt-price được tính toán trước thông qua xấp xỉ log bit-by-bit:
được triển khai dưới dạng phép lũy thừa dựa trên bảng tra cứu trong tick_math::get_sqrt_price_at_tick.
Liquidity như một đơn vị chính tắc
Bên trong một phạm vi[sqrt_a, sqrt_b] (với sqrt_a < sqrt_b), một vị trí có liquidity L ánh xạ tới các lượng token như sau. Gọi sqrt_c = sqrt_price_x64 là giá hiện tại của pool.
Cả ba đẳng thức đều xuất phát từ bất biến
x = L / sqrt_p, y = L · sqrt_p mà liquidity tập trung thỏa mãn trong một phạm vi.
Các nhà tích hợp thường muốn phép nghịch đảo: cho một khoản gửi amount0 / amount1, tính toán L tối đa phù hợp với phạm vi. SDK của Raydium LiquidityMath.getLiquidityFromTokenAmounts thực hiện điều này. Công thức cho trường hợp trong phạm vi:
Bên nào ràng buộc sẽ xác định tỷ lệ thực tế được tiêu thụ; bên kia có thể có phần dư.
Bước swap đơn tick
Một swap diễn ra theo các bước. Mỗi bước hoặc (a) tiêu thụ tất cả input có sẵn trong phạm vi tick hiện tại mà không vượt qua một tick, hoặc (b) di chuyển giá chính xác tới tick được khởi tạo tiếp theo. Cho trạng thái hiện tại(sqrt_c, L) và một swap xuống (token0 vào, token1 ra, sqrt_price giảm — giá được trích dẫn là token1/token0, vì vậy thêm token0 làm giảm nó), tick được khởi tạo tiếp theo bên dưới nằm ở sqrt_t < sqrt_c. Bên trong khoảng vi mô này, mối quan hệ giữa input và giá là:
và
Một swap token1-vào là hình ảnh phản chiếu: sqrt_price tăng hướng tới tick tiếp theo ở trên, và hai công thức hoán đổi điểm cuối nào bị trừ. Chương trình mã hóa hướng dưới dạng zero_for_one (true khi mint input là token_mint_0), và một swap zero_for_one kẹp hướng tới MIN_SQRT_PRICE_X64.
Chương trình thực hiện một trong hai điều:
-
Toàn bộ input có vừa không? Nếu input còn lại (sau phí) nhỏ hơn
Δamount0để đạtsqrt_t, giải quyết chosqrt_c'chính xác: (cho một swap exact-inputtoken0 → token1). Swap hoàn thành trong bước này mà không vượt qua một tick. -
Input vượt quá
Δamount0? Đặtsqrt_c' = sqrt_t, vượt qua tick (áp dụngliquidity_net), giảm input còn lại bằngΔamount0, tăng output bằngΔamount1, và lặp lại.
token1 → token0, giá đi xuống), các công thức có sqrt_c và sqrt_t hoán đổi và phép nghịch đảo ở vị trí khác.
Triển khai Rust đầy đủ nằm trong raydium-clmm/programs/amm/src/libraries/swap_math.rs. Logic ở đó khớp với SwapMath.computeSwapStep của Uniswap v3 một-một.
Phí trên mỗi bước
Phí giao dịch được lấy từ lượng input trong mỗi bước, cùng quy ước với CPMM:L_i nằm trong phạm vi qua swap này sẽ sau đó đọc lại L_i · Δfee_growth_global / 2^{64} token nợ.
Các phần protocol và fund tích lũy tới PoolState.protocol_fees_token_{0,1} và PoolState.fund_fees_token_{0,1} tương ứng, giống hệt CPMM. Chúng được quét bởi CollectProtocolFee / CollectFundFee.
Phí tăng trưởng bên ngoài và bên trong
Phần phức tạp của kế toán phí CLMM: một vị trí chỉ kiếm phí khi giá pool bên trong phạm vi của nó. Pool theo dõi phí tích lũy toàn cầu; vị trí cần biết phí tích lũy khi bên trong phạm vi cụ thể của nó. Giải pháp là một bộ tích lũy dựa trên tick. Mỗi tick lưu trữ:- Nếu giá pool trên tick này (
tick_current >= this_tick),fee_growth_outside = fee_growth_global. (Tất cả kiếm được cho đến nay là “bên ngoài” — tức là, bên dưới — tick này, tương đối với giá hiện tại.) - Nếu không
fee_growth_outside = 0.
fee_growth_outside của tick đó:
Bất biến này bảo toàn: với bất kỳ tick t nào, fee_growth_outside(t) bằng phí tích lũy khi tick_current ở phía đối diện của t.
Phí tăng trưởng bên trong một phạm vi [tick_lower, tick_upper] sau đó được suy ra:
Vị trí lưu trữ gì và nó đọc gì
MộtPersonalPositionState lưu trữ fee_growth_inside_0_last_x64 và fee_growth_inside_1_last_x64: các giá trị fee_growth_inside tại lần cuối cùng vị trí được chạm vào.
Tại bất kỳ lần chạm tiếp theo nào (tăng, giảm, thu thập), chương trình:
- Tính toán
fee_growth_inside_{0,1}_x64hiện tại bằng công thức trên. - Tính toán
Δ = fee_growth_inside_now − fee_growth_inside_last(phép trừ mô-đun trên u128). - Thêm
Δ × position.liquidity / 2^{64}vàotokens_fees_owed_{0,1}. - Cập nhật
fee_growth_inside_lastthành giá trị mới.
CollectFees / DecreaseLiquidity, chống lại tokens_fees_owed.
Phần thưởng
Mỗi trong số tối đa 3 luồng phần thưởng của pool sử dụng cùng một cơ chế growth-inside, trong bộ tích lũyreward_growth_global_x64 riêng của nó. Tại thời điểm phát hành:
— phát hành tỷ lệ nghịch với liquidity hoạt động, vì vậy một pool dày đặc trả cho mỗi vị trí ít hơn theo tỷ lệ mỗi giây, nhưng trên nhiều vị trí hơn tổng cộng. Phần thưởng nợ trên mỗi vị trí là
và được trả bởi DecreaseLiquidity / DecreaseLiquidityV2 — không có lệnh thu thập phần thưởng độc lập. Xem products/clmm/fees.
Ví dụ đã làm: swap exact-input
Giả sử:tick_spacing = 60sqrt_price_x64 = 1 × 2^{64}— giá = 1.0, vì vậytick_current = 0.- Liquidity hoạt động
L = 1_000_000 × 2^{64}. - Tick được khởi tạo tiếp theo bên dưới:
t = −60(sqrt_price_b ≈0.997005 × 2^{64}). Một swap token0-vào di chuyển xuống, vì vậy tick ở trên không liên quan ở đây. - Tỷ lệ phí giao dịch: 500 (0.05%).
SwapBaseInput exact-input 1,000 token0.
Bước 1 — phí:
999 < 3004.4, vì vậy toàn bộ input vừa mà không vượt qua tick.
Bước 3 — giá mới:
sqrt_c' hạ cánh hơi dưới sqrt_c, đó là hướng chính xác: một swap token0 → token1 thêm token0 vào pool và do đó làm giảm giá token1/token0.
Bước 4 — lượng ra:
trade_fee_rate × protocol_fee_rate / 1e6 (và tương tự cho fund); phần LP chảy vào fee_growth_global_0_x64.
Khớp lệnh giới hạn trong swap
Khi một bước swap vượt qua một tick chứa các lệnh giới hạn mở, những lệnh đó tiêu thụ input swap trước đường cong LP, ở giá chính xác của tick. Khớp là FIFO trong tick theo cohortorder_phase.
Trạng thái mỗi cohort trên TickState
orders_amount và kế thừa order_phase tiếp theo; chúng không thể điền cho đến khi cohort trước được tiêu thụ hoàn toàn.
Bước khớp
Mã giả cho khớp xảy ra tại mỗi lần vượt qua tick trong một swap:SettleLimitOrder (hoặc DecreaseLimitOrder). Pool chỉ theo dõi bao nhiêu của cohort hiện được điền thông qua unfilled_ratio_x64. Mỗi LimitOrderState lưu trữ snapshot (order_phase, unfilled_ratio_x64) riêng của nó tại thời điểm mở, vì vậy giải quyết rút gọn thành:
Tương tác với đường cong LP
Trong một bước swap, khớp lệnh giới hạn xảy ra tại tick (zeroΔsqrt_price); tiêu thụ đường cong LP xảy ra giữa các tick. Thứ tự do đó là:
- Vượt qua tick
t_cross(áp dụng thay đổi LPliquidity_nettrước, vì đây là cách Uniswap-V3 làm). - Điền bất kỳ lệnh giới hạn nào ngồi tại
t_cross. - Tiếp tục dọc theo đường cong LP tới tick được khởi tạo tiếp theo hoặc tới sự cạn kiệt
swap_input.
Suy ra phí động
PoolState.dynamic_fee_info mang trạng thái biến động. Mỗi bước swap tính toán tỷ lệ phí mỗi bước là:
trong đó:
- —
DYNAMIC_FEE_CONTROL_DENOMINATOR - —
VOLATILITY_ACCUMULATOR_SCALE vol_acclà bộ tích lũy mỗi swap sau quy tắc cập nhật bên dướitick_spacinglà từPoolState.tick_spacing
Cập nhật bộ tích lũy
Hai quy tắc được áp dụng mỗi swap, theo thứ tự: Phân rã. Sàn tham chiếu phân rã dựa trên thời gian kể từ lần cập nhật cuối cùng: Tích lũy. Bộ tích lũy mới là tham chiếu cộng với khoảng cách tick đã duyệt kể từ chỉ số tham chiếu trước:tick_spacing_index_reference () tính bằng đơn vị tick-spacing, không phải tick thô: .
Tại sao parabol trong khoảng cách tick
Bình phương bộ tích lũy có nghĩa là phí tăng lên khi bình phương của khoảng cách giá đã đi từ điểm tham chiếu của nó. Theo kinh nghiệm, điều này phù hợp với tỷ lệ phương sai của giá dưới áp lực bước ngẫu nhiên: một sự vượt quá tick 2× ngụ ý 4× biến động ngụ ý, vì vậy tính phí 4× khoản phụ phí. Tham sốdynamic_fee_control hiệu chỉnh mức tuyệt đối.
Cửa sổ filter_period ngăn chặn các dao động nhỏ dưới một giây (ví dụ: bot MEV sandwich) làm tăng bộ tích lũy. Cửa sổ decay_period ngăn chặn một spike quá khứ duy nhất tính phí vô thời hạn sau khi thị trường đã bình tĩnh.
Độ mạnh mẽ số học
- Tất cả các sản phẩm trung gian đi qua số học hình dạng
u128hoặcu256. CLMM sử dụng các trợ giúpU128Sqrtvà các mẫuFullMath::mulDivđược chuyển cổng trực tiếp từ Uniswap v3. - Làm tròn phép chia được chọn mỗi bước để thực thi bất biến
k' ≥ kcục bộ.SwapBaseInputlàm tròn đầu ra xuống;SwapBaseOutputlàm tròn input lên. - Các lần vượt qua tick làm giảm
PoolState.liquidityxuống 0 được phép (giá có thể duyệt qua một “lỗ liquidity”) nhưng swap chỉ cần tiến tới tick được khởi tạo tiếp theo mà không tiêu thụ input, không tính phí. - Bảo vệ tràn:
sqrt_price_x64được giữ trong phạm vi bao gồm[MIN_SQRT_PRICE_X64, MAX_SQRT_PRICE_X64]tương ứng với[MIN_TICK, MAX_TICK]. Một swap sẽ đẩy quá một trong hai giới hạn hoàn nguyên vớiSqrtPriceLimitOverflow.
Tiếp theo đi đâu
products/clmm/ticks-and-positionsđể biết cách bản đồ tick tham gia vào bước đi.products/clmm/feescho phía phí/phần thưởng của toán học chi tiết.algorithms/clmm-mathcho các suy ra đằng sauL = sqrt(x · y)và các công thức phạm vi-vs-liquidity.
raydium-io/raydium-clmm—libraries/swap_math.rs,libraries/tick_math.rs- “Uniswap v3 Core” whitepaper, §6–7

