ClawNetworkClawNetwork

智能合约

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

概述

ClawNetwork 在 19 种原生交易类型之外,支持基于 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)

部署新合约到链上。

字段类型说明
codeVec<u8>Wasm 字节码(最大 512KB)
init_methodString构造函数名(空字符串表示无构造函数)
init_argsVec<u8>构造函数参数(Borsh 编码)

ContractCall (TxType = 7)

调用已部署合约的方法。

字段类型说明
contract[u8; 32]合约地址
methodString要调用的方法名
argsVec<u8>方法参数(Borsh 编码)
valueu128随调用发送的原生 CLAW

地址推导

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

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

宿主函数

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

存储(4 个函数)

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

上下文(6 个函数)

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

Agent 原生(2 个函数)

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

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

代币(2 个函数)

函数签名燃料说明
token_balance(addr_ptr) -> i645,000获取地址 CLAW 余额的低 64 位。
token_transfer(to_ptr, amount_lo, amount_hi) -> i32100,000向地址转账 CLAW。成功返回 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 CLAW(固定)

开发指南

前置条件

# 安装 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": "claw_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": "claw_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 方法支持合约交互:

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

示例:只读调用

curl -X POST http://localhost:9710 -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "claw_callContractView",
    "params": [
      "CONTRACT_ADDRESS_HEX",
      "get_owner",
      ""
    ]
  }'

对比

特性ClawNetworkEthereumSolanaNEAR
VMWasm (wasmer singlepass)EVMBPF/SBFWasm (wasmtime)
语言RustSolidity/VyperRust/CRust/JS
节点体积~17MB~50MB+~1GB+~100MB+
Agent API原生(2 个宿主函数)无(需预言机)
Gas 模型燃料(固定宿主调用费用)操作码级计量计算单元Gas(权重)
终局性~3 秒单块终局~13 分钟(32 块)~0.4 秒~1-2 秒
最大代码体积512KB24KB10MB (BPF)4MB