Skip to main content
The nexus-contract-template repository is the fastest way to start writing a NEXUS smart contract. It includes a production-ready Cargo workspace, the nexus-sdk vendored locally, and a full test harness — no network access required to build or test.

Setup

Clone the template
git clone https://github.com/yattacorp/nexus-contract-template
cd nexus-contract-template
Install the WASM target
rustup target add wasm32-unknown-unknown
Install WABT tools (required for WASM memory patching in tests)
# macOS
brew install wabt

# Linux
apt install wabt

Project Structure

nexus-contract-template/
├── src/
│   └── lib.rs              # Your contract code
├── contract-tests/
│   └── src/
│       └── lib.rs          # Integration tests
├── vendor/
│   ├── nexus-sdk/          # SDK (no network needed)
│   └── nexus-xtask/        # WASM build tooling
├── Cargo.toml
└── rust-toolchain.toml

Cargo.toml

The template Cargo.toml is pre-configured for contract builds:
[package]
name = "my-nexus-contract"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]   # Required: produces a .wasm file

[dependencies]
nexus-sdk = { path = "vendor/nexus-sdk", default-features = false, features = ["contract"] }

[dev-dependencies]
nexus-sdk = { path = "vendor/nexus-sdk", features = ["testing"] }

[profile.release]
opt-level = "s"        # Optimize for size
lto = true             # Link-time optimization
codegen-units = 1      # Single codegen unit for better optimization
strip = true           # Strip debug symbols
panic = "abort"        # Smaller panic handler (required for no_std WASM)
crate-type = ["cdylib"] is mandatory. Without it, Cargo will not produce a .wasm file.

Writing Your First Contract

Edit src/lib.rs:
#![no_std]
extern crate alloc;

use nexus_sdk::contract_api::{ez::prelude::*, ez::ret};

// Persistent storage — survives between calls
pub static COUNTER: Mapping<&[u8], u64> = Mapping::new(b"count");

nexus_fn! {
    fn increment() {
        let current = COUNTER.get(&b"val".as_slice());
        COUNTER.set(&b"val".as_slice(), current + 1);
        emit("Incremented", &[]);
        ret::u64(current + 1)
    }
}

nexus_fn! {
    fn get_count() {
        let count = COUNTER.get(&b"val".as_slice());
        ret::u64(count)
    }
}
Key rules:
  • #![no_std] — contracts run in a bare WASM environment, no std library
  • extern crate alloc — gives you String, Vec, etc. via the alloc crate
  • nexus_fn! — the macro that exposes your function as a contract ABI entry point
  • ret::* — how you return values from a contract function
  • emit() — fire an event that indexers can listen to

Building

# Debug build (fast, larger output)
cargo build --target wasm32-unknown-unknown

# Release build (optimized, production-ready)
cargo build --target wasm32-unknown-unknown --release
Output: target/wasm32-unknown-unknown/release/my_nexus_contract.wasm

Testing

Write tests in contract-tests/src/lib.rs:
use nexus_sdk::testing::{TestEnv, TestArg, accounts};

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

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

    // Call increment
    env.set_caller(accounts::ALICE);
    let result = env.call(&contract, "increment", &[]).unwrap();
    result.assert_success();

    // Verify count
    let count = env.call(&contract, "get_count", &[]).unwrap();
    assert_eq!(count.as_u64(), 1);
}
Run tests:
# Build WASM first, then run tests
cargo build --target wasm32-unknown-unknown --release && cargo test
TestEnv::deploy() automatically patches the WASM memory export — the same transformation used in production builds — so your tests run against a valid artifact.

Deploying to NEXUS

Once your contract is built and tested, deploy it using the CLI:
nexus contract deploy \
  target/wasm32-unknown-unknown/release/my_nexus_contract.wasm \
  --rpc http://localhost:9945 \
  --wallet ~/.nexus/wallet.json
Or via the TypeScript SDK:
import { NexusClient, Wallet } from '@yattacorp/nexus-sdk';
import { readFileSync } from 'fs';

const wallet = Wallet.fromMnemonic(process.env.MNEMONIC!, 'mainnet', undefined, 'zcash');
const client = new NexusClient(
  { rpcUrl: 'https://api.yattacorp.xyz', network: 'mainnet', chain: 'zcash' },
  wallet
);

const wasm = readFileSync('target/wasm32-unknown-unknown/release/my_nexus_contract.wasm');
const { contractId, txHash, gasUsed } = await client.deployContract(wasm);
console.log('Contract deployed at:', contractId);
console.log('Gas used:', gasUsed);

Available SDK Primitives

PrimitivePurpose
Mapping<K, V>Key-value persistent storage
DoubleMapping<K1, K2, V>Two-key mapping (e.g. allowances)
Blockchain::msg.sender()Get the calling address
Blockchain::block.height()Current NEXUS block height
ReentrancyGuard::new()Prevent reentrant calls
require!(condition, msg)Assert with revert message
emit(event, data)Emit an indexed event
xcc::call(addr, fn, args)Call another contract
xcc::delegate_call(addr, fn, args)Delegate call
xcc::deploy(wasm, args, salt)Deploy contract from contract

rust-toolchain.toml

The template pins a specific Rust nightly version to ensure reproducible builds:
[toolchain]
channel = "nightly-2024-01-15"
targets = ["wasm32-unknown-unknown"]
Do not change the toolchain version without testing. NEXUS contracts use #![no_std] and some nightly features — a different toolchain version may cause compiler intrinsic errors.