Ir al contenido principal

Transferencias de tokens SPL en Solana: una guía completa

Actualizado el
26 de noviembre de 2025

19 minutos de lectura

Esta guía está disponible en las versiones de Solana Web3.js (versión heredada 1.x) y Solana Kit (v. 2.x). Selecciona la pestaña correspondiente para ver los fragmentos de código y las instrucciones de la biblioteca que prefieras:

Resumen

El envío de tokens de la biblioteca de programas de Solana (SPL) es un mecanismo fundamental para el desarrollo en Solana. Tanto si estás realizando un airdrop de tokens de la lista blanca a tu comunidad, como si estás enviando NFT de forma masiva a otra cartera o gestionando flujos de tokens entre cuentas de depósito en garantía, inevitablemente necesitarás poder transferir tokens SPL. La transferencia de tokens SPL es ligeramente diferente al envío de SOL, por lo que es esencial comprender cómo funciona a medida que avanzas en tu trayectoria de desarrollo en Solana.

Tus funciones

En esta guía, utilizarás TypeScript para escribir un script que transfiera un token SPL de una cuenta a otra en la red de desarrollo de Solana.

Si quieres saber cómo transferir tokens SPL y SOL en un programa utilizando el marco Anchor, echa un vistazo a nuestra guía: «Cómo transferir SOL y tokens SPL utilizando Anchor» .

¿Prefieres un tutorial en vídeo? Sigue los pasos de Sahil y aprende a transferir tokens SPL en Solana Devnet.
¡Suscríbete a nuestro canal de YouTube para ver más vídeos!

Lo que necesitarás

  • Node.js (versión 20.0 o superior) instalado
  • Experiencia con TypeScript y ts-node instalado

Resumen de las cuentas de tokens SPL

Antes de empezar, conviene comprender cómo funcionan algunos componentes de la cuenta del Programa de tokens de Solana: los ID de acuñación y las cuentas de tokens asociadas.

Identificadores de Mint

Cada token SPL tiene un ID de acuñación único que permite distinguirlo de cualquier otro tipo de token. Por ejemplo, el ID de acuñación del token SPL $USDC es EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v y el del token SPL $SAMO es 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU.

Cabe destacar que cada NFT tiene una dirección de acuñación única (lo que, en parte, lo convierte en no fungible). Por ejemplo, el Famous Fox n.º 4679 tiene un ID de acuñación de GS2AtoEoL9DgaYg9xC7cUumftB7vb2CPMrczZCKgJv1Y y el Famous Fox n.º 6562 tiene un ID de acuñación de 7FTdQdMqkk5Xc2oFsYR88BuJt2yyCPReTpqr3viH6b6C. Como verás en un momento, esta es una diferencia significativa entre los dos tokens.

Si quieres saber más sobre la emisión de tokens SPL, echa un vistazo a nuestras guías sobre Cómo crear un token SPL fungible con el nuevo estándar de tokens Metaplex y Cómo acuñar un NFT en Solana.

Cuentas de tokens asociadas

El Programa de Tokens de Solana genera «una clave de cuenta de token a partir de la dirección de la cuenta principal del sistema del usuario y de una dirección de acuñación de tokens, lo que permite al usuario crear una cuenta de token principal para cada token que posea» (Fuente: spl.solana.com). A esa cuenta se la denomina «Cuenta de Token Asociada» o «ATA». En la práctica, una ATA es una cuenta única vinculada a un usuario y a una acuñación de tokens específica. Por ejemplo, supongamos que soy el titular de la cuenta «DEMO...1234». Puedo tener muchas ATA: una por cada emisión de token que posea (por ejemplo, una ATA única para cada $USDC, $SAMO, etc., tal y como se ilustra a continuación).

Las transferencias de tokens siempre deben realizarse entre dos cuentas ATA asociadas a la misma dirección de emisión. Esto significa que no podemos enviar $USDC desde mi cuenta ATA a tu cuenta ATA de $SAMO (véase el ejemplo a continuación).

Como verás en nuestro código dentro de un momento, no todas las cuentas disponen ya de un ATA para cada casa de moneda. Si un usuario no ha interactuado antes con un token, el remitente debe «crearlo» y depositar la cuota necesaria para que la cuenta permanezca activa.

Este concepto puede resultar complicado al principio, pero es importante, ¡así que tómate tu tiempo para asegurarte de que lo entiendes bien! A mí me resulta útil echar un vistazo a mi monedero en Solana Explorer, en la página de detalles de los tokens: https://explorer.solana.com/address/YOUR_WALLET_ADDRESS/tokens?display=detail. Allí podrás ver cada uno de tus ATA, su ID de cuenta y el ID de acuñación asociado:

Si quieres profundizar un poco más en la biblioteca del Programa de tokens SPL de Solana, visita spl.solana.com.

¿Listo para empezar a programar? ¡Yo también!

Configura tu proyecto

Crea un nuevo directorio de proyecto en tu terminal con:

mkdir spl-transfer-kit
cd spl-transfer-kit

Crea un archivo para tu aplicación, app.ts:

echo > app.ts

Inicializa tu proyecto con el indicador «yes» para utilizar los valores predeterminados en tu nuevo paquete:

yarn init --yes
#o
npm init --yes

Crea un archivo tsconfig.json con resolución de módulos moderna:

tsc --init 

Actualiza tu archivo tsconfig.json con el siguiente contenido:

{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"target": "ESNext"
}
}

Instalar las dependencias de Solana Kit

Para este ejercicio, tendremos que añadir Solana Kit y la biblioteca del programa Token. Escribe lo siguiente en tu terminal:

yarn add @solana/kit @solana-program/token
#o
npm install @solana/kit @solana-program/token

Crea un monedero y recibe SOL mediante airdrop

Lo primero que tendremos que hacer es crear una cuenta con un monedero y recargarla. Utilizaremos la práctica herramienta que aparece a continuación para generar automáticamente un nuevo monedero y recibir un airdrop de 1 SOL en él.

🔑Generarun nuevo monedero con Devnet SOL

Nota: Enviar solicitudes de airdrop con demasiada frecuencia puede provocar un error 429 (demasiadas solicitudes).

Una vez que hayas generado correctamente tu par de claves, verás que aparecen dos nuevas constantes: secreto y FROM_KEYPAIR, un par de claves. El secreto es una matriz de 32 bytes que se utiliza para generar las claves pública y privada. La FROM_KEYPAIR es una instancia de Keypair que se utiliza para firmar transacciones (hemos repartido algunos SOL de la red de desarrollo para cubrir las comisiones de gas). Asegúrate de añadirla a tu código debajo de las demás constantes si aún no lo has hecho.

Debajo de las importaciones, pega tu nueva matriz secreta:

const secreto = [0,...,0]; // 👈 Sustituye por tu clave secreta

Emitir o distribuir mediante airdrop algunos tokens SPL

Para completar esta guía, necesitarás algunas fichas SPL en Devnet. Hay varias formas de conseguir fichas SPL de Devnet; a continuación te indicamos algunas.

  1. Crea tus propios tokens fungibles (Guía: Cómo crear un token SPL fungible con el nuevo estándar de tokens Metaplex).
  2. Crea uno o varios NFT con Candy Machine (Guía: Cómo implementar una colección de NFT en Solana utilizando Sugar (Candy Machine)).
  3. Solicita un airdrop de tokens $DUMMY desde un «faucet» de tokens SPL.*
  4. Si ya tienes tokens SPL de Devnet en otra cartera, puedes enviarlos utilizando Phantom u otra interfaz de cartera.*

Antes de continuar, deberías poder acceder a Solana Explorer y comprobar que tu monedero tiene un saldo en SOL y al menos un token SPL en la red de desarrollo (https://explorer.solana.com/address/YOUR_WALLET_ADDRESS/tokens?cluster=devnet). Debería tener un aspecto similar a este:

Empecemos.

Configura tu aplicación

Importar las dependencias necesarias

Abre el archivo app.ts y pega las siguientes importaciones en la línea 1:

import { 
createSolanaRpc,
createSolanaRpcSubscriptions,
address,
pipe,
createTransactionMessage,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signTransactionMessageWithSigners,
sendAndConfirmTransactionFactory,
airdropFactory,
lamports,
getSignatureFromTransaction,
createKeyPairFromBytes,
createSignerFromKeyPair
} from '@solana/kit';
import {
TOKEN_PROGRAM_ADDRESS,
fetchToken,
getTransferInstruction,
findAssociatedTokenPda,
getCreateAssociatedTokenIdempotentInstruction
} from '@solana-program/token';

Además del monedero que hemos creado en el paso anterior, vamos a importar métodos y clases esenciales de Solana Kit y de la biblioteca de programas Token.

Configura tu punto de acceso de Quicknode

Para desarrollar en Solana, necesitarás un punto final de API para conectarte a la red. Puedes utilizar nodos públicos o implementar y gestionar tu propia infraestructura; sin embargo, si quieres tiempos de respuesta 8 veces más rápidos, puedes dejarnos a nosotros el trabajo pesado. Descubre por qué más del 50 % de los proyectos en Solana eligen Quicknode y regístrate aquí para crear una cuenta. Vamos a utilizar un nodo de Solana Devnet.

Copia el enlace del proveedor HTTP:

En el archivo app.ts, debajo de las declaraciones de importación, declara tu RPC y establece la conexión con Solana:

const QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/0123456/';
const QUICKNODE_RPC_SUBSCRIPTIONS = 'wss://example.solana-devnet.quiknode.pro/0123456/';
const rpc = createSolanaRpc(QUICKNODE_RPC);
const rpcSubscriptions = createSolanaRpcSubscriptions(QUICKNODE_RPC_SUBSCRIPTIONS);
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions });

