Skip to main content

Overview

The StateManager manages the blockchain world state, including:
  • Account balances and nonces
  • Contract deployment and bytecode storage
  • Contract storage slots (SLOAD/SSTORE)
  • State root computation

StateManager

Manages all accounts and contract storage state.
pub struct StateManager<'a> {
    storage: &'a Storage,
}

Constructor

StateManager::new
fn
Creates a new StateManager wrapping the given storage.
pub fn new(storage: &'a Storage) -> Self
storage
&'a Storage
required
Reference to the underlying Storage instance
Example:
use minichain_storage::{Storage, StateManager};

let storage = Storage::open("./blockchain_data")?;
let state = StateManager::new(&storage);

Account Operations

StateManager::put_account
fn
Creates or updates an account.
pub fn put_account(&self, address: &Address, account: &Account) -> Result<()>
address
&Address
required
Account address
account
&Account
required
Account data to store
Example:
use minichain_core::{Address, Account};

let alice = Address([0xAA; 20]);
let account = Account::new_user(1000);
state.put_account(&alice, &account)?;
StateManager::get_account
fn
Gets an account, returning default (zero balance/nonce) if not found.
pub fn get_account(&self, address: &Address) -> Result<Account>
address
&Address
required
Account address
Result
Result<Account>
Returns the account or a default account with zero balance and nonce
Note: In blockchains, every address implicitly exists with zero balance. You can send coins to any address without it being “created” first.Example:
let account = state.get_account(&alice)?;
println!("Balance: {}, Nonce: {}", account.balance, account.nonce);
StateManager::account_exists
fn
Checks if an account has been explicitly stored.
pub fn account_exists(&self, address: &Address) -> Result<bool>
address
&Address
required
Account address to check
Example:
if state.account_exists(&alice)? {
    println!("Alice has an explicit account entry");
}

Balance Operations

StateManager::get_balance
fn
Gets an account’s balance.
pub fn get_balance(&self, address: &Address) -> Result<u64>
Example:
let balance = state.get_balance(&alice)?;
println!("Alice has {} tokens", balance);
StateManager::set_balance
fn
Sets an account’s balance directly.
pub fn set_balance(&self, address: &Address, balance: u64) -> Result<()>
address
&Address
required
Account address
balance
u64
required
New balance amount
Example:
// Set initial balance
state.set_balance(&alice, 1_000_000)?;
StateManager::add_balance
fn
Adds to an account’s balance (saturating addition).
pub fn add_balance(&self, address: &Address, amount: u64) -> Result<()>
address
&Address
required
Account address
amount
u64
required
Amount to add
Example:
// Credit account
state.add_balance(&alice, 500)?;
StateManager::sub_balance
fn
Subtracts from an account’s balance. Returns error if insufficient funds.
pub fn sub_balance(&self, address: &Address, amount: u64) -> Result<()>
address
&Address
required
Account address
amount
u64
required
Amount to subtract
Returns: Err(StorageError::InsufficientBalance) if balance is too low.Example:
match state.sub_balance(&alice, 300) {
    Ok(_) => println!("Debit successful"),
    Err(StorageError::InsufficientBalance { address, required, available }) => {
        println!("Insufficient: need {}, have {}", required, available);
    }
    Err(e) => return Err(e),
}
StateManager::transfer
fn
Transfers balance from one account to another.
pub fn transfer(&self, from: &Address, to: &Address, amount: u64) -> Result<()>
from
&Address
required
Source account
to
&Address
required
Destination account
amount
u64
required
Amount to transfer
Example:
// Transfer 100 tokens from Alice to Bob
let bob = Address([0xBB; 20]);
state.transfer(&alice, &bob, 100)?;

Nonce Operations

StateManager::get_nonce
fn
Gets an account’s nonce (transaction counter).
pub fn get_nonce(&self, address: &Address) -> Result<u64>
Example:
let nonce = state.get_nonce(&alice)?;
println!("Alice has sent {} transactions", nonce);
StateManager::increment_nonce
fn
Increments the nonce and returns the old value.
pub fn increment_nonce(&self, address: &Address) -> Result<u64>
Result
Result<u64>
Returns the nonce value before incrementing
Example:
// Get current nonce and increment atomically
let current_nonce = state.increment_nonce(&alice)?;
println!("Transaction nonce: {}", current_nonce);
// Account now has nonce = current_nonce + 1

Contract Code Storage

StateManager::put_code
fn
Stores contract bytecode by its hash (enables deduplication).
pub fn put_code(&self, code_hash: &Hash, code: &[u8]) -> Result<()>
code_hash
&Hash
required
Hash of the bytecode
code
&[u8]
required
Contract bytecode
Example:
use minichain_core::hash;

let bytecode = b"contract code here";
let code_hash = hash(bytecode);
state.put_code(&code_hash, bytecode)?;
StateManager::get_code
fn
Retrieves contract bytecode by its hash.
pub fn get_code(&self, code_hash: &Hash) -> Result<Option<Vec<u8>>>
code_hash
&Hash
required
Hash of the bytecode to retrieve
Example:
if let Some(code) = state.get_code(&code_hash)? {
    println!("Code length: {} bytes", code.len());
}
StateManager::deploy_contract
fn
Deploys a contract: stores code and creates contract account.
pub fn deploy_contract(
    &self,
    address: &Address,
    code: &[u8],
    initial_balance: u64,
) -> Result<Hash>
address
&Address
required
Contract deployment address
code
&[u8]
required
Contract bytecode
initial_balance
u64
required
Initial balance for the contract
Result
Result<Hash>
Returns the code hash
Example:
let contract_addr = Address([0xCC; 20]);
let bytecode = compile_contract("MyContract.sol")?;
let code_hash = state.deploy_contract(&contract_addr, &bytecode, 0)?;

println!("Contract deployed at {:?}", contract_addr);
println!("Code hash: {:?}", code_hash);
StateManager::get_code_for_address
fn
Gets the code for a contract address.
pub fn get_code_for_address(&self, address: &Address) -> Result<Option<Vec<u8>>>
address
&Address
required
Contract address
Result
Result<Option<Vec<u8>>>
Returns None if the address is not a contract or has no code
Example:
match state.get_code_for_address(&contract_addr)? {
    Some(code) => println!("Contract code: {} bytes", code.len()),
    None => println!("Not a contract"),
}

Contract Storage (SLOAD/SSTORE)

StateManager::sload
fn
Reads a 32-byte slot from contract storage (EVM SLOAD equivalent).
pub fn sload(&self, contract: &Address, slot: &StorageSlot) -> Result<[u8; 32]>
contract
&Address
required
Contract address
slot
&StorageSlot
required
Storage slot (32-byte key)
Result
Result<[u8; 32]>
Returns the 32-byte value, or zeros if uninitialized
Example:
use minichain_storage::StorageSlot;

let slot: StorageSlot = [0u8; 32]; // Slot 0
let value = state.sload(&contract_addr, &slot)?;
println!("Slot 0 value: {:?}", value);
StateManager::sstore
fn
Writes a 32-byte value to contract storage (EVM SSTORE equivalent).
pub fn sstore(&self, contract: &Address, slot: &StorageSlot, value: &[u8; 32]) -> Result<()>
contract
&Address
required
Contract address
slot
&StorageSlot
required
Storage slot (32-byte key)
value
&[u8; 32]
required
Value to store (32 bytes)
Example:
let slot: StorageSlot = [0u8; 32];
let value: [u8; 32] = [0xFF; 32];
state.sstore(&contract_addr, &slot, &value)?;
StateManager::storage_get
fn
Reads raw bytes from contract storage.
pub fn storage_get(&self, contract: &Address, key: &[u8]) -> Result<Option<Vec<u8>>>
contract
&Address
required
Contract address
key
&[u8]
required
Storage key (any length)
Example:
if let Some(data) = state.storage_get(&contract_addr, b"my_key")? {
    println!("Found {} bytes", data.len());
}
StateManager::storage_put
fn
Writes raw bytes to contract storage.
pub fn storage_put(&self, contract: &Address, key: &[u8], value: &[u8]) -> Result<()>
contract
&Address
required
Contract address
key
&[u8]
required
Storage key
value
&[u8]
required
Value to store
Example:
state.storage_put(&contract_addr, b"owner", alice.0.as_ref())?;
StateManager::storage_delete
fn
Deletes a key from contract storage.
pub fn storage_delete(&self, contract: &Address, key: &[u8]) -> Result<()>
Example:
state.storage_delete(&contract_addr, b"temp_data")?;

State Root Computation

StateManager::compute_state_root
fn
Computes the state root from all accounts.
pub fn compute_state_root(&self) -> Result<Hash>
Result
Result<Hash>
Returns a deterministic hash representing the entire state
Note: This is a naive O(n) implementation that iterates all accounts. Production systems use incremental structures like Merkle Patricia Tries.Example:
let state_root = state.compute_state_root()?;
println!("State root: {}", state_root);

// Same state = same root (deterministic)
let root2 = state.compute_state_root()?;
assert_eq!(state_root, root2);

StorageSlot Type

pub type StorageSlot = [u8; 32];
Represents a 32-byte storage slot, compatible with EVM storage layout.

Complete Example

use minichain_storage::{Storage, StateManager, StorageSlot};
use minichain_core::{Address, Account};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let storage = Storage::open("./blockchain_data")?;
    let state = StateManager::new(&storage);

    // Create accounts
    let alice = Address([0xAA; 20]);
    let bob = Address([0xBB; 20]);

    // Set initial balances
    state.set_balance(&alice, 1000)?;
    state.set_balance(&bob, 500)?;

    // Transfer
    state.transfer(&alice, &bob, 100)?;
    println!("Alice: {}", state.get_balance(&alice)?); // 900
    println!("Bob: {}", state.get_balance(&bob)?);     // 600

    // Deploy contract
    let contract_addr = Address([0xCC; 20]);
    let bytecode = b"contract bytecode";
    let code_hash = state.deploy_contract(&contract_addr, bytecode, 0)?;
    println!("Deployed contract: {:?}", code_hash);

    // Contract storage
    let slot: StorageSlot = [0u8; 32];
    let value: [u8; 32] = [0x42; 32];
    state.sstore(&contract_addr, &slot, &value)?;
    
    let loaded = state.sload(&contract_addr, &slot)?;
    assert_eq!(loaded, value);

    // Compute state root
    let root = state.compute_state_root()?;
    println!("State root: {:?}", root);

    Ok(())
}

See Also

  • Storage - Low-level database operations
  • ChainStore - Block storage and chain management

Build docs developers (and LLMs) love