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 →
sdk-api/rust-cpi bao gồm các cơ chế cấp thấp để gọi từng chương trình Raydium. Trang này là người bạn đồng hành cấp cao hơn: tại sao bạn sẽ composition Raydium vào chương trình của riêng mình, mẫu nào phù hợp với trường hợp sử dụng của bạn, và toàn bộ glue bạn cần từ đầu đến cuối.

Khi nào CPI là công cụ phù hợp

Một chương trình tùy chỉnh có ý nghĩa khi giao dịch cần xảy ra một cách nguyên tử với các thay đổi trạng thái on-chain khác mà chỉ chương trình của bạn mới có thể thực hiện. Các trường hợp phổ biến:
  • Escrow / limit-order programs — người dùng gửi một mint vào escrow của bạn, chương trình của bạn theo dõi điều kiện giá, và khi nó kích hoạt, chương trình của bạn swap một cách nguyên tử thông qua Raydium và ghi có tài khoản của người dùng.
  • Aggregator proxies — một instruction duy nhất định tuyến swap thông qua Raydium + một hoặc nhiều DEX khác, với tất cả các hop dưới một kiểm tra slippage duy nhất do chương trình của bạn sở hữu.
  • Auto-compounding vaults — gửi LP hoặc farm stake vào vault của bạn, vault thu hoạch rewards theo lịch trình, cung cấp lại liquidity, phát hành share tokens.
  • Strategy vaults — các vị trí LP có đòn bẩy rebalance bằng cách swap thông qua CLMM; liquidators đóng vị trí và swap collateral trong một giao dịch.
  • Token-launch platforms with custom vesting — chương trình của bạn giữ vesting tokens và phát hành vào pool Raydium theo lịch trình.
Nếu bạn chỉ muốn gửi một swap từ mã off-chain, CPI là quá mức — hãy sử dụng SDK. CPI chỉ xứng đáng với độ phức tạp của nó khi tính nguyên tử với trạng thái của riêng bạn là yêu cầu.

Các mẫu composition

Mẫu 1: Thin proxy

Chương trình của bạn expose một instruction duy nhất xác thực một số chính sách (ví dụ: whitelisted mint pairs, fee discount cho người dùng được xác minh) và sau đó chuyển tiếp đến Raydium.
Trạng thái sống trong ATAs của người dùng. Chương trình của bạn không sở hữu bất kỳ token nào. Footprint tin tưởng tối thiểu.

Mẫu 2: Escrow

Chương trình của bạn sở hữu một PDA giữ mint đầu vào của người dùng. Khi kích hoạt, PDA ký một CPI đến Raydium để swap số dư của chính nó.
Chi tiết quan trọng: PDA ký thông qua CpiContext::new_with_signer. Xem Signer seeds.

Mẫu 3: Composed multi-hop

Chương trình của bạn phát hành nhiều CPIs trong một instruction, thực thi một giới hạn slippage duy nhất trên tất cả chúng. Các instructions swap Raydium mỗi cái có minimum_amount_out riêng của chúng, nhưng bạn đặt những cái đó thành 0 (hoặc một sàn rất lỏng lẻo) và thực thi một minimum cuối cùng nghiêm ngặt của riêng bạn sau hop cuối cùng.
Điều này cung cấp cho bạn một cổng reverting duy nhất cho toàn bộ route. Chỉ sử dụng mẫu này khi bạn tin tưởng mỗi hop là slippage-safe; nếu không, hãy để mỗi hop thực thi min của riêng nó.

Mẫu 4: Vault / strategy

Chương trình của bạn giữ LP tokens hoặc farm stake trong một PDA. Một keeper (hoặc người dùng) gọi compound(), cái mà:
  1. Thu hoạch rewards từ farm.
  2. Swap rewards cho pool tokens (CPI vào CPMM hoặc CLMM).
  3. Gửi lại các khoản tiền vào LP (một CPI khác).
  4. Stake LP mới (một CPI khác).
Tất cả trong một giao dịch để NAV của vault di chuyển một cách nguyên tử. Compute budget thường là 600k–1M CU; address lookup tables là bắt buộc.

Xây dựng danh sách tài khoản

Struct Accounts của chương trình gọi phản ánh thứ tự tài khoản của chương trình Raydium, nhưng hầu hết các tài khoản phía Raydium là UncheckedAccount vì Raydium xác thực chúng. Bạn chỉ thêm ràng buộc trên các tài khoản bạn sở hữu:
Sự bất đối xứng — xác thực nghiêm ngặt trên các tài khoản của bạn, UncheckedAccount trên các tài khoản của Raydium — không phải là sự lười biếng. Người nhận xác thực của riêng họ; double-validating tại caller chỉ đốt CU và rủi ro đi ra khỏi đồng bộ khi Raydium gửi một trường layout struct mới.

Cuộc gọi CPI chính nó

PDA signer seeds

CPI chỉ thành công nếu PDA được truyền làm authority khớp với derivation mà caller tuyên bố. Hai cái phải đồng ý về:
  1. Chuỗi byte seed (ở đây [b"escrow", user.key().as_ref()]).
  2. Bump.
  3. ID chương trình gọi (chương trình của bạn, không phải của Raydium).
Raydium không quan tâm authority là ai — nó chỉ quan tâm rằng chữ ký authority bao gồm giao dịch và ATA đầu vào được sở hữu bởi authority đó. Xác thực xảy ra trong anchor_spl::token::transfer: trường authority của ATA phải bằng signer. Bug phổ biến: truyền user làm authority (và chuyển từ escrow_input_ata được sở hữu bởi escrow PDA). Chương trình SPL Token từ chối với owner mismatch. Luôn làm cho trường authority khớp với chủ sở hữu ATA.

Remaining accounts

Một số instructions Raydium lấy một danh sách tài khoản có độ dài thay đổi được thêm vào sau những cái cố định — remaining accounts.
  • CLMM SwapV2: 1–8 tài khoản TickArrayState cho các tick arrays mà swap có thể traverse, theo hướng swap.
  • Farm v6 Deposit / Harvest / Withdraw: các cặp (reward_vault, user_reward_ata), một cặp cho mỗi live reward slot.
  • Token-2022 transfer-hook mints: chương trình transfer-hook cộng với bất kỳ tài khoản nào mà hook cần.
Các helper CPI Anchor không type-check remaining accounts. Truyền chúng qua:
Thứ tự quan trọng. Đối với CLMM:
Đối với farm v6 harvest:
Chương trình gọi của bạn phải truyền remaining accounts mà nó nhận từ client thông qua không thay đổi. Đừng cố gắng lọc hoặc sắp xếp lại chúng.

Compute budget cho composed calls

Một CPI chi phí ~1,500 CU cho call frame chính nó; sử dụng CU của callee xếp chồng lên trên. Ngân sách thô trên mỗi CPI Raydium: Thêm ~1,500 cho mỗi CPI frame và ~20,000 cho overhead của chương trình của riêng bạn. Một auto-compounder làm harvest → swap A → swap B → deposit LP → stake LP dễ dàng đạt 700k CU. Luôn đặt một ComputeBudgetProgram::set_compute_unit_limit rõ ràng:
Trần 200k CU mặc định sẽ im lặng cạn kiệt lâu trước khi một composed call hoàn thành.

Error propagation

Các chương trình Raydium trả về Anchor errors với mã lỗi ổn định. Chương trình gọi của bạn thấy chúng là Err(ProgramError::Custom(code)). Bubble through theo mặc định:
Hoặc intercept cho các mã cụ thể:
Ánh xạ mã lỗi-to-meaning là ổn định theo chính sách IDL (sdk-api/anchor-idl); các mã mới append ở cuối, các mã hiện tại không bao giờ thay đổi ý nghĩa.

Ví dụ đầy đủ được thực hiện: limit-order escrow

Flow:
  1. open_order — người dùng gửi amount_in của input_mint vào escrow PDA; ghi lại target min_amount_out và expiry.
  2. execute_order — bất kỳ ai (keeper) gọi với các tài khoản pool hiện tại. Chương trình kiểm tra quote hiện tại ≥ min_amount_out, sau đó CPIs Raydium swap và giữ output trong escrow.
  3. claim — người dùng rút output mint từ escrow.
Keeper trả phí giao dịch (họ nhận được keeper fee ở nơi khác — không hiển thị). Escrow PDA ký CPI. Cả kiểm tra slippage phía Raydium kiểm tra delta của escrow thực thi sàn — belt and braces.

Testing

Kéo các chương trình Raydium vào một local validator cho integration tests (từ Anchor.toml):
Clone các tài khoản pool state cũng vậy để các bài kiểm tra của bạn thực sự có thể thực hiện swaps; anchor test tìm nạp chúng từ mainnet khi khởi động. Xem sdk-api/rust-cpi.

Pitfalls cụ thể cho composition

Reentrancy

Solana không có true reentrancy — một CPI không thể gọi lại vào chương trình gốc trong cùng một invocation. Nhưng bạn vẫn có thể xây dựng bản thân vào một logical reentrancy: một CPI đọc trạng thái của bạn, sau đó mã của bạn đọc lại nó giả định CPI không thay đổi nó. Đối với Raydium, các CPIs không chạm vào trạng thái của bạn, vì vậy đây là ít quan tâm hơn ví dụ như flash-loan contexts. Nhưng nếu bạn composition Raydium với một giao thức cho vay, hãy nhận thức được.

Account mutability drift

Nếu chương trình của bạn truyền một tài khoản là mut nhưng Raydium mong đợi nó read-only (hoặc ngược lại), runtime từ chối invocation với InvalidAccountData. Luôn kiểm tra mutability mong đợi của instruction Raydium trong IDL; anchor_cp_swap::cpi::accounts::Swap thực thi nó thông qua các loại trường của nó.

Token-2022 program field

Input và output mints có thể dưới các chương trình token khác nhau — một SPL Token, một Token-2022. CPI có các trường input_token_programoutput_token_program riêng biệt vì lý do này. Luôn kiểm tra trường owner của mỗi mint và định tuyến chương trình chính xác vào mỗi slot.

Versioned transactions

Một composed tx làm 2+ Raydium CPIs cộng với một ATA creation hiếm khi vừa trong một legacy (v0-without-LUT) transaction. Sử dụng V0 với address lookup tables; kéo LUTs công khai của Raydium thông qua raydium.getRaydiumLutAddresses().

Pointers

Sources: