Storage Fields
Storage fields hold the persistent state of a contract. In Fe, storage is defined as struct types that contain storage-capable fields.
Declaring Storage
Section titled “Declaring Storage”Storage is defined as a struct with storage-compatible fields:
pub struct TokenStorage { pub balances: Map<u256, u256>, pub total_supply: u256,}
contract Token { store: TokenStorage,}The contract field store holds an instance of TokenStorage, which persists between transactions.
Storage-Compatible Types
Section titled “Storage-Compatible Types”Primitive Types
Section titled “Primitive Types”All primitive types can be stored:
pub struct Config { pub enabled: bool, pub count: u256, pub threshold: i128,}StorageMap
Section titled “StorageMap”For key-value mappings, use StorageMap:
pub struct TokenStorage { // Maps account -> balance pub balances: Map<u256, u256>,
// Maps owner -> (spender -> allowance) pub allowances: Map<u256, Map<u256, u256>>,}Nested Structs
Section titled “Nested Structs”Storage structs can contain other structs:
pub struct Metadata { pub name_length: u256, pub decimals: u8,}
pub struct TokenStorage { pub balances: Map<u256, u256>, pub metadata: Metadata,}Accessing Storage
Section titled “Accessing Storage”Storage is accessed through effects, not directly:
fn get_balance(account: u256) -> u256 uses (store: TokenStorage) { store.balances.get(account)}
fn set_balance(account: u256, amount: u256) uses (mut store: TokenStorage) { store.balances.set(account, amount)}In handlers, use the uses clause to access storage fields:
contract Token { mut store: TokenStorage,
recv TokenMsg { BalanceOf { account } -> u256 uses (store) { store.balances.get(account) } }}StorageMap Operations
Section titled “StorageMap Operations”Retrieve a value (returns zero/default if not set):
let balance = store.balances.get(account)Store a value:
store.balances.set(account, new_balance)Nested Maps
Section titled “Nested Maps”For nested mappings, chain the operations:
pub struct AllowanceStorage { // owner -> spender -> amount pub allowances: Map<u256, Map<u256, u256>>,}
fn get_allowance(owner: u256, spender: u256) -> u256 uses (store: AllowanceStorage) { store.allowances.get(owner).get(spender)}
fn set_allowance(owner: u256, spender: u256, amount: u256) uses (mut store: AllowanceStorage) { store.allowances.get(owner).set(spender, amount)}Multiple Storage Fields
Section titled “Multiple Storage Fields”Contracts can have multiple storage fields for logical separation:
pub struct BalanceStorage { pub balances: Map<u256, u256>, pub total_supply: u256,}
pub struct OwnerStorage { pub owner: u256, pub pending_owner: u256,}
contract OwnableToken { mut tokens: BalanceStorage, mut ownership: OwnerStorage,
recv TokenMsg { Transfer { to, amount } -> bool uses (mut tokens) { do_transfer(caller(), to, amount) } }
recv OwnerMsg { TransferOwnership { new_owner } uses (mut ownership) { initiate_transfer(new_owner) } }}Storage Layout
Section titled “Storage Layout”Fe computes storage slots automatically. Each field gets a deterministic location based on:
- The struct layout
- The field position
- For maps, the key combined with the base slot
You don’t need to manually specify storage slots.
Summary
Section titled “Summary”| Concept | Description |
|---|---|
| Storage struct | Struct type containing persistent fields |
| Contract field | Instance of storage struct in contract |
StorageMap<K, V> | Key-value mapping in storage |
.get(key) | Read from map |
.set(key, value) | Write to map |
| Effect access | Use with to provide storage to functions |