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

Defining Messages

Messages define the external interface of a contract: the operations that can be called from outside. They’re similar to external functions in Solidity but with a cleaner, more explicit design.

Define a message group using the msg keyword:

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,
#[selector = sol("totalSupply()")]
TotalSupply -> u256,
}

Each message group contains one or more variants: individual operations that can be called.

A variant defines a single callable operation:

#[selector = sol("transfer(address,uint256)")]
Transfer { to: u256, amount: u256 } -> bool,

Components:

  • Selector attribute: The 4-byte identifier (#[selector = sol(...)])
  • Name: The variant name (Transfer)
  • Fields: Parameters in curly braces ({ to: u256, amount: u256 })
  • Return type: What the handler returns (-> bool)

Some operations don’t need parameters:

#[selector = sol("totalSupply()")]
TotalSupply -> u256,

Operations that don’t return a value omit the return type:

#[selector = sol("safeTransferFrom(address,address,uint256)")]
SafeTransfer { from: u256, to: u256, token_id: u256 },

This implicitly returns () (unit).

A simple token message interface:

use std::abi::sol
msg Erc20 {
#[selector = sol("transfer(address,uint256)")]
Transfer { to: u256, amount: u256 } -> bool,
#[selector = sol("approve(address,uint256)")]
Approve { spender: u256, amount: u256 } -> bool,
#[selector = sol("transferFrom(address,address,uint256)")]
TransferFrom { from: u256, to: u256, amount: u256 } -> bool,
#[selector = sol("balanceOf(address)")]
BalanceOf { account: u256 } -> u256,
#[selector = sol("allowance(address,address)")]
Allowance { owner: u256, spender: u256 } -> u256,
#[selector = sol("totalSupply()")]
TotalSupply -> u256,
}

Messages provide:

  1. Clear interface definition: All callable operations in one place
  2. ABI compatibility: Selectors match Ethereum’s function selector mechanism
  3. Type safety: Parameters and return types are checked at compile time
  4. Separation of concerns: Interface definition separate from implementation

Messages are handled in recv blocks within contracts:

contract Token {
// storage fields...
recv Erc20 {
Transfer { to, amount } -> bool {
// handle transfer
true
}
// ... other handlers
}
}

See Receive Blocks for details on implementing handlers.