Rust SDK Examples
This page provides practical examples of using the native Arch Network Rust SDK for building high-performance applications and on-chain programs.
Basic Examples
Account Management
use arch_sdk::{Connection, Keypair, Account}; use arch_program::pubkey::Pubkey; use anyhow::Result; #[tokio::main] async fn main() -> Result<()> { let connection = Connection::new("http://localhost:9002"); // Create new account let new_account = Keypair::new(); println!("New account: {}", new_account.pubkey()); // Check if account exists match connection.get_account(&new_account.pubkey()).await? { Some(account) => { println!("Account exists with {} lamports", account.lamports); } None => { println!("Account does not exist yet"); } } // Get multiple accounts efficiently let pubkeys = vec![ Pubkey::new_unique(), Pubkey::new_unique(), new_account.pubkey(), ]; let accounts = connection.get_multiple_accounts(&pubkeys).await?; for (i, account) in accounts.iter().enumerate() { match account { Some(acc) => println!("Account {}: {} lamports", i, acc.lamports), None => println!("Account {}: Not found", i), } } Ok(()) }
UTXO Operations
#![allow(unused)] fn main() { use arch_sdk::{Connection, Utxo}; use arch_program::pubkey::Pubkey; use std::str::FromStr; async fn utxo_operations() -> Result<()> { let connection = Connection::new("http://localhost:9002"); let address = Pubkey::from_str("YourBitcoinAddress...")?; // Get UTXOs for an address let utxos = connection.get_utxos(&address).await?; println!("Found {} UTXOs", utxos.len()); // Process UTXOs let total_value: u64 = utxos.iter() .map(|utxo| utxo.value) .sum(); println!("Total value: {} satoshis", total_value); // Find spendable UTXOs above threshold let threshold = 10_000; // satoshis let spendable: Vec<&Utxo> = utxos.iter() .filter(|utxo| utxo.value >= threshold) .collect(); println!("Spendable UTXOs: {}", spendable.len()); Ok(()) } }
Transaction Building
#![allow(unused)] fn main() { use arch_sdk::{Connection, Keypair, Transaction}; use arch_program::{ instruction::Instruction, system_instruction, pubkey::Pubkey, message::Message, }; async fn build_complex_transaction() -> Result<()> { let connection = Connection::new("http://localhost:9002"); let payer = Keypair::new(); // Create multiple instructions let mut instructions = vec![]; // 1. Create a new account let new_account = Keypair::new(); let space = 1024; let rent = connection.get_minimum_balance_for_rent_exemption(space).await?; instructions.push(system_instruction::create_account( &payer.pubkey(), &new_account.pubkey(), rent, space, &my_program_id(), )); // 2. Initialize the account instructions.push(Instruction::new_with_bytes( my_program_id(), &[0], // Initialize instruction vec![ AccountMeta::new(new_account.pubkey(), true), AccountMeta::new_readonly(payer.pubkey(), true), ], )); // 3. Transfer some lamports instructions.push(system_instruction::transfer( &payer.pubkey(), &new_account.pubkey(), 1_000_000, )); // Build and sign transaction let message = Message::new(&instructions, Some(&payer.pubkey())); let mut transaction = Transaction::new_unsigned(message); let recent_blockhash = connection.get_latest_blockhash().await?; transaction.sign(&[&payer, &new_account], recent_blockhash); // Send transaction let signature = connection.send_and_confirm_transaction(&transaction).await?; println!("Transaction successful: {}", signature); Ok(()) } }
On-Chain Program Examples
State Management Program
#![allow(unused)] fn main() { use arch_program::{ account_info::{next_account_info, AccountInfo}, entrypoint, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, }; use borsh::{BorshDeserialize, BorshSerialize}; #[derive(BorshSerialize, BorshDeserialize, Debug)] pub struct Counter { pub count: u64, pub authority: Pubkey, pub last_updated: i64, } entrypoint!(process_instruction); pub fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { let instruction = CounterInstruction::try_from_slice(instruction_data)?; match instruction { CounterInstruction::Initialize { authority } => { process_initialize(program_id, accounts, authority) } CounterInstruction::Increment => { process_increment(program_id, accounts) } CounterInstruction::Reset => { process_reset(program_id, accounts) } } } fn process_initialize( program_id: &Pubkey, accounts: &[AccountInfo], authority: Pubkey, ) -> ProgramResult { let accounts_iter = &mut accounts.iter(); let counter_account = next_account_info(accounts_iter)?; let payer = next_account_info(accounts_iter)?; // Verify account ownership if counter_account.owner != program_id { return Err(ProgramError::IncorrectProgramId); } // Initialize counter let counter = Counter { count: 0, authority, last_updated: Clock::get()?.unix_timestamp, }; counter.serialize(&mut &mut counter_account.data.borrow_mut()[..])?; msg!("Counter initialized with authority: {}", authority); Ok(()) } fn process_increment( program_id: &Pubkey, accounts: &[AccountInfo], ) -> ProgramResult { let accounts_iter = &mut accounts.iter(); let counter_account = next_account_info(accounts_iter)?; let authority = next_account_info(accounts_iter)?; // Deserialize counter let mut counter = Counter::try_from_slice(&counter_account.data.borrow())?; // Verify authority if !authority.is_signer || *authority.key != counter.authority { return Err(ProgramError::MissingRequiredSignature); } // Increment counter counter.count = counter.count .checked_add(1) .ok_or(ProgramError::ArithmeticOverflow)?; counter.last_updated = Clock::get()?.unix_timestamp; // Save state counter.serialize(&mut &mut counter_account.data.borrow_mut()[..])?; msg!("Counter incremented to: {}", counter.count); Ok(()) } }
Cross-Program Invocation (CPI)
#![allow(unused)] fn main() { use arch_program::{ account_info::AccountInfo, program::{invoke, invoke_signed}, instruction::{AccountMeta, Instruction}, pubkey::Pubkey, }; pub fn transfer_via_cpi( from: &AccountInfo, to: &AccountInfo, amount: u64, signer_seeds: &[&[&[u8]]], ) -> ProgramResult { let transfer_instruction = system_instruction::transfer( from.key, to.key, amount, ); // If the from account is a PDA, use invoke_signed if signer_seeds.is_empty() { invoke( &transfer_instruction, &[from.clone(), to.clone()], ) } else { invoke_signed( &transfer_instruction, &[from.clone(), to.clone()], signer_seeds, ) } } // Example: Escrow program using CPI pub fn release_escrow( program_id: &Pubkey, accounts: &[AccountInfo], ) -> ProgramResult { let accounts_iter = &mut accounts.iter(); let escrow_account = next_account_info(accounts_iter)?; let recipient = next_account_info(accounts_iter)?; let escrow_pda = next_account_info(accounts_iter)?; // Verify PDA let (pda, bump) = Pubkey::find_program_address( &[b"escrow", escrow_account.key.as_ref()], program_id, ); if pda != *escrow_pda.key { return Err(ProgramError::InvalidAccountData); } // Transfer funds from PDA to recipient transfer_via_cpi( escrow_pda, recipient, escrow_pda.lamports(), &[&[b"escrow", escrow_account.key.as_ref(), &[bump]]], )?; Ok(()) } }
Advanced Patterns
Concurrent Operations
#![allow(unused)] fn main() { use futures::future::{join_all, try_join_all}; use std::sync::Arc; use tokio::task::JoinHandle; async fn concurrent_account_processing( connection: Arc<Connection>, pubkeys: Vec<Pubkey>, ) -> Result<Vec<Option<Account>>> { // Create tasks for concurrent fetching let tasks: Vec<JoinHandle<Result<Option<Account>>>> = pubkeys .into_iter() .map(|pubkey| { let conn = connection.clone(); tokio::spawn(async move { conn.get_account(&pubkey).await }) }) .collect(); // Wait for all tasks to complete let results = join_all(tasks).await; // Handle results let mut accounts = Vec::new(); for result in results { match result { Ok(Ok(account)) => accounts.push(account), Ok(Err(e)) => return Err(e), Err(e) => return Err(anyhow::anyhow!("Task failed: {}", e)), } } Ok(accounts) } // Batch processing with rate limiting use tokio::time::{sleep, Duration}; async fn batch_process_with_rate_limit<T, F, Fut>( items: Vec<T>, batch_size: usize, delay_ms: u64, process_fn: F, ) -> Result<Vec<Result<(), anyhow::Error>>> where F: Fn(T) -> Fut + Clone, Fut: std::future::Future<Output = Result<()>>, T: Send + 'static, { let mut results = Vec::new(); for chunk in items.chunks(batch_size) { let futures: Vec<_> = chunk .iter() .map(|item| { let f = process_fn.clone(); f(item.clone()) }) .collect(); let batch_results = try_join_all(futures).await; results.extend(batch_results); // Rate limit between batches sleep(Duration::from_millis(delay_ms)).await; } Ok(results) } }
Custom Error Handling
#![allow(unused)] fn main() { use thiserror::Error; #[derive(Error, Debug)] pub enum ArchAppError { #[error("Connection error: {0}")] Connection(#[from] arch_sdk::ArchError), #[error("Invalid account: {0}")] InvalidAccount(String), #[error("Insufficient balance: needed {needed}, available {available}")] InsufficientBalance { needed: u64, available: u64 }, #[error("Transaction failed: {0}")] TransactionFailed(String), } // Retry logic with exponential backoff async fn retry_with_backoff<T, F, Fut>( operation: F, max_retries: u32, ) -> Result<T, ArchAppError> where F: Fn() -> Fut, Fut: std::future::Future<Output = Result<T, ArchAppError>>, { let mut delay = Duration::from_millis(100); for attempt in 0..max_retries { match operation().await { Ok(result) => return Ok(result), Err(e) => { if attempt == max_retries - 1 { return Err(e); } println!("Attempt {} failed: {}. Retrying...", attempt + 1, e); sleep(delay).await; delay *= 2; // Exponential backoff } } } unreachable!() } }
Testing Patterns
Integration Tests
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; use arch_sdk::test_utils::{TestValidator, TestValidatorGenesis}; #[tokio::test] async fn test_program_deployment() { // Start test validator let mut genesis = TestValidatorGenesis::default(); genesis.add_program("my_program", my_program_id()); let validator = TestValidator::with_genesis(genesis).await; let connection = validator.connection(); // Test program functionality let payer = validator.payer(); let account = Keypair::new(); // Create and initialize account let ix = create_initialize_instruction( &my_program_id(), &account.pubkey(), &payer.pubkey(), ); let mut transaction = Transaction::new_with_payer( &[ix], Some(&payer.pubkey()), ); let recent_blockhash = connection.get_latest_blockhash().await.unwrap(); transaction.sign(&[&payer, &account], recent_blockhash); let result = connection.send_and_confirm_transaction(&transaction).await; assert!(result.is_ok()); } } }
Performance Optimization
Connection Pooling
#![allow(unused)] fn main() { use std::sync::Arc; use dashmap::DashMap; pub struct ConnectionPool { connections: DashMap<String, Arc<Connection>>, } impl ConnectionPool { pub fn new() -> Self { Self { connections: DashMap::new(), } } pub fn get_connection(&self, endpoint: &str) -> Arc<Connection> { self.connections .entry(endpoint.to_string()) .or_insert_with(|| Arc::new(Connection::new(endpoint))) .clone() } } // Usage lazy_static::lazy_static! { static ref POOL: ConnectionPool = ConnectionPool::new(); } async fn use_pooled_connection() -> Result<()> { let connection = POOL.get_connection("http://localhost:9002"); let block_count = connection.get_block_count().await?; println!("Block count: {}", block_count); Ok(()) } }
Resources
For more examples and patterns: