0. Revisión
1. Descripción General
En el blog anterior, presentamos cómo desplegar e interactuar con el programa. Además de invocar las instrucciones de un programa desde el lado del cliente, Solana también permite que los programas se llamen entre sí mediante un mecanismo llamado invocación entre programas. En esta publicación, ilustramos cómo se utiliza la invocación entre programas. El código de prueba está aquí.
2. Invocación Entre Programas
La invocación entre programas generalmente se implementa con la función invoke. En esta sección, ilustramos el uso de invoke transfiriendo lamports dentro de un programa.
En Solana, existe un programa nativo llamado System Program que desempeña un papel en la creación de nuevas cuentas y en la transferencia de lamports (SOL).
En este caso, para transferir lamports dentro de un programa (por ejemplo, Programa A), el programa A invocará la función transfer() en System Program. Veamos el ejemplo concreto a continuación.
2.1 Revisión del Código

Las líneas 3 a 10 importan las bibliotecas requeridas. Tenga en cuenta que la función invoke() se encuentra en la biblioteca solana_program::program.

En la función process_instruction, las líneas 22 a 30 extraen las tres cuentas pasadas por el cliente. Tenga en cuenta que los lamports se transferirán desde from_account hacia to_account. Desde la línea 33 hasta la línea 44, se invoca la función invoke(). Recibe dos argumentos. El primero es la instrucción invocada de destino y el segundo es un conjunto de cuentas.
En este ejemplo, la instrucción de destino es transfer(), que se utiliza para transferir lamports. Las cuentas incluyen todas las cuentas requeridas por la instrucción que se está invocando. En este caso, from_account y to_account.
El programa desplegado se puede encontrar en el siguiente enlace.
https://explorer.solana.com/address/EPaLuYQ4c11BJAe9ucLbta3xGFb17Zy3cZh3UDPbXRG9?cluster=devnet
2.2 Transferir los Lamports

Como se mencionó, enviamos la transacción al programa desplegado. El programa desplegado invocará además el System Program para transferir los lamports a la dirección de destino.
El código anterior muestra cómo el cliente construye las transacciones. Las líneas 93 a 101 extraen el programId del programa desplegado. Las líneas 103 a 109 generan la dirección del remitente, el receptor y el System Program. Las líneas 111 a 117 construyen la transacción. En la línea 118, la transacción se envía al clúster de Solana. Tenga en cuenta que el único firmante en esta transacción es el remitente, que se establece en la línea 112. El remitente debe autorizar (es decir, firmar) la transacción que toma dinero de su cuenta, y el receptor no tiene que hacerlo (línea 113).
La transacción se puede encontrar con el siguiente enlace.
https://explorer.solana.com/tx/4cxqnff8SakVcE9y5phmh6utcbBUGaLDPvqMRgSy9aPGdNVH6DCsQyJXCCzRGvW5CpygUi5pqhgBQxczXnWCoqPJ?cluster=devnet
3. Invoke o Invoke_signed
En Solana, los programas pueden generar cuentas en tiempo de ejecución, las cuales se denominan Direcciones Derivadas de Programa (PDA, por sus siglas en inglés). Si la instrucción de destino invocada por un programa contiene cuentas firmadas con PDA, se debe usar invoke_signed() en lugar de invoke(). Usaremos otro ejemplo para demostrar el uso de invoke_signed(). Ahora veamos primero el código del contrato.
3.1 Revisión del Código
En este ejemplo, creamos una PDA en tiempo de ejecución dentro de un programa (es decir, Programa B). Para crear una PDA, se debe invocar el System Program en el programa B.

Las líneas 3 a 10 importarán las bibliotecas requeridas. Tenga en cuenta que invoke_signed() se importa esta vez (línea 6).

Similar al ejemplo de la Sección 2, extraemos las cuentas requeridas (líneas 22 - 27), que son System Program y PDA. Luego usamos la función find_program_address() para generar la dirección de la PDA y la semilla utilizada para empujar esta PDA fuera de la curva ed25519 (líneas 29 - 30). Esto garantiza que la dirección no tenga una clave privada asociada. En este ejemplo, el cliente y el programa usan la misma semilla (es decir, 'You pass butter') para generar la PDA. Por lo tanto, la clave pública debería ser la misma, lo cual se verifica desde la línea 31 hasta la línea 34. Después de eso, invocamos invoke_signer para emitir la instrucción allocate() desde la línea 37 hasta la línea 47. A diferencia de la función invoke(), recibe un argumento adicional que son las semillas utilizadas para crear la PDA, así como la semilla de bump utilizada por la función find_program_address().
Para crear/asignar una cuenta, la propia cuenta debe proporcionar una firma, y en este ejemplo la PDA es la firmante. Para firmar la PDA, se usa invoke_signed. Específicamente, Solana utilizará las semillas y el programId del llamador (es decir, Programa B) para recrear la PDA y compararla con las cuentas dadas (el segundo argumento). Si son iguales, la PDA será firmada.
El programa desplegado se puede verificar a continuación.
https://explorer.solana.com/address/4h3RXGsouTRvUWNG1Dqq2tuuASTXFebHC3wFzv3tSFCK?cluster=devnet
3.2 Crear una PDA
Veamos el script del cliente para el segundo ejemplo.

En la línea 104, extraemos la PDA y la semilla de bump mediante la función findProgramAddress().
Desde la línea 112 hasta la línea 120, la transacción se creará y enviará. Tenga en cuenta que la propiedad (isSigner) de la PDA es 'false' (línea 113) ya que no puede ser firmada por el cliente. En tiempo de ejecución, el Programa B firmará la PDA e invocará la instrucción allocate en el programa System Program.
Puede encontrar la PDA asignada en la siguiente transacción.
https://explorer.solana.com/tx/5fzdfzbD4281pY1HJm37f1fxSEMGmWtQmbmFPzEiP9v6hWF8GRRZmfcMDvPJugTrs3npnCsaWUGvw15URyBhx3LS?cluster=devnet
3.3 ¿Podemos Usar Invoke()?
Para demostrar que invoke no se puede usar aquí, utilizamos otro programa (es decir, Programa C) para invocar la instrucción allocation.
El único cambio que realizamos es cambiar la función invoke_signed() por la función invoke(). Observamos que la cuenta no se puede crear.

El error muestra que la invocación entre programas necesita un firmante para ejecutarse correctamente. Es decir, se necesita la función invoke_signed().
4. Conclusión
En este artículo, presentamos cómo implementar la invocación entre programas mediante la función invoke(). También usamos diferentes ejemplos para ilustrar las diferencias entre invoke() e invoke_signed(). Permanezca atento, ya que se publicarán más artículos para esta serie.
Lea otros artículos de esta serie:
- Asegurar el Ecosistema de Solana (1) — Hola Solana
- Asegurar el Ecosistema de Solana (3) — Actualización de Programa
- Asegurar el Ecosistema de Solana (4) — Validación de Cuentas
- Asegurar el Ecosistema de Solana (5) — Multi-Sig
- Asegurar el Ecosistema de Solana (6) — Multi-Sig2
- Asegurar el Ecosistema de Solana (7) — Confusión de Tipos



