Skip to content

Subxt Rust API

Introduction

subxt is a Rust library designed to interact with Polkadot SDK-based blockchains. It provides a type-safe interface for submitting transactions, querying on-chain state, and performing other blockchain interactions. By leveraging Rust's strong type system, subxt ensures that your code is validated at compile time, reducing runtime errors and improving reliability.

Prerequisites

Before using subxt, ensure you have the following requirements:

  • Rust and Cargo installed on your system. You can install them using Rustup
  • A Rust project initialized. If you don't have one, create it with:
    cargo new my_project && cd my_project
    

Installation

To use subxt in your project, you must install the necessary dependencies. Each plays a specific role in enabling interaction with the blockchain:

  1. Install the subxt CLI - subxt-cli is a command-line tool that provides utilities for working with Polkadot SDK metadata. In the context of subxt, it is essential to download chain metadata, which is required to generate type-safe Rust interfaces for interacting with the blockchain. Install it using:

    cargo install subxt-cli
    
  2. Add core dependencies - these dependencies are essential for interacting with the blockchain:

    • subxt - the main library for communicating with Polkadot SDK nodes. It handles RPC requests, encoding/decoding, and type generation

      cargo add subxt
      
    • subxt-signer - provides cryptographic functionality for signing transactions. Without this, you can only read data but cannot submit transactions

      cargo add subxt-signer
      
    • tokio - an asynchronous runtime for Rust. Since blockchain operations are async, Tokio enables the efficient handling of network requests. The rt feature enables Tokio's runtime, including the current-thread single-threaded scheduler, which is necessary for async execution. The macros feature provides procedural macros like #[tokio::main] to simplify runtime setup

      cargo add tokio --features rt,macros
      

    After adding the dependencies, your Cargo.toml should look like this:

    [package]
    name = "my-project"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    subxt = "0.39.0"
    subxt-signer = "0.39.0"
    tokio = { version = "1.43.0", features = ["rt", "macros"] }
    

Get Started

This guide will walk you through the fundamental operations of subxt, from setting up your environment to executing transactions and querying blockchain state.

Download Chain Metadata

Before interacting with a blockchain, you need to retrieve its metadata. This metadata defines storage structures, extrinsics, and other runtime details. Use the subxt-cli tool to download the metadata, replacing INSERT_NODE_URL with the URL of the node you want to interact with:

subxt metadata --url INSERT_NODE_URL > polkadot_metadata.scale

Generate Type-Safe Interfaces

Use the #[subxt::subxt] macro to generate a type-safe Rust interface from the downloaded metadata:

// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "./polkadot_metadata.scale")]
pub mod polkadot {}

Once subxt interfaces are generated, you can interact with your node in the following ways. You can use the links below to view the related subxt documentation:

  • Transactions - builds and submits transactions, monitors their inclusion in blocks, and retrieves associated events
  • Storage - enables querying of node storage data
  • Events - retrieves events emitted from recent blocks
  • Constants - accesses constant values stored in nodes that remain unchanged across a specific runtime version.
  • Blocks - loads recent blocks or subscribes to new/finalized blocks, allowing examination of extrinsics, events, and storage at those blocks
  • Runtime APIs - makes calls into pallet runtime APIs to fetch data
  • Custom values - accesses "custom values" contained within metadata
  • Raw RPC calls - facilitates raw RPC requests to compatible nodes

Initialize the Subxt client

To interact with a blockchain node using subxt, create an asynchronous main function and initialize the client. Replace INSERT_NODE_URL with the URL of your target node:

use std::str::FromStr;
use subxt::utils::AccountId32;
use subxt::{OnlineClient, PolkadotConfig};
use subxt_signer::{bip39::Mnemonic,sr25519::Keypair};

// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "./polkadot_metadata.scale")]
pub mod polkadot {}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Define the node URL.
    const NODE_URL: &str = "INSERT_NODE_URL";

    // Initialize the Subxt client to interact with the blockchain.
    let api = OnlineClient::<PolkadotConfig>::from_url(NODE_URL).await?;

    // Your code here...

    Ok(())
}

Read Chain Data

subxt provides multiple ways to access on-chain data:

  • Constants - constants are predefined values in the runtime that remain unchanged unless modified by a runtime upgrade

    For example, to retrieve the existential deposit, use:

    // A query to obtain some constant.
    let constant_query = polkadot::constants().balances().existential_deposit();
    
    // Obtain the value.
    let value = api.constants().at(&constant_query)?;
    
    println!("Existential deposit: {:?}", value);
    
  • State - state refers to the current chain data, which updates with each block

    To fetch account information, replace INSERT_ADDRESS with the address you want to fetch data from and use:

    // Define the target account address.
    const ADDRESS: &str = "INSERT_ADDRESS";
    let account = AccountId32::from_str(ADDRESS).unwrap();
    
    // Build a storage query to access account information.
    let storage_query = polkadot::storage().system().account(&account.into());
    
    // Fetch the latest state for the account.
    let result = api
        .storage()
        .at_latest()
        .await?
        .fetch(&storage_query)
        .await?
        .unwrap();
    
    println!("Account info: {:?}", result);
    

Submit Transactions

To submit a transaction, you must construct an extrinsic, sign it with your private key, and send it to the blockchain. Replace INSERT_DEST_ADDRESS with the recipient's address, INSERT_AMOUNT with the amount to transfer, and INSERT_SECRET_PHRASE with the sender's mnemonic phrase:

// Define the recipient address and transfer amount.
const DEST_ADDRESS: &str = "INSERT_DEST_ADDRESS";
const AMOUNT: u128 = INSERT_AMOUNT;

// Convert the recipient address into an `AccountId32`.
let dest = AccountId32::from_str(DEST_ADDRESS).unwrap();

// Build the balance transfer extrinsic.
let balance_transfer_tx = polkadot::tx()
    .balances()
    .transfer_allow_death(dest.into(), AMOUNT);

// Load the sender's keypair from a mnemonic phrase.
const SECRET_PHRASE: &str = "INSERT_SECRET_PHRASE";
let mnemonic = Mnemonic::parse(SECRET_PHRASE).unwrap();
let sender_keypair = Keypair::from_phrase(&mnemonic, None).unwrap();

// Sign and submit the extrinsic, then wait for it to be finalized.
let events = api
    .tx()
    .sign_and_submit_then_watch_default(&balance_transfer_tx, &sender_keypair)
    .await?
    .wait_for_finalized_success()
    .await?;

// Check for a successful transfer event.
if let Some(event) = events.find_first::<polkadot::balances::events::Transfer>()? {
    println!("Balance transfer successful: {:?}", event);
}

Where to Go Next

Now that you've covered the basics dive into the official subxt documentation for comprehensive reference materials and advanced features.

Last update: March 12, 2025
| Created: March 12, 2025