Intermediate
Intermediate : How to transfer Token on Solana blockchain
To continue with this workshop you need to first complete workshop
Welcome to this tutorial on how to transfer tokens on the Solana blockchain. In this tutorial, we will be covering how to wrap SOL in a token, transfer tokens to another user, and unwrap tokens back to SOL. This tutorial is intended for intermediate users who have already completed the beginner tutorial on creating an account on the Solana network. By the end of this tutorial, you will have a solid understanding of how to transfer tokens on the Solana blockchain and the necessary steps involved in the process.
Example: Wrapping SOL in a Token
When you want to wrap SOL, you can send SOL to an associated token account on the native mint and call syncNative. syncNative updates the amount field on the token account to match the amount of wrapped SOL available. That SOL is only retrievable by closing the token account and choosing the desired address to send the token account’s lamports.
import {NATIVE_MINT, createAssociatedTokenAccountInstruction, getAssociatedTokenAddress, createSyncNativeInstruction, getAccount} from "@solana/spl-token"; import {clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL, SystemProgram, Transaction, sendAndConfirmTransaction} from "@solana/web3.js"; (async () => { const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); const wallet = Keypair.generate(); const airdropSignature = await connection.requestAirdrop( wallet.publicKey, 2 * LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); const associatedTokenAccount = await getAssociatedTokenAddress( NATIVE_MINT, wallet.publicKey ) // Create token account to hold your wrapped SOL const ataTransaction = new Transaction() .add( createAssociatedTokenAccountInstruction( wallet.publicKey, associatedTokenAccount, wallet.publicKey, NATIVE_MINT ) ); await sendAndConfirmTransaction(connection, ataTransaction, [wallet]); // Transfer SOL to associated token account and use SyncNative to update wrapped SOL balance const solTransferTransaction = new Transaction() .add( SystemProgram.transfer({ fromPubkey: wallet.publicKey, toPubkey: associatedTokenAccount, lamports: LAMPORTS_PER_SOL }), createSyncNativeInstruction( associatedTokenAccount ) ) await sendAndConfirmTransaction(connection, solTransferTransaction, [wallet]); const accountInfo = await getAccount(connection, associatedTokenAccount); console.log(`Native: ${accountInfo.isNative}, Lamports: ${accountInfo.amount}`); })();
To unwrap the Token back to SOL:
const walletBalance = await connection.getBalance(wallet.publicKey); console.log(`Balance before unwrapping 1 WSOL: ${walletBalance}`) await closeAccount(connection, wallet, associatedTokenAccount, wallet.publicKey, wallet); const walletBalancePostClose = await connection.getBalance(wallet.publicKey); console.log(`Balance after unwrapping 1 WSOL: ${walletBalancePostClose}`)
Some lamports were removed for transaction fees
Example: Transferring tokens to another user
First the receiver uses spl-token create-account to create their associated token account for the Token type. Then the receiver obtains their wallet address by running solana address and provides it to the sender.
The sender then runs:
import { clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL } from '@solana/web3.js'; import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer } from '@solana/spl-token'; (async () => { // Connect to cluster const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); // Generate a new wallet keypair and airdrop SOL const fromWallet = Keypair.generate(); const fromAirdropSignature = await connection.requestAirdrop(fromWallet.publicKey, LAMPORTS_PER_SOL); // Wait for airdrop confirmation await connection.confirmTransaction(fromAirdropSignature); // Generate a new wallet to receive newly minted token const toWallet = Keypair.generate(); // Create new token mint const mint = await createMint(connection, fromWallet, fromWallet.publicKey, null, 9); // Get the token account of the fromWallet address, and if it does not exist, create it const fromTokenAccount = await getOrCreateAssociatedTokenAccount( connection, fromWallet, mint, fromWallet.publicKey ); // Get the token account of the toWallet address, and if it does not exist, create it const toTokenAccount = await getOrCreateAssociatedTokenAccount(connection, fromWallet, mint, toWallet.publicKey); // Mint 1 new token to the "fromTokenAccount" account we just created let signature = await mintTo( connection, fromWallet, mint, fromTokenAccount.address, fromWallet.publicKey, 1000000000 ); console.log('mint tx:', signature); // Transfer the new token to the "toTokenAccount" we just created signature = await transfer( connection, fromWallet, fromTokenAccount.address, toTokenAccount.address, fromWallet.publicKey, 50 ); })();
Example: Transferring tokens to another user, with sender-funding
If the receiver does not yet have an associated token account, the sender may choose to fund the receiver’s account.
The receiver obtains their wallet address by running solana address and provides it to the sender.
The sender then runs to fund the receiver’s associated token account, at the sender’s expense, and then transfers 50 tokens into it:
const signature = await transfer( connection, toWallet, fromTokenAccount.address, toTokenAccount.address, fromWallet.publicKey, 50, [fromWallet, toWallet] );
Example: Transferring tokens to an explicit recipient token account
Tokens may be transferred to a specific recipient token account. The recipient token account must already exist and be of the same Token type.
import {getAccount, createMint, createAccount, mintTo, getOrCreateAssociatedTokenAccount, transfer} from "@solana/spl-token"; import {clusterApiUrl, Connection, Keypair, LAMPORTS_PER_SOL} from "@solana/web3.js"; (async () => { const connection = new Connection(clusterApiUrl('devnet'), 'confirmed'); const wallet = Keypair.generate(); const auxiliaryKeypair = Keypair.generate(); const airdropSignature = await connection.requestAirdrop( wallet.publicKey, LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); const mint = await createMint( connection, wallet, wallet.publicKey, wallet.publicKey, 9 ); // Create custom token account const auxiliaryTokenAccount = await createAccount( connection, wallet, mint, wallet.publicKey, auxiliaryKeypair ); const associatedTokenAccount = await getOrCreateAssociatedTokenAccount( connection, wallet, mint, wallet.publicKey ); await mintTo( connection, wallet, mint, associatedTokenAccount.address, wallet, 50 ); const accountInfo = await getAccount(connection, associatedTokenAccount.address); console.log(accountInfo.amount); // 50 await transfer( connection, wallet, associatedTokenAccount.address, auxiliaryTokenAccount, wallet, 50 ); const auxAccountInfo = await getAccount(connection, auxiliaryTokenAccount); console.log(auxAccountInfo.amount); // 50 })();