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 basadas en Anchor que hacen que el sitio de llamada se parezca a una llamada de función tipada, con estructuras de cuenta 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 cada 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, incluidos los guiones. 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 fase de prototipado, ya que un cambio de diseño de cuenta ascendente en master romperá tu compilación sin previo aviso. La bandera de característica cpi hace que los crates se compilen solo a la superficie de CPI (estructuras de cuenta + invocadores) en lugar del programa completo, por lo que tu binario se mantiene pequeño. anchor-lang / anchor-spl debe coincidir con lo que fija el crate de destino:
Toma ambos crates de la misma línea de Anchor. Ambas ramas master fijan =1.0.2, por lo que un programa puede hacer CPI en CPMM y CLMM desde un solo crate. Mezclar líneas rompe la compilación: por ejemplo, raydium-cp-swap en master con raydium-clmm fijado a una rev anterior a su actualización de Anchor 1.0. 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 CPI tipado para un lado y codifica manualmente esa instrucción (el patrón mostrado para AMM v4 funciona para cualquier programa).
Anchor 1.0 cambió dos cosas que toca cada sitio de llamada 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 cuenta 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 el cableado de estructura de cuenta, no para los pines de versión que esta página ordena.

El patrón general de CPI en Anchor

Esta sección recorre CPMM de principio a fin como ejemplo práctico: struct Accounts, CpiContext, cpi::<ix>(). CLMM sigue la misma estructura, con una lista de cuentas diferente y un requisito de remaining-accounts. LaunchLab sigue la misma mecánica, pero su lista de cuentas incluye 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 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 un struct Accounts en el programa que realiza la llamada. Sus campos son las cuentas que tu instrucción necesita, con validadores a nivel de campo; el orden de su declaración no tiene que coincidir con el orden de cuentas de la instrucción de Raydium, ya que tu 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 programa llamado (Raydium) es responsable de la validación. Tu programa que realiza la llamada solo valida estrictamente las cuentas que tú posees, como las ATAs del usuario y tus propios PDAs. El comentario de documentación /// CHECK: suprime la advertencia de Anchor sobre validaciones faltantes. La única 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, así que se tipifica como Program<T> y obtiene la verificación automática de dirección 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, añade 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 CPI de abajo en lugar de token_program.

Construcción de la llamada CPI

Anchor genera un helper por instrucción, junto con un struct de cuentas CPI (cpi::accounts::Swap, aliasado como CpmmSwap abajo). A diferencia de tu struct MyProxySwap anterior, los nombres y el orden de los campos de este están fijados por el IDL propio de raydium-cp-swap y tienen que coincidir exactamente:
cpi::swap_base_input se genera a partir del 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 helpers 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.

