---
title: "Transactions and Instructions"
description:
  Learn about Solana transactions and instructions - the fundamental building
  blocks for interacting with the Solana blockchain. Understand transaction
  structure and instruction composition with practical examples.
---

On Solana, we send [transactions](/docs/core/transactions#transaction) to
interact with the network. Transactions include one or more
[instructions](/docs/core/transactions#instruction) that specify operations to
be processed. The execution logic for instructions are stored on
[programs](/docs/core/programs) deployed to the Solana network, where each
program defines its own set of instructions.

Below are key details about how transactions are processed:

- If a transaction includes multiple instructions, the instructions execute in
  the order they are added to the transaction
- Transactions are "atomic" - either all instructions process successfully, or
  the entire transaction fails and no changes are made.

For simplicity, a transaction can be thought of as a request to process one or
multiple instructions.

![Transaction Simplified](/assets/docs/core/transactions/transaction-simple.svg)

Think of a transaction like an envelope containing forms. Each form is an
instruction that tells the network what we are requesting to do. When you send
the transaction, it's like mailing the envelope to get the forms processed.

## Key Points

- Solana transactions include instructions that are requests to invoke programs
  on the network.
- Transactions are **atomic** - if any instruction fails, the entire transaction
  fails and no changes occur.
- Instructions on a transaction are **processed in sequential order**.
- The maximum size of a transaction is
  [1232](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/packet/src/lib.rs#L29)
  bytes.
- Each instruction requires 3 pieces of information:
  1. The address of the program to invoke
  2. The accounts the instruction will read from or write to
  3. Any additional data required by the instruction (e.g. function arguments)

## Basic Example

Below is a diagram representing a transaction with a single instruction to
transfer SOL from a sender to a receiver.

On Solana, accounts we refer to as "wallets" are owned by the
[System Program](/docs/core/accounts#system-program). Only the program owner can
modify an account's data, so transferring SOL requires sending a transaction to
invoke the System Program.

![SOL Transfer](/assets/docs/core/transactions/sol-transfer.svg)

The sender account must sign (`is_signer`) the transaction to authorize
deducting its lamport balance. Both sender and recipient accounts need to be
marked as writable (`is_writable`) since the lamport balances on these accounts
will change.

Once the transaction is sent, the System Program is invoked to process the
transfer instruction. The System Program then updates the lamport balances of
both the sender and recipient accounts accordingly.

![SOL Transfer Process](/assets/docs/core/transactions/sol-transfer-process.svg)

### Transfer SOL

Here is a [Solana Playground](https://beta.solpg.io/656a0ea7fb53fa325bfd0c3e)
example of how to build a SOL transfer instruction using the
`SystemProgram.transfer` method:

```typescript title="Transfer SOL"
// Define the amount to transfer
const transferAmount = 0.01; // 0.01 SOL

// Create a transfer instruction for transferring SOL from wallet_1 to wallet_2
const transferInstruction = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: receiver.publicKey,
  lamports: transferAmount * LAMPORTS_PER_SOL, // Convert transferAmount to lamports
});

// Add the transfer instruction to a new transaction
const transaction = new Transaction().add(transferInstruction);
```

Run the example by using the `run` command in the Playground terminal or
clicking the "Run" button.

<Callout type="info">

Ensure your Playground wallet has devnet SOL. Get devnet SOL from the
[Solana Faucet](https://faucet.solana.com/).

</Callout>

In the sections below, we'll walk through the details of transactions and
instructions.

## Transaction

A Solana
[transaction](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/src/transaction/mod.rs#L207)
consists of:

1. [Signatures](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/signature/src/lib.rs#L30):
   An array of signatures included on the transaction.
2. [Message](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/legacy.rs#L131):
   List of instructions to be processed atomically.

```rust title="Transaction"
pub struct Transaction {
    #[wasm_bindgen(skip)]
    #[serde(with = "short_vec")]
    pub signatures: Vec<Signature>,

    #[wasm_bindgen(skip)]
    pub message: Message,
}
```

![Transaction Format](/assets/docs/core/transactions/tx_format.png)

The structure of a transaction message consists of:

- [Message Header](/docs/core/transactions#message-header): Specifies the number
  of signer and read-only account.
- [Account Addresses](/docs/core/transactions#array-of-account-addresses): An
  array of account addresses required by the instructions on the transaction.
- [Recent Blockhash](/docs/core/transactions#recent-blockhash): Acts as a
  timestamp for the transaction.
- [Instructions](/docs/core/transactions#array-of-instructions): An array of
  instructions to be executed.

```rust title="Message"
pub struct Message {
    /// The message header, identifying signed and read-only `account_keys`.
    pub header: MessageHeader,

    /// All the account keys used by this transaction.
    #[serde(with = "short_vec")]
    pub account_keys: Vec<Pubkey>,

    /// The id of a recent ledger entry.
    pub recent_blockhash: Hash,

    /// Programs that will be executed in sequence and committed in
    /// one atomic transaction if all succeed.
    #[serde(with = "short_vec")]
    pub instructions: Vec<CompiledInstruction>,
}
```

![Transaction Message](/assets/docs/core/transactions/legacy_message.png)

### Transaction Size

Solana transactions are limited to
[1232](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/packet/src/lib.rs#L29)
bytes. This limit comes from the IPv6 MTU size of 1280 bytes, minus 48 bytes for
network headers (40 bytes IPv6 + 8 bytes fragment header).

A transaction's total size (signatures and message) must stay under this limit
and consists of:

- Signatures: 64 bytes each
- Message: Header (3 bytes), account keys (32 bytes each), recent blockhash (32
  bytes), and instructions

![Transaction Format](/assets/docs/core/transactions/issues_with_legacy_txs.png)

### Message Header

The
[message header](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/mod.rs#L97)
uses three bytes to define account privileges:

1. Required signatures and
   [message version](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/versions/mod.rs#L220-L231)
   (eg. legacy vs v0)
2. Number of read-only signed accounts
3. Number of read-only unsigned accounts

```rust title="MessageHeader"
pub struct MessageHeader {
    /// The number of signatures required for this message to be considered
    /// valid. The signers of those signatures must match the first
    /// `num_required_signatures` of [`Message::account_keys`].
    pub num_required_signatures: u8,

    /// The last `num_readonly_signed_accounts` of the signed keys are read-only
    /// accounts.
    pub num_readonly_signed_accounts: u8,

    /// The last `num_readonly_unsigned_accounts` of the unsigned keys are
    /// read-only accounts.
    pub num_readonly_unsigned_accounts: u8,
}
```

![Message Header](/assets/docs/core/transactions/message_header.png)

### Compact-Array Format

A compact array in a transaction message refers to an array serialized in the
following format:

1. The array length (encoded as
   [compact-u16](https://github.com/anza-xyz/agave/blob/v2.1.13/short-vec/src/lib.rs))
2. The array items listed one after another

![Compact array format](/assets/docs/core/transactions/compact_array_format.png)

This format is used to encode the lengths of the
[Account Addresses](/docs/core/transactions#array-of-account-addresses) and
[Instructions](/docs/core/transactions#array-of-instructions) arrays in
transaction messages.

### Array of Account Addresses

A transaction message contains an array of
[account addresses](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/legacy.rs#L138)
required by its instructions. The array begins with a
[compact-u16](/docs/core/transactions#compact-array-format) number indicating
how many addresses it contains. The addresses are then ordered based on their
privileges, which is determined by the message header.

- Accounts that are writable and signers
- Accounts that are read-only and signers
- Accounts that are writable and not signers
- Accounts that are read-only and not signers

![Compact array of account addresses](/assets/docs/core/transactions/compat_array_of_account_addresses.png)

### Recent Blockhash

Every transaction requires a
[recent blockhash](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/legacy.rs#L141)
that serves two purposes:

1. Acts as a timestamp
2. Prevents duplicate transactions

A blockhash expires after
[150](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/clock/src/lib.rs#L134)
blocks (about 1 minute assuming 400ms block times), after which the transaction
cannot be processed.

You can use the [`getLatestBlockhash`](/docs/rpc/http/getlatestblockhash) RPC
method to get the current blockhash and last block height at which the blockhash
will be valid. Here is an example on
[Solana Playground](https://beta.solpg.io/661a06e1cffcf4b13384d046).

### Array of Instructions

A transaction message contains an array of
[instructions](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/message/legacy.rs#L146)
in the
[CompiledInstruction](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/program/src/instruction.rs#L22)
type. Instructions are converted to this type when added to a transaction.

Like the account addresses array in the message, it starts with a
[compact-u16](/docs/core/transactions#compact-array-format) length followed by
the instruction data. Each instruction contains:

1. Program ID Index: An u8 index that points to the program's address in the
   account addresses array. This specifies the program that will process the
   instruction.
2. Account Indexes: An array of u8 indexes that point to the account addresses
   required for this instruction.
3. Instruction Data: A byte array specifying which instruction to invoke on the
   program and any additional data required by the instruction (eg. function
   arguments).

```rust title="CompiledInstruction"
pub struct CompiledInstruction {
    /// Index into the transaction keys array indicating the program account that executes this instruction.
    pub program_id_index: u8,
    /// Ordered indices into the transaction keys array indicating which accounts to pass to the program.
    #[serde(with = "short_vec")]
    pub accounts: Vec<u8>,
    /// The program input data.
    #[serde(with = "short_vec")]
    pub data: Vec<u8>,
}
```

![Compact array of Instructions](/assets/docs/core/transactions/compact_array_of_ixs.png)

### Example Transaction Structure

Below is an example transaction including a single
[SOL transfer](/docs/core/transactions#basic-example) instruction. The
transaction's components include:

- `header`: Specifies read/write and signer privileges for addresses in the
  `accountKeys` array

- `accountKeys`: Array of all account addresses used in the transaction's
  instructions

- `recentBlockhash`: Blockhash used to timestamp the transaction

- `instructions`: Array of instructions to execute. Each `account` and
  `programIdIndex` in an instruction references the `accountKeys` array by
  index.

- `signatures`: Array including signatures for all accounts required as signers
  by the instructions on the transaction. A signature is created by signing the
  transaction message using the corresponding private key for an account.

```json
"transaction": {
    "message": {
      "header": {
        "numReadonlySignedAccounts": 0,
        "numReadonlyUnsignedAccounts": 1,
        "numRequiredSignatures": 1
      },
      "accountKeys": [
        "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
        "5snoUseZG8s8CDFHrXY2ZHaCrJYsW457piktDmhyb5Jd",
        "11111111111111111111111111111111"
      ],
      "recentBlockhash": "DzfXchZJoLMG3cNftcf2sw7qatkkuwQf4xH15N5wkKAb",
      "instructions": [
        {
          "accounts": [
            0,
            1
          ],
          "data": "3Bxs4NN8M2Yn4TLb",
          "programIdIndex": 2,
          "stackHeight": null
        }
      ],
      "indexToProgramIds": {}
    },
    "signatures": [
      "5LrcE2f6uvydKRquEJ8xp19heGxSvqsVbcqUeFoiWbXe8JNip7ftPQNTAVPyTK7ijVdpkzmKKaAQR7MWMmujAhXD"
    ]
  }
```

## Instruction

An
[instruction](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/instruction/src/lib.rs#L94)
on a deployed [program](/docs/core/accounts#program-account) can be thought of
as a public function that can be called by anyone using the Solana network.

Invoking a program's instruction requires providing three key pieces of
information:

- Program ID: The program being invoked to execute the instruction
- Accounts: List of accounts the instruction requires
- Instruction Data: Byte array specifying the instruction on the program to
  invoke and any function arguments required by the instruction

```rust title="Instruction"
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>,
}
```

![Transaction Instruction](/assets/docs/core/transactions/instruction.svg)

### AccountMeta

Each account required by an instruction must be provided as an
[AccountMeta](https://github.com/anza-xyz/agave/blob/v2.1.13/sdk/instruction/src/account_meta.rs#L25)
that contains:

- `pubkey`: Account's address
- `is_signer`: Whether the account must sign the transaction
- `is_writable`: Whether the instruction will modify the account's data

```rust title="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,
}
```

![AccountMeta](/assets/docs/core/transactions/accountmeta.svg)

By specifying up front which accounts an instruction will read from or write to,
transactions that do not modify the same accounts can be processed in parallel.

### Example Instruction Structure

Below is a simple example showing the structure of a SOL transfer instruction:

- `keys`: Includes the `AccountMeta` for each account required by an
  instruction.
- `programId`: The address of the program which contains the execution logic for
  the instruction invoked.
- `data`: The instruction data for the instruction as a buffer of bytes

```json
{
  "keys": [
    {
      "pubkey": "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
      "isSigner": true,
      "isWritable": true
    },
    {
      "pubkey": "BpvxsLYKQZTH42jjtWHZpsVSa7s6JVwLKwBptPSHXuZc",
      "isSigner": false,
      "isWritable": true
    }
  ],
  "programId": "11111111111111111111111111111111",
  "data": [2, 0, 0, 0, 128, 150, 152, 0, 0, 0, 0, 0]
}
```

## Expanded Example

The details for building program instructions are often abstracted away by
client libraries. However, if one is not available, you can always fall back to
manually building the instruction.

### Transfer SOL

Here is a [Solana Playground](https://beta.solpg.io/656a102efb53fa325bfd0c3f)
example of how to manually build a SOL transfer instruction.

Under the hood, the
[simple example](/docs/core/transactions#simple-sol-transfer) using the
`SystemProgram.transfer` method is functionally equivalent to the more verbose
example below. The `SystemProgram.transfer` method simply abstracts away the
details of creating the instruction data buffer and `AccountMeta` for each
account required by the instruction.

The snippets in the two tabs below are functionally equivalent.

<Tabs items={['Build Instruction Manually', 'Use Library']}>
<Tab value="Build Instruction Manually">

```typescript
// Define the amount to transfer
const transferAmount = 0.01; // 0.01 SOL

// Instruction index for the SystemProgram transfer instruction
const transferInstructionIndex = 2;

// Create a buffer for the data to be passed to the transfer instruction
const instructionData = Buffer.alloc(4 + 8); // uint32 + uint64
// Write the instruction index to the buffer
instructionData.writeUInt32LE(transferInstructionIndex, 0);
// Write the transfer amount to the buffer
instructionData.writeBigUInt64LE(BigInt(transferAmount * LAMPORTS_PER_SOL), 4);

// Manually create a transfer instruction for transferring SOL from sender to receiver
const transferInstruction = new TransactionInstruction({
  keys: [
    { pubkey: sender.publicKey, isSigner: true, isWritable: true },
    { pubkey: receiver.publicKey, isSigner: false, isWritable: true },
  ],
  programId: SystemProgram.programId,
  data: instructionData,
});

// Add the transfer instruction to a new transaction
const transaction = new Transaction().add(transferInstruction);
```

</Tab>
<Tab value="Use Library">

```typescript
// Define the amount to transfer
const transferAmount = 0.01; // 0.01 SOL

// Create a transfer instruction for transferring SOL from wallet_1 to wallet_2
const transferInstruction = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: receiver.publicKey,
  lamports: transferAmount * LAMPORTS_PER_SOL, // Convert transferAmount to lamports
});

// Add the transfer instruction to a new transaction
const transaction = new Transaction().add(transferInstruction);
```

</Tab>
</Tabs>
