Modules
Modules own their storage, errors, events, and functions, and declare the externals they offer. A contract uses modules and exposes exactly what it chooses, by declaring a module's externals or by wrapping its functions. Each module here is a file of its own under lib/, and the contract's file imports them.
lib/ownable.lithe
import "std/evm";
module Ownable is {
storage ownable at "example.ownable" is record {
owner : address;
}
error NotOwner;
external view function owner() : address is
ownable.owner
view function onlyOwner()
raises NotOwner
is {
assert(Evm.sender() = ownable.owner, NotOwner);
}
external function claim() is {
ownable.owner := Evm.sender();
}
}lib/pausable.lithe
module Pausable is {
storage pausable at "example.pausable" is record {
paused : bool;
}
error Paused;
external view function paused() : bool is
pausable.paused
view function whenNotPaused()
raises Paused
is {
assert(not pausable.paused, Paused);
}
function setPaused(const paused : bool) is {
pausable.paused := paused;
}
}lib/admin.lithe
import "./ownable";
import "./pausable";
(* A module that builds on another says so with `requires`. *)
module Admin is {
requires Ownable;
requires Pausable;
external function pause()
raises NotOwner
is {
Ownable.onlyOwner();
Pausable.setPaused(true);
}
}modules.lithe
(* Modules replace inheritance. A module owns its storage, errors, events, and
functions, and declares the externals it offers. A contract declares exactly
which of them it exposes. Nothing becomes public by accident. *)
import "./lib/ownable";
import "./lib/pausable";
import "./lib/admin";
contract Vault is {
(* Declare a module's externals to expose them as they are. Each declaration
restates the external's header, so the contract lists its whole interface. *)
use Ownable exposing {
view function owner() : address;
function claim();
}
use Pausable exposing {
view function paused() : bool;
}
use Admin exposing {
function pause()
raises NotOwner;
}
storage is record {
total : uint;
}
(* Or wrap a module's functions. Outside its module, module storage is
`Module.domain.field`. *)
external function deposit(const amount : uint)
raises Paused
is {
Pausable.whenNotPaused();
storage.total := storage.total + amount;
}
external view function isPaused() : bool is
Pausable.pausable.paused
}requiresdeclares a module's dependencies. A contract that usesAdminmust also useOwnableandPausable.- A declaration in
use M exposingmust match the module's external exactly: its effect, parameters, result, and errors. It has no body. - A module's externals it doesn't declare are not part of the contract.
- Each module's storage sits at its own ERC-7201 root, so modules never collide.
- Outside its module, storage is written as
Pausable.pausable.paused.