Signer seeds (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:
Los signer seeds deben coincidir con la derivación del PDA. Para cualquier cuenta pasada como authority (o rol de firmante similar), el runtime de Solana verifica que el PDA firme a través de estos seeds.

Remaining accounts

Algunas instrucciones de Raydium toman remaining accounts, una lista de longitud variable añadida después de las cuentas fijas. Los helpers CPI de Anchor no verifican el tipo de remaining accounts; pásalos a través de .with_remaining_accounts(...):
El orden siempre importa, ya que el programa receptor itera los remaining accounts en el orden que los pasas. Dos órdenes confirmados:
  • CLMM SwapV2: tick arrays, ordenados direccionalmente.
  • Farm v6: pares (reward_vault, user_reward_ata), pero solo a partir del segundo flujo 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 remaining-accounts para tick arrays. El módulo #[program] del crate se llama raydium_clmm, que también es su ruta de use en Rust.
El struct de cuentas CPI se llama SwapSingleV2, no SwapV2. SwapV2 es el nombre de la instrucción en cadena.
Calcula la lista de tick-array 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 número fijo; un swap que supera los arrays que pasaste 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 interno 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, el código fuente del programa no está disponible públicamente (ver reference/program-addresses). No hay una dependencia git = "..." a la que apuntar con Cargo, ni código fuente para confirmar cuál sería la ruta use real de Rust en un crate. Genera bindings 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 structs raydium_launchpad::cpi::accounts::<Ix> y funciones cpi::<ix>() directamente desde el IDL, sin necesidad del código fuente del programa. El nombre del struct de cuentas generado siempre es el nombre de la instrucción en PascalCase (buy_exact_in → BuyExactIn), y los nombres de los campos coinciden exactamente con los nombres de las cuentas del IDL, la misma lista de cuentas ya utilizada en MyProxyBuy a continuación. La forma del CPI sigue el patrón general. La lista de cuentas y los argumentos a continuación provienen de la instrucción buy_exact_in del IDL en cadena, no de products/launchlab/instructions.mdx:
Después de la graduación, el programa destino es CPMM o AMM v4 dependiendo de pool_state.migrate_type, que según products/launchlab/accounts.mdx se establece en el momento de Initialize. Tu lista de cuentas CPI tiene que estar preparada para cualquiera de los dos, o necesitas leer migrate_type de PoolState primero y ramificarte.

Propagación de errores

Cada programa Raydium basado en Anchor devuelve su propio enum de error; Anchor los envuelve, por lo que tu programa llamador los ve como Err(ProgramError::Custom(code)). Para manejar errores específicos:
Sustituye 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 del 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ómputo en CPIs compuestos

Cada frame de CPI tiene sobrecarga, y el consumo de CU propio del programa llamado se suma al tuyo, por lo que una transacción que llama a Raydium desde dentro de tu programa necesita un presupuesto de cómputo explícito en lugar de depender del default de 200k CU.
Medido, no estimado. Un swap_base_input de CPMM en mainnet consume ~23,000 CU en el programa CPMM mismo — muestreado el 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 log 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 reportaba ~47,700 CU para un CPI de proxy-swap. Esa cifra era la transacción completa (computeUnitsConsumed), que incluye tu propio programa, el frame de CPI y cualquier configuración de ATA — no el costo del programa llamado. Ambos son útiles, pero no son el mismo número, así que compara lo similar con lo similar. Mide tu propia transacción en lugar de presupuestar basándote en ninguno de los dos.
Los CPIs de CLMM y LaunchLab cuestan más (CLMM en particular recorre arrays de tick adicionales vía remaining_accounts, agregando CU por array), pero solo la cifra de CPMM anterior es un valor medido. Siempre establece un límite explícito ComputeBudgetProgram::set_compute_unit_limit(...) dimensionado a partir de tu propia medición, no un número copiado de la documentación, ya que el límite default 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 Instruction

AMM v4 es anterior a Anchor y no tiene un 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:
Ver products/amm-v4/code-demos para la lista completa de cuentas.

Farm v6

Usa el SDK de TS si esa es una opción para tu integración. raydium.farm.deposit(...) (ver products/farm-staking/code-demos) está ejercitado por demos reales y no depende de si existe un crate Anchor de Rust para este programa.
Farm v6 no ofrece ruta de CPI de Anchor. No hay un crate raydium_farm_v6 en crates.io, no hay repositorio de código fuente público, y no hay IDL en cadena — el programa no tiene ni una cuenta anchor:idl heredada ni una entrada en el Program Metadata program (ver sdk-api/anchor-idl). Trátalo como un programa que no es de Anchor y construye sus instrucciones manualmente, como se muestra 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 layouts de TypeScript del SDK (raydium-sdk-V2’s farm module), decodificando transacciones reales directamente (ver a continuación), o volcando y desensambland el programa desplegado. Para la forma de instrucción de cero argumentos consistente con una llamada de harvest o claim, el orden de cuentas real es un prefijo fijo (token_program, la cuenta de estado del farm, un PDA de autoridad de bóveda, la primera bóveda de recompensas 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 stream de recompensa después del primero. La convención de emparejamiento es real, pero solo comienza en el segundo stream de recompensa: la bóveda y ATA del primer stream son cuentas fijas, no adyacentes entre sí, y no son parte de remaining_accounts en absoluto.

Prueba de un flujo CPI

El desarrollo local requiere que los programas de Raydium estén disponibles en tu validador de prueba. Tienes tres opciones:
  1. anchor test con clonación de programa. Extrae el bytecode desplegado en mainnet hacia tu validador local; consulta Clonación de programas en un validador local más abajo para la configuración de Anchor.toml y dos cosas que suelen causar problemas en las pruebas de creación de pools.
  2. Devnet. Raydium despliega la mayoría de programas en devnet, pero con IDs de programa diferentes a los de mainnet para cada uno (CPMM, CLMM, AMM v4, Stable AMM y LaunchLab tienen direcciones distintas en devnet; consulta la tabla de Devnet en reference/program-addresses). Farm v3/v5/v6 no se publican de forma confiable en devnet; la API en vivo (https://api-v3-devnet.raydium.io/main/info) tiene la información actual. Si usas las constantes DEVNET_PROGRAM_ID incluidas en raydium_clmm (o equivalentes en otros crates), no asumas que un ID de mainnet también funciona en devnet. Ejecuta anchor test --provider.cluster devnet para probar el código en vivo una vez que tengas las direcciones correctas.
  3. Despliegue local. Clona los repositorios de Raydium (CPMM, CLMM; el código fuente de LaunchLab no está disponible para esta opción) y ejecuta anchor deploy en un validador local. Añade sobrecarga al ciclo de prueba pero te permite modificar el programa llamado para depuración.
Ejecuta con anchor test, o primero anchor build y luego anchor test --skip-build si estás iterando sobre el archivo de prueba sin cambiar el programa.

Clonación de programas en un validador local

Esto funciona por ID de programa independientemente de si el código fuente del programa es público, así que LaunchLab se clona de la misma manera que CPMM y CLMM aunque su código 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, así que necesitas clonarlas también, o initialize falla directamente. Para CPMM específicamente: clona el AmmConfig de nivel de comisión que desees (obtén su dirección de GET https://api-v3.raydium.io/main/cpmm-config, el índice 0 es el nivel del 0.25%) y la cuenta de token receptora de comisiones, validada por dirección exacta, no creada sobre la marcha, así 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 }), así 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 sin demora golpeará NotApproved. Un 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.

Referencias

Fuentes: