Skip to main content
The nexus-sdk crate (crates/sdk/) serves two completely different purposes selected by feature flags:
ModeFeatureUse case
Contractcontract (default off)Write WASM smart contracts (#![no_std])
Clientclient (default off)Call NEXUS nodes from a Rust application
TestingtestingTest harness for contract unit tests
The SDK currently targets Zcash as the primary chain. Contract execution, state, and gas are all denominated in zatoshi (vZEC on NEXUS).

Contract Mode (no_std)

Used inside smart contracts compiled to WASM. No std, no networking, no heap allocator beyond what the SDK provides. Cargo.toml:
[dependencies]
nexus-sdk = { path = "vendor/nexus-sdk", default-features = false, features = ["contract"] }

[profile.release]
opt-level = "s"
lto = true
codegen-units = 1
strip = true
panic = "abort"
Writing a contract:
#![no_std]
extern crate alloc;
use alloc::string::String;
use nexus_sdk::{
    solidity::{*, SafeMath},
    contract_api::{ez::prelude::*, ez::ret},
    require,
};
use nexus_sdk::solidity::uint256 as U256;

// Persistent storage — survives between calls
pub static BALANCES: Mapping<Address, U256> = Mapping::new(b"bal");
pub static OWNER:    Mapping<&[u8], Address> = Mapping::new(b"owner");

nexus_fn! {
    fn transfer(to: Address, amount: U256) {
        let _guard = ReentrancyGuard::new();  // Prevents reentrancy
        let sender = Blockchain::msg.sender();

        let bal = BALANCES.get(&sender);
        require!(bal >= amount, "Insufficient balance");

        BALANCES.set(&sender, bal - amount.clone());
        BALANCES.set(&to, BALANCES.get(&to) + amount);

        emit("Transfer", &[]);
        ret::u32(1)
    }
}

Contract Primitives

PrimitiveDescription
Mapping<K, V>Persistent key-value storage
DoubleMapping<K1, K2, V>Two-key mapping (allowances, etc.)
nexus_fn! { fn name(args) { ... } }Expose a function as a contract ABI entry point
Blockchain::msg.sender()Address of the caller
Blockchain::block.height()Current NEXUS block height
Blockchain::block.timestamp()Current block timestamp
ReentrancyGuard::new()Block reentrant calls (drop guard, auto-released)
require!(cond, msg)Revert with message if condition is false
emit(event, data)Emit an indexed event

Return Types

ReturnDescription
ret::u32(n)Return a u32
ret::u64(n)Return a u64
ret::u256(bytes)Return a 32-byte big number
ret::bytes(b)Return raw bytes (strings, addresses)
ret::address(addr)Return an address

Cross-Contract Calls

use nexus_sdk::contract_api::xcc;

nexus_fn! {
    fn call_other(token: Address, amount: U256) {
        // Call another contract
        xcc::call(&token.0, "transfer", &args_bytes);

        // Delegate call (run target code in our storage context)
        xcc::delegate_call(&impl_addr.0, "initialize", &[]);

        // Deploy a new contract from this contract
        let new_addr = xcc::deploy(CHILD_WASM, &init_args, &salt);
    }
}

U256 Arithmetic

NEXUS contracts use nexus_sdk::solidity::uint256 for 256-bit integers. Because the WASM target cannot use compiler intrinsics, all arithmetic must be done with the SDK’s manual implementations:
use nexus_sdk::solidity::uint256 as U256;

let a = U256::from(1000u64);
let b = U256::from(500u64);

// All operations are intrinsic-free
let sum  = u256_add(&a, &b);
let diff = u256_sub(&a, &b);
let lt   = u256_lt(&a, &b);   // true if a < b
let eq   = u256_eq(&a, &b);   // true if a == b

// Overflow-safe addition (returns None on overflow)
let safe = u256_checked_add(&a, &b);

Client Mode

Used in off-chain Rust applications to interact with NEXUS nodes. Requires tokio and network access. Cargo.toml:
[dependencies]
nexus-sdk = { path = "../../crates/sdk", features = ["client"] }
tokio    = { version = "1", features = ["full"] }

NexusClient

use nexus_sdk::{NexusClient, ClientConfig};

let config = ClientConfig {
    rpc_url: "http://localhost:9945".to_string(),
    rpc_urls: vec![],        // optional fallbacks
    network: "regtest".to_string(),
    default_gas_limit: 2_000_000,
    timeout_ms: 30_000,
    max_requests_per_second: 100,
};

let client = NexusClient::new(config);

Key Methods

// Block info
let height = client.get_block_height().await?;

// Balances (in zatoshi / vZEC)
let balance = client.get_balance("tmAddr...").await?;

// Deploy a WASM contract
let contract_id = client.deploy_contract(
    contract,        // ContractBuilder output
    &deployer_pubkey,
    Some(&secret_key),
    0                // value
).await?;

// Call (read-only — no state change)
let result: u64 = client.call_contract(
    &contract_id, "get_count", &args, &caller, 0
).await?;

// Send (state-changing transaction)
let tx_id = client.send_contract_call_raw(
    &contract_id, "transfer", &args, &sender, Some(&secret_key), 0
).await?;

// Read contract storage directly
let value = client.get_state(&contract_id, b"my_key").await?;

// Query events
let events = client.query_events(
    Some(&contract_id), Some("Transfer"), Some(0), Some(1000)
).await?;

// Token balance (NEP-20)
let bal = client.get_token_balance(&token_id, "tmAddr...").await?;

ContractBuilder

use nexus_sdk::ContractBuilder;

let contract = ContractBuilder::new("my_token")
    .wasm_file("./nep20.wasm")
    .init_args_raw(init_bytes)
    .build()?;

Testing Mode

The testing feature enables the TestEnv harness for contract integration tests. Tests spawn a local NEXUS execution subprocess — no VM source code is embedded in your tests.
[dev-dependencies]
nexus-sdk = { path = "vendor/nexus-sdk", features = ["testing"] }
use nexus_sdk::testing::{TestEnv, TestArg, accounts};

const WASM: &[u8] = include_bytes!(
    "../../target/wasm32-unknown-unknown/release/my_contract.wasm"
);

#[test]
fn test_transfer() {
    let mut env = TestEnv::new();
    let token = env.deploy(WASM).expect("deploy failed");

    env.set_caller(accounts::ALICE);
    env.call(&token, "init_token", &TestEnv::encode_args(&[
        TestArg::String("MyToken"),
        TestArg::String("MTK"),
        TestArg::U64(18),
        TestArg::U256("1000000000000000000000000"),
    ])).unwrap().assert_success();

    env.call(&token, "transfer", &TestEnv::encode_args(&[
        TestArg::Address(accounts::BOB),
        TestArg::U256("1000000000000000000"),
    ])).unwrap().assert_success();

    let bal = env.call(&token, "balanceOf", &TestEnv::encode_args(&[
        TestArg::Address(accounts::BOB),
    ])).unwrap();
    assert_eq!(bal.as_u256_str(), "1000000000000000000");
}
Built-in test accounts:
ConstantDescription
accounts::ALICETest account A
accounts::BOBTest account B
accounts::CAROLTest account C
accounts::DEPLOYERDefault deployer account

Error Types

pub enum SdkError {
    NetworkError(String),
    SerializationError(String),
    ExecutionFailed(String),
    InvalidInput(String),
    ContractNotFound,
    InsufficientBalance,
    InvalidNonce,
}