Asegúrate de sustituir el QUICKNODE_RPC y QUICKNODE_RPC_SUBSCRIPTIONS con tus URL de punto final de Quicknode reales (enlaces https y wss del panel de control de Quicknode).

Declarar variables

Vas a tener que declarar algunas variables para ejecutar tu script: tu cuenta de destino (el titular al que deseas transferir los tokens), la dirección de acuñación del token que vas a transferir y el número de tokens que vas a transferir. Añade las siguientes declaraciones debajo de tu configuración RPC:

const CARTERA_DE_DESTINO = dirección('DemoKMZWkk483hX4mUrcJoo3zVvsKhm8XXs28TuwZw9H'); 
const MINT_ADDRESS = dirección('DoJuta7joTSuuoozqQtjtnASRYiVsT435gh4srh5LLGK'); //¡Debes cambiar este valor!
const TRANSFER_AMOUNT = 1n; // Uso de BigInt para importes precisos de tokens

Sustituir MINT_ADDRESS con la dirección de acuñación del token que quieres enviar. Si no conoces la dirección de acuñación de tu token, puedes ir a https://explorer.solana.com/address/YOUR_WALLET_ADDRESS/tokens?cluster=devnet y haz clic en el Copiar icono para obtener tu dirección de Mint. Pégala en el MINT_ADDRESS variable.

Nota: También puedes actualizar tu DESTINATION_WALLET y el TRANSFER_AMOUNT si lo deseas, aunque ambos valores servirán para este ejemplo.

Obtener el número de decimales

Si ya has creado alguna vez un token SPL fungible, sabrás que los decimales son importantes. Dado que la cadena representa el suministro de tokens como un valor entero, debemos convertir la cantidad de tokens en función del número de decimales asignados en los metadatos del token (por ejemplo, si una emisión tiene tres decimales, debemos enviar 1.000 unidades para transferir efectivamente un token [1.000 / 10**3]).

Para obtener los decimales, llamaremos a «getAccountInfo» en nuestra instancia de RPC y analizaremos los datos de la cuenta de Mint. Añade esta función debajo de tus declaraciones:

async function getNumberDecimals(mintAddress) {

// getAccountInfo defaults to binary, using `jsonParsed` lets us read parsed.info.decimals without manual byte offsets.
const accountInfo = await rpc.getAccountInfo(mintAddress, { encoding: 'jsonParsed' }).send();

const data = accountInfo.value?.data;
if (!data || !('parsed' in data) || !data.parsed?.info) {
throw new Error('Failed to find mint account');
}

return (data.parsed.info as { decimals: number }).decimals;
}

Técnicamente, no es necesario si conoces el número de decimales de tu emisión de tokens (puedes encontrarlo en la página del token en Solana Explorer). No obstante, nos gusta incluirlo, ya que te permite adaptar tu código a diferentes emisiones sin tener que modificar ninguna otra variable.

¡Vamos a lo divertido!

Crear función de transferencia

Crea una nueva función asíncrona, ` sendTokens`:

asíncrono función sendTokens() {
}

En primer lugar, tenemos que crear nuestro firmante de pares de claves a partir del array «secret» y recargarlo con SOL. Dentro de la función «sendTokens», añade el paso 1:

    console.log(`Sending ${TRANSFER_AMOUNT} ${MINT_ADDRESS} to ${DESTINATION_WALLET}.`)

//Step 1
console.log(`1 - Creating keypair and funding with SOL`);
const keyPair = await createKeyPairFromBytes(new Uint8Array(secret));
const FROM_KEYPAIR = await createSignerFromKeyPair(keyPair);


// Fund the keypair with SOL for transaction fees
// (you can skip this step if you already have devnet SOL in your FROM_KEYPAIR)
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: FROM_KEYPAIR.address,
lamports: lamports(1_000_000_000n), // 1 SOL
commitment: 'confirmed',
});

console.log(` From wallet: ${FROM_KEYPAIR.address}`);

Nota: En un entorno de producción, se utilizaría generateKeyPairFromBytes(new Uint8Array(secret)) utilizando tu clave secreta real en lugar de generar una nueva.

A continuación, calcularemos las cuentas de tokens asociadas («ATA») para nuestras cuentas de origen y destino. Recuerda que cada cuenta debe tener una ATA que asocie un monedero con una emisión; una ATA solo puede contener tokens de una única emisión; y la emisión de las cuentas de tokens asociadas tanto del remitente como del destinatario debe ser la misma. Añade el paso 2:

    //Step 2
