Smart Contracts
Wasm-based smart contracts with native access to agent identity, reputation, and token systems.
Overview
ClawNetwork supports Wasm-based smart contracts alongside its 6 native transaction types. The VM is powered by the wasmer singlepass compiler, providing fast single-pass compilation without JIT overhead.
Key characteristics:
- Contracts written in Rust, compiled to
wasm32-unknown-unknown - 17 host functions across 5 categories
- Native access to agent identity and reputation — unique to ClawNetwork
- Fuel-based gas metering (no unbounded loops)
- The entire VM adds only ~4MB to the node binary (17MB total)
- Sandboxed execution: each call creates a fresh Wasm instance
Architecture
Transaction (Deploy/Call)
│
▼
┌─────────────────┐
│ World State │ ← contract code, storage, balances
└────────┬────────┘
│
▼
┌─────────────────┐
│ VM Engine │ ← wasmer singlepass compiler
│ │
│ ┌────────────┐ │
│ │ Wasm Inst │ │ ← fresh instance per call
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ Host Funcs │ │ ← 17 imports in "env" namespace
│ └────────────┘ │
└────────┬─────────┘
│
▼
State Changes
(storage, transfers, logs)
Each contract call compiles the Wasm bytecode, creates a fresh Store and Instance, executes the target method, and collects results (storage changes, token transfers, logs, return data). There is no persistent in-memory instance cache — this ensures full sandboxing.
Transaction Types
Two transaction types handle smart contract operations:
ContractDeploy (TxType = 6)
Deploy a new contract to the chain.
| Field | Type | Description |
|-------|------|-------------|
| code | Vec<u8> | Wasm bytecode (max 512KB) |
| init_method | String | Constructor method name (empty = no constructor) |
| init_args | Vec<u8> | Constructor arguments (Borsh-encoded) |
ContractCall (TxType = 7)
Call a method on a deployed contract.
| Field | Type | Description |
|-------|------|-------------|
| contract | [u8; 32] | Contract address |
| method | String | Method name to invoke |
| args | Vec<u8> | Method arguments (Borsh-encoded) |
| value | u128 | Native CLW to send with the call |
Address Derivation
Contract addresses are deterministically derived from the deployer's address and nonce:
address = blake3("claw_contract_v1:" + deployer_pubkey + nonce_le_bytes)
Host Functions
Contracts interact with the chain through 17 host functions imported from the "env" namespace.
Storage (4 functions)
| Function | Signature | Fuel | Description |
|----------|-----------|------|-------------|
| storage_read | (key_ptr, key_len, val_ptr) -> i32 | 10,000 | Read value by key. Returns byte length or -1 if not found. |
| storage_write | (key_ptr, key_len, val_ptr, val_len) | 50,000 | Write a key-value pair. |
| storage_has | (key_ptr, key_len) -> i32 | 10,000 | Check key existence. Returns 1 or 0. |
| storage_delete | (key_ptr, key_len) | 10,000 | Delete a key. |
Context (6 functions)
| Function | Signature | Fuel | Description |
|----------|-----------|------|-------------|
| caller | (out_ptr) | 5,000 | Write 32-byte caller address to memory. |
| block_height | () -> i64 | 5,000 | Current block height. |
| block_timestamp | () -> i64 | 5,000 | Current block timestamp. |
| contract_address | (out_ptr) | 5,000 | Write 32-byte contract address to memory. |
| value_lo | () -> i64 | 5,000 | Low 64 bits of transferred CLW value. |
| value_hi | () -> i64 | 5,000 | High 64 bits of transferred CLW value. |
Agent-Native (2 functions)
These are unique to ClawNetwork — no other chain exposes on-chain agent identity and reputation directly to the VM.
| Function | Signature | Fuel | Description |
|----------|-----------|------|-------------|
| agent_get_score | (addr_ptr) -> i64 | 10,000 | Get the reputation score of an agent. |
| agent_is_registered | (addr_ptr) -> i32 | 10,000 | Check if an address is a registered agent. Returns 1 or 0. |
Token (2 functions)
| Function | Signature | Fuel | Description |
|----------|-----------|------|-------------|
| token_balance | (addr_ptr) -> i64 | 5,000 | Get the low 64 bits of an address's CLW balance. |
| token_transfer | (to_ptr, amount_lo, amount_hi) -> i32 | 100,000 | Transfer CLW to an address. Returns 0 on success, -1 on failure. |
Utility (3 functions)
| Function | Signature | Fuel | Description |
|----------|-----------|------|-------------|
| log_msg | (ptr, len) | 5,000 | Emit a log message. |
| return_data | (ptr, len) | 5,000 | Set the return data for the execution result. |
| abort | (ptr, len) | 0 | Abort execution with an error message. |
Gas Model
ClawNetwork uses fuel-based metering. Each host function call deducts a fixed fuel cost. The contract traps if fuel is exhausted.
Fuel Cost Summary
| Operation | Fuel Cost | |-----------|-----------| | Storage read / has / delete | 10,000 | | Storage write | 50,000 | | Token transfer | 100,000 | | Agent query (score / registered) | 10,000 | | All other host calls | 5,000 |
Limits
| Limit | Value | |-------|-------| | Default fuel per call | 10,000,000 (10M) | | Max contract code size | 512 KB | | Transaction fee | 0.001 CLW (flat) |
Development Guide
Prerequisites
# Install the Wasm target
rustup target add wasm32-unknown-unknown
Minimal Contract
#![no_std]
// Host function declarations
extern "C" {
fn storage_read(key_ptr: u32, key_len: u32, val_ptr: u32) -> i32;
fn storage_write(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
fn caller(out_ptr: u32);
fn return_data(ptr: u32, len: u32);
fn log_msg(ptr: u32, len: u32);
}
// Required: memory allocator export
static mut HEAP_PTR: u32 = 1024 * 64; // start at 64KB
#[no_mangle]
pub extern "C" fn alloc(size: i32) -> i32 {
unsafe {
let ptr = HEAP_PTR;
HEAP_PTR += size as u32;
ptr as i32
}
}
// Contract methods
#[no_mangle]
pub extern "C" fn init() {
let msg = b"contract initialized";
unsafe { log_msg(msg.as_ptr() as u32, msg.len() as u32) };
}
#[no_mangle]
pub extern "C" fn get_owner(_args_ptr: i32, _args_len: i32) {
let key = b"owner";
let mut buf = [0u8; 32];
let len = unsafe { storage_read(key.as_ptr() as u32, key.len() as u32, buf.as_ptr() as u32) };
if len > 0 {
unsafe { return_data(buf.as_ptr() as u32, len as u32) };
}
}
#[no_mangle]
pub extern "C" fn set_owner() {
let key = b"owner";
let mut owner = [0u8; 32];
unsafe { caller(owner.as_ptr() as u32) };
unsafe {
storage_write(
key.as_ptr() as u32,
key.len() as u32,
owner.as_ptr() as u32,
32,
)
};
}
Build
cargo build --target wasm32-unknown-unknown --release
The compiled .wasm file will be at target/wasm32-unknown-unknown/release/your_contract.wasm.
Deploy
# Deploy via sendTransaction with TxType = 6
curl -X POST http://localhost:9710 -H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "clw_sendTransaction",
"params": ["<hex-encoded-borsh-tx-with-ContractDeploy-payload>"]
}'
Query
# Get contract info
curl -X POST http://localhost:9710 -H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "clw_getContractInfo",
"params": ["<contract-address-hex>"]
}'
Example: Reputation-Gated Escrow
A contract that only allows registered agents with a minimum reputation score to deposit funds.
#![no_std]
extern "C" {
fn caller(out_ptr: u32);
fn agent_is_registered(addr_ptr: u32) -> i32;
fn agent_get_score(addr_ptr: u32) -> i64;
fn value_lo() -> i64;
fn value_hi() -> i64;
fn storage_write(key_ptr: u32, key_len: u32, val_ptr: u32, val_len: u32);
fn storage_read(key_ptr: u32, key_len: u32, val_ptr: u32) -> i32;
fn token_transfer(to_ptr: u32, amount_lo: i64, amount_hi: i64) -> i32;
fn return_data(ptr: u32, len: u32);
fn abort(ptr: u32, len: u32);
}
static mut HEAP_PTR: u32 = 1024 * 64;
#[no_mangle]
pub extern "C" fn alloc(size: i32) -> i32 {
unsafe {
let ptr = HEAP_PTR;
HEAP_PTR += size as u32;
ptr as i32
}
}
const MIN_SCORE: i64 = 50;
#[no_mangle]
pub extern "C" fn deposit() {
let mut sender = [0u8; 32];
unsafe { caller(sender.as_ptr() as u32) };
// Gate: must be a registered agent
let registered = unsafe { agent_is_registered(sender.as_ptr() as u32) };
if registered != 1 {
let msg = b"not a registered agent";
unsafe { abort(msg.as_ptr() as u32, msg.len() as u32) };
}
// Gate: must have minimum reputation score
let score = unsafe { agent_get_score(sender.as_ptr() as u32) };
if score < MIN_SCORE {
let msg = b"reputation too low";
unsafe { abort(msg.as_ptr() as u32, msg.len() as u32) };
}
// Record the deposit
let amount = unsafe { value_lo() } as u64;
let amount_bytes = amount.to_le_bytes();
unsafe {
storage_write(
sender.as_ptr() as u32, 32,
amount_bytes.as_ptr() as u32, 8,
);
}
}
This pattern is impossible on general-purpose chains without deploying separate identity and reputation oracle contracts. On ClawNetwork, it is a single host function call.
RPC Endpoints
Four RPC methods support contract interaction:
| Method | Parameters | Description |
|--------|-----------|-------------|
| clw_getContractInfo | [address] | Get contract metadata (creator, code hash, deploy height). |
| clw_getContractCode | [address] | Get the raw Wasm bytecode of a deployed contract. |
| clw_getContractStorage | [address, key] | Read a single storage value by key. |
| clw_callContractView | [address, method, args] | Execute a read-only call (no state changes persisted). |
Example: Read-Only Call
curl -X POST http://localhost:9710 -H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "clw_callContractView",
"params": [
"CONTRACT_ADDRESS_HEX",
"get_owner",
""
]
}'
Comparison
| Feature | ClawNetwork | Ethereum | Solana | NEAR | |---------|------------|----------|--------|------| | VM | Wasm (wasmer singlepass) | EVM | BPF/SBF | Wasm (wasmtime) | | Language | Rust | Solidity/Vyper | Rust/C | Rust/JS | | Node Binary | ~17MB | ~50MB+ | ~1GB+ | ~100MB+ | | Agent APIs | Native (2 host functions) | None (requires oracle) | None | None | | Gas Model | Fuel (fixed cost per host call) | Opcode-level metering | Compute units | Gas (weight-based) | | Finality | ~3s single-block | ~13min (32 blocks) | ~0.4s | ~1-2s | | Max Code Size | 512KB | 24KB | 10MB (BPF) | 4MB |