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 →

Cuándo CPI es la herramienta correcta

Un programa personalizado tiene sentido cuando el intercambio debe ocurrir de forma atómica con otros cambios de estado en cadena que solo tu programa puede realizar. Casos comunes:
  • Programas de depósito en garantía / órdenes limitadas — el usuario deposita un mint en tu depósito en garantía, tu programa vigila una condición de precio, y cuando se activa, tu programa intercambia atómicamente a través de Raydium y acredita la cuenta del usuario.
  • Proxies agregadores — una única instrucción que enruta un intercambio a través de Raydium + uno o más DEXes adicionales, con todos los saltos bajo una única verificación de slippage propiedad de tu programa.
  • Bóvedas de auto-capitalización — deposita LP o participación en granja en tu bóveda, la bóveda cosecha recompensas según un cronograma, re-suministra liquidez, emite tokens de participación.
  • Bóvedas de estrategia — posiciones LP apalancadas que se reequilibran intercambiando a través de CLMM; liquidadores que cierran posiciones e intercambian garantías en una transacción.
  • Plataformas de lanzamiento de tokens con vesting personalizado — tu programa mantiene tokens de vesting y los libera en un pool de Raydium según un cronograma.
Si solo quieres enviar un intercambio desde código fuera de cadena, CPI es excesivo — usa el SDK. CPI justifica su complejidad solo cuando la atomicidad con tu propio estado es el requisito.

Patrones de composición

Patrón 1: Proxy delgado

Tu programa expone una única instrucción que valida alguna política (p. ej. pares de mint en lista blanca, descuento de tarifa para usuarios verificados) y luego reenvía a Raydium.
El estado vive en los ATAs del usuario. Tu programa no posee tokens. Huella de confianza mínima.

Patrón 2: Depósito en garantía

Tu programa posee un PDA que mantiene el mint de entrada del usuario. Al activarse, el PDA firma un CPI a Raydium para intercambiar su propio saldo.
Detalle crítico: el PDA firma a través de CpiContext::new_with_signer. Ver Semillas de firmante PDA.

Patrón 3: Multi-salto compuesto

Tu programa emite múltiples CPIs en una instrucción, aplicando un único límite de slippage en todos ellos. Las instrucciones de intercambio de Raydium tienen cada una su propio minimum_amount_out, pero estableces esos en 0 (o un piso muy flexible) y aplicas un mínimo estricto final tú mismo después del último salto.
Esto te da una única puerta de reversión para toda la ruta. Solo usa este patrón cuando confíes en que cada salto sea seguro en slippage; de lo contrario, deja que cada salto aplique su propio mínimo.

Patrón 4: Bóveda / estrategia

Tu programa mantiene tokens LP o participación en granja en un PDA. Un guardián (o el usuario) llama a compound(), que:
  1. Cosecha recompensas de la granja.
  2. Intercambia recompensas por tokens de pool (CPI en CPMM o CLMM).
  3. Deposita los ingresos de vuelta en el LP (otro CPI).
  4. Participa en el nuevo LP (otro CPI).
Todo en una transacción para que el NAV de la bóveda se mueva atómicamente. El presupuesto de cómputo es típicamente 600k–1M CU; las tablas de búsqueda de direcciones son obligatorias.

Construcción de lista de cuentas

La estructura Accounts del programa que llama refleja el orden de cuentas del programa de Raydium, pero la mayoría de las cuentas del lado de Raydium son UncheckedAccount porque Raydium las valida a sí mismo. Solo añades restricciones en cuentas que posees:
La asimetría — validación estricta en tus cuentas, UncheckedAccount en las de Raydium — no es pereza. El receptor valida las suyas; validar dos veces en el llamador solo quema CU y arriesga desincronización cuando Raydium envía un nuevo campo de diseño de struct.

La llamada CPI en sí

Semillas de firmante PDA

El CPI solo tiene éxito si el PDA pasado como authority coincide con la derivación que el llamador reclama. Los dos deben estar de acuerdo en:
  1. La secuencia de bytes de semilla (aquí [b"escrow", user.key().as_ref()]).
  2. El bump.
  3. El ID del programa que llama (tu programa, no el de Raydium).
Raydium no le importa quién sea la autoridad — solo le importa que la firma de authority cubra la transacción y que el ATA de entrada sea propiedad de esa autoridad. La validación ocurre en anchor_spl::token::transfer: el campo authority del ATA debe ser igual al firmante. Error común: pasar user como la autoridad (e intercambiar desde escrow_input_ata que es propiedad del PDA de depósito en garantía). El programa SPL Token rechaza con owner mismatch. Siempre haz que el campo authority coincida con el propietario del ATA.

