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 →

Thiết lập

Mọi demo trên trang này tương ứng với một tệp trong raydium-sdk-V2-demo/src/clmm; liên kết GitHub nằm ngay cạnh mỗi phần. Phần khởi tạo theo tệp config.ts.template của demo repo (nguồn). Với mọi tích hợp không đơn giản, nên đặt disableFeatureCheck: true:

Tạo pool CLMM

Nguồn: src/clmm/createPool.ts
SDK sẽ:
  • Sắp xếp mint1/mint2 theo thứ tự byte trước khi suy ra địa chỉ.
  • Tính sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64).
  • Tạo các tài khoản observation và tick_array_bitmap_extension.
  • Thanh toán phí tạo pool được quy định bởi ammConfig.

Mở position trong khoảng giá đã chọn

Nguồn: src/clmm/createPosition.ts
SDK tính toán các tick array mà khoảng giá chạm tới và truyền chúng vào dưới dạng accounts. SDK không cần gộp lệnh khởi tạo nào: không tồn tại lệnh init-tick-array. OpenPosition* tự cấp phát tick array còn thiếu, với chi phí do payer chi trả.

Tăng thanh khoản cho position hiện có

Nguồn: src/clmm/increaseLiquidity.ts

Giảm thanh khoản (đồng thời thu phí)

Nguồn: src/clmm/decreaseLiquidity.ts và src/clmm/closePosition.ts
Để chỉ thu phí và phần thưởng, gọi decreaseLiquidity với liquidity = new BN(0). Tác dụng phụ của lệnh này là quyết toán token_fees_owed_{0,1} và reward_amount_owed, rồi chuyển chúng ra khỏi pool. Đây là cách duy nhất để thu một trong hai khoản này. Để đóng position hoàn toàn sau khi đưa thanh khoản và phí về 0, truyền ownerInfo: { closePosition: true } ở lệnh decreaseLiquidity cuối cùng. SDK sẽ thêm ClosePosition và đốt NFT.
Position của issuer bị hạn chế cần trình tạo lệnh đóng tương thích. Các position này có tài khoản token NFT bị đóng băng. ClosePosition phải thêm pool ID của position làm tài khoản remaining đầu tiên để CLMM có thể mở băng tài khoản trước khi đốt. Nhánh mã nguồn chương trình không bao gồm thay đổi nào ở SDK. Trước khi bật tạo position của issuer bị hạn chế, hãy xác nhận rằng phiên bản SDK của bạn hỗ trợ rõ ràng luồng đóng khi tài khoản bị đóng băng.
Với client Anchor gọi trực tiếp, giữ nguyên các tài khoản đã khai báo và thêm pool vào:
Bạn có thể truyền poolId ở mọi lần đóng. CLMM chỉ đọc giá trị này khi positionNftAccount bị đóng băng, nhờ đó một đường dẫn client duy nhất vẫn tương thích với cả position cũ và position mới.

Thu phần thưởng

Nguồn: src/clmm/harvestAllRewards.ts
harvestAllRewards duyệt mọi position trên mọi pool được truyền vào, gom các lệnh DecreaseLiquidity có thanh khoản bằng 0 dùng để quyết toán phí và phần thưởng (cùng mọi UpdateRewardInfos nếu có), rồi chia chúng sang nhiều giao dịch khi cần.

Swap

Nguồn: src/clmm/swap.ts Ví dụ này bám theo src/clmm/swap.ts, nhưng mô phỏng bằng swapInternal thay vì PoolUtils.computeAmountOutFormat.
Quá trình mô phỏng duyệt tick map ngoài chuỗi theo cùng logic với chương trình on-chain, rồi trả về số lượng token nhận được (amountCalculated) cùng danh sách tài khoản chính xác mà lệnh swap sẽ truy cập (accounts). Luôn truyền remainingAccounts mà bước mô phỏng trả về: nếu thiếu, swap sẽ bị revert giữa chừng với lỗi NotEnoughTickArrayAccount; nếu thừa hoặc đã cũ thì chỉ lãng phí compute.
PoolUtils.computeAmountOutFormat vẫn tồn tại, nhưng nó cần một ComputeClmmPoolInfo (là computePoolInfo lấy từ getPoolInfoFromRpc, không phải đối tượng pool từ API) và thêm hai tham số bắt buộc là tickarrayBitmapExtension và blockTimestamp. Ngoài ra, không tồn tại phương thức raydium.clmm.fetchTickArrays (fetchTickArrays là một hàm độc lập; các hàm hỗ trợ ở cấp module là PoolUtils.fetchMultiplePoolTickArrays, cùng với tickData / tickArrays được trả về bởi getPoolInfoFromRpc).

Tạo pool CLMM tùy chỉnh được

createCustomizablePool là hàm đầu vào cho phép bật/tắt phí động và phí một phía ngay tại thời điểm tạo pool. Hàm này nhận cùng tham số với createPool, cộng thêm hai tham số bổ sung:
Không có tham số enableDynamicFee, dynamicFeeConfigId hay startTime. Chính việc truyền dynamicFeeConfig mới bật phí động. Nếu bỏ tham số này, bạn sẽ nhận được một pool phí tĩnh mà không có bất kỳ lỗi nào. Ngoài ra, các thành viên enum trong SDK là TokenOnlyA / TokenOnlyB, trong khi enum Rust on-chain viết là Token0Only / Token1Only. Giá trị số tương ứng là giống nhau (FromInput = 0).
createPool vẫn hoạt động cho trường hợp dùng phí mặc định, không có phí động. Hãy dùng createCustomizablePool khi bạn cần một trong hai tùy chọn trên. Xem products/clmm/instructions để biết danh sách tài khoản on-chain.

Lệnh giới hạn (Limit orders)

Lệnh giới hạn giữ tiền đầu vào của người dùng tại một tick duy nhất và được khớp theo nguyên tắc FIFO khi một lệnh swap vượt qua tick đó. Kết quả được chuyển về ATA của chủ lệnh tại thời điểm quyết toán; chủ lệnh không cần trực tuyến để lệnh được khớp.

Mở lệnh giới hạn

Không có tài khoản cấu hình lệnh giới hạn riêng cho từng pool và cũng không có hàm hỗ trợ getClmmLimitOrderConfigs(). Các lệnh được định danh theo tick.
SDK sẽ suy ra PDA LimitOrderState từ (owner, nonce PDA, order nonce), tăng LimitOrderNonce của từng ví, và đưa lệnh vào nhóm FIFO tại tick đó.

Tăng / giảm một lệnh đang mở

decreaseLimitOrder chỉ có thể rút phần chưa khớp của lệnh; phần đã khớp bị khóa cho đến khi quyết toán. Cả hai lệnh đều revert với InvalidOrderPhase nếu lệnh đã được khớp hoàn toàn.

Quyết toán lệnh đã khớp

settleLimitOrder đọc unfilled_ratio_x64 của lệnh so với bộ theo dõi nhóm, tính toán lượng token đã khớp và chuyển về ATA của chủ lệnh. Chủ lệnh có thể tự gọi hàm này; limit_order_admin (một keeper vận hành ngoài chuỗi) cũng có thể gọi thay chủ lệnh, nhưng kết quả vẫn được chuyển về cho chủ lệnh. Để đóng các lệnh đã quyết toán hoàn toàn nhằm thu hồi rent, hãy dùng closeLimitOrder (từng lệnh) hoặc closeAllLimitOrder (theo lô). Để quyết toán nhiều lệnh cùng lúc, settleAllLimitOrder sẽ gom càng nhiều lệnh SettleLimitOrder càng tốt vào một giao dịch v0.

Liệt kê các lệnh đang treo của ví (ngoài chuỗi)

Endpoint danh sách lệnh đang hoạt động trả về cả lệnh chưa khớp và lệnh khớp một phần trong cùng một payload; các trường totalAmount / filledAmount / pendingSettle giúp phân biệt từng giai đoạn. Để xem lịch sử lệnh đã đóng, hãy dùng /limit-order/history/order/list-by-user?wallet=… (theo từng ví, phân trang bằng nextPageId); để xem toàn bộ nhật ký sự kiện của một lệnh cụ thể, hãy dùng /limit-order/history/event/list-by-pda?pda=….

Khung CPI bằng Rust

Thứ tự remaining-account cho SwapV2:
Nếu swap không bao giờ cần extension, bạn có thể bỏ qua nó; ngược lại, nó là remaining account đầu tiên.

Những lỗi thường gặp

  • Tick đầu mút không khớp bước tick (tick spacing) → TickAndSpacingNotMatch. Luôn căn chỉnh qua TickUtil.getPriceAndTick (dùng TickUtil số ít).
  • Không cung cấp đủ tick array trong SwapV2 → NotEnoughTickArrayAccount. Lấy danh sách từ swapInternal(...).accounts.
  • Vị trí full-range nhưng thiếu bitmap extension → PDA của extension phải có quyền ghi; SDK tự xử lý việc này.
  • Nhầm sqrt_price_x64 với price → nhầm lẫn hệ số 2 ở đây đặc biệt khó chịu. Khi không chắc, hãy để SDK tính từ giá dễ đọc cho con người.
  • Thu phần thưởng quá thường xuyên → mỗi lần thu là một DecreaseLiquidity với liquidity bằng không và tốn một giao dịch. Hãy gom nhiều vị thế bằng harvestAllRewards, và nhớ rằng execute của hàm này cần { sequentially: true }.
  • Tự đóng tài khoản NFT → ClosePosition đốt NFT và đóng ATA của nó. Lệnh này cũng đóng mint NFT Token-2022; còn mint SPL Token cổ điển vẫn tồn tại với supply bằng không vì chương trình đó không thể đóng mint. Đừng tự đóng các tài khoản được hỗ trợ riêng, nếu không lệnh sẽ bị revert.
  • Mở lệnh limit tại tick không nằm trên bước tick → TickAndSpacingNotMatch. Luôn lượng tử hóa bằng hàm trợ giúp getOrderTick được export.
  • Gọi decreaseLimitOrder trên lệnh đã khớp hoàn toàn → InvalidOrderPhase. Thay vào đó, hãy dùng settleLimitOrder rồi closeLimitOrder.
  • Kỳ vọng có cờ enableDynamicFee → không tồn tại cờ này. Bỏ qua dynamicFeeConfig chỉ đơn giản tạo một pool phí cố định, âm thầm và không báo lỗi. Nếu bạn muốn phí động, hãy truyền PublicKey của tài khoản cấu hình, lấy từ /vi/main/clmm-dynamic-config.

Bước tiếp theo

Nguồn: