Cheatsheet
Boilerplate
#![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 my_op(ctx: Context) -> Result<Response> {
// ...
Ok(Response::success())
}
}
Build
RUSTFLAGS="-C link-arg=--allow-undefined" cargo build --release --target wasm32-unknown-unknown
Execute
curl -X POST http://localhost:8091/api/v1/modules/MODULE_ID/execute \
-H "Content-Type: application/json" \
-d '{"operation": "my_op", "key": "value"}'
Parameters
let did: String = ctx.param("did")?; // Required
let role: String = ctx.param_or("role", "admin".into()); // Default
let expiry: Option<u64> = ctx.param_opt("expiry")?; // Optional
let raw: Option<&Value> = ctx.raw_param("data"); // Raw JSON
Storage
let s = ctx.storage();
// Raw
s.set_str("k", "v")?; s.get_str("k"); s.remove("k");
// Item<T>
let item = s.item::<u64>("count");
item.save(&42)?; item.load()?; item.exists();
item.update(|v| v + 1)?; item.remove();
// Map<K, V>
let map = s.map::<String, User>("user");
map.save(&k, &v)?; map.load(&k)?; map.exists(&k);
map.remove(&k);
Authorization
ctx.require_governance("proposal-id")?;
ctx.require_signature(did, message, sig_hex)?;
ctx.require_exists("admin:key")?;
Events
ctx.emit("EventName", &json!({"key": "value"}));
log_str("debug message");
Responses
Response::success() // {"ok":true}
Response::data(json!({...})) // Custom JSON
Response::data_str("hello") // {"ok":true,"data":"hello"}
Response::raw(r#"{"x":1}"#) // Raw JSON string
Errors
ContractError::msg("reason") // Generic
ContractError::Unauthorized("reason".into()) // Auth failure
ContractError::Param("reason".into()) // Bad input
ContractError::Storage("reason".into()) // Storage failure
Host functions
store_state("k", "v"); load_state("k")
verify_signature(did, &hex_encode(msg.as_bytes()), sig_hex)
verify_signature_batch(batch_json) // → u32 count
check_governance("proposal-id") // → bool
current_block_height() // → u64
read_namespace_entry("ns", "id") // → Option<String>
write_namespace_entry("ns", json) // → bool
call_module("module-id", json) // → Option<String>
log_str("message")
hex_encode(&bytes) // → String
Block context
let block = ctx.block_height();
Cargo.toml template
[package]
name = "my-contract"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
name = "my_contract"
[dependencies]
citizen-contract-sdk = { path = "path/to/crates/contract-sdk" }
citizen-contract-derive = { path = "path/to/crates/contract-derive" }
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
serde_json = { version = "1", default-features = false, features = ["alloc"] }
[profile.release]
opt-level = "s"
lto = true
strip = "none"
[workspace]