Cuentas restantes

Varias instrucciones de Raydium toman una lista de longitud variable de cuentas añadidas después de las fijas — cuentas restantes.
  • CLMM SwapV2: 1–8 cuentas TickArrayState para los arrays de tick que el intercambio puede atravesar, en dirección de intercambio.
  • Farm v6 Deposit / Harvest / Withdraw: pares (reward_vault, user_reward_ata), un par por ranura de recompensa activa.
  • Mints de gancho de transferencia Token-2022: el programa de gancho de transferencia más cualquier cuenta que el gancho necesite.
Los ayudantes CPI de Anchor no verifican tipos en cuentas restantes. Pásalas a través:
El orden importa. Para CLMM:
Para cosecha de farm v6:
Tu programa que llama debe pasar las cuentas restantes que recibe del cliente sin cambios. No intentes filtrarlas o reordenarlas.

Presupuesto de cómputo para llamadas compuestas

Un CPI cuesta ~1.500 CU por el marco de llamada en sí; el uso de CU propio del llamado se apila encima. Presupuesto aproximado por CPI de Raydium: Añade ~1.500 por cada marco CPI y ~20.000 por la sobrecarga de tu propio programa. Un auto-capitalizador haciendo harvest → swap A → swap B → deposit LP → stake LP fácilmente alcanza 700k CU. Siempre establece un ComputeBudgetProgram::set_compute_unit_limit explícito:
El techo de 200k CU predeterminado se agotará silenciosamente mucho antes de que una llamada compuesta se complete.

Propagación de errores

Los programas de Raydium devuelven errores de Anchor con códigos estables. Tu programa que llama los ve como Err(ProgramError::Custom(code)). Propaga por defecto:
O intercepta códigos específicos:
La asignación de código de error a significado es estable según la política de IDL (sdk-api/anchor-idl); nuevos códigos se añaden al final, códigos existentes nunca cambian de significado.

Ejemplo completo trabajado: depósito en garantía de orden limitada

Flujo:
  1. open_order — el usuario deposita amount_in de input_mint en el PDA de depósito en garantía; registra min_amount_out objetivo y vencimiento.
  2. execute_order — cualquiera (guardián) llama con las cuentas de pool actuales. El programa verifica que la cotización actual ≥ min_amount_out, luego CPI intercambio de Raydium y mantiene la salida en depósito en garantía.
  3. claim — el usuario retira el mint de salida del depósito en garantía.
El guardián paga la tarifa de transacción (obtiene una tarifa de guardián en otro lugar — no se muestra). El PDA de depósito en garantía firma el CPI. Tanto la verificación de slippage del lado de Raydium como la verificación de delta propia del depósito en garantía aplican el piso — doble seguridad.

Pruebas

Extrayendo programas de Raydium en un validador local para pruebas de integración (desde Anchor.toml):
Clona también las cuentas de estado del pool para que tus pruebas puedan ejecutar realmente intercambios; anchor test las obtiene de mainnet al inicio. Ver sdk-api/rust-cpi.

Trampas específicas de composición

Reentrancia

Solana no tiene verdadera reentrancia — un CPI no puede llamar de vuelta al programa originador en la misma invocación. Pero aún puedes construirte una reentrancia lógica: un CPI que lee tu estado, luego tu código lo lee de nuevo asumiendo que el CPI no lo cambió. Para Raydium, los CPIs no tocan tu estado, así que esto es menos una preocupación que p. ej. contextos de préstamo flash. Pero si compones Raydium con un protocolo de préstamo, ten cuidado.

Deriva de mutabilidad de cuenta

Si tu programa pasa una cuenta como mut pero Raydium la espera de solo lectura (o viceversa), el tiempo de ejecución rechaza la invocación con InvalidAccountData. Siempre verifica la mutabilidad esperada de la instrucción de Raydium en el IDL; anchor_cp_swap::cpi::accounts::Swap la aplica a través de sus tipos de campo.

Campo de programa Token-2022

Los mints de entrada y salida pueden estar bajo diferentes programas de token — uno SPL Token, uno Token-2022. El CPI tiene campos input_token_program y output_token_program separados por esta razón. Siempre verifica el campo owner de cada mint y enruta el programa correcto en cada ranura.

Transacciones versionadas

Una tx compuesta que hace 2+ CPIs de Raydium más una creación de ATA raramente cabe en una transacción heredada (v0-sin-LUT). Usa V0 con tablas de búsqueda de direcciones; extrae los LUTs públicos de Raydium a través de raydium.getRaydiumLutAddresses().

Punteros

Fuentes: