In the previous section, you learned how to read data from the Solana network by fetching accounts. Writing data to the Solana network requires a transaction. A transaction contains one or more instructions, and each instruction invokes a program.
Programs define the business logic for each instruction. When you send a transaction, the Solana runtime executes the transaction's instructions in order. Transactions are atomic. Either every instruction in the transaction succeeds, or the entire transaction fails.
The examples in this section show how to:
- Transfer SOL between accounts
- Create a new token mint
Transfer SOL
The example below transfers SOL from one account to another. Only the program designated as an account's owner can modify the account's data or deduct lamports from its balance. Wallet accounts are owned by the System Program, so transferring SOL between wallet accounts requires an instruction that invokes the System Program's transfer instruction. The source account must also sign the transaction.
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { getTransferSolInstruction } from "@solana-program/system";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const receiver = await generateKeyPairSigner();const transferInstruction = getTransferSolInstruction({source: client.payer,destination: receiver.address,amount: lamports(10_000_000n)});const result = await client.sendTransaction([transferInstruction]);console.log("Transaction Signature:", result.context.signature);const { value: senderBalance } = await client.rpc.getBalance(client.payer.address).send();const { value: receiverBalance } = await client.rpc.getBalance(receiver.address).send();console.log("Sender Balance:", senderBalance);console.log("Receiver Balance:", receiverBalance);
Create a Kit client for the local test validator. This snippet adds a payer signer, connects to the local RPC endpoint, enables airdrops, and funds the payer with test SOL for the transfer.
const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));
Generate a signer for the receiver. The sender is client.payer, which was
created by generatedPayer() and funded by airdropPayer().
const receiver = await generateKeyPairSigner();
The getTransferSolInstruction() helper creates a System Program instruction.
The instruction transfers SOL from the source signer to the
destination address for the specified
amount of lamports.
const transferInstruction = getTransferSolInstruction({source: client.payer,destination: receiver.address,amount: lamports(10_000_000n)});
Call client.sendTransaction() with an array of instructions. The Kit client
turns the instructions into one transaction, signs with the signers attached to
the instructions, sends the transaction, and waits for confirmation.
const result = await client.sendTransaction([transferInstruction]);console.log("Transaction Signature:", result.context.signature);
After the transaction is confirmed, fetch both balances using client.rpc.
const { value: senderBalance } = await client.rpc.getBalance(client.payer.address).send();const { value: receiverBalance } = await client.rpc.getBalance(receiver.address).send();
Create a token
The example below creates a new token mint using the Token Extensions Program. A mint account is the account that defines a token's global settings, such as decimals, supply, mint authority, and freeze authority.
Creating a mint account requires two instructions:
- Invoke the System Program to create a new account owned by the Token Extensions Program.
- Invoke the Token Extensions Program to initialize that account as a mint.
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { getCreateAccountInstruction } from "@solana-program/system";import {fetchMint,getInitializeMintInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();const space = BigInt(getMintSize());const rent = await client.rpc.getMinimumBalanceForRentExemption(space).send();const createAccountInstruction = getCreateAccountInstruction({payer: client.payer,newAccount: mint,space,lamports: rent,programAddress: TOKEN_2022_PROGRAM_ADDRESS});const initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 2,mintAuthority: client.payer.address,freezeAuthority: client.payer.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});const result = await client.sendTransaction([createAccountInstruction,initializeMintInstruction]);console.log("Mint Address:", mint.address);console.log("Transaction Signature:", result.context.signature);const mintAccount = await fetchMint(client.rpc, mint.address);console.log("Mint Account:", mintAccount);
Create and fund a Kit client, then generate a signer to use as the address of the new mint account. The client payer funds account creation and pays the transaction fee.
const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();
Calculate the mint account size in bytes, then make an RPC request to calculate the lamports required to store that data in the account. This required balance is referred to as rent.
const space = BigInt(getMintSize());const rent = await client.rpc.getMinimumBalanceForRentExemption(space).send();
The first instruction invokes the System Program. The instruction uses the
payer to fund a newAccount,
allocates the mint account space, transfers the rent-exempt
lamports, and assigns ownership to the
programAddress.
const createAccountInstruction = getCreateAccountInstruction({payer: client.payer,newAccount: mint,space,lamports: rent,programAddress: TOKEN_2022_PROGRAM_ADDRESS});
The second instruction invokes the Token Extensions Program. The instruction
initializes the mint address with a
decimals value, a
mintAuthority, a
freezeAuthority, and specifies the
tokenProgram that owns the mint account.
const initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 2,mintAuthority: client.payer.address,freezeAuthority: client.payer.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS});
Send both instructions in one transaction. The create account instruction must come before the initialize mint instruction because the mint account must exist before the Token Extensions Program can write mint data to the account.
const result = await client.sendTransaction([createAccountInstruction,initializeMintInstruction]);
After the transaction is confirmed, fetch the mint account.
const mintAccount = await fetchMint(client.rpc, mint.address);console.log("Mint Account:", mintAccount);
Is this page helpful?