Instructions
Instructions are the fundamental building block for interacting with the Solana blockchain. An instruction is essentially a public function that anyone using the Solana network can call. Each instruction is used to perform a specific action. The execution logic for instructions are stored on programs, where each program defines its own set of instructions. To interact with the Solana network, one or more instructions are added to a transaction and sent to the network to be processed.
SOL transfer example
The diagram below shows how transactions and instructions work together to allow users to interact with the network. In this example, SOL is transferred from one account to another.
The sender account's metadata indicates that it must sign for the transaction. (This allows the System Program to deduct lamports.) Both the sender and recipient accounts must be writable, in order for their lamport balance to change. To execute this instruction, the sender's wallet sends the transaction containing its signature and the message containing the SOL transfer instruction.
SOL transfer diagram
After the transaction is sent, the System Program processes the transfer instruction and updates the lamport balance of both accounts.
SOL transfer process diagram
The example below shows the code relevant to the above diagrams. (See the System Program's transfer instruction.)
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";// Create a connection to clusterconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();const LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Fund sender with airdropawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: sender.address,lamports: lamports(LAMPORTS_PER_SOL), // 1 SOLcommitment: "confirmed"});// Check balance before transferconst { value: preBalance1 } = await rpc.getBalance(sender.address).send();const { value: preBalance2 } = await rpc.getBalance(recipient.address).send();// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount // 0.01 SOL in lamports});// Add the transfer instruction to a new transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();const transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));// Send the transaction to the networkconst signedTransaction =await signTransactionMessageWithSigners(transactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });const transactionSignature = getSignatureFromTransaction(signedTransaction);// Check balance after transferconst { value: postBalance1 } = await rpc.getBalance(sender.address).send();const { value: postBalance2 } = await rpc.getBalance(recipient.address).send();console.log("Sender prebalance:",Number(preBalance1) / Number(LAMPORTS_PER_SOL));console.log("Recipient prebalance:",Number(preBalance2) / Number(LAMPORTS_PER_SOL));console.log("Sender postbalance:",Number(postBalance1) / Number(LAMPORTS_PER_SOL));console.log("Recipient postbalance:",Number(postBalance2) / Number(LAMPORTS_PER_SOL));console.log("Transaction Signature:", transactionSignature);
Console
Click to execute the code.
Instructions
Diagram depicting a transaction with an instruction, broken up into its 3 components
An
Instruction
consists of the following information:
program_id
: The ID of the program being invoked.accounts
: An array of account metadatadata
: A byte array with additional [data] to be used by the instruction.
Instruction struct
pub struct Instruction {/// Pubkey of the program that executes this instruction.pub program_id: Pubkey,/// Metadata describing accounts that should be passed to the program.pub accounts: Vec<AccountMeta>,/// Opaque data passed to the program for its own interpretation.pub data: Vec<u8>,}
Program ID
The instruction's program_id
is the
public key address of the program that contains the instruction's business
logic.
Account metadata
The instruction's accounts
array is an array of
AccountMeta
structs. Metadata must be provided for each account the instruction interacts
with. (This allows transaction to execute instructions in parallel, as long as
they do not modify the same account.)
The diagram below depicts a transaction that contains a single instruction. The
instruction's accounts
array contains metadata for two accounts.
A transaction with one instruction. The instruction contains two AccountMeta structs in its accounts array.
The account metadata includes the following information:
- pubkey: The account's public key address
- is_signer: Set to
true
if the account must sign the transaction - is_writable: Set to
true
if the instruction modifies the account's data
To know which accounts an instruction requires, including which must be writable, read-only, or sign the transaction, you must refer to the implementation of the instruction, as defined by the program.
AccountMeta
pub struct AccountMeta {/// An account's public key.pub pubkey: Pubkey,/// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.pub is_signer: bool,/// True if the account data or metadata may be mutated during program execution.pub is_writable: bool,}
Data
The instruction's data
is a byte array that specifies which of the program's
instruction to invoke. It also includes any arguments required by the
instruction.
Create instruction example
The example below shows the structure of a SOL transfer instruction.
import { generateKeyPairSigner, lamports } from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();// Define the amount to transferconst LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount});console.log(JSON.stringify(transferInstruction, null, 2));
Console
Click to execute the code.
The code below shows the output from the previous code snippets. The format will
differ between SDKs, but notice that each instruction contains the same three
pieces of required information: program_id
,
accounts
, data
.
{"accounts": [{"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC","role": 3,"signer": {"address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC","keyPair": {"privateKey": {},"publicKey": {}}}},{"address": "2mBY6CTgeyJNJDzo6d2Umipw2aGUquUA7hLdFttNEj7p","role": 1}],"programAddress": "11111111111111111111111111111111","data": {"0": 2,"1": 0,"2": 0,"3": 0,"4": 128,"5": 150,"6": 152,"7": 0,"8": 0,"9": 0,"10": 0,"11": 0}}
The examples below show how to manually build the transfer instruction. (The
Expanded Instruction
tab is functionally equivalent to the Instruction
tab.)
In practice, you usually don’t have to construct an Instruction
manually.
Most programs provide client libraries with helper functions that create the
instructions for you. If a library isn't available, you can manually build the
instruction.
const transferAmount = 0.01; // 0.01 SOLconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount * LAMPORTS_PER_SOL});
Is this page helpful?