Citizen Smart Contracts¶
Citizen Protocol smart contracts are programs written in Rust that compile to WebAssembly (WASM) and execute inside a sandboxed runtime on every validator node. They have full access to on-chain state, signature verification, governance approval checks, and cross-contract composition.
Why a new smart contract platform?¶
Most blockchain smart contract platforms force you to learn a domain-specific language. Citizen takes a different approach: you write in Rust with a SDK that provides Solidity-like ergonomics — typed storage, modifiers, events, and automatic dispatch — but compiles to deterministic WASM that runs in a gas-metered sandbox.
| Feature | Solidity (EVM) | Citizen Contracts (WASM) |
|---|---|---|
| Language | Solidity | Rust |
| Runtime | EVM | wasmtime (WASM) |
| Storage | Key-value slots | Typed Item<T> and Map<K,V> |
| Auth | msg.sender |
ctx.require_signature(), ctx.require_governance() |
| Events | emit Event() |
ctx.emit("Event", &json!({...})) |
| Cross-contract | Contract.foo() |
call_module("contract", params) |
| State model | Per-address storage | Per-module scoped state |
| Governance | External (no native) | Built-in host_check_governance_approval |
| Block context | block.number, block.timestamp |
ctx.block_height() |
How contracts work¶
┌─────────────────────────────────────────────────────────┐
│ Your Contract (Rust source) │
│ ├── #[contract] mod contract { pub fn op(ctx) ... } │
│ └── Compiles to WASM │
└──────────────────────┬──────────────────────────────────┘
│ POST /api/v1/modules/:id/execute
▼
┌─────────────────────────────────────────────────────────┐
│ Citizen Node (wasmtime sandbox) │
│ ├── Parses JSON params → Context │
│ ├── Dispatches to function by name │
│ ├── Gas-metered execution │
│ ├── Host functions bridge to ledger/storage/crypto │
│ └── Returns JSON response │
└─────────────────────────────────────────────────────────┘
Quick example¶
#![no_std]
#![no_main]
extern crate alloc;
#[global_allocator]
static ALLOC: citizen_contract_sdk::Allocator = citizen_contract_sdk::Allocator;
use citizen_contract_sdk::prelude::*;
use citizen_contract_derive::contract;
#[contract]
mod contract {
use super::*;
pub fn increment(ctx: Context) -> Result<Response> {
let count = ctx.storage().item::<u64>("count");
let new_val = count.update(|v| v + 1)?;
ctx.emit("Incremented", &json!({"value": new_val}));
Ok(Response::data(json!({"count": new_val})))
}
pub fn get_count(ctx: Context) -> Result<Response> {
let count: u64 = ctx.storage().item::<u64>("count").load()?.unwrap_or(0);
Ok(Response::data(json!({"count": count})))
}
}
What you'll learn¶
This documentation covers everything from setup to advanced patterns:
- Installation — Toolchain prerequisites and project setup
- Hello World — Build, deploy, and invoke your first contract
- Storage — Typed state variables and mappings
- Authorization — Signature verification and governance gates
- Events — Emit structured events for indexing
- Cross-Contract Calls — Compose multiple contracts
- Deployment — Register and activate contracts on-chain
- Examples & Exercises — Hands-on practice contracts
Design principles¶
- Governance-first: Contracts integrate natively with Citizen's governance namespace — no external oracle needed for approval checks.
- Deterministic: All host functions return the same result on every validator given the same state. No floating-point, no I/O, no randomness.
- Sandboxed: Contracts cannot access the filesystem, network, or host memory outside their allocated region.
- Gas-metered: Every instruction consumes gas; execution halts at the limit.
- Typed: serde serialization eliminates manual JSON parsing — the SDK handles it.
Comparison: SDK vs raw WASM¶
| Raw WASM | SDK | |
|---|---|---|
| Lines of code (admin-registry) | 729 | 247 |
| JSON parsing | Hand-rolled parser | ctx.param::<T>() |
| State access | Manual buffer management | storage.map::<K,V>() |
| Memory management | Manual alloc() |
Automatic global allocator |
| Error handling | Manual JSON error strings | Result<T> with ? operator |