Skip to main content
Esta página fue traducida automáticamente por IA. La versión en inglés es la fuente autorizada.Ver versión en inglés →
CPI (“cross-program invocation”) es el mecanismo mediante el cual un programa de Solana invoca a otro. La mayoría de los programas de Raydium incluyen crates de envoltura CPI de Anchor que hacen que el sitio de llamada se parezca a una llamada de función tipada, con estructuras de cuentas que tienen nombres de campo validados y ayudantes cpi::<ix>(). Esta página documenta el patrón general una sola vez, luego las diferencias por programa. Para TypeScript ejecutable, consulta la página code-demos de cada capítulo de producto.

Qué patrón se aplica a qué programa

Si estás integrando CPMM, CLMM o LaunchLab, lee primero el patrón general, luego salta a la sección de tu programa para la lista de cuentas y cualquier diferencia. Farm v6 y AMM v4 son lo suficientemente diferentes como para justificar leer sus secciones de forma independiente.

Dependencias de Cargo

La clave de dependencia debe coincidir exactamente con el [package] name del repositorio de destino, guiones incluidos. Cargo no trata raydium_cp_swap como equivalente a raydium-cp-swap al resolver una dependencia de git.
branch = "master" rastrea la fuente publicada más reciente; fija a un rev = "<commit>" específico si necesitas una compilación reproducible. Esto se recomienda una vez que hayas pasado la prototipación, ya que un cambio de diseño de cuenta ascendente en master romperá tu compilación sin advertencia de otro modo. La bandera de característica cpi hace que los crates se compilen solo a la superficie de CPI (estructuras de cuentas + invocadores) en lugar del programa completo, por lo que tu binario se mantiene pequeño. anchor-lang / anchor-spl deben coincidir con lo que fija el crate de destino:
Toma ambos crates de la misma línea de Anchor. Ambas ramas de actualización fijan =1.0.2, por lo que un programa puede hacer CPI en CPMM y CLMM desde un único crate. Mezclar líneas rompe la compilación: por ejemplo, raydium-cp-swap en chore/upgrade-anchor con raydium-clmm en master. Cargo tendría que vincular dos copias incompatibles de los rasgos de Anchor en un binario. Si estás atrapado en un par mixto, divide el programa en dos, o abandona el crate de CPI tipado para un lado y codifica manualmente esa instrucción (el patrón mostrado para AMM v4 funciona para cualquier programa). Vuelve a verificar ambos archivos Cargo.toml antes de comenzar, ya que las ramas eventualmente se moverán a master.
Anchor 1.0 cambió dos cosas que toca cada sitio de llamada de CPI. Si estás moviendo una integración funcional de 0.3x:
  • CpiContext::new toma un Pubkey, no un AccountInfo. CpiContext::new(ctx.accounts.cpmm_program.to_account_info(), accts) se convierte en CpiContext::new(*ctx.accounts.cpmm_program.key, accts). Lo mismo para new_with_signer. El campo de estructura ahora es program_id: Pubkey.
  • Context tiene una vida útil, no cuatro. Context<'_, '_, 'info, 'info, MyProxySwap<'info>> se convierte en Context<'info, MyProxySwap<'info>>.
En el lado del cliente, anchor-client’s RequestBuilder::instructions() ahora devuelve Vec<Instruction> en lugar de Result<Vec<Instruction>> (suelta el ?), y CommitmentConfig se movió fuera de solana-sdk — tómalo de anchor_client en su lugar. spl-associated-token-account 8.0 re-exporta sus ayudantes desde el nuevo crate spl-associated-token-account-interface. get_associated_token_address e ID aún son alcanzables en la raíz del crate (spl_associated_token_account::{get_associated_token_address, ID}), pero los ayudantes de dirección están deprecados allí — prefiere depender de spl-associated-token-account-interface directamente e importar spl_associated_token_account_interface::address::get_associated_token_address y spl_associated_token_account_interface::program::ID. Ten en cuenta que ::address y ::program son módulos del crate de interfaz; spl_associated_token_account::address::… no se resuelve.
Para ejemplos de CPI funcionales que cablean las estructuras de cuentas de extremo a extremo, consulta raydium-io/raydium-cpi-example (cubre AMM v4, CPMM y CLMM). Su rama más nueva es anchor-0.31.0 — aún no hay rama de Anchor 1.x, así que trata ese repositorio como la referencia para cableado de estructura de cuentas, no para los fijos de versión que esta página ordena.

El patrón general de CPI de Anchor

Esta sección recorre CPMM de extremo a extremo como el ejemplo trabajado: estructura Accounts, CpiContext, cpi::<ix>(). CLMM sigue la forma idéntica, con una lista de cuentas diferente y un requisito de cuentas restantes. LaunchLab sigue la misma mecánica pero su lista de cuentas lleva varias cuentas sin equivalente en CPMM/CLMM (global_config, platform_config, event_authority, program), así que trátalo como el mismo patrón, no la misma forma. Consulta la sección propia de cada programa en lugar de asumir que la lista de cuentas de este tutorial se transfiere directamente.

Construcción de la lista de cuentas

Cada CPI de Raydium requiere una estructura Accounts en el programa que llama. Sus campos son las cuentas que tu instrucción necesita, con validadores a nivel de campo; su orden de declaración no tiene que coincidir con el orden de cuentas de instrucción de Raydium, ya que tu propio cliente generado por IDL las direcciona por nombre, no por posición:
La mayoría de las cuentas del lado de Raydium son UncheckedAccount porque el llamado (Raydium) posee la validación. Tu programa que llama solo valida estrictamente cuentas que tú posees, como ATAs de usuario y tus propios PDAs. El comentario de documentación /// CHECK: suprime la advertencia de Anchor sobre verificaciones faltantes. La excepción del lado de Raydium es cpmm_program en sí: es el programa que se invoca en lugar de una cuenta de datos que Raydium valida internamente, por lo que se tipea Program<T> y obtiene la verificación de dirección automática de Anchor en lugar de un /// CHECK: manual. Esta forma mayormente UncheckedAccount, donde Raydium valida sus propias cuentas, es la misma para CLMM y LaunchLab. Este ejemplo asume que ambos mints son Token SPL clásico; si cualquiera de los lados puede ser un mint Token-2022, agrega un campo token_program_2022: Program<'info, anchor_spl::token_2022::Token2022> y pásalo como input_token_program/output_token_program de ese lado en la llamada de CPI a continuación en lugar de token_program.

Construir la llamada de CPI

Anchor genera un ayudante por instrucción, junto con una estructura de cuentas de CPI (cpi::accounts::Swap, con alias CpmmSwap a continuación). A diferencia de tu propia estructura MyProxySwap anterior, los nombres y el orden de los campos de esta son fijos por el IDL propio de raydium-cp-swap y tienen que coincidir exactamente:
cpi::swap_base_input se genera desde el IDL; su lista de argumentos refleja la lista de argumentos de la instrucción de Anchor. Cada programa de Raydium basado en Anchor confirmado (CPMM, CLMM, LaunchLab) genera sus ayudantes cpi::<ix>() de la misma manera, con el nombre de la función coincidiendo con el nombre de la instrucción en snake_case. Si esto se extiende a Farm v6 no está confirmado; consulta su sección.

Semillas de firmante (CPI firmado por PDA)

Cuando tu programa firma el CPI en nombre de un PDA (común para bóvedas, depósitos en garantía, etc.), usa CpiContext::new_with_signer:
Las semillas del firmante deben coincidir con la derivación del PDA. Para cualquier cuenta pasada como authority (o rol de firmante similar), el tiempo de ejecución de Solana verifica que el PDA firme a través de estas semillas.

Cuentas restantes

Algunas instrucciones de Raydium toman cuentas restantes, una lista de longitud variable anexada después de las cuentas fijas. Los ayudantes de CPI de Anchor no verifican de tipo las cuentas restantes; pásalas a través de .with_remaining_accounts(...):
El orden siempre importa, ya que el programa receptor itera las cuentas restantes en el orden en que las pasas. Dos órdenes confirmados:
  • CLMM SwapV2: arrays de tick, ordenados direccionalmente.
  • Farm v6: pares (reward_vault, user_reward_ata), pero solo desde la segunda secuencia de recompensas en adelante; consulta Farm v6 para ver qué muestra la decodificación de una transacción real.

Aplicar el patrón: CLMM

SwapV2 sigue el patrón general anterior con una lista de cuentas diferente y un requisito de cuentas restantes para arrays de tick. El módulo #[program] del crate se llama raydium_clmm, que también es su ruta de use de Rust.
La estructura de cuentas de CPI se llama SwapSingleV2, no SwapV2. SwapV2 es el nombre de la instrucción en cadena.
Calcula la lista de arrays de tick de la misma manera que lo hace el SDK, a través de una cotización contra el estado actual del pool, en lugar de adivinar un conteo fijo; un swap que supera los arrays que pasaste se revierte con TickArrayNotFound (consulta products/clmm/instructions para la tabla de cuentas completa y la lista de errores). Pásalos en la dirección del recorrido de precios: primer array en dirección de swap primero.

Aplicar el patrón: LaunchLab

