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

Receive Blocks

Receive blocks (recv) are where contracts handle incoming messages. They connect message definitions to their implementations.

A named recv block handles all variants of a specific message type:

use std::abi::sol
msg TokenMsg {
#[selector = sol("transfer(address,uint256)")]
Transfer { to: u256, amount: u256 } -> bool,
#[selector = sol("balanceOf(address)")]
BalanceOf { account: u256 } -> u256,
}
contract Token {
recv TokenMsg {
Transfer { to, amount } -> bool {
// Handle transfer
true
}
BalanceOf { account } -> u256 {
// Return balance
0
}
}
}

Named recv blocks must handle all variants of the message type:

contract Token {
recv TokenMsg {
Transfer { to, amount } -> bool {
true
}
// Error: missing handler for BalanceOf
}
}

The compiler ensures you don’t forget any handlers.

A bare recv block handles messages without specifying a message type:

contract Example {
recv {
TokenMsg::Transfer { to, amount } -> bool {
true
}
OtherMsg::Query { id } -> u256 {
0
}
}
}

In bare blocks:

  • Use fully qualified paths (MsgType::Variant)
  • No exhaustiveness checking
  • Can mix variants from different message types

Recv blocks appear inside contract definitions after fields and the init block:

contract Token {
// Fields
total_supply: u256,
// Init block
init() {
// initialization
}
// Recv block
recv TokenMsg {
Transfer { to, amount } -> bool {
true
}
// ...
}
}

Each handler in a recv block has:

VariantName { fields } -> u256 {
// handler body
0
}
  • Pattern: The variant name and field destructuring
  • Return type: Must match the message variant’s return type
  • Body: The handler implementation

Handlers can use effects to access contract state:

pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
}
fn do_transfer(from: Address, to: u256, amount: u256) -> bool uses (store: mut TokenStorage) {
let from_bal = store.balances.get(to)
if from_bal < amount {
return false
}
store.balances.set(key: to, value: from_bal - amount)
true
}
contract Token uses (ctx: Ctx) {
mut store: TokenStorage
recv TokenMsg {
Transfer { to, amount } -> bool uses (ctx, mut store) {
do_transfer(from: ctx.caller(), to, amount)
}
}
}

A contract can have multiple recv blocks for different message types:

contract MultiInterface {
recv Erc20 {
Transfer { to, amount } -> bool { true }
BalanceOf { account } -> u256 { 0 }
}
recv Erc721 {
OwnerOf { token_id } -> u256 { 0 }
SafeTransferFrom { from, to, token_id } { }
}
}

See Multiple Message Types for details.

Block TypeUse When
Named (recv MsgType)Implementing a complete interface
Bare (recv)Cherry-picking specific handlers
Multiple namedImplementing multiple interfaces
SyntaxDescription
recv MsgType { }Named block, must handle all variants
recv { }Bare block, no exhaustiveness check
Variant { } -> T { }Handler with return type
Variant { } { }Handler returning ()