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 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 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 phụ thuộc phải khớp chính xác với [package] name của repo mục tiêu, 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 phụ thuộc 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ì 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 + invoker) 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 mục tiêu ghim:
Lấy cả hai crate từ cùng một dòng Anchor. Cả hai nhánh nâng cấp đều ghim =1.0.2, vì vậy một chương trình có thể CPI vào CPMM và CLMM từ một crate duy nhất. Trộn các dòng sẽ phá vỡ bản dựng: ví dụ, raydium-cp-swap trên chore/upgrade-anchor với raydium-clmm trên master. 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 Anchor vào một tệp nhị phân. Nếu bạn bị mắc kẹt với một cặp hỗn hợp, hãy chia chương trình thành hai, hoặc bỏ crate CPI được gõ cho một bên 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, vì các nhánh sẽ chuyển sang master cuối cùng.
Anchor 1.0 đã thay đổi hai điều mà mọi trang gọi CPI đều chạm vào. Nếu bạn đang chuyển một tích hợp 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 máy khách, 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 phụ thuộc vào 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 hoạt động 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à tài liệu tham khảo cho dây dẫn 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ụ thực tế: 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ột mô hình, không phải cùng một 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 của Raydium đều 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 hướng dẫn của Raydium, vì máy khách được tạo IDL của riêng bạn 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 xác thực. Chương trình gọi của bạn chỉ xác thực chặt chẽ các tài khoản bạn sở hữu, chẳng hạn như ATA của người dùng và PDA của riêng bạn. Nhận xét doc /// CHECK: 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 UncheckedAccount chủ yếu này, nơi Raydium xác thực các tài khoản của riêng nó, giống nhau cho CLMM và LaunchLab. Ví dụ này giả định cả hai mint là Token SPL cổ điển; nếu một 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 tài khoản CPI (cpi::accounts::Swap, được đặt bí danh CpmmSwap dưới đây). Không giống như struct MyProxySwap của riêng 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 riêng 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 đến 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 đạo hàm 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ự), thời gian chạy Solana 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 thêm vào 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 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 phần thưởng 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 tài khoản CPI đượ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 báo giá 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 mảng 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 bước đi giá: mảng đầ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 như CPMM và CLMM, tuy nhiên, mã nguồn của chương trình riêng không có sẵn công khai (xem reference/program-addresses). Không có phụ thuộc git = "..." để chỉ Cargo vào, 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 các ràng buộc 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 tài khoản đượ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 mục tiêu 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 một trong hai, hoặc bạn cần đọc migrate_type từ PoolState trước tiên 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 riêng của nó; Anchor bao bọc chúng, vì vậy chương trình gọi của bạn thấy chúng là 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.

Ngân sách tính toán trong CPI được soạn thảo

Mỗi khung CPI có chi phí chung, và tiêu thụ CU riêng của người được gọi xếp chồng lên trê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 ngân sách tính toán 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 riêng — được lấy mẫu 2026-09-09 trên tám swap 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: swap AMM v4 ~26,000; CLMM swap ~41,000; CLMM swap_v2 ~48,000 (43,838–52,887), tăng với mỗi lần vượt qua tick.Một bản sửa đổi trước đó của trang này báo cáo ~47,700 CU cho một CPI proxy-swap. Con số đó là toàn bộ giao dịch (computeUnitsConsumed), bao gồm chương trình của riêng bạn, 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 như với như. Đ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.
CLMM và LaunchLab CPI có chi phí cao hơn (CLMM đặc biệt bước đi các tick arrays bổ sung thông qua remaining_accounts, thêm CU cho mỗi mảng), nhưng chỉ con số CPMM ở trên là giá trị được đo lường. Luôn đặt một giới hạn 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 bản demo thực tế và không phụ thuộc vào việc crate 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ó là 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 cần Rust CPI dù sao, ví dụ soạn thảo từ một chương trình trên chuỗi khác, xây dựng Instruction bằng tay, theo cách tương tự như AMM v4: suy ra danh sách tài khoản thực tế và discriminator lệnh độ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ã giao dịch thực tế trực tiếp (xem dưới đây), hoặc xả và tháo dỡ 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à tiền tố cố định (token_program, tài khoản trạng thái của farm, PDA vault-authority, vault phần thưởng đầu tiên của PDA đó, PDA thứ hai, người gọi, và ATA của người gọi cho mint phần thưởng đầ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 phần thưởng sau cái đầu tiên. Quy ước ghép đôi là thực tế, nhưng nó chỉ bắt đầu ở luồng phần thưởng thứ hai: vault và ATA của luồng đầu tiên là tài khoản cố định, không kề nhau, và hoàn toàn không phải là 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 gây khó khăn cho các bài kiểm tra tạo pool cụ thể.
  2. Devnet. Raydium triển khai hầu hết các chương trình đến 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 một cách đá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 để đánh 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 của LaunchLab không có sẵn cho tùy chọn này) và anchor deploy vào trình xác thực cục bộ. Thêm chi phí 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 tiên và anchor test --skip-build sau nếu bạn đang lặp lại 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ể 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 cái đã 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ần sao chép những cái đó cũng, hoặc initialize sẽ thất bại hoàn toàn. Đối với CPMM cụ thể: sao chép AmmConfig tầng phí bạn muốn (tìm nạp đị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ể hoán đổi ngay lập tức. initialize của CPMM im lặng ghi đè 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 swap. Một bài kiểm tra tạo pool và swap so với nó mà không có độ trễ sẽ đánh 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 việc 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: