Contract Standard Library
The language builtins under std:: (min, sha256, …) are always in
scope — see Standard Library. Separately, cambrian-lang
ships reusable .cam components under stdlib/: integer helpers plus
ready-to-deploy token and vault entities, each with tests.
These files are ordinary Cambrian sources. Point library_paths at the
stdlib/ directory (or copy the files you need) and either import the
shared helpers or list an entity under sources:. See
Multi-File Projects.
Layout
| Path | Kind | Role |
|---|---|---|
stdlib/math.cam | helpers | Integer sqrt and a wide mul_div |
stdlib/token/erc20-core.cam | helpers | Balance / allowance / EIP-712 helpers |
stdlib/token/ERC20.cam | entity | Singleton ERC-20 + EIP-2612 permit |
stdlib/token/ERC20Multi.cam | entity | Same token with an identity (several instances) |
stdlib/token/erc7943-core.cam | helpers | Shared ERC-7943 permission helpers |
stdlib/token/ERC7943Min.cam | entity | Permissioned token: who may move value |
stdlib/token/ERC7943.cam | entity | Permissioned token plus exact accounting |
stdlib/vault/erc4626-core.cam | helpers | Asset interface and share/asset conversion |
stdlib/vault/ERC4626Min.cam | entity | ERC-4626 route set, textbook ratios |
stdlib/vault/ERC4626.cam | entity | ERC-4626 with virtual shares and counted deposits |
Helper files hold only shared declarations (pure fn, extern entity, …).
Entity files are the contracts you deploy; list those under sources:
together with their *.test.cam / *.fuzz.cam / *.invariant.cam suites.
Using a component
From a project that can see the cambrian-lang tree:
name: my-app
target: evm
library_paths:
- ${CAMBRIAN_STDLIB} # directory that contains math.cam and token/
imports:
- math.cam
- token/erc20-core.cam
sources:
- MyVault.cam
// MyVault.cam
import "math.cam"
entity MyVault {
routes {
// …
}
// …
}
A path that starts with ./ or ../ is always relative to the importing
file — that is how files inside stdlib/ refer to each other
(import "./erc20-core.cam"). A path without that prefix also searches
each library_paths entry. Names from imported files join the same
program-wide namespace as your own pure fns; two files cannot declare
the same name.
To deploy a shipped entity as-is, put it on sources: instead of
importing it:
sources:
- token/ERC20.cam
- token/ERC20.test.cam
Math helpers (math.cam)
Prefer built-in std::math for min, max, clamp, and
ordinary muldiv. math.cam exists for two operations the builtins do
not cover:
| Function | What it does |
|---|---|
sqrt(x) | Integer square root. There is no std::math::sqrt. |
mul_div(a, b, c) | ⌊a·b/c⌋ with a 512-bit intermediate. std::math::muldiv is (a * b) / c and reverts if a·b overflows U256, even when the quotient fits. |
Do not re-export min / max from this file: a user pure fn of that
name shadows the builtin for the whole program.
Tokens
ERC20 and ERC20Multi
Fungible token with transfer / transferFrom / approve, metadata
views, gated mint / holder burn, and EIP-2612 permit.
ERC20— one deployment. Use this for an application, governance, or wrapped-asset token.ERC20Multi— the same routes plusidentity m_token_id. Deploy several tokens from one codebase at CREATE2 addresses (ERC20Multi.address(0),ERC20Multi.address(1)). That is what an AMM harness needs; a single-deployment token should stay onERC20.
transfer / transferFrom / approve return true so Solidity
IERC20 callers can decode the result. A rejected transfer throws;
the bool is for the ABI, not a second error channel.
Deviation: a zero-value transfer reverts. ERC-20 requires those to
succeed. Drop the amount > 0 guard if you must accept them, and re-run
the shipped tests.
ERC7943Min and ERC7943
Permissioned (RWA-style) tokens on top of the ERC-20 helpers.
ERC7943Min— operator-controlledforcedTransfer, freeze,canSend/canReceive/canTransfer. Claims are about who may move value, and they stay meaningful over a rebasing or fee-on-transfer base.ERC7943— the same permission routes plus metadata,permit,holderCount, and exact balance arithmetic (frozen + unfrozen = balance, seizures moveamount). Adopt this only when the underlying asset does not rebase or take fees.
One privileged key (m_operator) is both minter and compliance
operator. Split those roles in an outer contract if you need two keys.
Vaults
Both vaults implement ERC-4626 (deposit / mint / withdraw /
redeem, convert/preview/limit views) and treat the share as an ERC-20.
They talk to the underlying asset through an extern entity — point
m_asset at whatever ERC-20 you deploy, including ERC20Multi.
Rounding always favours the vault (against the caller) on the four write routes.
ERC4626Min— textbookassets/sharesratios, including an empty-vault branch. Interface and revert shape only; not safe against an untrusted first depositor.ERC4626— virtual shares plus deposits credited from the amount the asset actually delivered (so a fee-on-transfer token cannot mint shares against value the vault never received). The first-depositor attack becomes expensive; it does not revert. A rebasing asset can still move the vault balance after the second read.
The vault routes capture a return from a call into the asset. The Lean
backend does not yet lower that pattern, so the shipped Lean suite
covers the token entities; exercise the vaults on EVM (forge test).
Tests
Each entity has matching *.test.cam, *.fuzz.cam, and
*.invariant.cam files in the same directory. The token project
(stdlib/token/project.yaml) and vault project
(stdlib/vault/project.yaml) are the EVM entry points. The vault
project also deploys ERC20Multi as the asset under test.
The Token example in this book is a short
teaching merge, not these components. Start from stdlib/token/ when
you want a deployable ERC-20.