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(())
}