Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

PathKindRole
stdlib/math.camhelpersInteger sqrt and a wide mul_div
stdlib/token/erc20-core.camhelpersBalance / allowance / EIP-712 helpers
stdlib/token/ERC20.camentitySingleton ERC-20 + EIP-2612 permit
stdlib/token/ERC20Multi.camentitySame token with an identity (several instances)
stdlib/token/erc7943-core.camhelpersShared ERC-7943 permission helpers
stdlib/token/ERC7943Min.camentityPermissioned token: who may move value
stdlib/token/ERC7943.camentityPermissioned token plus exact accounting
stdlib/vault/erc4626-core.camhelpersAsset interface and share/asset conversion
stdlib/vault/ERC4626Min.camentityERC-4626 route set, textbook ratios
stdlib/vault/ERC4626.camentityERC-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:

FunctionWhat 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 plus identity 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 on ERC20.

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-controlled forcedTransfer, 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 move amount). 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 — textbook assets/shares ratios, 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.