---
title: Instruction Structure
description:
  Solana instruction structure — program_id, accounts, and data fields,
  AccountMeta flags is_signer and is_writable, compiled instructions, and SOL
  transfer examples in TypeScript and Rust.
url: /docs/core/instructions/instruction-structure
type: conceptual
prerequisites:
  - /docs/core/instructions
  - /docs/core/accounts/account-structure
related:
  - /docs/core/transactions/transaction-structure
  - /docs/core/cpi
---

<Callout type="info" title="Summary">
  An instruction has 3 fields: `program_id` (which program to invoke),
  `accounts` (AccountMeta list with is_signer/is_writable flags), and `data`
  (byte array of data that the program interprets).
</Callout>

<WithMentions>

## Instruction structure

An
[`Instruction`](https://github.com/anza-xyz/solana-sdk/blob/clock%40v2.2.3/instruction/src/lib.rs#L97)
consists of three fields:

- [`program_id`](mention:program-id): The [ID](#program-id) of the program being
  invoked.
- [`accounts`](mention:accounts): An array of
  [account metadata](#account-metadata)
- [`data`](mention:instruction-data): A byte array with additional [data](#data)
  to be used by the instruction.

```rust title="Instruction struct"
pub struct Instruction {
    /// Pubkey of the program that executes this instruction.
    // !mention program-id
    pub program_id: Pubkey,
    /// Metadata describing accounts that should be passed to the program.
    // !mention accounts
    pub accounts: Vec<AccountMeta>,
    /// Opaque data passed to the program for its own interpretation.
    // !mention instruction-data
    pub data: Vec<u8>,
}
```

</WithMentions>

### Program ID

The instruction's [`program_id`](/docs/references/terminology#program-id) is the
public key address of the program that contains the instruction's execution
logic. The runtime uses this field to route the instruction to the correct
program for processing.

### Account metadata

<WithMentions>

The instruction's `accounts` array is an ordered list of
[`AccountMeta`](https://github.com/anza-xyz/solana-sdk/blob/clock%40v2.2.3/instruction/src/account_meta.rs#L25)
structs. Metadata must be provided for each account the instruction interacts
with. The validator uses this metadata to determine which transactions can run
in parallel. Transactions that write to different accounts can execute in
parallel.

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.](/assets/docs/core/transactions/accountmeta.svg)

Each _rs`AccountMeta`_ has three fields:

- [pubkey](mention:pubkey): The account's public key address
- [is_signer](mention:is-signer): Set to `true` if the account must sign the
  transaction
- [is_writable](mention:is-writable): Set to `true` if the instruction modifies
  the account's data

<Callout>
  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.
</Callout>

```rust title="AccountMeta"
pub struct AccountMeta {
    /// An account's public key.
    // !mention pubkey
    pub pubkey: Pubkey,
    /// True if an `Instruction` requires a `Transaction` signature matching `pubkey`.
    // !mention is-signer
    pub is_signer: bool,
    /// True if the account data or metadata may be mutated during program execution.
    // !mention is-writable
    pub is_writable: bool,
}
```

</WithMentions>

### Data

The instruction's `data` field is a byte array that tells the program which
function to invoke and supplies the arguments for that function. The data
typically begins with a discriminator or index byte(s) that identifies the
target function, followed by the serialized arguments. The encoding format is
defined by each program (for example, Borsh serialization or a custom layout).

Common encoding conventions:

- **Core programs** (System, Stake, Vote): Use a Bincode-serialized enum variant
  index followed by serialized arguments.
- **Anchor programs**: Use an 8-byte discriminator (the first 8 bytes of the
  SHA-256 hash of `"global:<function_name>"`) followed by Borsh-serialized
  arguments.

The runtime does not interpret the `data` field. It is passed as-is to the
program's `process_instruction` entrypoint.

## Compiled instruction

When instructions are serialized into a transaction message, they become
[`CompiledInstruction`](/docs/core/transactions/transaction-structure#instructions)
structs that replace all public keys with compact integer indices into the
message's `account_keys` array.

## Example: SOL transfer instruction

The example below shows the structure of a SOL transfer instruction.

<CodeTabs storage="sol-transfer" flags="r">

```ts !! title="Kit"
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import { systemProgram } 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)))
  .use(systemProgram());

// Generate sender and recipient keypairs
const sender = client.payer;
const recipient = await generateKeyPairSigner();

// Define the amount to transfer
const 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 recipient
const transferInstruction = client.system.instructions.transferSol({
  source: sender,
  destination: recipient.address,
  amount: transferAmount
});

console.log(JSON.stringify(transferInstruction, null, 2));
```

```ts !! title="Legacy"
import {
  LAMPORTS_PER_SOL,
  SystemProgram,
  Transaction,
  Keypair
} from "@solana/web3.js";

// Generate sender and recipient keypairs
const sender = Keypair.generate();
const recipient = new Keypair();

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

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

console.log(JSON.stringify(transferInstruction, null, 2));
```

```rs !! title="Rust"
use anyhow::Result;
use solana_sdk::{native_token::LAMPORTS_PER_SOL, signature::Signer, signer::keypair::Keypair};
use solana_system_interface::instruction::transfer;

#[tokio::main]
async fn main() -> Result<()> {
    // Generate sender and recipient keypairs
    let sender = Keypair::new();
    let recipient = Keypair::new();

    // Define the amount to transfer
    let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOL

    // Create a transfer instruction for transferring SOL from sender to recipient
    let transfer_instruction = transfer(&sender.pubkey(), &recipient.pubkey(), transfer_amount);

    println!("{:#?}", transfer_instruction);

    Ok(())
}
```

</CodeTabs>

<WithMentions>

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`](mention:program-id),
[`accounts`](mention:accounts), [`data`](mention:data).

<CodeTabs storage="sol-transfer">

```json !! title="Kit"
{
  // !mention(1:16) accounts
  "accounts": [
    {
      "address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
      "role": 3,
      "signer": {
        "address": "Hu28vRMGWpQXN56eaE7jRiDDRRz3vCXEs7EKHRfL6bC",
        "keyPair": {
          "privateKey": {},
          "publicKey": {}
        }
      }
    },
    {
      "address": "2mBY6CTgeyJNJDzo6d2Umipw2aGUquUA7hLdFttNEj7p",
      "role": 1
    }
  ],
  // !mention program-id
  "programAddress": "11111111111111111111111111111111",
  // !mention(1:14) data
  "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
  }
}
```

```json !! title="Legacy"
{
  // !mention(1:12) accounts
  "keys": [
    {
      "pubkey": "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
      "isSigner": true,
      "isWritable": true
    },
    {
      "pubkey": "BpvxsLYKQZTH42jjtWHZpsVSa7s6JVwLKwBptPSHXuZc",
      "isSigner": false,
      "isWritable": true
    }
  ],
  // !mention program-id
  "programId": "11111111111111111111111111111111",
  // !mention data
  "data": [2, 0, 0, 0, 128, 150, 152, 0, 0, 0, 0, 0]
}
```

```json !! title="Rust"
{
  // !mention program-id
  "program_id": "11111111111111111111111111111111",
  // !mention(1:12) accounts
  "accounts": [
    {
      "pubkey": "Hhh6vrA6xUNwaNftJVAXSTzfHiRiAKFKLGmHdcRH6Pmo",
      "is_signer": true,
      "is_writable": true
    },
    {
      "pubkey": "6RYMY3mFLixELbfNCMA7zNtzgNfRyEZs5YYkZQb8aK4t",
      "is_signer": false,
      "is_writable": true
    }
  ],
  // !mention data
  "data": [2, 0, 0, 0, 128, 150, 152, 0, 0, 0, 0, 0]
}
```

</CodeTabs>

</WithMentions>

The examples below show how to manually build the transfer instruction. (The
`Expanded Instruction` tab is functionally equivalent to the `Instruction` tab.)

<Callout type="info">
  In practice, you usually don’t have to construct an _rs`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.
</Callout>

<Tabs items={['Kit', 'Legacy', 'Rust']}>

<Tab value="Kit">

<CodeTabs>

```ts !! title="Instruction"
const transferAmount = 0.01; // 0.01 SOL

const transferInstruction = client.system.instructions.transferSol({
  source: sender,
  destination: recipient.address,
  amount: transferAmount * LAMPORTS_PER_SOL
});
```

```ts !! title="Expanded Instruction"
const transferAmount = 0.01; // 0.01 SOL

// Instruction index for the System Program's transfer instruction
const TRANSFER_INSTRUCTION_INDEX = 2;

// Create a buffer for the data to include in the instruction
const instructionData = Buffer.alloc(4 + 8); // uint32 + uint64
instructionData.writeUInt32LE(TRANSFER_INSTRUCTION_INDEX, 0);
instructionData.writeBigUInt64LE(transferAmount * LAMPORTS_PER_SOL, 4);

const SYSTEM_PROGRAM_ADDRESS = "11111111111111111111111111111111" as Address;

// Manually create the transfer instruction
const transferInstruction: IInstruction = {
  programAddress: SYSTEM_PROGRAM_ADDRESS,
  accounts: [
    {
      address: sender.address,
      role: AccountRole.WRITABLE_SIGNER
    },
    {
      address: recipient.address,
      role: AccountRole.WRITABLE
    }
  ],
  data: new Uint8Array(instructionData)
};
```

</CodeTabs>

</Tab>

<Tab value="Legacy">

<CodeTabs>

```ts !! title="Instruction"
const transferAmount = 0.01; // 0.01 SOL

const transferInstruction = SystemProgram.transfer({
  fromPubkey: sender.publicKey,
  toPubkey: receiver.publicKey,
  lamports: transferAmount * LAMPORTS_PER_SOL
});
```

```ts !! title="Expanded Instruction"
const transferAmount = 0.01; // 0.01 SOL

// Instruction index for the System Program's transfer instruction
const transferInstructionIndex = 2;

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

// Manually create a transfer instruction
const transferInstruction = new TransactionInstruction({
  keys: [
    { pubkey: sender.publicKey, isSigner: true, isWritable: true }, // from account, is signer and is writable
    { pubkey: receiver.publicKey, isSigner: false, isWritable: true } // to account, is not signer but is writable
  ],
  programId: SystemProgram.programId,
  data: instructionData
});
```

</CodeTabs>

</Tab>

<Tab value="Rust">

<CodeTabs>

```rs !! title="Instruction"
let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOL

let transfer_instruction =
    system_instruction::transfer(&sender.pubkey(), &recipient.pubkey(), transfer_amount);
```

```rs !! title="Expanded Instruction"
// Instruction index for the System Program's transfer instruction
let transfer_instruction_index: u32 = 2;

// Define the amount to transfer
let transfer_amount = LAMPORTS_PER_SOL / 100; // 0.01 SOL

// Create instruction data manually (12 bytes: 4 for u32 index + 8 for u64 lamports)
let mut instruction_data = Vec::with_capacity(12);
instruction_data.extend_from_slice(&transfer_instruction_index.to_le_bytes());
instruction_data.extend_from_slice(&transfer_amount.to_le_bytes());

// Manually create the transfer instruction
let transfer_instruction = Instruction {
    program_id: system_program::id(),
    accounts: vec![
        AccountMeta::new(sender.pubkey(), true), // from account, is signer and is writable
        AccountMeta::new(recipient.pubkey(), false), // to account, is not signer but is writable
    ],
    data: instruction_data,
};
```

</CodeTabs>

</Tab>

</Tabs>
