Skip to main content
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 →
Trang này mô tả cấu trúc và vai trò của từng tài khoản. Các seed chuẩn được liệt kê tại reference/program-addresses. Một pool CLMM sử dụng nhiều tài khoản hơn pool CPMM vì thanh khoản được lưu trữ thưa thớt trên toàn bộ phạm vi tick — hiểu được sự thưa thớt đó là trọng tâm của trang này.

Danh sách tài khoản

Một pool CLMM đang hoạt động được mô tả bởi các nhóm tài khoản sau. Tất cả đều thuộc sở hữu của chương trình CLMM, ngoại trừ hai mint và các vault tương ứng.

PoolState

Trạng thái trực tiếp của pool, được đọc trong mọi lần swap và mọi thay đổi vị thế.
Các trường bạn sẽ thực sự cần đến:
  • sqrt_price_x64tick_current là trạng thái giá của pool. Cả hai được cập nhật đồng thời sau mỗi lần swap. tick_current là phần nguyên của log_{1.0001}(price).
  • liquidity là thanh khoản đang hoạt động — tổng giá trị L của tất cả các vị thế có phạm vi chứa tick_current. Giá trị này thay đổi mỗi khi swap vượt qua một tick và mỗi khi một vị thế được mở/đóng/thay đổi kích thước.
  • fee_growth_global_{0,1}_x64 là phí tích lũy thu được trên mỗi đơn vị thanh khoản trong toàn bộ lịch sử pool. Các vị thế đọc giá trị này để tính toán phí mà họ được nhận.
  • tick_spacing được khóa theo AmmConfig khi khởi tạo và không bao giờ thay đổi. Nó xác định những chỉ số tick nào được phép là điểm đầu/cuối của vị thế.
  • tick_array_bitmap là bitmap nội tuyến bao phủ phạm vi tick thường dùng quanh giá spot. Với các pool có vị thế trải dài ra xa, việc theo dõi tràn được thực hiện trong TickArrayBitmapExtension riêng biệt.
  • fee_on được cố định khi tạo pool. Giá trị 0 (FromInput) tái hiện hành vi Uniswap-V3 cổ điển. Giá trị 12 định tuyến phí swap về một phía duy nhất của sổ lệnh — xem products/clmm/fees để biết các đánh đổi.
  • dynamic_fee_info lưu trạng thái biến động cho phần phụ phí động. Khi được bật, mỗi lần swap sẽ tính toán lại dynamic_fee_component cộng thêm vào AmmConfig.trade_fee_rate. Cấu trúc được tài liệu hóa trong phần DynamicFeeInfo bên dưới; các pool không dùng phí động sẽ để toàn bộ struct này bằng không.

AmmConfig

Bộ fee tier CLMM tiêu biểu đã được công bố (xác nhận lại tại GET https://api-v3.raydium.io/main/clmm-config): protocol_fee_ratefund_fee_rate là phần trăm của phí giao dịch; quy ước giống CPMM. Xem products/clmm/fees.

TickArrayState

CLMM không lưu một bản ghi riêng cho mỗi tick — làm vậy sẽ tạo ra hàng tỷ tài khoản. Thay vào đó, nó nhóm TICK_ARRAY_SIZE tick liền kề (thường là 60 hoặc 88 tùy phiên bản chương trình) vào một TickArrayState được tạo lười khi lần đầu sử dụng.
Bốn trường limit-order đều bằng không trên bất kỳ tick nào chưa từng được dùng cho lệnh giới hạn. Khi các lệnh được mở trên một tick, chương trình theo dõi chúng theo chuỗi cohort:
  • order_phase là id của cohort. Nó tăng lên mỗi khi một cohort chuyển từ trạng thái “chưa khớp lệnh nào” sang “đã khớp một phần.”
  • orders_amount là tổng token đầu vào của cohort hiện tại (mới nhất).
  • part_filled_orders_remaining theo dõi cohort trước đó đang được các swap tiếp theo lấp đầy dần.
  • unfilled_ratio_x64 là hệ số nhân Q64.64 gắn với cohort: khi một swap lấp đầy X% cohort, tỷ lệ này được nhân với (1 − X). Mỗi lệnh mở lưu snapshot (order_phase, unfilled_ratio_x64) của riêng mình tại thời điểm mở, nên toán học thanh toán chỉ đơn giản là so sánh các snapshot.
Các quy tắc:
  • Tick điểm đầu/cuối t của một vị thế phải thỏa mãn t % tick_spacing == 0. Chương trình từ chối các vị thế có tick không đúng khoảng cách.
  • Mảng chứa tick được xác định tại floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing).
  • Tick array được khởi tạo lười: vị thế hoặc swap đầu tiên chạm vào một mảng chưa khởi tạo sẽ tạo ra nó và trả tiền thuê.
  • Tick array không bao giờ bị đóng bởi chương trình. Sau khi được phân bổ, nó tồn tại suốt vòng đời của pool, ngay cả khi mọi tick bên trong đã về lại liquidity_gross == 0. Các vị thế và swap sau này tái sử dụng tài khoản đã có mà không mất thêm tiền thuê. Không có luồng dọn dẹp nào cho tick array liên kết với ClosePosition.

TickArrayBitmapExtension

PoolState.tick_array_bitmap (nội tuyến) bao phủ phạm vi “gần giá spot” — ±1.024 tick array. Ngoài phạm vi đó (với các giá trị tick cực đoan), chương trình duy trì một tài khoản mở rộng:
Nếu phạm vi vị thế của bạn là “bình thường”, bạn không cần quan tâm đến tài khoản mở rộng này. Các vị thế full-range (ví dụ (MIN_TICK, MAX_TICK)) yêu cầu nó; SDK sẽ tự xử lý cho bạn.

