Everything you know still applies — contracts, state, events, access control. Cudo just moves the safety checks from runtime to compile time. This guide maps every Solidity concept to its Cudo equivalent.
Every Solidity concept has a Cudo equivalent. Some get stricter, some get simpler, some disappear entirely (because the compiler handles them).
contractcontractmapping / state variablesstate { } blockfunction (external/public)operationview / pure functionsviewevent + emitevent + emits: clausemodifier / require()requires: clauseonlyOwner / role checkscapability + hasnonReentrantSafeMath / unchecked blocks+% -% *% for wrappingcontention: (parallelism model)invariant (compiler-proven properties)resource (linear types)The same contracts, written in both languages. Notice what disappears.
pragma solidity ^0.8.20; contract Token { mapping(address => uint256) public balances; uint256 public totalSupply; event Transfer( address indexed from, address indexed to, uint256 amount ); function transfer( address to, uint256 amount ) external { require( balances[msg.sender] >= amount, "insufficient" ); balances[msg.sender] -= amount; balances[to] += amount; emit Transfer( msg.sender, to, amount ); } }
contract Token { version: 1 contention: per<address> state { total_supply: u256, balances: Map<address, u256>, } invariant conservation: self.total_supply == self.balances.values().sum() 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 }; } }
mutates: clauses that prevent
undeclared side effects. Reentrancy? Not possible — there's no callback mechanism.
contract Ownable { address public owner; mapping(address => bool) admins; modifier onlyOwner() { require( msg.sender == owner, "not owner" ); _; } modifier onlyAdmin() { require( admins[msg.sender], "not admin" ); _; } function addAdmin( address a ) external onlyOwner { admins[a] = true; } function doThing() external onlyAdmin { // admin logic } }
contract Managed { version: 1 contention: global capability Owner; capability Admin; state { admin_set: Set<address>, } operation add_admin( a: address ) requires: caller has Owner mutates: self.admin_set { self.admin_set.insert(a); } operation do_thing() requires: caller has Admin { // admin logic } }
Owner can't even attempt to call add_admin —
the transaction is rejected before execution.
import "ReentrancyGuard.sol"; contract Vault is ReentrancyGuard { mapping(address => uint256) deposits; function withdraw( uint256 amount ) external nonReentrant { require( deposits[msg.sender] >= amount ); // CEI pattern (easy to forget) deposits[msg.sender] -= amount; // external call — reentrancy // risk if guard is missing (bool ok, ) = msg.sender .call{value: amount}(""); require(ok); } }
contract Vault { version: 1 contention: per<address> state { deposits: Map<address, u256>, } operation withdraw( amount: u256 ) requires: self.deposits[caller] >= amount mutates: self.deposits[caller] { self.deposits[caller] -= amount; send(amount, caller); } // no callbacks exist. // reentrancy is a syntax error. // no guard needed. }
fallback(), no receive(),
no way for a called contract to call you back mid-execution.
is keyword. Contracts are self-contained.delegatecall, no proxy patterns.requires: clauses and capabilities.mutates: clause prevents silent side effects.Paste Solidity or pick a template. See the Cudo equivalent instantly. This is a pattern-based translation — it won't handle every edge case, but it'll get you 80% there.
// paste Solidity or pick a template to see the Cudo equivalent
Moving a Solidity codebase to Cudo, step by step.
mapping and state variables into a single state { } block.function to operation. Add mutates: clauses listing every field each operation writes.view/pure functions to view.modifier + require() with requires: clauses.onlyOwner) with capability declarations and has constraints.contention: model. If users operate on independent state, use per<K>.invariant declarations for any conservation laws or bounds you previously tested for.resource types instead.cudo check and fix compiler errors. Each one is a bug that existed silently in your Solidity.