Skip to content

Rust

hivekit-rs compiles a cdylib crate for wasm32-unknown-unknown and packages it as a .hbc. Rust gives the smallest gas for real work and access to no_std-friendly crates.

Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"] # rlib lets `cargo test` use the module natively
[dependencies]
hivekit = { path = "../necter-sdk/hivekit-rs" }
serde = { version = "1", features = ["derive"] }
[profile.release]
opt-level = "s"
lto = true
codegen-units = 1
panic = "abort"
strip = true
use hivekit::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct Submit { pair: String, price: u64 }
#[derive(Serialize, Deserialize)]
struct Quote { price: u64, updates: u64 }
/// Exported as "submit". Input is parsed from JSON into `Submit`; invalid input fails the call.
#[hive_export]
fn submit(input: Submit) -> Result<Json<Quote>, String> {
let key = format!("price:{}", input.pair);
let prev: Option<Quote> = storage::get_json(&key);
let q = Quote { price: input.price, updates: prev.map_or(0, |p| p.updates) + 1 };
storage::set_json(&key, &q);
emit("price_updated", &json!({ "pair": input.pair, "price": q.price }));
Ok(Json(q))
}
/// Exported under an explicit name.
#[hive_export("latestPrice")]
fn price(input: Value) -> Result<Value, String> {
let pair = input["pair"].as_str().ok_or("`pair` is required")?;
let q: Quote = storage::get_json(&format!("price:{pair}")).ok_or("no price")?;
Ok(json!({ "pair": pair, "price": q.price }))
}
hive_module!(submit, price); // every exported function, once per crate
  • #[hive_export] accepts fn(T) -> R or fn() -> R. T is any DeserializeOwned type (empty input is null). The export name is the camelCase of the identifier (add_numbers → addNumbers) unless you give one.
  • R may be serde_json::Value, hivekit::Json<impl Serialize>, String / &'static str (returned as raw text), () (empty output), or Result<any of those, impl Display>. Err aborts the call with the message.
  • hive_module! sorts the exports by name at compile time (duplicates are a compile error), writes them into a hivekit.functions custom section that hivec build reads for the manifest, and generates pub static HIVE_MODULE.
  • Panics are reported as an abort with the panic message. A failed call commits nothing.
Function Host import Notes
storage::get / get_string / get_json storage.get None if absent
storage::set / set_json storage.set key 1–256 bytes, value ≤ 64 KiB; empty value deletes
storage::del storage.del
emit(name, &data) hive.emit no floats; integers within ±(2^53−1)
call(addr, fn, &[u8]), call_json(addr, fn, &Value) hive.call Err(CallError); e.code() gives −1, −2, −3, −5 or −6
hash(&[u8]) crypto.hash Keccak-256 as 0x…
log(&str) console.log debug only
abort(&str) -> ! hive.abort fails the call

The compiled module imports only the functions you use, so a module without storage::* and call* is stateless and can be a committee worker.

Any crate that compiles for wasm32-unknown-unknown without WASI, clocks or randomness works. Two that are used in the use cases and verified under NDSR:

  • k256 (default-features = false, features = ["ecdsa"]): secp256k1 / EIP-191 signature recovery, about 9.6 M gas per recovery. Attestation
  • ed25519-dalek (default-features = false): ed25519 verification, about 2.5 M gas per signature. Telemetry
Terminal window
hivec build # current crate → dist/<name>.hbc
hivec build --example counter # build a cargo example instead of the library
hivec build path/to/module.wasm --functions a,b # package a wasm you built yourself
hivec inspect dist/my_module.hbc # verify like NDSR's loader; --json for machine output
hivec functions dist/my_module.hbc # func_id → name
hivec run dist/my_module.hbc increment '{"by":2}' --data-dir .node
hivec run dist/counter.hbc callAdd '{"module":"0x…","a":1,"b":2}' --module dist/math_module.hbc

hivec run has no interpreter of its own: it calls ndsr run, so results are exactly what a node produces. --module places another artifact in <data-dir>/modules/<address>.hbc so hive.call can reach it. Full flags: hivec CLI.

hivekit-rs/examples/: math_module (pure functions, crypto.hash, a storage-writing record), counter (storage, events, logging, a failing function, hive.call into math_module), price_oracle (typed input and Json<T> output). Built from source they reproduce the addresses deployed on the testnet: math_module = 0x87597a6f0bff260f27026945649ad6889c71848b2a2e47e8ba6daf5cde4cdad3, counter = 0xd67ffc0300427c9c5d391f31ad4379717d749a33b2e3459d9c44b89d7fbd80f3.

Testing: Testing modules.