Vị thế

Một vị thế CLMM là một bộ gồm ba tài khoản cộng với một mint:

NFT mint của vị thế

Một SPL Token mint với supply bằng 1. Địa chỉ mint là một PDA xác định; NFT vị thế trong ví của chủ sở hữu chỉ là một ATA giữ token duy nhất đó. Việc chuyển NFT là cách một vị thế đổi chủ — chương trình ủy quyền cho người đang nắm giữ số dư ATA của NFT, không phải Pubkey lưu trong state.

PersonalPositionState

Một tài khoản cho mỗi vị thế đang mở. Được lập chỉ mục theo NFT mint.

ProtocolPositionState (đã lỗi thời)

Các phiên bản CLMM cũ lưu dữ liệu tổng hợp theo (pool, tick_lower, tick_upper) trong một PDA ProtocolPositionState. Các phiên bản mới hơn không còn tạo hay đọc tài khoản này nữa. Slot vẫn xuất hiện trong danh sách tài khoản của OpenPosition / IncreaseLiquidity / DecreaseLiquidity dưới dạng UncheckedAccount để tương thích ABI, nhưng chương trình không ghi vào đó. Các tài khoản tồn tại sẵn on-chain chỉ là di vật; admin có thể gọi CloseProtocolPosition để thu hồi tiền thuê.Dữ liệu tổng hợp theo phạm vi hiện được suy ra trực tiếp từ hai tick điểm đầu/cuối (liquidity_gross, liquidity_netfee_growth_outside_* / reward_growths_outside_x64 theo từng tick) trong TickArrayState. Công thức fee-growth-inside fee_growth_inside = global − outside_lower − outside_upper vẫn hoạt động đúng mà không cần tài khoản vị thế tổng hợp.

Observation

Buffer observation của CLMM lưu tick tích lũy, không phải giá tích lũy. Các ứng dụng bên ngoài tính giá trung bình hình học trong một khoảng thời gian từ (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) rồi price = 1.0001 ** tick. Xem algorithms/clmm-math.

DynamicFeeConfigDynamicFeeInfo

Tham số phí động được lưu ở hai nơi. Template có thể tái sử dụng — DynamicFeeConfig — do admin quản lý và dùng chung cho các pool đăng ký. Trạng thái runtime theo từng pool — DynamicFeeInfo — được nhúng trong PoolState và cập nhật sau mỗi lần swap.

DynamicFeeConfig

PDA seed: ["dynamic_fee_config", index.to_be_bytes()]. Được tạo qua create_dynamic_fee_config (chỉ admin) và sửa đổi qua update_dynamic_fee_config. Một pool tạo với enable_dynamic_fee = true sẽ sao chép năm tham số hiệu chỉnh của config (filter_period, decay_period, reduction_factor, dynamic_fee_control, max_volatility_accumulator) vào DynamicFeeInfo của chính nó tại thời điểm tạo; các chỉnh sửa sau đó đối với DynamicFeeConfig không ảnh hưởng đến các pool đã có.

DynamicFeeInfo (nhúng trong PoolState)

Bốn trường cuối là state; năm trường đầu là tham số hiệu chỉnh sao chép từ DynamicFeeConfig. Toán học phí và quy tắc decay được tài liệu hóa tại products/clmm/mathproducts/clmm/fees. Các hằng số sử dụng trong công thức:

LimitOrderState

Một tài khoản cho mỗi lệnh giới hạn đang mở.
Vòng đời:
  1. Mở — người dùng gọi open_limit_order, nạp total_amount token đầu vào, lệnh được gắn với một cohort TickState.
  2. (Tùy chọn) Tăng / Giảmincrease_limit_order tăng thêm vào total_amount; decrease_limit_order trả lại các token chưa khớp (và bất kỳ output đã thanh toán nào đến thời điểm đó).
  3. Thanh toán — khi cohort được lấp đầy toàn bộ hoặc một phần, chủ sở hữu hoặc keeper vận hành gọi settle_limit_order để đẩy token output vào ATA của chủ sở hữu.
  4. Đóng — khi unfilled_amount == 0, tài khoản có thể đóng. Tiền thuê luôn trả về cho owner.
PDA seed: [owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]. PDA lệnh do đó là duy nhất theo (owner, nonce_index, order_nonce).

LimitOrderNonce

Bộ đếm theo (wallet, nonce_index) cho phép một người dùng chạy nhiều pipeline lệnh giới hạn song song mà không bị xung đột PDA.
PDA seed: [user_wallet.as_ref(), &[nonce_index]]. Hầu hết các client dùng nonce_index = 0 và để order_nonce đảm nhận vai trò đếm số lượng.

Suy ra các tài khoản chính

Các chuỗi seed chính xác nên luôn được kiểm tra lại với IDL on-chain và reference/program-addresses.

Tham chiếu nhanh vòng đời

Các tài khoản TickArrayState không bao giờ bị đóng bởi chương trình — chúng tồn tại suốt vòng đời của pool. Sau khi một tick array đã được khởi tạo, nó vẫn ở trên chain ngay cả khi mọi tick bên trong đã về lại liquidity_gross == 0. Tái sử dụng tick array đã có là miễn phí; chỉ vị thế đầu tiên chạm vào một mảng chưa khởi tạo mới phải trả tiền thuê.

Đọc thêm ở đâu

Nguồn tham khảo: