En este tutorial echaremos un vistazo entre bastidores de Anchor para ver cómo se despliega un programa de Solana.
Veamos el archivo de prueba que Anchor crea para nosotros cuando ejecutamos anchor init deploy_tutorial:
describe("deploy_tutorial", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.DeployTutorial as Program<DeployTutorial>;
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods.initialize().rpc();
console.log("Your transaction signature", tx);
});
});
El programa inicial que genera debería resultarte familiar:
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod deploy_tutorial {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
¿Dónde y cuándo se despliega el programa anterior?
El único lugar plausible donde el contrato podría haberse desplegado es en la línea del archivo de prueba:
const program = anchor.workspace.DeployTutorial as Program<DeployTutorial>;
Pero eso no tiene sentido, porque esperaríamos que fuera una función asíncrona.
Anchor está desplegando el programa silenciosamente en segundo plano.
Los programas de Solana no tienen constructores
Esto puede parecer inusual para quienes provienen de otros lenguajes orientados a objetos. Rust no tiene objetos ni clases.
En un contrato inteligente de Ethereum, un constructor puede configurar el almacenamiento o establecer el bytecode y las variables inmutables.
Entonces, ¿dónde está exactamente el “paso de despliegue”?
(Si todavía estás ejecutando el validador de Solana y los registros de Solana, te recomendamos que reinicies y limpies ambas terminales)
Hagamos la configuración habitual. Crea un nuevo proyecto de Anchor llamado program-deploy, y asegúrate de que el validador y los registros se estén ejecutando en otras terminales (shells).
En lugar de ejecutar anchor test, ejecuta el siguiente comando en la terminal:
anchor deploy

En la captura de pantalla de los registros anterior, podemos ver el punto en el que se desplegó el programa.
Ahora viene la parte interesante. Ejecuta anchor deploy de nuevo:

El programa se desplegó en la misma dirección, pero esta vez fue actualizado, no desplegado.
El program id no ha cambiado, el programa fue sobrescrito.
Los programas de Solana son mutables por defecto
Esto podría sorprender a los desarrolladores de Ethereum, donde se asume la inmutabilidad.
¿Qué sentido tiene un programa si el autor puede simplemente cambiarlo? Es posible hacer que un programa de Solana sea inmutable. La premisa es que el autor desplegará primero la versión mutable, y después de que pase el tiempo y no se descubran errores, entonces lo volverá a desplegar como inmutable.
Funcionalmente, esto no es diferente de un contrato proxy controlado por un administrador donde el propietario posteriormente renuncia a la propiedad enviándola a la dirección cero. Pero podría decirse que el modelo de Solana es mucho más limpio porque muchas cosas pueden salir mal con los proxies de Ethereum.
Otra implicación: Solana no tiene delegatecall, porque no lo necesita.
El uso principal de delegatecall en los contratos de Solidity es poder actualizar la funcionalidad de un contrato proxy emitiendo delegatecalls a un nuevo contrato de implementación. Sin embargo, dado que el bytecode de un programa en Solana se puede actualizar, no hay necesidad de hacer delegatecalling a contratos de implementación.
Otro corolario más: Solana no tiene variables inmutables de la forma en que Solidity las interpreta (variables que solo se pueden establecer en el constructor).
Ejecutando pruebas sin volver a desplegar el programa
Dado que Anchor vuelve a desplegar el programa por defecto, vamos a demostrar cómo ejecutar las pruebas sin volver a desplegar.
Modifica la prueba para que sea la siguiente:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import fs from 'fs'
let idl = JSON.parse(fs.readFileSync('target/idl/deployed.json', 'utf-8'))
describe("deployed", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
// Change this to be your programID
const programID = "6p29sM4hEK8ZFT5AvsGJQG5nKUtHBKs13iVL6juo5Uqj";
const program = new Program(idl, programID, anchor.getProvider());
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods.initialize().rpc();
console.log("Your transaction signature", tx);
});
});
Antes de ejecutar la prueba, te sugerimos limpiar la terminal de los registros de Solana y reiniciar el solana-test-validator.
Ahora, ejecuta la prueba con:
anchor test --skip-local-validator --skip-deploy
Ahora mira la terminal de registros:

Vemos que la instrucción initialize se ejecutó, pero el programa no fue desplegado ni actualizado, ya que usamos el argumento --skip-deploy con anchor test.
Ejercicio: Para ver que el bytecode del programa realmente cambió, despliega dos contratos que impriman diferentes valores con msg!. Específicamente,
- Actualiza la función
initializeen lib.rs para incluir una declaraciónmsg!que escriba una cadena de texto en los registros. anchor deployanchor test --skip-local-validator --skip-deploy- Revisa el registro para ver el mensaje registrado
- Repite los pasos del 1 al 4, pero cambia la cadena de texto en el
msg! - Verifica que el program id no haya cambiado
Deberías observar cambios en la cadena del mensaje, pero el program id sigue siendo el mismo.
Resumen
- Solana no tiene un constructor, los programas “simplemente se despliegan”
- Solana no tiene variables inmutables
- Solana no tiene delegatecall, los programas pueden “simplemente actualizarse”
Aprende más con RareSkills
Este tutorial es parte de nuestro curso de Solana gratuito.
Publicado originalmente el 12 de febrero de 2024