Rivellum

Rivellum Portal

Download Wallet (Chrome)
Checking...
testnet

Rust SDK (`rivellum-sdk`)

The official async Rust client crate for the Rivellum network. Built on reqwest and tokio — ideal for high-performance backend services, Rust-based agents, and systems-level integrations.

Installation

Add to Cargo.toml:

[dependencies]
rivellum-sdk = "0.1"
tokio = { version = "1", features = ["full"] }

Quick Start

use rivellum_sdk::RivellumClient;

#[tokio::main]
async fn main() -> Result<(), rivellum_sdk::Error> {
    let client = RivellumClient::new("https://rpc.rivellum.network");

    let health = client.health().await?;
    println!("Status: {}", health.status);

    let bal = client.get_balance("a1b2...64hex").await?;
    println!("Balance: {}, Nonce: {}", bal.balance, bal.nonce);

    Ok(())
}

RivellumClient

Constructors

// Default 30-second timeout
pub fn new(node_url: &str) -> Self

// Custom timeout
pub fn with_timeout(node_url: &str, timeout: Duration) -> Self

// Bring your own reqwest client (TLS config, proxy, etc.)
pub fn with_http_client(node_url: &str, http: reqwest::Client) -> Self
use std::time::Duration;

// Custom timeout
let client = RivellumClient::with_timeout("https://rpc.rivellum.network", Duration::from_secs(60));

// Custom TLS or proxy
let http = reqwest::Client::builder()
    .danger_accept_invalid_certs(false)
    .build()?;
let client = RivellumClient::with_http_client("https://rpc.rivellum.network", http);

Error Type

pub enum Error {
    Http(reqwest::Error),
    Node { status: u16, body: String },
    Json(serde_json::Error),
    InvalidInput(String),
}

Check Error::Node { status, .. } for HTTP-level errors (404, 501, etc.).


Health & Info

health()

pub async fn health(&self) -> Result<HealthResponse, Error>

pub struct HealthResponse {
    pub status: String,
}

is_healthy()

pub async fn is_healthy(&self) -> bool

get_chain_info()

pub async fn get_chain_info(&self) -> Result<ChainInfo, Error>

pub struct ChainInfo {
    pub protocol_version: Option<String>,
    pub chain_id: Option<String>,
    pub features: Option<serde_json::Value>,
}

Account

get_balance(address)

pub async fn get_balance(&self, address: &str) -> Result<Balance, Error>

pub struct Balance {
    pub balance: String,
    pub nonce: u64,
    pub address: Option<String>,
}

get_ledger_tip()

pub async fn get_ledger_tip(&self) -> Result<LedgerTip, Error>

pub struct LedgerTip {
    pub height: Option<u64>,
    pub root_hash: Option<String>,
}

Contract (Mist)

contract(contract_id)

pub fn contract(&self, contract_id: &str) -> ContractHandle<'_>

Returns a ContractHandle bound to the given contract address.

ContractHandle::call(function, args)

pub async fn call(
    &self,
    function: &str,
    args: serde_json::Value,
) -> Result<CallResult, Error>

pub struct CallResult {
    pub receipt_id: String,
    pub contract_id: String,
    pub function: String,
    pub status: String,
    pub intent_id: Option<String>,
    pub failure_code: Option<String>,
    pub failure_detail: Option<String>,
    pub gas_used: Option<u64>,
    pub slot: Option<u64>,
}
let counter = client.contract("deadbeef...64hex");
let result = counter.call("increment", serde_json::json!({ "by": 5 })).await?;
println!("Receipt: {}, Status: {}", result.receipt_id, result.status);

ContractHandle::get_info()

pub async fn get_info(&self) -> Result<ContractInfo, Error>

pub struct ContractInfo {
    pub contract_id: Option<String>,
    pub name: Option<String>,
    pub bytecode_hash: Option<String>,
    pub functions: Option<serde_json::Value>,
}

deploy_contract(req)

pub async fn deploy_contract(&self, req: &DeployRequest) -> Result<DeployResult, Error>

pub struct DeployRequest {
    pub bundle: serde_json::Value,
    pub name: Option<String>,
}

pub struct DeployResult {
    pub contract_id: String,
    pub receipt_id: String,
    pub status: String,
}

get_receipt(receipt_id) / get_receipts()

pub async fn get_receipt(&self, receipt_id: &str) -> Result<Receipt, Error>
pub async fn get_receipts(&self) -> Result<Vec<Receipt>, Error>

pub struct Receipt {
    pub receipt_id: String,
    pub contract_id: Option<String>,
    pub function: Option<String>,
    pub status: Option<String>,
    pub gas_used: Option<u64>,
    pub slot: Option<u64>,
}

Envelope Submission

submit_envelope(envelope)

pub async fn submit_envelope(
    &self,
    envelope: &EncryptedEnvelope,
) -> Result<EnvelopeSubmitResult, Error>

pub struct EncryptedEnvelope {
    pub ciphertext: String,
    pub nonce: Option<String>,
    pub tag: Option<String>,
    pub sender: Option<String>,
}

pub struct EnvelopeSubmitResult {
    pub envelope_id: String,
    pub status: String,
}

submit_envelope_batch(envelopes)

pub async fn submit_envelope_batch(
    &self,
    envelopes: &[EncryptedEnvelope],
) -> Result<EnvelopeBatchSubmitResult, Error>

pub struct EnvelopeBatchSubmitResult {
    pub count: u64,
    pub envelope_ids: Vec<String>,
}

get_envelope_status(envelope_id)

pub async fn get_envelope_status(
    &self,
    envelope_id: &str,
) -> Result<EnvelopeStatus, Error>

pub struct EnvelopeStatus {
    pub envelope_id: String,
    pub status: String,        // received|admitted|batched|sealed|decrypted|ordered|executed|finalized|rejected
    pub batch_id: Option<String>,
    pub slot: Option<u64>,
}

get_batch_info(batch_id) / get_admission_ticket(intent_id)

pub async fn get_batch_info(&self, batch_id: &str) -> Result<BatchInfo, Error>
pub async fn get_admission_ticket(&self, intent_id: &str) -> Result<AdmissionTicket, Error>

pub struct BatchInfo {
    pub batch_id: String,
    pub envelope_count: Option<u64>,
    pub slot: Option<u64>,
    pub sealed_at: Option<String>,
}

pub struct AdmissionTicket {
    pub intent_id: String,
    pub status: String,
    pub committee: Option<u32>,
}

Simulation

simulate_intent(intent)

pub async fn simulate_intent(
    &self,
    intent: &serde_json::Value,
) -> Result<SimulationResult, Error>

pub struct SimulationResult {
    pub success: bool,
    pub gas_estimate: Option<u64>,
    pub result: Option<serde_json::Value>,
}

Events & Ledger

get_recent_events() / query_events(query)

pub async fn get_recent_events(&self) -> Result<EventsResponse, Error>
pub async fn query_events(&self, query: &EventsQuery) -> Result<EventsResponse, Error>

pub struct EventsResponse {
    pub events: Vec<serde_json::Value>,
    pub total: Option<u64>,
    pub next_cursor: Option<String>,
}

pub struct EventsQuery {
    pub topic: Option<String>,
    pub contract: Option<String>,
    pub address: Option<String>,
    pub intent_id: Option<String>,
    pub limit: Option<u32>,
    pub from_cursor: Option<String>,
}
let events = client.query_events(&EventsQuery {
    topic: Some("native.transfer".to_string()),
    limit: Some(50),
    ..Default::default()
}).await?;
println!("Found {} events", events.events.len());

PoUW

get_provers() / get_pending_jobs()

pub async fn get_provers(&self) -> Result<Vec<ProverStats>, Error>
pub async fn get_pending_jobs(&self) -> Result<Vec<JobSummary>, Error>

pub struct ProverStats {
    pub prover_id: String,
    pub jobs_completed: u64,
    pub jobs_failed: u64,
    pub total_fee_earned: String,
    pub avg_completion_time_ms: f64,
    pub last_seen_ms: u64,
}

pub struct JobSummary {
    pub job_id: String,
    pub tier: String,         // "Low" | "Medium" | "High"
    pub trace_hash: Vec<u8>,
    pub fee_budget: String,
    pub created_at_ms: u64,
    pub deadline_ms: u64,
}

Faucet (Testnet)

pub async fn faucet_mint(
    &self,
    address: &str,
    amount: &str,
) -> Result<FaucetMintResult, Error>

pub struct FaucetMintResult {
    pub success: bool,
    pub tx_hash: String,
    pub minted: String,
}

Governance

pub async fn get_governance_params(&self) -> Result<serde_json::Value, Error>
pub async fn get_governance_history(&self) -> Result<Vec<Proposal>, Error>

pub struct Proposal {
    pub id: Option<u64>,
    pub key: Option<String>,
    pub new_value: Option<String>,
    pub status: Option<String>,
    pub proposed_at_slot: Option<u64>,
    pub activation_slot: Option<u64>,
}

NFTs

pub async fn get_nft(&self, nft_id: &str) -> Result<NftMetadata, Error>
pub async fn get_nfts_owned_by(&self, address: &str) -> Result<NftListResponse, Error>
pub async fn get_nft_transfer_history(&self, nft_id: &str) -> Result<Vec<serde_json::Value>, Error>
pub async fn list_collections(&self) -> Result<CollectionListResponse, Error>
pub async fn get_collection(&self, collection_id: &str) -> Result<NftCollection, Error>
pub async fn get_collection_nfts(&self, collection_id: &str) -> Result<NftListResponse, Error>

