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 →
PDAs (direcciones derivadas de programas) y CPIs (invocaciones entre programas) son los dos primitivos que hacen posible Raydium. Los PDAs permiten que un programa “posea” direcciones deterministas sin claves privadas — así es como funcionan las autoridades de pools y las bóvedas. Los CPIs permiten que un programa llame a otro — así es como Raydium intercambia tokens a través del programa SPL Token y cómo los integradores componen Raydium en sus propios flujos. Ambos merecen ser entendidos antes de leer el código fuente de Raydium.

PDAs: direcciones sin claves

Una Dirección Derivada de Programa es una clave pública que:
  • No está en la curva ed25519 (no existe clave privada para ella).
  • Se deriva de forma determinista a partir de un ID de programa y un conjunto de seeds.
  • Solo puede ser firmada por el programa de derivación, mediante invoke_signed.
Cada autoridad de pool de Raydium, cada cuenta de estado de pool, cada bóveda, cada estado de granja — todos son PDAs.

Derivación

Un PDA se calcula hasheando el ID del programa con los seeds, luego encontrando un byte “bump” que fuerza el resultado fuera de la curva. El primer bump (típicamente comenzando desde 255 y decrementando) que produce una dirección fuera de la curva gana; este es el bump canónico.
Los seeds pueden ser cualquier cosa — strings, otras pubkeys, valores u64 como bytes little-endian. La convención de Raydium es un prefijo legible por humanos seguido de identificadores únicos.

Patrones de PDA en Raydium

PDAs comunes en los programas de Raydium: Los usuarios e integradores pueden calcular estos sin obtener nada — dados los inputs públicos (ID de pool, ID de granja, clave de usuario), el PDA es determinista.

Bump canónico

Aunque en principio puede haber múltiples bumps que produzcan direcciones fuera de la curva, los programas de Raydium siempre usan el bump canónico (encontrado decrementando desde 255). Este se almacena en los datos de la cuenta del PDA para que las transacciones posteriores puedan pasarlo y omitir el bucle de derivación (costoso):
(El PoolState de CLMM almacena bump: [u8; 1] en su lugar, así que verifica la struct del programa específico en lugar de asumir una forma.) En transacciones posteriores, el bump se lee del estado del pool en lugar de ser recomputado.

CPIs: llamar a otros programas

Invocación Entre Programas permite que un programa invoque las instrucciones de otro programa en línea dentro de una única transacción. Raydium usa CPIs extensamente:
  • Las instrucciones de swap llaman al programa SPL Token para mover tokens.
  • CLMM llama a Metaplex para acuñar el NFT de posición.
  • La creación de pool llama al Programa del Sistema para asignar cuentas.
  • Farm v6 llama a SPL Token para transferir recompensas.
Los integradores también usan CPIs para llamar hacia Raydium — así es como funcionan las estrategias de bóveda, los protocolos de LP apalancados y los auto-compounders. Ver integration-guides/cpi-integration.

invoke vs invoke_signed

El runtime de Solana ofrece dos primitivos de CPI:
  • invoke: llamar a otro programa; el programa llamado hereda los firmantes de la transacción externa.
  • invoke_signed: llamar a otro programa en nombre de un PDA; el runtime verifica los seeds del PDA y autoriza la firma.
invoke_signed es la magia que permite que los programas tengan autoridad sobre cuentas sin gestionar claves privadas.

Ejemplo: Raydium transfiriendo desde una bóveda de pool

Una bóveda de pool es una Cuenta de Token cuya autoridad es un PDA del programa de pool. Para transferir tokens durante un swap, el programa de pool debe firmar como ese PDA:
El runtime ve que invoke_signed es llamado por el programa CPMM, verifica que vault_and_lp_mint_auth_seed + bump se derive a la dirección de pool_authority cuando se hashea con el ID del programa CPMM, y permite la firma de autoridad en la transferencia de token. Sin clave privada involucrada.

Ejemplo: integrador llamando a Raydium CPMM

Un programa integrador (por ejemplo, un escrow) puede invocar swap_base_input de Raydium mediante CPI:
Este es el patrón de integración canónico — ver integration-guides/cpi-integration para el ejemplo completo de escrow.

Límite de profundidad de CPI

Solana limita la profundidad de CPI a 4 niveles. La instrucción de nivel superior de una transacción cuenta como profundidad 0; cada invocación de CPI incrementa la profundidad. Implicación práctica: el propio swap de Raydium ya usa 1-2 niveles de CPI (Raydium → SPL Token). Un integrador que llama a Raydium usa 2. Si ese integrador es llamado por otro integrador, es 3. El 4º nivel es el límite. La mayoría de las composiciones se mantienen por debajo de esto fácilmente, pero el anidamiento profundo (agregador → router → Raydium → hook) puede alcanzarlo. Diseña de forma plana en lugar de profunda.

Cuentas restantes

Cuando una instrucción de Raydium necesita un número variable de cuentas (por ejemplo, un swap de CLMM cruzando un número desconocido de arrays de tick), las cuentas adicionales se pasan como cuentas restantes — anexadas a la lista de cuentas fijas, interpretadas por posición. El SwapV2 de CPMM usa cuentas restantes para las cuentas adicionales requeridas de los programas de transfer-hook. Los clientes obtienen las cuentas necesarias y las anexan:
A nivel de CPI, los integradores deben reenviar las cuentas restantes a través de su propia instrucción:

Trampas de PDA

Seeds incorrectos → dirección incorrecta

Un bug donde los seeds están en el orden incorrecto, codificación incorrecta, o incluyen/excluyen un byte adicional produce silenciosamente un PDA diferente. La transacción falla de forma ambigua (el programa intenta leer una cuenta que no existe). Siempre prueba unitariamente la derivación de seeds contra valores dorados conocidos.

No almacenar bump

Si re-derives el bump en cada transacción, pagas compute por el bucle de derivación. Almacena el bump canónico en los datos del PDA y léelo desde allí.

Confundir bump canónico vs no canónico

Los bumps no canónicos (si alguien encuentra uno que produce fuera de la curva) son permitidos por invoke_signed pero rechazados por los programas de Raydium mediante assert_eq!(bump, canonical_bump). Si alguien intenta reclamar un PDA con un bump no canónico, la tx falla.

Pasar un PDA como firmante cuando no eres el programa propietario

Solo el programa cuyo ID está en la derivación del PDA puede invoke_signed con sus seeds. Si lo intentas, el runtime lo rechaza.

Trampas de CPI

Olvidar reenviar remaining_accounts

Si tu instrucción externa pasa cuentas de transfer-hook en remaining_accounts pero el CPI hacia Raydium no las reenvía, Raydium falla porque no puede encontrar las cuentas de hook. Siempre incluye with_remaining_accounts en CPIs que lo necesiten.

Desajuste de flags escribibles

Una cuenta que la instrucción externa marca como escribible también debe ser escribible en la llamada de CPI si el programa llamado intenta escribirla. Desajuste → rechazo del runtime.

No contabilizar renta

CPI a un programa que crea una cuenta (por ejemplo, creación de ATA) requiere que el pagador tenga suficiente SOL para renta. Las verificaciones de renta fallidas aparecen como errores oscuros.

Ejemplo trabajado: calculando PDAs de CPMM de Raydium

Esto es exactamente lo que hace el SDK de Raydium bajo el capó cuando llamas a getPoolInfoFromRpc({ poolId }) — deriva los PDAs asociados sin un viaje de ida y vuelta.

Referencias

Fuentes: