docs

Cudo Language Reference

Everything you need to write, compile, and deploy Cudo contracts on Ferros. If you're coming from Solidity, also see the migration guide.

Cudo is a smart contract language where the compiler proves your parallelism is safe, double-spend is a type error, and reentrancy doesn't exist in the grammar. Every contract declares what state it touches, what it requires, and how it contends — the compiler holds you to it.

Design principle: If the compiler can catch it, the runtime shouldn't have to. Cudo eliminates entire bug classes at compile time rather than mitigating them at runtime.

Installation

Cudo requires a Rust toolchain. Install it, then build from source:

terminal
# clone and build
$ git clone https://github.com/user/cudo && cd cudo
$ cargo build --release

# verify
$ cudo --version
cudo 0.1.0

The CLI gives you four commands: cudo check, cudo compile, cudo run, and cudo inspect.

Hello World

The simplest Cudo contract: a counter. It declares one piece of state, one invariant, and two operations.

counter.cudo cudo
contract Counter {
    version: 1
    contention: global

    state {
        count: u64,
    }

    operation increment()
        mutates: self.count
    {
        self.count += 1;
    }

    view get() -> u64 {
        self.count
    }
}
terminal
$ cudo check counter.cudo
 counter.cudo: 0 errors, 0 warnings
  contention: global (1 thread)

Contracts

Every Cudo program is a contract. A contract is a self-contained unit with declared state, operations, invariants, and a contention model. There is no inheritance, no import system for mutable state, and no implicit globals.

structure cudo
contract Name<TypeParams> {
    version: u32             // required, monotonic
    contention: model        // required

    capability ...           // zero or more
    state { ... }             // exactly one
    invariant ...            // zero or more
    operation ...            // one or more
    view ...                 // zero or more
}

Version

Every contract has a version field. It must be a positive integer and must increase monotonically between deployments. The runtime uses this for state migration.

Type Parameters

Contracts can be generic. Type parameters appear after the name and can carry trait bounds like copy and drop which control whether values of that type can be duplicated or silently destroyed.

generic contract cudo
contract Token<Symbol: copy + drop> {
    // Symbol can be duplicated and dropped freely
    // useful for marker types like "USDC" or "ETH"
}

State

The state block declares every piece of persistent storage the contract owns. All state is explicit — there are no hidden storage slots, no dynamic allocation outside what you declare.

state declaration cudo
state {
    total_supply: u256,
    balances:     Map<address, u256>,
    frozen:       Set<address>,
    owner:        address,
}

State can also be parameterized by the contention key. If your contract declares contention: per<Pair>, you can have state scoped to each partition:

partitioned state cudo
contention: per<Pair>

state<Pair> {
    bids: SortedMap<u256, Vec<Order>>,
    asks: SortedMap<u256, Vec<Order>>,
}

Operations

Operations are the write-path of your contract. They are the only way to mutate state. Every operation must declare exactly what it touches through clauses.

operation anatomy cudo
operation transfer(to: address, amount: u256)
    requires: self.balances[caller] >= amount
    mutates:  self.balances[caller], self.balances[to]
    emits:    Transfer
{
    self.balances[caller] -= amount;
    self.balances[to]     += amount;
    emit Transfer { from: caller, to, amount };
}

Clauses

Clauses are the contract between you and the compiler. Break it and your code doesn't compile.

Clause Purpose Enforcement
requires: Precondition that must hold before execution Checked at entry; transaction reverts if false
mutates: Exhaustive list of state fields the operation writes Compiler rejects writes to undeclared fields
emits: Events the operation may emit Compiler rejects undeclared emits
consumes: Linear resources destroyed by this operation Resource must be moved in and destroyed exactly once
No escape hatch. You cannot touch state outside your mutates: clause. There is no unsafe block, no assembly inline, no backdoor. This is what enables the scheduler to parallelize without speculation.

Resources

Resources are Cudo's linear types. A resource must be used exactly once — you can't copy it, you can't silently drop it. This is how Cudo eliminates double-spend at the type level.

resource definition cudo
resource Coin { amount: u256 }

operation pay(coin: Coin, to: address)
    consumes: coin
{
    send(coin, to);    // coin is moved here
    // send(coin, attacker);  <-- compile error: used after move
}

Resources follow three rules:

destroy a resource cudo
operation burn(coin: Coin)
    consumes: coin
    mutates:  self.total_supply
{
    self.total_supply -= coin.amount;
    destroy coin;   // explicitly destroyed
}

Capabilities

Capabilities replace role-based access control with type-level permissions. A capability isn't a boolean check — it's a type constraint that the compiler enforces. If you don't have the capability, the compiler won't let you call the operation.

capabilities cudo
capability Minter;
capability Freezer;

operation mint(to: address, amount: u256)
    requires: caller has Minter
    mutates:  self.total_supply, self.balances[to]
{
    self.total_supply += amount;
    self.balances[to]  += amount;
}

operation freeze(account: address)
    requires: caller has Freezer
    mutates:  self.frozen
{
    self.frozen.insert(account);
}

