Contract-Level Effects
Contracts bridge the gap between storage and the effect system. Contract fields hold storage, and handlers use uses clauses to access those fields as effects.
The Contract-Effect Relationship
Section titled “The Contract-Effect Relationship”When you declare a contract field with a storage type, that field becomes available as an effect in handlers:
contract Token { mut store: TokenStorage, // Storage field
recv TokenMsg { BalanceOf { account } -> u256 uses (store) { // Access storage through the uses clause store.balances.get(account) } }}The uses (store) clause on the handler:
- Makes the contract field
storeavailable in the handler - Allows code to use
store.fieldsyntax directly - Propagates the effect to any called functions
Providing Effects to Functions
Section titled “Providing Effects to Functions”The primary use of contract-level effects is providing them to helper functions:
fn get_balance(account: u256) -> u256 uses (store: TokenStorage) { store.balances.get(account)}
fn add_balance(account: u256, amount: u256) uses (mut store: TokenStorage) { let current = store.balances.get(account) store.balances.set(account, current + amount)}
contract Token { mut store: TokenStorage,
recv TokenMsg { BalanceOf { account } -> u256 uses (store) { get_balance(account) }
Transfer { to, amount } -> bool uses (mut store) { add_balance(to, amount) true } }}Mutable vs Immutable Effects
Section titled “Mutable vs Immutable Effects”Use mut when you need to modify storage:
contract Token { mut store: TokenStorage,
recv TokenMsg { // Read-only: no mut needed BalanceOf { account } -> u256 uses (store) { store.balances.get(account) }
// Writing: mut required Transfer { to, amount } -> bool uses (mut store) { store.balances.set(to, amount) true } }}When calling functions that require mut effects, the handler must declare mut access.
Multiple Effect Fields
Section titled “Multiple Effect Fields”Contracts can have multiple fields for different effects:
pub struct TokenStorage { pub balances: Map<u256, u256>, pub total_supply: u256,}
pub struct AllowanceStorage { pub allowances: Map<u256, Map<u256, u256>>,}
contract Token { mut tokens: TokenStorage, mut permits: AllowanceStorage,
recv TokenMsg { Transfer { to, amount } -> bool uses (mut tokens) { do_transfer(caller(), to, amount) }
Approve { spender, amount } -> bool uses (mut permits) { set_allowance(caller(), spender, amount) true }
TransferFrom { from, to, amount } -> bool uses (mut tokens, mut permits) { // Multiple effects in one handler do_transfer_from(caller(), from, to, amount) } }}Effect Scope
Section titled “Effect Scope”Effects declared in a handler’s uses clause are available throughout that handler:
contract Token { mut store: TokenStorage,
recv TokenMsg { Transfer { to, amount } -> bool uses (store) { // store is available throughout this handler let balance = store.balances.get(caller()) let _ = (to, amount, balance) true } }}Effects are scoped to the handler that declares them, making it clear what storage each handler accesses.
Why This Design?
Section titled “Why This Design?”The contract-effect pattern provides:
- Explicit dependencies: Functions declare what storage they need
- Testability: Effects can be mocked in tests
- Clarity: Storage access is always visible in the code
- Separation: Business logic (functions) is separate from state (contracts)
Compare to Solidity:
// Solidity - implicit state accesscontract Token { mapping(address => uint256) balances;
function transfer(address to, uint256 amount) public { balances[msg.sender] -= amount; // Hidden dependency on contract state balances[to] += amount; }}// Fe - explicit effect dependencyfn transfer(to: u256, amount: u256) -> bool uses (mut store: TokenStorage) { // Clear that this function needs TokenStorage store.balances.set(to, amount) true}Summary
Section titled “Summary”| Concept | Description |
|---|---|
| Contract field | mut store: Storage holds storage as effect |
Handler uses | uses (store) accesses storage in handler |
| Multiple effects | uses (mut a, mut b) |
| Mutable access | Use mut when modifying storage |