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
})();