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

Contract Declaration

Contracts are the primary building blocks of Fe smart contract development. They define storage, initialization logic, and message handlers.

Declare a contract with the contract keyword:

contract Token {
// Contract body
}

A contract can contain:

  • Fields: Storage and effect bindings
  • Init block: Constructor logic (optional)
  • Recv blocks: Message handlers
contract Token {
// Fields
store: TokenStorage,
// Init block
init(initial_supply: u256) {
// initialization logic
}
// Recv blocks
recv TokenMsg {
Transfer { to, amount } -> bool {
true
}
}
}

Fields declare the contract’s storage and effect dependencies:

pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
pub total_supply: u256,
}
contract Token {
store: TokenStorage,
}

Fields are bound to effect types. The field name (store) becomes available for use in with expressions to provide effects to functions.

Unlike structs, contracts have specific restrictions:

  • No impl blocks: You cannot implement methods on contracts
  • No associated functions: Contracts don’t have fn declarations inside them
  • No direct field access: Storage is accessed through effects, not self.field
// WRONG: Contracts cannot have impl blocks
contract Token {
store: TokenStorage,
}
impl Token { // Error!
fn get_balance(self) -> u256 { ... }
}

Instead, use standalone functions with effects:

// CORRECT: Use functions with effects
fn get_balance(account: u256) -> u256 uses (store: TokenStorage) {
store.balances.get(account)
}
contract Token {
store: TokenStorage,
recv TokenMsg {
BalanceOf { account } -> u256 uses store {
get_balance(account)
}
}
}

The canonical structure of a Fe contract:

// 1. Storage struct definition
pub struct TokenStorage {
pub balances: StorageMap<u256, u256>,
pub total_supply: u256,
}
// 2. Message definitions
msg TokenMsg {
#[selector = 0xa9059cbb]
Transfer { to: u256, amount: u256 } -> bool,
#[selector = 0x70a08231]
BalanceOf { account: u256 } -> u256,
}
// 3. Helper functions with effects
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
}
// 4. Contract declaration
contract Token {
mut store: TokenStorage,
init(initial_supply: u256) uses (mut store) {
store.total_supply = initial_supply
}
recv TokenMsg {
Transfer { to, amount } -> bool uses (ctx: Ctx, mut store) {
do_transfer(ctx.caller(), to, amount)
}
BalanceOf { account } -> u256 uses store {
store.balances.get(account)
}
}
}

A single Fe file can define multiple contracts:

contract TokenA {
store: TokenStorage,
}
contract TokenB {
store: TokenStorage,
}

Each contract is compiled to separate bytecode and deployed independently.

ElementDescription
contract Name { }Declares a contract
FieldsStorage and effect bindings
init() { }Constructor logic
recv MsgType { }Message handlers

Contracts are intentionally simple. They hold state, initialize it, and handle messages. All business logic lives in standalone functions that use effects.