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 →
Tại sao ticks tồn tại
Thanh khoản của CLMM được tập trung vào các khoảng giá. Để làm cho các khoảng này khả thi trên chuỗi, giá được lượng tử hóa thành các số nguyên ticks, trong đó mỗi tick là một bội số hằng số của tick trước: Một tick tương ứng với mức giá di chuyển 0,01%, hoặc khoảng 1 điểm cơ sở. Ánh xạ như sau:MIN_TICK và MAX_TICK được chọn sao cho sqrt_price_x64 vừa trong một u128 ở cả hai đầu. Mỗi pool đảm bảo rằng tick_lower >= MIN_TICK và tick_upper <= MAX_TICK. Trong thực tế, giao diện web cắt giới hạn khoảng thành một khoảng hẹp hơn nhiều để ngăn người dùng khóa thanh khoản vào các ticks không thể tiếp cận.
Khoảng cách tick
AmmConfig của một pool xác định khoảng cách tick — các tick duy nhất mà một vị trí được phép sử dụng làm điểm cuối. Nếu tick_spacing = 60, chỉ các ticks …, −120, −60, 0, 60, 120, … là hợp lệ. Nếu cố gắng mở một vị trí với điểm cuối 31 sẽ hoàn tác với InvalidTickIndex.
Các khoảng cách được công bố phổ biến:
Khoảng cách càng thô, càng ít tick arrays cần khởi tạo, chi phí mở một vị trí rộng càng thấp, và ranh giới giá càng mờ. Các cặp biến động thường nằm trong các tầng khoảng cách 120; các stables nằm trong các tầng khoảng cách 1.
Tick arrays
Pool không lưu trữ trạng thái cho mỗi tick trong các tài khoản riêng biệt. Thay vào đó,TICK_ARRAY_SIZE ticks liền kề (60 trong Raydium CLMM hiện tại) được đóng gói vào một TickArrayState duy nhất. Tick đầu tiên của mảng là start_tick_index của nó, và nó bao phủ chính xác TICK_ARRAY_SIZE * tick_spacing đơn vị tick nguyên.
Đối với tick_spacing = 60 và TICK_ARRAY_SIZE = 60:
- Mỗi tick array bao trùm
60 × 60 = 3600ticks nguyên. start_tick_indexlà bội số của 3600:…, -7200, -3600, 0, 3600, 7200, ….
t = 2040 ở tick_spacing = 60 nằm trong tick array có start_tick_index = 0. Một điểm cuối vị trí t = 4200 nằm trong mảng có start_tick_index = 3600.
Khi một mảng được tạo
Một tick array là lười biếng: vị trí đầu tiên tham chiếu bất kỳ tick nào bên trong nó sẽ khởi tạo mảng, trả tiền thuê. Các swaps không khởi tạo tick arrays — chúng bỏ qua các mảng chưa khởi tạo bằng cách sử dụng bitmap. Luồng open-position của SDK kiểm tra khoảng được chọn, tính toán danh sách các tick arrays nó chạm vào, và thêm các lệnhinit_tick_array trong cùng một giao dịch với OpenPosition nếu có bất kỳ lệnh nào bị thiếu.
Tick arrays không được đóng
Sau khi một tick array đã được khởi tạo, nó tồn tại suốt vòng đời của pool. Chương trình không cung cấp đường dẫn để đóng một tick array, ngay cả sau khiinitialized_tick_count quay trở lại không. Không có khôi phục tiền thuê cho tick arrays; tiền thuê được trả bởi vị trí đầu tiên chạm vào một mảng bị khóa vào tài khoản đó vĩnh viễn. Đây là một sự đánh đổi cố ý: tái sử dụng một tick array hiện có là miễn phí cho mỗi vị trí tiếp theo, vì vậy một pool được giao dịch nhiều chỉ trả chi phí thuê một lần cho mỗi slot (pool, start_tick_index) bất kể sự thay đổi.
Bitmap
Tìm “tick được khởi tạo tiếp theo ở bên trái/phải của tick hiện tại” phải nhanh — một swap có thể vượt qua nhiều ticks. Pool lưu trữ một bitmap 1-bit-per-tick-array nội tuyến trongPoolState cho khoảng ±1.024 mảng xung quanh tick 0. Ngoài khoảng đó (vị trí full-range, thiết lập kỳ lạ), TickArrayBitmapExtension cung cấp tràn.
Một swap đi qua bitmap: lowest_set_bit_above(tick_current_array_index) cho mảng tiếp theo có một tick được khởi tạo ở phía swap đang vượt qua. Trong mảng đó, một quét bit tương tự xác định vị trí tick được khởi tạo tiếp theo.
liquidity_gross và liquidity_net
Mỗi tick được khởi tạo lưu trữ hai giá trị thanh khoản:
liquidity_gross— tổng củaLtrên tất cả các vị trí tham chiếu tick này làm điểm cuối. Khiliquidity_grossđạt không, tick trở thành chưa khởi tạo và có thể bị xóa khỏi bitmap.liquidity_net— thay đổi có dấu đối vớiliquiditycấp pool khi giá vượt qua tick này di chuyển hướng lên (trái sang phải trong không gian tick). Nếu tick này là giới hạn dưới của một vị trí có kích thướcL, nó đóng góp+L; nếu nó là giới hạn trên của vị trí đó, nó đóng góp−L.
- Vị trí A:
tick_lower = -120,tick_upper = 0, thanh khoảnL_A = 100. - Vị trí B:
tick_lower = -60,tick_upper = 60, thanh khoảnL_B = 50.
liquidity cấp pool cho các giá trị tick_current khác nhau:
tick_current = -180:liquidity = 0(trước bất kỳ vị trí nào)tick_current = -90:liquidity = 100(chỉ bên trong A)tick_current = -30:liquidity = 150(bên trong A và B)tick_current = 30:liquidity = 50(chỉ bên trong B)tick_current = 90:liquidity = 0(vượt qua cả hai)
liquidity_net (có thể âm) vào PoolState.liquidity. Đây là cơ chế chính xác của Uniswap-v3.
Vị trí dưới dạng NFTs
Một vị trí Raydium CLMM là một NFT. Mở một vị trí sẽ mint một mint hoàn toàn mới với cung cấp 1 vào ví của người gọi, và quyền của mint là chương trình CLMM. Chương trình liên kết quyền sở hữu vị trí với bất kỳ ai nắm giữ số dư trong một ATA của mint đó tại thời điểm CPI. Hậu quả:- Vị trí thường có thể chuyển nhượng được. Một ví có thể bán hoặc airdrop một vị trí bằng cách chuyển NFT. Chủ sở hữu mới sau đó có thể gọi
CollectRewards,IncreaseLiquidity, v.v. Ngoại lệ là một vị trí bị đóng băng theo đường dẫn restricted-issuer bên dưới. - Vị trí có thể được địa chỉ hóa bên ngoài CLMM. Các sàn giao dịch và ví hiển thị vị trí giống như các NFT khác. SDK đặt
name/symbolhợp lý trên siêu dữ liệu mint. - PDA của một vị trí được lấy từ NFT mint. Bạn có thể tìm
PersonalPositionStatemà không cần biết ai hiện đang nắm giữ nó.
Vị trí restricted-issuer
Mỗi mint NFT vị trí được tạo sau bản nâng cấp 2026-08 ghi lạipool_state CLMM của nó làm quyền freeze. Điều này không có nghĩa là mỗi vị trí mới đều bị đóng băng. Đối với các pool thông thường và mỗi vị trí không khớp, tài khoản token NFT vẫn không bị đóng băng và có thể chuyển nhượng được. PDA của pool không thể ký bên ngoài chương trình CLMM, và CLMM không cung cấp lệnh freeze mục đích chung.
Đóng băng yêu cầu cả hai điều kiện này:
- Vị trí được mở thông qua
OpenPositionV2hoặcOpenPositionWithToken22Nft. - Ít nhất một mint vault của pool mang quyền freeze từ danh sách restricted-issuer được mã hóa cứng của chương trình.
OpenPosition V1 không áp dụng bộ lọc này. Xem reference/program-addresses để biết danh sách hiện tại.
Một vị trí bị đóng băng:
- Không thể chuyển NFT của nó sang tài khoản token khác.
- Không thể thay đổi chủ sở hữu của tài khoản token NFT.
- Vẫn có thể tăng hoặc giảm thanh khoản và thu thập phí hoặc phần thưởng khi chủ sở hữu được ghi lại ký.
- Vẫn có thể đóng.
ClosePositionsử dụng pool PDA để rã đông tài khoản NFT, sau đó đốt NFT và đóng các tài khoản vị trí trong cùng một lệnh.
Vị trí Token-2022
CLMM có thể mint một NFT vị trí theo Token SPL cổ điển thông quaOpenPositionV2, hoặc theo Token-2022 thông qua OpenPositionWithToken22Nft. Cả hai đường dẫn V2 kiểm tra các mint vault của pool và áp dụng quy tắc freeze restricted-issuer tương tự. OpenPosition V1 là đường dẫn token cổ điển kế thừa và không thể phục vụ một pool có các mint vault Token-2022. Khả năng tương thích ví và sàn giao dịch khác nhau; giao diện người dùng của Raydium theo dõi cả hai chương trình NFT.
Quy tắc khoảng cho phép
Tại thời điểmOpenPosition, chương trình thực thi:
tick_lower < tick_upper.tick_lower % tick_spacing == 0vàtick_upper % tick_spacing == 0.MIN_TICK <= tick_lowervàtick_upper <= MAX_TICK.- Người gọi đã cung cấp các tick arrays chứa
tick_lowervàtick_upper— hoặc đã được khởi tạo hoặc thông quainit_tick_arraytrong cùng một giao dịch. - Tài khoản mở rộng bitmap, nếu vị trí này mở rộng vào phạm vi mở rộng.
InvalidTickIndex, NotApproved, hoặc InsufficientLiquidity tùy thuộc vào ràng buộc nào. Xem reference/error-codes.
”In-range” so với “out-of-range”
Một vị trí in range khitick_lower <= tick_current < tick_upper. Chỉ các vị trí in-range mới đóng góp vào PoolState.liquidity và do đó chỉ chúng mới kiếm được phí swap.
Một vị trí out-of-range:
- Nắm giữ 100% của một token (token mà khoảng của nó đã vượt qua). Cụ thể, nếu
tick_current < tick_lower, vị trí chỉ nắm giữ token1 (nó đã được “bán” bởi giá di chuyển đi); nếutick_current >= tick_upper, nó chỉ nắm giữ token0. - Không kiếm được phí swap.
- Tiếp tục tích lũy phần thưởng nếu các luồng phần thưởng của pool phát hành cho thanh khoản out-of-range — nhưng hành vi mặc định của Raydium là “chỉ phát hành cho in-range”, phù hợp với quy ước Uniswap v3. Xem
products/clmm/fees.
Những cạm bẫy tích hợp phổ biến
- Điểm cuối không cách đều. Mã tính toán một tick từ giá mục tiêu phải snap thành bội số của
tick_spacingtrước khi chuyển nó tớiOpenPosition. Các trình trợ giúp SDK (TickUtils.getTickWithPriceAndTickspacing) làm điều này; toán học tự tạo thường không. - Tick arrays bị thiếu. Mở một vị trí rộng có thể yêu cầu khởi tạo một số tick arrays; quên chuyển chúng làm tài khoản có thể ghi sẽ hoàn tác. SDK’s
openPositionFromBasetrả về danh sách cho bạn. - Tick cũ sau một swap.
tick_currentcó thể vượt qua nhiều ticks trong một swap. Nếu UX của bạn hiển thị một “current tick” từ một lệnh gọi RPC và sau đó mở một vị trí trong một lệnh gọi sau, vị trí tương đối so với giá trực tiếp có thể sai lệch hàng chục ticks. Tìm nạp lại ngay trước khi ký. - NFT vị trí có siêu dữ liệu bổ sung. Nếu bạn xây dựng một ví nhận ra các vị trí Raydium, hãy sử dụng vị trí PDA / dữ liệu chương trình và không phải một trường siêu dữ liệu được mã hóa cứng. Các mint vị trí mới sử dụng pool PDA làm quyền mint và freeze tại thời điểm tạo; quyền mint bị xóa sau khi mint NFT duy nhất.
- Giả định mỗi vị trí đều có thể chuyển nhượng được. Đọc trạng thái
isFrozencủa tài khoản token NFT trước khi hiển thị các hành động chuyển, sàn giao dịch, escrow, hoặc Burn & Earn.
Tiếp theo
- Math — bước swap và dẫn xuất tăng trưởng phí mà ranh giới tick tham gia.
- Accounts — bố cục
TickArrayStatevàPositionState. - Phí và phần thưởng — cách in-range-ness gating accrual phí.
algorithms/clmm-math— dẫn xuất chung của các công thức thanh khoản tập trung.
raydium-io/raydium-clmm—tick_array,tick,positionmodules- “Uniswap v3 Core” whitepaper, §6 (ticks), §7 (fee growth)

