Introduction
Solana, known for its high throughput and low latency, is rapidly becoming a preferred blockchain for decentralized applications (dApps). Writing a smart contract on Solana is both a rewarding and challenging experience due to its unique programming model. This blog will guide you through the basics of creating your first Solana smart contract.
Understanding Solana Smart Contracts
Solana smart contracts, also known as programs, are written in Rust. Unlike Ethereum's EVM-based architecture, Solana’s runtime is built from scratch, designed to handle thousands of transactions per second.
Key Concepts:
- Accounts: The fundamental data storage mechanism in Solana. Accounts can store arbitrary data and are accessible by programs.
- Programs: Smart contracts on Solana. They define the logic for processing transactions and manipulating accounts.
- Instructions: The operations that modify accounts. Each instruction is associated with a program.
Setting Up Your Development Environment
Before diving into code, you'll need to set up your development environment:
Install Rust:
Solana programs are written in Rust, so you'll need to install Rust by following the instructions on rust.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
After installing, make sure installation is success
rustc --version
Install Solana CLI
The Solana Command Line Interface (CLI) is essential for interacting with the Solana blockchain. You can install it by running:
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
After installing, make sure installation is success. The version might be different.
solana --version
# solana-cli 1.18.22 (src:9efdd74b; feat:4215500110, client:Agave)
Set Up Anchor Framework (Optional but Recommended)
Anchor simplifies the process of writing and deploying Solana programs. To install Anchor, run:
cargo install --git https://github.com/coral-xyz/anchor anchor-cli --locked
After installing, make sure installation is success. The version might be different.
anchor --version
# anchor-cli 0.30.1
Creating Your First Solana Program
Let's start by creating a simple program that allows users to store and update a value in an account. First project will be printing out "Hello, World!".
Initialize a New Project:
cargo new --lib my_solana_program
cd my_solana_program
This creates a new Rust library, which is the starting point for our program.
Install dependencies
When you write a program, you need to install dependencies.
cargo add solana-program
Set Up the Program Structure:
In src/lib.rs
, define your program's entry point and the basic structure:
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
msg,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello, World");
Ok(())
}
This basic structure sets up an entry point for the program. The process_instruction function is where the logic resides.
Make sure the program compiles:
cargo c
The compiler gives a lot of warnings, but we can ignore for that now.
Define Your Program Logic:
Let's add some logic to our program. We'll store a single u64 value in an account and allow users to update it.
To define an account, we use BorshSerialize, BorshDeserialize
trait. These trait takes care of serialization and deserialization.
cargo add borsh
Inside src/lib.rs
, we are going to modify like:
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
sysvar::{rent::Rent, Sysvar},
};
entrypoint!(process_instruction);
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct MyAccount {
pub value: u64,
}
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let accounts_iter = &mut accounts.iter();
let account = next_account_info(accounts_iter)?;
// Check the account owner is the this program
if account.owner != program_id {
return Err(ProgramError::IncorrectProgramId);
}
// Deserialize the `MyAccount`
let mut data = MyAccount::try_from_slice(&account.data.borrow())?;
let value = u64::from_le_bytes(instruction_data.try_into().unwrap());
// Update the value in the MyAccount
data.value = value;
data.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Updated value: {}", data.value);
Ok(())
}
Make sure the program compiles:
cargo c
Build the program:
Before deploying, we are going to editCargo.toml
file.
[package]
name = "my_solana_program"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
borsh = "1.5.1"
solana-program = "2.0.6"
[lib]
crate-type = ["cdylib", "lib"]
name = "my_solana_program"
[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []
With the program logic in place, you can now build and deploy it.
Build the program:
cargo-build-sbf
This command would compile the program and create a file in the target folder.
target/sbf-solana-solana/release/my_solana_program.so
Conclusion
Writing a smart contract on Solana is an exciting journey. With its unique architecture, Solana offers developers a powerful platform to build high-performance dApps. This guide covers the basics, but there’s much more to explore—like cross program invocations, CPI, and custom error handling. As you dive deeper, you’ll discover the nuances and possibilities of Solana’s programming model. In the next blog, we will deploy our program on Localnet or Devnet.