Contract Declaration
Contracts are the primary building blocks of Fe smart contract development. They define storage, initialization logic, and message handlers.
Basic Contract Syntax
Section titled “Basic Contract Syntax”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 } }}Contract Fields
Section titled “Contract Fields”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.
What Contracts Cannot Have
Section titled “What Contracts Cannot Have”Unlike structs, contracts have specific restrictions:
- No impl blocks: You cannot implement methods on contracts
- No associated functions: Contracts don’t have
fndeclarations inside them - No direct field access: Storage is accessed through effects, not
self.field
// WRONG: Contracts cannot have impl blockscontract Token { store: TokenStorage,}
impl Token { // Error! fn get_balance(self) -> u256 { ... }}Instead, define methods on the storage struct and call them through the effect binding:
pub struct TokenStorage { pub balances: StorageMap<u256, u256>,}
impl TokenStorage { fn get_balance(self, account: u256) -> u256 { self.balances.get(account) }}
contract Token { store: TokenStorage,
recv TokenMsg { BalanceOf { account } -> u256 uses store { store.get_balance(account) } }}Contract Structure
Section titled “Contract Structure”The canonical structure of a Fe contract:
// 1. Storage struct definitionpub struct TokenStorage { pub balances: StorageMap<u256, u256>, pub total_supply: u256,}
// 2. Methods on the storage structimpl TokenStorage { fn do_transfer(mut self, from: u256, to: u256, amount: u256) -> bool { let from_bal = self.balances.get(from) if from_bal < amount { return false } self.balances.set(from, from_bal - amount)
let to_bal = self.balances.get(to) self.balances.set(to, to_bal + amount) true }}
// 3. Message definitionsmsg TokenMsg { #[selector = sol("transfer(address,uint256)")] Transfer { to: u256, amount: u256 } -> bool,
#[selector = sol("balanceOf(address)")] BalanceOf { account: u256 } -> u256,}
// 4. Contract declarationcontract 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) { store.do_transfer(ctx.caller(), to, amount) }
BalanceOf { account } -> u256 uses store { store.balances.get(account) } }}Multiple Contracts
Section titled “Multiple Contracts”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.
Summary
Section titled “Summary”| Element | Description |
|---|---|
contract Name { } | Declares a contract |
| Fields | Storage and effect bindings |
init() { } | Constructor logic |
recv MsgType { } | Message handlers |
Contracts are intentionally simple. They hold state, initialize it, and handle messages. Business logic lives in impl blocks on storage structs, keeping related behavior cohesive.