Skip to content
Fe 26.0 is not production-ready. This is an initial release of a new compiler. Learn more

Storage Structs

Storage structs are structs designed to hold persistent blockchain state. They serve as effect types, enabling the explicit storage access pattern that Fe uses.

A storage struct contains fields that persist on-chain:

pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
pub total_supply: u256,
pub owner: u256,
}

Key characteristics:

  • Contains Map fields for mappings
  • Contains primitive fields for simple values
  • Used as effect types with uses clause
  • Bound to contract fields

Storage structs serve as effect types. Define methods on them using impl blocks, with self for read-only and mut self for write access:

pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
pub total_supply: u256,
}
impl TokenStorage {
// Read-only access
fn get_balance(self, account: u256) -> u256 {
self.balances.get(key: account)
}
// Write access
fn set_balance(mut self, account: u256, amount: u256) {
self.balances.set(key: account, value: amount)
}
}

Contracts hold storage structs as fields and provide them as effects:

contract Token {
mut store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses (store) {
store.get_balance(account)
}
Transfer { to, amount } -> bool uses (mut store) {
store.transfer(from: caller(), to, amount)
}
}
}

The handler’s uses (store) clause binds the contract field to the effect. Methods on the storage struct are called through this binding.

For simple contracts, one storage struct is sufficient:

pub struct CounterStorage {
pub value: u256,
}
impl CounterStorage {
fn get_value(self) -> u256 {
self.value
}
fn increment(mut self) {
self.value = self.value + 1
}
}

For complex contracts, split storage by concern:

// Token balances
pub struct BalanceStorage {
pub balances: StorageMap<u256, u256>,
pub total_supply: u256,
}
// Allowance tracking
pub struct AllowanceStorage {
pub allowances: StorageMap<(u256, u256), u256>,
}
// Access control
pub struct OwnerStorage {
pub owner: u256,
pub pending_owner: u256,
}
// Pausability
pub struct PauseStorage {
pub paused: bool,
}

Each becomes an independent effect:

fn transfer(from: u256, to: u256, amount: u256)
-> bool uses (balances: mut BalanceStorage, pause: PauseStorage)
{
if pause.paused {
return false
}
// ... transfer logic
true
}

Map is the primary collection type for storage:

pub struct Registry {
// Simple mapping: key -> value
pub entries: StorageMap<u256, u256>,
// Composite key mapping: (key, key) -> value
pub nested: StorageMap<(u256, u256), u256>,
}

Access patterns:

pub struct Registry {
pub entries: StorageMap<u256, u256>,
pub nested: StorageMap<(u256, u256), u256>,
}
impl Registry {
fn get_entry(self, key: u256) -> u256 {
self.entries.get(key)
}
fn set_entry(mut self, key: u256, value: u256) {
self.entries.set(key, value)
}
fn get_nested(self, outer: u256, inner: u256) -> u256 {
self.nested.get(key: (outer, inner))
}
fn set_nested(mut self, outer: u256, inner: u256, value: u256) {
self.nested.set(key: (outer, inner), value)
}
}

Storage structs and their fields are typically public:

pub struct TokenStorage {
pub balances: StorageMap<u256, u256>, // Public for effect access
pub total_supply: u256,
}

The pub on fields allows store.balances syntax in functions using the effect.

AspectStorage StructRegular Struct
PurposePersistent stateIn-memory data
ContainsMap, primitivesAny types
Used asEffect typeValue type
AccessVia uses clauseDirect
LocationOn-chainMemory

A full token with storage structs:

// Storage definitions
pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
pub allowances: StorageMap<(u256, u256), u256>,
pub total_supply: u256,
}
impl TokenStorage {
fn get_balance(self, account: u256) -> u256 {
self.balances.get(key: account)
}
fn transfer(mut self, from: u256, to: u256, amount: u256) -> bool {
let from_bal = self.balances.get(key: from)
if from_bal < amount {
return false
}
self.balances.set(key: from, value: from_bal - amount)
let to_bal = self.balances.get(key: to)
self.balances.set(key: to, value: to_bal + amount)
true
}
}
pub struct MetadataStorage {
pub name_hash: u256,
pub symbol_hash: u256,
pub decimals: u8,
}
impl MetadataStorage {
fn get_decimals(self) -> u8 {
self.decimals
}
}
// Contract binding storage to effects
contract Token {
mut tokens: TokenStorage,
metadata: MetadataStorage,
recv Erc20 {
Transfer { to, amount } -> bool uses (mut tokens) {
tokens.transfer(from: caller(), to, amount)
}
BalanceOf { account } -> u256 uses (tokens) {
tokens.get_balance(account)
}
Decimals -> u8 uses (metadata) {
metadata.get_decimals()
}
}
}
ConceptDescription
Storage structStruct holding persistent state
Effect typeStorage struct used in uses clause
StorageMap<K, V>Key-value storage field
uses (store: Storage)Read-only access
uses (store: mut Storage)Read-write access
Handler uses (field)Bind contract field to effect in handlers