LaunchLab está basado en Anchor e IDL-publicado: raydium_launchpad/raydium_launchpad.json en el repositorio público raydium-idl. El identificador de metadatos internos de ese IDL es raydium_launchpad, un nombre técnico para el programa subyacente, no un nombre alternativo para el producto. A diferencia de CPMM y CLMM, sin embargo, la fuente del programa en sí no está disponible públicamente (consulta reference/program-addresses). No hay dependencia de git = "..." a la que apuntar Cargo, y no hay fuente para confirmar cuál sería la ruta de use de Rust de un crate real. Genera enlaces desde el IDL publicado usando la macro declare_program! de Anchor. Guarda el JSON del IDL como idls/raydium_launchpad.json en tu crate (Cargo busca un directorio idls/ relativo a CARGO_MANIFEST_DIR), luego declare_program!(raydium_launchpad); genera estructuras raydium_launchpad::cpi::accounts::<Ix> y funciones cpi::<ix>() directamente desde el IDL, sin fuente de programa requerida. El nombre de la estructura de cuentas generada es siempre el nombre de la instrucción en PascalCase (buy_exact_in → BuyExactIn), y los nombres de los campos coinciden exactamente con los nombres de cuentas del IDL, la misma lista de cuentas ya utilizada en MyProxyBuy a continuación. La forma de CPI sigue el patrón general. La lista de cuentas y argumentos a continuación provienen del IDL en cadena de la instrucción buy_exact_in, no de products/launchlab/instructions.mdx:
Después de la graduación, el programa de destino es CPMM o AMM v4 dependiendo de pool_state.migrate_type, que products/launchlab/accounts.mdx dice que se establece en el momento de Initialize. Tu lista de cuentas de CPI tiene que estar preparada para cualquiera de los dos, o necesitas leer migrate_type de PoolState primero y ramificar.

Propagación de errores

Cada programa de Raydium basado en Anchor devuelve su propio enum de error; Anchor los envuelve, por lo que tu programa que llama los ve como Err(ProgramError::Custom(code)). Para manejar errores específicos:
Intercambia el tipo de error relevante para el programa que estás llamando (raydium_clmm::error::ErrorCode para CLMM, y así sucesivamente). Los números de código de error son estables según la política de IDL (sdk-api/anchor-idl), por lo que puedes probar contra códigos específicos comparando contra el valor numérico. Tablas de errores completas: CPMM, CLMM, AMM v4, Farm v6 y LaunchLab.

Presupuesto de cálculo en CPIs compuestos

Cada marco de CPI tiene sobrecarga, y el consumo de CU propio del llamado se apila encima del tuyo, por lo que una transacción que llama a Raydium desde dentro de tu programa necesita un presupuesto de cálculo explícito en lugar de confiar en el predeterminado de 200k CU.
Medido, no estimado. Un swap_base_input de CPMM en mainnet consume ~23,000 CU en el programa CPMM en sí — muestreado 2026-09-09 en ocho swaps en vivo en un pool de alto volumen (22,721–23,052), leído de la línea de registro Program CPMMoo8… consumed N of M compute units. Para comparación: swap de AMM v4 ~26,000; swap de CLMM ~41,000; swap_v2 de CLMM ~48,000 (43,838–52,887), aumentando con cada cruce de tick.Una revisión anterior de esta página reportó ~47,700 CU para un CPI de proxy-swap. Esa cifra fue la transacción completa (computeUnitsConsumed), que incluye tu propio programa, el marco de CPI y cualquier configuración de ATA — no el costo del llamado. Ambos son útiles, pero no son el mismo número, así que compara como con como. Mide tu propia transacción en lugar de presupuestar de cualquiera de los dos.
Los CPIs de CLMM y LaunchLab cuestan más (CLMM en particular camina arrays de tick adicionales a través de remaining_accounts, agregando CU por array), pero solo la cifra de CPMM anterior es un valor medido. Siempre establece un límite explícito de ComputeBudgetProgram::set_compute_unit_limit(...) dimensionado desde tu propia medición, no un número copiado de la documentación, ya que el límite predeterminado de 200k CU se agotará silenciosamente y los costos por instrucción cambian a medida que los programas se actualizan.

AMM v4: construcción manual de instrucciones

AMM v4 es anterior a Anchor y no tiene crate de CPI, lo que lo convierte en el único programa en este documento que no sigue el patrón general anterior. Construye la Instruction manualmente:
Consulta products/amm-v4/code-demos para la lista de cuentas completa.

Farm v6

Usa el SDK de TS si esa es una opción para tu integración. raydium.farm.deposit(...) (consulta products/farm-staking/code-demos) es ejercido por demostraciones reales y no depende de si existe un crate de Anchor de Rust para este programa.
Farm v6 no ofrece ruta de CPI de Anchor. No hay crate raydium_farm_v6 en crates.io, no hay repositorio de fuente pública, y no hay IDL en cadena — el programa no tiene ni una cuenta anchor:idl heredada ni una entrada en el programa de Metadatos del Programa (consulta sdk-api/anchor-idl). Trátalo como un programa que no es de Anchor y construye sus instrucciones manualmente, como a continuación.
Si necesitas CPI de Rust de todas formas, por ejemplo componiendo desde otro programa en cadena, construye la Instruction manualmente, de la misma manera que AMM v4: deriva la lista de cuentas real y los discriminadores de instrucción de forma independiente, por ejemplo decodificando los diseños de TypeScript del SDK (raydium-sdk-V2’s farm module), decodificando transacciones reales directamente (consulta a continuación), o volcando y desensambla el programa desplegado. Para la forma de instrucción de argumento cero consistente con una llamada de cosecha o reclamación, el orden de cuentas real es un prefijo fijo (token_program, la cuenta de estado del farm, un PDA de autoridad de bóveda, el primer vault de recompensa de ese PDA, un segundo PDA, el llamador, y el ATA del llamador para ese primer mint de recompensa), seguido de pares (reward_vault_i, user_reward_ata_i) en remaining_accounts para cada secuencia de recompensas después de la primera. La convención de emparejamiento es real, pero solo comienza en la segunda secuencia de recompensas: el vault y ATA de la primera secuencia son cuentas fijas, no adyacentes entre sí, y no son parte de remaining_accounts en absoluto.

Probar un flujo de CPI

El desarrollo local requiere que los programas de Raydium estén disponibles en tu validador de prueba. Tres opciones:
  1. anchor test con clonación de programa. Extrae bytecode desplegado en mainnet a tu validador local; consulta Clonar programas en un validador local a continuación para la configuración de Anchor.toml y dos cosas que atrapan específicamente las pruebas de creación de pool.
  2. Devnet. Raydium despliega la mayoría de los programas a devnet, pero en diferentes IDs de programa que mainnet para cada programa (CPMM, CLMM, AMM v4, Stable AMM y LaunchLab cada uno tienen una dirección de devnet distinta; consulta la tabla de Devnet en reference/program-addresses). Farm v3/v5/v6 no se publican de manera confiable en devnet; la API en vivo (https://api-v3-devnet.raydium.io/main/info) tiene la imagen actual. Si usas constantes DEVNET_PROGRAM_ID incluidas en raydium_clmm (o el equivalente para otros crates), no asumas que un ID de mainnet también funciona en devnet. Ejecuta anchor test --provider.cluster devnet para golpear código en vivo una vez que tengas las direcciones correctas.
  3. Despliegue local. Clona los repositorios de Raydium (CPMM, CLMM; la fuente de LaunchLab no está disponible para esta opción) y anchor deploy a un validador local. Agrega sobrecarga de ciclo de prueba pero te permite modificar el llamado para depuración.
Ejecuta con anchor test, o anchor build primero y anchor test --skip-build después si estás iterando en el archivo de prueba sin cambiar el programa.

Clonar programas en un validador local

Esto funciona por ID de programa independientemente de si la fuente del programa es pública, por lo que LaunchLab se clona de la misma manera que CPMM y CLMM incluso aunque su fuente no esté disponible. reference/program-addresses es la fuente de verdad para cada dirección aquí.
Clonar el programa no es suficiente si tu prueba también crea un pool (en lugar de hacer swap contra uno que ya existe). La instrucción initialize de CPMM valida sus cuentas amm_config y create_pool_fee contra datos reales en cadena, por lo que necesitas clonar esas también, o initialize falla directamente. Para CPMM específicamente: clona el AmmConfig de nivel de tarifa que desees (obtén su dirección de GET https://api-v3.raydium.io/main/cpmm-config, el índice 0 es la tarifa del 0.25%) y la cuenta de token receptor de tarifa, validada por dirección exacta, no creada sobre la marcha, por lo que ya debe existir.
Un pool que tu prueba acaba de crear no es intercambiable en el mismo instante. La instrucción initialize de CPMM anula silenciosamente un open_time solicitado que no está estrictamente en el futuro (if open_time <= block_timestamp { open_time = block_timestamp + 1 }), por lo que incluso startTime: 0 (“abierto inmediatamente,” según el SDK) deja una brecha real de ≥1 segundo antes de que el pool acepte swaps. Una prueba que crea un pool e inmediatamente hace swap contra él golpeará NotApproved. Una breve await (1–2s) entre la creación del pool y el primer swap es suficiente. Esto es específico de las pruebas; un humano ejecutando dos comandos manuales separados normalmente no lo notaría, ya que escribir e iniciar procesos ya consumen más de un segundo.

Punteros

Fuentes: