---
title: Program Structure
description:
  Learn how to structure Solana programs in Rust, including entrypoints, state
  management, instruction handling, and testing.
h1: Rust Program Structure
---

Solana programs written in Rust have minimal structural requirements, allowing
for flexibility in how code is organized. The only requirement is that a program
must have an `entrypoint`, which defines where the execution of a program
begins.

## Program Structure

While there are no strict rules for file structure, Solana programs typically
follow a common pattern:

- `entrypoint.rs`: Defines the entrypoint that routes incoming instructions.
- `state.rs`: Defines program state (account data).
- `instructions.rs`: Defines the instructions that the program can execute.
- `processor.rs`: Defines the instruction handlers (functions) that implement
  the business logic for each instruction.
- `error.rs`: Defines custom errors that the program can return.

For example, see the
[Token Program](https://github.com/solana-program/token/tree/main/program/src).

## Example Program

To demonstrate how to build a native Rust program with multiple instructions,
we'll walk through a simple counter program that implements two instructions:

1. `InitializeCounter`: Creates and initializes a new account with an initial
   value.
2. `IncrementCounter`: Increments the value stored in an existing account.

For simplicity, the program will be implemented in a single `lib.rs` file,
though in practice you may want to split larger programs into multiple files.

<Accordions>
<Accordion title="Full Program Code">

<CodeTabs>

```rs !! title="src/lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

// Program entrypoint
entrypoint!(process_instruction);

// Function to route instructions to the correct handler
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Unpack instruction data
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    // Match instruction type
    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => process_increment_counter(program_id, accounts)?,
    };
    Ok(())
}

// Instructions that our program can execute
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 }, // variant 0
    IncrementCounter,                         // variant 1
}

// Initialize a new counter account
fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let counter_account = next_account_info(accounts_iter)?;
    let payer_account = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;

    // Size of our counter account
    let account_space = 8; // u64 requires 8 bytes

    // Calculate minimum balance for rent exemption
    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(account_space);

    // Create the counter account
    invoke(
        &system_instruction::create_account(
            payer_account.key,    // Account paying for the new account
            counter_account.key,  // Account to be created
            required_lamports,    // Amount of lamports to transfer to the new account
            account_space as u64, // Size in bytes to allocate for the data field
            program_id,           // Set program owner to our program
        ),
        &[
            payer_account.clone(),
            counter_account.clone(),
            system_program.clone(),
        ],
    )?;

    // Create a new CounterAccount struct with the initial value
    let counter_data = CounterAccount {
        count: initial_value,
    };

    // Get a mutable reference to the counter account's data
    let mut account_data = &mut counter_account.data.borrow_mut()[..];

    // Serialize the CounterAccount struct into the account's data
    counter_data.serialize(&mut account_data)?;

    msg!("Counter initialized with value: {}", initial_value);

    Ok(())
}

// Update an existing counter's value
fn process_increment_counter(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();
    let counter_account = next_account_info(accounts_iter)?;

    // Verify account ownership
    if counter_account.owner != program_id {
        return Err(ProgramError::IncorrectProgramId);
    }

    // Mutable borrow the account data
    let mut data = counter_account.data.borrow_mut();

    // Deserialize the account data into our CounterAccount struct
    let mut counter_data: CounterAccount = CounterAccount::try_from_slice(&data)?;

    // Increment the counter value
    counter_data.count = counter_data
        .count
        .checked_add(1)
        .ok_or(ProgramError::InvalidAccountData)?;

    // Serialize the updated counter data back into the account
    counter_data.serialize(&mut &mut data[..])?;

    msg!("Counter incremented to: {}", counter_data.count);
    Ok(())
}

// Struct representing our counter account's data
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    count: u64,
}

#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        // Create a new LiteSVM instance
        let mut svm = LiteSVM::new();

        // Create a keypair for the transaction payer
        let payer = Keypair::new();

        // Airdrop some lamports to the payer
        svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();

        // Load our program
        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();
        svm.add_program_from_file(program_id, "target/deploy/counter_program.so")
            .unwrap();

        // Create a new keypair to use as the address for our counter account
        let counter_keypair = Keypair::new();
        let initial_value: u64 = 42;

        // Step 1: Initialize the counter
        println!("Testing counter initialization...");

        // Use Borsh serialization for the instruction
        let init_instruction_data =
            borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
                .expect("Failed to serialize instruction");

        let initialize_instruction = Instruction::new_with_bytes(
            program_id,
            &init_instruction_data,
            vec![
                AccountMeta::new(counter_keypair.pubkey(), true),
                AccountMeta::new(payer.pubkey(), true),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        );

        // Create transaction
        let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(&[&payer, &counter_keypair], message, svm.latest_blockhash());

        // Send transaction
        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Initialize transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);

        // Check account data
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");
        assert_eq!(counter.count, 42);
        println!(
            "Counter initialized successfully with value: {}",
            counter.count
        );

        // Step 2: Increment the counter
        println!("Testing counter increment...");

        // Use Borsh serialization for increment instruction
        let increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter)
            .expect("Failed to serialize instruction");

        let increment_instruction = Instruction::new_with_bytes(
            program_id,
            &increment_data,
            vec![AccountMeta::new(counter_keypair.pubkey(), true)],
        );

        // Create transaction
        let message = Message::new(&[increment_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(&[&payer, &counter_keypair], message, svm.latest_blockhash());

        // Send transaction
        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Increment transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);

        // Check account data
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");
        assert_eq!(counter.count, 43);
        println!("Counter incremented successfully to: {}", counter.count);
    }
}
```

```rs !! title="examples/client.rs"
use counter_program::CounterInstruction;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    system_program,
    transaction::Transaction,
};
use std::str::FromStr;

#[tokio::main]
async fn main() {
    // Replace with your actual program ID from deployment
    let program_id = Pubkey::from_str("AUia4JuToXDAB4gR2ZXWqJ6kDyCqn7WqunGAgw1KxdKU")
        .expect("Invalid program ID");

    // Connect to local cluster
    let rpc_url = String::from("http://localhost:8899");
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());

    // Generate a new keypair for paying fees
    let payer = Keypair::new();

    // Request airdrop of 1 SOL for transaction fees
    println!("Requesting airdrop...");
    let airdrop_signature = client
        .request_airdrop(&payer.pubkey(), 1_000_000_000)
        .expect("Failed to request airdrop");

    // Wait for airdrop confirmation
    loop {
        if client
            .confirm_transaction(&airdrop_signature)
            .unwrap_or(false)
        {
            break;
        }
        std::thread::sleep(std::time::Duration::from_millis(500));
    }
    println!("Airdrop confirmed");

    println!("\nInitializing counter...");
    let counter_keypair = Keypair::new();
    let initial_value = 100u64;

    // Serialize the initialize instruction data
    let instruction_data = borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
        .expect("Failed to serialize instruction");

    let initialize_instruction = Instruction::new_with_bytes(
        program_id,
        &instruction_data,
        vec![
            AccountMeta::new(counter_keypair.pubkey(), true),
            AccountMeta::new(payer.pubkey(), true),
            AccountMeta::new_readonly(system_program::id(), false),
        ],
    );

    let mut transaction =
        Transaction::new_with_payer(&[initialize_instruction], Some(&payer.pubkey()));

    let blockhash = client
        .get_latest_blockhash()
        .expect("Failed to get blockhash");
    transaction.sign(&[&payer, &counter_keypair], blockhash);

    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => {
            println!("Counter initialized!");
            println!("Transaction: {}", signature);
            println!("Counter address: {}", counter_keypair.pubkey());
        }
        Err(err) => {
            eprintln!("Failed to initialize counter: {}", err);
            return;
        }
    }

    println!("\nIncrementing counter...");
    // Serialize the increment instruction data
    let increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter)
        .expect("Failed to serialize instruction");

    let increment_instruction = Instruction::new_with_bytes(
        program_id,
        &increment_data,
        vec![AccountMeta::new(counter_keypair.pubkey(), true)],
    );

    let mut transaction =
        Transaction::new_with_payer(&[increment_instruction], Some(&payer.pubkey()));

    transaction.sign(&[&payer, &counter_keypair], blockhash);

    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => {
            println!("Counter incremented!");
            println!("Transaction: {}", signature);
        }
        Err(err) => {
            eprintln!("Failed to increment counter: {}", err);
        }
    }
}
```

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"

[dev-dependencies]
litesvm = "0.6.1"
solana-client = "2.2.0"
solana-sdk = "2.2.0"
tokio = "1.47.1"

[[example]]
name = "client"
path = "examples/client.rs"
```

</CodeTabs>

</Accordion>
</Accordions>

## Part 1: Writing the Program

Let's start by building the counter program. We'll create a program that can
initialize a counter with a starting value and increment it.

<ScrollyCoding>

## !!steps Create a new program

First, let's create a new Rust project for our Solana program.

```terminal
$ cargo new counter_program --lib
$ cd counter_program
```

You should see the default `src/lib.rs` and `Cargo.toml` files.

<Callout type="warn">
  Update the `edition` field in `Cargo.toml` to 2021. Otherwise, you might
  encounter an error when building the program.
</Callout>

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
# !mark
edition = "2021"

[dependencies]
```

```rs !! title="lib.rs"
pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
```

## !!steps Add dependencies

Now let's add the necessary dependencies for building a Solana program. We need
`solana-program` for the core SDK and `borsh` for serialization.

```terminal
$ cargo add solana-program@2.2.0
$ cargo add borsh
```

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"

[dependencies]
# !focus(1:4)
# Borsh is used for serializing/deserializing
borsh = "1.5.7"
# The core Solana program SDK
solana-program = "2.2.0"
```

<Callout type="info">
  There is no requirement to use Borsh. However, it is a commonly used
  serialization library for Solana programs.
</Callout>

## !!steps Configure crate-type

Solana programs must be compiled as dynamic libraries. Add the `[lib]` section
to configure how Cargo builds the program.

```toml title="Cargo.toml"
[lib]
crate-type = ["cdylib", "lib"]
```

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"

# !focus(1:2)
[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"
```

<Callout type="warn">
  If you don't include this config, the target/deploy directory will not be
  generated when you build the program.
</Callout>

## !!steps Setup program entrypoint

Every Solana program has an entrypoint, which is the function that gets called
when the program is invoked. Let's start with adding the imports we'll need for
the program and setting up the entrypoint.

Add the following code to `lib.rs`:

```rs title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    Ok(())
}
```

The
[entrypoint](https://github.com/anza-xyz/solana-sdk/blob/449d97c0ed164611dae538e2ee91ca0caaaec515/program-entrypoint/src/lib.rs#L126-L140)
macro handles the deserialization of the `input` data into the parameters of the
`process_instruction` function.

A Solana program `entrypoint` has the following function signature. Developers
are free to create their own implementation of the `entrypoint` function.

```rs
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64;
```

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

// This macro defines the program's entrypoint
// !mark
entrypoint!(process_instruction);

// The main function that processes all incoming instructions
pub fn process_instruction(
    program_id: &Pubkey,      // This program's public key
    accounts: &[AccountInfo],  // Accounts passed to the instruction
    instruction_data: &[u8],   // Raw instruction data as bytes
) -> ProgramResult {
    // Program logic will go here
    Ok(())
}
```

## !!steps Define program state

Now let's define the data structure that will be stored in our counter accounts.
This is the data that will be stored in the `data` field of the account.

Add the following code to `lib.rs`:

```rs title="lib.rs"
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    Ok(())
}

// !focus(1:7)
// Define the data structure for our counter account
// The derive macros enable automatic serialization/deserialization
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    // Store the counter value
    pub count: u64,
}
```

## !!steps Define instruction enum

Let's define the instructions our program can execute. We'll use an enum where
each variant represents a different instruction.

Add the following code to `lib.rs`:

```rs title="lib.rs"
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 },
    IncrementCounter,
}
```

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    Ok(())
}

// !focus(1:8)
// Define the instructions our program can execute
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    // Variant 0: Initialize a counter with a starting value
    InitializeCounter { initial_value: u64 },
    // Variant 1: Increment an existing counter by 1
    IncrementCounter,
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

## !!steps Implement instruction deserialization

Now we need to deserialize the `instruction_data` (raw bytes) into one of our
`CounterInstruction` enum variants. The Borsh `try_from_slice` method handles
this conversion automatically.

Update the `process_instruction` function to use Borsh deserialization:

```rs title="lib.rs"
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    Ok(())
}
```

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

// !focus(1:10)
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Deserialize instruction data using Borsh
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    Ok(())
}

// !focus(1:8)
// Instructions are automatically serialized/deserialized by Borsh
// The enum variants are numbered 0, 1, etc. in order of declaration
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 },  // Variant 0
    IncrementCounter,                          // Variant 1
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

## !!steps Route instructions to handlers

Now let's update the main `process_instruction` function to route instructions
to their appropriate handler functions.

This routing pattern is common in Solana programs. The `instruction_data` is
deserialized into a variant of an enum representing the instruction, then the
appropriate handler function is called. Each handler function includes the
implementation for that instruction.

Add the following code to `lib.rs` updating the `process_instruction` function
and adding the handlers for the `InitializeCounter` and `IncrementCounter`
instructions:

```rs title="lib.rs"
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => {
            process_increment_counter(program_id, accounts)?
        }
    };
    Ok(())
}

fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    Ok(())
}

fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    Ok(())
}
```

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

// !focus(1:40)
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Deserialize the instruction using Borsh
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    // Route to the appropriate handler based on the instruction
    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => {
            process_increment_counter(program_id, accounts)?
        }
    };

    Ok(())
}

// Handler function for initializing a counter
fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    // Implementation coming next
    Ok(())
}

// Handler function for incrementing a counter
fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    // Implementation coming next
    Ok(())
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 },
    IncrementCounter,
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

## !!steps Implement initialize handler

Let's implement the handler to create and initialize a new counter account.
Since only the System Program can create accounts on Solana, we'll use a Cross
Program Invocation (CPI), essentially calling another program from our program.

Our program makes a CPI to call the System Program's `create_account`
instruction. The new account is created with our program as the owner, giving
our program the ability to write to the account and initialize the data.

Add the following code to `lib.rs` updating the `process_initialize_counter`
function:

```rs title="lib.rs"
fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let counter_account = next_account_info(accounts_iter)?;
    let payer_account = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;

    let account_space = 8;

    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(account_space);

    invoke(
        &system_instruction::create_account(
            payer_account.key,
            counter_account.key,
            required_lamports,
            account_space as u64,
            program_id,
        ),
        &[
            payer_account.clone(),
            counter_account.clone(),
            system_program.clone(),
        ],
    )?;

    let counter_data = CounterAccount {
        count: initial_value,
    };

    let mut account_data = &mut counter_account.data.borrow_mut()[..];
    counter_data.serialize(&mut account_data)?;

    msg!("Counter initialized with value: {}", initial_value);

    Ok(())
}
```

<Callout type="warn">
  This instruction is for demonstration purposes only. It does not include
  security and validation checks that are required for production programs.
</Callout>

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,                      // For logging messages
    program::invoke,          // For Cross Program Invocations
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,       // System Program instructions
    sysvar::{rent::Rent, Sysvar},  // For rent calculations
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => {
            process_increment_counter(program_id, accounts)?
        }
    };

    Ok(())
}

// !focus(1:50)
fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    // Create an iterator over the accounts
    let accounts_iter = &mut accounts.iter();

    // Extract the required accounts in order
    let counter_account = next_account_info(accounts_iter)?;  // The new counter account
    let payer_account = next_account_info(accounts_iter)?;    // Who pays for the account
    let system_program = next_account_info(accounts_iter)?;   // System Program for CPI

    // Calculate the space needed for our counter data
    let account_space = 8; // 8 bytes for a u64

    // Get the minimum balance required for rent exemption
    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(account_space);

    // Create the counter account via CPI to the System Program
    // !focus(1:15)
    invoke(
        &system_instruction::create_account(
            payer_account.key,    // Account paying for creation
            counter_account.key,  // New account being created
            required_lamports,    // Lamports to transfer
            account_space as u64, // Space to allocate in bytes
            program_id,          // Program that will own this account (our program)
        ),
        &[
            payer_account.clone(),
            counter_account.clone(),
            system_program.clone(),
        ],
    )?;

    // Initialize the counter data
    let counter_data = CounterAccount {
        count: initial_value,
    };

    // Serialize and write the data to the account
    let mut account_data = &mut counter_account.data.borrow_mut()[..];
    counter_data.serialize(&mut account_data)?;

    msg!("Counter initialized with value: {}", initial_value);

    Ok(())
}

fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    Ok(())
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 },
    IncrementCounter,
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

## !!steps Implement increment handler

Now let's implement the handler that increments an existing counter. This
instruction:

- Reads the account `data` field for the `counter_account`
- Deserializes it into a `CounterAccount` struct
- Increments the `count` field by 1
- Serializes the `CounterAccount` struct back into the account's `data` field

Add the following code to `lib.rs` updating the `process_increment_counter`
function:

```rs title="lib.rs"
fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let counter_account = next_account_info(accounts_iter)?;

    if counter_account.owner != program_id {
        return Err(ProgramError::IncorrectProgramId);
    }

    let mut data = counter_account.data.borrow_mut();

    let mut counter_data: CounterAccount = CounterAccount::try_from_slice(&data)?;

    counter_data.count = counter_data
        .count
        .checked_add(1)
        .ok_or(ProgramError::InvalidAccountData)?;

    counter_data.serialize(&mut &mut data[..])?;

    msg!("Counter incremented to: {}", counter_data.count);

    Ok(())
}
```

<Callout type="warn">
  This instruction is for demonstration purposes only. It does not include
  security and validation checks that are required for production programs.
</Callout>

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

entrypoint!(process_instruction);

pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => {
            process_increment_counter(program_id, accounts)?
        }
    };

    Ok(())
}

fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let counter_account = next_account_info(accounts_iter)?;
    let payer_account = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;

    let account_space = 8;

    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(account_space);

    invoke(
        &system_instruction::create_account(
            payer_account.key,
            counter_account.key,
            required_lamports,
            account_space as u64,
            program_id,
        ),
        &[
            payer_account.clone(),
            counter_account.clone(),
            system_program.clone(),
        ],
    )?;

    let counter_data = CounterAccount {
        count: initial_value,
    };

    let mut account_data = &mut counter_account.data.borrow_mut()[..];
    counter_data.serialize(&mut account_data)?;

    msg!("Counter initialized with value: {}", initial_value);

    Ok(())
}

// !focus(1:33)
fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    // Get the counter account to increment
    let counter_account = next_account_info(accounts_iter)?;

    // Security check: Verify this program owns the account
    if counter_account.owner != program_id {
        return Err(ProgramError::IncorrectProgramId);
    }

    // Get a mutable reference to the account's data
    let mut data = counter_account.data.borrow_mut();

    // Deserialize the current counter value
    let mut counter_data: CounterAccount = CounterAccount::try_from_slice(&data)?;

    // Increment the counter value
    counter_data.count = counter_data
        .count
        .checked_add(1)
        .ok_or(ProgramError::InvalidAccountData)?;

    // Serialize the updated data back to the account
    counter_data.serialize(&mut &mut data[..])?;

    msg!("Counter incremented to: {}", counter_data.count);

    Ok(())
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    InitializeCounter { initial_value: u64 },
    IncrementCounter,
}

#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    pub count: u64,
}
```

## !!steps Completed Program

Congratulations! You've built a complete Solana program that demonstrates the
basic structure shared by all Solana programs:

- **Entrypoint**: Defines where program execution begins and routes all incoming
  requests to appropriate instruction handlers
- **Instruction Handling**: Defines instructions and their associated handlers
  functions
- **State Management**: Defines account data structures and manages their state
  in program owned accounts
- **Cross Program Invocation (CPI)**: Calls the System Program to create new
  program owned accounts

The next step is to test the program to ensure everything works correctly.

```rs !! title="lib.rs"
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program::invoke,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_instruction,
    sysvar::{rent::Rent, Sysvar},
};

// Program entrypoint - this is where execution starts
entrypoint!(process_instruction);

/// Main instruction processing function
/// Routes incoming instructions to appropriate handlers
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // Parse instruction data
    let instruction = CounterInstruction::try_from_slice(instruction_data)
        .map_err(|_| ProgramError::InvalidInstructionData)?;

    // Route to appropriate handler
    match instruction {
        CounterInstruction::InitializeCounter { initial_value } => {
            msg!("Instruction: Initialize Counter");
            process_initialize_counter(program_id, accounts, initial_value)?
        }
        CounterInstruction::IncrementCounter => {
            msg!("Instruction: Increment Counter");
            process_increment_counter(program_id, accounts)?
        }
    };

    Ok(())
}

/// Instructions supported by the counter program
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
    /// Initialize a new counter with the given value
    InitializeCounter { initial_value: u64 },

    /// Increment an existing counter by 1
    IncrementCounter,
}

/// Initialize a new counter account
///
/// Accounts expected:
/// 1. `[signer, writable]` Counter account to create
/// 2. `[signer, writable]` Payer account
/// 3. `[]` System Program
fn process_initialize_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    initial_value: u64,
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();

    let counter_account = next_account_info(accounts_iter)?;
    let payer_account = next_account_info(accounts_iter)?;
    let system_program = next_account_info(accounts_iter)?;

    let account_space = 8;
    let rent = Rent::get()?;
    let required_lamports = rent.minimum_balance(account_space);

    // Create account via CPI to System Program
    invoke(
        &system_instruction::create_account(
            payer_account.key,
            counter_account.key,
            required_lamports,
            account_space as u64,
            program_id,
        ),
        &[
            payer_account.clone(),
            counter_account.clone(),
            system_program.clone(),
        ],
    )?;

    // Initialize counter data
    let counter_data = CounterAccount {
        count: initial_value,
    };

    let mut account_data = &mut counter_account.data.borrow_mut()[..];
    counter_data.serialize(&mut account_data)?;

    msg!("Counter initialized with value: {}", initial_value);

    Ok(())
}

/// Increment an existing counter
///
/// Accounts expected:
/// 1. `[writable]` Counter account to increment
fn process_increment_counter(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
) -> ProgramResult {
    let accounts_iter = &mut accounts.iter();
    let counter_account = next_account_info(accounts_iter)?;

    // Verify ownership
    if counter_account.owner != program_id {
        return Err(ProgramError::IncorrectProgramId);
    }

    // Read, update, write
    let mut data = counter_account.data.borrow_mut();
    let mut counter_data: CounterAccount = CounterAccount::try_from_slice(&data)?;

    counter_data.count = counter_data
        .count
        .checked_add(1)
        .ok_or(ProgramError::InvalidAccountData)?;

    counter_data.serialize(&mut &mut data[..])?;

    msg!("Counter incremented to: {}", counter_data.count);

    Ok(())
}

/// Counter account data structure
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
    /// Current counter value
    pub count: u64,
}
```

</ScrollyCoding>

## Part 2: Testing the Program

Now let's test our counter program. We'll use
[LiteSVM](https://github.com/LiteSVM/litesvm), a testing framework that lets us
test programs without deploying to a cluster.

<ScrollyCoding>

## !!steps Add test dependencies

First, let's add the dependencies needed for testing. We'll use `litesvm` for
testing and `solana-sdk`.

```terminal
$ cargo add litesvm@0.6.1 --dev
$ cargo add solana-sdk@2.2.0 --dev
```

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"

# !focus(1:3)
[dev-dependencies]
litesvm = "0.6.1"
solana-sdk = "2.2.0"
```

## !!steps Create test module

Now let's add a test module to our program. We'll start with the basic scaffold
and imports.

Add the following code to `lib.rs`, directly below the program code:

```rs title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        // Test implementation will go here
    }
}
```

```rs !! title="lib.rs"
// ... (program code above) ...

// !focus(1:22)
// Test module - only compiled when running tests
#[cfg(test)]
mod test {
    use super::*;  // Import everything from the parent module
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        // Test implementation will go here
    }
}
```

The `#[cfg(test)]` attribute ensures this code is only compiled when running
tests.

## !!steps Initialize test environment

Let's set up the test environment with LiteSVM and fund a payer account.

LiteSVM simulates the Solana runtime environment, allowing us to test our
program without deploying to a real cluster.

Add the following code to `lib.rs` updating the `test_counter_program` function:

```rs title="lib.rs"
let mut svm = LiteSVM::new();

let payer = Keypair::new();

svm.airdrop(&payer.pubkey(), 1_000_000_000)
    .expect("Failed to airdrop");
```

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        // !focus(1:12)
        // Create a new instance of the Solana VM for testing
        let mut svm = LiteSVM::new();

        // Create a keypair for the transaction payer
        let payer = Keypair::new();

        // Fund the payer account with 1 SOL (1 billion lamports)
        // This is needed to pay for transaction fees and account creation
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");
    }
}
```

## !!steps Load the program

Now we need to build and load our program into the test environment. Run the
`cargo build-sbf` command to build the program. This will generate the
`counter_program.so` file in the `target/deploy` directory.

```terminal
$ cargo build-sbf
```

<Callout type="warn">
  Ensure the `edition` in `Cargo.toml` is set to `2021`.
</Callout>

After building, we can load the program.

Update the `test_counter_program` function to load the program into the test
environment.

```rs title="lib.rs"
let program_keypair = Keypair::new();
let program_id = program_keypair.pubkey();

svm.add_program_from_file(
    program_id,
    "target/deploy/counter_program.so"
).expect("Failed to load program");
```

<Callout type="warn">
  You must run `cargo build-sbf` before running tests to generate the `.so`
  file. The test loads the compiled program.
</Callout>

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        let mut svm = LiteSVM::new();

        let payer = Keypair::new();
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");

        // !focus(1:11)
        // Load our compiled program into the test environment
        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();

        // Load the compiled program
        svm.add_program_from_file(
            program_id,
            "target/deploy/counter_program.so"
        ).expect("Failed to load program");
    }
}
```

## !!steps Test initialization instruction

Let's test the initialization instruction by creating a new counter account with
a starting value.

Add the following code to `lib.rs` updating the `test_counter_program` function:

```rs title="lib.rs"
let counter_keypair = Keypair::new();
let initial_value: u64 = 42;

println!("Testing counter initialization...");

let init_instruction_data =
    borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
        .expect("Failed to serialize instruction");

let initialize_instruction = Instruction::new_with_bytes(
    program_id,
    &init_instruction_data,
    vec![
        AccountMeta::new(counter_keypair.pubkey(), true),
        AccountMeta::new(payer.pubkey(), true),
        AccountMeta::new_readonly(system_program::id(), false),
    ],
);

let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
let transaction = Transaction::new(
    &[&payer, &counter_keypair],
    message,
    svm.latest_blockhash()
);

let result = svm.send_transaction(transaction);
assert!(result.is_ok(), "Initialize transaction should succeed");

let logs = result.unwrap().logs;
println!("Transaction logs:\n{:#?}", logs);
```

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        let mut svm = LiteSVM::new();

        let payer = Keypair::new();
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");

        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();
        svm.add_program_from_file(
            program_id,
            "target/deploy/counter_program.so"
        ).expect("Failed to load program");

        // !focus(1:38)
        // Step 1: Initialize the counter
        let counter_keypair = Keypair::new();
        let initial_value: u64 = 42;

        println!("Testing counter initialization...");

        // Use Borsh serialization for the instruction
        let init_instruction_data =
            borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
                .expect("Failed to serialize instruction");

        // Create the initialization instruction
        let initialize_instruction = Instruction::new_with_bytes(
            program_id,
            &init_instruction_data,
            vec![
                // Account 1: Counter account (signer, writable)
                AccountMeta::new(counter_keypair.pubkey(), true),
                // Account 2: Payer (signer, writable)
                AccountMeta::new(payer.pubkey(), true),
                // Account 3: System Program (not signer, not writable)
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        );

        // Build and send the transaction
        let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],  // Signers
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Initialize transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);
    }
}
```

## !!steps Verify initialization

After initialization, let's verify the counter account was created correctly
with the expected value.

Add the following code to `lib.rs` updating the `test_counter_program` function:

```rs title="lib.rs"
let account = svm
    .get_account(&counter_keypair.pubkey())
    .expect("Failed to get counter account");

let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
    .expect("Failed to deserialize counter data");

assert_eq!(counter.count, 42);
println!("Counter initialized successfully with value: {}", counter.count);
```

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        let mut svm = LiteSVM::new();

        let payer = Keypair::new();
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");

        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();
        svm.add_program_from_file(program_id, "target/deploy/counter_program.so")
            .unwrap();

        let counter_keypair = Keypair::new();
        let initial_value: u64 = 42;

        println!("Testing counter initialization...");

        // Use Borsh serialization for the instruction
        let init_instruction_data =
            borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
                .expect("Failed to serialize instruction");

        let initialize_instruction = Instruction::new_with_bytes(
            program_id,
            &init_instruction_data,
            vec![
                AccountMeta::new(counter_keypair.pubkey(), true),
                AccountMeta::new(payer.pubkey(), true),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        );

        let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Initialize transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);

        // !focus(1:14)
        // Check account data after initialization
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        // Deserialize and verify the counter data
        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");

        assert_eq!(counter.count, 42);
        println!("Counter initialized successfully with value: {}", counter.count);
    }
}
```

## !!steps Test increment instruction

Now let's test the increment instruction to ensure it properly updates the
counter value.

Add the following code to `lib.rs` updating the `test_counter_program` function:

```rs title="lib.rs"
println!("Testing counter increment...");

let increment_instruction_data =
    borsh::to_vec(&CounterInstruction::IncrementCounter)
        .expect("Failed to serialize instruction");

let increment_instruction = Instruction::new_with_bytes(
    program_id,
    &increment_instruction_data,
    vec![AccountMeta::new(counter_keypair.pubkey(), true)],
);

let message = Message::new(&[increment_instruction], Some(&payer.pubkey()));
let transaction = Transaction::new(
    &[&payer, &counter_keypair],
    message,
    svm.latest_blockhash()
);

let result = svm.send_transaction(transaction);
assert!(result.is_ok(), "Increment transaction should succeed");

let logs = result.unwrap().logs;
println!("Transaction logs:\n{:#?}", logs);
```

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        let mut svm = LiteSVM::new();

        let payer = Keypair::new();
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");

        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();
        svm.add_program_from_file(program_id, "target/deploy/counter_program.so")
            .unwrap();

        let counter_keypair = Keypair::new();
        let initial_value: u64 = 42;

        // Step 1: Initialize the counter
        println!("Testing counter initialization...");

        // Use Borsh serialization for the instruction
        let init_instruction_data =
            borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
                .expect("Failed to serialize instruction");

        let initialize_instruction = Instruction::new_with_bytes(
            program_id,
            &init_instruction_data,
            vec![
                AccountMeta::new(counter_keypair.pubkey(), true),
                AccountMeta::new(payer.pubkey(), true),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        );

        let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Initialize transaction should succeed");

        // Check account data
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");
        assert_eq!(counter.count, 42);
        println!("Counter initialized successfully with value: {}", counter.count);

        // !focus(1:26)
        // Step 2: Increment the counter
        println!("Testing counter increment...");

        // Use Borsh serialization for increment instruction
        let increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter)
            .expect("Failed to serialize instruction");

        let increment_instruction = Instruction::new_with_bytes(
            program_id,
            &increment_data,
            vec![AccountMeta::new(counter_keypair.pubkey(), true)],
        );

        // Build and send increment transaction
        let message = Message::new(&[increment_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Increment transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);
    }
}
```

## !!steps Verify final results

Finally, let's verify that the increment worked correctly by checking the
updated counter value.

Add the following code to `lib.rs` updating the `test_counter_program` function:

```rs title="lib.rs"
let account = svm
    .get_account(&counter_keypair.pubkey())
    .expect("Failed to get counter account");

let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
    .expect("Failed to deserialize counter data");
assert_eq!(counter.count, 43);
println!("Counter incremented successfully to: {}", counter.count);
```

```rs !! title="lib.rs"
#[cfg(test)]
mod test {
    use super::*;
    use litesvm::LiteSVM;
    use solana_sdk::{
        account::ReadableAccount,
        instruction::{AccountMeta, Instruction},
        message::Message,
        signature::{Keypair, Signer},
        system_program,
        transaction::Transaction,
    };

    #[test]
    fn test_counter_program() {
        let mut svm = LiteSVM::new();

        let payer = Keypair::new();
        svm.airdrop(&payer.pubkey(), 1_000_000_000)
            .expect("Failed to airdrop");

        let program_keypair = Keypair::new();
        let program_id = program_keypair.pubkey();
        svm.add_program_from_file(program_id, "target/deploy/counter_program.so")
            .unwrap();

        let counter_keypair = Keypair::new();
        let initial_value: u64 = 42;

        // Step 1: Initialize the counter
        println!("Testing counter initialization...");

        // Use Borsh serialization for the instruction
        let init_instruction_data =
            borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
                .expect("Failed to serialize instruction");

        let initialize_instruction = Instruction::new_with_bytes(
            program_id,
            &init_instruction_data,
            vec![
                AccountMeta::new(counter_keypair.pubkey(), true),
                AccountMeta::new(payer.pubkey(), true),
                AccountMeta::new_readonly(system_program::id(), false),
            ],
        );

        let message = Message::new(&[initialize_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Initialize transaction should succeed");

        // Check account data
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");
        assert_eq!(counter.count, 42);
        println!("Counter initialized successfully with value: {}", counter.count);

        // Step 2: Increment the counter
        println!("Testing counter increment...");

        // Use Borsh serialization for increment instruction
        let increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter)
            .expect("Failed to serialize instruction");

        let increment_instruction = Instruction::new_with_bytes(
            program_id,
            &increment_data,
            vec![AccountMeta::new(counter_keypair.pubkey(), true)],
        );

        // Build and send increment transaction
        let message = Message::new(&[increment_instruction], Some(&payer.pubkey()));
        let transaction = Transaction::new(
            &[&payer, &counter_keypair],
            message,
            svm.latest_blockhash()
        );

        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Increment transaction should succeed");

        let logs = result.unwrap().logs;
        println!("Transaction logs:\n{:#?}", logs);

        // !focus(1:11)
        // Check account data
        let account = svm
            .get_account(&counter_keypair.pubkey())
            .expect("Failed to get counter account");

        let counter: CounterAccount = CounterAccount::try_from_slice(account.data())
            .expect("Failed to deserialize counter data");
        assert_eq!(counter.count, 43);
        println!("Counter incremented successfully to: {}", counter.count);
    }
}
```

Run the tests with the following command. The `--nocapture` flag prints the
output of the test.

```terminal
$ cargo test -- --nocapture
```

Expected output:

```
Testing counter initialization...
Transaction logs:
[
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq invoke [1]",
    "Program 11111111111111111111111111111111 invoke [2]",
    "Program 11111111111111111111111111111111 success",
    "Program log: Counter initialized with value: 42",
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq consumed 3803 of 200000 compute units",
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq success",
]
Counter initialized successfully with value: 42
Testing counter increment...
Transaction logs:
[
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq invoke [1]",
    "Program log: Counter incremented to: 43",
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq consumed 762 of 200000 compute units",
    "Program 3QpyHXhFtYY32iY7foF3EjkVdCDrUppADk9aDwSWn6Sq success",
]
Counter incremented successfully to: 43
```

</ScrollyCoding>

## Part 3: Invoking the Program

Now let's add a client script to invoke the program.

<ScrollyCoding>

## !!steps Create client example

Let's create a Rust client to interact with our deployed program.

```terminal
$ mkdir examples
$ touch examples/client.rs
```

Add the following configuration to `Cargo.toml`:

```toml title="Cargo.toml"
[[example]]
name = "client"
path = "examples/client.rs"
```

```toml !! title="Cargo.toml"
[package]
name = "counter_program"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
borsh = "1.5.7"
solana-program = "2.2.0"

[dev-dependencies]
litesvm = "0.6.1"
solana-sdk = "2.2.0"
# !focus(1:2)
solana-client = "2.2.0"
tokio = "1.47.1"

# !focus(1:3)
[[example]]
name = "client"
path = "examples/client.rs"
```

Install the client dependencies:

```terminal
$ cargo add solana-client@2.2.0 --dev
$ cargo add tokio --dev
```

## !!steps Implement client code

Now let's implement the client that will invoke our deployed program.

Run the following command to get your program ID from the keypair file:

```terminal
$ solana address -k ./target/deploy/counter_program-keypair.json
```

Add the client code to `examples/client.rs` and replace the `program_id` with
the output of the previous command:

```rs title="examples/client.rs"
// !mark
let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH")
    .expect("Invalid program ID");
```

<CodePlaceholder title="examples/client.rs" />

```rs !! title="examples/client.rs"
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    system_program,
    transaction::Transaction,
};
use std::str::FromStr;
use counter_program::CounterInstruction;

#[tokio::main]
async fn main() {
    // Replace with your actual program ID from deployment
    // !mark
    let program_id = Pubkey::from_str("BDLLezrtFEXVGYqG3aS7eAC7GVeojJ4JHhKJM6pAFCDH")
        .expect("Invalid program ID");

    // Connect to local cluster
    let rpc_url = String::from("http://localhost:8899");
    let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());

    // Generate a new keypair for paying fees
    let payer = Keypair::new();

    // Request airdrop of 1 SOL for transaction fees
    println!("Requesting airdrop...");
    let airdrop_signature = client
        .request_airdrop(&payer.pubkey(), 1_000_000_000)
        .expect("Failed to request airdrop");

    // Wait for airdrop confirmation
    loop {
        if client
            .confirm_transaction(&airdrop_signature)
            .unwrap_or(false)
        {
            break;
        }
        std::thread::sleep(std::time::Duration::from_millis(500));
    }
    println!("Airdrop confirmed");

    println!("\nInitializing counter...");
    let counter_keypair = Keypair::new();
    let initial_value = 100u64;

    // Serialize the initialize instruction data
    let instruction_data = borsh::to_vec(&CounterInstruction::InitializeCounter { initial_value })
        .expect("Failed to serialize instruction");

    let initialize_instruction = Instruction::new_with_bytes(
        program_id,
        &instruction_data,
        vec![
            AccountMeta::new(counter_keypair.pubkey(), true),
            AccountMeta::new(payer.pubkey(), true),
            AccountMeta::new_readonly(system_program::id(), false),
        ],
    );

    let mut transaction =
        Transaction::new_with_payer(&[initialize_instruction], Some(&payer.pubkey()));

    let blockhash = client
        .get_latest_blockhash()
        .expect("Failed to get blockhash");
    transaction.sign(&[&payer, &counter_keypair], blockhash);

    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => {
            println!("Counter initialized!");
            println!("Transaction: {}", signature);
            println!("Counter address: {}", counter_keypair.pubkey());
        }
        Err(err) => {
            eprintln!("Failed to initialize counter: {}", err);
            return;
        }
    }

    println!("\nIncrementing counter...");
    // Serialize the increment instruction data
    let increment_data = borsh::to_vec(&CounterInstruction::IncrementCounter)
        .expect("Failed to serialize instruction");

    let increment_instruction = Instruction::new_with_bytes(
        program_id,
        &increment_data,
        vec![AccountMeta::new(counter_keypair.pubkey(), true)],
    );

    let mut transaction =
        Transaction::new_with_payer(&[increment_instruction], Some(&payer.pubkey()));

    transaction.sign(&[&payer, &counter_keypair], blockhash);

    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => {
            println!("Counter incremented!");
            println!("Transaction: {}", signature);
        }
        Err(err) => {
            eprintln!("Failed to increment counter: {}", err);
        }
    }
}
```

</ScrollyCoding>

## Part 4: Deploying the Program

Now that we have our program and client ready, let's build, deploy, and invoke
the program.

<Steps>

<Step>

### Build the program

First, let's build our program.

```terminal
$ cargo build-sbf
```

This command compiles your program and generates two important files in
`target/deploy/`:

```txt
counter_program.so              # The compiled program
counter_program-keypair.json    # Keypair for the program ID
```

You can view your program's ID by running the following command:

```terminal
$ solana address -k ./target/deploy/counter_program-keypair.json
```

Example output:

```
HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
```

</Step>

<Step>

### Start local validator

For development, we'll use a local test validator.

First, configure the Solana CLI to use localhost:

```terminal
$ solana config set -ul
```

Example output:

```
Config File: ~/.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: ~/.config/solana/id.json
Commitment: confirmed
```

Now start the test validator in a separate terminal:

```terminal
$ solana-test-validator
```

</Step>

<Step>

### Deploy the program

With the validator running, deploy your program to the local cluster:

```terminal
$ solana program deploy ./target/deploy/counter_program.so
```

Example output:

```
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF

Signature: 5xKdnh3dDFnZXB5UevYYkFBpCVcuqo5SaUPLnryFWY7eQD2CJxaeVDKjQ4ezQVJfkGNqZGYqMZBNqymPKwCQQx5h
```

You can verify the deployment using the `solana program show` command with your
program ID:

```terminal
$ solana program show HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
```

Example output:

```
Program Id: HQ5Q2XXqbTKKQsWPtLzMn7rDhM8v9UPYPe7DfSoFQqJF
Owner: BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: 47MVf5tRZ4zWXQMX7ydrkgcFQr8XTk1QBjohwsUzaiuM
Authority: 4kh6HxYZiAebF8HWLsUWod2EaQQ6iWHpHYCz8UcmFbM1
Last Deployed In Slot: 16
Data Length: 82696 (0x14308) bytes
Balance: 0.57676824 SOL
```

</Step>

<Step>

### Run the client

With the local validator still running, execute the client:

```terminal
$ cargo run --example client
```

Expected output:

```
Requesting airdrop...
Airdrop confirmed

Initializing counter...
Counter initialized!
Transaction: 2uenChtqNeLC1fitqoVE2LBeygSBTDchMZ4gGqs7AiDvZZVJguLDE5PfxsfkgY7xs6zFWnYsbEtb82dWv9tDT14k
Counter address: EppPAmwqD42u4SCPWpPT7wmWKdFad5VnM9J4R9Zfofcy

Incrementing counter...
Counter incremented!
Transaction: 4qv1Rx6FHu1M3woVgDQ6KtYUaJgBzGcHnhej76ZpaKGCgsTorbcHnPKxoH916UENw7X5ppnQ8PkPnhXxEwrYuUxS
```

With the local validator running, you can view the transactions on
[Solana Explorer](https://explorer.solana.com/?cluster=custom) using the output
transaction signatures. Note the cluster on Solana Explorer must be set to
"Custom RPC URL", which defaults to `http://localhost:8899` that the
`solana-test-validator` is running on.

</Step>

</Steps>
