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 →
CPI (“cross-program invocation”) là cơ chế cho phép một chương trình Solana gọi chương trình khác. Hầu hết các chương trình của Raydium đi kèm với các crate CPI wrapper Anchor giúp trang gọi trông giống như một lệnh gọi hàm được gõ, với các struct tài khoản có tên trường được xác thực và các trợ giúp cpi::<ix>(). Trang này ghi lại mô hình chung một lần, sau đó là các khác biệt theo từng chương trình. Để xem các ví dụ TypeScript có thể chạy được, hãy xem trang code-demos của mỗi chương sản phẩm.

Mô hình nào áp dụng cho chương trình nào

Nếu bạn đang tích hợp CPMM, CLMM, hoặc LaunchLab, hãy đọc mô hình chung trước, sau đó chuyển đến phần của chương trình của bạn để xem danh sách tài khoản và bất kỳ khác biệt nào. Farm v6 và AMM v4 khác biệt đủ để đáng giá việc đọc các phần của chúng một cách độc lập.

Cargo dependencies

Khóa dependency phải khớp chính xác với [package] name của repo đích, bao gồm dấu gạch ngang. Cargo không coi raydium_cp_swap tương đương với raydium-cp-swap khi giải quyết một dependency git.
branch = "master" theo dõi mã nguồn được xuất bản mới nhất; ghim vào một rev = "<commit>" cụ thể nếu bạn cần một bản dựng có thể tái tạo được. Điều này được khuyến nghị khi bạn vượt qua giai đoạn nguyên mẫu, vì một thay đổi bố cục tài khoản trên master sẽ phá vỡ bản dựng của bạn mà không có cảnh báo nào. Cờ tính năng cpi làm cho các crate biên dịch thành chỉ bề mặt CPI (struct tài khoản + invokers) thay vì toàn bộ chương trình, vì vậy tệp nhị phân của bạn vẫn nhỏ. anchor-lang / anchor-spl phải khớp với những gì crate đích ghim, và tính đến 2026-09 hai crate Raydium công khai không đồng ý:
Bạn không thể phụ thuộc vào cả hai crate từ một chương trình ngay bây giờ. Mỗi crate ghim Anchor bằng =, vì vậy Cargo sẽ phải liên kết hai bản sao không tương thích của các đặc điểm của Anchor vào một tệp nhị phân, và bản dựng sẽ thất bại. Nếu chương trình của bạn CPIs vào cả CPMM và CLMM, bạn phải chia nó thành hai chương trình, hoặc bỏ crate CPI được gõ cho một trong số chúng và mã hóa tay lệnh đó (mô hình được hiển thị cho AMM v4 hoạt động cho bất kỳ chương trình nào). Kiểm tra lại cả hai tệp Cargo.toml trước khi bắt đầu — điều này dự kiến sẽ được giải quyết khi CLMM chuyển sang Anchor 1.x.
Anchor 1.0 đã thay đổi hai điều mà mọi trang gọi CPI đều chạm tới. Nếu bạn đang chuyển một tích hợp đang hoạt động từ 0.3x:
  • CpiContext::new nhận một Pubkey, không phải AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) trở thành CpiContext::new(*ctx.accounts.cpmm_program.key, accts). Tương tự cho new_with_signer. Trường struct hiện là program_id: Pubkey.
  • Context có một lifetime, không phải bốn. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> trở thành Context<'info, MyProxySwap<'info>>.
Ở phía client, RequestBuilder::instructions() của anchor-client hiện trả về Vec<Instruction> thay vì Result<Vec<Instruction>> (bỏ ?), và CommitmentConfig đã chuyển ra khỏi solana-sdk — lấy nó từ anchor_client thay vào đó. spl-associated-token-account 8.0 tái xuất bản các trợ giúp của nó từ crate spl-associated-token-account-interface mới. get_associated_token_address và ID vẫn có thể truy cập được tại gốc crate (spl_associated_token_account::{get_associated_token_address, ID}), nhưng các trợ giúp địa chỉ bị không dùng nữa ở đó — thích dùng spl-associated-token-account-interface trực tiếp và nhập spl_associated_token_account_interface::address::get_associated_token_address và spl_associated_token_account_interface::program::ID. Lưu ý ::address và ::program là các mô-đun của crate interface; spl_associated_token_account::address::… không giải quyết được.
Để xem các ví dụ CPI đang hoạt động kết nối các struct tài khoản từ đầu đến cuối, hãy xem raydium-io/raydium-cpi-example (bao gồm AMM v4, CPMM, và CLMM). Nhánh mới nhất của nó là anchor-0.31.0 — chưa có nhánh Anchor 1.x, vì vậy hãy coi repo đó là tham chiếu cho wiring struct tài khoản, không phải cho các ghim phiên bản mà trang này yêu cầu.