Capabilities can be granted, revoked, and combined. They compose naturally — an operation can require multiple capabilities, and the compiler proves each caller has all of them at the call site.

Contention

Every contract declares a contention model that tells the scheduler how to parallelize transactions. This isn't an optimization hint — it's a compiler-enforced guarantee about which operations can run concurrently.

Model Syntax Parallelism Use Case
per<K> contention: per<address> Operations on different keys run in parallel Tokens, balances, per-user state
owned contention: owned Single-owner, strictly sequential Wallets, personal vaults
global contention: global Total ordering, no parallelism Governance, rare singleton contracts
per-address contention cudo
contract Token {
    version: 1
    contention: per<address>   // Alice→Bob ‖ Carol→Dave

    // transfers between non-overlapping address sets
    // execute on different threads simultaneously
}
Why not auto-detect? Explicit contention declarations give the scheduler perfect information without speculation. There is no rollback, no re-execution, no wasted work. You say what contends; the scheduler believes you because the compiler already proved it.

Invariants

Invariants are properties that the compiler proves hold after every operation. This is not testing. This is not SMT solving. The compiler structurally verifies that every possible execution path preserves the invariant.

invariant example cudo
invariant conservation:
    self.total_supply == self.balances.values().sum()

invariant non_negative:
    self.balances.values().all(|b| b >= 0)

If the compiler can't prove that an operation preserves an invariant, it rejects the program with a concrete counter-example showing which execution path breaks it.

Views

Views are the read-only path. They can access state but cannot mutate it. Views don't need mutates: clauses because they don't touch anything. They're free to call without gas considerations.

view functions cudo
view balance_of(account: address) -> u256 {
    self.balances[account]
}

view is_frozen(account: address) -> bool {
    self.frozen.contains(account)
}

Events

Events must be declared and listed in the emits: clause. This means the compiler knows every possible event an operation can produce, enabling indexers to be generated automatically.

events cudo
event Transfer {
    from: address,
    to: address,
    amount: u256,
}

operation transfer(...)
    emits: Transfer
{
    // ...
    emit Transfer { from: caller, to, amount };
}

Primitive Types

TypeDescriptionSize
u8 .. u256Unsigned integers (8 to 256 bits)1-32 bytes
i8 .. i256Signed integers1-32 bytes
boolBoolean1 byte
address32-byte account address32 bytes
bytesDynamic byte arrayvariable
stringUTF-8 stringvariable

Arithmetic

All arithmetic is checked by default. Overflow aborts the transaction. If you need wrapping arithmetic, opt in explicitly:

OperatorCheckedWrapping
Add++%
Sub--%
Mul**%

Generics

Type parameters can carry trait bounds that control value semantics:

BoundMeaning
copyValue can be duplicated (implicit copy on assignment)
dropValue can be silently destroyed when it goes out of scope
neitherValue is a linear resource — use exactly once
trait bounds cudo
// Symbol can be freely copied and dropped
contract Token<Symbol: copy + drop> { ... }

// Coin has no bounds — it's a linear resource
resource Coin { amount: u256 }

Linear Types

Linear types are the foundation of Cudo's safety model. Any type without copy and drop bounds is linear: it must be used exactly once. The compiler tracks ownership through every branch, loop, and function call.

Why linear? In smart contracts, values represent real assets. A token that can be silently dropped means lost funds. A token that can be copied means infinite money. Linear types make both of these compile errors.

The compiler catches:

Collections

TypeDescription
Map<K, V>Key-value mapping (hash map semantics)
Set<T>Unique value set
Vec<T>Dynamic array
SortedMap<K, V>Ordered map (B-tree semantics)

Collections declared in state are persisted on-chain. Local collections in operation bodies are transient and discarded after execution.

CLI Reference

CommandDescription
cudo check <file>Type-check and verify invariants. Reports contention model.
cudo check <file> --scheduleType-check + show parallelism schedule.
cudo compile <file>Compile to dual-ISA binary (RISC-V + Wasm).
cudo run <file> --op <name>Execute an operation in a local sandbox. Reports gas usage.
cudo inspect <file>Print contract metadata: state layout, operations, contention.
cudo prove <file>Export invariant proofs as verifiable artifacts.
cudo new <name>Scaffold a new contract project.

Error Catalog

CodeNameDescription
E0301Resource used after moveA linear resource was used after it was already moved. Double-spend attempt.
E0302Resource dropped without consumeA linear resource went out of scope without being explicitly consumed or destroyed.
E0303Undeclared state mutationOperation body writes to a state field not listed in its mutates: clause.
E0304Invariant violationCompiler cannot prove that an operation preserves a declared invariant.
E0305Undeclared event emissionOperation emits an event not listed in its emits: clause.
E0401Missing capabilityCaller does not have the required capability for this operation.
E0501Contention conflictOperation accesses state outside its declared contention partition.
E0601Integer overflowArithmetic would overflow. Use wrapping operators (+% -% *%) if intentional.