pub struct NftMetadata {
    pub nft_id: String,
    pub owner: String,
    pub collection_id: Option<String>,
    pub metadata: serde_json::Value,
}

Prefabs

pub async fn list_prefabs(&self) -> Result<Vec<PrefabDefinition>, Error>
pub async fn get_prefab(&self, prefab_id: &str) -> Result<PrefabDefinition, Error>

pub struct PrefabDefinition {
    pub prefab_id: String,
    pub name: String,
    pub description: String,
    pub category: String,
    pub version: String,
}

Bridge & XCM

pub async fn get_bridge_status(&self) -> Result<serde_json::Value, Error>
pub async fn get_bridge_chains(&self) -> Result<Vec<serde_json::Value>, Error>
pub async fn get_bridge_messages(&self) -> Result<Vec<serde_json::Value>, Error>
pub async fn get_protocol_upgrades(&self) -> Result<Vec<serde_json::Value>, Error>

Randomness

pub async fn get_randomness_beacon(&self) -> Result<RandomnessBeacon, Error>

pub struct RandomnessBeacon {
    pub height: u64,
    pub output: String,
    pub proof: String,
    pub producer: String,
    pub timestamp_ms: u64,
    pub verified: bool,
}

Constraint Engine

pub async fn get_asset_constraints(&self, asset_id: &str) -> Result<serde_json::Value, Error>

pub async fn get_freeze_status(
    &self,
    asset_id: &str,
    account: &str,
) -> Result<FreezeStatus, Error>

pub struct FreezeStatus {
    pub frozen: bool,
    pub reason: Option<String>,
    pub frozen_at_ms: Option<u64>,
}

Move VM Endpoint Status

Public production APIs do not expose /move/* routes.

The Rust SDK keeps the following methods for compatibility, but they return Error::Node { status: 410, ... } on production networks:

pub async fn call_move_view(&self, req: &MoveViewRequest) -> Result<serde_json::Value, Error>
pub async fn get_move_module(&self, module_id: &str) -> Result<serde_json::Value, Error>
pub async fn get_move_module_abi(&self, module_id: &str) -> Result<serde_json::Value, Error>
pub async fn get_account_resources(&self, address: &str) -> Result<Vec<serde_json::Value>, Error>
pub async fn get_account_modules(&self, address: &str) -> Result<Vec<serde_json::Value>, Error>

Use Mist and /v1/* RPC capabilities for public production workflows.


Bounded Collections

The Rust SDK exports Mist bounded collection types for use with on-chain contract data:

use rivellum_sdk::{BoundedList, BoundedMap, BoundedSet, BoundedQueue, BoundedSortedMap};

These correspond to Mist's bounded collections and enforce compile-time capacity constraints.


Full Example

use rivellum_sdk::{RivellumClient, EncryptedEnvelope, EventsQuery};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), rivellum_sdk::Error> {
    let client = RivellumClient::with_timeout(
        "https://rpc.rivellum.network",
        Duration::from_secs(30),
    );

    // 1. Health check
    assert!(client.is_healthy().await);

    // 2. Get balance
    let bal = client.get_balance("a1b2...64hex").await?;
    println!("Balance: {} (nonce {})", bal.balance, bal.nonce);

    // 3. Submit an envelope
    let env = EncryptedEnvelope {
        ciphertext: "encrypted_payload_hex".to_string(),
        nonce: Some("nonce_hex".to_string()),
        tag: Some("tag_hex".to_string()),
        sender: Some("sender_address_hex".to_string()),
    };
    let result = client.submit_envelope(&env).await?;
    println!("Envelope {}: {}", result.envelope_id, result.status);

    // 4. Poll status
    loop {
        let status = client.get_envelope_status(&result.envelope_id).await?;
        println!("Status: {}", status.status);
        match status.status.as_str() {
            "finalized" | "rejected" => break,
            _ => tokio::time::sleep(Duration::from_secs(2)).await,
        }
    }

    // 5. Query recent transfers
    let events = client.query_events(&EventsQuery {
        topic: Some("native.transfer".to_string()),
        limit: Some(10),
        ..Default::default()
    }).await?;
    println!("Found {} transfers", events.events.len());

    // 6. Call a contract
    let contract = client.contract("contract_id_hex");
    let res = contract.call("get_score", serde_json::json!({ "player": "addr_hex" })).await?;
    println!("Score receipt: {}", res.receipt_id);

    Ok(())
}