Tokens en Solana

Los tokens son activos digitales que representan propiedad sobre diversas categorías de activos. La tokenización permite la digitalización de los derechos de propiedad, sirviendo como un componente fundamental para gestionar activos fungibles y no fungibles.

  • Los tokens fungibles representan activos intercambiables y divisibles del mismo tipo y valor (ej. USDC).
  • Los tokens no fungibles (NFT) representan la propiedad de activos indivisibles (por ejemplo: obras de arte).

Esta sección cubrirá los fundamentos de cómo los tokens son representados en Solana. Estos son conocidos como SPL (Solana Program Library) Tokens.

  • El programa de tokens contiene toda la lógica para interactuar con tokens en la red de Solana (tanto fungibles como no fungibles).

  • Una cuenta mint representa un tipo específico de token y almacena metadatos globales sobre el token, como el suministro total y la autoridad de mint (dirección autorizada para crear nuevas unidades de un token).

  • Una cuenta token mantiene un registro de la propiedad individual de cuántas unidades de un tipo específico de token (mint account) son propiedad de una dirección específica.

Info

Actualmente hay dos versiones del programa de tokens. El programa de tokens original y el programa de tokens con extensiones (Token2022). El programa de tokens con extensiones funciona igual que el programa de tokens original, pero con características y mejoras adicionales. El programa de tokens con extensiones es la versión recomendada para crear nuevos tokens (mint accounts).

Puntos clave #

  • Los tokens representan la propiedad sobre activos fungibles (intercambiables) o no fungibles (únicos).

  • El programa de tokens contiene todas las instrucciones para interactuar con ambos tokens fungibles y no fungibles en la red.

  • El programa de tokens con extensiones es una nueva versión del programa de tokens que incluye características adicionales al mismo tiempo que mantiene las mismas funcionalidades centrales.

  • Una cuenta mint representa un token único en la red y almacena metadatos globales como el suministro total.

  • Una cuenta token registra la propiedad individual de los tokens para una cuenta mint.

  • Una cuenta de token asociada es una cuenta token creada con una dirección derivada de las direcciones del propietario y de la cuenta mint.

Programa de tokens #

El programa de tokens contiene toda la lógica para interactuar con tokens en la red (fungible y no fungible). Todos los tokens en Solana son efectivamente cuentas de datos propiedad del programa de tokens.

Puedes encontrar la lista completa de las instrucciones del programa de tokens aquí.

Programa de tokensPrograma de tokens

Algunas instrucciones comúnmente usadas incluyen:

  • InitializeMint: Crea una cuenta mint para representar un nuevo tipo de token.
  • InitializeAccount: Crea una cuenta token para mantener unidades de un tipo específico de token (mint).
  • MintTo: Crea unidades de un tipo específico de token y las agrega a una cuenta token. Esto aumenta el suministro del token y solo puede hacerlo la autoridad del mint de la cuenta mint respectiva.
  • Transfer: Transfiere unidades de un tipo específico de token de una cuenta a otra.

Cuenta mint #

Los tokens en Solana se identifican de forma única por la dirección de una cuenta mint propiedad del programa de tokens. Esta cuenta es efectivamente un contador global para un token específico, y almacena datos como:

  • Suministro: Suministro total del token
  • Decimales: Precisión decimal del token
  • Autoridad del mint: La cuenta autorizada para crear nuevas unidades del token, aumentando así el suministro
  • Autoridad de congelación: La cuenta autorizada para congelar tokens de ser transferidos desde "cuentas token"

Cuenta mintCuenta mint

Los detalles almacenados en las cuentas mint incluyen lo siguiente:

pub struct Mint {
    /// Autoridad opcional utilizada para crear unidades de tokens. La autoridad sólo puede
    /// ser proporcionada durante la creación del token. Si no hay ninguna autoridad
    /// el token tiene un suministro fijo y no se pueden crear más unidades.
    pub mint_authority: COption<Pubkey>,
    /// Suministro total de los tokens.
    pub supply: u64,
    /// Número de dígitos de base 10 a la derecha del decimal.
    pub decimals: u8,
    /// Es `true` si esta estructura ha sido inicializada
    pub is_initialized: bool,
    /// Autoridad opcional para congelar cuentas token.
    pub freeze_authority: COption<Pubkey>,
}

Para referencia, aquí hay un enlace de Solana Explorer a la cuenta mint de USDC.

Cuenta token #

Para rastrear la propiedad de cada unidad de un token específico, otro tipo de cuenta de datos propiedad del programa de tokens debe ser creado. Esta cuenta es referida como una cuenta token.

Los datos almacenados en la cuenta token que se consultan con más frecuencia son los siguientes:

  • Mint: El tipo de token del que la cuenta token tiene unidades
  • Dueño: La cuenta con la autoridad de transferir tokens desde la cuenta token
  • Monto: Unidades del token que actualmente tiene la cuenta token

Cuenta tokenCuenta token

Los detalles almacenados en cada cuenta token incluyen lo siguiente:

pub struct Account {
    /// El mint asociado con esta cuenta
    pub mint: Pubkey,
    /// El dueño de esta cuenta
    pub owner: Pubkey,
    /// El monto de tokens que esta cuenta tiene
    pub amount: u64,
    /// Si `delegate` es `Some` entonces `delegated_amount` representa
    /// el monto autorizado por el delegado
    pub delegate: COption<Pubkey>,
    /// El estado de la cuenta
    pub state: AccountState,
    /// Si is_native.is_some, este es un token nativo, y el valor registra la
    /// reserva de exención de renta. Una cuenta debe estar exenta de renta, entonces
    /// el valor es utilizado por el procesador para garantizar que las cuentas wrapped
    /// SOL no desciendan por debajo de este umbral.
    pub is_native: COption<u64>,
    /// El monto delegado
    pub delegated_amount: u64,
    /// Autoridad opcional para cerrar la cuenta
    pub close_authority: COption<Pubkey>,
}

Para que una billetera posea unidades de un token determinado, necesita crear una cuenta token para un tipo específico de token (mint) que designe la billetera como el propietario de la cuenta token. Una billetera puede crear múltiples cuentas token para el mismo tipo de token, pero cada cuenta token solo puede ser propiedad de una billetera y tener unidades de un tipo de token.

Relación de cuentasRelación de cuentas

Info

Tenga en cuenta que los datos de cada cuenta token incluyen un campo owner usado para identificar quien tiene autoridad sobre esa cuenta token específica. Esto es independiente del progama owner especificado en la AccountInfo, que es el programa de tokens para todas las cuentas token.

Cuenta token asociada #

Para simplificar el proceso de encontrar la dirección de la cuenta token para un mint y un dueño específico, solemos utilizar cuentas de token asociadas.

Una cuenta de token asociada es una cuenta token cuya dirección es derivada determinísticamente usando la dirección del dueño y la dirección de la cuenta mint. Puede pensar en la cuenta token asociada como la cuenta token "por defecto" para un mint y dueño específico.

Es importante entender que una cuenta token asociada no es un tipo diferente de cuenta token. Es solo una cuenta token con una dirección específica.

Cuenta token asociadaCuenta token asociada

Esto introduce un concepto clave en el desarrollo de Solana: Dirección derivada de un programa (PDA). Conceptualmente, una PDA proporciona una manera determinista de generar una dirección usando algunas entradas predefinidas. Esto nos permite encontrar fácilmente la dirección de una cuenta más adelante.

Aquí está un ejemplo de Solana Playground que deriva la dirección de USDC asociada al dueño. Siempre generará la misma dirección para el mismo mint y dueño.

import { getAssociatedTokenAddressSync } from "@solana/spl-token";
 
const associatedTokenAccountAddress = getAssociatedTokenAddressSync(
  USDC_MINT_ADDRESS,
  OWNER_ADDRESS,
);

Específicamente, la dirección de una cuenta token asociada se deriva usando la siguientes entradas. Aquí está un ejemplo de Solana Playground que genera la misma dirección que el ejemplo anterior.

import { PublicKey } from "@solana/web3.js";
 
const [PDA, bump] = PublicKey.findProgramAddressSync(
  [
    OWNER_ADDRESS.toBuffer(),
    TOKEN_PROGRAM_ID.toBuffer(),
    USDC_MINT_ADDRESS.toBuffer(),
  ],
  ASSOCIATED_TOKEN_PROGRAM_ID,
);

Para que dos billeteras mantengan unidades del mismo tipo de token, cada billetera necesita su propia cuenta token para la cuenta mint específica. La siguiente imagen demuestra cómo se ve esta relación de cuenta.

Relación de cuentas expandidaRelación de cuentas expandida

Ejemplos de Token #

La CLI spl-token puede ser usada para experimentar con tokens SPL. En los siguientes ejemplos, usaremos la terminal de Solana Playground para ejecutar los comandos CLI directamente en el navegador sin tener que instalar el CLI localmente.

La creación de tokens y cuentas requiere SOL para los depósitos de renta de cuentas y tarifas de transacción. Si es la primera vez que utilizas Solana Playground, crea una billetera de Playground y ejecuta el comando solana airdrop en la terminal del Playground. También puedes obtener devnet SOL usando la web faucet.

solana airdrop 2

Ejecuta spl-token --help para una descripción completa de los comandos disponibles.

spl-token --help

Alternativamente, puedes instalar localmente la CLI spl-token usando el siguiente comando. Esto requiere primero instalar Rust.

Info

En las siguientes secciones, las direcciones de cuenta que se muestran al ejecutar el comando CLI diferirán de la salida de ejemplo que se muestra a continuación. Por favor, utilice la dirección que aparece en su terminal de Playground para continuar. Por ejemplo, la dirección de salida de create-token es la cuenta de mint donde la billetera de Playground se establece como la autoridad de mint.

Crear un nuevo token #

Para crear un nuevo token (cuenta mint) ejecuta el siguiente comando en la terminal de Solana Playground.

spl-token create-token

Deberías ver algo similar en la terminal. Puedes inspeccionar el token y los detalles de la transacción en el explorador de solana usando el Address y Signature.

En la siguiente respuesta de ejemplo, el identificador único (dirección) del nuevo token es 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg.

Terminal
Creating token 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg
 
Address:  99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg
Decimals:  9
 
Signature: 44fvKfT1ezBUwdzrCys3fvCdFxbLMnNvBstds76QZyE6cXag5NupBprSXwxPTzzjrC3cA6nvUZaLFTvmcKyzxrm1

Los nuevos tokens inicialmente no tienen suministro. Puedes comprobar el suministro actual de un token usando el siguiente comando:

spl-token supply <TOKEN_ADDRESS>

Ejecutar el comando supply para un token creado recientemente devolverá un valor de 0:

spl-token supply 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg

Bajo el capó, la creación de una cuenta mint requiere el envío de una transacción con dos instrucciones. Aquí está un ejemplo de Javascript en Solana Playground.

  1. Invocas el programa del sistema para crear una cuenta con espacio suficiente para los datos de la cuenta mint y, a continuación, transfieres la propiedad al programa de tokens.

  2. Invocas al programa de tokens para inicializar los datos de la cuenta como una cuenta mint

Crear una cuenta token #

Para tener unidades de un token particular, tienes que primer crear una cuenta token. Para crear una cuenta token, usa el comando:

spl-token create-account [OPTIONS] <TOKEN_ADDRESS>

Por ejemplo, ejecutando el siguiente comando en la terminal de Solana Playground:

spl-token create-account 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg

Devuelve la siguiente salida:

  • AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9 es la dirección de la cuenta token creada para guardar unidades del token especificado en el comando create-account.
Terminal
Creating account AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9
 
Signature: 2BtrynuCLX9CNofFiaw6Yzbx6hit66pup9Sk7aFjwU2NEbFz7NCHD9w9sWhrCfEd73XveAGK1DxFpJoQZPXU9tS1

Por defecto, el comando create-account crea una cuenta token asociada con la dirección de tu billetera como dueña de la cuenta token.

Puedes crear una cuenta token con un dueño diferente usando el siguiente comando:

spl-token create-account --owner <OWNER_ADDRESS> <TOKEN_ADDRESS>

Por ejemplo, ejecutando el siguiente comando:

spl-token create-account --owner 2i3KvjDCZWxBsqcxBHpdEaZYQwQSYE6LXUMx5VjY5XrR 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg

Devuelve la siguiente salida:

  • Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt es la dirección de la cuenta token creada para contener unidades del token especificado en el comando create-account (99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg) y propiedad de la dirección especificada tras el indicador --owner (2i3KvjDCZWxBsqcxBHpdEaZYQwQSYE6LXUMx5VjY5XrR). Esto es útil cuando necesitas para crear una cuenta token para otro usuario.
Terminal
Creating account Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt
 
Signature: 44vqKdfzspT592REDPY4goaRJH3uJ3Ce13G4BCuUHg35dVUbHuGTHvqn4ZjYF9BGe9QrjMfe9GmuLkQhSZCBQuEt

Bajo el capó, crear una cuenta token asociada requiere una sola instrucción que invoca el programa de tokens asociados. Aquí está un ejemplo de Javascript en Solana Playground.

El programa de tokens asociado usa invocaciones entre programas para:

Alternativamente, la creación de una cuenta token utilizando un par de llaves generadas aleatoriamente (no una cuenta de token asociada) requiere el envío de una transacción con dos instrucciones. Aquí está un ejemplo de Javascript en Solana Playground.

  1. Invoca al programa del sistema para crear una cuenta con suficiente espacio para los datos de la cuenta token y luego transferir la propiedad al programa de tokens.

  2. Invoca el programa de tokens para inicializar los datos de la cuenta como una cuenta token

Mint de tokens #

Para crear nuevas unidades de un token, utilice el siguiente comando:

spl-token mint [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> [--] [RECIPIENT_TOKEN_ACCOUNT_ADDRESS]

Por ejemplo, ejecutando el siguiente comando:

spl-token mint 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg 100

Devuelve la siguiente salida:

  • 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg es la dirección de la cuenta mint que incrementa las unidades de tokens (aumentando el suministro total).

  • AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9 es la dirección de la cuenta token de su billetera en la que se están agregando las unidades del token (cantidad creciente).

Terminal
Minting 100 tokens
  Token: 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg
  Recipient: AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9
 
Signature: 2NJ1m7qCraPSBAVxbr2ssmWZmBU9Jc8pDtJAnyZsZJRcaYCYMqq1oRY1gqA4ddQno3g3xcnny5fzr1dvsnFKMEqG

Para hacer mint de tokens a una cuenta token diferente, especificas la dirección de la cuenta token del destinatario. Por ejemplo, ejecutando el siguiente comando:

spl-token mint 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg 100 -- Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt

Devuelve la siguiente salida:

  • 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg es la dirección de la cuenta mint que incrementa las unidades tokens (aumentando el suministro total).

  • Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt` es la dirección de la cuenta token en la que se están agregando las unidades del token (cantidad creciente).

Terminal
Minting 100 tokens
  Token: 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg
  Recipient: Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt
 
Signature: 3SQvNM3o9DsTiLwcEkSPT1Edr14RgE2wC54TEjonEP2swyVCp2jPWYWdD6RwXUGpvDNUkKWzVBZVFShn5yntxVd7

Bajo el capó, crear nuevas unidades de un token requiere invocar la instrucción MintTo en el programa de tokens. Esta instrucción debe ser firmada por la autoridad del mint. La instrucción agrega nuevas unidades del token a una cuenta token y aumenta el suministro total en la cuenta mint. Aquí está un ejemplo de Javascript en Solana Playground.

Transferir tokens #

Para transferir unidades de un token entre dos cuentas token, utilice el siguiente comando:

spl-token transfer [OPTIONS] <TOKEN_ADDRESS> <TOKEN_AMOUNT> <RECIPIENT_ADDRESS
or RECIPIENT_TOKEN_ACCOUNT_ADDRESS>

Por ejemplo, ejecutando el siguiente comando:

spl-token transfer 99zqUzQGohamfYxyo8ykTEbi91iom3CLmwCA75FK5zTg 100 Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt

Devuelve la siguiente salida:

  • AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9 es la dirección de la cuenta token desde la que se transfieren los tokens. Esta sería la dirección de su cuenta token para el token especificado que se está transfiriendo.

  • Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt es la dirección de la cuenta token a la que se transfieren los tokens.

Terminal
Transfer 100 tokens
  Sender: AfB7uwBEsGtrrBqPTVqEgzWed5XdYfM1psPNLmf7EeX9
  Recipient: Hmyk3FSw4cfsuAes7sanp2oxSkE9ivaH6pMzDzbacqmt
 
Signature: 5y6HVwV8V2hHGLTVmTmdySRiEUCZnWmkasAvJ7J6m7JR46obbGKCBqUFgLpZu5zQGwM4Xy6GZ4M5LKd1h6Padx3o

Bajo el capó, la transferencia de tokens requiere invocar la instrucción 'Transfer' en el programa de tokens. Esta instrucción debe ser firmada por el dueño de la cuenta token del remitente. La instrucción transfiere unidades de un token de una cuenta token a otra cuenta token. Aquí está un ejemplo de Javascript en Solana Playground.

Es importante entender que tanto el remitente como el destinatario deben tener cuentas token existentes para el tipo específico de token que se transfiere. El remitente puede incluir instrucciones adicionales en la transacción para crear la cuenta token del destinatario, que generalmente es la cuenta token asociada.

Crear tokens con metadatos #

El programa de tokens con extensiones permite almacenar metadatos adicionales (como nombre, símbolo, enlace a una imagen) directamente en la cuenta mint.

Info

Para utilizar los indicadores de la CLI de las extensiones de tokens, asegúrese de que dispone de una instalación local de la CLI, versión 3.4.0 o posterior:

cargo install --version 3.4.0 spl-token-cli

Para crear un nuevo token con la extensión de metadatos activada, usa el siguiente comando:

spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
--enable-metadata

El comando devuelve la siguiente salida:

  • BdhzpzhTD1MFqBiwNdrRy4jFo2FHFufw3n9e8sVjJczP es la dirección del nuevo token creado con la extensión de metadatos activada.
Terminal
Creación del token BdhzpzhTD1MFqBiwNdrRy4jFo2FHFufw3n9e8sVjJczP bajo el programa TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb Para inicializar los metadatos dentro del mint, ejecute `spl-token initialize-metadata BdhzpzhTD1MFqBiwNdrRy4jFo2FHFufw3n9e8sVjJczP <YOUR_TOKEN_NAME> <YOUR_TOKEN_SYMBOL> <YOUR_TOKEN_URI>`, y firme con la  mint authority.
 
Address:  BdhzpzhTD1MFqBiwNdrRy4jFo2FHFufw3n9e8sVjJczP
Decimals:  9
 
Signature: 5iQofFeXdYhMi9uTzZghcq8stAaa6CY6saUwcdnELST13eNSifiuLbvR5DnRt311frkCTUh5oecj8YEvZSB3wfai

Una vez creado un nuevo token con la extensión de metadatos activada, utilice el siguiente comando para inicializar los metadatos.

spl-token initialize-metadata <TOKEN_MINT_ADDRESS> <YOUR_TOKEN_NAME>
<YOUR_TOKEN_SYMBOL> <YOUR_TOKEN_URI>

El URI del token suele ser un enlace a metadatos que desea asociar al token, estos suelen ser almacenados fuera de la cadena de bloques. Puede encontrar un ejemplo del formato JSON aquí.

Por ejemplo, ejecutando el siguiente comando se almacenarán los metadatos adicionales directamente en la cuenta mint especificada:

spl-token initialize-metadata BdhzpzhTD1MFqBiwNdrRy4jFo2FHFufw3n9e8sVjJczP "TokenName" "TokenSymbol" "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json"

A continuación, puede buscar la dirección de la cuenta mint en un explorador para inspeccionar los metadatos. Por ejemplo, aquí hay un token creado con la extensión de metadatos activada en el explorador SolanaFm.

Puede obtener más información en la guía de la extensión de metadatos. Para obtener más información sobre las distintas extensiones de tokens, consulte la guía de introducción y la Documentación de SPL.