Saltar al contenido principal

1. Hola Mundo

Una vez que hayas configurado tu entorno de desarrollo, estás listo para crear tu primer contrato inteligente.

Crear un nuevo proyecto

Crear un nuevo proyecto usando el comando init para crear un proyecto soroban-hello-world.

stellar contract init soroban-hello-world

El comando init creará un proyecto de espacio de trabajo en Rust, usando la estructura recomendada para incluir contratos Soroban. Veamos la estructura del proyecto:

.
├── Cargo.lock
├── Cargo.toml
├── README.md
└── contracts
└── hello_world
├── Cargo.toml
└── src
├── lib.rs
└── test.rs

Cargo.toml

El archivo Cargo.toml en la raíz del proyecto está configurado como Espacio de Trabajo en Rust, lo que nos permite incluir múltiples contratos inteligentes en un solo proyecto.

Espacio de Trabajo en Rust

El archivo Cargo.toml establece los miembros del espacio de trabajo como todo el contenido del directorio contracts y establece la versión de la dependencia soroban-sdk del espacio de trabajo, incluyendo la característica testutils, que permitirá generar utilidades de prueba para llamar al contrato en las pruebas.

Cargo.toml
[workspace]
resolver = "2"
members = [
"contracts/*",
]

[workspace.dependencies]
soroban-sdk = "20.3.2"
información

Los testutils se habilitan automáticamente dentro de pruebas unitarias de Rust dentro del mismo crate que tu contrato. Si escribes pruebas desde otro crate, necesitarás requerir la característica testutils para esas pruebas y habilitar la característica testutils al ejecutar tus pruebas con cargo test --features testutils para poder usar esas utilidades de prueba.

Perfil release

Configurar el perfil release para optimizar la construcción del contrato es fundamental. Los contratos Soroban tienen un tamaño máximo de 64KB. Los programas en Rust, incluso los pequeños, sin estas configuraciones, casi siempre superan este tamaño.

El archivo Cargo.toml tiene el siguiente perfil de lanzamiento configurado.

[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true

Perfil release-with-logs

Configurar un perfil release-with-logs puede ser útil si necesitas construir un archivo .wasm que tenga habilitados los registros para imprimir registros de depuración al usar el stellar-cli. Nota que esto no es necesario para acceder a registros de depuración en pruebas o para usar un depurador paso a paso.

[profile.release-with-logs]
inherits = "release"
debug-assertions = true

Consulta el ejemplo de registro para más información sobre cómo registrar.

Directorio de contratos

El directorio contracts es donde vivirán los contratos Soroban, cada uno en su propio directorio. Ya hay un contrato hello_world allí para ayudarte a comenzar.

Archivo Cargo.toml específico del contrato

Cada contrato debería tener su propio archivo Cargo.toml, que depende del Cargo.toml de nivel superior del que acabamos de hablar.

Aquí es donde podemos especificar información del paquete específica del contrato.

contracts/hello_world/Cargo.toml
[package]
name = "hello-world"
version = "0.0.0"
edition = "2021"
publish = false

El crate-type está configurado como cdylib, que es necesario para construir contratos.

[lib]
crate-type = ["cdylib"]
doctest = false

También hemos incluido la dependencia soroban-sdk, configurada para usar la versión del Cargo.toml del espacio de trabajo.

[dependencies]
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }

Código fuente del contrato

Crear un contrato Soroban implica escribir código Rust en el archivo lib.rs del proyecto.

Todos los contratos deben comenzar con #![no_std] para asegurar que no se incluya la biblioteca estándar de Rust en la construcción. La biblioteca estándar de Rust es grande y no se adapta bien a ser desplegada en programas pequeños como los que se despliegan en blockchains.

#![no_std]

El contrato importa los tipos y macros que necesita del crate soroban-sdk.

use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};

Muchos de los tipos disponibles en programas típicos de Rust, como std::vec::Vec, no están disponibles, ya que no hay asignador y no hay memoria en el heap en los contratos Soroban. El soroban-sdk proporciona una variedad de tipos como Vec, Map, Bytes, BytesN, Symbol, que todos utilizan la memoria y las capacidades nativas del entorno Soroban. Los valores primitivos como u128, i128, u64, i64, u32, i32, y bool también se pueden usar. No se admiten flotantes y matemáticas de punto flotante.

Las entradas del contrato no deben ser referencias.

El atributo #[contract] designa la estructura Contract como el tipo al que se asocian las funciones del contrato. Esto implica que la estructura tendrá funciones de contrato implementadas para ella.

#[contract]
pub struct HelloContract;

Las funciones del contrato se definen dentro de un bloque impl para la estructura, que está anotado con #[contractimpl]. Es importante notar que las funciones del contrato deben tener nombres con una longitud máxima de 32 caracteres. Además, si una función se pretende invocar desde fuera del contrato, debe estar marcada con el modificador de visibilidad pub. Es común que el primer argumento de una función de contrato sea de tipo Env, permitiendo acceder a una copia del entorno Soroban, que es típicamente necesario para varias operaciones dentro del contrato.

#[contractimpl]
impl HelloContract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
}
}

Uniendo esas piezas, un contrato simple se ve así.

contracts/hello_world/src/lib.rs
#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};

#[contract]
pub struct HelloContract;

#[contractimpl]
impl HelloContract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
}
}

mod test;

Nota la línea mod test al final, esto le dirá a Rust que compile y ejecute el código de prueba, que veremos a continuación.

Pruebas unitarias del contrato

Escribir pruebas para contratos Soroban implica escribir código Rust usando las instalaciones de prueba y la cadena de herramientas que usarías para probar cualquier código Rust.

Dado nuestro HelloContract, una prueba simple se verá así.

#![cfg(test)]

use super::*;
use soroban_sdk::{symbol_short, vec, Env};

#[test]
fn test() {
let env = Env::default();
let contract_id = env.register_contract(None, HelloContract);
let client = HelloContractClient::new(&env, &contract_id);

let words = client.hello(&symbol_short!("Dev"));
assert_eq!(
words,
vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),]
);
}

En cualquier prueba, lo primero que siempre se requiere es un Env, que es el entorno Soroban en el que se ejecutará el contrato.

let env = Env::default();

El contrato se registra con el entorno usando el tipo de contrato. Los contratos pueden especificar un ID de contrato fijo como el primer argumento, o proporcionar None y se generará uno.

let contract_id = env.register_contract(None, Contract);

Todas las funciones públicas dentro de un bloque impl que está anotado con el atributo #[contractimpl] tienen una función correspondiente generada en un tipo de cliente generado. El tipo de cliente se nombrará igual que el tipo de contrato con Client añadido. Por ejemplo, en nuestro contrato, el tipo de contrato es HelloContract y el cliente se llama HelloContractClient.

let client = HelloContractClient::new(&env, &contract_id);
let words = client.hello(&symbol_short!("Dev"));

Los valores devueltos por las funciones pueden ser afirmados:

assert_eq!(
words,
vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),]
);

Ejecutar las pruebas

Ejecuta cargo test y observa cómo se ejecuta la prueba unitaria. Deberías ver la siguiente salida:

cargo test
running 1 test
test test::test ... ok

Intenta cambiar los valores en la prueba para ver cómo funciona.

nota

La primera vez que ejecutes las pruebas, es posible que veas la salida en el terminal de cargo compilando todas las dependencias antes de ejecutar las pruebas.

Construir el contrato

Para construir un contrato inteligente para desplegar o ejecutar, usa el comando stellar contract build.

stellar contract build
consejo

Si obtienes un error como no se puede encontrar el crate para 'core', significa que no instalaste el objetivo wasm32 durante el paso de configuración. Puedes hacerlo ejecutando rustup target add wasm32-unknown-unknown.

Este es un pequeño envoltorio alrededor de cargo build que establece el objetivo en wasm32-unknown-unknown y el perfil en release. Puedes pensarlo como un acceso directo para el siguiente comando:

cargo build --target wasm32-unknown-unknown --release

Un archivo .wasm se generará en el directorio target, en target/wasm32-unknown-unknown/release/hello_world.wasm. El archivo .wasm es el contrato construido.

El archivo .wasm contiene la lógica del contrato, así como las especificaciones / tipos de interfaz del contrato, que pueden ser importados a otros contratos que deseen llamarlo. Este es el único artefacto necesario para desplegar el contrato, compartir la interfaz con otros, o hacer pruebas de integración contra el contrato. Este es el único artefacto necesario para desplegar el contrato, compartir la interfaz con otros, o realizar pruebas de integración contra el contrato.

Optimizing Builds

Usa stellar contract optimize para minimizar aún más el tamaño del .wasm. Primero, reinstala stellar-cli con la característica opt:

cargo install --locked stellar-cli --features opt

Luego construye un archivo .wasm optimizado:

stellar contract optimize --wasm target/wasm32-unknown-unknown/release/hello_world.wasm

Esto optimizará y generará un nuevo archivo hello_world.optimized.wasm en la misma ubicación que el archivo de entrada .wasm.

consejo

Construir contratos optimizados solo es necesario al desplegar en una red con tarifas o al analizar y perfilar un contrato para hacerlo lo más pequeño posible. Si recién estás comenzando a escribir un contrato, estos pasos no son necesarios. Consulta Construir para obtener detalles sobre cómo construir para desarrollo.

Resumen

En esta sección, escribimos un contrato simple que se puede despliegar en una red Soroban.

A continuación, aprenderemos a desplegar el contrato HelloWorld en la red Testnet de Stellar e interactuar con él a través de RPC usando el CLI.