arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 45

Stellar en español

Loading...

Stellar

Loading...

Soroban

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Instalación

¿Qué es Stellar?

hashtag
¿Qué es la blockchain de Stellar?

La blockchain de Stellar es una plataforma descentralizada, lo que significa que no depende de un solo servidor o entidad. En cambio, una red de computadoras (o nodos) trabaja en conjunto para verificar y registrar todas las transacciones de forma transparente. Esto hace que cada operación sea muy rápida (generalmente en 2 a 5 segundos) y que las tarifas sean mínimas (¡prácticamente simbólicas!). ⚡️🔒

hashtag
Un poco de su historia

Stellar fue creada en 2014 por Jed McCaleb y Joyce Kim. Ambos ya tenían experiencia en el mundo de las criptomonedas (McCaleb, por ejemplo, estuvo involucrado en proyectos como Ripple y Mt. Gox). La idea era sencilla pero poderosa: crear una plataforma que facilitara el envío de dinero de forma global, especialmente para aquellos que no tienen acceso a los servicios bancarios tradicionales. Para lograrlo, se diseñó una red en la que se pudiera usar una moneda digital propia llamada Lumens (XLM), la cual se usa para pagar las pequeñas tarifas de transacción y para mantener el sistema en marcha. 🚀🌟

hashtag
¿Por qué es importante?

  • Inclusión financiera: Stellar busca que más personas en el mundo puedan acceder a servicios financieros sin tener que depender de bancos tradicionales.

  • Velocidad y bajo costo: Con Stellar, las transacciones internacionales son casi instantáneas y con comisiones muy bajas.

  • Transparencia y seguridad: Al ser una red descentralizada, todas las operaciones se registran de manera pública y segura, lo que reduce el riesgo de fraudes.

Inicio

¡Bienvenido a Stellar Español! 🚀✨

Este es tu nuevo punto de referencia en español para todo lo relacionado con Stellar. Diseñado por desarrolladores para desarrolladores, nuestro portal se centra inicialmente en los contratos inteligentes de Soroban y en cómo interactuar con ellos a través de interfaces web intuitivas. 💻🤖

hashtag
¿Qué encontrarás en Stellar Español?

  • Documentación de Soroban: Descubre guías paso a paso, ejemplos de código y recursos técnicos para crear y desplegar contratos inteligentes en Stellar con Soroban. ¡Aprende a sacarle el máximo provecho a esta tecnología revolucionaria! 📚🚀

  • Interfaz web para contratos: Explora cómo utilizar herramientas web diseñadas para interactuar de manera fácil y rápida con tus contratos inteligentes. Desde la gestión de cuentas hasta la ejecución de transacciones, te mostramos todo lo que necesitas saber. 🌐🖱️

  • Recursos en constante evolución: Nuestro portal se actualizará continuamente. A futuro, complementaremos esta información con fundamentos de Stellar, un blog con noticias y análisis, y muchos temas afines al universo Stellar. 🔄📈

Si deseas apoyar a esta iniciativa

https://buymeacoffee.com/stellarespanolarrow-up-right

Cliente Stellar

Referencia lenguaje Soroban

NVM

NVM (Node Version Manager) es una herramienta que permite instalar, gestionar y cambiar entre múltiples versiones de Node.js en un mismo sistema. Es útil para desarrolladores que trabajan en proyectos con diferentes versiones de Node.js, ya que facilita la instalación, actualización y cambio de versiones sin afectar otras configuraciones del sistema. Funciona en macOS, Linux y, con herramientas adicionales, en Windows.

hashtag
Instalación

hashtag
Mac y linux

Se ingresa a este link para ver las últimas versiones

Abrimos la terminal y ejecutamos lo siguiente:

hashtag
Windows

Nos dirigimos al siguiente link y bajamos la última versión

Node Js

Node.js es un entorno de ejecución de JavaScript basado en el motor V8 de Chrome, que permite desarrollar aplicaciones escalables del lado del servidor con un modelo asíncrono y no bloqueante. Utiliza un solo hilo con eventos para manejar múltiples conexiones de manera eficiente, es altamente escalable y cuenta con un extenso ecosistema de paquetes a través de npm. Es ideal para APIs, microservicios, aplicaciones en tiempo real y servidores web.

Para nosotros es relevante la instalación de nodejs para la interacción web con los contratos de la red Stellar

hashtag
Instalación

Nos dirijimos a para ver la última version LTS

Abrimos la terminal y ejecutamos:

Está es la última version el 12 de Junio de 2025

Fácil no? asi podemos instalar y gestionar varias versiones de Node js mucho más fácil 😊

Visual studio code

Visual studio code es el editor por excelencia para los desarrollos de contratos y programación en general

Descarga del Visual studio code

Ir al siguiente link https://code.visualstudio.com/Downloadarrow-up-right

hashtag
Instalación de plugins útiles

cross_contract

Interconectar Contratos

dapps

Contratos ejemplo

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updatingarrow-up-right
https://github.com/coreybutler/nvm-windows/releasesarrow-up-right
nvm install 22.16.0 
https://nodejs.org/es/downloadarrow-up-right

Contrato actualizable

Counter with Auth

#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
pub enum DataKey {
    Counter(Address),
}

#[contract]
pub struct IncrementContract;

#[contractimpl]
impl IncrementContract {
    /// Increment increments a counter for the user, and returns the value.
    pub fn increment(env: Env, user: Address, value: u32) -> (Address, u32) {
        // Requires user to have authorized call of the increment of this
        // contract with all the arguments passed to increment, i.e. user
        // and value. This will panic if auth fails for any reason.
        // When this is called, Soroban host performs the necessary
        // authentication, manages replay prevention and enforces the user's
        // authorization policies.
        // The contracts normally shouldn't worry about these details and just
        // write code in generic fashion using Address and require_auth (or
        // require_auth_for_args).
        user.require_auth();

        // This call is equilvalent to the above:
        // user.require_auth_for_args((&user, value).into_val(&env));

        // The following has less arguments but is equivalent in authorization
        // scope to the above calls (the user address doesn't have to be
        // included in args as it's guaranteed to be authenticated).
        // user.require_auth_for_args((value,).into_val(&env));

        // Construct a key for the data being stored. Use an enum to set the
        // contract up well for adding other types of data to be stored.
        let key = DataKey::Counter(user.clone());

        // Get the current count for the invoker.
        let mut count: u32 = env.storage().persistent().get(&key).unwrap_or_default();

        // Increment the count.
        count += value;

        // Save the count.
        env.storage().persistent().set(&key, &count);

        // Return the count to the caller.
        (user, count)
    }
}

Explicación del código

Timelock

Cliente Stellar

El cliente de Stellar es super importante en la escritura de contratos en Stellar, ya que se encarga de la creación de la estructura básica y archivos, su compilación, despliegue y pruebas respectivas.

hashtag
Intalación:

MAC

Se ejecuta el siguiente comando en la terminal

Adopcion de mascotas

Votaciones

hashtag
Linux

Vamos a la pagina de releases https://github.com/stellar/stellar-cli/releases/arrow-up-right

circle-info

busca el binario corresponidente a la arquitectura de tu ordenador [arch64 o x86_64]

Para saber la versión de Linux corremos el siguiente comando:

Descomprime el archivo

La versión 22.8.1 es la última versión que se revisó hasta el 2025/06/01

Crea un alias con el binario para que se ejecute en terminal con el comando stellar

Comprueba que se instaló correctamente


⚠️ Nota si se usa un buntu 20.04.6 e inferiores ejecutar las siguientes instrucciones

dentro del editor de texto agegar la siguiente linea de código

Se guarda los cambios (Ctrl+O) y cierra (Ctrl+X)

Ejecuta:


Windows

Para los diferentes sistemas operativos también se puede ir al repositorio de github y bajar de allí los últimos ejecutables

https://github.com/stellar/stellar-cli/releases/arrow-up-right

Una vez instalado en la terminal ejecutamos el comando stellar ( en la terminal).

Ejecución de prueba

Rust

Instalación de Rust en Mac/Linux y Windows

Rust es el lenguaje de propósito general en el cual se escriben los contratos para la red de Stellar a continuación un paso a paso para su instalación.

Para la instalación de rust, dirigirse a este enlace: https://www.rust-lang.org/es/tools/installarrow-up-right

hashtag
Instalación en macOS y Linux

hashtag
Paso 1: Abrir la Terminal

Abre tu aplicación de terminal (Terminal.app en macOS o la terminal de tu distribución Linux).

hashtag
Paso 2: Descargar e instalar Rust con rustup

Este comando descargará un script y te guiará por el proceso de instalación. Generalmente se recomienda usar la opción predeterminada para instalar la versión estable.

hashtag
Paso 3: Configurar el entorno

Una vez finalizada la instalación, cierra y vuelve a abrir la terminal o ejecuta:

Esto garantiza que el directorio de Cargo (donde se instalan las herramientas, por ejemplo, rustc, cargo y rustup) se añada a tu variable de entorno PATH.

hashtag
Paso 4: Verificar la instalación

Comprueba que Rust se instaló correctamente ejecutando:

hashtag
Paso 5: Instalar un compilador de C (si es necesario)

Rust requiere un enlazador para compilar correctamente:

  • En macOS: Instala las herramientas de línea de comandos de Xcode

  • En Linux (por ejemplo, Ubuntu): Asegúrate de tener instalado el paquete build-essential

  • Fedora

  • Arch

hashtag
Paso 6: Agregar lo siguiente a Rust, para poder generar archivos WebAssembly

Ejecutamos lo siguente para saver laversión de Rust:

Si el rust es anterior a la versión 1.85

Si el rust es igual o mayor a la versión 1.85

hashtag
Instalación en Windows

hashtag
Paso 1: Descargar el instalador

Visita la y descarga el instalador para Windows (rustup-init.exe). Elige la versión de 32 o 64 bits según tu sistema.

hashtag
Paso 2: Ejecutar el instalador

Ejecuta el archivo descargado y sigue las instrucciones en pantalla. Durante la instalación, es posible que se te pida instalar las Visual Studio C++ Build Tools (necesarias para compilar Rust en Windows). También debemos instalar el windows SDK acorde a la versión de windows que tengamos.

hashtag
Paso 3: Verificar la instalación

Abre una terminal (CMD o PowerShell) y ejecuta:

hashtag
Paso 4: Agregamos el paquete que genera el WebAsembly

Ejecutamos lo siguente para saver laversión de Rust:

Si el rust es anterior a la versión 1.85

Si el rust es igual o mayor a la versión 1.85

Segundos pasos

Una vez visto lo básico del cliente, miremos lo pertinente enfocado a los contratos inteligentes, como es:

  • Creación de un contratos.

  • Compilación de contratos.

  • Despliegue.

  • Ejecución de contrato.

Creación de un contrato Syntaxis: stellar contract init "nombre_contrato"

stellar contract init hola_mundo

Estructura y archivos creados

Dentro de lib.rs está el contrato por defecto

Compilación del contrato Se ejecuta el siguiente comando:

Podemos ver que se creo una carpeta llamada release, más internamente vemos que está creado el archivo hello_world.wasm

💡el nombre del archivos en web assembly es el que automáticamente se pone dentro del archivo Cargo.toml

Despliegue del contrato

Mac/linux

Windows

Felicidades, ya tienes el contratro deplegado en testnet 🥳

Interactuando con los contratos Sintaxis stellar contract invoke --id "contrato" --source "identidad" --network testnet -- "función" --"parametro" "dato del parámetro"

Linux y MAC

Windows

El resultado es:

Primeros pasos

El cliente de Stellar es super importante en la escritura de contratos en Stellar, ya que se encarga de la creación de la estructura básica y archivos, su compilación, despliegue y pruebas respectivas.

En esta primera pare veremos lo siguiente:

  • Creación de una cuenta (identidad)

  • Obtención de la llave pública (address)

helloworld

Clásico hello world autogenerado por el cliente de Stellar

Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

Como podemos observar el proyecto como tal se llama helloworld y en la carpeta contracts se creó una carpeta llamada hello-world, este contrato se genera por defecto cuando creamos un proyecto pero no indicamos como se llama el contrato como tal

Análisis del código generado

1 Declaración de No-Standard Library

brew install stellar-cli
lsb_release -a
# Caso de x86_64
wget https://github.com/stellar/stellar-cli/releases/download/v22.8.1/stellar-cli-22.8.1-x86_64-unknown-linux-gnu.tar.gz

# Caso de arch64

wget https://github.com/stellar/stellar-cli/releases/download/v22.8.1/stellar-cli-22.8.1-aarch64-unknown-linux-gnu.tar.gz
#caso x86_64
tar -xvf stellar-cli-22.8.1-x86_64-unknown-linux-gnu.tar.gz

# Caso de arch64
tar -xvf stellar-cli-22.8.1-aarch64-unknown-linux-gnu.tar.gz
sudo mv stellar /usr/local/bin/ && sudo chmod +x /usr/local/bin/stellar
stellar --version
sudo nano /etc/apt/sources.list.d/jammy.list
deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy main
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install build-essential libssl3 libstdc++6
winget install --id Stellar.StellarCLI 
stellar
página oficial de Rustarrow-up-right
Windows 11 SDK si tenemos Windows 11, Windows 10 SDK, si tenemos la versión 10 de Windows
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustc --version
xcode-select --install
sudo apt-get update
sudo apt-get install build-essential
sudo dnf update
sudo dnf install make automake gcc gcc-c++ kernel-devel
sudo pacman -Syu
sudo pacman -S base-devel
rustc --version
rustup target add wasm32-unknown-unknown 
rustup target add wasm32v1-none
rustc --version
rustc --version
rustup target add wasm32-unknown-unknown 
rustup target add wasm32v1-none

Consultar el saldo

  • Envio de lumens a otra cuenta

  • Creación de una cuenta (identidad)

    La identidad es un alias , que nos sirve para manejar la cuenta generada de una forma mucho más sencilla.

    Para este caso por ejemplo vamos a generar una identidad llamada developer , ya dejemos descansar a Bob y Alice 😅

    Para este caso generamos una identidad global llamada developer en la red de testnet.

    Ejecucuón de prueba

    Al abrir el archivo generado vemos que es un contenedor de una frase semilla.

    O con linea de comando

    Más información del comando

    Obtención de la llave pública (address)

    Para saber cual es la llave pública de una identidad corremos el siguiente comando:

    Obtenemos:

    Ejecución de prueba

    Consultar el saldo

    Hay 2 formas por web o por comando

    El modo sencillo, explorador web ingresamos la dirección en esta dirección https://stellar.expert/explorer/testnetarrow-up-right

    Acá vemos que la cuenta viene con 10.000 lumens, lo suficiente para seguir con los ejercicios

    Para los amantes de la consola. 😉

    1. Averiguamos en testnet cual es el id de XLM stellar contract id asset --asset native --network testnet obtenemos esta respuesta: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC

    2. Obtenemos la llave pública de la identidad developer stellar keys address developer obtenemos: GD45T2VRMYBSGRHLMVTS4QQZVXAM7WD6IYWKYRS7DFURRR2EKWCNGOAN

    3. Ejecutamos los siguiente: Sintaxis: stellar contract invoke --id "dirección XLM" --network testnet --source-account developer -- balance --id "llave pública developer"

    Escribimos lo siguiente:

    La respuesta es:

    Pasar fondos de una billetera a otra

    Vamos a pasar 100 XLM de la cuenta developer a developer1.

    Sintaxis:

    stellar contract invoke --id <asset_contract_ID> --networdk testnet --source-account developer -- transfer --to <developer1_ID> --from developer --amount 100

    Como es de observar no hemos creado la identidad developer1 por lo tanto ejecutamos: stellar keys generate --global developer1 --network testnet stellar keys address developer1 obtenemos la siguiente llave pública :

    GCETVOMJKZ5OPTBIWBADL2PT6DTL7VPZMS5P4MIAYSINZYH5IIZRE27U

    Ahora que tenemos todos los datos ejecutamos:

    Obtenemos lo siguiente:

    #![no_std]
    • Esta directiva le indica al compilador que el contrato se compilará sin la biblioteca estándar de Rust. Esto con el fin de hacer lo más liviano posible.

    2 Importación de Elementos del SDK

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

    • contract Es un macro atributo que se aplica a una estructura para marcarla como el contenedor del contrato. Esto permite que el entorno de Soroban reconozca el contrato y genere la metadata necesaria.

    • contractimpl Es otro macro atributo que se utiliza en el bloque impl para indicar que las funciones definidas en ese bloque serán exportadas y podrán ser invocadas desde el entorno de ejecución del contrato.

    • vec Es un macro propio de Soroban que crea un vector (colección dinámica) adaptado al entorno de contratos. Permite inicializar vectores de forma similar a vec! de Rust, pero considerando las particularidades del entorno.

    • Env Es una estructura que representa el entorno en el que se ejecuta el contrato. A través de ella se accede a funciones esenciales como el almacenamiento, emisión de eventos, logs, etc.

    • String Es una implementación propia del tipo cadena optimizada para entornos sin estándar (no_std) y adaptada a las limitaciones de ejecución de los contratos inteligentes en Soroban.

    • Vec Es el tipo vector (colección dinámica) proporcionado por Soroban, similar al Vec estándar de Rust pero diseñado para trabajar de forma segura y eficiente en contratos inteligentes.

    Definición del Contrato

    Implementación de las Funciones del Contrato

    • La anotación #[contractimpl] sobre el bloque impl Contract indica que las funciones definidas serán expuestas para que puedan ser llamadas por el entorno de Soroban.

    • Función hello:

    Firma:

    Recibe dos argumentos: env: Env: El entorno de ejecución, que se usa para acceder a funcionalidades propias del runtime de Soroban.

    to: String: Una cadena que se pasa como parámetro. Retorna un Vec, es decir, un vector de cadenas.

    Cuerpo de la función:

    Se usa el macro vec! propio de Soroban para crear un vector. Elementos del vector:

    1. &env: Se pasa el entorno para que el macro pueda asignar memoria o gestionar la colección en el contexto de Soroban.

    2. String::from_str(&env, "Hello"): Convierte el literal "Hello" en un objeto de tipo String propio de Soroban, usando el entorno env para la conversión.

    3. to: Se agrega la cadena que se recibió como argumento.

    4. Al no tener ";" al final es el valor a retornar que se especifica en la función

    Compilación:

    Despliegue:

    • Mac/Linux

    • Windows

    Pruebas del contrato

    Linux y MAC

    Windows

    Obtenemos lo siguiente:

    Estructuras de archivos generados
    archivo lib.rs dentro de la carpeta hello-world
    Estructura de archivos generada bajo la carpeta release

    Git

    hashtag
    🗂️ ¿Qué es un Repositorio de Código?

    Imagínate que estás escribiendo un libro con tus amigos. Necesitas un lugar donde guardar todas las páginas, ver quién escribió qué, y poder volver a versiones anteriores si algo sale mal. ¡Un repositorio de código es exactamente eso, pero para tu código! 📚✨

    Un repositorio (o "repo" como le dicen los cool 😎) es como una carpeta mágica que:

    • 💾 Guarda todo tu código de forma segura

    • 📝 Recuerda cada cambio que haces (¡como una máquina del tiempo!)

    • 👥 Permite que varios programadores trabajen juntos sin hacer un desastre

    • 🔄 Te deja volver atrás si rompes algo (¡todos lo hacemos!)

    hashtag
    🎯 ¿Para qué sirve exactamente?

    hashtag
    🕰️ Control de versiones

    Es como tener un "deshacer" súper poderoso. Cada vez que guardas cambios, Git crea una "foto" de tu proyecto. Si algo se rompe, ¡simplemente vuelves a una foto anterior!

    hashtag
    👥 Colaboración

    Imagina que tú y tus amigos están pintando un mural gigante. Sin organización, todos pintarían encima del trabajo de otros. ¡Un repositorio evita este caos digital!

    hashtag
    🛡️ Respaldo automático

    Tu código vive en la nube, así que aunque tu computadora se rompa, tu trabajo está seguro. ¡Es como tener un backup automático súper inteligente!

    hashtag
    📖 Documentación

    Puedes guardar instrucciones, notas y explicaciones junto con tu código. ¡Tu futuro yo te lo agradecerá!


    hashtag
    🌟 Las Plataformas Más Populares

    hashtag
    🐙 GitHub - El Rey de los Repos

    GitHub es como el Instagram de los programadores. Es donde vive el código más cool del mundo:

    • 🔥 Más de 100 millones de repositorios

    • 🆓 Gratis para proyectos públicos

    • 🤝 Perfecto para mostrar tu trabajo a empleadores

    ¿Por qué es genial para principiantes?

    • Interface súper amigable 😊

    • Toneladas de tutoriales y documentación

    • GitHub Pages (¡puedes hacer sitios web gratis!)

    hashtag
    🦊 GitLab - El Todoterreno

    GitLab es como el Swiss Army knife del desarrollo:

    • 🛠️ No solo guarda código, también lo prueba y despliega

    • 🏢 Perfecto para empresas

    • 🔧 Herramientas integradas de CI/CD (no te preocupes, lo aprenderás después)

    hashtag
    🪣 GitBucket - El Independiente

    GitBucket es como el rebelde cool:

    • 🆓 100% gratis y de código abierto

    • 🏠 Lo instalas en tu propio servidor

    • 🎮 Perfecto para aprender sin depender de otros


    hashtag
    💻 Git en Consola - Tu Nueva Herramienta Favorita

    Git es como el motor que hace funcionar todos estos repositorios. La consola (terminal) es donde ocurre la magia real. ¡No te asustes, es más fácil de lo que parece! 🪄

    hashtag
    📥 ¿Cómo instalar Git?

    🍎 Para Mac (macOS)

    Siguiendo las instrucciones de:

    Opción 1:

    Si tienes instalado brew, se corre la instrucción en consola

    Opción 2:

    Si tienes instalado MacPorts, se corre la instrucción en consola

    hashtag
    🐧 Para Linux

    Ubuntu/Debian (Los más comunes) 📦

    Fedora/Red Hat 🎩

    Arch Linux (Para los aventureros) 🏔️

    hashtag
    🪟 Para Windows

    1. 📥 Ve a

    2. 🖱️ Descarga el instalador


    hashtag
    🎉 ¡Tu Primer Paso!

    Una vez instalado, abre tu terminal y escribe:

    Si ves algo como git version 2.x.x, ¡felicidades! 🎊 Git está listo para usar.

    hashtag
    ⚙️ Configuración inicial (Súper importante)

    Dile a Git quién eres:

    ¡Y listo! Ya estás preparado para comenzar tu aventura en el mundo de Git y los repositorios. 🚀

    get set helloworld

    Contrato típico donde podemos poner y extraer un mensaje

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado por defecto y lo sustituimos por el siguiente código

    hashtag
    Análisis del código:

    Importaciones del SDK de Soroban

    • Symbol y symbol_short: Se usan para trabajar con identificadores cortos y optimizados en el entorno de Soroban.

      • Symbol Es un string de 32 caracteres, viene ocupa 64 bit (a-z A-Z 0-9)

      • symbol_short! Es un string de 9 caracteres (a-z A-Z 0- 9)

    • String: Es una versión adaptada del tipo cadena de texto para el entorno sin estándar (no_std) y específicamente optimizada para contratos inteligentes en Soroban.

    Definición de una Constante const MESSAGE: Symbol = symbol_short!("Message");

    hashtag
    🛠 Explicación de las funciones

    Todas las funciones trabajan con env: Env, que proporciona acceso al almacenamiento y otras funcionalidades del entorno Soroban.

    hashtag
    1️⃣ set_message(env: Env, message: String)

    ✅ Guarda un mensaje en el almacenamiento.

    📌 Paso a paso:

    1. Toma como entrada un mensaje de tipo String.

    2. Usa env.storage().instance().set() para almacenar el mensaje en la blockchain usando la clave MESSAGE.


    hashtag
    2️⃣ get_message(env: Env) -> String

    ✅ Obtiene el mensaje almacenado.

    📌 Paso a paso:

    1. Intenta recuperar el valor asociado a MESSAGE en el almacenamiento.

    2. Si la clave no existe, usa .unwrap_or() para devolver un mensaje por defecto: "Default Message".

    3. Devuelve el mensaje almacenado o el mensaje por defecto.


    hashtag
    📌 Resumen

    Este contrato inteligente permite almacenar y recuperar un mensaje de la blockchain de Soroban:

    • set_message(): Guarda un mensaje en la blockchain.

    • get_message(): Obtiene el mensaje guardado o un mensaje por defecto si aún no se ha establecido.

    Compilación del contrato la compilación y creación del webassembly es con el siguiente comando: stellar contract build

    Despliegue del contratro

    Para Mac y Linux el salto de línea es con el caractér " \" y en Windows con el caracter " ´ "

    Reemplaze el simbolo * por el respectivo caractér de salto de linea a su sistema operativo.

    Pruebas del contratro

    Para linux y Mac el salto de línea de la instrucción es con el caracter " \ " para Windows con el caracter " ` "

    Invocación de la función set_message

    Invocación de la función get_message

    Pruebas

    hashtag
    🧪 Guía Completa de Testing en Stellar Soroban

    hashtag
    🔍 ¿Por qué necesitamos tests? (detallado)

    🛡️ Prevención de bugs críticos: detecta errores en lógica de negocio antes de deployment que podrían resultar en pérdida de fondos o estados inconsistentes. 🎯 Validación de edge cases: simula condiciones extremas (overflow, underflow, fondos insuficientes) para asegurar comportamiento robusto. 💰 Protección de inversión: evita costos de re-deployment y pérdida de confianza por bugs en mainnet. 🔄 Refactoring seguro: permite cambiar implementaciones manteniendo garantías de comportamiento. 🧪 Simulación de ataques: prueba vectores de ataque conocidos (reentrancy, front-running, privilege escalation) de forma controlada.

    hashtag
    🌟 ¿Qué ofrece Soroban para testing? (explicación técnica)

    📦 soroban_sdk::testutils: módulo con herramientas para crear entornos de prueba aislados y mockear comportamientos. 🏗️ Env::default(): crea un entorno de testing limpio con ledger simulado, storage independiente y control total sobre el tiempo. 👤 MockAuth: permite simular firmas y autorizaciones sin claves privadas reales. 📊 Budget tracking: monitorea consumo de CPU y memoria durante tests para optimizar gas costs. 🎭 Address generation: crea direcciones determinísticas para tests reproducibles.

    hashtag
    🔧 Herramientas y conceptos clave (más allá de la sintaxis)

    hashtag
    Env::default() — qué hace y efectos

    • Crea un entorno aislado donde cada test ejecuta independientemente

    • Simula un ledger con timestamp, sequence numbers y storage limpio

    • Permite control sobre authorizaciones y mock data

    hashtag
    MockAuth patterns

    • MockAuth::authorize(): simula que una dirección específica autorizó la transacción

    • MockAuthInvoke: para casos complejos con múltiples contratos

    • Cuidado: solo para testing; en mainnet require_auth() usa firmas reales

    hashtag
    Testing strategies

    • Unit tests: funciones individuales con inputs específicos

    • Integration tests: flujos completos entre múltiples funciones

    • Fuzzing: inputs aleatorios para encontrar casos no contemplados

    hashtag
    Error handling validation

    • Usa should_panic o assert!(result.is_err()) para validar fallos esperados

    • Verifica que error codes específicos se generen correctamente

    • Simula condiciones de falla (fondos insuficientes, permisos incorrectos)

    hashtag
    📋 Estrategias y patrones prácticos (con por qué y cuándo usarlas)

    hashtag
    Arrange-Act-Assert pattern

    • Arrange: configura estado inicial (addresses, balances, roles)

    • Act: ejecuta la función bajo prueba

    • Assert: verifica resultados esperados

    hashtag
    Test data management

    • Usa direcciones determinísticas para reproducibilidad

    • Crea helpers para setup común (inicializar contratos, crear usuarios)

    • Mantén tests independientes — no dependas de estado previo

    hashtag
    Mocking strategies

    • Mock external contracts durante unit tests

    • Simula condiciones de red (timeouts, failures) en integration tests

    • Usa MockAuth para probar flujos sin manejar claves privadas

    hashtag
    Performance testing

    • Mide budget consumption para optimizar gas costs

    • Prueba límites de storage y memoria

    • Valida que operaciones batch no excedan límites de transacción

    hashtag
    💡 Ejemplos Prácticos — Código + explicaciones detalladas (línea a línea)

    En proceso...

    Freighter wallet

    🚀 ¿Qué es una Cripto Wallet? Una cripto wallet (billetera de criptomonedas) es como tu billetera digital personal para guardar, enviar y recibir criptomonedas. A diferencia de una billetera física qu

    hashtag
    🚀 ¿Qué es una Cripto Wallet?

    Una cripto wallet (billetera de criptomonedas) es como tu billetera digital personal para guardar, enviar y recibir criptomonedas. A diferencia de una billetera física que guarda dinero en efectivo, una cripto wallet almacena las claves privadas que te dan acceso y control sobre tus activos digitales en la blockchain.

    stellar keys generate --global developer --network testnet --fund
    cat /home/<USUARIO>/.config/stellar/identity/developer.toml
    stellar keys address developer
    stellar contract invoke --id CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC 
    --network testnet --source-account developer 
    -- balance --id GD45T2VRMYBSGRHLMVTS4QQZVXAM7WD6IYWKYRS7DFURRR2EKWCNGOAN
    stellar contract invoke --id CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC 
    --network testnet --source-account developer -- transfer --from developer --to GD52UMJ54UXIGEGYT6GYRM4FAVATV7JD3RGEX6QGTV5HY66G5Q4T6RUS --amount 1000000000
    stellar contract init helloworld
    //#[contract]
    pub struct Contract;
    #[contractimpl]
    impl Contract {
        pub fn hello(env: Env, to: String) -> Vec<String> {
            vec![&env, String::from_str(&env, "Hello"), to]
        }
    }
    pub fn hello(env: Env, to: String) -> Vec
    vec![&env, String::from_str(&env, "Hello"), to]
    stellar contract build
    stellar contract deploy \
      --wasm target/wasm32-unknown-unknown/release/hello_world.wasm \
      --source developer \
      --network testnet \
      --alias hello_world
    stellar contract deploy `
      --wasm target/wasm32-unknown-unknown/release/hello_world.wasm `
      --source developer `
      --network testnet `
      --alias hello_world
    stellar contract invoke \
    --id CD5VLB73SUDKSXGWA5B3U52V4PUS7BKNILRN5CRNCWBDX4PAC44OBUVK \
    --source developer \
    --network testnet \
    -- \
    hello \
    --to Pascual
    stellar contract invoke `
    --id CD5VLB73SUDKSXGWA5B3U52V4PUS7BKNILRN5CRNCWBDX4PAC44OBUVK `
    --source developer `
    --network testnet `
    -- `
    hello `
    --to Manolo
    #![no_std]
    use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};
    
    #[contract]
    pub struct Contract;
    
    #[contractimpl]
    impl Contract {
        pub fn hello(env: Env, to: String) -> Vec<String> {
            vec![&env, String::from_str(&env, "Hello"), to]
        }
    }
    
    mod test;
    stellar contract build
    stellar contract deploy \
    --wasm /<RUTA_DE_LA_CARPETA>/target/wasm32-unknown-unknown/release/hello_world.wasm \
    --source developer \
    --network testnet \
    --alias hello_world
    stellar contract deploy `
    --wasm /<RUTA_DE_LA_CARPETA>/target/wasm32-unknown-unknown/release/hello_world.wasm `
    --source developer `
    --network testnet `
    --alias hello_world
    stellar contract invoke \
    --id CDCIWIAOWCDIORMZWV53VY5DVLOGB5PWG4ETSAEKKHX43RZVSPYUSLRS \
    --source developer \
    --network testnet \
    -- \
    hello \
    --to Pascual
    stellar contract invoke `
    --id CDCIWIAOWCDIORMZWV53VY5DVLOGB5PWG4ETSAEKKHX43RZVSPYUSLRS `
    --source developer `
    --network testnet `
    -- `
    hello `
    --to Manolo
    stellar contract init get_set_helloworld --name get_set_message
    #![no_std]
    use soroban_sdk::{contract, contractimpl, Env, Symbol, symbol_short, String};
    
    const MESSAGE: Symbol = symbol_short!("Message");
    
    #[contract]
    pub struct MessageContract;
    
    #[contractimpl]
    impl MessageContract {
    
        pub fn set_message(env: Env, message: String) {
                    env.storage().instance().set(&MESSAGE, &message)
        }
    
        pub fn get_message(env: Env) -> String {
            env.storage().instance().get(&MESSAGE)
                .unwrap_or(String::from_str(&env, "Default Message"))
        }
    
    }
    use soroban_sdk::{contract, contractimpl, Env, Symbol, symbol_short, String};
    Úsalo siempre al inicio de cada función de test
    Attack simulation: reproduce vectores de ataque conocidos
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/get_set_message.wasm *
      --source developer *
      --network testnet *
      --alias get_set_message
    stellar contract invoke `
      --id CCSZAYLAUZ6FR656ORQMQJOOY3X6Y4BLRHDI3DW4YYWCIYUYWFOYQLB4 `
      --source developer `
      --network testnet `
      -- `
      set_message `
      --message "Hey master developer 😉"
    stellar contract invoke `
      --id CCSZAYLAUZ6FR656ORQMQJOOY3X6Y4BLRHDI3DW4YYWCIYUYWFOYQLB4 `
      --source developer `
      --network testnet `
      -- `
      get_message 
    🌍 Hogar de proyectos famosos como Linux, React, y Python
    🏠 Puedes instalarlo en tu propio servidor
    🔒 Control total de tu código
    https://git-scm.com/downloads/macarrow-up-right
    git-scm.com/download/winarrow-up-right
    hashtag
    🔑 Conceptos Clave

    Las wallets NO guardan las criptomonedas físicamente - tus monedas siempre están en la blockchain. Lo que realmente guarda tu wallet son:

    • 🔐 Clave privada: Tu "contraseña maestra" que demuestra que eres el dueño

    • 🆔 Clave pública: Tu "dirección" donde otros pueden enviarte criptomonedas

    • 📊 Historial de transacciones: Registro de todas tus operaciones


    hashtag
    🌐 Wallets de Navegador (Browser Wallets)

    Las wallets de navegador son extensiones que se instalan directamente en tu navegador web (Chrome, Firefox, Edge, etc.). Son muy populares porque:

    hashtag
    ✅ Ventajas:

    • 🚀 Fácil acceso: Siempre disponibles mientras navegas

    • 🔗 Conexión directa: Se conectan automáticamente con aplicaciones web (DeFi, NFTs, etc.)

    • 💻 Interfaz familiar: Funcionan como cualquier extensión de navegador

    • ⚡ Transacciones rápidas: Confirmas operaciones con un clic

    hashtag
    ⚠️ Consideraciones:

    • 🔒 Dependen de la seguridad de tu navegador

    • 💻 Solo funcionan en el dispositivo donde están instaladas

    • 🌐 Requieren conexión a internet


    hashtag
    🌟 Freighter Wallet: Tu Puerta de Entrada a Stellar

    Freighter es la wallet de navegador más popular y confiable para la red Stellar ⭐. Desarrollada por el Stellar Development Foundation (SDF), es la herramienta oficial recomendada para interactuar con el ecosistema Stellar.

    hashtag
    🎯 ¿Qué es Stellar?

    Stellar es una blockchain ultrarrápida y económica, diseñada especialmente para:

    • 💸 Pagos internacionales: Transferencias casi instantáneas y con comisiones de centavos

    • 🏦 Servicios financieros: DeFi, préstamos, intercambios

    • 🌍 Inclusión financiera: Acceso a servicios financieros para todos

    • 🪙 Activos digitales: Creación y gestión de tokens personalizados

    hashtag
    🛠️ Características de Freighter:

    • ⭐ Nativa de Stellar: Optimizada específicamente para la red Stellar

    • 🔐 Seguridad avanzada: Tus claves privadas nunca salen de tu dispositivo

    • 🎨 Interfaz intuitiva: Diseño limpio y fácil de usar

    • 🔄 Gestión completa: Envía, recibe y gestiona XLM y otros activos Stellar

    • 🌐 Conexión DApps: Interactúa con aplicaciones descentralizadas en Stellar


    hashtag
    📥 Cómo Instalar Freighter Wallet

    hashtag
    Paso 1: Acceder a la Tienda de Extensiones 🛒

    Para Chrome/Edge/Brave:

    1. Ve a Chrome Web Store : https://chromewebstore.google.com/arrow-up-right

    2. Busca "Freighter Wallet"

    3. Asegúrate de que sea la oficial (desarrollada por Stellar Development Foundation)

    Para Firefox:

    1. Ve a Firefox Add-ons: https://addons.mozilla.org/es-ES/firefox/arrow-up-right

    2. Busca "Freighter Wallet"

    hashtag
    Paso 2: Instalación 🔽

    1. 🎯 Haz clic en "Añadir a Chrome" (o tu navegador)

    2. ✅ Confirma en "Añadir extensión"

    3. 📌 La extensión se instalará automáticamente

    4. 🔍 Verás el ícono de Freighter en tu barra de herramientas

    hashtag
    Paso 3: Configuración Inicial ⚙️

    1. 🚀 Haz clic en el ícono de Freighter para abrirla

    2. 🆕 Selecciona "Create new wallet" (Crear nueva wallet)

    3. 🔐 Crea una contraseña segura para proteger tu wallet localmente

    4. 📝 ¡MUY IMPORTANTE! Guarda tu frase de recuperación (seed phrase) de 12 palabras:

      • ✍️ Escríbela en papel

      • 🔒 Guárdala en un lugar seguro

      • ❌ NUNCA la compartas con nadie

    hashtag
    Paso 4: ¡Listo para Usar! 🎉

    Una vez configurada, podrás:

    • 👀 Ver tu dirección pública para recibir XLM

    • 💰 Consultar tu balance de XLM y otros activos Stellar

    • 📤 Enviar transacciones a otras direcciones

    • 🔗 Conectarte a DApps del ecosistema Stellar


    hashtag
    🛡️ Consejos de Seguridad Importantes

    hashtag
    🔒 Protege tu Frase de Recuperación:

    • ✍️ Escríbela a mano, nunca digitalmente

    • 📦 Guárdala en múltiples lugares seguros

    • ❌ Nunca la compartas ni la ingreses en sitios web sospechosos

    • 🚫 Ningún soporte técnico legítimo te la pedirá

    hashtag
    🌐 Navegación Segura:

    • 🔍 Siempre verifica las URLs de las DApps

    • 🛡️ Solo conecta tu wallet a sitios confiables

    • 📖 Lee cuidadosamente antes de firmar transacciones


    hashtag
    🌟 ¿Por qué Elegir Stellar y Freighter?

    hashtag
    Ventajas de Stellar:

    • ⚡ Transacciones súper rápidas: 3-5 segundos

    • 💵 Comisiones mínimas: Menos de $0.01 por transacción

    • 🌍 Alcance global: Diseñada para pagos internacionales

    • 🏗️ Ecosistema creciente: Cada vez más DApps y servicios

    hashtag
    Ventajas de Freighter:

    • 🏆 Oficial: Respaldada por Stellar Development Foundation

    • 🔄 Actualizaciones constantes: Siempre compatible con las últimas funcionalidades

    • 🆓 Completamente gratuita: Sin costos ocultos

    • 👥 Comunidad activa: Soporte y documentación excelente


    ¡Freighter Wallet es tu mejor compañera para explorar el increíble mundo de Stellar! 🚀⭐ Con transacciones ultrarrápidas y económicas, estarás listo para aprovechar todo el potencial de esta innovadora blockchain.

    Tipos de datos primitivos

    Tipos de datos que no necesitan ninguna importación, se manejan primitivamente en el contrato

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    hashtag
    📌 Explicación general

    • Usa #![no_std], lo que indica que no usa la biblioteca estándar de Rust.

    • Importa módulos de soroban_sdk, necesarios para ejecutar el contrato en Soroban.

    • Define un contrato llamado DataTypesContract.

    • Implementa varias funciones que realizan suma de números con diferentes tipos (u32, i32, u64, i64, u128, i128).

    • Incluye una función para negar un booleano.


    hashtag
    🛠 Explicación de las funciones

    Todas las funciones reciben _env: Env, aunque no lo utilizan directamente. Es un requisito en Soroban para la ejecución de contratos.

    hashtag
    1️⃣ Funciones de suma

    Cada una de estas funciones suma dos números del mismo tipo y retorna el resultado.

    📌 Ejemplo de uso: Si llamamos a add_u32(5, 10), devuelve 15. Si llamamos a add_i64(-20, 50), devuelve 30.


    hashtag
    2️⃣ negate_bool(_env: Env, flag: bool) -> bool

    ✅ Invierte un valor booleano (true ↔ false).

    📌 Paso a paso:

    1. Recibe un valor booleano (flag).

    2. Usa el operador lógico ! para invertirlo.

    3. Retorna el valor opuesto.

    📌 Ejemplo de uso: Si llamamos negate_bool(true), devuelve false. Si llamamos negate_bool(false), devuelve true.


    📌 Resumen

    Este contrato define funciones matemáticas y lógicas en Soroban:

    • Funciones de suma (add_u32, add_i32, add_u64, add_i64, add_u128, add_i128): Permiten sumar números de diferentes tamaños y signos.

    • Función booleana

    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Función add_u32

    Función negate_bool

    counter

    Ejemplo de un contador

    Para crear el contrato ejecutamos lo siguiente:

    Dentro de la carpeta counter, en el archivo lib.rs borramos todo el código y ponemos el siguiente código:

    Básicamente es un contrato con un contador y 3 funciones:

    hashtag
    Análisis del código:

    Importaciones del SDK de Soroban

    • Symbol y symbol_short: Se usan para trabajar con identificadores cortos y optimizados en el entorno de Soroban.

      • Symbol Es un string de 32 caracteres, viene ocupa 64 bit (a-z A-Z 0-9)

      • symbol_short! Es un string de 9 caracteres (a-z A-Z 0- 9)

    hashtag
    🛠 Explicación de las funciones

    Todas las funciones trabajan con env: Env, que proporciona acceso al almacenamiento y otras funcionalidades del entorno Soroban.

    hashtag
    1️⃣ add_to_counter(env: Env, increment: u32)

    ✅ Suma un número arbitrario al contador.

    📌 Paso a paso:

    1. Recupera el valor actual de COUNTER desde el almacenamiento, usando .unwrap_or(0), lo que garantiza que si la clave no existe, se toma como 0.

    2. Suma el increment recibido como parámetro.


    hashtag
    2️⃣ inc_counter(env: Env)

    ✅ Incrementa el contador en 1. 📌 Paso a paso: Esta función es similar a add_to_counter, pero en lugar de recibir un parámetro, simplemente suma 1 al contador.


    hashtag
    3️⃣ get_counter(env: Env) -> u32

    ✅ Devuelve el valor actual del contador.

    📌 Paso a paso:

    1. Recupera el valor de COUNTER del almacenamiento.

    2. Si la clave no existe, devuelve 0.

    3. Retorna el valor del contador.


    hashtag
    📌 Resumen

    Este contrato mantiene un contador en la blockchain de Soroban con:

    • add_to_counter(): Incrementa el contador en un valor específico.

    • inc_counter(): Incrementa el contador en 1.

    • get_counter(): Obtiene el valor actual del contador.

    Compilación del contrato:

    Despliegue del contratro

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Pruebas del contratro

    Para linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Invocación de la función add_to_counter

    Invocación de la función inc_counter

    Invocación de la función get_counter

    String

    Tipo de dato string que viene dentro de la libreria soroban

    Los strings pueden pasarse a contratos y almacenes utilizando el tipo Bytes.

    Tenga en cuenta que los bytes contenidos en los strings no se ajustan necesariamente a ninguna codificación de texto estándar, como ASCII o Unicode UTF-8. Son bytes sin interpretar. Se trata de bytes sin interpretar, y los usuarios que esperen una codificación determinada deberán aplicarla manualmente.

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    hashtag

    brew install git
    sudo port install git
    sudo apt updatesudo apt install git
    sudo dnf install git
    sudo pacman -S git
    git --version
    git config --global user.name "Tu Nombre Genial"
    git config --global user.email "[email protected]"
    stellar contract init  primitivedata --name primitivedata
    #![no_std]
    use soroban_sdk::{contract, contractimpl, Env};
    
    #[contract]
    pub struct DataTypesContract;
    #[contractimpl]
    impl DataTypesContract {
        // Suma dos números de 32 bits sin signo (u32).
        pub fn add_u32(_env: Env, a: u32, b: u32) -> u32 {
            a + b
        }
        // Suma dos números de 32 bits con signo (i32).
        pub fn add_i32(_env: Env, a: i32, b: i32) -> i32 {
            a + b
        }
        // Suma dos números de 64 bits sin signo (u64).
        pub fn add_u64(_env: Env, a: u64, b: u64) -> u64 {
            a + b
        }
        // Suma dos números de 64 bits sin signo (u64).
        pub fn add_i64(_env: Env, a: u64, b: u64) -> u64 {
            a + b
        }
        // Suma dos números de 128 bits sin signo (u128).
        pub fn add_u128(_env: Env, a: u128, b: u128) -> u128 {
            a + b
        }
        // Suma dos números de 128 bits con signo (i128).
        pub fn add_i128(_env: Env, a: i128, b: i128) -> i128 {
            a + b
        }
        // Invierte un valor booleano.
        pub fn negate_bool(_env: Env, flag: bool) -> bool {
            !flag
        }
    
    }
    
    stellar contract init  counter --name counter
    #![no_std]
    use soroban_sdk::{contract, contractimpl, Env, Symbol, symbol_short, log};
    
    const COUNTER: Symbol = symbol_short!("COUNTER");
    #[contract]
    pub struct CounterContract;
    
    #[contractimpl]
    impl CounterContract {
        
        pub fn add_to_counter(env: Env, increment: u32) {
            let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
            log!(&env, "current count: {}", count);
            count += increment;
            env.storage().instance().set(&COUNTER, &count);
            env.storage().instance().extend_ttl(50, 100);
        }
    
        pub fn inc_counter(env: Env)  {
            let mut count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
            count += 1;
            env.storage().instance().set(&COUNTER, &count);
            env.storage().instance().extend_ttl(50, 100);
        }
    
        pub fn get_counter(env: Env) -> u32 {
            let count: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
            count
        }
    }
    
    add_to_counter: Adiciona un numero al contador.
    inc_counter: Incrementa en 1 el contador.
    get_counter: Obtiene el contador actual.
    🆘 Es la única forma de recuperar tu wallet si pierdes acceso

    u128 (sin signo, 128 bits)

    0 a 340,282,366,920,938,463,463,374,607,431,768,211,455

    add_i128

    i128 (con signo, 128 bits)

    -170,141,183,460,469,231,731,687,303,715,884,105,728 a 170,141,183,460,469,231,731,687,303,715,884,105,727

    (
    negate_bool
    ): Invierte el valor de un booleano.

    Función

    Tipo de datos

    Rango de valores

    add_u32

    u32 (sin signo, 32 bits)

    0 a 4,294,967,295

    add_i32

    i32 (con signo, 32 bits)

    -2,147,483,648 a 2,147,483,647

    add_u64

    u64 (sin signo, 64 bits)

    0 a 18,446,744,073,709,551,615

    add_i64

    i64 (con signo, 64 bits)

    -9,223,372,036,854,775,808 a 9,223,372,036,854,775,807

    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba

    add_u128

    log: Muy parecido al log de javascript, se imprime por consola mensajes y contenido de variables

    Guarda el nuevo valor en el almacenamiento.
  • Extiende el Time-To-Live (TTL) de la clave en el almacenamiento (de 50 a 100 bloques).

  • Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/primitivedata.wasm *
      --source <Identity> *
      --network testnet *
      --alias primitivedata
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    add_u32 *
    --a 123 *
    --b 123
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <identity> *
    --network testnet *
    -- *
    negate_bool *
    --flag true
    use soroban_sdk::{contract, contractimpl, Env, Symbol,symbol_short };
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/counter.wasm *
      --source developer *
      --network testnet *
      --alias counter
      
      
    stellar contract invoke *
      --id CARAAZHSZ5RYZDEPQ6GVNQKMQFQMAJRGP4I5KXSSW2NOGKA5EWAOTX7W *
      --source developer *
      --network testnet *
      -- *
        add_to_counter *
      --increment 200
    stellar contract invoke *
      --id CARAAZHSZ5RYZDEPQ6GVNQKMQFQMAJRGP4I5KXSSW2NOGKA5EWAOTX7W *
      --source developer *
      --network testnet *
      -- *
        inc_counter
    stellar contract invoke *
      --id CARAAZHSZ5RYZDEPQ6GVNQKMQFQMAJRGP4I5KXSSW2NOGKA5EWAOTX7W *
      --source developer *
      --network testnet *
      -- *
        get_counter
    📌 Explicación general
    • Usa #![no_std], lo que indica que no usa la biblioteca estándar de Rust.

    • Importa módulos de soroban_sdk, necesarios para ejecutar el contrato en Soroban.

    • Define un contrato llamado Strings.

    • Implementa funciones para crear, obtener la longitud, comparar, verificar si está vacío y concatenar strings.

    • Como Soroban no admite concatenación directa de strings (+), se usa un Vec<String> para concatenación.


    hashtag
    🛠 Explicación de las funciones

    Todas las funciones reciben env: Env, que es necesario para la ejecución en Soroban.

    hashtag
    1️⃣ create_string(env: Env, text: String) -> String

    ✅ Crea un string y lo devuelve.

    📌 Paso a paso:

    1. Recibe un string como entrada.

    2. Simplemente lo retorna.

    📌 Ejemplo: Si llamamos create_string("Hola"), devuelve "Hola".


    hashtag
    2️⃣ get_length(env: Env, text: String) -> u32

    ✅ Obtiene la cantidad de caracteres en un string.

    📌 Paso a paso:

    1. Recibe un string como entrada.

    2. Usa .len() para obtener su longitud.

    3. Retorna la cantidad de caracteres.

    📌 Ejemplo: Si llamamos get_length("Hola"), devuelve 4.


    hashtag
    3️⃣ compare_strings(env: Env, text1: String, text2: String) -> bool

    ✅ Compara si dos strings son iguales.

    📌 Paso a paso:

    1. Recibe dos strings como entrada.

    2. Compara ambos usando ==.

    3. Retorna true si son iguales, false si no.

    📌 Ejemplo: Si llamamos compare_strings("Hola", "Hola"), devuelve true. Si llamamos compare_strings("Hola", "Mundo"), devuelve false.


    hashtag
    4️⃣ is_empty(env: Env, text1: String) -> bool

    ✅ Verifica si un string está vacío.

    📌 Paso a paso:

    1. Recibe un string como entrada.

    2. Usa .is_empty() para verificar si tiene contenido.

    3. Retorna true si el string está vacío, false si tiene caracteres.

    📌 Ejemplo: Si llamamos is_empty(""), devuelve true. Si llamamos is_empty("Hola"), devuelve false.


    hashtag
    5️⃣ concatenate(env: Env, text1: String, text2: String) -> Vec<String>

    ✅ Une dos strings en un vector.

    📌 Paso a paso:

    1. Recibe dos strings como entrada.

    2. Crea un Vec<String> con ambos valores.

    3. Retorna el vector.

    📌 Ejemplo: Si llamamos concatenate("Hola", "Mundo"), devuelve ["Hola", "Mundo"].

    🔹 Nota: Soroban aún no soporta concatenación directa con +, por lo que se usa un vector para unir los valores.

    hashtag
    📌 Resumen

    Este contrato proporciona funciones útiles para manipular strings en Soroban:

    • create_string(): Retorna el string recibido.

    • get_length(): Retorna la longitud del string.

    • compare_strings(): Compara si dos strings son iguales.

    • is_empty(): Verifica si un string está vacío.

    • concatenate(): Une dos strings en un vector (ya que la concatenación directa no está disponible en Soroban).

    Este contrato permite manejar textos dentro de la blockchain de manera eficiente. 🚀

    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Ejecución de prueba

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Funcióncreate_string

    Ejecución de prueba

    Función get_length

    Ejecución de prueba

    Función compare_strings

    ejecución de prueba

    Función is_empty

    Ejecución de prueba

    Función concatenate

    Ejecuciónd e prueba

    Sentencias condicionales

    hashtag
    📚 Teoría sobre if, else y match en Rust

    Rust ofrece dos estructuras de control clave para la toma de decisiones: if-else y match.

    hashtag
    ✅ if-else

    Se usa para tomar decisiones en función de una condición booleana.

    📌 Sintaxis básica:

    📌 Ejemplo en Rust:

    📌 Cuándo usar if-else

    • Cuando se necesita verificar una condición booleana única.

    • Cuando solo hay dos opciones (true o false).


    hashtag
    ✅ match

    Se usa para comparar un valor con múltiples posibles casos, similar a switch en otros lenguajes.

    📌 Sintaxis básica:

    📌 Ejemplo en Rust:

    📌 Cuándo usar match

    • Cuando hay varias condiciones posibles.

    • Cuando se trabaja con enum, strings o patrones complejos.

    hashtag
    Código en Soroban

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    hashtag
    📌 Explicación general del código

    • Usa #![no_std], lo que significa que no usa la biblioteca estándar de Rust.

    • Importa módulos de soroban_sdk, necesarios para escribir contratos inteligentes en Soroban.

    • Define una estructura de contrato


    🛠 Explicación de las Funciones

    1️⃣ check_number

    Descripción: Determina si un número es positivo o negativo.

    Mecanismo:

    • Recibe un número entero (i32) como entrada.

    • Emplea una estructura condicional if-else para evaluar el valor de num.

    • Si num es mayor o igual a 0, retorna un String

    2️⃣ check_role

    Descripción: Asigna permisos según el rol del usuario.

    Mecanismo:

    • Define tres roles fijos utilizando el macro symbol_short!:

      • Admin

      • User

    📌 Resumen General Este contrato inteligente demuestra el uso de condicionales y estructuras de control en Soroban. A través de check_number se evalúa la positividad o negatividad de un número, mientras que check_role asigna permisos basándose en roles específicos.


    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Función get_length

    Función check_role

    Option

    A pesar de ser un tipo Enum, dado su uso lo vamos a tratar aparte para una mejor claridad

    En Rust, el tipo Option es una forma segura y explícita de representar que un valor puede estar presente o no. Es como decir "puede haber algo o no" 🔍. En lugar de usar null (que puede causar errores difíciles de encontrar), Rust usa Option para obligarte a manejar ambos casos.

    La definición de Option es muy sencilla:

    • Some(valor): Indica que sí hay un valor. 😊

    Loops

    📚 Loops en Rust: Repetición de código de forma eficiente

    En programación, los loops (o bucles) permiten repetir la ejecución de un bloque de código automáticamente. Son útiles para realizar tareas repetitivas, recorrer colecciones de datos o ejecutar acciones hasta que se cumpla una condición.

    ⚠️ En contratos inteligentes, es importante que los loops tengan límites claros para evitar ciclos infinitos y un consumo excesivo de gas.

    ✅ loop (bucle infinito controlado) Se ejecuta indefinidamente hasta que se encuentra una condición de salida.

    📌 Sintaxis básica:

    📌 Ejemplo en Rust:

    📌 Cuándo usar loop Cuando no se sabe cuántas iteraciones se necesitarán y se desea un control manual de salida.

    stellar contract init  strings --name strings
    #![no_std]
    use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};
    
    #[contract]
    pub struct Strings;
    
    #[contractimpl]
    impl Strings {
        // Crear un nuevo string
        pub fn create_string(env: Env, text: String) -> String {
            text
        }
    
        // Obtener la longitud de un string
        pub fn get_length(env: Env, text: String) -> u32 {
            text.len()
        }
    
        // Comparar dos strings
        pub fn compare_strings(env: Env, text1: String, text2: String) -> bool {
            text1 == text2
        }
    
        // detecta si un strig está vacio
        pub fn is_empty(env: Env, text1: String) -> bool {
            text1.is_empty()
        }
        /*está pendiente en las funcioalidades de  soroban_sdk::String la funcionalidad directa
        de apend o una cobre carga del operador +, por lo tanto usaremos un vector */
        pub fn concatenate(env: Env, text1: String, text2: String) -> Vec<String> {
            vec![&env, text1, text2]
        }
    }
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/strings.wasm *
      --source developer *
      --network testnet *
      --alias strings
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    create_string *
    --text "Hello Soroban"
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    get_length *
    --text "Hello Soroban"
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    compare_strings *
    --text1 "Hello Soroban" *
    --text2 "Hello Stellar" *
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    is_empty *
    --text1 "Algún texto" 
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    concatenate *
    --text1 "Algún texto" *
    --text2 "Algún otro texto" 
    llamada
    ConditionalSt
    .
  • Implementa dos funciones clave:

    1. check_number(): Usa if-else para verificar si un número es positivo o negativo.

    2. check_role(): Usa match para asignar permisos según el rol de un usuario.

  • con el mensaje
    "Numero positivo"
    .
  • Si num es negativo, retorna un String con el mensaje "Numero negativo".

  • Guest

  • Utiliza una estructura de control match para comparar el role recibido con los roles definidos.

  • Retorna un String con el permiso correspondiente:

    • "Acceso total" si el rol es Admin.

    • "Acceso limitado" si el rol es User.

    • "Solo lectura" si el rol es Guest.

  • Si el rol no coincide con ninguno de los predefinidos, retorna "Rol no reconocido".

  • rustCopiarEditarif condicion {
        // Código si la condición es verdadera
    } else {
        // Código si la condición es falsa
    }
    rustCopiarEditarlet num = 10;
    if num > 0 {
        println!("El número es positivo");
    } else {
        println!("El número es negativo");
    }
    rustCopiarEditarmatch variable {
        valor1 => { /* Código para valor1 */ },
        valor2 => { /* Código para valor2 */ },
        _      => { /* Código por defecto */ },
    }
    rustCopiarEditarlet rol = "Admin";
    match rol {
        "Admin" => println!("Acceso total"),
        "User"  => println!("Acceso limitado"),
        "Guest" => println!("Solo lectura"),
        _       => println!("Rol no reconocido"),
    }
    stellar contract init  conditionalStatements --name ConditionalStatements
    #![no_std]
    use soroban_sdk::{contract, contractimpl, symbol_short, Env, String, Symbol};
    #[contract]
    pub struct ConditionalSt;
    
    #[contractimpl]
    impl ConditionalSt {
        // Función que usa if-else para determinar si un número es positivo o negativo.
        pub fn check_number(env: Env, num: i32) -> String {
            if num >= 0 {
                String::from_str(&env, "Numero positivo")
            } else {
                String::from_str(&env, "Numero negativo")
            }
        }
    
        // Función que usa match para asignar permisos según el rol del usuario.
        pub fn check_role(env: Env, role: Symbol) -> String {
            let admin: Symbol = symbol_short!("Admin");
            let user: Symbol = symbol_short!("User");
            let guest: Symbol = symbol_short!("Guest");
    
            match role {
                admin => String::from_str(&env, "Acceso total"),
                user => String::from_str(&env, "Acceso limitado"),
                guest => String::from_str(&env, "Solo lectura"),
                _ => String::from_str(&env, "Rol no reconocido"),
            }
        }
    }
    pub fn check_number(env: Env, num: i32) -> String {
            if num >= 0 {
                String::from_str(&env, "Numero positivo")
            } else {
                String::from_str(&env, "Numero negativo")
            }
        }
    pub fn check_role(env: Env, role: Symbol) -> String {
            let admin: Symbol = symbol_short!("Admin");
            let user: Symbol = symbol_short!("User");
            let guest: Symbol = symbol_short!("Guest");
    
            match role {
                admin => String::from_str(&env, "Acceso total"),
                user => String::from_str(&env, "Acceso limitado"),
                guest => String::from_str(&env, "Solo lectura"),
                _ => String::from_str(&env, "Rol no reconocido"),
            }
        }
    }
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/ConditionalStatements.wasm *
      --source developer *
      --network testnet *
      --alias ConditionalStatements
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    check_number *
    --num 10
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    check_role *
    --role "Admin"
    None: Indica que no hay ningún valor. 😞

    hashtag
    Ejemplo

    Imagina que queremos dividir dos números, pero tenemos que evitar dividir por cero. Podemos usar Option para devolver un resultado solo cuando la división tiene sentido:

    Contratos inteligentes ejemplo:

    Abrimos la consola en la ruta donde deseamos crear el proyecto y ejecutamos.

    Borramos todo el código y ponemos lo siguiente:

    hashtag
    Explicación del contrato

    📌 Estructuras y Tipos del Contrato

    1. Atributo #![no_std] Indica que el contrato no utiliza la biblioteca estándar de Rust, lo cual es común en entornos embebidos o en contratos inteligentes para reducir dependencias y adaptarse a restricciones de recursos.

    2. Macros de Contrato #[contract] y #[contractimpl] Estas macros son proporcionadas por el soroban_sdk y se usan para marcar:

    • #[contract]: Define la estructura principal del contrato.

    • #[contractimpl]: Implementa la lógica de negocio del contrato. Para más detalles, consulta la documentación de contratos Sorobanarrow-up-right.


    🛠 Funciones del Contrato

    1️⃣ set_message

    Descripción: Establece (o elimina) un mensaje en el almacenamiento del contrato. Si se pasa Some(mensaje), se guarda el mensaje; si se pasa None, se elimina el mensaje existente en el storage.

    Mecanismo:

    • Se define una clave (key) con el símbolo "msg".

    • Se utiliza la estructura de control match para evaluar el parámetro message:

      • Caso Some(msg): Se guarda el mensaje en el storage.

      • Caso None: Se elimina el mensaje del storage.


    2️⃣ get_message

    Descripción: Recupera el mensaje almacenado en el storage del contrato. Devuelve Some(mensaje) si existe un mensaje, o None si no se ha establecido ninguno.

    Mecanismo:

    • Se define la misma clave "msg" con un Symbol.

    • Se accede al storage del contrato para obtener el mensaje asociado a esa clave.


    3️⃣ greet

    Descripción: Genera un saludo utilizando un parámetro opcional. Si se proporciona un nombre (Some(nombre)), se saluda a esa persona; si no se proporciona ningún nombre (None), se saluda por defecto a "amigo".

    Mecanismo:

    • Se utiliza match para evaluar el parámetro name:

      • Caso Some(n): Devuelve el nombre proporcionado como saludo.

      • Caso None: Devuelve el saludo predeterminado "¡Hola, amigo! 😃" mediante String::from_str.


    📌 Resumen General Este contrato inteligente demuestra cómo utilizar el tipo Option para gestionar valores que pueden estar presentes o no. Permite:

    • Establecer o eliminar datos en el storage mediante la función set_message.

    • Recuperar datos del storage con get_message, retornando un valor opcional.

    • Generar respuestas predeterminadas en caso de ausencia de valor mediante la función greet.

    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Ejecución de prueba

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Función set_message:

    Ejecución de prueba

    Función get_message:

    Ejecución de prueba

    Función get_message:

    Vamos a hacer que se ejecute el código por la opción none

    Prueba de ejecución

    ✅ while (bucle condicional) Ejecuta el bloque de código mientras la condición sea verdadera.

    📌 Sintaxis básica:

    📌 Ejemplo en Rust:

    📌 Cuándo usar while Cuando no se conoce el número exacto de iteraciones, pero se tiene una condición de parada.

    ✅ for (bucle iterativo) Recorre colecciones de datos de manera segura y controlada.

    📌 Sintaxis básica:

    📌 Ejemplo con array:

    📌 Ejemplo con vector:

    📌 Cuándo usar for Cuando se necesita iterar sobre una colección con un número finito de elementos.

    ⚡ El bucle for es el más recomendado en contratos inteligentes, ya que garantiza que el número de iteraciones es limitado.


    hashtag
    Código en Soroban

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    📌 Descripción General del Contrato

    Este contrato inteligente, denominado LoopContract, está desarrollado en Rust para la plataforma Soroban. Utiliza la directiva #![no_std] y módulos de soroban_sdk para operar sin la biblioteca estándar de Rust.

    El contrato define tres funciones principales: ejemplo_loop, ejemplo_while y ejemplo_for, cada una demostrando diferentes estructuras de control de flujo para iteración en Rust.​

    🛠 Explicación de las Funciones

    1️⃣ ejemplo_loop

    Descripción: Genera una lista de números enteros desde 0 hasta 4 utilizando un bucle infinito controlado por una condición interna.​

    Mecanismo:

    • Inicializa la variable count en 0 y crea un vector vacío result para almacenar los resultados.​

    • Utiliza un bucle loop que se ejecuta indefinidamente hasta que se cumpla una condición de salida.​

    • En cada iteración, añade el valor actual de count al final del vector result usando push_back.​

    • Incrementa count en 1.​

    • Si count alcanza el valor de 5, se ejecuta la sentencia break para salir del bucle.​

    • Finalmente, retorna el vector result con los valores acumulados.​

    2️⃣ ejemplo_while

    Descripción: Genera una lista de números enteros desde 0 hasta 4 utilizando un bucle while que se ejecuta mientras se cumpla una condición específica.​

    Mecanismo:

    • Inicializa count en 0 y crea un vector vacío result para almacenar los resultados.​

    • Emplea un bucle while que continúa ejecutándose mientras count sea menor que 5.​

    • En cada iteración, añade el valor actual de count al vector result y luego incrementa count en 1.​

    • Cuando count alcanza 5, la condición del while deja de cumplirse y el bucle termina.​

    • Retorna el vector result con los valores acumulados.​

    3️⃣ ejemplo_for

    Descripción: Itera sobre un array de cadenas de texto y las almacena en un vector, demostrando el uso del bucle for en Rust.​

    Mecanismo:

    • Define un array elementos con tres cadenas de texto: "a", "b" y "c".​

    • Crea un vector vacío result para almacenar las cadenas convertidas.​

    • Utiliza un bucle for para iterar sobre cada elemento del array elementos.​

    • Dentro del bucle, convierte cada &str en un String utilizando String::from_str y el entorno env, y luego añade el resultado al vector result con push_back.​

    • Después de procesar todos los elementos, retorna el vector result que contiene las cadenas "a", "b" y "c" como objetos String.​

    📌 Resumen General El contrato LoopContract ejemplifica el uso de diferentes estructuras de control de flujo en Rust dentro del contexto de contratos inteligentes en Soroban. Las funciones demostradas —loop, while y for— ofrecen distintas maneras de iterar y manipular datos, permitiendo a los desarrolladores elegir la estructura más adecuada según las necesidades específicas de su lógica de negocio.


    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Ejecución de prueba

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Función ejemplo_loop:

    Función ejemplo_while:

    Función ejemplo_for:

    StellarExpert | Stellar XLM block explorer and analytics platformstellar.expertchevron-right
    Link del contrato desplegado en la red testnet de Stellar

    Saldo de una dirección en XLM

    A continuación vamos a realizar nuestro primer ejercicio en react 😃

    Consiste en realizar una conexión usando la libreria de que nos permite integrarnos a varias billeteras y el uso del servicio api rest de el cual ofrece varios servicios, para este caso en particular la lectura de saldo de una dirección en particular.

    Por recomendaciones de la documentación de react, como indican usar un framework, usaremos el de , que es uno de los más populares.

    hashtag
    Creación del proyecto

    fn dividir(a: f64, b: f64) -> Option<f64> {
        if b == 0.0 {
            // Si b es 0, no podemos dividir, así que devolvemos None 😞
            None
        } else {
            // Si b no es 0, devolvemos el resultado dentro de Some 😊
            Some(a / b)
        }
    }
    
    fn main() {
        let resultado = dividir(10.0, 2.0);
        
        // Usamos pattern matching para ver si hay un resultado
        match resultado {
            Some(valor) => println!("El resultado es: {}", valor), // Caso cuando hay valor 😊
            None => println!("No se puede dividir por cero 😞"),       // Caso cuando no hay valor
        }
    }
    stellar contract init option --name option
    #![no_std]
    use soroban_sdk::{contract, contractimpl, symbol_short, Env, String, Symbol};
    
    #[contract]
    pub struct OptionContract;
    #[contractimpl]
    impl OptionContract {
        // Establece (o elimina) un mensaje en el almacenamiento del contrato.
        // Si se pasa Some(mensaje), se guarda. Si se pasa None, se borra.
        pub fn set_message(env: Env, message: Option<String>) {
            const key: Symbol = symbol_short!("msg");
    
            match message {
                Some(msg) => {
                    // Guardamos el mensaje en el storage
                    env.storage().instance().set(&key, &msg)
                }
                None => {
                    // Si se pasa None, eliminamos el mensaje del storage
                    env.storage().instance().remove(&key)
                }
            }
        }
        // Recupera el mensaje almacenado (si existe) del storage.
        // Devuelve Some(mensaje) si hay un mensaje, o None si no hay ninguno.
        pub fn get_message(env: Env) -> Option<String> {
            let key: Symbol = symbol_short!("msg");
            env.storage().instance().get(&key)
        }
    
        // Método de saludo que usa un parámetro opcional.
        // Si se proporciona un nombre, saluda a esa persona; si no, saluda a "amigo".
        pub fn greet(env: Env, name: Option<String>) -> String {
            match name {
                Some(n) => n,
                None => String::from_str(&env, "¡Hola, amigo! 😃"),
            }
        }
    }
    rustCopiarEditarpub fn set_message(env: Env, message: Option<String>) {
        const key: Symbol = symbol_short!("msg");
    
        match message {
            Some(msg) => {
                // Guardamos el mensaje en el storage
                env.storage().instance().set(&key, &msg)
            }
            None => {
                // Si se pasa None, eliminamos el mensaje del storage
                env.storage().instance().remove(&key)
            }
        }
    }
    rustCopiarEditarpub fn get_message(env: Env) -> Option<String> {
        let key: Symbol = symbol_short!("msg");
        env.storage().instance().get(&key)
    }
    rustCopiarEditarpub fn greet(env: Env, name: Option<String>) -> String {
        match name {
            Some(n) => n,
            None => String::from_str(&env, "¡Hola, amigo! 😃"),
        }
    }
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/option.wasm *
      --source <Identity> *
      --network testnet *
      --alias option
    stellar contract invoke *                                                                          
    --id <ID_CONTRACT> *
    --source developer *
    --network testnet *
    -- *
    set_message *
    --message "Hola mundo 🙂"
    stellar contract invoke *                                                                          
    --id <ID_CONTRACT> *
    --source developer *
    --network testnet *
    -- *
    get_message
    stellar contract invoke *                                                                          
    --id <ID_CONTRACT> *
    --source developer *
    --network testnet *
    -- *
    greet
    loop {
        // Código a ejecutar en cada iteración
        if condicion {
            break; // Termina el loop
        }
    }
    fn main() {
        let mut count = 0;
        loop {
            println!("Contador: {} 😄", count);
            count += 1;
            if count == 5 {
                break;
            }
        }
    }
    while condicion {
        // Código a ejecutar mientras la condición sea verdadera
    }
    fn main() {
        let mut count = 0;
        while count < 5 {
            println!("Contador: {} 🚀", count);
            count += 1;
        }
    }
    for elemento in coleccion.iter() {
        // Código que usa cada elemento
    }
    fn main() {
        let numeros = [1, 2, 3, 4, 5];
        for numero in numeros.iter() {
            println!("Número: {} 💡", numero);
        }
    }
    fn main() {
        let elementos = vec!["a", "b", "c"];
        for elemento in elementos.iter() {
            println!("Elemento: {} 🔥", elemento);
        }
    }
    stellar contract init  loops --name loops
    #![no_std]
    use soroban_sdk::{contract, contractimpl,  Env, String, Vec};
    
    #[contract]
    pub struct LoopContract;
    
    #[contractimpl]
    impl LoopContract {
        
        pub fn ejemplo_loop(env: Env) -> Vec<u32> {
            let mut count = 0;
            let mut result:Vec<u32> = Vec::new(&env);
            loop {
                result.push_back(  count);
                count += 1;
                if count == 5 {
                    break;
                }
            }
            result
        }
    
        pub fn ejemplo_while(env: Env) -> Vec<u32> {
            let mut count = 0;
            let mut result:Vec<u32> = Vec::new(&env);
            while count < 5 {
                result.push_back(count);
                count += 1;
            }
            result
        }
    
        pub fn ejemplo_for(env: Env) -> Vec<String> {
          
            let elementos: [&str; 3] = ["a", "b", "c"];
            let mut result = Vec::new(&env);
            for elemento in elementos.iter() {
                result.push_back(String::from_str( &env, elemento));
            }
            result
        }
    }
    rustCopiarEditarpub fn ejemplo_loop(env: Env) -> Vec<u32> {
        let mut count = 0;
        let mut result: Vec<u32> = Vec::new(&env);
        loop {
            result.push_back(count);
            count += 1;
            if count == 5 {
                break;
            }
        }
        result
    }
    rustCopiarEditarpub fn ejemplo_while(env: Env) -> Vec<u32> {
        let mut count = 0;
        let mut result: Vec<u32> = Vec::new(&env);
        while count < 5 {
            result.push_back(count);
            count += 1;
        }
        result
    }
    rustCopiarEditarpub fn ejemplo_for(env: Env) -> Vec<String> {
        let elementos: [&str; 3] = ["a", "b", "c"];
        let mut result = Vec::new(&env);
        for elemento in elementos.iter() {
            result.push_back(String::from_str(&env, elemento));
        }
        result
    }
    stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/loops.wasm *
      --source developer *
      --network testnet *
      --alias loops
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    ejemplo_loop
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    ejemplo_while
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    ejemplo_for

    Nos ubicamos en la ubicación que deseemos abrimos consola y ejecutamos:

    A continuación el prompt hace varias preguntas:

    Entramos en la carpeta y en consola ponemos el siguiente comando:

    Con esto ya tenemos todas instalaciones necesarias para este proyecto 😃

    Abrimos el directorio de wallet-balance con visual studio code o tu editor favorito

    Dentro de la carpeta src/app cambiamos el siguiente código en el archivo layout.tsx

    También cambiamos el contenido del archivo page.tsx

    Como podemos ver la "magia" del programa está los componentes:

    1. WalletButton: Es el resposable de accesar a la billetera y saber su dirección

    2. Balance: una vez tenemos la dirección, mediante el api rest de horizon, obtenemos el saldo.

    Dentro de la carpeta src/app creamos la carpeta utils y components

    Estructura de directorios sugerido

    Dentro de components Crear el archivo WalletButton.tsx

    Crear el archivo Balance.tsx

    Dentro de la carpeta utils

    Creamos el archivo balance.ts

    Para ejecutar el programa ejecutamos:

    Ejecución del programa 😁

    Todo el proyecto esta en un repositorio githubarrow-up-right

    Stellar-Wallets-Kitarrow-up-right
    stellar horizon arrow-up-right
    Next.jsarrow-up-right

    Estados de almacenamiento

    hashtag
    Tipos de Almacenamiento en Stellar Soroban

    hashtag
    🌟 Introducción Teórica

    Imagina que tu contrato inteligente es como una casa 🏠, y necesitas diferentes tipos de espacios para guardar tus cosas:

    • 🗄️ Un archivo permanente para documentos importantes

    • 📋 Una mesa de trabajo para proyectos actuales

    • 🗑️ Una papelera para cosas temporales

    En Soroban tenemos exactamente eso: tres tipos de almacenamiento que nos permiten gestionar nuestros datos de manera eficiente y económica.


    hashtag
    🎯 Los Tres Tipos de Almacenamiento

    hashtag
    1. 💾 PERSISTENT STORAGE (Almacenamiento Persistente)

    🧠 ¿Qué es?

    Es el almacenamiento más duradero y confiable de Soroban. Los datos aquí se mantienen "para siempre" (hasta que explícitamente los borres). Es como guardar algo en una caja fuerte 🔒.

    ✨ Características:

    • 🔄 Permanente: Los datos NO se borran automáticamente

    • 💰 Más costoso: Requiere más fees porque ocupa espacio indefinidamente

    • 🛡️ Más seguro: Ideal para datos críticos del contrato

    🎯 ¿Cuándo usarlo?

    • 👤 Información de usuarios (balances, perfiles)

    • ⚙️ Configuraciones del contrato que no cambian

    • 📊 Datos históricos importantes

    💡 Ejemplo Práctico:

    Compilación del contrato:

    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    hashtag
    Función set_balance

    hashtag
    2. 🏃‍♂️ INSTANCE STORAGE (Almacenamiento de Instancia)

    🧠 ¿Qué es?

    Es el almacenamiento intermedio de Soroban. Los datos aquí duran bastante tiempo, pero pueden "caducar" si no se usan. Es como tu escritorio de trabajo 📝 - mantienes las cosas que usas regularmente.

    ✨ Características:

    • ⏳ Semi-permanente: Los datos pueden expirar si no se acceden

    • 💸 Costo medio: Más barato que persistent, más caro que temporary

    • 🔄 Auto-renovable: Cada acceso extiende su tiempo de vida

    🎯 ¿Cuándo usarlo?

    • ⚙️ Configuraciones del contrato que pueden cambiar

    • 📊 Metadatos del contrato (nombre, símbolo, decimales)

    • 🎮 Estados de juego que duran varias partidas

    💡 Ejemplo Práctico:

    Compilación del contrato:

    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    hashtag
    Función init_token

    hashtag
    3. ⚡ TEMPORARY STORAGE (Almacenamiento Temporal)

    🧠 ¿Qué es?

    Es el almacenamiento más barato y temporal de Soroban. Los datos aquí se borran automáticamente después de un tiempo. Es como usar notas adhesivas 📝 para recordatorios rápidos.

    ✨ Características:

    • ⏰ Temporal: Los datos se borran automáticamente (aprox. 24 horas)

    • 💰 Más barato: Ideal para optimizar costos

    • 🚀 Rápido: Perfecto para operaciones que no necesitan persistir

    🎯 ¿Cuándo usarlo?

    • 🔢 Cálculos intermedios en transacciones complejas

    • 🎫 Nonces y tokens de sesión

    • 📊 Caché de datos temporales

    💡 Ejemplo Práctico:

    Compilación del contrato:

    hashtag
    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    hashtag
    🎯 Comparación Rápida

    Aspecto
    💾 Persistent
    🏃‍♂️ Instance
    ⚡ Temporary

    hashtag
    🎨 Consejos de Mejores Prácticas

    hashtag
    💾 Para PERSISTENT:

    • ✅ Usa para balances de usuarios

    • ✅ Datos que nunca deben perderse

    • ❌ Evita para datos temporales (caro)

    hashtag
    🏃‍♂️ Para INSTANCE:

    • ✅ Configuraciones del contrato

    • ✅ Metadatos que cambian ocasionalmente

    • ❌ Evita para datos de usuarios individuales

    hashtag
    ⚡ Para TEMPORARY:

    • ✅ Cálculos intermedios

    • ✅ Sistema de cooldowns

    • ✅ Cache temporal

    • ❌ Evita para datos importantes

    Errores

    hashtag
    📚 Manejo de errores

    hashtag
    🎯 Parte Teórica: Fundamentos del Manejo de Errores

    hashtag
    ¿Por qué es importante el manejo de errores? 🤔

    En los contratos inteligentes de Soroban, el manejo de errores es crítico porque:

    • 🛡️ Seguridad: Evita comportamientos inesperados que podrían ser explotados

    • 💰 Protección de fondos: Previene pérdidas de tokens o activos

    • 🎯 Experiencia del usuario: Proporciona mensajes claros sobre qué salió mal

    hashtag
    🔧 Tipos de Errores en Soroban

    1. Errores del Sistema (Host Errors) 🏠

    Son errores que maneja automáticamente el entorno de Soroban:

    • Falta de memoria

    • Límites de gas excedidos

    • Operaciones matemáticas inválidas (división por cero)

    2. Errores Personalizados (Custom Errors) ✨

    Son errores que tú defines para tu lógica de negocio:

    • Permisos insuficientes

    • Parámetros inválidos

    • Estados de contrato incorrectos

    hashtag
    🛠️ Herramientas para Manejar Errores

    Result<T, E> - Tu mejor amigo 🤝

    Macros útiles 🎨

    • panic!() - Para errores irrecuperables (¡usar con cuidado!)

    • assert!() - Para validaciones críticas

    • log!() - Para debugging (solo en testnet)

    hashtag
    📋 Estrategias de Manejo de Errores

    1. Validación Temprana ⚡

    Valida todos los parámetros al inicio de la función

    2. Errores Específicos 🎯

    Crea tipos de error específicos para cada situación

    3. Propagación Controlada 📤

    Usa ? para propagar errores de forma limpia

    4. Logging Inteligente 📝

    Registra errores para facilitar el debugging

    💡 Ejemplos Prácticos:

    hashtag
    🌱 Ejemplo : Contrato Básico SIN Errores Personalizados

    Empecemos con algo simple que usa solo los errores que Soroban ya tiene integrados:

    hashtag
    🎯 Conceptos Clave de este Ejemplo:

    hashtag
    ✅ Herramientas Básicas de Manejo de Errores:

    1. Option<T> 📦

      • Some(value) cuando todo está bien

      • None

    hashtag
    📋 Cuándo Usar Cada Herramienta:

    • Option: Para datos que pueden o no existir

    • assert!: Para condiciones que NUNCA deberían ser falsas

    • checked_*

    hashtag
    🏗️ Ejemplo : Contrato Básico con Errores Personalizados

    Este ejemplo muestra un contrato simple para gestionar un contador con validaciones:

    hashtag
    Lecciones Importantes del Ejemplo :

    1. Manejo de Estado Explícito 🎯

    2. Validaciones Paso a Paso 🛡️

    3. Errores Específicos y Útiles 📋

    hashtag
    🎉 Conclusión

    El manejo de errores en Soroban es fundamental para crear contratos seguros y confiables. Recuerda:

    • 🎯 Sé específico con tus errores

    • 🛡️ Valida todo lo que puedas

    • 🧪 Prueba todos los escenarios

    Registro de actividades (logs)

    📚 Logging en Soroban: Cómo registrar información útil durante la ejecución de contratos inteligentes

    El logging es una herramienta esencial para los desarrolladores de contratos inteligentes, ya que permite registrar información clave durante la ejecución del contrato. Estos registros pueden ser útiles para depuración, seguimiento del comportamiento del contrato o para proveer información de auditoría.

    ⚠️ En Soroban, los logs no almacenan datos permanentes, pero pueden verse en el historial de ejecución. Son eventos diagnósticos y no afectan el estado del contrato.

    hashtag

    npx create-next-app@latest
    √ What is your project named? ... wallet-balance
    √ Would you like to use TypeScript? ... No / Yes
    √ Would you like to use ESLint? ... No / Yes
    √ Would you like to use Tailwind CSS? ... No / Yes
    √ Would you like your code inside a src/ directory? ... No / Yes
    √ Would you like to use App Router? (recommended) ... No / Yes
    √ Would you like to use Turbopack for next dev? ... No / Yes
    √ Would you like to customize the import alias (@/* by default)? ... No / Yes
    npm install @creit.tech/stellar-wallets-kit
    export const metadata: Metadata = {
      title: "Wallet balance",
      description: "Mi primera daap gracias a Stellar español",
    };
    // Archivo principal de la aplicación: muestra la interfaz principal y conecta los componentes clave
    // "use client" indica que este archivo se ejecuta del lado del cliente en Next.js
    'use client'
    import React, { useState } from "react";
    import WalletButton from "./components/WalletButton";
    import Balance from "./components/Balance";
    
    /**
     * Componente principal Home
     * Este componente representa la página principal de la aplicación.
     * Permite al usuario conectar su wallet de Stellar y ver el saldo de su cuenta.
     * Utiliza estados locales para manejar la conexión y la dirección de la cuenta.
     */
    export default function Home() {
      // Estado que indica si la wallet está conectada (true/false)
      const [isConnected, setIsConnected] = useState(false);
      // Estado que almacena la dirección pública de la cuenta Stellar conectada
      const [address, setAddress] = useState<string | null>(null);
    
      // Renderiza la interfaz principal de la aplicación
      return (
        <>
          <main className="flex justify-center items-start min-h-screen bg-black py-12">
            <div className="bg-black bg-opacity-90 rounded-xl shadow-[0_4px_32px_0_rgba(255,255,255,0.25)] border border-white/20 px-8 py-10 max-w-xl w-full text-center">
              {/* Título principal */}
              <h1 className="text-3xl md:text-4xl font-bold text-white mb-4">Bienvenido a Stellar en español</h1>
              {/* Descripción de la aplicación */}
              <p className="text-base md:text-lg text-gray-200">
                Esta aplicación te permite consultar de forma rápida y sencilla el saldo de tu cuenta en la red Stellar. Conecta tu wallet y visualiza al instante cuántos XLM tienes disponibles en tu cuenta.
              </p>
              {/* Botón para conectar/desconectar la wallet */}
              <WalletButton isConnected={isConnected} address={address} setIsConnected={setIsConnected} setAddress={setAddress} />
              {/* Componente que muestra el saldo si la wallet está conectada */}
              <Balance isConnected={isConnected} address={address} />
              
            </div>
          </main>
         
        </>
      );
    }
    
    // Componente WalletButton: permite conectar y desconectar una wallet de Stellar
    // "use client" indica que este componente se ejecuta del lado del cliente en Next.js
    'use client'
    import React from "react";
    import {
        StellarWalletsKit,
        WalletNetwork,
        allowAllModules,
        FREIGHTER_ID,
        ISupportedWallet
    } from '@creit.tech/stellar-wallets-kit';
    
    /**
     * Props que recibe el componente WalletButton
     * @property {boolean} isConnected - Indica si la wallet está conectada
     * @property {string | null} address - Dirección de la cuenta Stellar
     * @property {function} setIsConnected - Función para actualizar el estado de conexión
     * @property {function} setAddress - Función para actualizar la dirección
     */
    interface WalletButtonProps {
        isConnected: boolean; // Indica si la wallet está conectada
        address: string | null; // Dirección de la cuenta Stellar
        setIsConnected: React.Dispatch<React.SetStateAction<boolean>>; // Función para actualizar el estado de conexión
        setAddress: React.Dispatch<React.SetStateAction<string | null>>; // Función para actualizar la dirección
    }
    
    /**
     * Componente funcional que maneja la conexión y desconexión de la wallet de Stellar.
     * Permite al usuario conectar su wallet, ver la dirección y desconectarla.
     * Utiliza el kit de wallets de Stellar para facilitar la integración.
     */
    const WalletButton: React.FC<WalletButtonProps> = ({ isConnected, address, setIsConnected, setAddress }) => {
        // Inicializa el kit de wallets de Stellar para la red de prueba (TESTNET)
        const kit: StellarWalletsKit = new StellarWalletsKit({
            network: WalletNetwork.TESTNET,
            selectedWalletId: FREIGHTER_ID,
            modules: allowAllModules(),
        });
    
        /**
         * Función para conectar la wallet
         * Abre un modal para seleccionar la wallet y obtiene la dirección de la cuenta
         */
        const handleConnect = async () => {
            // Abre el modal para seleccionar la wallet
            await kit.openModal({
                onWalletSelected: async (option: ISupportedWallet) => {
                    kit.setWallet(option.id); // Selecciona la wallet elegida
                    const { address } = await kit.getAddress(); // Obtiene la dirección de la cuenta
                    if (address) {
                        setAddress(address); // Actualiza la dirección en el estado
                        setIsConnected(true); // Marca como conectada
                    }
                }
            });
        };
    
        /**
         * Función para desconectar la wallet
         * Limpia el estado de conexión y la dirección
         */
        const handleDisconnect = () => {
            setIsConnected(false); // Marca como desconectada
            setAddress(null); // Limpia la dirección
        };
    
        // Renderiza el botón correspondiente según el estado de conexión
        return (
            <div className="mt-6 flex flex-col items-center">
                {isConnected ? (
                    <>
                        {/* Muestra la dirección conectada y botón para desconectar */}
                        <p className="text-white mb-2">Conectado como:</p>
                        <p className="text-white font-mono mb-4">{address}</p>
                        <button
                            className="bg-blue-400 hover:bg-blue-500 text-white px-4 py-2 rounded"
                            onClick={handleDisconnect}
                        >
                            Desconectar Wallet
                        </button>
                    </>
                ) : (
                    // Botón para conectar la wallet
                    <button
                        className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded"
                        onClick={handleConnect}
                    >
                        Conectar Wallet
                    </button>
                )}
            </div>
        );
    };
    
    // Exporta el componente para que pueda ser usado en otras partes de la app
    export default WalletButton;
    / Componente Balance: muestra el saldo de XLM de la cuenta conectada
    // "use client" indica que este componente se ejecuta del lado del cliente en Next.js
    'use client'
    // Importamos React y useEffect para manejar el ciclo de vida del componente
    import React, { useEffect } from "react";
    // Importamos la función GetBalance que consulta el saldo en la blockchain
    import GetBalance from "../utils/balance";
    
    // Definición de las propiedades (props) que recibe el componente Balance
    // Estas props permiten saber si la wallet está conectada y cuál es la dirección de la cuenta
    interface BalanceProps {
      isConnected: boolean; // Indica si la wallet está conectada
      address: string | null; // Dirección de la cuenta Stellar
    }
    
    // Componente funcional que recibe las props definidas arriba
    const Balance: React.FC<BalanceProps> =  ({ isConnected, address }) => {
      // Estado local para guardar el saldo obtenido de la cuenta
      // useState inicializa el saldo en 0
      const [balance, setBalance] = React.useState(0);
     
      // useEffect se ejecuta cada vez que cambian isConnected o address
      // Sirve para actualizar el saldo cuando la wallet se conecta o cambia de cuenta
      useEffect(() => {
          // Si la wallet está conectada y hay dirección, consulta el saldo
        if (isConnected && address) {
          // Llama a la función GetBalance y actualiza el estado con el resultado
          GetBalance(address).then((result) => {
            setBalance(result); // Actualiza el saldo en el estado
          });
         
        }
      }, [isConnected, address]); // Dependencias: se ejecuta cuando cambian estos valores
      
      // Si no está conectada la wallet o no hay dirección, no muestra nada
      if (!isConnected || !address) {
        return null; // No renderiza nada
      }
    
      // Renderiza el saldo en pantalla si la wallet está conectada
      return (
        <div className="mt-4 text-white text-lg font-semibold">
          {/* Muestra el saldo en XLM */}
          Su saldo en XLM es: {balance}
        </div>
      );
    };
    
    // Exporta el componente para que pueda ser usado en otras partes de la app
    export default Balance;
    // Función asíncrona que obtiene el saldo de XLM de una cuenta Stellar
    // Esta función es fundamental para consultar el saldo de una cuenta en la blockchain de Stellar.
    // Recibe como parámetro la dirección de la cuenta y devuelve el saldo en XLM.
    // Si la dirección es inválida o no se encuentra saldo, retorna 0.
    //
    // Parámetros:
    //   address (string): Dirección pública de la cuenta Stellar a consultar.
    //
    // Retorna:
    //   Promise<number>: Saldo de la cuenta en XLM (puede ser 0 si no hay fondos o la cuenta no existe).
    async function GetBalance(address: string) {
        // Verifica que la dirección no sea nula o vacía
        if (address) {
            // Realiza una petición HTTP a la API de Stellar para obtener los datos de la cuenta
            const response = await fetch(`https://horizon-testnet.stellar.org/accounts/${address}`);
           
            // Si la respuesta es exitosa (status 200)
            if (response.ok) {
                // Convierte la respuesta a formato JSON
                const data = await response.json();
                // Busca el objeto que representa el saldo nativo (XLM) dentro del array de balances
                const xlmBalance = data.balances.find((b: any) => b.asset_type === "native");
                // Retorna el saldo si es mayor a 0, si no, retorna 0
                return xlmBalance.balance > 0 ? xlmBalance.balance : 0;
            }
        }
        // Si la dirección es inválida o la petición falla, retorna 0
        return 0;
    }
    // Exporta la función para que pueda ser utilizada en otros archivos
    export default GetBalance;
    npm run dev
    ⚡ Acceso rápido: Optimizado para lecturas frecuentes
    🔑 Claves y identificadores únicos
    📈 Eficiente: Perfecto para datos que usas con frecuencia
    📝 Información que se actualiza regularmente
    🧹 Auto-limpieza: No necesitas borrar manualmente
    🔄 Estados de transacciones en progreso

    Auto-borrado

    ❌ No

    🔄 Si no se usa

    ✅ Automático

    Duración

    ♾️ Permanente

    ⏳ Semi-permanente

    ⏰ ~24 horas

    Costo

    💰💰💰 Alto

    💰💰 Medio

    💰 Bajo

    Uso ideal

    👤 Datos críticos

    ⚙️ Configuraciones

    Resultado de la compilación
    Resultado del despliegue
    Resultado del llamado al contrato
    Resultado de la compilación
    Resultado del despliegue
    Resultado del llamado al contrato
    Resultado de la compilación
    Resultado del despliegue

    🔢 Cálculos temporales

    📊 Debugging: Facilita la identificación y solución de problemas
    cuando algo falla o no existe
  • Perfecto para "encontrado/no encontrado"

  • assert!(condición, mensaje) ⚡

    • Valida condiciones críticas

    • Si falla, causa panic automático

    • Ideal para validaciones que nunca deberían fallar

  • checked_add(), checked_sub() 🧮

    • Operaciones matemáticas seguras

    • Devuelven Option: Some(result) o None si hay overflow

    • Previenen errores numéricos silenciosos

  • Operador ? con Option 🎯

    • Si es Some, continúa con el valor

    • Si es None, termina la función devolviendo None

    • Hace el código más limpio

  • unwrap_or(default) 🛡️

    • Proporciona un valor por defecto si es None

    • Evita crashes cuando un valor faltante es aceptable

  • : Para operaciones matemáticas que pueden overflow
  • ?: Para propagar "falta de datos" de forma limpia

  • unwrap_or: Cuando un valor faltante tiene un reemplazo lógico

  • 📝 Documenta tu código
  • 🚀 Itera y mejora continuamente

  • #![no_std]
    use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env};
    
    #[contract]
    pub struct TokenContract;
    
    #[contractimpl]
    impl TokenContract {
        pub fn set_balance(env: Env, user: Address, amount: i128) {
            let key = symbol_short!("balance");
            // Este dato debe persistir siempre 🔒
            env.storage().persistent().set(&(key, user), &amount);
        }
        
        pub fn get_balance(env: Env, user: Address) -> i128 {
            let key = symbol_short!("balance");
            env.storage().persistent().get(&(key, user)).unwrap_or(0)
        }
    }
    stellar contract build
    stellar contract deploy *
    --wasm target\wasm32v1-none\release\persistent.wasm *
    --source developer *
    --network testnet *
    --alias persistent
    stellar contract invoke *
    --id <id contract> *
    --source developer *
    --network testnet *
    -- *
    set_balance *
    --user  wallet_address*
    --amount 1000000
    #[contractimpl]
    impl TokenContract {
        // 🏃‍♂️ Configuración del token (INSTANCE)
        pub fn init_token(env: Env, name: String, symbol: String) {
            // Metadatos que se usan frecuentemente pero pueden cambiar
            env.storage().instance().set(&symbol_short!("name"), &name);
            env.storage().instance().set(&symbol_short!("symbol"), &symbol);
            env.storage().instance().set(&symbol_short!("decimals"), &7u32);
        }
        
        pub fn get_name(env: Env) -> String {
            env.storage().instance()
                .get(&symbol_short!("name"))
                .unwrap_or_else(|| String::from_str(&env, "Unknown Token"))
        }
        
        // 🔄 Actualizar configuración
        pub fn update_name(env: Env, new_name: String) {
            env.storage().instance().set(&symbol_short!("name"), &new_name);
            // ✨ Cada acceso renueva automáticamente el tiempo de vida
        }
    }
    stellar contract build
    stellar contract deploy *
    --wasm target\wasm32v1-none\release\instance.wasm *
    --source developer *
    --network testnet *
    --alias instance
    stellar contract invoke *
    --id <id contract> *
    --source developer *
    --network testnet *
    -- *
    init_token *
    --name  "Stellar Espanol"*
    --symbol "SET"
    #![no_std]
    use soroban_sdk::{contract, contractimpl, symbol_short, vec, Address, Env, String, Vec};
    
    #[contract]
    pub struct TokenContract;
    
    #[contractimpl]
    impl TokenContract {
        // ⚡ Operación de transferencia con estado temporal
        pub fn transfer_with_memo(env: Env, from: Address, to: Address, amount: i128, memo: String) {
            // 🔢 Guardamos temporalmente el ID de transacción
            let tx_id = env.ledger().sequence();
            let temp_key = symbol_short!("tx_temp");
    
            // ⚡ Datos temporales para esta transacción
            env.storage().temporary().set(
                &(temp_key, tx_id),
                &(from.clone(), to.clone(), amount, memo),
            );
    
            // Realizar la transferencia...
            // Los datos temporales se borrarán automáticamente ✨
        }
    
        // 🎮 Sistema de cooldown temporal
        pub fn use_special_ability(env: Env, user: Address) {
            let cooldown_key = symbol_short!("cooldown");
    
            // ⏰ Verificar si está en cooldown
            if env.storage().temporary().has(&(&cooldown_key, user.clone())) {
                panic!("🚫 Habilidad en cooldown!");
            }
    
            // ⚡ Activar cooldown temporal (se borra solo)
            env.storage().temporary().set(&(cooldown_key, user), &true);
    
            // Ejecutar habilidad especial...
        }
    }
    stellar contract build
    stellar contract deploy *
    --wasm target\wasm32v1-none\release\temporary.wasm *
    --source developer *
    --network testnet *
    --alias temporary
    // Tipo Result básico
    Result<Valor_Exitoso, Tipo_de_Error>
    
    // En Soroban usamos principalmente:
    Result<ReturnType, Error>
    #![no_std]
    use soroban_sdk::{contract, contractimpl, contracttype, panic_with_error, Env, Symbol};
    // 📦 Estructura simple para guardar datos
    #[contracttype]
    pub struct UserData {
        pub name: Symbol,
        pub balance: i64,
    }
    #[contract]
    pub struct SimpleContract;
    
    #[contractimpl]
    impl SimpleContract {
        // 💾 Función para guardar datos de usuario
        pub fn set_user(env: Env, user_id: Symbol, name: Symbol, balance: i64) {
            // ✅ Validación simple usando assert! (si falla, panic automático)
            assert!(balance >= 0, "Balance no puede ser negativo");
    
            // 💾 Crear y guardar datos
            let user_data = UserData { name, balance };
            env.storage().persistent().set(&user_id, &user_data);
        }
        // 👀 Función para leer datos con Option (sin errores personalizados)
        pub fn get_user(env: Env, user_id: Symbol) -> Option<UserData> {
            // 🔍 Intentar obtener datos del storage
            // Si no existe, devuelve None automáticamente
            env.storage().persistent().get(&user_id)
        }
        // 💰 Función para actualizar balance con validaciones básicas
        pub fn update_balance(env: Env, user_id: Symbol, new_balance: i64) -> Option<i64> {
            // ✅ Validación simple
            if new_balance < 0 {
                // 🚨 Forma simple de manejar error: devolver None
                return None;
            }
    
            // 📋 Intentar obtener datos existentes
            let mut user_data: UserData = match env.storage().persistent().get(&user_id) {
                Some(data) => data,
                None => return None, // Usuario no existe
            };
    
            // ✅ Actualizar y guardar
            user_data.balance = new_balance;
            env.storage().persistent().set(&user_id, &user_data);
    
            // 🎉 Devolver el nuevo balance
            Some(new_balance)
        }
        // ➕ Función que puede fallar por overflow (usa checked_add)
        pub fn add_to_balance(env: Env, user_id: Symbol, amount: i64) -> Option<i64> {
            // 📋 Obtener datos del usuario
            let mut user_data: UserData = env.storage().persistent().get(&user_id)?; // 🎯 Usa ? con Option (si es None, devuelve None)
    
            // 🧮 Suma segura (evita overflow)
            let new_balance = user_data.balance.checked_add(amount)?;
    
            // ✅ Solo actualizar si todo salió bien
            user_data.balance = new_balance;
            env.storage().persistent().set(&user_id, &user_data);
    
            Some(new_balance)
        }
        // 🔍 Función helper que demuestra diferentes formas de manejar "no encontrado"
        pub fn get_balance_or_zero(env: Env, user_id: Symbol) -> i64 {
            // 🎯 Usar unwrap_or para proporcionar valor por defecto
            env.storage()
                .persistent()
                .get::<Symbol, UserData>(&user_id)
                .map(|data| data.balance) // Si existe, tomar el balance
                .unwrap_or(0) // Si no existe, usar 0
        }
    }
    #![no_std]
    use soroban_sdk::{contract, contracttype, contractimpl, contracterror, Env, Symbol};
    
    // 📝 IMPORTANTE: Definir errores ANTES del contrato
    #[contracterror]
    #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
    #[repr(u32)]
    pub enum CounterError {
        ValueTooHigh = 1,
        Unauthorized = 2,
        NotInitialized = 3,
    }
    
    #[contracttype]
    pub struct CounterData {
        pub value: u32,
        pub owner: Symbol,
        pub max_value: u32,
    }
    
    #[contract]
    pub struct CounterContract;
    
    #[contractimpl]
    impl CounterContract {
        
        // ✅ FUNCIONA: Devuelve Result directamente
        pub fn init(env: Env, owner: Symbol, max_value: u32) -> Result<(), CounterError> {
            if max_value == 0 {
                return Err(CounterError::ValueTooHigh);
            }
            
            let data = CounterData {
                value: 0,
                owner,
                max_value,
            };
            
            env.storage().persistent().set(&Symbol::new(&env, "counter"), &data);
            Ok(())
        }
        
        // ✅ FUNCIONA: Manejo manual de errores
        pub fn increment(env: Env, caller: Symbol, amount: u32) -> Result<u32, CounterError> {
            
            // Obtener datos
            let data_key = Symbol::new(&env, "counter");
            let mut data: CounterData = match env.storage().persistent().get(&data_key) {
                Some(d) => d,
                None => return Err(CounterError::NotInitialized),
            };
            
            // Verificar permisos
            if data.owner != caller {
                return Err(CounterError::Unauthorized);
            }
            
            // Verificar overflow
            let new_value = match data.value.checked_add(amount) {
                Some(v) => v,
                None => return Err(CounterError::ValueTooHigh),
            };
            
            // Verificar límite
            if new_value > data.max_value {
                return Err(CounterError::ValueTooHigh);
            }
            
            // Actualizar
            data.value = new_value;
            env.storage().persistent().set(&data_key, &data);
            
            Ok(new_value)
        }
        
        // ✅ FUNCIONA: Lectura simple
        pub fn get_value(env: Env) -> Result<u32, CounterError> {
            let data: CounterData = match env.storage().persistent().get(&Symbol::new(&env, "counter")) {
                Some(d) => d,
                None => return Err(CounterError::NotInitialized),
            };
            
            Ok(data.value)
        }
    }
    // 🔍 Cada get() se maneja explícitamente
    let mut data: CounterData = match env.storage().persistent().get(&data_key) {
        Some(d) => d,           // ✅ Datos encontrados
        None => return Err(CounterError::NotInitialized),  // ❌ Error específico
    };
    // 🔐 Validación 1: Permisos
    if data.owner != caller {
        return Err(CounterError::Unauthorized);
    }
    
    // 🧮 Validación 2: Overflow
    let new_value = match data.value.checked_add(amount) {
        Some(v) => v,
        None => return Err(CounterError::ValueTooHigh),
    };
    
    // 📊 Validación 3: Límites de negocio
    if new_value > data.max_value {
        return Err(CounterError::ValueTooHigh);
    }
    #[contracterror]
    #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
    #[repr(u32)]
    pub enum CounterError {
        ValueTooHigh = 1,    // 🚫 Para overflow O límites excedidos
        Unauthorized = 2,    // 🔒 Para problemas de permisos
        NotInitialized = 3,  // ❌ Para contrato no inicializado
    }
    Código en Soroban

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    hashtag
    📌 Código del contrato: lib.rs

    🛠 Explicación de la función hello

    🔍 Descripción:

    La función hello utiliza el macro log! de Soroban para registrar un mensaje que incluye un valor dinámico.

    📌 Mecanismo:

    • log!(&env, "Hello {}", value); → Utiliza el macro log!, que acepta el entorno (env) y una cadena formateada estilo Rust.

    • {} es un marcador de posición que se reemplaza por el valor pasado, en este caso, un Symbol.

    • Este log no modifica el estado del contrato y es accesible mediante las herramientas de análisis de Soroban.

    🧠 Importante: Los logs son visibles cuando se ejecutan pruebas o cuando se hace seguimiento en una red (por ejemplo, testnet) con herramientas como el CLI de Soroban.

    Borramos el contrato generado en test.rs y ponemos el siguiente código

    🧪 Código de prueba: test.rs

    🧪 ¿Qué hace esta prueba?

    1️⃣ Configura un entorno (env) local de pruebas. 2️⃣ Crea una dirección y registra el contrato. 3️⃣ Llama a la función hello pasando el símbolo "Dev". 4️⃣ Verifica que se haya registrado correctamente el log esperado.

    hashtag
    📝 Resultado esperado del log:

    📌 Este log muestra:

    • El ID del contrato que generó el log.

    • El tipo de evento: log.

    • Los datos logueados, en este caso "Hello {}" y el símbolo Dev.


    hashtag
    🧠 Cuándo usar log!

    ✅ Para seguimiento de ejecución en diferentes entornos (testnet, futurenet). ✅ Para depurar comportamientos inesperados. ✅ Para mantener trazabilidad durante el desarrollo. ❌ No usar para almacenar información crítica del negocio, ya que los logs no son persistentes ni recuperables dentro del contrato.

    hashtag
    Compilación y prueba

    resultado de la ejecución

    hashtag
    🧪 ¿Por qué se necesita -- --nocapture?

    Por defecto, cuando ejecutas cargo test, Rust captura la salida estándar (stdout) y la salida de error (stderr) de cada prueba para mantener la salida de la terminal limpia y enfocada en los resultados de las pruebas. Esto significa que cualquier salida generada por macros como println! o log! no se mostrará en la terminal, a menos que una prueba falle.

    El flag --nocapture le indica al ejecutable de pruebas que no capture estas salidas, permitiendo que cualquier mensaje impreso se muestre directamente en la terminal, independientemente de si la prueba pasa o falla.

    Colecciones

    hashtag
    📚 Colecciones

    Las colecciones son estructuras de datos que te permiten almacenar y manipular múltiples valores. En Soroban, están optimizadas para el entorno blockchain y difieren de las implementaciones estándar de Rust.

    • Vec (soroban_sdk::vec::Vec): Implementación propia de Soroban para vectores dinámicos.

    • Map (soroban_sdk::map::Map): Implementación propia de Soroban para colecciones de pares clave-valor.

    hashtag
    1. Vec en Soroban 🌟

    El Vec en Soroban es una colección dinámica que permite almacenar una secuencia de elementos de un mismo tipo. Al igual que en Rust, su tamaño puede crecer de forma dinámica. En el contexto de Soroban se trabaja con él a través del entorno (env), lo que facilita su integración en contratos inteligentes.


    hashtag
    2. Map en Soroban 🔄

    El Map en Soroban es una colección de pares clave-valor, muy útil para asociar claves únicas a valores, similar a un diccionario. También requiere el entorno (env) para su creación y manipulación, lo que lo hace ideal para el manejo de datos en contratos inteligentes.

    Contratos inteligentes ejemplo:

    Abrimos la consola en la ruta donde deseamos crear el proyecto y ejecutamos.

    Borramos todo el código y ponemos lo siguiente:

    hashtag
    Explicación del contrato

    📌 Estructuras y Tipos del Contrato

    Atributo #![no_std] Indica que el contrato no utiliza la biblioteca estándar de Rust, lo cual es común en entornos embebidos o en contratos inteligentes para reducir dependencias y adaptarse a restricciones de recursos.

    Macros de Contrato #[contract] y #[contractimpl] Estas macros son proporcionadas por el soroban_sdk y se usan para marcar:

    • #[contract]: Define la estructura principal del contrato.

    • #[contractimpl]: Implementa la lógica de negocio del contrato. Para más detalles, consulta la documentación de contratos Soroban en:

    Constantes del Contrato

    • VEC_KEY: Clave (tipo Symbol) para almacenar y recuperar el vector (Vec<String>) en el storage.

    • MAP_KEY: Clave (tipo Symbol) para almacenar y recuperar el mapa (Map<Symbol, String>


    🛠 Funciones del Contrato

    1️⃣ get_vec

    Descripción: Recupera el vector almacenado en el contrato. Si no existe, devuelve un vector vacío.

    Mecanismo:

    • Accede al storage utilizando la clave VEC_KEY.

    • Utiliza unwrap_or para devolver un Vec nuevo en caso de que no se encuentre ningún valor.


    2️⃣ set_vec

    Descripción: Almacena el vector proporcionado en el storage del contrato, usando la clave VEC_KEY.

    Mecanismo:

    • Se guarda el Vec de cadenas en el storage para persistir los cambios.


    3️⃣ add_vec

    Descripción: Agrega un elemento al final del vector y devuelve la nueva longitud del mismo.

    Mecanismo:

    • Recupera el vector actual mediante get_vec.

    • Usa push_back para agregar el valor (tipo String).

    • Actualiza el storage con el vector modificado mediante


    4️⃣ get_vec_element

    Descripción: Obtiene el elemento ubicado en el índice especificado del vector. Devuelve Some(elemento) si existe o None si el índice es inválido.

    Mecanismo:

    • Recupera el vector desde el storage.

    • Usa el método get para obtener el elemento en la posición indicada.


    5️⃣ remove_vec_element

    Descripción: Elimina el elemento en el índice indicado del vector. Retorna Some(()) si la eliminación fue exitosa o None si el índice no es válido.

    Mecanismo:

    • Verifica si el índice es válido utilizando get.

    • Si existe el elemento, se elimina mediante remove.

    • Se actualiza el vector en el storage usando set_vec.


    6️⃣ get_map

    Descripción: Recupera el mapa almacenado en el contrato. Si no existe, devuelve un mapa nuevo y vacío.

    Mecanismo:

    • Accede al storage usando la clave MAP_KEY.

    • Utiliza unwrap_or para retornar un Map vacío si no se encuentra ningún valor.


    7️⃣ set_map

    Descripción: Almacena el mapa proporcionado en el storage del contrato usando la clave MAP_KEY.

    Mecanismo:

    • Se guarda el Map en el storage para persistir los cambios.


    8️⃣ add_map_entry

    Descripción: Agrega una entrada al mapa, utilizando una clave (Symbol) y su valor asociado (String).

    Mecanismo:

    • Recupera el mapa actual desde el storage.

    • Utiliza el método set del Map para agregar la nueva entrada.

    • Actualiza el storage con el mapa modificado.


    9️⃣ get_map_value

    Descripción: Obtiene el valor asociado a la clave especificada en el mapa. Retorna Some(valor) si existe o None si la clave no se encuentra.

    Mecanismo:

    • Recupera el mapa desde el storage.

    • Utiliza get para buscar el valor asociado a la clave dada.


    🔟 remove_map_entry

    Descripción: Elimina la entrada del mapa correspondiente a la clave especificada. Retorna Some(()) si la operación fue exitosa o None si la clave no existía en el mapa.

    Mecanismo:

    • Recupera el mapa actual desde el storage.

    • Utiliza el método remove para eliminar la entrada asociada a la clave.

    • Si se elimina, actualiza el storage con el mapa modificado mediante set_map.


    📌 Resumen General Este contrato inteligente demuestra cómo manejar estructuras de datos dinámicas en Soroban utilizando Vec y Map para gestionar colecciones de elementos de manera persistente en el storage. Permite:

    • Manipulación de Vectores (Vec): Agregar, obtener y eliminar elementos del vector almacenado.

    • Manipulación de Mapas (Map): Agregar, recuperar y eliminar entradas del mapa almacenado.

    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Función add_vec:

    Función get_vec_element:

    Función remove_vec_element:

    Función add_map_entry:

    Función get_map_value:

    Función remove_map_entry:

    get set helloworld

    Esta es la interfaz del contrato MessageContract. La cual es una operación de poner un valor tipo string a un contrato y capturar tambien este valor. el contrato desplegado es el :

    CBLHRYKD6UPJA75PJ5CCTCY6LO4H3ZY7HFW2SS2JP4US5VRYDEO3I67Harrow-up-right

    para realizar los pasos iniciales , es exactamente los mismos pasos iniciales del la dapp Saldo de una dirección en XLM.

    Una vez instalado Next o el entorno favorito, y dejarlo listo, procedemos a abrir la terminal e instalar los siguientes paquetes:

    react-hot-toast:arrow-up-right Sirve para dar mensajes al usuario acerca de alguna operación

    @stellar/stellar-sdk:arrow-up-right Libreria en javascript para interactuar con la red de stellar, en esta ocasión la usaremos para poder convertir nuestros valores de los mensajes al entandar de mensajes de stellar XDRarrow-up-right

    Los amigos de la empresa 🥑, una vez más al rescate, extendiendo la libreria de permitiendo interaractuar con los contratos y la operación de conexión a la billetera de una forma más sencilla ( developer friendly 🤓)

    hashtag
    Archivos a crear o modificar

    StellarWalletProvider.tsx: En la ruta src/app

    Sirve para la configuración inicial de la billetera, red por defecto, el contrato que vamos a usar, esta configuración la vamos a necesitar a través de toda la aplicación

    layout.tsx: En la ruta src/app

    Es nuestro "template" de la aplicación, allí indicamos que toda la aplicación va a tener acceso a los datos de la billetera, billetera conectada, red, servidor al que está conectado, etc...

    deployments.json: En la ruta src/app

    array json donde podemos almacenar los diferentes contratos con que podemos interactuar.

    page.tsx: En la ruta src/app

    Página principal de la aplicación, a resaltar allí la interactividad es nula, ya que está se da en el componente Header y TablaValores .

    📝 A continuación se crea la carpeta components en la ruta src/app

    Header.tsx: En la ruta src/app/components

    Es el encargado de la conexión y desconexión de la billetera que tengamos instalada al navegador de la red de stellar

    TablaValores.tsx: En la ruta src/app/components

    Es el componente estrella de la daap ⭐, sobre todo las funciones:

    • readMessage: Invoca el metodo get_message del contrato

    Algo relevante es que pasamos el mensaje de su

    • writeMessageInvoca el metodo set_message del contrato.

    Pasamos el mensaje de nativo a

    Para ejecutar el programa ejecutamos:

    Todo el proyecto esta en un repositorio

    Tipos de Datos Estructurados

    hashtag
    🧱 Tipos de Datos Estructurados

    Los tipos de datos estructurados son construcciones que te permiten organizar y agrupar datos relacionados bajo una misma entidad. Son fundamentales para modelar conceptos complejos en tus contratos inteligentes.

    • Structs: Estructuras que agrupan campos con nombre y tipos diferentes.

    stellar contract init  logging --name logging
    rustCopiarEditar#![no_std]
    use soroban_sdk::{contract, contractimpl, log, Env, Symbol};
    
    #[contract]
    pub struct Contract;
    
    #[contractimpl]
    impl Contract {
        pub fn hello(env: Env, value: Symbol) {
            log!(&env, "Hello {}", value);
        }
    }
    
    mod test;
    rustCopiarEditarpub fn hello(env: Env, value: Symbol) {
        log!(&env, "Hello {}", value);
    }
    #![cfg(test)]
    
    use super::*;
    use soroban_sdk::{
        symbol_short, testutils::Logs, xdr::Hash, xdr::ScAddress, Address, BytesN, Env, TryFromVal,
    };
    
    extern crate std;
    
    #[test]
    fn test() {
        let env = Env::default();
    
        let id_bytes = BytesN::from_array(&env, &[8; 32]);
    
        let addr: Address =
            Address::try_from_val(&env, &ScAddress::Contract(Hash(id_bytes.to_array()))).unwrap();
        let contract_id = env.register_at(&addr, Contract, ());
        let client = ContractClient::new(&env, &contract_id);
    
        client.hello(&symbol_short!("Dev"));
    
        let logs = env.logs().all();
        assert_eq!(logs, std::vec!["[Diagnostic Event] contract:CAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQCAIBAEAQMCJ, topics:[log], data:[\"Hello {}\", Dev]"]);
        std::println!("{}", logs.join("\n"));
    }
    bashCopiarEditar[Diagnostic Event] contract:<ID_CONTRATO>, topics:[log], data:["Hello {}", Dev]
    contract build
    cargo test -- --nocapture
     npm install react-hot-toast
     npm install @stellar/stellar-sdk
     npm install stellar-react
    ) en el storage.
    set_vec
    .
  • Retorna la longitud del vector actualizado.

  • Escribir Contratos en Sorobanarrow-up-right
    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba
    Ejecución de prueba
    stellar-react:arrow-up-right
    Palta Labsarrow-up-right
    Stellar-wallets-kitsarrow-up-right
    almacenamiento del contrato a un tipo nativoarrow-up-right
    valor XDRarrow-up-right
    githubarrow-up-right
    Programa en ejecución
    use soroban_sdk::{Env, Vec};
    
    pub fn ejemplo_vec(env: &Env) {
        let mut numeros = Vec::new(env); // Crea un Vec vacío
        numeros.push(10);
        numeros.push(20);
        // Puedes registrar el contenido usando el log del entorno
        env.log().info("Contenido del Vec:", numeros);
    }
    use soroban_sdk::{Env, Map};
    
    pub fn ejemplo_map(env: &Env) {
        let mut mi_mapa = Map::new(env); // Crea un Map vacío
        mi_mapa.set(1, "uno");
        mi_mapa.set(2, "dos");
        // Registra el contenido del Map
        env.log().info("Contenido del Map:", mi_mapa);
    }
    stellar contract init collections --name vecmap
    #![no_std]
    use soroban_sdk::{contract, contractimpl, symbol_short, Env, Map, String, Symbol, Vec};
    
    #[contract]
    pub struct VecMapContract;
    
    #[contractimpl]
    impl VecMapContract {
        const VEC_KEY: Symbol = symbol_short!("vec");
        const MAP_KEY: Symbol = symbol_short!("map");
    
        fn get_vec(env: &Env) -> Vec<String> {
            env.storage()
                .instance()
                .get(&Self::VEC_KEY)
                .unwrap_or(Vec::new(&env))
        }
    
        fn set_vec(env: &Env, vec: &Vec<String>) {
            env.storage().instance().set(&Self::VEC_KEY, vec);
        }
    
        // Funciones para manejo de Vec
        pub fn add_vec(env: Env, value: String) -> u32 {
            let mut my_vec: Vec<String> = Self::get_vec(&env);
            my_vec.push_back(value);
            Self::set_vec(&env, &my_vec);
            my_vec.len()
        }
    
        //Obtenemos un elemento del vector
        pub fn get_vec_element(env: Env, index: u32) -> Option<String> {
            let my_vec: Vec<String> = Self::get_vec(&env);
            my_vec.get(index)
        }
    
        pub fn remove_vec_element(env: Env, index: u32) -> Option<()> {
            let mut my_vec = Self::get_vec(&env);
            // Verificamos primero si el índice es válido
            if my_vec.get(index).is_some() {
                let element: Option<()> = my_vec.remove(index);
                Self::set_vec(&env, &my_vec);
                Some(())
            } else {
                None
            }
        }
    
        fn get_map(env: &Env) -> Map<Symbol, String> {
            env.storage()
                .instance()
                .get(&Self::MAP_KEY)
                .unwrap_or(Map::new(env))
        }
    
        fn set_map(env: &Env, map: &Map<Symbol, String>) {
            env.storage().instance().set(&Self::MAP_KEY, map);
        }
    
        // Funciones para manejo de Map
        
        // Agregamos un elemento al map
        pub fn add_map_entry(env: Env, key: Symbol, value: String) {
            let mut my_map = Self::get_map(&env);
            my_map.set(key, value);
            Self::set_map(&env, &my_map);
        }
        //Obtener algun valor del map
        pub fn get_map_value(env: Env, key: Symbol) -> Option<String> {
            let my_map = Self::get_map(&env);
            my_map.get(key)
        }
    
        // Eliminar un elemento del map
        pub fn remove_map_entry(env: Env, key: Symbol) -> Option<()> {
            let env_ref = &env;
            let mut my_map = Self::get_map(env_ref);
            if my_map.remove(key).is_some() {
                Self::set_map(&env, &my_map);
                Some(()) // Éxito
            } else {
                None     // La clave no existía
            }
        }
    }
    fn get_vec(env: &Env) -> Vec<String> {
        env.storage()
            .instance()
            .get(&Self::VEC_KEY)
            .unwrap_or(Vec::new(&env))
    }
    fn set_vec(env: &Env, vec: &Vec<String>) {
        env.storage().instance().set(&Self::VEC_KEY, vec);
    }
    pub fn add_vec(env: Env, value: String) -> u32 {
        let mut my_vec: Vec<String> = Self::get_vec(&env);
        my_vec.push_back(value);
        Self::set_vec(&env, &my_vec);
        my_vec.len()
    }
    pub fn get_vec_element(env: Env, index: u32) -> Option<String> {
        let my_vec: Vec<String> = Self::get_vec(&env);
        my_vec.get(index)
    }
    pub fn remove_vec_element(env: Env, index: u32) -> Option<()> {
        let mut my_vec = Self::get_vec(&env);
        if my_vec.get(index).is_some() {
            let element: Option<()> = my_vec.remove(index);
            Self::set_vec(&env, &my_vec);
            Some(())
        } else {
            None
        }
    }
    fn get_map(env: &Env) -> Map<Symbol, String> {
        env.storage()
            .instance()
            .get(&Self::MAP_KEY)
            .unwrap_or(Map::new(env))
    }
    fn set_map(env: &Env, map: &Map<Symbol, String>) {
        env.storage().instance().set(&Self::MAP_KEY, map);
    }
    pub fn add_map_entry(env: Env, key: Symbol, value: String) {
        let mut my_map = Self::get_map(&env);
        my_map.set(key, value);
        Self::set_map(&env, &my_map);
    }
    pub fn get_map_value(env: Env, key: Symbol) -> Option<String> {
        let my_map = Self::get_map(&env);
        my_map.get(key)
    }
    pub fn remove_map_entry(env: Env, key: Symbol) -> Option<()> {
        let env_ref = &env;
        let mut my_map = Self::get_map(env_ref);
        if my_map.remove(key).is_some() {
            Self::set_map(&env, &my_map);
            Some(())
        } else {
            None
        }
    }
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/vecmap.wasm *
      --source <Identity> *
      --network testnet *
      --alias option
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    add_vec *
    --value "Hello 🌎"
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    get_vec_element *
    --index 0
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    remove_vec_element *
    --index 0
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    add_map_entry *
    --key "key1" *
    --value "hola que más?"
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    get_map_value *
    --key "key1"
    stellar contract invoke *
    --id <CONTRACT_ID> *
    --source <Identity> *
    --network testnet *
    -- *
    remove_map_entry *
    --key "key1"
    // Este archivo define un Provider personalizado para conectar una aplicación Next.js con la red de Stellar usando Soroban React.
    
    'use client'; // Indica que este componente se ejecuta del lado del cliente en Next.js
    import { ReactNode } from "react";
    import { NetworkDetails, SorobanReactProvider, WalletNetwork } from "stellar-react";
    import deployments from './deployments.json'; // Importa la configuración de despliegue de contratos
    
    // Componente Provider que envuelve la aplicación y provee acceso a la red de Stellar
    const StellarWalletProvider = ({ children }: { children: ReactNode }) => {
    
        // Definimos los detalles de la red de prueba (testnet) de Stellar
        // Puedes cambiar estos valores para conectar a otra red si lo necesitas
        const testnetNetworkDetails: NetworkDetails = {
            network: WalletNetwork.TESTNET, // Selecciona la red de prueba de Stellar
            sorobanRpcUrl: 'https://soroban-testnet.stellar.org/', // URL del nodo Soroban para testnet
            horizonRpcUrl: 'https://horizon-testnet.stellar.org' // URL de Horizon para testnet
        }
        return (
            // SorobanReactProvider provee el contexto de Stellar a toda la app
            <SorobanReactProvider
                appName={"Hello World Stellar App"} // Nombre de la app que se muestra en la wallet
                allowedNetworkDetails={[testnetNetworkDetails]} // Lista de redes permitidas (aquí solo testnet)
                activeNetwork={WalletNetwork.TESTNET} // Red activa por defecto
                deployments={deployments} // Información de despliegue de contratos inteligentes
            >
                {/* children representa los componentes hijos que tendrán acceso al contexto de Stellar */}
                {children}
            </SorobanReactProvider>
        )
    }
    // Exportamos el Provider para usarlo en el layout principal de la app
    export default StellarWalletProvider;
    // Este archivo define el layout principal de la aplicación Next.js.
    // Aquí se configuran los estilos globales, fuentes, providers y la estructura base de la app.
    
    
    import type { Metadata } from "next"; // Importa el tipo Metadata para definir metadatos de la página
    import { Geist, Geist_Mono } from "next/font/google"; // Importa fuentes de Google para mejorar la apariencia
    import "./globals.css"; // Importa los estilos globales de la aplicación
    import StellarWalletProvider from "./StellarWalletProvider"; // Importa el provider personalizado para conectar con la red de Stellar
    import { Toaster } from "react-hot-toast"; // Importa el componente para mostrar notificaciones tipo toast
    
    // Configuración de la fuente sans-serif personalizada usando Geist
    const geistSans = Geist({
      variable: "--font-geist-sans",
      subsets: ["latin"],
    });
    
    // Configuración de la fuente monoespaciada personalizada usando Geist Mono
    const geistMono = Geist_Mono({
      variable: "--font-geist-mono",
      subsets: ["latin"],
    });
    
    // Definición de los metadatos de la aplicación (título y descripción)
    export const metadata: Metadata = {
      title: "Get set messaje",
      description: "get y set de una variable de contratos",
    };
    
    // Componente principal del layout
    // children representa el contenido de cada página que se renderiza dentro del layout
    export default function RootLayout({
      children,
    }: Readonly<{
      children: React.ReactNode;
    }>) {
      return (
        <html lang="en">
          <body
            className={`${geistSans.variable} ${geistMono.variable} antialiased`}
          >
            {/*
              StellarWalletProvider envuelve toda la aplicación y provee el contexto de conexión a la red de Stellar.
              Esto permite que cualquier componente hijo pueda interactuar con la blockchain de Stellar fácilmente.
            */}
            <StellarWalletProvider>{children}</StellarWalletProvider>
            {/*
              Toaster se utiliza para mostrar notificaciones emergentes (toast) en la interfaz.
              El div con id="toast-root" es el contenedor donde se renderizan estos mensajes.
            */}
            <div id="toast-root"><Toaster/></div>
          </body>
        </html>
      );
    }
    [
    {
        "contractId": "hello_world",
        "networkPassphrase": "Test SDF Network ; September 2015",
        "contractAddress": "CBLHRYKD6UPJA75PJ5CCTCY6LO4H3ZY7HFW2SS2JP4US5VRYDEO3I67H"
      }
    ]
    // Este archivo define la página principal de la aplicación, integrando los componentes de encabezado y tabla de valores.
    import Header from "./components/Header";
    import TablaValores from "./components/TablaValores";
    import React from "react";
    
    // Componente principal de la página Home
    export default function Home() {
      return (
        // Estructura principal de la página con estilos y disposición
        <main className="min-h-screen bg-black flex flex-col items-center justify-start">
          {/* Componente de encabezado que gestiona la conexión de la billetera */}
          <Header />
          <section className="w-full max-w-2xl mt-10 px-4">
            <div className="bg-gray-900 bg-opacity-80 rounded-xl shadow-[0_4px_24px_0_rgba(255,255,255,0.15)] border border-gray-700 p-8">
              <h2 className="text-3xl font-bold text-white mb-2 text-center">Get Set Message Stellar Español</h2>
              <p className="text-gray-300 text-center mb-8">
                Esta aplicación permite recuperar y establecer un valor tipo string en un contrato inteligente sobre la red Stellar. 
              </p>
              <p className="text-gray-400 text-center mb-8 font-mono">
                Contrato: CBLHRYKD6UPJA75PJ5CCTCY6LO4H3ZY7HFW2SS2JP4US5VRYDEO3I67H
              </p>
              {/* Componente que muestra y permite actualizar el valor del contrato */}
              <TablaValores />
            </div>
          </section>
        </main>
      );
    }
    // Este componente Header muestra el título de la aplicación y permite conectar o desconectar la billetera Stellar.
    'use client';
    import React, { useState } from "react";
    import { useSorobanReact } from "stellar-react";
    
    // Componente principal del encabezado
    const Header: React.FC = () => {
      // Extraemos la dirección de la billetera, y las funciones para conectar y desconectar usando el hook de Soroban React
      const { address, connect, disconnect } = useSorobanReact();
      return (
        // Encabezado visual con estilos y título
        <header className="w-full flex justify-between items-center px-6 py-4 bg-black bg-opacity-80 border-b border-gray-800">
          <h1 className="text-2xl font-bold text-white">Stellar Wallet</h1>
          {/* Si no hay dirección, mostramos el botón para conectar la billetera */}
          {!address ? (
            <button
              className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded transition"
              onClick={connect}
            >
              Conectar billetera
            </button>
          // Si hay dirección, mostramos la dirección y el botón para desconectar */}
          ) : (
            <div className="flex items-center gap-4">
              {/* Mostramos la dirección de la billetera conectada */}
              <span className="text-gray-300 text-sm">{address}</span>
              <button
                className="bg-blue-400 hover:bg-blue-500 text-white px-4 py-2 rounded transition shadow-lg shadow-white/30"
                onClick={disconnect}
              >
                Desconectar
              </button>
            </div>
          )}
        </header>
      );
    };
    
    // Exportamos el componente para su uso en otras partes de la app
    export default Header;
     // Obtenemos la dirección del contrato y llamamos al método get_message
          const addr = contract?.deploymentInfo?.contractAddress;
          const result = await contract?.invoke({ method: "get_message", args: [] });
          setMessage(scValToNative(result as xdr.ScVal) as string)
    // Llamamos al método set_message del contrato, firmando la transacción
          const result = await contract?.invoke({
            method: "set_message",
            args: [nativeToScVal(newMessage, { type: "string" })],
            signAndSend: true,
            reconnectAfterTx: false
          });
    // Este componente permite leer y escribir un mensaje en un contrato inteligente de Stellar.
    'use client';
    import { nativeToScVal, scValToNative, xdr } from "@stellar/stellar-sdk";
    import React, { useEffect, useState } from "react";
    import toast from "react-hot-toast";
    import { useRegisteredContract, useSorobanReact } from "stellar-react";
    
    // Componente principal de la tabla de valores
    const TablaValores: React.FC = () => {
      // Obtenemos información de la red, el servidor y la dirección de la billetera
      const { activeNetwork, sorobanServer, address } = useSorobanReact();
      // Obtenemos el contrato registrado con el nombre "hello_world"
      const contract = useRegisteredContract("hello_world");
      // Estado para el mensaje actual y el nuevo mensaje a enviar
      const [message, setMessage] = useState("");
      const [newMessage, setNewMessage] = useState("");
    
      // Función para leer el mensaje almacenado en el contrato inteligente
      const readMessage = async () => {
        if (!contract) {
          return;
        }
        try {
          // Obtenemos la dirección del contrato y llamamos al método get_message
          const addr = contract?.deploymentInfo?.contractAddress;
          const result = await contract?.invoke({ method: "get_message", args: [] });
          setMessage(scValToNative(result as xdr.ScVal) as string)
        }
        catch (error) {
          console.log(error);
        }
      }
      // Función para escribir un nuevo mensaje en el contrato inteligente
      const writeMessage = async () => {
        // Validamos que la billetera esté conectada
        if (address === undefined) {
          toast.error("Wallet no conectada");
          return;
        }
        // Validamos que el campo no esté vacío
        if (newMessage === "") {
          toast.error("Introduce un mensaje");
          return;
        }
        try {
          // Llamamos al método set_message del contrato, firmando la transacción
          const result = await contract?.invoke({
            method: "set_message",
            args: [nativeToScVal(newMessage, { type: "string" })],
            signAndSend: true,
            reconnectAfterTx: false
          });
          if (result) {
            toast.success("mensaje actualizado");
            readMessage(); // Actualizamos el mensaje mostrado
          } else {
            toast.error("Actuliazacion fallida");
          }
        } catch (error) {
          if (error instanceof Error) {
            console.log(`Error: ${error.message}`);
          }
          else {
            console.log('ocurrio un error desconocido');
          }
        }
      }
      
      // Hook useEffect para leer el mensaje cada vez que cambia el servidor o el contrato
      useEffect(() => {
        readMessage();
      }, [sorobanServer, contract]);
      return (
        // Tabla visual para mostrar y actualizar el valor del contrato
        <div className="w-full max-w-md mx-auto mt-8 bg-gray-900 bg-opacity-80 rounded-lg shadow-lg p-6">
          <table className="w-full text-white border-separate border-spacing-y-4">
            <tbody>
              <tr>
                <td className="font-semibold">Valor actual</td>
                <td className="bg-gray-800 rounded px-4 py-2 text-center">{message}</td>
              </tr>
              <tr>
                <td className="font-semibold">Valor nuevo</td>
                <td>
                  <input
                    type="text"
                    className="w-full px-3 py-2 rounded bg-gray-700 text-white focus:outline-none focus:ring-2 focus:ring-blue-600"
                    value={newMessage}
                    onChange={e => setNewMessage(e.target.value)}
                    placeholder="Introduce un nuevo valor"
                  />
                </td>
              </tr>
              <tr>
                <td></td>
                <td>
                  <button
                    className="w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded transition mt-2"
                    onClick={writeMessage}
                  >
                    Enviar
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      );
    };
    
    // Exportamos el componente para su uso en otras partes de la app
    export default TablaValores;
    npm run dev

    Enums: Tipos que pueden contener uno de varios valores posibles predefinidos.

  • Tuplas: Colecciones ordenadas y de tamaño fijo que pueden contener elementos de diferentes tipos.

  • Arrays: Colecciones de elementos del mismo tipo con longitud fija.

  • hashtag
    1. Structs 🏗️

    Las structs en Rust te permiten agrupar campos con nombres y tipos diferentes. Es como crear tu propio "molde" para agrupar datos relacionados.


    hashtag
    2. Enums 🎨

    Los enums definen un tipo que puede ser uno de varios valores predefinidos. Son muy útiles para representar estados o variantes.


    hashtag
    3. Tuplas 🔗

    Las tuplas son colecciones ordenadas de tamaño fijo que pueden contener elementos de diferentes tipos. ¡Son perfectas para agrupar datos relacionados sin necesidad de nombres!


    hashtag
    4. Arrays 📚

    Los arrays son colecciones de elementos del mismo tipo con una longitud fija. Se usan cuando conoces la cantidad exacta de elementos y estos son del mismo tipo.

    Contratos inteligentes ejemplo:

    En esta ocasión vamos a crear un contrato independiente por cada tipo de dato de la siguiente manera.

    Abrimos la consola en la ruta donde deseamos crear el proyecto y ejecutamos.

    Borramos todo el código y ponemos lo siguiente:

    hashtag
    Explicación del contrato

    hashtag
    📌 Estructuras y Tipos del Contrato

    hashtag
    1. Atributo #![no_std]

    • Indica que el contrato no utiliza la biblioteca estándar de Rust, lo que es común en entornos embebidos o en contratos inteligentes para reducir dependencias y adaptarse a restricciones de recursos.

    hashtag
    2. Macros de Contrato

    • #[contract], #[contractimpl] y #[contracttype] Estas macros son proporcionadas por el soroban_sdk y se usan para marcar:

      • #[contract]: Define la estructura principal del contrato.

      • #[contractimpl]: Implementa la lógica de negocio del contrato.

      • #[contracttype]: Declara tipos (enums o structs) que se utilizarán en la interfaz del contrato.

    hashtag
    3. Enum TaskStatus

    • Define los posibles estados de una tarea:

      • Open (valor 0)

      • InProgress (valor 1)

      • Completed (valor 2)

    • Se deriva de Clone, Debug, Eq y PartialEq para facilitar la clonación, la depuración y la comparación de valores.

    hashtag
    4. Struct Task

    • Representa una tarea y contiene:

      • id: u32: Identificador único de la tarea.

      • description: String: Descripción de la tarea.

      • status: TaskStatus: Estado actual de la tarea.

      • assignee: Address: Dirección del usuario asignado a la tarea.

    • Address es un tipo que representa direcciones de cuentas o contratos en Soroban.

    hashtag
    5. Tipos del SDK

    • Env: Proporciona el entorno de ejecución del contrato, permitiendo el acceso a almacenamiento, logging y otras funciones. Consulta Env en la documentación de Soroban.arrow-up-right

    • String: Tipo de cadena adaptado a entornos no_std que se utiliza para manejar textos en el contrato. Más información en String en Soroban.arrow-up-right


    hashtag
    🛠 Funciones del Contrato

    hashtag
    1. find_fruit

    • Descripción: Busca en un arreglo de 5 frutas (como cadenas) la que coincida con la cadena de entrada fruit y devuelve su posición (índice).

    • Mecanismo:

      • Se crea un arreglo estático de frutas.

      • Se recorre el arreglo utilizando enumerate(), que proporciona el índice y el elemento.

      • Si se encuentra una coincidencia (usando if), se retorna el índice.

      • Si no hay coincidencia, se retorna -1.

    hashtag
    2. create_task

    • Descripción: Crea una nueva tarea con un identificador, descripción y usuario asignado. El estado inicial de la tarea se establece en TaskStatus::Open.

    • Mecanismo:

      • Se construye una instancia de la estructura Task con los valores proporcionados.

      • Se asigna el estado inicial de la tarea como abierta.

    hashtag
    3. get_info

    • Descripción: Devuelve una tupla que contiene un mensaje y un número entero.

    • Mecanismo:

      • Se crea una cadena "Ejemplo Simple".

      • Se retorna junto a un entero fijo 123.

    hashtag
    4. get_status_description

    • Descripción: Devuelve una cadena descriptiva según el estado de una tarea.

    • Mecanismo:

      • Se utiliza la estructura de control match para evaluar el valor del TaskStatus.

      • Según el caso (Open, InProgress, Completed), se retorna la cadena correspondiente: "Abierta", "En Progreso" o "Completada".

    • Contexto Teórico del match: En Rust, match es similar a la instrucción switch de otros lenguajes, pero es más poderoso, permitiendo comparar contra patrones y asegurando que todos los casos sean tratados o manejados mediante un caso por defecto. Esto proporciona una forma segura y expresiva de controlar el flujo del programa.


    hashtag
    📌 Resumen General

    Este contrato inteligente demuestra:

    • Definición de Tipos Personalizados: Con el enum TaskStatus y la estructura Task, se modelan estados y tareas para un posible sistema de gestión.

    • Búsqueda en Arreglos: La función find_fruit muestra cómo recorrer un arreglo de cadenas y buscar un elemento.

    • Creación y Manejo de Estructuras: La función create_task crea una tarea inicializada con un estado predeterminado.

    • Uso de Tuplas y match: La función get_info devuelve información empaquetada en una tupla y get_status_description utiliza match para retornar descripciones basadas en el estado.

    Compilación del contrato

    Ejecutamos lo siguiente:

    Despliegue del contrato

    Para Mac y Linux el salto de línea es con el carácter " \" y en Windows con el carácter " ´ "

    Reemplaze el simbolo * por el respectivo carácter de salto de linea a su sistema operativo.

    Ejecución de prueba

    Pruebas del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    función find_fruit

    Ejecución de prueba

    Función create_task

    Ejecución de ejemplo

    Función get_info

    Ejecución de prueba

    Función get_status_description

    Ejecución de prueba
    rustCopiarEditarstruct Persona {
        nombre: String,
        edad: u32,
    }
    
    fn main() {
        let persona = Persona {
            nombre: String::from("Juan"),
            edad: 30,
        };
        println!("{} tiene {} años 👤", persona.nombre, persona.edad);
    }
    rustCopiarEditarenum Estado {
        Activo,
        Inactivo,
        Desconocido,
    }
    
    fn main() {
        let estado = Estado::Activo;
        match estado {
            Estado::Activo => println!("¡Está activo! 😊"),
            Estado::Inactivo => println!("Está inactivo. 😴"),
            Estado::Desconocido => println!("Estado desconocido. 🤔"),
        }
    }
    rustCopiarEditarfn main() {
        let persona: (&str, u32) = ("Ana", 25);
        println!("{} tiene {} años 👩", persona.0, persona.1);
    }
    rustCopiarEditarfn main() {
        let numeros: [i32; 5] = [1, 2, 3, 4, 5];
        println!("El primer número es: {} ", numeros[0]);
    }
    stellar contract init structured_data_types --name structured_data_types
    #![no_std]
    use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String, };
    
    #[contracttype]
    #[derive(Clone, Debug, Eq, PartialEq)]
    pub enum TaskStatus {
        Open = 0,
        InProgress = 1,
        Completed = 2,
    }
    
    #[contracttype]
    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct Task {
        pub id: u32,
        pub description: String,
        pub status: TaskStatus,
        pub assignee: Address,
    }
    
    #[contract]
    pub struct SimpleContract;
    #[contractimpl]
    impl SimpleContract {
        // Función que busca una fruta en un array y devuelve su posición o -1 si no la encuentra.
        pub fn find_fruit(env: Env,fruit: String) -> i32 {
            let fruits: [String; 5] = [
                String::from_str(&env,"manzana"),
                String::from_str(&env,"banana"),
                String::from_str(&env,"naranja"),
                String::from_str(&env,"uva"),
                String::from_str(&env,"fresa"),
            ];
    
            for (index, f) in fruits.iter().enumerate() {
                if f == &fruit {
                    // Compara la fruta dada con cada elemento del array.
                    return index as i32; // Devuelve la posición (índice) si la encuentra.
                }
            }
    
            -1 // Devuelve -1 si la fruta no está en el array.
        }
    
     
        // Función que crea una tarea y devuelve el struct
       pub fn create_task(env: Env, id: u32, description: String, assignee: Address) -> Task {
            Task {
                id,
                description,
                status: TaskStatus::Open,
                assignee,
            }
        }
     
         // Función que devuelve una tupla con información
        pub fn get_info(env: Env) -> (String, i32) {
            (String::from_str(&env,"Ejemplo Simple"), 123)
        }
    
        // Función que devuelve la descripción del estado de la tarea
        pub fn get_status_description(env: Env,status: TaskStatus) -> String {
            match status {
                TaskStatus::Open => String::from_str(&env,"Abierta"),
                TaskStatus::InProgress => String::from_str(&env,"En Progreso"),
                TaskStatus::Completed => String::from_str(&env,"Completada"),
            }
        }
    }
    pub fn find_fruit(env: Env, fruit: String) -> i32 {
        let fruits: [String; 5] = [
            String::from_str(&env,"manzana"),
            String::from_str(&env,"banana"),
            String::from_str(&env,"naranja"),
            String::from_str(&env,"uva"),
            String::from_str(&env,"fresa"),
        ];
    
        for (index, f) in fruits.iter().enumerate() {
            if f == &fruit {
                // Si encuentra la fruta, retorna el índice convertido a i32.
                return index as i32;
            }
        }
        // Si no se encuentra, retorna -1.
        -1
    }
    pub fn create_task(env: Env, id: u32, description: String, assignee: Address) -> Task {
        Task {
            id,
            description,
            status: TaskStatus::Open,
            assignee,
        }
    }
    pub fn get_info(env: Env) -> (String, i32) {
        (String::from_str(&env,"Ejemplo Simple"), 123)
    }
    rustCopiarEditarpub fn get_status_description(env: Env, status: TaskStatus) -> String {
        match status {
            TaskStatus::Open => String::from_str(&env,"Abierta"),
            TaskStatus::InProgress => String::from_str(&env,"En Progreso"),
            TaskStatus::Completed => String::from_str(&env,"Completada"),
        }
    }
    Stellar contract build
    stellar contract deploy *
      --wasm target/wasm32-unknown-unknown/release/structured_data_types.wasm *
      --source developer *
      --network <Identity> *
      --alias structured_data_types
    stellar contract invoke `
    --id <CONTRACT_ID> *
    --source <Identity> `
    --network testnet `
    -- <Identity>`
    find_fruit `
    --fruit "manzana"
    stellar contract invoke *
      --id <CONTRACT_ID> *
      --source<Identity> *
      --network testnet *
      -- *
      create_task *
    --id 1 *
    --description "Tarea de ejemplo" *
    --assignee <Stellar address>"
    stellar contract invoke `
      --id <CONTRACT_ID> *
      --source <identity> `
      --network testnet `
      -- `
      get_info
    stellar contract invoke `
      --id <CONTRACT_ID> `
      --source <identity> `
      --network testnet `
      --  `
    get_status_description  `
    --status 0
    Para más detalles, consulta la documentación de contratos Soroban.arrow-up-right
    Más información en la documentación de Address.arrow-up-right
    Stellar CLI Manual | Stellar Docsdevelopers.stellar.orgchevron-right
    Logo

    NFT

    hashtag
    ¿Qué es un Token No Fungible (NFT)?

    hashtag
    🌟 Conceptos Básicos

    Un Token No Fungible (NFT) es como un certificado digital único e irrepetible. Imagínate que tienes una obra de arte física: no puedes dividirla en pedacitos y cada una es única. Los NFTs funcionan igual pero en el mundo digital.

    hashtag
    🤔 ¿Qué significa "No Fungible"?

    • 💰 Fungible: Como el dinero. Un billete de $1000 es igual a otro billete de $1000, son intercambiables.

    • 🎨 No Fungible: Como una pintura original. La Mona Lisa es única, no puedes cambiarla por otra pintura y que valga exactamente lo mismo.

    hashtag
    🌟 ¿Por Qué son Importantes los NFTs?

    Los NFTs permiten:

    • ✅ Propiedad digital verificable: Puedes demostrar que algo digital es tuyo

    • 🎨 Arte digital: Artistas pueden vender obras digitales únicas

    • 🎮 Gaming: Objetos únicos en videojuegos

    hashtag
    📚 Diferencias clave con Tokens Fungibles

    Tokens Fungibles 🪙
    NFTs 🎨

    hashtag
    🏗️ Estructura de un NFT - Los Metadatos

    hashtag
    🧠 ¿Qué significa que un NFT use un "archivo JSON en lugar de la ubicación directa de la imagen"?

    Cuando creas un NFT, es común pensar que la imagen (como un JPG o PNG) se guarda directamente en la blockchain, pero eso no es así. Por razones de costo y eficiencia, no se suben archivos pesados a la blockchain, sino que se usa un sistema de doble enlace (también llamado "two-step metadata linking").

    🔗 ¿Qué es el sistema de doble enlace?

    Es una estructura donde el contrato del NFT no apunta directamente a la imagen, sino a un archivo .json que contiene los metadatos del NFT. Ese archivo .json, a su vez, contiene un enlace a la imagen u otros recursos.

    Veámoslo como una cadena:

    📁 ¿Por qué no enlazar directamente la imagen desde el contrato?

    Porque al enlazar a un archivo .json, tienes varias ventajas importantes:

    1. Puedes incluir mucha más información, no solo la imagen.

    2. Es más flexible: si luego quieres mostrar un video, animación o cambiar la descripción, lo puedes hacer sin tocar el contrato.

    3. Es más económico y limpio: solo apuntas a un archivo liviano con metadatos.


    hashtag
    📄 Ejemplo claro de un archivo metadata.json

    Este archivo puede estar alojado en IPFS u otro sistema descentralizado, y contiene:

    • name: El nombre visible del NFT.

    • description: Una breve descripción o historia.

    • url: El enlace real a la imagen del NFT.


    hashtag
    ✅ Ventajas clave de esta arquitectura

    Beneficio
    Descripción

    Para este ejemplo usamos el servicio de , primero se sube la imagen como tal, para tener su CID , ese lo incluimos en el archivo de metadata.json y luego subimos el archivo metadata.json

    hashtag
    🧙‍♂️ OpenZeppelin Wizard para Stellar

    OpenZeppelin ha creado un wizard (asistente) súper útil para generar tokens SEP-41: 🔗

    🌟 ¿Qué hace el wizard por ti?

    • 🎨 Interfaz visual: Sin necesidad de escribir código desde cero

    • ⚙️ Configuración simple: Solo selecciona las funciones que necesitas

    • 📋 Código generado: Listo para usar en minutos

    hashtag
    ⚠️ Importante: Limitaciones Actuales

    Aunque el wizard es genial para empezar, todavía tiene algunos errores 🐛. Por eso, es mejor usar los ejemplos oficiales como referencia:

    🔗


    Instructivo para poder utilizar el código de OpenZeppelin

    hashtag
    Pre-requisitos

    1. Tener instalado Rust.

    2. Tener instalado El cliente Stellar

    3. tener instalado el cliente de GIt

    • Copiar el repositorio de github de OpenZeppelin en una carpeta previamente seleccionada con la siguiente instrucción.

    • Con el editor favorito de código abrimos el folder stellar-contracts.

    • En el directorio raiz abrir el archivo Cargo.toml

    Para el ejemplo vamos a crear una subcarpeta dentro de examples

    hashtag
    Ejemplo :NFT con consecutivo.

    Una vez creado el contrato del NFT como tal , se hace el proceso de acuñación del NFT se genera un consecutivo.

    Primero se copia la carpeta nft-consecutive

    Copiamos la carpeta y la nueva carpeta lo nombramos como mynft

    Dentro de mynft editados el archivo Cargo.toml

    editamos el archivo.

    Antes de proceder con el código añadimos la ruta de mynft dentro del archivo Cargo.toml en el directorio raiz

    Escribimos lo siguiente dentro de lib.rs

    Código de contract.rs

    hashtag
    🖼️ Explicación del Contrato mynft (NFT Consecutivo)

    hashtag
    ¿Qué es este contrato?

    Este contrato crea un token no fungible (NFT) en la blockchain de Stellar, usando el módulo Consecutive, que permite crear muchos NFTs de forma eficiente en una sola transacción. Además, los NFTs pueden quemarse (eliminarlos para siempre).

    🧰 1. Importaciones: Herramientas necesarias

    hashtag
    ¿Qué significa?

    • soroban_sdk: Caja de herramientas básica para contratos Soroban.

    • stellar_non_fungible: Librería oficial de Stellar para trabajar con NFTs.

      • burnable

    🧱 2. Definición del contrato

    hashtag
    Para qué sirve esto?

    • Define una clave personalizada (Owner) para guardar el dueño del contrato en el almacenamiento interno del contrato.

    • Es útil para que solo ese dueño pueda ejecutar ciertas funciones como "mint".

    hashtag
    🧱 3. Declaración del contrato

    • mynft es el nombre del contrato.

    • Con #[contract] le decimos a Soroban que esto es un contrato inteligente.


    hashtag
    🏗️ 4. Constructor (__constructor)

    hashtag
    ¿Qué hace?

    1. Guarda el dueño del contrato (quien puede mintear NFTs).

    2. Define la metadata básica:

      • "My NFT": nombre del token.

    hashtag
    🧯 5. Función batch_mint (minteo en lote)

    hashtag
    ¿Qué hace?

    • Solo el dueño original del contrato puede mintear NFTs.

    • Permite crear varios NFTs (según el amount) de forma eficiente.

    • Usa la función batch_mint del módulo Consecutive

    💡 Ejemplo: Si llamas batch_mint(to, 5), se crean 5 NFTs y se asignan al usuario to.

    hashtag
    🧾 6. Implementación estándar de NonFungibleToken

    Esta parte define todas las funciones necesarias para que el NFT funcione según los estándares:

    hashtag
    ¿Qué funciones se incluyen?

    🔎 Consultas:

    • balance(): ¿Cuántos NFTs tiene un usuario?

    • owner_of(token_id): ¿Quién es el dueño de un NFT específico?

    • name(), symbol()

    🔄 Transferencias:

    • transfer(): Transferir un NFT.

    • transfer_from(): Transferencia con aprobación previa.

    • approve(), approve_for_all()


    hashtag
    🔥 7. Funciones para quemar NFTs (Burnable)

    hashtag
    ¿Qué hacen?

    • burn(from, token_id): El dueño puede destruir su NFT.

    • burn_from(spender, from, token_id): Un tercero autorizado puede quemar un NFT en nombre del dueño.

    💡 Esto sirve para eliminar NFTs obsoletos o por decisión del usuario.


    hashtag
    ✅ 8. Consecutive extension

    Esto indica que el contrato utiliza el módulo Consecutive, que permite:

    • Mintear muchos NFTs seguidos.

    • Optimizar almacenamiento y eficiencia.


    hashtag
    🧠 Analogía general

    • Piensa en este contrato como una fábrica de obras de arte digitales que puede crear muchas piezas a la vez, entregarlas a quien tú quieras, y también destruirlas si ya no quieres que existan.

    • Todo está bajo el control del creador original (owner).

    hashtag
    Compilación del contrato

    Nos ubicamos dentro de ../stellar-contracts/examples/mynft allí en consola ejecutamos:

    hashtag
    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    stellar contract deploy * --wasm ../../target/wasm32v1-none/release/mynft.wasm * --source developer * --network testnet * --alias MyNft * -- * --owner <owner_address>

    hashtag
    Haciendo un mint con una cuenta no valida

    hashtag
    Haciendo un mint con una cuenta valida

    Obtenemos el primer consecutivo que es el 0 y ya tenemos nuestro NFT 😃

    📜 Certificados: Diplomas, licencias, etc.

    issuer: La dirección del creador en Stellar.

  • code: El símbolo o identificador del NFT o colección.

  • 🔧 Personalizable: Agrega tus propias funciones después
    : Permite destruir (quemar) NFTs.
  • consecutive: Permite crear muchos NFTs de forma consecutiva (por lotes).

  • Base, NonFungibleToken: Funciones estándar para manejar NFTs.

  • "NFTKN": símbolo del token.

  • "CID": Dirección de contenido (por ejemplo, un enlace IPFS con más datos visuales o técnicos del NFT).

  • .
    ,
    token_uri(token_id)
    : Metadata del NFT.
    : Dar permisos a otros.
  • get_approved(), is_approved_for_all(): Consultar permisos.

  • Todos son iguales

    Cada uno es único

    Se pueden dividir

    No se pueden dividir

    Ejemplo: 1 USDC = 1 USDC

    Cada NFT tiene su propio ID

    Escalabilidad

    Puedes agregar más campos al .json en el futuro sin modificar el contrato.

    Eficiencia

    El contrato solo guarda una línea (el enlace al .json), ahorrando espacio.

    Flexibilidad

    Puedes usar imágenes, videos, animaciones, incluso archivos 3D.

    Compatibilidad

    Los marketplaces ya saben cómo leer estos archivos .json.

    https://pinata.cloud/arrow-up-right
    https://wizard.openzeppelin.com/stellar#arrow-up-right
    https://github.com/OpenZeppelin/stellar-contracts/arrow-up-right
    Vista breve del archivo Cargo.toml
    scssCopiarEditarContrato NFT
         ↓ (apunta a)
    Archivo JSON (metadatos)
         ↓ (apunta a)
    Imagen real (JPG, GIF, video, etc.)
    jsonCopiarEditar{
      "name": "My NF Token",
      "description": "Cat under Stellar moon",
      "url": "ipfs://bafkreidfpskdnx45xzzskx5jszsolrw3wfi7hsqkxkpynvhdz6mxs4ck3q",
      "issuer": "GATTQ6RGZSL3BJG6TMLEENNFHCCUHDZGOJYJ2AMWCJD4H3IEI3CCDESB",
      "code": "MNFT"
    }
    git clone https://github.com/OpenZeppelin/stellar-contracts.git
    [package]
    name = "mynft"
    edition.workspace = true
    license.workspace = true
    repository.workspace = true
    publish = false
    version.workspace = true
    
    [lib]
    crate-type = ["cdylib"]
    doctest = false
    
    [dependencies]
    soroban-sdk = { workspace = true }
    stellar-non-fungible = { workspace = true }
    
    [dev-dependencies]
    soroban-sdk = { workspace = true, features = ["testutils"] }
    #![no_std]
    pub mod contract;
    //! Non-Fungible Consecutive Example Contract.
    //!
    //! Demonstrates an example usage of the Consecutive extension, enabling
    //! efficient batch minting in a single transaction.
    
    use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
    use stellar_non_fungible::{
        burnable::NonFungibleBurnable,
        consecutive::{Consecutive, NonFungibleConsecutive},
        Base, ContractOverrides, NonFungibleToken,
    };
    
    #[contracttype]
    pub enum DataKey {
        Owner,
    }
    
    #[contract]
    pub struct Mynft;
    
    #[contractimpl]
    impl Mynft {
        pub fn __constructor(e: &Env, owner: Address) {
            e.storage().instance().set(&DataKey::Owner, &owner);
            Base::set_metadata(
                e,
                String::from_str(e, "bafkreiherjgvtailnmiddilp5go7kjt3vgduzxp5k2yujs3uvjfvmgck5e"),
                String::from_str(e, "My NFT"),
                String::from_str(e, "NFTKN"),
            );
        }
    
        pub fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {
            let owner: Address =
                e.storage().instance().get(&DataKey::Owner).expect("owner should be set");
            owner.require_auth();
            Consecutive::batch_mint(e, &to, amount)
        }
    }
    
    // You don't have to provide the implementations for all the methods,
    // `#[default_impl]` macro does this for you. This example showcases
    // what is happening under the hood when you use `#[default_impl]` macro.
    #[contractimpl]
    impl NonFungibleToken for Mynft {
        type ContractType = Consecutive;
    
        fn balance(e: &Env, owner: Address) -> u32 {
            Self::ContractType::balance(e, &owner)
        }
    
        fn owner_of(e: &Env, token_id: u32) -> Address {
            Self::ContractType::owner_of(e, token_id)
        }
    
        fn transfer(e: &Env, from: Address, to: Address, token_id: u32) {
            Self::ContractType::transfer(e, &from, &to, token_id);
        }
    
        fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, token_id: u32) {
            Self::ContractType::transfer_from(e, &spender, &from, &to, token_id);
        }
    
        fn approve(
            e: &Env,
            approver: Address,
            approved: Address,
            token_id: u32,
            live_until_ledger: u32,
        ) {
            Self::ContractType::approve(e, &approver, &approved, token_id, live_until_ledger);
        }
    
        fn approve_for_all(e: &Env, owner: Address, operator: Address, live_until_ledger: u32) {
            Self::ContractType::approve_for_all(e, &owner, &operator, live_until_ledger);
        }
    
        fn get_approved(e: &Env, token_id: u32) -> Option<Address> {
            Self::ContractType::get_approved(e, token_id)
        }
    
        fn is_approved_for_all(e: &Env, owner: Address, operator: Address) -> bool {
            Self::ContractType::is_approved_for_all(e, &owner, &operator)
        }
    
        fn name(e: &Env) -> String {
            Self::ContractType::name(e)
        }
    
        fn symbol(e: &Env) -> String {
            Self::ContractType::symbol(e)
        }
    
        fn token_uri(e: &Env, token_id: u32) -> String {
            Self::ContractType::token_uri(e, token_id)
        }
    }
    
    impl NonFungibleConsecutive for Mynft {}
    
    #[contractimpl]
    impl NonFungibleBurnable for Mynft {
        fn burn(e: &Env, from: Address, token_id: u32) {
            Self::ContractType::burn(e, &from, token_id);
        }
    
        fn burn_from(e: &Env, spender: Address, from: Address, token_id: u32) {
            Self::ContractType::burn_from(e, &spender, &from, token_id);
        }
    }
    
    
    use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
    use stellar_non_fungible::{
        burnable::NonFungibleBurnable,
        consecutive::{Consecutive, NonFungibleConsecutive},
        Base, ContractOverrides, NonFungibleToken,
    };
    #[contracttype]
    pub enum DataKey {
        Owner,
    }
    #[contract]
    pub struct mynft;
    pub fn __constructor(e: &Env, owner: Address) {
        e.storage().instance().set(&DataKey::Owner, &owner);
        Base::set_metadata(e, String::from_str(e, "CID"), String::from_str(e, "My NFT"), String::from_str(e, "NFTKN"));
    }
    fn batch_mint(e: &Env, to: Address, amount: u32) -> u32 {
        let owner: Address = e.storage().instance().get(...).expect(...);
        owner.require_auth();
        Consecutive::batch_mint(e, &to, amount)
    }
    impl NonFungibleToken for mynft {
        ...
    }
    impl NonFungibleBurnable for mynft {
        fn burn(...);
        fn burn_from(...);
    }
    impl NonFungibleConsecutive for mynft {}
    cargo build --target wasm32v1-none --release
    stellar contract invoke *
    --id MyNft *
    --source  <not_owner_account> *
    --network testnet *
    -- *
    batch_mint *
    --to <destination_account> *
    --amount 1
    stellar contract invoke *
    --id MyNft *
    --source  <owner_account> *
    --network testnet *
    -- *
    batch_mint *
    --to <destination_account> *
    --amount 1
    Logo

    Autenticación

    hashtag
    📚 Autenticación y Autorizaciones en Stellar Soroban

    🎯 Parte Teórica: ¿Qué son las autorizaciones? Las autorizaciones son el conjunto de mecanismos y patrones que garantizan que solo quien tiene derecho pueda ejecutar acciones concretas en un contrato inteligente. En Soroban esto se traduce principalmente a comprobar quién firmó la transacción y comparar esa identidad con lo que el contrato espera (owner, admin, etc.).

    🤔 Analogía: imagina una cerradura inteligente que solo abre con la llave correcta. require_auth() es la comprobación de llave; el almacenamiento del contrato (env.storage()) contiene la información sobre quién tiene la llave (admin, owner, roles).


    hashtag
    🔍 ¿Por qué necesitamos autorizaciones? (detallado)

    • 🛡️ Prevención de ataques: bloquea la ejecución de funciones críticas por parte de actores no autorizados (ej.: transferir fondos, cambiar parámetros).

    • 🎛️ Control de acceso fino: separa responsabilidades (admin vs. usuario) y reduce la superficie de riesgo.

    • 💰 Protección de activos: evita que fondos o estados sensibles sean alterados por quien no debe.


    hashtag
    🌟 ¿Qué ofrece Soroban para autorizaciones? (explicación técnica)

    • Address::require_auth(): solicita al entorno (host) que verifique que la dirección proporcionada autorizó la transacción. Si la firma/ autorización no está presente, la ejecución aborta. Esto se usa para garantizar que el titular de Address realmente firmó.

    • Storage para roles: env.storage().instance().set/get(...) típico para guardar direcciones que representan roles (admin, oracle, treasury).

    Nota práctica: require_auth verifica la autorización del Address pasado; si tu interfaz front-end intenta pasar otra Address sin su firma, require_auth() fallará. Esto protege contra “param tampering”.


    hashtag
    🔧 Herramientas y conceptos clave (más allá de la sintaxis)

    • require_auth() — qué hace y efectos

      • Pide al host que valide que el signature set de la transacción incluye autorización para esa Address.


    hashtag
    📋 Estrategias y patrones prácticos (con por qué y cuándo usarlas)

    1. Funciones públicas vs privadas

      • Públicas: lecturas o endpoints no sensibles. Riesgo: nadie con malos fines puede cambiar estado.

      • Privadas/autenticadas: toda escritura importante debe exigir require_auth.


    hashtag
    💡 Ejemplos Prácticos — Código + explicaciones detalladas (línea a línea)


    hashtag
    🌱 Ejemplo 1 — Función pública (sin autorización)

    hashtag
    Explicación profunda del ejemplo 1

    • #![no_std]: estándar para contratos Soroban (sin std).

    • use ...: importa macros y tipos.

    • #[contract] pub struct PublicContract; declara el contrato.


    hashtag
    🔑 Ejemplo 2 — Autenticación básica con require_auth

    hashtag
    Explicación profunda del ejemplo 2 (punto por punto)

    • pub fn secure_action(env: Env, user: Address) -> Symbol

      • user: Address: el contrato recibe la dirección que debe autorizar la operación.


    hashtag
    👑 Ejemplo 3 — Contrato con Roles (Admin + Usuario)

    hashtag
    Explicación profunda del ejemplo 3 (punto por punto)

    • initialize

      • admin.require_auth() garantiza que quien firma la transacción es la dirección admin indicada. Esto evita que alguien inicialice el contrato con un admin inexistente sin su firma.


    hashtag
    ❗ Errores comunes y cómo evitarlos

    • Confiar en parámetros sin require_auth: nunca confíes en que el parámetro user enviado por la UI está autorizado. Usa require_auth.

    • Usar unwrap() en production: causa abortos no controlados. Preferir Result.


    hashtag
    🎉 Conclusión (resumen práctico)

    • require_auth() es la herramienta central para comprobar identidad en Soroban.

    • Guarda roles en storage y siempre verifica firma + almacenamiento antes de acciones sensibles.

    • Prefiere Result y errores explícitos frente a panic!()

    Eventos

    hashtag
    📚¿Qué son los Eventos?

    Los eventos en Soroban son como notificaciones que tu contrato inteligente envía al mundo exterior. Imagínate que tu contrato es como una caja negra 📦, y los eventos son como notas que saca por una ranura para contarte qué está pasando adentro.

    hashtag

    🏢 Gestión de roles: posibilita auditorías, rotación de llaves y políticas como “solo admin puede pausar contrato”.

  • 🧪 Mejor testeo y trazabilidad: con autorizaciones claras puedes simular y probar casos de abuso (attack vectors) y asegurar que mecanismos fallan de forma controlada.

  • Logs (log!): registrar quién hizo qué ayuda en auditoría y debugging en testnet.
  • Patrones de diseño: Owner pattern, Role-based Access Control (RBAC), Two-step transfer (pending_admin → accept), multisig delegados vía contratos.

  • Si falla, la ejecución termina en host error: el estado no cambia.
  • Úsalo siempre antes de ejecutar acciones irreversibles (transferencias, cambios de roles, mint/burn).

  • Storage: instance() vs persistent() (uso práctico)

    • instance() es útil para datos del contrato en el contexto de su instancia (clave por Symbol). En muchos ejemplos se usa para roles.

    • persistent() se usa para esquemas de almacenamiento más genéricos (aunque ambos son persistentes entre ejecuciones). Mantén consistencia en tu elección.

  • Preferir Result<..., ContractError> vs panic!()

    • Result permite devolver errores legibles y manejables por callers.

    • panic!() aborta la transacción abruptamente (útil para invariantes que no deberían romperse, pero menos amigable para testing/UX).

  • Diseños de seguridad

    • Minimiza el poder del admin: reduce funciones sensibles que solo admin puede ejecutar.

    • Two-step admin transfer: evita pérdida accidental de control.

    • Auditoría & logging: crucial durante pruebas.

    • Gas & storage: cada dirección que guardes consume espacio — planifica cambios de roles y rotaciones para minimizar writes innecesarios.

  • Owner/Admin pattern

    • Guardar admin en storage y validar en funciones críticas. Sencillo y efectivo.

  • Two-step role transfer (mejor práctica)

    • set_pending_admin(new) (solo admin) → accept_admin() (firma new). Evita transferencias accidentales.

  • Multisig o permisos compuestos

    • Para operaciones críticas, delegar autorización a un contrato multisig o sistema de aprobación en varias firmas.

  • Principio de menor privilegio

    • Da lo mínimo necesario a cada rol. Si solo necesita pausar, crea un rol “pauser” en vez de usar al admin para todo.

  • pub fn hello(env: Env) -> Symbol

    • ¿Por qué devuelve Symbol?: Symbol es un tipo barato y pequeño (clave textual) ideal para mensajes o claves. Aquí ilustramos una función pública que devuelve un identificador estático.

    • log!(&env, "..."): escribe un mensaje que es útil en testnet para debugging. No confíes en logs para seguridad, son solo para observabilidad.

  • Cuándo usar este patrón: endpoints informativos (version, status, lectura de métricas públicas).

  • Riesgos: nunca lances operaciones que cambien estado/tokenes desde una función pública. Si necesitas una función pública que cambie estado, añade validaciones fuertes.

  • user.require_auth()
    : instruye al host a verificar que la transacción incluye la autorización de
    user
    .
    • Efecto: si el remitente de la transacción no posee la firma de user, la llamada falla y no hay cambios de estado.

    • Por qué es seguro: aun si un atacante intenta pasar otra dirección como user, no podrá firmar por esa dirección sin la llave privada.

  • Casos de uso típicos:

    • Cambios realizados por el propietario de una cuenta (ej.: reclamar fondos, actualizar perfil).

    • Acciones donde el contrato necesita confirmar que la persona indicada dio su consentimiento explícito.

  • Puntos a cuidar:

    • Inyección de user maliciosa: si tu front-end pasa la dirección de otro usuario pero ese otro no firmó, require_auth fallará — es correcto.

    • Confusión caller vs. user: el caller puede ser quien invoca (p. ej. una cuenta distinta o un relayer). La verificación real es la firma del user. Si esperas que caller sea el que firma, usa caller.require_auth() o no pases user como parámetro.

    • Relayers / meta-transactions: si usas relayers, estructura tu contrato para que verifique firmas o pruebas off-chain y no confíes solo en require_auth sin el patrón correcto.

  • env.storage().instance().set(&Symbol::new(&env, "admin"), &admin); guarda la dirección del admin bajo la clave "admin".
  • Mejora recomendada: proteger initialize para que solo pueda ejecutarse una vez (ej.: comprobar get("admin") y devolver error si ya existe).

  • user_action

    • Similar a secure_action del ejemplo 2: patrón para acciones que un usuario hace sobre su propia cuenta.

  • admin_action

    • caller.require_auth() exige que la persona que presenta la transacción sea quien dice ser.

    • let admin: Address = ...get(...).unwrap(); riesgo: unwrap() hará panic!() si no hay admin guardado. Si el contrato no fue inicializado, esto aborta la transacción.

      • Recomendación: manejar ese caso con Result y un error tipo NotInitialized para devolver un mensaje claro.

    • if caller != admin { panic!(...) } compara direcciones y aborta si no coincide.

      • Alternativa segura: retornar Err(ContractError::Unauthorized) para manejo más limpio en llamadas compositoras.

  • transfer_admin

    • Patrón sencillo: el admin actual firma y reemplaza el admin con new_admin.

    • Problemas potenciales: si new_admin es equivocada, se pierde control.

      • Mejor patrón (recomendado): two-step transfer:

        1. set_pending_admin(env, new_admin) (firma del admin actual).

        2. accept_admin(env)

  • Permitir re-initialization: añade guardas para que initialize solo corra una vez.

  • No contemplar relayers: si usas relayers, implementa patrones de meta-transaction o firmados off-chain.

  • Dar demasiado poder al admin: separa roles y reduce privilegios.

  • para mejorar UX y testeo.
  • Implementa patrones seguros (two-step transfer, multisig, timelocks) para producción.

  • Documenta, prueba y audita tus funciones de autorización — son la primera línea de defensa contra exploits.

  • 🎯 ¿Para qué sirven?
    • Transparencia: Permiten que aplicaciones externas sepan qué ha ocurrido

    • Debugging: Te ayudan a rastrear qué hace tu contrato

    • Integración: Otras apps pueden "escuchar" estos eventos y reaccionar

    • Auditoría: Crean un registro inmutable de las acciones

    hashtag
    🔍 Tipos de Eventos

    1. System Events 🔧: Generados automáticamente por Soroban

    2. Contract Events 📝: Los que tú defines en tu contrato

    hashtag
    Código en Soroban

    Nos vamos a una ruta donde queramos crear nuestro proyecto y ponemos el siguiente comando:

    Borramos el contrato generado en lib.rs y ponemos el siguiente código

    hashtag
    📌 Código del contrato: lib.rs

    hashtag
    🎯 Explicación Detallada de los Conceptos

    hashtag
    📢 ¿Cómo Funcionan los Eventos?

    Los eventos en Soroban funcionan como un sistema de notificaciones:

    1. Topics 🏷️: Son como "etiquetas" que permiten filtrar eventos

      • Primer topic: Identifica el contrato

      • Segundo topic: Identifica el tipo de evento

    2. Data 📦: La información específica del evento

      • Puede ser un struct, tupla, o valor simple

      • Se serializa automáticamente

    hashtag
    🔧 Componentes Clave

    hashtag
    🛠 Explicación de la función initialize

    🔍 Descripción: Inicializa el valor del contador y emite un evento indicando su creación.

    📌 Mecanismo:

    • env.storage().instance().set(...) → Guarda el valor inicial del contador en el almacenamiento persistente del contrato.

    • env.events().publish(...) → Publica un evento del tipo CounterCreated, que incluye el valor inicial y el símbolo del creador.

    • (symbol_short!("counter"), symbol_short!("created")) → Define los topics del evento, útiles para filtrarlo desde herramientas de análisis.

    🧠 Importante: Este evento puede ser consultado luego en herramientas como el explorador de eventos de Soroban para auditar quién creó el contador y con qué valor.


    hashtag
    🛠 Explicación de la función increment

    🔍 Descripción: Incrementa el valor del contador en 1 y emite un evento con los detalles del cambio.

    📌 Mecanismo:

    • Obtiene el valor actual del contador, lo incrementa, lo guarda nuevamente y emite un evento CounterUpdated con:

      • old_value: valor antes del incremento.

      • new_value: valor luego del incremento.

      • operation: símbolo "increment".

    🧠 Importante: Este evento permite rastrear cada operación que afectó el estado del contador.


    hashtag
    🛠 Explicación de la función decrement

    🔍 Descripción: Disminuye el valor del contador (si es mayor a cero) y registra el cambio con un evento.

    📌 Mecanismo:

    • Se protege contra valores negativos con una verificación.

    • El evento publicado incluye la operación "decrement".

    🧠 Importante: El estado nunca bajará de 0, manteniéndose válido en todo momento.


    hashtag
    🛠 Explicación de la función reset

    🔍 Descripción: Establece el contador en cero y emite un evento indicando quién lo reseteó.

    📌 Mecanismo:

    • Guarda el valor anterior para registrarlo en el evento CounterReset.

    • reset_by permite rastrear al responsable del reinicio.

    🧠 Importante: Este evento es útil para auditoría o lógica de negocio que responda a resets.


    hashtag
    🛠 Explicación de la función get_counter

    🔍 Descripción: Consulta el valor actual del contador sin modificar el estado del contrato.

    📌 Mecanismo:

    • Solo lectura: no genera logs ni eventos.

    • Devuelve 0 si el contador nunca fue inicializado.

    🧠 Importante: Función ideal para mostrar el estado actual en una interfaz de usuario o al cliente.


    hashtag
    🛠 Explicación de la función log_milestone

    🔍 Descripción: Permite emitir un evento especial para marcar hitos del contador.

    📌 Mecanismo:

    • El evento contiene una tupla con el valor actual, el hito alcanzado, y un mensaje descriptivo.

    • No altera el estado del contrato.

    🧠 Importante: Útil para casos de uso como alcanzar un récord, pasar un umbral o anunciar logros.

    Compilación del contrarto:

    Resultado de la compilación

    Despliegue del contrato

    Mac/Linux

    Windows

    Contrato desplegado

    Pruebas de ejecución y visualización de los eventos

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Empezando en valores iniciales el contrato stellar contract invoke * --id <CONTRACT_ID> * --source * --network testnet * -- * initialize * --initial_value 42 * --creator "developer"

    Resultado de la ejecución

    Podemos ver algo nuevo en el resultado

    Se ve lo generado por el siguiente código de la función initialize

    Si nos vamos al explorador y miramos la última transacciónarrow-up-right

    podemos ver lo siguiente:

    Visualización en el explorador

    Vemos que función se llamó y con que parámetros

    También observamos evento en la parte de "raised event" con el contenido que le hemos pasado.

    Otra opción de ver el evento por el cliente de Stellar

    1 por lumenscanarrow-up-right sacamos el ledger donde está la operación:, esto lo obtenemos por el historial de operacione del contrato y con que billetera interactuó. En esta caso la billetera "developer"

    ledger

    2 en la consola ejecutamos:

    Resultados de la operación

    Si vemos la parte Value vemos ambos valores del evento el 42 y el valor developer.

    Tokens

    hashtag
    🌟 Introducción a los Tokens SEP-41 en Stellar

    hashtag
    🤔 ¿Qué es un Token SEP-41?

    El SEP-41 es un estándar para tokens fungibles en la red Stellar que utiliza contratos inteligentes Soroban. Pero, ¿qué significa "fungible"? 🤷‍♂️

    hashtag
    🪙 Fungibilidad Explicada de Forma Simple

    Imagina que tienes billetes de $10 pesos en tu billetera. Cada billete de $10 es intercambiable por cualquier otro billete de $10 - todos tienen el mismo valor y función. Esto es fungibilidad: cada unidad es idéntica e intercambiable con otra del mismo tipo.

    Pero aquí viene algo súper importante: Los tokens fungibles también son divisibles ✂️

    hashtag
    🔢 Divisibilidad y Unidad Mínima

    Al igual que el dinero físico, los tokens fungibles pueden dividirse en partes más pequeñas, pero siempre hay un límite:

    💰 Ejemplo con dinero físico:

    • 1 peso se puede dividir en 100 centavos

    • El centavo es la unidad mínima - no puedes tener 0.5 centavos físicos

    🪙 Ejemplo con tokens digitales:

    • 1 token puede dividirse según sus decimales configurados

    • Si un token tiene 6 decimales: 1 token = 1,000,000 unidades mínimas

    • Si un token tiene 2 decimales: 1 token = 100 unidades mínimas

    🧮 Casos prácticos:

    • Bitcoin: 8 decimales → 1 BTC = 100,000,000 satoshis

    • USDC: 6 decimales → 1 USDC = 1,000,000 micro-USDC

    • Tu token: Tú decides cuántos decimales → flexibilidad total

    ⚠️ Regla de oro: No puedes tener fracciones de la unidad mínima. Si tu token tiene 2 decimales, no puedes enviar 0.001 tokens (necesitarías al menos 0.01).

    Ejemplos de tokens fungibles:

    • 💰 Monedas digitales (como USDC, Bitcoin)

    • 💳 Puntos de recompensa

    • 🎮 Monedas de videojuegos

    • 📈 Tokens de inversión

    ¿Por qué son importantes? Los tokens fungibles son perfectos para crear:

    • 💱 Monedas digitales

    • 🏦 Sistemas de pagos

    • 💸 Programas de lealtad

    • 📊 Activos financieros

    hashtag
    🧙‍♂️ OpenZeppelin: Tu Escudo de Seguridad

    hashtag
    🛡️ ¿Por qué usar OpenZeppelin?

    OpenZeppelin es como tener un equipo de expertos en seguridad trabajando para ti 24/7. Aquí te explico por qué es crucial:

    🔐 Contratos Ultra-Seguros:

    • ⚔️ Batalla-probados: Millones de dólares protegidos en producción

    • 🧪 Testeo exhaustivo: Cada línea de código probada por expertos

    • 🏆 Estándar de la industria: Usado por los proyectos más grandes

    💰 ¿Sabes cuánto dinero se ha perdido por contratos inseguros?

    • 🔥 +$3 billones USD perdidos en hacks de DeFi desde 2020

    • 💸 The DAO Hack (2016): $60 millones robados

    • 🚨 Poly Network (2021): $600 millones hackeados

    🛠️ El Trabajo Pesado Ya Está Hecho: OpenZeppelin hace el trabajo que tú no quieres (ni debes) hacer:

    • 🔒 Protección contra reentrancy attacks

    • 🛡️ Validación de permisos y roles

    • ⚡ Optimización de gas


    hashtag
    🧙‍♂️ OpenZeppelin Wizard para Stellar

    OpenZeppelin ha creado un wizard (asistente) súper útil para generar tokens SEP-41: 🔗

    🌟 ¿Qué hace el wizard por ti?

    • 🎨 Interfaz visual: Sin necesidad de escribir código desde cero

    • ⚙️ Configuración simple: Solo selecciona las funciones que necesitas

    • 📋 Código generado: Listo para usar en minutos

    hashtag
    ⚠️ Importante: Limitaciones Actuales

    Aunque el wizard es genial para empezar, todavía tiene algunos errores 🐛. Por eso, es mejor usar los ejemplos oficiales como referencia:

    🔗


    Instructivo para poder utilizar el código de OpenZeppelin

    hashtag
    Pre-requisitos

    1. Tener instalado Rust.

    2. Tener instalado El cliente Stellar

    3. tener instalado el cliente de GIt

    • Copiar el repositorio de github de OpenZeppelin en una carpeta previamente seleccionada con la siguiente instrucción.

    • Con el editor favorito de código abrimos el folder stellar-contracts.

    • En el directorio raiz abrir el archivo Cargo.toml

    Para cada ejemplo, por facilidad vamos a crear una subcarpeta


    hashtag
    Ejemplo 1: Un token sencillo

    Vamos a crear el token MYT (My token).

    MyToken usa la funcionalidad limitada ( capped ) de un token fungible, en esta ocasión la función set_cap, en esta le indicamos cual es el maximo de monedas posibles a generar del token.

    En el archivo Cargo.toml del directorio raiz. en members, agregamos

    Dentro de la carpeta examples creamos la carpeta "myt"

    En el folder myt creamos el archivo Cargo.toml con lo siguiente

    En el caso de ser un token fungible es con

    En el caso de ser un NFT se pone

    Dentro de myt creamos una carpeta llamada src con los siguientes archivos:

    contract.rs ( Contrato del token)

    lib.rs ( archivo que engancha el contrato y su respectivo test)

    Escribimos lo siguiente dentro de lib.rs

    Código de contract.rs

    hashtag
    Explicación del Contrato MyToken

    hashtag
    ¿Qué es este contrato?

    Este es un token fungible (como una moneda digital) que funciona en la blockchain de Stellar. Piensa en él como crear tu propia criptomoneda, pero con reglas específicas que tú defines.

    hashtag
    Estructura General del Código

    hashtag
    1. Importaciones (Las herramientas que necesitamos)

    ¿Qué significa esto?

    • soroban_sdk: Es como la "caja de herramientas" básica para crear contratos en Stellar

    • stellar_fungible: Son las funciones pre-construidas para crear tokens (como plantillas ya hechas)

    • capped: Funciones para limitar la cantidad máxima de tokens que se pueden crear

    hashtag
    2. Definición del Contrato

    Explicación simple:

    • Myt es el nombre de nuestro contrato (puedes cambiarlo por el que quieras)

    • #[contract] le dice a Stellar "esto es un contrato inteligente"

    • Es como crear una "fábrica" que va a producir tokens

    hashtag
    Partes Más Importantes

    hashtag
    🏗️ Constructor (La función que inicializa todo)

    ¿Qué hace?

    1. Base::set_metadata(...): Define las características básicas del token:

      • 2: Decimales (como los centavos de las monedas)

      • "MyToken": El nombre completo del token

    Analogía: Es como registrar una nueva moneda en el banco central, definiendo su nombre, símbolo y cuántas unidades máximo pueden existir.

    hashtag
    🪙 Función Mint (Crear nuevos tokens)

    ¿Qué hace?

    1. check_cap(e, amount): Verifica que no se exceda el límite máximo

    2. Base::mint(...): Crea los tokens y los asigna a una cuenta específica

    Analogía: Es como una máquina impresora de billetes, pero que primero verifica que no imprimas más de lo permitido.

    hashtag
    🔄 Implementación de FungibleToken (Las funciones estándar)

    Esta parte implementa todas las funciones que cualquier token debe tener:

    Funciones de Consulta (Solo leen información):

    • total_supply(): ¿Cuántos tokens existen en total?

    • balance(): ¿Cuántos tokens tiene una cuenta específica?

    • decimals()

    Funciones de Transferencia:

    • transfer(): Enviar tokens de una cuenta a otra

    • transfer_from(): Permitir que alguien más mueva tus tokens (con permiso previo)

    hashtag
    ¿Por qué este diseño es inteligente?

    hashtag
    1. Reutilización de código

    En lugar de escribir todas las funciones desde cero, usa Base que ya tiene todo implementado y probado.

    hashtag
    2. Seguridad con límites

    La función check_cap asegura que nunca se puedan crear más tokens de los permitidos.

    hashtag
    3. Estándar compatible

    Al implementar FungibleToken, tu token funciona con todas las aplicaciones que esperan tokens estándar.

    hashtag
    Compilación del contrato

    Nos ubicamos dentro de ../stellar-contracts/examples/myt allí en consola ejecutamos:

    hashtag
    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Nota:Para un millón o cualquier cifra se ponen 2 ceros de más que son los centavos

    Acá podemos ver que se llama al constuctor del contrato y estamos poniendo que el máximo de tokens son 1 millón.

    No obstante, a pesar que tenemos como máximo 1 millón, no hemos acuñado ninguna moneda.

    A continuación vamos a invocar la función mint

    Para ver el balance

    Si queremos ver el saldo de una forma visual en nuestra billetera 😃

    Primero ejecutamos el siguiente comando

    En nuetos caso el alias de cuenta o entidad es developer, mandamos a descansar a Bob o Alice 😉 Esto nos da la llave secreta, esta llave secreta la añadimos en nuestra billetera favorita, en nuestro caso la billetera freigther

    Una vez importada la billetera importamos el contrato de token

    Al añadir el contrato del token vemos lo siguiente en nuestra billetera:

    ⚠️ OJO: Si eres detallista, ves un agujero ENORME de seguridad, cualquiera puede hacer la operación de mint, este ejemplo sólo ha sido con fines ilustrativos 😅.


    hashtag
    Ejemplo 2: Un token sencillo, pero sólo el dueño puede acuñarlo

    Lo primero que hacemos es copiar la carpeta myt y la renombramos con el nombre mytsf ( My Token Safe version)

    dentro de la carpeta mytsv cambiamos el contenido de name en el archivo Cargo.toml

    En la carpeta raiz agregamos en el archivo Cargo.toml dentro de members la carpeta que se acabo de crear

    El código en en contract.rs

    hashtag
    Explicación del Contrato MyToken con Control de Propietario 🔐

    hashtag
    Estructura General del Código

    hashtag
    1. Importaciones (Las herramientas que necesitamos)

    ¿Qué significa esto?

    • soroban_sdk: Es como la "caja de herramientas" básica para crear contratos en Stellar

    • stellar_fungible: Son las funciones pre-construidas para crear tokens (como plantillas ya hechas)

    • capped: Funciones para limitar la cantidad máxima de tokens que se pueden crear

    hashtag
    2. Definición de Constantes (Los valores que no cambian)

    ¿Qué es esto?

    • Es como crear una "etiqueta" que identifica quién es el propietario del contrato

    • symbol_short!("OWNER"): Es una forma eficiente de almacenar esta información en Stellar

    • Piénsalo como la "llave maestra" del contrato 🗝️

    hashtag
    2. Definición del Contrato

    Explicación simple:

    • MyTSV es el nombre de nuestro contrato (cambió de Myt a MyTSV)

    • #[contract] le dice a Stellar "esto es un contrato inteligente"

    hashtag
    Partes Más Importantes

    hashtag
    🏗️ Constructor (La función que inicializa todo) - ¡MEJORADO!

    ¿Qué hace ahora? 🎯

    1. Base::set_metadata(...): Define las características básicas del token:

      • 2: Decimales ( como los centavos del peso)

      • "My Token Safe Version"

    Analogía: Es como registrar una nueva moneda en el banco central, pero ahora también registras oficialmente quién es el director del banco que puede autorizar la impresión de nuevos billetes. 🏦

    hashtag
    🚨 CAMBIO IMPORTANTE: Control de Propietario

    hashtag
    🪙 Función Mint (Crear nuevos tokens) - ¡AHORA MÁS SEGURA!

    ¿Qué hace ahora? 🔍

    1. 🆕 let owner: Address = e.storage().instance().get(&OWNER)...:

      • Busca quién es el propietario registrado del contrato

    2. 🆕 owner.require_auth()

    Analogía: Antes era como una máquina impresora de billetes que cualquiera podía usar. Ahora es como una máquina que requiere la huella dactilar del director del banco para funcionar. 👆

    hashtag
    ¿Por qué es una EXCELENTE práctica? 🌟

    hashtag
    🛡️ Seguridad Crítica

    Antes: Cualquier persona podía crear tokens nuevos

    Ahora: Solo el propietario puede crear tokens

    hashtag
    💰 Control de Inflación

    • Sin control: Los tokens podrían volverse sin valor si cualquiera los crea

    • Con control: El propietario decide cuándo y cuántos tokens crear

    hashtag
    🎯 Casos de Uso Reales

    1. Token de empresa: Solo el CEO puede autorizar nuevas emisiones

    2. Token de recompensas: Solo el sistema de la app puede crear tokens por logros

    3. Token de comunidad: Solo el comité puede crear tokens para nuevos miembros

    hashtag
    ⚖️ Transparencia

    • Todo el mundo puede ver quién es el propietario

    • Todas las operaciones de mint quedan registradas en la blockchain

    • La comunidad puede auditar quién y cuándo se crean nuevos tokens

    hashtag
    Ejemplo de Ataque Prevenido 🚫

    Escenario peligroso sin control de propietario:

    Con control de propietario:

    hashtag
    🔄 Implementación de FungibleToken (Las funciones estándar)

    Esta parte implementa todas las funciones que cualquier token debe tener (sin cambios, pero ahora más seguro porque el mint está protegido):

    Funciones de Consulta (Solo leen información):

    • total_supply(): ¿Cuántos tokens existen en total?

    • balance(): ¿Cuántos tokens tiene una cuenta específica?

    • decimals()

    Funciones de Transferencia:

    • transfer(): Enviar tokens de una cuenta a otra

    • transfer_from(): Permitir que alguien más mueva tus tokens (con permiso previo)

    hashtag
    ¿Por qué este diseño es inteligente? 🧠

    hashtag
    1. Reutilización de código

    En lugar de escribir todas las funciones desde cero, usa Base que ya tiene todo implementado y probado.

    hashtag
    2. Seguridad multinivel 🔒

    • Nivel 1: check_cap asegura que no se excedan los límites

    • Nivel 2: require_auth asegura que solo el propietario pueda crear tokens

    • Nivel 3: Todo queda registrado en la blockchain (inmutable y auditable)

    hashtag
    3. Estándar compatible

    Al implementar FungibleToken, tu token funciona con todas las aplicaciones que esperan tokens estándar.

    hashtag
    Compilación del contrato

    Nos ubicamos dentro de ../stellar-contracts\examples\mytsv allí en consola ejecutamos:

    hashtag
    Despliegue del contrato

    Para Linux y Mac el salto de línea de la instrucción es con el carácter " \ " para Windows con el carácter " ` "

    Nota:Para un millón o cualquier cifra se ponen 2 ceros de más que son los centavos

    hashtag
    Haciendo un mint con una cuenta no valida

    hashtag
    Haciendo un mint con una cuenta valida

    ⚠️ OJO: Si eres detallista, ves que se revela la wallet del owner 🙈, esto lo podemos solucionar, si comparamos la billetera que nos envian si es la misma del owner 😉, si es la misma ok, si no es la misma mensaje de error. Además de la instruccion de la verificación si la firma digital es del owner owner.require_auth().

    #![no_std]
    use soroban_sdk::{contract, contractimpl, log, Env, Symbol};
    
    // 📦 Contrato simple sin autenticación
    #[contract]
    pub struct PublicContract;
    
    #[contractimpl]
    impl PublicContract {
        pub fn hello(env: Env) -> Symbol {
            // 📢 Cualquiera puede ejecutar esta función
            log!(&env, "Función pública ejecutada sin autorización.");
            Symbol::new(&env, "COUNTER")
        }
    }
    #![no_std]
    use soroban_sdk::{contract, contractimpl, Env, Symbol, log, Address};
    
    #[contract]
    pub struct AuthContract;
    
    #[contractimpl]
    impl AuthContract {
       pub fn secure_action(env: Env, user: Address) -> Symbol {
            // 🔐 Usuario debe firmar la transacción
            user.require_auth();
    
            log!(&env, "Acción segura ejecutada por un usuario autorizado.");
            Symbol::new(&env, "ok")
        }
    }
    #![no_std]
    use soroban_sdk::{contract, contractimpl, log, Env, Symbol, Address};
    
    #[contract]
    pub struct RoleBasedContract;
    
    #[contractimpl]
    impl RoleBasedContract {
        // 🏗️ Inicializar contrato con un administrador
        pub fn initialize(env: Env, admin: Address) -> Symbol {
            admin.require_auth();
            env.storage().instance().set(&Symbol::new(&env, "admin"), &admin);
            log!(&env, "Contrato inicializado con administrador.");
            Symbol::new(&env, "INITIALIZED")
        }
    
        // 👤 Acción para usuarios normales
        pub fn user_action(env: Env, user: Address) -> Symbol {
            user.require_auth();
            log!(&env, "Acción de usuario ejecutada.");
            Symbol::new(&env, "USER_OK")
        }
    
        // 👑 Acción solo para administradores
        pub fn admin_action(env: Env, caller: Address) -> Symbol {
            caller.require_auth();
            let admin: Address = env.storage()
                .instance()
                .get(&Symbol::new(&env, "admin"))
                .unwrap();
    
            if caller != admin {
                panic!("Solo el administrador puede ejecutar esta función.");
            }
            log!(&env, "Acción administrativa ejecutada por el admin.");
            Symbol::new(&env, "ADMIN_OK")
        }
    
        // 🔄 Transferir rol de administrador
        pub fn transfer_admin(env: Env, current_admin: Address, new_admin: Address) -> Symbol {
            current_admin.require_auth();
            let stored_admin: Address = env.storage()
                .instance()
                .get(&Symbol::new(&env, "admin"))
                .unwrap();
    
            if current_admin != stored_admin {
                panic!("Solo el administrador actual puede transferir el rol.");
            }
    
            env.storage().instance().set(&Symbol::new(&env, "admin"), &new_admin);
            log!(&env, "Administrador transferido exitosamente.");
            Symbol::new(&env, "ADMIN_TRANSFERRED")
        }
    }
    stellar contract init  events --name events
    #![no_std]
    use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Env, Symbol};
    // 📝 Definimos los tipos de datos para nuestros eventos
    #[contracttype]
    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct CounterCreated {
        pub initial_value: u32,
        pub creator: Symbol,
    }
    #[contracttype]
    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct CounterUpdated {
        pub old_value: u32,
        pub new_value: u32,
        pub operation: Symbol,
    }
    #[contracttype]
    #[derive(Clone, Debug, Eq, PartialEq)]
    pub struct CounterReset {
        pub previous_value: u32,
        pub reset_by: Symbol,
    }
    // 🏗️ Definimos las claves para almacenar datos
    const COUNTER: Symbol = symbol_short!("COUNTER");
    #[contract]
    pub struct CounterContract;
    #[contractimpl]
    impl CounterContract {
        /// 🎯 Inicializa el contador y emite evento de creación
        pub fn initialize(env: Env, initial_value: u32, creator: Symbol) {
            // Guardamos el valor inicial
            env.storage().instance().set(&COUNTER, &initial_value);
    
            // 📢 ¡Emitimos nuestro primer evento!
           
             env.events().publish(
                (symbol_short!("counter"), symbol_short!("created")), // Topics para filtrar
                CounterCreated {
                    initial_value,
                    creator,
                },
            );
        }
    
        /// ➕ Incrementa el contador
        pub fn increment(env: Env) -> u32 {
            let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
    
            let new_value = current + 1;
            env.storage().instance().set(&COUNTER, &new_value);
    
            // 📢 Evento de actualización
            env.events().publish(
                (symbol_short!("counter"), symbol_short!("updated")),
                CounterUpdated {
                    old_value: current,
                    new_value,
                    operation: symbol_short!("increment"),
                },
            );
    
            new_value
        }
        /// ➖ Decrementa el contador
        pub fn decrement(env: Env) -> u32 {
            let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
    
            let new_value = if current > 0 { current - 1 } else { 0 };
            env.storage().instance().set(&COUNTER, &new_value);
    
            // 📢 Evento de actualización
            env.events().publish(
                (symbol_short!("counter"), symbol_short!("updated")),
                CounterUpdated {
                    old_value: current,
                    new_value,
                    operation: symbol_short!("decrement"),
                },
            );
    
            new_value
        }
        /// 🔄 Resetea el contador a cero
        pub fn reset(env: Env, reset_by: Symbol) {
            let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
    
            env.storage().instance().set(&COUNTER, &0u32);
    
            // 📢 Evento de reset
            env.events().publish(
                (symbol_short!("counter"), symbol_short!("reset")),
                CounterReset {
                    previous_value: current,
                    reset_by,
                },
            );
        }
        /// 👀 Obtiene el valor actual (sin eventos, es solo lectura)
        pub fn get_counter(env: Env) -> u32 {
            env.storage().instance().get(&COUNTER).unwrap_or(0)
        }
        /// 📊 Función que emite evento personalizado con datos múltiples
        pub fn log_milestone(env: Env, milestone: u32, message: Symbol) {
            let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
    
            // 📢 Evento complejo con múltiples datos
            env.events().publish(
                (symbol_short!("counter"), symbol_short!("milestone")),
                (current, milestone, message),
            );
        }
    }
    rustenv.events().publish(    (topic1, topic2),  // 🏷️ Topics para filtrar    event_data         // 📦 Datos del evento);
    rustCopiarEditarpub fn initialize(env: Env, initial_value: u32, creator: Symbol) {
        env.storage().instance().set(&COUNTER, &initial_value);
    
        env.events().publish(
            (symbol_short!("counter"), symbol_short!("created")),
            CounterCreated {
                initial_value,
                creator,
            },
        );
    }
    rustCopiarEditarpub fn increment(env: Env) -> u32 {
        let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
        let new_value = current + 1;
        env.storage().instance().set(&COUNTER, &new_value);
    
        env.events().publish(
            (symbol_short!("counter"), symbol_short!("updated")),
            CounterUpdated {
                old_value: current,
                new_value,
                operation: symbol_short!("increment"),
            },
        );
    
        new_value
    }
    rustCopiarEditarpub fn decrement(env: Env) -> u32 {
        let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
        let new_value = if current > 0 { current - 1 } else { 0 };
        env.storage().instance().set(&COUNTER, &new_value);
    
        env.events().publish(
            (symbol_short!("counter"), symbol_short!("updated")),
            CounterUpdated {
                old_value: current,
                new_value,
                operation: symbol_short!("decrement"),
            },
        );
    
        new_value
    }
    rustCopiarEditarpub fn reset(env: Env, reset_by: Symbol) {
        let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
        env.storage().instance().set(&COUNTER, &0u32);
    
        env.events().publish(
            (symbol_short!("counter"), symbol_short!("reset")),
            CounterReset {
                previous_value: current,
                reset_by,
            },
        );
    }
    rustCopiarEditarpub fn get_counter(env: Env) -> u32 {
        env.storage().instance().get(&COUNTER).unwrap_or(0)
    }
    rustCopiarEditarpub fn log_milestone(env: Env, milestone: u32, message: Symbol) {
        let current: u32 = env.storage().instance().get(&COUNTER).unwrap_or(0);
    
        env.events().publish(
            (symbol_short!("counter"), symbol_short!("milestone")),
            (current, milestone, message),
        );
    }
    stellar contract build
    stellar contract deploy \
    --wasm target\wasm32v1-none\release\events.wasm \
    --source developer \
    --network testnet \
    --alias events
    stellar contract deploy `
    --wasm target\wasm32v1-none\release\events.wasm `
    --source developer `
    --network testnet `
    --alias events
     Event: [{"symbol":"counter"},{"symbol":"created"}] = {"map":[{"key":{"symbol":"creator"},"val":{"symbol":"developer"}},{"key":{"symbol":"initial_value"},"val":{"u32":42}}]}
    // 📢 ¡Emitimos nuestro primer evento!
           
             env.events().publish(
                (symbol_short!("counter"), symbol_short!("created")), // Topics para filtrar
                CounterCreated {
                    initial_value,
                    creator,
                },
            );
    stellar events --network testnet --start-ledger 1420216
    (firma del
    new_admin
    para aceptar).
  • Esto evita transferencias accidentales a direcciones que no controlas.

  • 1420216arrow-up-right
    👥 Revisión comunitaria: Miles de desarrolladores revisando el código
    ⚠️ Y muchos más...
    🧩 Compatibilidad con estándares
  • 🔧 Patrones de actualización seguros

  • 🚦 Controles de acceso robustos

  • 🔧 Personalizable: Agrega tus propias funciones después

    "MYT": El símbolo corto (como "USD" para dólares)

  • set_cap(e, cap): Establece el límite máximo de tokens que se pueden crear

    • Ejemplo: Si pones cap = 1000000, nunca se podrán crear más de 1 millón de tokens

  • : ¿Cuántos decimales tiene el token?
  • name() y symbol(): ¿Cómo se llama el token?

  • approve()
    : Dar permiso a alguien para que use tus tokens
  • allowance(): ¿Cuántos tokens puede usar alguien en mi nombre?

  • 🆕 symbol_short: Para crear identificadores eficientes de datos en el contrato

  • Es como crear una "fábrica" que va a producir tokens
    : El nombre completo del token (¡más descriptivo!)
  • "MYTSV": El símbolo corto (que coincide con el nombre del struct)

  • set_cap(e, cap): Establece el límite máximo de tokens que se pueden crear

  • 🆕 e.storage().instance().set(&OWNER, &owner): ¡Esta es la parte nueva!

    • Guarda quién es el propietario del contrato en la blockchain

    • Es como escribir en piedra quién tiene el control del contrato

  • :
    ¡ESTA ES LA CLAVE!
    🔐
    • Verifica que quien está llamando la función sea realmente el propietario

    • Si no es el propietario, la transacción falla automáticamente

  • check_cap(e, amount): Verifica que no se exceda el límite máximo ✅

  • Base::mint(...): Solo si todo está bien, crea los tokens

  • : ¿Cuántos decimales tiene el token?
  • name() y symbol(): ¿Cómo se llama el token?

  • approve()
    : Dar permiso a alguien para que use tus tokens
  • allowance(): ¿Cuántos tokens puede usar alguien en mi nombre?

  • https://wizard.openzeppelin.com/stellar#arrow-up-right
    https://github.com/OpenZeppelin/stellar-contracts/arrow-up-right
    Vista breve del archivo Cargo.toml
    Estructura para el token Myt
    Resultado de una ejecución exitosa
    Despliegue exitoso
    Vista del contrato desplegado en testnet
    Resultado de la operación
    Resultado de la operación
    Retorno de la operación
    resultado de la operación
    Error al no ser el dueño de la cuenta
    Resultado de la operación
    git clone https://github.com/OpenZeppelin/stellar-contracts.git
    "examples/myt"
    [package]
    name = "myt"
    edition.workspace = true
    license.workspace = true
    repository.workspace = true
    publish = false
    version.workspace = true
    
    [lib]
    crate-type = ["cdylib"]
    doctest = false
    
    [dependencies]
    soroban-sdk = { workspace = true }
    stellar-access-control = { workspace = true }
    stellar-access-control-macros = { workspace = true }
    stellar-fungible = { workspace = true }
    stellar-default-impl-macro = { workspace = true }
    
    [dev-dependencies]
    soroban-sdk = { workspace = true, features = ["testutils"] }
    stellar-fungible = { workspace = true }
    stellar-non-fungible = { workspace = true }
    #![no_std]
    pub mod contract;
    use soroban_sdk::{contract, contractimpl, Address, Env, String};
    use stellar_fungible::{
        capped::{check_cap, set_cap},
        Base, FungibleToken,
    };
    
    #[contract]
    pub struct MyT;
    
    #[contractimpl]
    impl MyT {
        pub fn __constructor(e: &Env, cap: i128) {
            Base::set_metadata(e, 2, String::from_str(e, "MyToken"), String::from_str(e, "MYT"));
            set_cap(e, cap);
        }
    
        pub fn mint(e: &Env, account: Address, amount: i128) {
            check_cap(e, amount);
            Base::mint(e, &account, amount);
        }
    }
    
    #[contractimpl]
    impl FungibleToken for MyT {
        type ContractType = Base;
    
        fn total_supply(e: &Env) -> i128 {
            Self::ContractType::total_supply(e)
        }
    
        fn balance(e: &Env, account: Address) -> i128 {
            Self::ContractType::balance(e, &account)
        }
    
        fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {
            Self::ContractType::allowance(e, &owner, &spender)
        }
    
        fn transfer(e: &Env, from: Address, to: Address, amount: i128) {
            Self::ContractType::transfer(e, &from, &to, amount);
        }
    
        fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {
            Self::ContractType::transfer_from(e, &spender, &from, &to, amount);
        }
    
        fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {
            Self::ContractType::approve(e, &owner, &spender, amount, live_until_ledger);
        }
    
        fn decimals(e: &Env) -> u32 {
            Self::ContractType::decimals(e)
        }
    
        fn name(e: &Env) -> String {
            Self::ContractType::name(e)
        }
    
        fn symbol(e: &Env) -> String {
            Self::ContractType::symbol(e)
        }
    }
    use soroban_sdk::{contract, contractimpl, Address, Env, String};use stellar_fungible::{    capped::{check_cap, set_cap},    Base, FungibleToken,};
    #[contract]
    pub struct Myt;
    pub fn __constructor(e: &Env, cap: i128) {
       Base::set_metadata(e, 2, String::from_str(e, "MyToken"), String::from_str(e, "MYT"));
        set_cap(e, cap); 
    }
    rustpub fn mint(e: &Env, account: Address, amount: i128) {    check_cap(e, amount);    Base::mint(e, &account, amount);}
    cargo build --target wasm32-unknown-unknown --release
    stellar contract deploy *
      --wasm ../../target/wasm32-unknown-unknown/release/myt.wasm *
      --source developer *
      --network testnet *
      --alias MyToken  *
      -- *
      --cap 100000000
    stellar contract invoke *
    --id <contract_id> *
    --source developer *
    --network testnet *
    -- *
    mint *
    --account <tu_cuenta_destino> *
    --amount 100000
    stellar contract invoke *
    --id <contract_id> *
    --source developer *
    --network testnet *
    -- balance
    --account <cuenta_a_verificar>
    stellar keys secret <alias-de-cuenta>
    [package]
    name = "mytsv"
    [workspace]
    resolver = "2"
    members = [
        "examples/mytsv",   
    use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String, Symbol};
    use stellar_fungible::{
        capped::{check_cap, set_cap},
        Base, FungibleToken,
    };
    pub const OWNER: Symbol = symbol_short!("OWNER");
    
    #[contract]
    pub struct MyTSV;
    
    #[contractimpl]
    impl MyTSV {
        pub fn __constructor(e: &Env, cap: i128, owner: Address) {
            Base::set_metadata(e, 2, String::from_str(e, "My Token Safe  Version"), String::from_str(e, "MYTSV"));
            set_cap(e, cap);
            e.storage().instance().set(&OWNER, &owner);
        }
    
        pub fn mint(e: &Env, account: Address, amount: i128) {
            check_cap(e, amount);
            let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");
            owner.require_auth();
            Base::mint(e, &account, amount);
        }
    }
    
    #[contractimpl]
    impl FungibleToken for MyTSV {
        type ContractType = Base;
    
        fn total_supply(e: &Env) -> i128 {
            Self::ContractType::total_supply(e)
        }
    
        fn balance(e: &Env, account: Address) -> i128 {
            Self::ContractType::balance(e, &account)
        }
    
        fn allowance(e: &Env, owner: Address, spender: Address) -> i128 {
            Self::ContractType::allowance(e, &owner, &spender)
        }
    
        fn transfer(e: &Env, from: Address, to: Address, amount: i128) {
            Self::ContractType::transfer(e, &from, &to, amount);
        }
    
        fn transfer_from(e: &Env, spender: Address, from: Address, to: Address, amount: i128) {
            Self::ContractType::transfer_from(e, &spender, &from, &to, amount);
        }
    
        fn approve(e: &Env, owner: Address, spender: Address, amount: i128, live_until_ledger: u32) {
            Self::ContractType::approve(e, &owner, &spender, amount, live_until_ledger);
        }
    
        fn decimals(e: &Env) -> u32 {
            Self::ContractType::decimals(e)
        }
    
        fn name(e: &Env) -> String {
            Self::ContractType::name(e)
        }
    
        fn symbol(e: &Env) -> String {
            Self::ContractType::symbol(e)
        }
    }
    use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String, Symbol};use stellar_fungible::{    capped::{check_cap, set_cap},    Base, FungibleToken,};
    rustpub const OWNER: Symbol = symbol_short!("OWNER");
    rust#[contract]pub struct MyTSV;
    pub fn __constructor(e: &Env, cap: i128, owner: Address) {    Base::set_metadata(e, 2, String::from_str(e, "MyToken"), String::from_str(e, "MYT"));    set_cap(e, cap);    e.storage().instance().set(&OWNER, &owner);}
    pub fn mint(e: &Env, account: Address, amount: i128) {   
       let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");   
       owner.require_auth();    
       check_cap(e, amount);
       Base::mint(e, &account, amount);
     }
    // ❌ Cualquiera podía hacer esto:contract.mint(mi_cuenta, 1_000_000); // ¡Crear un millón de tokens!
    // ✅ Solo el owner puede hacer esto:contract.mint(cuenta_destino, 1000); // Y debe firmar la transacción
    1. Hacker descubre el contrato.
    2. Llama a mint(su_cuenta, 999_999_999).
    3. Se vuelve millonario instantáneamente.
    4. El valor del token se colapsa.
    5. Todos los holders pierden dinero
    1. Hacker intenta llamar mint().
    2. El contrato verifica: "¿Eres el propietario?".
    3. Respuesta: "No".
    4. Transacción rechazada automáticamente ✋.
    5. El token mantiene su integridad
    cargo build --target wasm32-unknown-unknown --release
    stellar contract deploy *
      --wasm ../../target/wasm32-unknown-unknown/release/mytsv.wasm *
      --source developer *
      --network testnet *
      --alias MyTokenSafeVersion  *
      -- *
      --cap 100000000 *
      --owner <owner_address>
    stellar contract invoke `
    --id MyTokenSafeVersion `
    --source <otra cuenta> `
    --network testnet `
    -- `
    mint `
    --account <otra cuenta> `
    --amount 100000
    stellar contract invoke `
    --id MyTokenSafeVersion `
    --source <cuenta owner> `
    --network testnet `
    -- `
    mint `
    --account <cuenta destino> `
    --amount 100000