Skip to content

Quickstart (10 minutes)

In this quickstart you write a small module with persistent state, and HiveKit compiles it to Hive Bytecode (a .hbc file) that NDSR executes deterministically. You run it on your machine with the same runtime the network uses, then deploy it to the testnet and call it through validator consensus. Pick your language once; every tab on this page follows your choice.

The outputs below were recorded by running exactly these commands with ndsr 1.0.0 and the HiveKit SDKs from the deployed testnet branch. Module addresses depend on your source code and SDK version, so yours may differ; gas and outputs for the same artifact are identical on every machine.

Terminal window
rustup target add wasm32-unknown-unknown
cargo install --path necter-sdk/hivekit-rs # installs the `hivec` CLI

Each SDK ships its own hivec; install the one for your language. hivec run and hivec inspect call ndsr (found through $NDSR_BIN, ndsr on your PATH, or tools/ndsr in a parent directory).

A module exports functions. Each takes the call input (usually JSON) and returns a string. This one adds two numbers and keeps a counter in persistent storage, emitting an event on every increment.

Terminal window
cargo new --lib my_module && cd my_module
Cargo.toml
[package]
name = "my_module"
version = "0.1.0"
edition = "2021"
[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
src/lib.rs
use hivekit::prelude::*;
/// Exported as "addNumbers" (camelCase of the identifier).
#[hive_export]
fn add_numbers(input: Value) -> Result<Value, String> {
let a = input["a"].as_i64().ok_or("`a` must be an integer")?;
let b = input["b"].as_i64().ok_or("`b` must be an integer")?;
Ok(json!({ "total": a + b }))
}
/// Exported under an explicit name.
#[hive_export("increment")]
fn bump(input: Value) -> Result<Value, String> {
let by = input["by"].as_i64().unwrap_or(1);
let n = storage::get_json::<i64>("n").unwrap_or(0) + by;
storage::set_json("n", &n);
emit("incremented", &json!({ "value": n }));
Ok(json!({ "value": n }))
}
// Every exported function, once per crate.
hive_module!(add_numbers, bump);
Terminal window
hivec build # cargo build --target wasm32-unknown-unknown --release, ABI check, package
hivec inspect dist/my_module.hbc
file: dist/my_module.hbc
manifest_address: 0x9cf5a3ab9caa72f518a1366b913a6c48d75b9e5b07e421a6e445c377aabe6127 (verified)
name: my_module
language: rust
compiler: hivec-rs/0.2.0
runtime: hive-wasm-v1
module.wasm: 108624 bytes
imports: storage.get, storage.set, hive.abort, hive.emit
functions:
0 addNumbers
1 increment
abi: hive-wasm-v1 ok

functions is always sorted: a function’s id is its index in that list, never the order you defined them.

ndsr run executes one function exactly like a node does and prints the signed receipt. With --data-dir the module’s state persists between runs.

Terminal window
ndsr run dist/my_module.hbc addNumbers --input '{"a":2,"b":3}'
hivec run dist/my_module.hbc increment '{"by":2}' --data-dir .node
hivec run dist/my_module.hbc increment '{"by":2}' --data-dir .node
Call output gas_used events
addNumbers {"a":2,"b":3} {"total":5} 10 570 none
increment {"by":2} {"value":2} 18 671 incremented {"value":2}
increment {"by":2} again {"value":4} 19 635 incremented {"value":4}

Every node that runs the same call on the same state gets the same receipt_hash. That is what lets validators and miners agree. Receipts

  1. Sign in. Open testnet.necter.network and connect your wallet. Signing the Sign-In with Ethereum message costs nothing.

  2. Upload the module. Go to Develop → Create, open the Work step and choose Upload .hbc. The Hub recomputes the content address and rejects any mismatch. (From a script, POST /v1/modules with {"hbc_b64": "…"} and your session token or an API key does the same; see Upload and call a module.)

  3. Call it through validator consensus. No sign-in is needed to call a deployed module:

    Terminal window
    curl -s -X POST https://testnet-rpc.necter.network/v1/execute \
    -H 'content-type: application/json' \
    -d '{"module":"0x<your manifest_address>","function":"increment","input":{"by":2},"gas_limit":50000000}'

    The Hub sends the call to every validator and answers once a quorum (3 of 4) agree on the receipt hash:

    {
    "success": true,
    "output": "{\"value\":2}",
    "receipt_hash": "0x…",
    "gas_used": 18671,
    "events": [{ "name": "incremented", "data": { "value": 2 } }],
    "round_id": "exec:0x…",
    "agreeing": 3,
    "quorum": 3,
    "consensus": { "state": "finalized", "validators": 4, "quorum": 3 }
    }

    State changes made this way are kept by the validators, so the next increment returns 4.

  4. Find it in the store. Paste your module address into the search box of the store’s Explorer.

Calling a module through /v1/execute runs it on the validators. To have miners run your work and get paid for it, you publish a project: a stateless worker module plus a signed manifest, a vault and a task source. That is the next step: