Writing Your First Arch Program
This guide will walk you through creating your first Arch program from scratch. We’ll build a simple counter program that demonstrates the core concepts of Arch development while providing hands-on experience with the development workflow.
Prerequisites
Before starting, ensure you have:
- Rust and Cargo installed
- CLI installed and configured (see Quick Start Guide)
- A running validator node
- Basic understanding of Arch concepts
Project Setup
- Create a new project directory:
mkdir my-counter-program
cd my-counter-program
- Initialize a new Rust project:
cargo init --lib
- Add necessary dependencies to
Cargo.toml
:
[package]
name = "my-counter-program"
version = "0.1.0"
edition = "2021"
[dependencies]
arch-program = { git = "https://github.com/Arch-Network/arch-program" }
borsh = "0.10.3"
[lib]
crate-type = ["cdylib"]
Writing the Program
Let’s create a simple program that:
- Stores a counter in an account
- Can increment the counter
- Can decrement the counter
- Can reset the counter
- Define our program’s state structure in
src/lib.rs
:
use arch_program::{
account::AccountInfo,
entrypoint,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct CounterAccount {
pub count: u64,
}
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub enum CounterInstruction {
Increment,
Decrement,
Reset,
}
- Implement the program logic:
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> Result<(), ProgramError> {
// Get the account to store counter data
let account_iter = &mut accounts.iter();
let counter_account = next_account_info(account_iter)?;
// Verify account is writable
if !counter_account.is_writable {
return Err(ProgramError::InvalidAccountData);
}
// Deserialize the instruction
let instruction = CounterInstruction::try_from_slice(instruction_data)?;
// Get current counter value
let mut counter = match CounterAccount::try_from_slice(&counter_account.data.borrow()) {
Ok(data) => data,
Err(_) => CounterAccount { count: 0 },
};
// Process the instruction
match instruction {
CounterInstruction::Increment => {
counter.count = counter.count.checked_add(1).ok_or(ProgramError::Custom(1))?;
msg!("Counter incremented to {}", counter.count);
}
CounterInstruction::Decrement => {
counter.count = counter.count.checked_sub(1).ok_or(ProgramError::Custom(2))?;
msg!("Counter decremented to {}", counter.count);
}
CounterInstruction::Reset => {
counter.count = 0;
msg!("Counter reset to 0");
}
}
// Serialize and save the counter
counter.serialize(&mut &mut counter_account.data.borrow_mut()[..])?;
Ok(())
}
Building the Program
- Build your program:
cargo build-sbf
This will create a compiled program at target/deploy/my_counter_program.so
Deploying the Program
- Deploy your program using the CLI:
cli deploy target/deploy/my_counter_program.so
Save the Program ID output - you’ll need it to interact with your program.
Creating a Counter Account
Before we can use our counter, we need to create an account to store its state:
cli create-account <PROGRAM_ID> 8
Save the account address - you’ll need it to interact with your counter.
Testing Your Program
You can now interact with your program using the CLI:
- Increment the counter:
cli invoke <PROGRAM_ID> <ACCOUNT_ADDRESS> --data 00
- Decrement the counter:
cli invoke <PROGRAM_ID> <ACCOUNT_ADDRESS> --data 01
- Reset the counter:
cli invoke <PROGRAM_ID> <ACCOUNT_ADDRESS> --data 02
Next Steps
Now that you’ve created your first program, you can:
- Add more features to the counter program
- Learn about cross-program invocation
- Explore more complex Program Examples
- Study the Understanding Arch Programs guide for deeper insights
# Start the Arch Network validator
cli validator-start