Mô hình CPI Anchor chung

Phần này hướng dẫn CPMM từ đầu đến cuối như ví dụ đã làm: struct Accounts, CpiContext, cpi::<ix>(). CLMM tuân theo hình dạng giống hệt, với danh sách tài khoản khác và yêu cầu remaining-accounts. LaunchLab tuân theo cơ chế giống nhau nhưng danh sách tài khoản của nó mang theo một số tài khoản không có tương đương CPMM/CLMM (global_config, platform_config, event_authority, program), vì vậy hãy coi nó là cùng mô hình, không phải cùng hình dạng. Xem phần riêng của mỗi chương trình thay vì giả định danh sách tài khoản của hướng dẫn này chuyển trực tiếp.

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

Mỗi CPI Raydium yêu cầu một struct Accounts trong chương trình gọi. Các trường của nó là bất kỳ tài khoản nào chương trình của bạn cần, với các trình xác thực cấp trường; thứ tự khai báo của chúng không phải khớp với thứ tự tài khoản lệnh của Raydium, vì client được tạo từ IDL của bạn sẽ xử lý chúng theo tên, không phải vị trí:
Hầu hết các tài khoản phía Raydium là UncheckedAccount vì người được gọi (Raydium) sở hữu việc xác thực. Chương trình gọi của bạn chỉ thực sự xác thực các tài khoản bạn sở hữu, chẳng hạn như ATAs của người dùng và các PDA của riêng bạn. Nhận xét doc /// CHECK: sẽ loại bỏ cảnh báo của Anchor về các kiểm tra bị thiếu. Ngoại lệ phía Raydium duy nhất là cpmm_program chính nó: đó là chương trình được gọi chứ không phải tài khoản dữ liệu mà Raydium xác thực nội bộ, vì vậy nó được gõ Program<T> và nhận kiểm tra địa chỉ tự động của Anchor thay vì kiểm tra /// CHECK: thủ công. Hình dạng này, nơi hầu hết là UncheckedAccount, với Raydium xác thực các tài khoản của riêng nó, cũng giống nhau cho CLMM và LaunchLab. Ví dụ này giả định cả hai mint là SPL Token cổ điển; nếu một trong hai bên có thể là mint Token-2022, hãy thêm trường token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> và chuyển nó làm input_token_program/output_token_program của bên đó trong lệnh gọi CPI dưới đây thay vì token_program.

Xây dựng lệnh gọi CPI

Anchor tạo một trợ giúp cho mỗi lệnh, cùng với struct CPI-accounts (cpi::accounts::Swap, được đặt bí danh CpmmSwap dưới đây). Không giống struct MyProxySwap của bạn ở trên, tên trường và thứ tự của cái này được cố định bởi IDL của raydium-cp-swap và phải khớp chính xác:
cpi::swap_base_input được tạo từ IDL; danh sách đối số của nó phản ánh danh sách đối số của lệnh Anchor. Mỗi chương trình Raydium dựa trên Anchor được xác nhận (CPMM, CLMM, LaunchLab) tạo các trợ giúp cpi::<ix>() của nó theo cách tương tự, với tên hàm khớp với tên lệnh trong snake_case. Liệu điều này có mở rộng sang Farm v6 hay không là chưa xác nhận; xem phần của nó.

Signer seeds (CPI được ký bởi PDA)

Khi chương trình của bạn ký CPI thay mặt cho một PDA (phổ biến cho vault, escrow, v.v.), hãy sử dụng CpiContext::new_with_signer:
Các signer seeds phải khớp với derivation của PDA. Đối với bất kỳ tài khoản nào được chuyển làm authority (hoặc vai trò signer tương tự), runtime Solana sẽ kiểm tra rằng PDA ký thông qua các seeds này.

Remaining accounts