console.log(`2 - Deriving Associated Token Accounts`);

const [sourceTokenAccount] = await findAssociatedTokenPda({
owner: FROM_KEYPAIR.address,
mint: MINT_ADDRESS,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});

const [destinationTokenAccount] = await findAssociatedTokenPda({
owner: DESTINATION_WALLET,
mint: MINT_ADDRESS,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});

console.log(` Source Token Account: ${sourceTokenAccount}`);
console.log(` Destination Token Account: ${destinationTokenAccount}`);

A continuación, debemos comprobar si la cuenta del token de destino existe y crearla si es necesario. Añadir el paso 3:

    console.log(`3 - Checking Destination Token Account`);
let needsDestinationAccount = false;
try {
await rpc.getAccountInfo(destinationTokenAccount).send();
} catch (error) {
needsDestinationAccount = true;
}

if (needsDestinationAccount) {
console.log(` Destination token account does not exist, will create it`);
} else {
console.log(` Destination token account exists`);
}

Obtengamos el número de decimales para calcular correctamente la cantidad. Añade el paso 4:

    //Step 4
console.log(`4 - Fetching Number of Decimals for Mint: ${MINT_ADDRESS}`);
const numberDecimals = await getNumberDecimals(MINT_ADDRESS);
console.log(` Number of Decimals: ${numberDecimals}`);

const transferAmountWithDecimals = TRANSFER_AMOUNT * BigInt(Math.pow(10, numberDecimals));

Por último, tenemos que compilar y enviar nuestra transacción utilizando el enfoque funcional de Solana Kit. Añade el paso 5:

    //Step 5
console.log(`5 - Creating and Sending Transaction`);

const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(FROM_KEYPAIR, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => {
// Add create ATA instruction if needed
if (needsDestinationAccount) {
return appendTransactionMessageInstruction(
getCreateAssociatedTokenIdempotentInstruction({
payer: FROM_KEYPAIR,
ata: destinationTokenAccount,
owner: DESTINATION_WALLET,
mint: MINT_ADDRESS,
}),
tx
);
}
return tx;
},
(tx) => appendTransactionMessageInstruction(
getTransferInstruction({
source: sourceTokenAccount,
destination: destinationTokenAccount,
authority: FROM_KEYPAIR,
amount: transferAmountWithDecimals,
}),
tx
)
);

const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
await sendAndConfirmTransaction(signedTransaction, { commitment: 'confirmed' });
const signature = await getSignatureFromTransaction(signedTransaction);

console.log(
'\x1b[32m', //Green Text
` Transaction Success!🎉`,
`\n https://explorer.solana.com/tx/${signature}?cluster=devnet`
);

Lo que estamos haciendo aquí es indicar a nuestra aplicación que transfiera tokens por valor de TRANSFER_AMOUNT (ten en cuenta que debemos multiplicar por 10 elevado a numberDecimals) desde la cuenta sourceTokenAccount a la cuenta destinationTokenAccount.

El enfoque de Solana Kit utiliza un estilo de programación funcional con el tubo función para crear la transacción paso a paso. Esto proporciona una mayor seguridad de tipos y hace que el proceso de creación de la transacción sea más explícito. También nos encargamos de crear el ATA de destino si no existe dentro de la misma transacción mediante la instrucción idempotente, que no dará error si la cuenta ya existe.

No te fijes en ese «console.log»: solo es un truco para que el resultado aparezca en verde.

🏃‍♂️ Ejecuta tu código

Por último, al final de tu código, llama a sendTokens:

sendTokens().catch(console.error);

En tu terminal, ejecuta el código escribiendo:

ts-node app.ts

¡Enhorabuena! Si has seguido los pasos anteriores, deberías ver algo como esto:

Próximos pasos y resumen

¡Genial! ¡Ya sabes cómo transferir tokens SPL de un usuario a otro! ¿Quieres ir un paso más allá? Aquí tienes un par de ideas para llevar esto un poco más lejos:

  1. ¿Puedes escribir un script que transfiera todos tus NFT a otra cartera? (Pista: quizá te convenga informarte primero sobre cómo recuperar todos los tokens de la cartera de un usuario ).

  2. ¿Podrías escribir un script para distribuir tokens SPL mediante airdrop a varias carteras que puedan o no tener un ATA asociado a tu acuñación? (Pista: echa un vistazo a nuestra guía sobre cómo enviar distribuciones masivas de SOL para inspirarte).

Si lo pruebas o tienes alguna pregunta que hacernos, ¡dínoslo! ¡Síguenos en Discord y Twitter!

¡Nos encanta recibir comentarios!

Si tienes algún comentario o sugerencia sobre nuevos temas, no dudes en hacérnoslo saber. Nos encantaría saber tu opinión.

Comparte esta guía