The nexus-sdk crate (crates/sdk/) serves two completely different purposes selected by feature flags:
| Mode | Feature | Use case |
|---|
| Contract | contract (default off) | Write WASM smart contracts (#![no_std]) |
| Client | client (default off) | Call NEXUS nodes from a Rust application |
| Testing | testing | Test 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
| Primitive | Description |
|---|
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
| Return | Description |
|---|
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:
| Constant | Description |
|---|
accounts::ALICE | Test account A |
accounts::BOB | Test account B |
accounts::CAROL | Test account C |
accounts::DEPLOYER | Default deployer account |
Error Types
pub enum SdkError {
NetworkError(String),
SerializationError(String),
ExecutionFailed(String),
InvalidInput(String),
ContractNotFound,
InsufficientBalance,
InvalidNonce,
}