Một số lệnh Raydium nhận remaining accounts, một danh sách có độ dài thay đổi được nối sau các tài khoản cố định. Các trợ giúp CPI của Anchor không kiểm tra loại remaining accounts; chuyển chúng qua .with_remaining_accounts(...):
Thứ tự luôn quan trọng, vì chương trình nhận sẽ lặp lại remaining accounts theo thứ tự bạn chuyển chúng. Hai thứ tự được xác nhận:
  • CLMM SwapV2: tick arrays, được sắp xếp theo hướng.
  • Farm v6: các cặp (reward_vault, user_reward_ata), nhưng chỉ từ luồng reward thứ hai trở đi; xem Farm v6 để xem những gì giải mã một giao dịch thực tế cho thấy.

Áp dụng mô hình: CLMM

SwapV2 tuân theo mô hình chung ở trên với danh sách tài khoản khác và yêu cầu remaining-accounts cho tick arrays. Mô-đun #[program] của crate được đặt tên raydium_clmm, cũng là đường dẫn Rust use của nó.
Struct CPI accounts được đặt tên SwapSingleV2, không phải SwapV2. SwapV2 là tên lệnh trên chuỗi.
Tính toán danh sách tick-array theo cách SDK làm, thông qua một quote so với trạng thái pool hiện tại, thay vì đoán một số lượng cố định; một swap vượt quá các arrays bạn chuyển sẽ hoàn nguyên với TickArrayNotFound (xem products/clmm/instructions để xem bảng tài khoản đầy đủ và danh sách lỗi). Chuyển chúng theo hướng của price walk: array đầu tiên theo hướng swap trước tiên.

Áp dụng mô hình: LaunchLab

LaunchLab dựa trên Anchor và được xuất bản IDL: raydium_launchpad/raydium_launchpad.json trong repo raydium-idl công khai. Định danh siêu dữ liệu nội bộ của IDL đó là raydium_launchpad, tên kỹ thuật cho chương trình cơ bản, không phải tên thay thế cho sản phẩm. Không giống CPMM và CLMM, mã nguồn của chương trình không công khai (xem reference/program-addresses). Không có dependency git = "..." để chỉ Cargo tới, và không có mã nguồn để xác nhận đường dẫn Rust use của crate thực tế sẽ là gì. Tạo bindings từ IDL được xuất bản bằng macro declare_program! của Anchor. Lưu JSON IDL dưới dạng idls/raydium_launchpad.json trong crate của bạn (Cargo tìm kiếm thư mục idls/ tương đối với CARGO_MANIFEST_DIR), sau đó declare_program!(raydium_launchpad); tạo các struct raydium_launchpad::cpi::accounts::<Ix> và các hàm cpi::<ix>() trực tiếp từ IDL, không cần mã nguồn chương trình. Tên struct accounts được tạo luôn là tên lệnh trong PascalCase (buy_exact_in → BuyExactIn), và tên trường khớp với tên tài khoản của IDL chính xác, danh sách tài khoản giống nhau đã được sử dụng trong MyProxyBuy dưới đây. Hình dạng CPI tuân theo mô hình chung. Danh sách tài khoản và đối số dưới đây đến từ lệnh buy_exact_in của IDL trên chuỗi, không phải từ products/launchlab/instructions.mdx:
Sau khi tốt nghiệp, chương trình đích là CPMM hoặc AMM v4 tùy thuộc vào pool_state.migrate_type, mà products/launchlab/accounts.mdx nói được đặt tại thời gian Initialize. Danh sách tài khoản CPI của bạn phải được chuẩn bị cho cả hai, hoặc bạn cần đọc migrate_type từ PoolState trước và phân nhánh.

Truyền lỗi

Mỗi chương trình Raydium dựa trên Anchor trả về enum lỗi của riêng nó; Anchor bao bọc chúng, vì vậy chương trình gọi của bạn sẽ thấy chúng dưới dạng Err(ProgramError::Custom(code)). Để xử lý các lỗi cụ thể:
Hoán đổi loại lỗi liên quan cho chương trình bạn đang gọi (raydium_clmm::error::ErrorCode cho CLMM, v.v.). Các số mã lỗi ổn định theo chính sách IDL (sdk-api/anchor-idl), vì vậy bạn có thể kiểm tra các mã cụ thể bằng cách so sánh với giá trị số. Bảng lỗi đầy đủ: CPMM, CLMM, AMM v4, Farm v6, và LaunchLab.

Compute budget trong CPIs được tạo

Mỗi khung CPI có overhead, và tiêu thụ CU của người được gọi xếp chồng lên của bạn, vì vậy một giao dịch gọi vào Raydium từ bên trong chương trình của bạn cần một compute budget rõ ràng thay vì dựa vào mặc định 200k CU.
Đo lường, không ước tính. Một swap_base_input CPMM trên mainnet tiêu thụ ~23,000 CU trong chương trình CPMM — được lấy mẫu 2026-09-09 trên tám swaps trực tiếp trên một pool có khối lượng cao (22,721–23,052), đọc từ dòng nhật ký Program CPMMoo8… consumed N of M compute units. Để so sánh: AMM v4 swap ~26,000; CLMM swap ~41,000; CLMM swap_v2 ~48,000 (43,838–52,887), tăng với mỗi tick crossing.Một bản sửa đổi trước của trang này báo cáo ~47,700 CU cho một proxy-swap CPI. Con số đó là toàn bộ giao dịch (computeUnitsConsumed), bao gồm chương trình của người gọi, khung CPI và bất kỳ thiết lập ATA nào — không phải chi phí của người được gọi. Cả hai đều hữu ích, nhưng chúng không phải là cùng một số, vì vậy hãy so sánh giống với giống. Đo lường giao dịch của riêng bạn thay vì lập ngân sách từ một trong hai.
CPIs CLMM và LaunchLab tốn kém hơn (CLMM đặc biệt đi bộ các tick arrays bổ sung thông qua remaining_accounts, thêm CU cho mỗi array), nhưng chỉ con số CPMM ở trên là giá trị được đo lường. Luôn đặt một lệnh ComputeBudgetProgram::set_compute_unit_limit(...) rõ ràng được định kích thước từ phép đo của riêng bạn, không phải một số được sao chép từ tài liệu, vì giới hạn CU mặc định 200k sẽ im lặng cạn kiệt và chi phí mỗi lệnh thay đổi khi các chương trình được nâng cấp.

AMM v4: xây dựng lệnh thủ công

AMM v4 ra đời trước Anchor và không có crate CPI, khiến nó trở thành chương trình duy nhất trong tài liệu này không tuân theo mô hình chung ở trên. Xây dựng Instruction bằng tay:
Xem products/amm-v4/code-demos để xem danh sách tài khoản đầy đủ.

Farm v6

Sử dụng TS SDK nếu đó là một tùy chọn cho tích hợp của bạn. raydium.farm.deposit(...) (xem products/farm-staking/code-demos) được thực hiện bởi các demo thực tế và không phụ thuộc vào việc liệu crate Rust Anchor có tồn tại cho chương trình này hay không.
Farm v6 không cung cấp đường dẫn CPI Anchor. Không có crate raydium_farm_v6 trên crates.io, không có repo mã nguồn công khai, và không có IDL trên chuỗi — chương trình không có tài khoản anchor:idl kế thừa cũng không có mục nhập trong chương trình Program Metadata (xem sdk-api/anchor-idl). Coi nó như một chương trình không phải Anchor và xây dựng các lệnh của nó bằng tay, như dưới đây.
Nếu bạn vẫn cần Rust CPI, ví dụ tạo từ một chương trình trên chuỗi khác, hãy xây dựng Instruction bằng tay, theo cách tương tự như AMM v4: lấy danh sách tài khoản thực tế và discriminators lệnh một cách độc lập, ví dụ bằng cách giải mã bố cục TypeScript của SDK (raydium-sdk-V2’s farm module), giải mã các giao dịch thực tế trực tiếp (xem dưới đây), hoặc xả và disassemble chương trình được triển khai. Đối với hình dạng lệnh không có đối số nhất quán với lệnh harvest hoặc claim, thứ tự tài khoản thực tế là một tiền tố cố định (token_program, tài khoản trạng thái của farm, PDA vault-authority, vault reward đầu tiên của PDA đó, PDA thứ hai, người gọi, và ATA của người gọi cho mint reward đầu tiên), theo sau là các cặp (reward_vault_i, user_reward_ata_i) trong remaining_accounts cho mỗi luồng reward sau luồng đầu tiên. Quy ước ghép đôi là thực tế, nhưng nó chỉ bắt đầu ở luồng reward thứ hai: vault và ATA của luồng đầu tiên là các tài khoản cố định, không kề nhau, và hoàn toàn không phải là một phần của remaining_accounts.

