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, use standalone functions with effects:
// CORRECT: Use functions with effectsfn 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) } }}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. Message definitionsmsg TokenMsg { #[selector = 0xa9059cbb] Transfer { to: u256, amount: u256 } -> bool,
#[selector = 0x70a08231] BalanceOf { account: u256 } -> u256,}
// 3. Helper functions with effectsfn 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 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) { 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. All business logic lives in standalone functions that use effects.