Skip to content
This guide targets Fe 26.4. Fe is not production-ready. Install Fe 26.4

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: StorageMap<Address, u256>,
pub total_supply: u256,
}
msg TokenMsg {
#[selector = sol("balanceOf(address)")]
BalanceOf { account: Address } -> u256,
#[selector = sol("transfer(address,uint256)")]
Transfer { to: Address, amount: u256 } -> bool,
}
contract Token uses (ctx: Ctx) {
mut store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses (store) {
store.balances.get(key: account)
}
Transfer { to, amount } -> bool uses (ctx, mut store) {
do_transfer(from: ctx.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(key: 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: Address) -> u256 uses (store: TokenStorage) {
store.balances.get(key: account)
}
fn do_transfer(from: Address, to: Address, amount: u256) -> bool uses (store: mut TokenStorage) {
let from_bal = store.balances.get(key: from)
if from_bal < amount {
return false
}
store.balances.set(key: from, value: from_bal - amount)
let to_bal = store.balances.get(key: to)
store.balances.set(key: to, value: to_bal + amount)
true
}
contract Token uses (ctx: Ctx) {
mut store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses (store) {
get_balance(account)
}
Transfer { to, amount } -> bool uses (ctx, mut store) {
do_transfer(from: ctx.caller(), to, amount)
}
}
}

When handlers need multiple storage types:

pub struct BalanceStorage {
pub balances: StorageMap<Address, u256>,
}
pub struct AllowanceStorage {
pub allowances: StorageMap<Address, StorageMap<Address, u256>>,
}
contract Token uses (ctx: Ctx) {
mut balances: BalanceStorage,
mut allowances: AllowanceStorage,
recv TokenMsg {
TransferFrom { from, to, amount } -> bool uses (ctx, mut balances, mut allowances) {
do_transfer_from(c: ctx.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 {
mut 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<31> { "Token" }
Symbol {} -> String<8> { "TKN" }
Decimals {} -> u8 { 18 }
}
}

Handlers reject nonzero ETH value unless marked #[payable]. A bare recv block can include one wildcard arm, _, for unmatched selectors and calldata shorter than four bytes. Mark the fallback payable if it should accept plain ETH transfers:

use std::abi::sol
msg DonationMsg {
#[selector = sol("received()")]
Received -> u256,
}
pub contract Donations {
mut received: u256,
recv {
DonationMsg::Received {} -> u256 uses received { received }
#[payable]
_ uses (ctx: Ctx, mut received) {
received += ctx.value()
}
}
}

This fallback accepts unknown selectors too; it is not restricted to empty calldata. Without a fallback, unmatched calls revert. A matched but nonpayable handler still rejects ETH rather than falling through to the wildcard.

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
#[payable]Handler accepts nonzero ETH value
_ { }Fallback arm in a bare recv block