ClawNetwork

智能合约

基于 Wasm 的智能合约,原生访问 Agent 身份、信誉和代币系统。

概述

ClawNetwork 在 6 种原生交易类型之外,支持基于 Wasm 的智能合约。VM 使用 wasmer singlepass 编译器,提供快速单遍编译,无 JIT 开销。

核心特性:

  • 使用 Rust 编写合约,编译到 wasm32-unknown-unknown
  • 17 个宿主函数,分为 5 大类
  • 原生访问 Agent 身份和信誉 — ClawNetwork 独有
  • 基于燃料(Fuel)的 Gas 计量(杜绝无限循环)
  • VM 仅为节点增加约 4MB 体积(总计 17MB)
  • 沙箱执行:每次调用创建全新 Wasm 实例

架构

交易 (Deploy/Call)
        │
        ▼
┌─────────────────┐
│   World State   │  ← 合约代码、存储、余额
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    VM Engine     │  ← wasmer singlepass 编译器
│                  │
│  ┌────────────┐  │
│  │  Wasm Inst │  │  ← 每次调用创建新实例
│  └─────┬──────┘  │
│        │         │
│  ┌─────▼──────┐  │
│  │ Host Funcs │  │  ← 17 个 "env" 命名空间导入
│  └────────────┘  │
└────────┬─────────┘
         │
         ▼
   状态变更
   (存储、转账、日志)

每次合约调用都会编译 Wasm 字节码,创建全新的 StoreInstance,执行目标方法,并收集结果(存储变更、代币转账、日志、返回数据)。不会在内存中缓存实例——确保完全沙箱隔离。

交易类型

两种交易类型用于智能合约操作:

ContractDeploy (TxType = 6)

部署新合约到链上。

| 字段 | 类型 | 说明 | |------|------|------| | code | Vec<u8> | Wasm 字节码(最大 512KB) | | init_method | String | 构造函数名(空字符串表示无构造函数) | | init_args | Vec<u8> | 构造函数参数(Borsh 编码) |

ContractCall (TxType = 7)

调用已部署合约的方法。

| 字段 | 类型 | 说明 | |------|------|------| | contract | [u8; 32] | 合约地址 | | method | String | 要调用的方法名 | | args | Vec<u8> | 方法参数(Borsh 编码) | | value | u128 | 随调用发送的原生 CLW |

地址推导

合约地址由部署者地址和 nonce 确定性推导:

address = blake3("claw_contract_v1:" + deployer_pubkey + nonce_le_bytes)

宿主函数

合约通过 17 个从 "env" 命名空间导入的宿主函数与链交互。

存储(4 个函数)

| 函数 | 签名 | 燃料 | 说明 | |------|------|------|------| | storage_read | (key_ptr, key_len, val_ptr) -> i32 | 10,000 | 按键读取值。返回字节长度,不存在返回 -1。 | | storage_write | (key_ptr, key_len, val_ptr, val_len) | 50,000 | 写入键值对。 | | storage_has | (key_ptr, key_len) -> i32 | 10,000 | 检查键是否存在。返回 1 或 0。 | | storage_delete | (key_ptr, key_len) | 10,000 | 删除键。 |

上下文(6 个函数)

| 函数 | 签名 | 燃料 | 说明 | |------|------|------|------| | caller | (out_ptr) | 5,000 | 将 32 字节调用者地址写入内存。 | | block_height | () -> i64 | 5,000 | 当前区块高度。 | | block_timestamp | () -> i64 | 5,000 | 当前区块时间戳。 | | contract_address | (out_ptr) | 5,000 | 将 32 字节合约地址写入内存。 | | value_lo | () -> i64 | 5,000 | 转入 CLW 值的低 64 位。 | | value_hi | () -> i64 | 5,000 | 转入 CLW 值的高 64 位。 |

Agent 原生(2 个函数)

这是 ClawNetwork 独有的特性 — 没有其他链能让 VM 直接访问链上 Agent 身份和信誉。

| 函数 | 签名 | 燃料 | 说明 | |------|------|------|------| | agent_get_score | (addr_ptr) -> i64 | 10,000 | 获取 Agent 的信誉分。 | | agent_is_registered | (addr_ptr) -> i32 | 10,000 | 检查地址是否为已注册 Agent。返回 1 或 0。 |

代币(2 个函数)

| 函数 | 签名 | 燃料 | 说明 | |------|------|------|------| | token_balance | (addr_ptr) -> i64 | 5,000 | 获取地址 CLW 余额的低 64 位。 | | token_transfer | (to_ptr, amount_lo, amount_hi) -> i32 | 100,000 | 向地址转账 CLW。成功返回 0,失败返回 -1。 |

工具(3 个函数)

| 函数 | 签名 | 燃料 | 说明 | |------|------|------|------| | log_msg | (ptr, len) | 5,000 | 输出日志消息。 | | return_data | (ptr, len) | 5,000 | 设置执行结果的返回数据。 | | abort | (ptr, len) | 0 | 终止执行并输出错误消息。 |

Gas 模型

ClawNetwork 使用基于燃料的计量。每次宿主函数调用扣除固定燃料费用,燃料耗尽时合约触发陷阱(trap)。

燃料费用汇总

| 操作 | 燃料费用 | |------|---------| | 存储读取 / 存在检查 / 删除 | 10,000 | | 存储写入 | 50,000 | | 代币转账 | 100,000 | | Agent 查询(信誉分 / 注册状态) | 10,000 | | 其他所有宿主调用 | 5,000 |

限制

| 限制项 | 值 | |--------|-----| | 每次调用默认燃料 | 10,000,000 (10M) | | 最大合约代码体积 | 512 KB | | 交易手续费 | 0.001 CLW(固定) |

开发指南

前置条件

# 安装 Wasm 编译目标
rustup target add wasm32-unknown-unknown

最小合约示例

#![no_std]

// 宿主函数声明
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);
}

// 必须导出:内存分配器
static mut HEAP_PTR: u32 = 1024 * 64; // 从 64KB 开始

#[no_mangle]
pub extern "C" fn alloc(size: i32) -> i32 {
    unsafe {
        let ptr = HEAP_PTR;
        HEAP_PTR += size as u32;
        ptr as i32
    }
}

// 合约方法
#[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,
        )
    };
}

编译

cargo build --target wasm32-unknown-unknown --release

编译后的 .wasm 文件位于 target/wasm32-unknown-unknown/release/your_contract.wasm

部署

# 通过 sendTransaction 部署,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>"]
  }'

查询

# 获取合约信息
curl -X POST http://localhost:9710 -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "clw_getContractInfo",
    "params": ["<contract-address-hex>"]
  }'

示例:信誉门控托管合约

一个仅允许已注册且信誉分达标的 Agent 存入资金的合约。

#![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) };

    // 门控:必须是已注册 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) };
    }

    // 门控:信誉分必须达标
    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) };
    }

    // 记录存款
    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,
        );
    }
}

这种模式在通用链上不可能实现,除非额外部署身份和信誉预言机合约。在 ClawNetwork 上,只需一次宿主函数调用。

RPC 端点

四个 RPC 方法支持合约交互:

| 方法 | 参数 | 说明 | |------|------|------| | clw_getContractInfo | [address] | 获取合约元数据(创建者、代码哈希、部署高度)。 | | clw_getContractCode | [address] | 获取已部署合约的原始 Wasm 字节码。 | | clw_getContractStorage | [address, key] | 按键读取单个存储值。 | | clw_callContractView | [address, method, args] | 执行只读调用(不持久化状态变更)。 |

示例:只读调用

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",
      ""
    ]
  }'

对比

| 特性 | ClawNetwork | Ethereum | Solana | NEAR | |------|------------|----------|--------|------| | VM | Wasm (wasmer singlepass) | EVM | BPF/SBF | Wasm (wasmtime) | | 语言 | Rust | Solidity/Vyper | Rust/C | Rust/JS | | 节点体积 | ~17MB | ~50MB+ | ~1GB+ | ~100MB+ | | Agent API | 原生(2 个宿主函数) | 无(需预言机) | 无 | 无 | | Gas 模型 | 燃料(固定宿主调用费用) | 操作码级计量 | 计算单元 | Gas(权重) | | 终局性 | ~3 秒单块终局 | ~13 分钟(32 块) | ~0.4 秒 | ~1-2 秒 | | 最大代码体积 | 512KB | 24KB | 10MB (BPF) | 4MB |