Token Fungible
Los tokens fungibles representan activos donde cada unidad es idéntica e intercambiable, como monedas, productos básicos o tokens de utilidad. En Stellar, puedes crear tokens fungibles donde cada token tiene el mismo valor y propiedades, con saldos y propiedad rastreados a través de contratos inteligentes Soroban.
Resumen
El módulo fungible ofrece tres variantes diferentes de Token Fungible que difieren en cómo se manejan ciertas funciones como transferencias y aprobaciones de tokens:
El módulo proporciona varias opciones de implementación para adaptarse a diferentes casos de uso:
- Implementación base (
FungibleToken
con tipo de contratoBase
): Adecuado para la mayoría de casos de uso estándar de tokens. - Extensión AllowList (
FungibleToken
con tipo de contratoAllowList
): Para tokens que requieren un mecanismo de lista de permitidos para controlar quién puede transferir tokens. - Extensión BlockList (
FungibleToken
con tipo de contratoBlockList
): Para tokens que necesitan bloquear direcciones específicas de realizar transferencias.
Estas implementaciones comparten funcionalidad central y una interfaz común, exponiendo funciones de contrato idénticas como puntos de entrada. Sin embargo, las extensiones proporcionan un comportamiento especializado al sobrescribir ciertas funciones para implementar sus requisitos específicos.
Ejecutar el Ejemplo
Primero pasa por el proceso de Configuración para tener tu entorno de desarrollo configurado, luego clona el repositorio OpenZeppelin Stellar Contracts:
git clone https://github.com/OpenZeppelin/stellar-contracts
cd stellar-contracts
O, salta la configuración del entorno de desarrollo y abre este ejemplo en GitHub Codespaces o Code Anywhere.
Para ejecutar las pruebas del ejemplo, navega al directorio fungible-token-interface
y usa cargo test
.
cd examples/fungible-token-interface
cargo test
Código
Este ejemplo muestra cómo usar la biblioteca OpenZeppelin Stellar Contracts para crear un token fungible. La biblioteca proporciona implementaciones desarrolladas y auditadas que siguen las prácticas recomendadas.
use soroban_sdk::{contract, contractimpl, token::Interface as TokenInterface, Address, Env};
use stellar_access::ownable::{self as ownable, Ownable};
use stellar_macros::{default_impl, only_owner, when_not_paused};
use stellar_pausable::pausable::{self as pausable, Pausable};
use stellar_tokens::fungible::{Base, FungibleToken};
#[contract]
pub struct FungibleTokenContract;
#[contractimpl]
impl FungibleTokenContract {
pub fn __constructor(e: &Env, owner: Address) {
// Set token metadata
Base::set_metadata(
e,
18, // 18 decimals
"My Token".into(),
"TKN".into(),
);
// Set the contract owner
ownable::set_owner(e, &owner);
}
#[only_owner]
pub fn mint(e: &Env, to: Address, amount: i128) {
Base::mint(e, &to, amount);
}
#[only_owner]
pub fn pause(e: &Env) {
pausable::pause(e);
}
#[only_owner]
pub fn unpause(e: &Env) {
pausable::unpause(e);
}
}
#[default_impl]
#[contractimpl]
impl FungibleToken for FungibleTokenContract {
type ContractType = Base;
}
#[default_impl]
#[contractimpl]
impl TokenInterface for FungibleTokenContract {
#[when_not_paused]
fn transfer(e: Env, from: Address, to: Address, amount: i128) {
Base::transfer(&e, &from, &to, amount);
}
#[when_not_paused]
fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) {
Base::transfer_from(&e, &spender, &from, &to, amount);
}
#[when_not_paused]
fn burn(e: Env, from: Address, amount: i128) {
Base::burn(&e, &from, amount);
}
#[when_not_paused]
fn burn_from(e: Env, spender: Address, from: Address, amount: i128) {
Base::burn_from(&e, &spender, &from, amount);
}
#[when_not_paused]
fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) {
Base::approve(&e, &from, &spender, amount, expiration_ledger);
}
fn allowance(e: Env, from: Address, spender: Address) -> i128 {
Base::allowance(&e, &from, &spender)
}
fn balance(e: Env, id: Address) -> i128 {
Base::balance(&e, &id)
}
fn decimals(e: Env) -> u32 {
Base::decimals(&e)
}
fn name(e: Env) -> soroban_sdk::String {
Base::name(&e)
}
fn symbol(e: Env) -> soroban_sdk::String {
Base::symbol(&e)
}
}
#[default_impl]
#[contractimpl]
impl Ownable for FungibleTokenContract {}
#[default_impl]
#[contractimpl]
impl Pausable for FungibleTokenContract {}
Referencia: https://github.com/OpenZeppelin/stellar-contracts/tree/main/examples/fungible-token-interface
Cómo Funciona
Este ejemplo muestra cómo crear un token fungible usando la biblioteca OpenZeppelin Stellar Contracts. La biblioteca ofrece implementaciones desarrolladas y auditadas que siguen las mejores prácticas e estándares de seguridad de la industria.
El contrato implementa varias características clave:
- Cumplimiento con la Interfaz de Token: Implementa completamente la Interfaz estándar de Token para máxima interoperabilidad
- Control de Propiedad: Usa el patrón Ownable para funciones administrativas
- Funcionalidad de Pausa: Permite al propietario pausar/despausar operaciones del token en situaciones de emergencia
- Emisión Segura: Solo el propietario del contrato puede emitir nuevos tokens
Al aprovechar la biblioteca OpenZeppelin, los desarrolladores pueden centrarse en su lógica de negocio específica mientras confían en implementaciones probadas para la funcionalidad central del token.
Uso de Componentes de la Biblioteca OpenZeppelin
La biblioteca OpenZeppelin Stellar Contracts proporciona componentes modulares que pueden componerse fácilmente juntos:
Base
: Funcionalidad central del token fungible con operaciones estándar de tokenOwnable
: Patrón de control de acceso para funciones administrativasPausable
: Mecanismo de paro de emergencia para operaciones del token- Macros:
#[only_owner]
y#[when_not_paused]
para control de acceso declarativo
La macro #[default_impl]
genera automáticamente las implementaciones estándar, reduciendo el código repetitivo y manteniendo la compatibilidad completa con la Interfaz de Token.
Características Mejoradas de Seguridad
Este ejemplo muestra varias funciones de seguridad incorporadas en la biblioteca OpenZeppelin:
Control de Acceso: La macro #[only_owner]
asegura que sólo el propietario designado pueda realizar acciones administrativas como emitir tokens o pausar el contrato.
Operaciones con Pausa: La macro #[when_not_paused]
permite que el contrato sea pausado en situaciones de emergencia, impidiendo transferencias, quemas y aprobaciones pero permitiendo consultas de saldo.
Valores Predeterminados Seguros: La biblioteca implementa valores predeterminados seguros para todas las operaciones de tokens, incluyendo verificaciones de autorización adecuadas y emisiones de eventos.
La biblioteca OpenZeppelin Stellar Contracts ha sido auditada y sigue las prácticas recomendadas de la industria. Usar estos componentes preconstruidos reduce el riesgo de vulnerabilidades de seguridad en tus contratos de tokens.
Uso
Crearemos un token sencillo para la moneda dentro de un juego. Los jugadores pueden ganar tokens completando tareas, y pueden gastar tokens en objetos dentro del juego. El propietario del contrato puede emitir nuevos tokens según sea necesario, y los jugadores pueden transferir tokens entre cuentas.
Así es como podría verse un contrato básico de token fungible:
use soroban_sdk::{contract, contractimpl, Address, Env, String};
use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken};
use stellar_access::ownable::{self as ownable, Ownable};
use stellar_macros::{default_impl, only_owner};
#[contract]
pub struct GameCurrency;
#[contractimpl]
impl GameCurrency {
pub fn __constructor(e: &Env, initial_owner: Address) {
// Set token metadata
Base::set_metadata(
e,
8, // 8 decimals
String::from_str(e, "Game Currency"),
String::from_str(e, "GCUR"),
);
// Set the contract owner
ownable::set_owner(e, &initial_owner);
}
#[only_owner]
pub fn mint_tokens(e: &Env, to: Address, amount: i128) {
// Mint tokens to the recipient
Base::mint(e, &to, amount);
}
}
#[default_impl]
#[contractimpl]
impl FungibleToken for GameCurrency {
type ContractType = Base;
}
#[default_impl]
#[contractimpl]
impl FungibleBurnable for GameCurrency {}
Pruebas
El ejemplo de OpenZeppelin incluye pruebas integrales que verifican tanto la funcionalidad estándar del token como las características adicionales de seguridad.
Escenarios clave de prueba incluyen:
- Operaciones de Token: Pruebas de emisión, transferencia, quema y funciones de aprobación
- Control de Acceso: Verificar que sólo el propietario pueda realizar acciones administrativas
- Funcionalidad de Pausa: Asegurar que las operaciones de token estén correctamente restringidas cuando se encuentren pausadas
- Autorización: Confirmar requisitos de autenticación adecuados para cada función
- Casos límite: Pruebas de condiciones límite y escenarios de error
Para ejecutar las pruebas:
cd examples/fungible-token-interface
cargo test
Las pruebas demuestran cómo la biblioteca OpenZeppelin maneja escenarios complejos manteniendo seguridad y control de acceso adecuados.
Crear el Contrato
Para crear el contrato, usa el comando stellar contract build
.
stellar contract build
Un archivo .wasm
debería generarse en el directorio target
:
target/wasm32v1-none/release/fungible_token_interface.wasm