Kiểm tra luồng CPI

Dev cục bộ yêu cầu các chương trình Raydium có sẵn trong trình xác thực kiểm tra của bạn. Ba tùy chọn:
  1. anchor test với sao chép chương trình. Kéo bytecode được triển khai mainnet vào trình xác thực cục bộ của bạn; xem Sao chép chương trình vào trình xác thực cục bộ dưới đây để xem cấu hình Anchor.toml và hai điều làm cho các bài kiểm tra tạo pool bị vấp phải.
  2. Devnet. Raydium triển khai hầu hết các chương trình tới devnet, nhưng ở các ID chương trình khác nhau so với mainnet cho mỗi chương trình (CPMM, CLMM, AMM v4, Stable AMM, và LaunchLab mỗi cái có một địa chỉ devnet riêng biệt; xem bảng Devnet trong reference/program-addresses). Farm v3/v5/v6 không được xuất bản đáng tin cậy trên devnet; API trực tiếp (https://api-v3-devnet.raydium.io/main/info) có bức tranh hiện tại. Nếu bạn sử dụng các hằng số DEVNET_PROGRAM_ID được gói trong raydium_clmm (hoặc tương đương cho các crate khác), đừng giả định ID mainnet cũng hoạt động trên devnet. Chạy anchor test --provider.cluster devnet để chạm vào mã trực tiếp khi bạn có các địa chỉ đúng.
  3. Triển khai cục bộ. Sao chép các repo Raydium (CPMM, CLMM; mã nguồn LaunchLab không có sẵn cho tùy chọn này) và anchor deploy tới trình xác thực cục bộ. Thêm overhead chu kỳ kiểm tra nhưng cho phép bạn sửa đổi người được gọi để gỡ lỗi.
Chạy với anchor test, hoặc anchor build trước và anchor test --skip-build sau nếu bạn đang lặp lại trên tệp kiểm tra mà không thay đổi chương trình.

Sao chép chương trình vào trình xác thực cục bộ

Điều này hoạt động theo ID chương trình bất kể liệu mã nguồn của chương trình có công khai hay không, vì vậy LaunchLab sao chép theo cách tương tự như CPMM và CLMM ngay cả khi mã nguồn của nó không có sẵn. reference/program-addresses là nguồn sự thật cho mọi địa chỉ ở đây.
Sao chép chương trình là không đủ nếu bài kiểm tra của bạn cũng tạo một pool (thay vì swap so với một pool đã tồn tại). Lệnh initialize của CPMM xác thực các tài khoản amm_config và create_pool_fee của nó so với dữ liệu trên chuỗi thực, vì vậy bạn cũng cần sao chép những cái đó, hoặc initialize sẽ thất bại hoàn toàn. Cụ thể cho CPMM: sao chép AmmConfig tầng phí bạn muốn (lấy địa chỉ của nó từ GET https://api-v3.raydium.io/main/cpmm-config, chỉ số 0 là tầng 0.25%) và tài khoản token nhận phí, được xác thực bằng địa chỉ chính xác, không được tạo nhanh chóng, vì vậy nó phải đã tồn tại.
Một pool mà bài kiểm tra của bạn vừa tạo không thể swap được ngay lập tức. initialize của CPMM im lặng ghi đè một open_time được yêu cầu không hoàn toàn trong tương lai (if open_time <= block_timestamp { open_time = block_timestamp + 1 }), vì vậy ngay cả startTime: 0 (“mở ngay lập tức,” theo SDK) để lại một khoảng cách thực tế ≥1 giây trước khi pool chấp nhận swaps. Một bài kiểm tra tạo pool và swap so với nó mà không có độ trễ sẽ chạm vào NotApproved. Một await ngắn (1–2s) giữa tạo pool và swap đầu tiên là đủ. Điều này cụ thể cho kiểm tra; một người dùng chạy hai lệnh thủ công riêng biệt thường sẽ không nhận thấy, vì gõ và khởi động quy trình đã ăn hết hơn một giây.

Con trỏ

Nguồn: