---
title: Rust Programs
description:
  Learn how to develop Solana programs using Rust, including step-by-step
  instructions for creating, building, testing, and deploying smart contracts on
  the Solana blockchain.
h1: Developing Programs in Rust
---

Solana programs are primarily developed using the Rust programming language.
This page focuses on writing Solana programs in Rust without using the Anchor
framework, an approach often referred to as writing "native Rust" programs.

Native Rust development provides developers with direct control over their
Solana programs. However, this approach requires more manual setup and
boilerplate code compared to using the Anchor framework. This method is
recommended for developers who:

- Seek granular control over program logic and optimizations
- Want to learn the underlying concepts before moving to higher-level frameworks

For beginners, we recommend starting with the Anchor framework. See the
[Anchor](https://www.anchor-lang.com/docs) section for more information.

## Prerequisites

For detailed installation instructions, visit the
[installation](/docs/intro/installation) page.

Before you begin, ensure you have the following installed:

- Rust: The programming language for building Solana programs.
- Solana CLI: Command-line tool for Solana development.

## Getting Started

The example below covers the basic steps to create your first Solana program
written in Rust. We'll create a minimal program that prints "Hello, world!" to
the program log.

<ScrollyCoding>

## !!steps Create a new program

First, create a new Rust project using the standard `cargo new` command with the
`--lib` flag.

```terminal
$ cargo new hello_world --lib
```

Navigate to the project directory. You should see the default `src/lib.rs` and
`Cargo.toml` files

```terminal
$ cd hello_world
```

<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 = "hello_world"
version = "0.1.0"
edition = "2021"

[dependencies]
```

```rs !! title="src/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 the solana-program dependency

Next, add the `solana-program` dependency. This is the minimum dependency
required to build a Solana program.

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

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

# !focus(1:2)
[dependencies]
solana-program = "2.2.0"
```

## !!steps Add the crate-type

Next, add the following snippet to `Cargo.toml`.

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

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

<CodePlaceholder title="Cargo.toml">
  Your `Cargo.toml` file should look like the following:
</CodePlaceholder>

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

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

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

## !!steps Add the program code

Next, replace the contents of `src/lib.rs` with the following code. This is a
minimal Solana program that prints "Hello, world!" to the program log when the
program is invoked.

The `msg!` macro is used in Solana programs to print a message to the program
log.

<CodePlaceholder title="src/lib.rs" />

```rs !! title="src/lib.rs"
use solana_program::{
    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

entrypoint!(process_instruction);

pub fn process_instruction(
    _program_id: &Pubkey,
    _accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    // !mark
    msg!("Hello, world!");
    Ok(())
}
```

## !!steps Build the program

Next, build the program using the `cargo build-sbf` command.

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

This command generates a `target/deploy` directory containing two important
files:

1. A `.so` file (e.g., `hello_world.so`): This is the compiled Solana program
   that will be deployed to the network as a "smart contract".
2. A keypair file (e.g., `hello_world-keypair.json`): The public key of this
   keypair is used as the program ID when deploying the program.

To view the program ID, run the following command in your terminal. This command
prints the public key of the keypair at the specified file path:

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

Example output:

```
4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
```

```json !! title="target/deploy/hello_world-keypair.json"
[
  203, 253, 43, 62, 165, 111, 94, 222, 105, 225, 218, 85, 143, 9, 114, 96, 243,
  181, 114, 89, 72, 230, 124, 85, 59, 165, 198, 23, 240, 212, 23, 154, 229, 241,
  153, 61, 153, 105, 79, 204, 193, 163, 33, 65, 82, 183, 49, 240, 224, 137, 248,
  24, 128, 25, 192, 197, 118, 235, 239, 67, 85, 156, 219, 231
]
```

```txt !! title="target/deploy/hello_world.so"
[binary]
```

## !!steps Add test dependencies

Next, test the program using the `litesvm` crate. Add the following dependencies
to `Cargo.toml`.

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

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

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

[dependencies]
solana-program = "2.2.0"

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

## !!steps Test the program

Add the following test to `src/lib.rs`, below the program code. This is a test
module that invokes the hello world program.

<CodePlaceholder title="src/lib.rs" />

```rs !! title="src/lib.rs"
use solana_program::{
    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

entrypoint!(process_instruction);

pub fn process_instruction(
    _program_id: &Pubkey,
    _accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("Hello, world!");
    Ok(())
}

// !focus(1:45)
#[cfg(test)]
mod test {
    use litesvm::LiteSVM;
    use solana_sdk::{
        instruction::Instruction,
        message::Message,
        signature::{Keypair, Signer},
        transaction::Transaction,
    };

    #[test]
    fn test_hello_world() {
        // 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/hello_world.so")
            .unwrap();

        // Create instruction with no accounts and no data
        let instruction = Instruction {
            program_id,
            accounts: vec![],
            data: vec![],
        };

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

        // Send transaction and verify it succeeds
        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Transaction should succeed");
        let logs = result.unwrap().logs;
        println!("Logs: {logs:#?}");
    }
}
```

Run the test using the `cargo test` command. The program log will display
"Hello, world!".

```terminal
$ cargo test -- --no-capture
```

Example output:

```txt title="Terminal"
running 1 test
Logs: [
    "Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk invoke [1]",
    "Program log: Hello, world!",
    "Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk consumed 211 of 200000 compute units",
    "Program 9528phXNvdWp5kkR4rgpoeZvR8ZWT5THVywK95YRprkk success",
]
test test::test_hello_world ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
```

## !!steps Deploy the program

Next, deploy the program. When developing locally, we can use the
`solana-test-validator`.

First, configure the Solana CLI to use the local Solana cluster.

```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
```

Open a new terminal and run the `solana-test-validators` command to start the
local validator.

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

While the test validator is running, run the `solana program deploy` command in
a separate terminal to deploy the program to the local validator.

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

Example output:

```
Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
Signature:
5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH
```

You can inspect the program ID and transaction signature on
[Solana Explorer](https://explorer.solana.com/?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899).

<Callout type="info">
  Note that the cluster on Solana Explorer must also be localhost. The "Custom
  RPC URL" option on Solana Explorer defaults to `http://localhost:8899`.
</Callout>

## !!steps Create example client

Next, we'll demonstrate how to invoke the program using a Rust client.

First create an `examples` directory and a `client.rs` file.

```terminal
$ mkdir -p examples && touch examples/client.rs
```

Add the following to `Cargo.toml`.

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

Add `solana-client` and `tokio` dependencies.

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

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

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

[dependencies]
solana-program = "2.2.0"

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

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

```rs !! title="examples/client.rs"

```

## !!steps Add the client

Add the following code to `examples/client.rs`. This is a Rust client script
that funds a new keypair to pay for transaction fees and then invokes the hello
world program.

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

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

#[tokio::main]
async fn main() {
    // Program ID (replace with your actual program ID)
    let program_id = Pubkey::from_str("4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz").unwrap();

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

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

    // Request airdrop
    let airdrop_amount = 1_000_000_000; // 1 SOL
    let signature = client
        .request_airdrop(&payer.pubkey(), airdrop_amount)
        .expect("Failed to request airdrop");

    // Wait for airdrop confirmation
    loop {
        let confirmed = client.confirm_transaction(&signature).unwrap();
        if confirmed {
            break;
        }
    }

    // Create the instruction
    let instruction = Instruction::new_with_borsh(
        program_id,
        &(),    // Empty instruction data
        vec![], // No accounts needed
    );

    // Add the instruction to new transaction
    let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
    transaction.sign(&[&payer], client.get_latest_blockhash().unwrap());

    // Send and confirm the transaction
    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => println!("Transaction Signature: {}", signature),
        Err(err) => eprintln!("Error sending transaction: {}", err),
    }
}
```

## !!steps Replace the program ID

Before running the client code, replace the program ID in the code snippet with
the one for your program.

You can get your program ID by running the following command.

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

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

#[tokio::main]
async fn main() {
    // Program ID (replace with your actual program ID)
    // !focus
    let program_id = Pubkey::from_str("4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz").unwrap();

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

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

    // Request airdrop
    let airdrop_amount = 1_000_000_000; // 1 SOL
    let signature = client
        .request_airdrop(&payer.pubkey(), airdrop_amount)
        .expect("Failed to request airdrop");

    // Wait for airdrop confirmation
    loop {
        let confirmed = client.confirm_transaction(&signature).unwrap();
        if confirmed {
            break;
        }
    }

    // Create the instruction
    let instruction = Instruction::new_with_borsh(
        program_id,
        &(),    // Empty instruction data
        vec![], // No accounts needed
    );

    // Add the instruction to new transaction
    let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
    transaction.sign(&[&payer], client.get_latest_blockhash().unwrap());

    // Send and confirm the transaction
    match client.send_and_confirm_transaction(&transaction) {
        Ok(signature) => println!("Transaction Signature: {}", signature),
        Err(err) => eprintln!("Error sending transaction: {}", err),
    }
}
```

## !!steps Invoke the program

Run the client script with the following command.

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

Example output:

```
Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV
```

You can inspect the transaction signature on
[Solana Explorer](https://explorer.solana.com/?cluster=custom&customUrl=http%3A%2F%2Flocalhost%3A8899)
(local cluster) to see "Hello, world!" in the program log.

## !!steps Update the program

Solana programs can be updated by redeploying to the same program ID. Update the
program in `src/lib.rs` to print "Hello, Solana!" instead of "Hello, world!".

```rs title="lib.rs"
pub fn process_instruction(
    _program_id: &Pubkey,
    _accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    // !diff -
    msg!("Hello, world!");
    // !diff +
    msg!("Hello, Solana!");
    Ok(())
}
```

```rs !! title="src/lib.rs"
use solana_program::{
    account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};

entrypoint!(process_instruction);

// !focus(1:8)
pub fn process_instruction(
    _program_id: &Pubkey,
    _accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
    msg!("Hello, Solana!");
    Ok(())
}

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

    #[test]
    fn test_hello_world() {
        // 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/hello_world.so")
            .unwrap();

        // Create instruction with no accounts and no data
        let instruction = Instruction {
            program_id,
            accounts: vec![],
            data: vec![],
        };

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

        // Send transaction and verify it succeeds
        let result = svm.send_transaction(transaction);
        assert!(result.is_ok(), "Transaction should succeed");
        let logs = result.unwrap().logs;
        println!("Logs: {logs:#?}");
    }
}
```

Run the `cargo build-sbf` command to generate an updated `.so` file.

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

Test the updated program by running the `cargo test` command.

```terminal
$ cargo test -- --no-capture
```

You should see "Hello, Solana!" in the program log.

```txt title="Terminal"
running 1 test
Logs: [
    "Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV invoke [1]",
    "Program log: Hello, Solana!",
    "Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV consumed 211 of 200000 compute units",
    "Program 5y8bHrnwfq2dLDgLn3WoTHb9dDuyorj9gyapW6aeyrpV success",
]
test test::test_hello_world ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
```

## !!steps Redeploy the program

Redeploy the program using the same `solana program deploy` command.

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

Run the client code again and inspect the transaction signature on Solana
Explorer to see "Hello, Solana!" in the program log.

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

## !!steps Close the program

You can close your Solana program to reclaim the SOL allocated to the account.
Closing a program is irreversible, so it should be done with caution.

To close a program, use the `solana program close <PROGRAM_ID>` command. For
example:

```terminal
$ solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz --bypass-warning
```

Example output:

```
Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOL
reclaimed
```

Note that once a program is closed, its program ID cannot be reused. Attempting
to deploy a program with a previously closed program ID will result in an error.

```
Error: Program 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz has been closed, use
a new Program Id
```

## !!steps Redeploy a closed program

If you need to redeploy a program with the same source code after closing a
program, you must generate a new program ID. To generate a new keypair for the
program, run the following command:

```terminal
$ solana-keygen new -o ./target/deploy/hello_world-keypair.json --force
```

<Callout type="info">
  Alternatively, you can delete the existing keypair file (e.g.
  `./target/deploy/hello_world-keypair.json`) and run `cargo build-sbf` again,
  which will generate a new keypair file.
</Callout>

</ScrollyCoding>
