Skip to content
Pre-Release: Fe is under active development. This documentation covers the upcoming release. Follow progress on GitHub

Receive Blocks in Contracts

Receive blocks inside contracts handle incoming messages and have access to the contract’s storage through effects. This section focuses on how recv blocks work within the contract context.

In a contract, recv blocks access storage via uses clauses:

pub struct TokenStorage {
pub balances: Map<u256, u256>,
pub total_supply: u256,
}
msg TokenMsg {
#[selector = 0x70a08231]
BalanceOf { account: u256 } -> u256,
#[selector = 0xa9059cbb]
Transfer { to: u256, amount: u256 } -> bool,
}
contract Token {
mut store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses (store) {
store.balances.get(account)
}
Transfer { to, amount } -> bool uses (mut store) {
do_transfer(caller(), to, amount)
}
}
}

The uses clause on a handler declares which contract fields it needs:

BalanceOf { account } -> u256 uses (store) {
// store is available here
store.balances.get(account)
}

This pattern:

  1. Declares that the handler needs the store field
  2. Makes store directly accessible in the handler body
  3. Propagates the effect to any called functions

Handlers typically delegate to helper functions:

fn get_balance(account: u256) -> u256 uses (store: TokenStorage) {
store.balances.get(account)
}
fn do_transfer(from: u256, to: u256, amount: u256) -> bool uses (mut store: TokenStorage) {
let from_bal = store.balances.get(from)
if from_bal < amount {
return false
}
store.balances.set(from, from_bal - amount)
let to_bal = store.balances.get(to)
store.balances.set(to, to_bal + amount)
true
}
contract Token {
mut store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses (store) {
get_balance(account)
}
Transfer { to, amount } -> bool uses (mut store) {
do_transfer(caller(), to, amount)
}
}
}

When handlers need multiple storage types:

pub struct BalanceStorage {
pub balances: Map<u256, u256>,
}
pub struct AllowanceStorage {
pub allowances: Map<u256, Map<u256, u256>>,
}
contract Token {
mut balances: BalanceStorage,
mut allowances: AllowanceStorage,
recv TokenMsg {
TransferFrom { from, to, amount } -> bool uses (mut balances, mut allowances) {
do_transfer_from(caller(), from, to, amount)
}
}
}

For a full ERC20-style token contract demonstrating all these patterns, see the Complete ERC20 example.

For contracts with many handlers, organize by interface:

contract Token {
store: TokenStorage,
// Core ERC20 operations
recv Erc20 {
Transfer { to, amount } -> bool {
let _ = (to, amount)
true
}
Approve { spender, amount } -> bool {
let _ = (spender, amount)
true
}
TransferFrom { from, to, amount } -> bool {
let _ = (from, to, amount)
true
}
BalanceOf { account } -> u256 {
let _ = account
0
}
Allowance { owner, spender } -> u256 {
let _ = (owner, spender)
0
}
TotalSupply {} -> u256 { 0 }
}
// Metadata extension
recv Erc20Metadata {
Name {} -> String<32> { "Token" }
Symbol {} -> String<8> { "TKN" }
Decimals {} -> u8 { 18 }
}
}
ConceptDescription
recv MsgType { }Recv block in contract
uses (field)Access storage in handler
Helper functionsFunctions with uses clause
Multiple effectsuses (mut a, mut b)
OrganizationSeparate recv blocks per interface