Program structure
A file is a contract
The simplest Lithe program is a file of top-level declarations. The file is the contract:
import "std/evm";
storage is record {
owner : address;
}
external function claim() is {
storage.owner := Evm.sender();
}To name the contract, or to put several contracts in one file, wrap each in contract Name is { ... }:
contract Registry is {
storage is record {
count : uint;
}
external function register() is {
storage.count := storage.count + 1;
}
}A file uses either top-level declarations or explicit contract blocks, never both. lithe compile file.lithe needs a file with exactly one contract. lithe compile directory builds every contract in every file and writes one artifact per contract.
Declarations
A contract is made of these declarations, in any order. Names are resolved across the whole file, so a function can use a type declared below it.
| Declaration | Example | See |
|---|---|---|
| Storage | storage is record { ... } | Storage |
| Named storage | storage config at "app.config" is record { ... } | Storage |
| Transient storage | transient storage is record { ... } | Storage |
| Type | type OrderId is bytes32; | Types |
| Error | error NotOwner; | Errors |
| Error set | errors admin_errors is | NotOwner | Paused | Errors |
| Event | event Paid of record { ... } | Events |
| Interface | interface Token is { ... } | Queries and deferred calls |
| Function | external function f() is { ... } | Functions |
| Module | module Ledger is { ... } | Modules |
| Use | use Ledger; | Modules |
| Guard | guard M.editor(const id : uint) is { ... }, for a module's guard editor(const id : uint); | Guards |
| Import | import "./lib"; | Imports |
| Upgrades | upgradeable, upgrade guard is { ... }, migrate f() is { ... } | Upgrades |
| Receive and fallback | payable receive is { ... }, fallback is { ... }, fallback delegates to target; | Functions |
Every contract has at least one external function. A contract with no state declares no storage:
external pure function version() : uint is
1Comments
Comments are written (* like this *). They can span lines, and they nest, so you can comment out code that already contains comments.
(* A comment.
(* A nested comment. *)
Still inside the outer comment. *)There are no line comments.
Punctuation
Each bracket has one job:
| Bracket | Used for |
|---|---|
( ) | grouping, calls, parameters, tuples |
[ ] | list literals, indexing, map lookup |
{ } | records, blocks, case arms, interfaces, modules, contracts |
Statements end with ;. Fields in a record type or record literal also end with ;. Parameters are separated by ;, and arguments by ,:
pure function scale(const amount : uint; const bps : uint) : uint is
amount * bps / 10_000
const fee : uint := scale(1_000, 25);Naming
Lithe does not enforce a naming style, but its examples and standard modules follow the EVM's, so the names a contract declares are the names its ABI shows:
| Kind | Convention | Example |
|---|---|---|
| Functions, fields, locals, parameters, constants | camelCase | transferFrom, basisPoints |
| Types, errors, events, variant cases | PascalCase | BasisPoints, NotOwner, Transfer |
| Modules, interfaces, contracts | PascalCase | Ledger, IERC20 |
An acronym keeps its capitals, as in OpenZeppelin's names: tokenURI, ERC20, EIP712Domain. The built-in types are lowercase: uint, address, bytes32, map, list.
Every name appears in the ABI exactly as written, so a name a standard fixes, such as DOMAIN_SEPARATOR, is written as the standard spells it. A variant case a contract dispatches on names its entrypoint with external: | SetFee external setFee of uint.
Keywords
These words are reserved:
and as assert at bound call case const contract create defer do else emit end error
errors event external fail failwith false for function if import in indexed interface
is list migrate module nil nonreentrant not of or payable private pure query raises
record requires skip then true type Unit until upgrade upgradeable use var view withstorage, transient, guard, receive, and fallback are keywords only where a declaration expects them, guarded only after an external's header, delegates, forwards, and to only after fallback, and forward only before a name at the start of a statement. Elsewhere they are names like any other. create also names a function, or a member after ., so a contract can expose, or call, a factory's create(...).

