apex-sdk-substrate 0.1.2

Substrate adapter for Apex SDK
Documentation

apex-sdk-substrate

Crates.io Documentation License

Substrate blockchain adapter for the Apex SDK, enabling seamless interaction with Polkadot, Kusama, and other Substrate-based chains.

Overview

apex-sdk-substrate provides a comprehensive Rust interface for interacting with Substrate-based blockchains. It offers type-safe APIs for transactions, storage queries, smart contracts (ink!), cross-chain messaging (XCM), and more.

Features

  • Multi-Chain Support: Polkadot, Kusama, Westend, and custom Substrate chains
  • Type-Safe Metadata: Compile-time type checking with generated metadata
  • Smart Contracts: Full ink! smart contract support with deployment and interaction
  • XCM Integration: Cross-chain messaging and asset transfers
  • Wallet Management: SR25519/ED25519 key pair management and signing
  • Connection Pooling: Robust connection management with health checks
  • Caching Layer: Intelligent caching for storage queries and account data
  • Metrics: Comprehensive monitoring and observability

Installation

Add this to your Cargo.toml:

[dependencies]
apex-sdk-substrate = "0.1.2"
tokio = { version = "1.0", features = ["full"] }

Feature Flags

[dependencies]
apex-sdk-substrate = { version = "0.1.2", features = ["typed-westend"] }

Available features:

  • typed-polkadot - Typed metadata for Polkadot
  • typed-kusama - Typed metadata for Kusama
  • typed-westend - Typed metadata for Westend
  • typed - Base typed metadata support

Quick Start

Basic Connection

use apex_sdk_substrate::SubstrateAdapter;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to Polkadot
    let adapter = SubstrateAdapter::polkadot("wss://rpc.polkadot.io").await?;
    
    // Get latest block number
    let block_number = adapter.get_latest_block().await?;
    println!("Latest block: {}", block_number);
    
    Ok(())
}

Different Chain Connections

// Polkadot
let polkadot = SubstrateAdapter::polkadot("wss://rpc.polkadot.io").await?;

// Kusama
let kusama = SubstrateAdapter::kusama("wss://kusama-rpc.polkadot.io").await?;

// Westend Testnet
let westend = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io").await?;

// Custom Substrate chain
let custom = SubstrateAdapter::custom("wss://your-node.com", "your-chain").await?;

Account and Wallet Management

Creating Wallets

use apex_sdk_substrate::{Wallet, Keypair};

// Generate new SR25519 keypair
let keypair = Keypair::generate_sr25519();
let wallet = Wallet::new(keypair);

println!("Address: {}", wallet.address());
println!("Public key: {}", hex::encode(wallet.public_key()));

From Mnemonic

use apex_sdk_substrate::Wallet;

// Create from mnemonic phrase
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
let wallet = Wallet::from_mnemonic(mnemonic, None)?;

// With custom derivation path
let wallet = Wallet::from_mnemonic(mnemonic, Some("//Alice"))?;

Account Queries

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io").await?;

// Get account balance
let account_id = wallet.account_id();
let balance = adapter.get_balance(&account_id).await?;
println!("Balance: {} WND", balance);

// Get account nonce
let nonce = adapter.get_nonce(&account_id).await?;
println!("Nonce: {}", nonce);

// Get account info
let account_info = adapter.get_account_info(&account_id).await?;
println!("Free balance: {}", account_info.data.free);
println!("Reserved: {}", account_info.data.reserved);

Transaction Building and Submission

Basic Transfers

use apex_sdk_substrate::{SubstrateAdapter, TransactionBuilder};

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io").await?;
let wallet = Wallet::from_mnemonic("your mnemonic", None)?;

// Build transfer transaction
let dest = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; // Alice
let amount = 1_000_000_000_000u128; // 1 WND

let tx = TransactionBuilder::new(&adapter)
    .transfer_keep_alive(dest, amount)
    .build()?;

// Sign and submit
let signed_tx = wallet.sign(&tx).await?;
let hash = adapter.submit_transaction(&signed_tx).await?;

println!("Transaction submitted: {}", hash);

// Wait for finalization
let events = adapter.wait_for_finalized(&hash).await?;
println!("Transaction finalized with {} events", events.len());

Batch Transactions

let tx = TransactionBuilder::new(&adapter)
    .batch(vec![
        // Multiple transfers in one transaction
        adapter.tx().balances().transfer_keep_alive(dest1, amount1),
        adapter.tx().balances().transfer_keep_alive(dest2, amount2),
        adapter.tx().balances().transfer_keep_alive(dest3, amount3),
    ])
    .build()?;

Smart Contract Integration (ink!)

Contract Deployment

use apex_sdk_substrate::{ContractClient, ContractMetadata};

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io").await?;
let wallet = Wallet::from_mnemonic("your mnemonic", None)?;

// Load contract metadata and WASM
let metadata = ContractMetadata::load("contract.json")?;
let wasm_code = std::fs::read("contract.wasm")?;

// Deploy contract
let contract = ContractClient::new(&adapter, &wallet)
    .deploy(
        wasm_code,
        metadata.clone(),
        "new", // constructor name
        vec![], // constructor args
        1_000_000_000_000u64, // endowment
        None, // salt
    )
    .await?;

println!("Contract deployed at: {}", contract.address());

Contract Interaction

// Call contract method
let result = contract
    .call("get_value")
    .args(vec![])
    .dry_run() // Read-only call
    .await?;

println!("Contract returned: {:?}", result);

// Execute contract transaction
let tx_hash = contract
    .call("set_value")
    .args(vec![42u32.into()])
    .value(0) // No payment
    .submit()
    .await?;

println!("Contract call submitted: {}", tx_hash);

Contract Events

// Listen for contract events
let mut event_stream = contract.events().await?;

while let Some(event) = event_stream.next().await {
    match event {
        ContractEvent::ValueChanged { old_value, new_value } => {
            println!("Value changed from {} to {}", old_value, new_value);
        }
        _ => {}
    }
}

Cross-Chain Messaging (XCM)

Asset Transfers

use apex_sdk_substrate::xcm::{XcmClient, Destination, Asset};

let adapter = SubstrateAdapter::polkadot("wss://rpc.polkadot.io").await?;
let wallet = Wallet::from_mnemonic("your mnemonic", None)?;

let xcm = XcmClient::new(&adapter, &wallet);

// Transfer DOT to a parachain
let dest = Destination::parachain(1000); // Acala parachain
let asset = Asset::native(10_000_000_000u128); // 1 DOT
let beneficiary = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";

let tx_hash = xcm
    .transfer_asset(dest, asset, beneficiary)
    .await?;

println!("XCM transfer submitted: {}", tx_hash);

Reserve Transfers

// Reserve-based transfer
let tx_hash = xcm
    .reserve_transfer_asset(dest, asset, beneficiary)
    .await?;

// Teleport (for trusted chains)
let tx_hash = xcm
    .teleport_asset(dest, asset, beneficiary)
    .await?;

Storage Queries

System Storage

// Query any storage item
let storage_key = adapter.storage().system().account(account_id);
let account_info = adapter.query_storage(&storage_key, None).await?;

// Get storage at specific block
let block_hash = adapter.get_block_hash(Some(100_000)).await?;
let historical_data = adapter.query_storage(&storage_key, Some(block_hash)).await?;

Custom Storage Queries

// Using storage client
let storage = adapter.storage();

// Query balances
let balance_key = storage.balances().account(&account_id);
let balance_info = adapter.query_storage(&balance_key, None).await?;

// Query runtime version
let version = adapter.runtime_version().await?;
println!("Runtime version: {}", version.spec_version);

Advanced Features

Connection Pooling

use apex_sdk_substrate::{SubstrateAdapter, PoolConfig};

let pool_config = PoolConfig {
    max_connections: 10,
    min_connections: 2,
    connection_timeout: Duration::from_secs(30),
    health_check_interval: Duration::from_secs(60),
    reconnect_attempts: 5,
};

let adapter = SubstrateAdapter::with_pool(
    vec![
        "wss://rpc.polkadot.io",
        "wss://polkadot-rpc.dwellir.com",
        "wss://1rpc.io/dot",
    ],
    pool_config,
).await?;

Caching

use apex_sdk_substrate::CacheConfig;

let cache_config = CacheConfig {
    max_entries: 10000,
    ttl: Duration::from_secs(300), // 5 minutes
    storage_cache_enabled: true,
    account_cache_enabled: true,
};

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io")
    .await?
    .with_cache(cache_config);

Typed Metadata

With typed metadata, you get compile-time type safety:

// Enable with feature flag: features = ["typed-westend"]
use apex_sdk_substrate::metadata::westend;

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io").await?;

// Type-safe transaction building
let tx = westend::tx()
    .balances()
    .transfer_keep_alive(dest, amount);

// Type-safe storage queries  
let storage_query = westend::storage()
    .system()
    .account(&account_id);

let account_info = adapter.query_storage(&storage_query, None).await?;

Monitoring and Metrics

Built-in Metrics

use apex_sdk_substrate::MetricsConfig;

let metrics_config = MetricsConfig {
    enabled: true,
    prometheus_endpoint: Some("0.0.0.0:9090".to_string()),
    collect_rpc_metrics: true,
    collect_cache_metrics: true,
};

let adapter = SubstrateAdapter::westend("wss://westend-rpc.polkadot.io")
    .await?
    .with_metrics(metrics_config);

// Access metrics
let metrics = adapter.metrics();
println!("RPC calls: {}", metrics.rpc_calls_total());
println!("Cache hit rate: {:.2}%", metrics.cache_hit_rate() * 100.0);

Error Handling

use apex_sdk_substrate::{SubstrateError, Result};

match some_operation().await {
    Err(SubstrateError::InsufficientFunds) => {
        println!("Account has insufficient balance");
    }
    Err(SubstrateError::TransactionFailed(reason)) => {
        println!("Transaction failed: {}", reason);
    }
    Err(SubstrateError::ConnectionError(msg)) => {
        println!("Connection error: {}", msg);
    }
    Err(SubstrateError::MetadataError(msg)) => {
        println!("Metadata error: {}", msg);
    }
    Ok(result) => {
        // Handle success
    }
}

Testing

Unit Tests

cargo test

Integration Tests

cargo test --features integration-tests

Integration tests connect to Westend testnet.

Examples

Complete examples in the examples directory:

Configuration

Chain Endpoints

// Polkadot
const POLKADOT_ENDPOINTS: &[&str] = &[
    "wss://rpc.polkadot.io",
    "wss://polkadot-rpc.dwellir.com", 
    "wss://1rpc.io/dot",
];

// Kusama
const KUSAMA_ENDPOINTS: &[&str] = &[
    "wss://kusama-rpc.polkadot.io",
    "wss://kusama-rpc.dwellir.com",
    "wss://1rpc.io/ksm",
];

Environment Variables

# Chain endpoints
POLKADOT_RPC_URL="wss://rpc.polkadot.io"
KUSAMA_RPC_URL="wss://kusama-rpc.polkadot.io"
WESTEND_RPC_URL="wss://westend-rpc.polkadot.io"

# Mnemonics (for testing)
TEST_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

# API keys for public nodes
DWELLIR_API_KEY="your-dwellir-key"
ONFINALITY_API_KEY="your-onfinality-key"

Generating Typed Metadata

To use typed metadata features:

cd apex-sdk-substrate
./scripts/generate_metadata.sh westend

This generates type-safe Rust code from live chain metadata.

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

Support