Subxt Guide (#890)

* WIP Starting to write book; extrinsics first pass done

* cargo fmt

* Ongoing work; events, constants, wip blocks

* at_latest() and wip blocks

* remove need to import parity-scale-codec crate with Subxt for macro to work

* More docs; expanding on setup guide and finish pass of main sections

* Tidy and remove example section for now

* format book lines to 100chars

* Fix example code

* cargo fmt

* cargo fmt

* fix example

* Fix typos

* fix broken doc links, pub mods

* Update Subxt macro docs

* can't link to Subxt here

* move macro docs to Subxt to make linking better and fix example code

* note on macro about docs

* cargo fmt

* document the no_default_derives macro feature

* Address feedback and remove redundant text

* address review comments; minor tweaks

* WIP add Runtime calls to book

* Improve Runtime API docs

* expose thing we forgot to expose and doc link fixes
This commit is contained in:
James Wilson
2023-05-04 15:03:42 +01:00
committed by GitHub
parent 53544a54b6
commit 562f12cd9b
58 changed files with 1472 additions and 1506 deletions
Generated
+37 -41
View File
@@ -94,9 +94,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -143,9 +143,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.70"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "array-bytes"
@@ -353,9 +353,9 @@ dependencies = [
[[package]]
name = "bounded-collections"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370"
checksum = "e3888522b497857eb606bf51695988dba7096941822c1bcf676e3a929a9ae7a0"
dependencies = [
"log",
"parity-scale-codec",
@@ -458,9 +458,9 @@ dependencies = [
[[package]]
name = "clap"
version = "3.2.23"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"bitflags",
"clap_lex 0.2.4",
@@ -470,9 +470,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.2.5"
version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819"
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
dependencies = [
"clap_builder",
"clap_derive",
@@ -481,9 +481,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.2.5"
version = "4.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab"
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
dependencies = [
"anstream",
"anstyle",
@@ -640,7 +640,7 @@ dependencies = [
"atty",
"cast",
"ciborium",
"clap 3.2.23",
"clap 3.2.25",
"criterion-plot",
"itertools",
"lazy_static",
@@ -837,12 +837,12 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b"
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
dependencies = [
"darling_core 0.20.0",
"darling_macro 0.20.0",
"darling_core 0.20.1",
"darling_macro 0.20.1",
]
[[package]]
@@ -861,9 +861,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9"
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
dependencies = [
"fnv",
"ident_case",
@@ -886,11 +886,11 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb"
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
dependencies = [
"darling_core 0.20.0",
"darling_core 0.20.1",
"quote",
"syn 2.0.15",
]
@@ -1691,7 +1691,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
"rustix 0.37.14",
"rustix 0.37.19",
"windows-sys 0.48.0",
]
@@ -1905,9 +1905,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "linux-raw-sys"
version = "0.3.4"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf"
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
[[package]]
name = "lock_api"
@@ -1958,7 +1958,7 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e"
dependencies = [
"rustix 0.37.14",
"rustix 0.37.19",
]
[[package]]
@@ -2630,15 +2630,15 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.37.14"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.4",
"linux-raw-sys 0.3.7",
"windows-sys 0.48.0",
]
@@ -3457,9 +3457,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "ss58-registry"
version = "1.39.0"
version = "1.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d"
checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b"
dependencies = [
"Inflector",
"num-format",
@@ -3574,7 +3574,7 @@ dependencies = [
name = "subxt-cli"
version = "0.28.0"
dependencies = [
"clap 4.2.5",
"clap 4.2.7",
"color-eyre",
"frame-metadata",
"hex",
@@ -3614,20 +3614,16 @@ version = "0.28.0"
dependencies = [
"futures",
"hex",
"parity-scale-codec",
"sp-core",
"sp-keyring",
"sp-runtime",
"subxt",
"tokio",
"tracing-subscriber 0.3.17",
]
[[package]]
name = "subxt-macro"
version = "0.28.0"
dependencies = [
"darling 0.20.0",
"darling 0.20.1",
"proc-macro-error",
"subxt-codegen",
"syn 2.0.15",
@@ -3824,9 +3820,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.7"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
dependencies = [
"bytes",
"futures-core",
@@ -4638,9 +4634,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
version = "0.4.1"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28"
checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
dependencies = [
"memchr",
]
+1 -1
View File
@@ -86,4 +86,4 @@ subxt-macro = { version = "0.28.0", path = "macro" }
subxt-metadata = { version = "0.28.0", path = "metadata" }
subxt-codegen = { version = "0.28.0", path = "codegen" }
test-runtime = { path = "testing/test-runtime" }
substrate-runner = { path = "testing/substrate-runner" }
substrate-runner = { path = "testing/substrate-runner" }
+7 -2
View File
@@ -225,8 +225,13 @@ impl RuntimeGenerator {
// Preserve any Rust items that were previously defined in the adorned module
#( #rust_items ) *
// Make it easy to access the root via `root_mod` at different levels:
use super::#mod_ident as root_mod;
// Make it easy to access the root items via `root_mod` at different levels
// without reaching out of this module.
#[allow(unused_imports)]
mod root_mod {
pub use super::*;
}
#types_mod
}
})
-4
View File
@@ -16,9 +16,5 @@ description = "Subxt example usage"
subxt = { workspace = true }
tokio = { workspace = true }
futures = { workspace = true }
codec = { package = "parity-scale-codec", workspace = true, features = ["derive", "bit-vec"] }
hex = { workspace = true }
sp-keyring = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
tracing-subscriber = { workspace = true }
+3 -1
View File
@@ -1,3 +1,5 @@
# Subxt Examples
Take a look in the [examples](./examples) subfolder for various `subxt` usage examples.
Take a look in the [examples](./examples) subfolder for various `subxt` usage examples.
All examples form part of the `subxt` documentation; there should be no examples without corresponding links from the docs.
-40
View File
@@ -1,40 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create a transaction to submit:
let tx = polkadot::tx()
.balances()
.transfer(dest, 123_456_789_012_345);
// Submit the transaction with default params:
let hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Balance transfer extrinsic submitted: {hash}");
Ok(())
}
@@ -0,0 +1,34 @@
use sp_keyring::AccountKeyring;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a balance transfer extrinsic.
let dest = AccountKeyring::Bob.to_account_id().into();
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
// Submit the balance transfer extrinsic from Alice, and wait for it to be successful
// and in a finalized block. We get back the extrinsic events if all is well.
let from = PairSigner::new(AccountKeyring::Alice.pair());
let events = api
.tx()
.sign_and_submit_then_watch_default(&balance_transfer_tx, &from)
.await?
.wait_for_finalized_success()
.await?;
// Find a Transfer event and print it.
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: {event:?}");
}
Ok(())
}
@@ -0,0 +1,58 @@
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use subxt::{
tx::{PairSigner, TxStatus},
OnlineClient, PolkadotConfig,
};
// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a balance transfer extrinsic.
let dest = AccountKeyring::Bob.to_account_id().into();
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
// Submit the balance transfer extrinsic from Alice, and then monitor the
// progress of it.
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut balance_transfer_progress = api
.tx()
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?;
while let Some(status) = balance_transfer_progress.next().await {
match status? {
// It's finalized in a block!
TxStatus::Finalized(in_block) => {
println!(
"Transaction {:?} is finalized in block {:?}",
in_block.extrinsic_hash(),
in_block.block_hash()
);
// grab the events and fail if no ExtrinsicSuccess event seen:
let events = in_block.wait_for_success().await?;
// We can look for events (this uses the static interface; we can also iterate
// over them and dynamically decode them):
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
}
// Just log any other status we encounter:
other => {
println!("Status: {other:?}");
}
}
}
Ok(())
}
@@ -1,52 +1,28 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
config::{
polkadot::{Era, PlainTip, PolkadotExtrinsicParamsBuilder as Params},
PolkadotConfig,
},
tx::PairSigner,
OnlineClient,
};
use subxt::config::polkadot::{Era, PlainTip, PolkadotExtrinsicParamsBuilder as Params};
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
// Create a client to use:
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create a transaction to submit:
let tx = polkadot::tx()
.balances()
.transfer(dest, 123_456_789_012_345);
// Build a balance transfer extrinsic.
let dest = AccountKeyring::Bob.to_account_id().into();
let tx = polkadot::tx().balances().transfer(dest, 10_000);
// Configure the transaction tip and era:
// Configure the transaction parameters; for Polkadot the tip and era:
let tx_params = Params::new()
.tip(PlainTip::new(20_000_000_000))
.tip(PlainTip::new(1_000))
.era(Era::Immortal, api.genesis_hash());
// submit the transaction:
let hash = api.tx().sign_and_submit(&tx, &signer, tx_params).await?;
println!("Balance transfer extrinsic submitted: {hash}");
let from = PairSigner::new(AccountKeyring::Alice.pair());
let hash = api.tx().sign_and_submit(&tx, &from, tx_params).await?;
println!("Balance transfer extrinsic submitted with hash : {hash}");
Ok(())
}
@@ -1,15 +1,3 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.29-41a9d84b152.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.29/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use subxt::{OnlineClient, PolkadotConfig};
@@ -18,14 +6,13 @@ pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to all finalized blocks:
let mut blocks_sub = api.blocks().subscribe_finalized().await?;
// For each block, print a bunch of information about it:
while let Some(block) = blocks_sub.next().await {
let block = block?;
@@ -36,6 +23,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" Hash: {block_hash}");
println!(" Extrinsics:");
// Log each of the extrinsic with it's associated events:
let body = block.body().await?;
for ext in body.extrinsics() {
let idx = ext.index();
@@ -51,8 +39,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pallet_name = evt.pallet_name();
let event_name = evt.variant_name();
let event_values = evt.field_values()?;
println!(" {pallet_name}_{event_name}");
println!(" {}", event_values);
}
}
}
@@ -1,39 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::join;
use sp_keyring::AccountKeyring;
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = OnlineClient::<PolkadotConfig>::new().await?;
let addr = AccountKeyring::Bob.to_account_id().into();
// Construct storage addresses to access:
let staking_bonded = polkadot::storage().staking().bonded(&addr);
let staking_ledger = polkadot::storage().staking().ledger(&addr);
// For storage requests, we can join futures together to
// await multiple futures concurrently:
let a_fut = api.storage().at_latest().await?.fetch(&staking_bonded);
let b_fut = api.storage().at_latest().await?.fetch(&staking_ledger);
let (a, b) = join!(a_fut, b_fut);
println!("{a:?}, {b:?}");
Ok(())
}
+17
View File
@@ -0,0 +1,17 @@
use subxt::{OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// A dynamic query to obtain some contant:
let constant_query = subxt::dynamic::constant("System", "BlockLength");
// Obtain the value:
let value = api.constants().at(&constant_query)?;
println!("Constant bytes: {:?}", value.encoded());
println!("Constant value: {}", value.to_value()?);
Ok(())
}
+19
View File
@@ -0,0 +1,19 @@
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// A query to obtain some contant:
let constant_query = polkadot::constants().system().block_length();
// Obtain the value:
let value = api.constants().at(&constant_query)?;
println!("Block length: {value:?}");
Ok(())
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This example should compile but should fail to work, since we've modified the
//! config to not align with a Polkadot node.
use sp_keyring::AccountKeyring;
use subxt::{
config::{substrate::SubstrateExtrinsicParams, Config, SubstrateConfig},
tx::PairSigner,
OnlineClient,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
/// Custom [`Config`] impl where the default types for the target chain differ from the
/// [`DefaultConfig`]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct MyConfig;
impl Config for MyConfig {
// This is different from the default `u32`.
//
// *Note* that in this example it does differ from the actual `Index` type in the
// polkadot runtime used, so some operations will fail. Normally when using a custom `Config`
// impl types MUST match exactly those used in the actual runtime.
type Index = u64;
type Hash = <SubstrateConfig as Config>::Hash;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Signature = <SubstrateConfig as Config>::Signature;
// ExtrinsicParams makes use of the index type, so we need to adjust it
// too to align with our modified index type, above:
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
// Create a client to use:
let api = OnlineClient::<MyConfig>::new().await?;
// Create a transaction to submit:
let tx = polkadot::tx()
.balances()
.transfer(dest, 123_456_789_012_345);
// submit the transaction with default params:
let hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Balance transfer extrinsic submitted: {hash}");
Ok(())
}
-12
View File
@@ -1,12 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
// If you'd like to use metadata directly from a running node, you
// can provide a URL to that node here. HTTP or WebSocket URLs can be
// provided. Note that if the metadata cannot be retrieved from this
// node URL at compile time, compilation will fail.
#[subxt::subxt(runtime_metadata_url = "wss://rpc.polkadot.io:443")]
pub mod polkadot {}
fn main() {}
-32
View File
@@ -1,32 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
#![allow(clippy::redundant_clone)]
#[subxt::subxt(
runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
// We can add (certain) custom derives to the generated types by providing
// a comma separated list to the below attribute. Most useful for adding `Clone`.
// The derives that we can add ultimately is limited to the traits that the base
// types relied upon by the codegen implement.
derive_for_all_types = "Clone, PartialEq, Eq",
// To apply derives to specific generated types, add a `derive_for_type` per type,
// mapping the type path to the derives which should be added for that type only.
// Note that these derives will be in addition to those specified above in
// `derive_for_all_types`
derive_for_type(path = "frame_support::PalletId", derive = "Eq, Ord, PartialOrd"),
derive_for_type(path = "sp_runtime::ModuleError", derive = "Eq, Hash"),
)]
pub mod polkadot {}
use polkadot::runtime_types::frame_support::PalletId;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pallet_id = PalletId([1u8; 8]);
let _ = pallet_id.clone();
Ok(())
}
-74
View File
@@ -1,74 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.31-3711c6f9b2a.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.31/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{dynamic::Value, tx::PairSigner, OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// My account.
let signer_account = AccountKeyring::Alice;
let signer_account_id = signer_account.to_account_id();
let signer = PairSigner::new(signer_account.pair());
// Transfer balance to this destination:
let dest = AccountKeyring::Bob.to_account_id();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create the inner balance transfer call.
let inner_tx = subxt::dynamic::tx(
"Balances",
"transfer",
vec![
Value::unnamed_variant("Id", [Value::from_bytes(&dest)]),
Value::u128(123_456_789_012_345),
],
);
// Now, build an outer call which this inner call will be a part of.
// This sets up the multisig arrangement.
//
// Note: Since this is a dynamic call, we can either use named or unnamed
// arguments (if unnamed, the order matters).
let tx = subxt::dynamic::tx(
"Multisig",
"as_multi",
vec![
("threshold", Value::u128(1)),
(
"other_signatories",
Value::unnamed_composite([Value::from_bytes(&signer_account_id)]),
),
("maybe_timepoint", Value::unnamed_variant("None", [])),
("call", inner_tx.into_value()),
(
"max_weight",
Value::named_composite([
("ref_time", Value::u128(10000000000)),
("proof_size", Value::u128(1)),
]),
),
],
);
// Submit it:
let encoded = hex::encode(api.tx().call_data(&tx)?);
println!("Call data: {encoded}");
let tx_hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Submitted tx with hash {tx_hash}");
Ok(())
}
-85
View File
@@ -1,85 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
// This example showcases working with dynamic values rather than those that are generated via the subxt proc macro.
use sp_keyring::AccountKeyring;
use subxt::{dynamic::Value, tx::PairSigner, OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = OnlineClient::<PolkadotConfig>::new().await?;
// 1. Dynamic Balance Transfer (the dynamic equivalent to the balance_transfer example).
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id();
// Create a transaction to submit:
let tx = subxt::dynamic::tx(
"Balances",
"transfer",
vec![
// A value representing a MultiAddress<AccountId32, _>. We want the "Id" variant, and that
// will ultimately contain the bytes for our destination address (there is a new type wrapping
// the address, but our encoding will happily ignore such things and do it's best to line up what
// we provide with what it needs).
Value::unnamed_variant("Id", [Value::from_bytes(&dest)]),
// A value representing the amount we'd like to transfer.
Value::u128(123_456_789_012_345),
],
);
// submit the transaction with default params:
let hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Balance transfer extrinsic submitted: {hash}");
// 2. Dynamic constant access (the dynamic equivalent to the fetch_constants example).
let constant_address = subxt::dynamic::constant("Balances", "ExistentialDeposit");
let existential_deposit = api.constants().at(&constant_address)?.to_value()?;
println!("Existential Deposit: {existential_deposit}");
// 3. Dynamic storage access
let storage_address = subxt::dynamic::storage(
"System",
"Account",
vec![
// Something that encodes to an AccountId32 is what we need for the map key here:
Value::from_bytes(&dest),
],
);
let account = api
.storage()
.at_latest()
.await?
.fetch_or_default(&storage_address)
.await?
.to_value()?;
println!("Bob's account details: {account}");
// 4. Dynamic storage iteration (the dynamic equivalent to the fetch_all_accounts example).
let storage_address = subxt::dynamic::storage_root("System", "Account");
let mut iter = api
.storage()
.at_latest()
.await?
.iter(storage_address, 10)
.await?;
while let Some((key, account)) = iter.next().await? {
println!("{}: {}", hex::encode(key), account.to_value()?);
}
Ok(())
}
+60
View File
@@ -0,0 +1,60 @@
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Get events for the latest block:
let events = api.events().at_latest().await?;
// We can dynamically decode events:
println!("Dynamic event details:");
for event in events.iter() {
let event = event?;
let pallet = event.pallet_name();
let variant = event.variant_name();
let field_values = event.field_values()?;
println!("{pallet}::{variant}: {field_values}");
}
// Or we can attempt to statically decode them into the root Event type:
println!("Static event details:");
for event in events.iter() {
let event = event?;
if let Ok(ev) = event.as_root_event::<polkadot::Event>() {
println!("{ev:?}");
} else {
println!("<Cannot decode event>");
}
}
// Or we can attempt to decode them into a specific arbitraty pallet enum
// (We could also set the output type to Value to dynamically decode, here):
println!("Event details for Balances pallet:");
for event in events.iter() {
let event = event?;
if let Ok(ev) = event.as_pallet_event::<polkadot::balances::Event>() {
println!("{ev:?}");
} else {
continue;
}
}
// Or we can look for specific events which match our statically defined ones:
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(ev) = transfer_event {
println!(" - Balance transfer success: value: {:?}", ev.amount);
} else {
println!(" - No balance transfer event found in this block");
}
Ok(())
}
-32
View File
@@ -1,32 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let address = polkadot::storage().system().account_root();
let mut iter = api.storage().at_latest().await?.iter(address, 10).await?;
while let Some((key, account)) = iter.next().await? {
println!("{}: {}", hex::encode(key), account.data.free);
}
Ok(())
}
-35
View File
@@ -1,35 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{OnlineClient, PolkadotConfig};
// Generate the API from a static metadata path.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a constant address to query:
let address = polkadot::constants().balances().existential_deposit();
// Look it up:
let existential_deposit = api.constants().at(&address)?;
println!("Existential Deposit: {existential_deposit}");
Ok(())
}
@@ -1,71 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_core::{sr25519, Pair};
use sp_keyring::AccountKeyring;
use subxt::{utils::AccountId32, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
let active_era_addr = polkadot::storage().staking().active_era();
let era = api
.storage()
.at_latest()
.await?
.fetch(&active_era_addr)
.await?
.unwrap();
println!(
"Staking active era: index: {:?}, start: {:?}",
era.index, era.start
);
let alice_id = AccountKeyring::Alice.to_account_id();
println!(" Alice account id: {alice_id:?}");
// Get Alice' Stash account ID
let alice_stash_id: AccountId32 = sr25519::Pair::from_string("//Alice//stash", None)
.expect("Could not obtain stash signer pair")
.public()
.into();
println!(" Alice//stash account id: {alice_stash_id:?}");
// Map from all locked "stash" accounts to the controller account.
let controller_acc_addr = polkadot::storage().staking().bonded(&alice_stash_id);
let controller_acc = api
.storage()
.at_latest()
.await?
.fetch(&controller_acc_addr)
.await?
.unwrap();
println!(" account controlled by: {controller_acc:?}");
let era_reward_addr = polkadot::storage().staking().eras_reward_points(era.index);
let era_result = api
.storage()
.at_latest()
.await?
.fetch(&era_reward_addr)
.await?;
println!("Era reward points: {era_result:?}");
Ok(())
}
@@ -1,33 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Each individual request will be validated against the static code that was
// used to construct it, if possible. We can also validate the entirety of the
// statically generated code against some client at a given point in time using
// this check. If it fails, then there is some breaking change between the metadata
// used to generate this static code, and the runtime metadata from a node that the
// client is using.
polkadot::validate_codegen(&api)?;
Ok(())
}
-72
View File
@@ -1,72 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.31-3711c6f9b2a.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.31/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// My account.
let signer_account = AccountKeyring::Alice;
let signer_account_id = signer_account.to_account_id();
let signer = PairSigner::new(signer_account.pair());
// Transfer balance to this destination:
let dest = AccountKeyring::Bob.to_account_id();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create the inner balance transfer call.
//
// Note: This call, being manually constructed, will have a specific pallet and call index
// which is determined by the generated code. If you're trying to submit this to a node which
// has the pallets/calls at different indexes, it will fail. See `dynamic_multisig.rs` for a
// workaround in this case which will work regardless of pallet and call indexes.
let inner_tx = polkadot::runtime_types::polkadot_runtime::RuntimeCall::Balances(
polkadot::runtime_types::pallet_balances::pallet::Call::transfer {
dest: dest.into(),
value: 123_456_789_012_345,
},
);
// Now, build an outer call which this inner call will be a part of.
// This sets up the multisig arrangement.
let tx = polkadot::tx().multisig().as_multi(
// threshold
1,
// other signatories
vec![signer_account_id.into()],
// maybe timepoint
None,
// call
inner_tx,
// max weight
polkadot::runtime_types::sp_weights::weight_v2::Weight {
ref_time: 10000000000,
proof_size: 1,
},
);
// Submit the extrinsic with default params:
let encoded = hex::encode(api.tx().call_data(&tx)?);
println!("Call data: {encoded}");
let tx_hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Submitted tx with hash {tx_hash}");
Ok(())
}
-32
View File
@@ -1,32 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let block_number = 1u32;
let block_hash = api.rpc().block_hash(Some(block_number.into())).await?;
if let Some(hash) = block_hash {
println!("Block hash for block number {block_number}: {hash}");
} else {
println!("Block number {block_number} not found.");
}
Ok(())
}
@@ -1,36 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{config::Header, OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = OnlineClient::<PolkadotConfig>::from_url("wss://rpc.polkadot.io:443").await?;
// For non-finalised blocks use `.subscribe_blocks()`
let mut blocks = api.rpc().subscribe_finalized_block_headers().await?;
while let Some(Ok(block)) = blocks.next().await {
println!(
"block number: {} hash:{} parent:{} state root:{} extrinsics root:{}",
block.number,
block.hash(),
block.parent_hash,
block.state_root,
block.extrinsics_root
);
}
Ok(())
}
+31
View File
@@ -0,0 +1,31 @@
use sp_keyring::AccountKeyring;
use subxt::dynamic::Value;
use subxt::{config::PolkadotConfig, OnlineClient};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create a dynamically runtime API payload that calls the
// `AccountNonceApi_account_nonce` function.
let account = AccountKeyring::Alice.to_account_id();
let runtime_api_call = subxt::dynamic::runtime_api_call(
"AccountNonceApi_account_nonce",
vec![Value::from_bytes(account)],
);
// Submit the call to get back a result.
let nonce = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await?;
println!("Account nonce: {:#?}", nonce.to_value());
Ok(())
}
+22
View File
@@ -0,0 +1,22 @@
use subxt::ext::codec::Compact;
use subxt::ext::frame_metadata::RuntimeMetadataPrefixed;
use subxt::{OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Use runtime APIs at the latest block:
let runtime_apis = api.runtime_api().at_latest().await?;
// Ask for metadata and decode it:
let (_, meta): (Compact<u32>, RuntimeMetadataPrefixed) =
runtime_apis.call_raw("Metadata_metadata", None).await?;
println!("{meta:?}");
Ok(())
}
+27
View File
@@ -0,0 +1,27 @@
use sp_keyring::AccountKeyring;
use subxt::{config::PolkadotConfig, OnlineClient};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Create a runtime API payload that calls into
// `AccountNonceApi_account_nonce` function.
let account = AccountKeyring::Alice.to_account_id().into();
let runtime_api_call = polkadot::apis().account_nonce_api().account_nonce(account);
// Submit the call and get back a result.
let nonce = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await;
println!("AccountNonceApi_account_nonce for Alice: {:?}", nonce);
Ok(())
}
-85
View File
@@ -1,85 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::dynamic::Value;
use subxt::{config::PolkadotConfig, OnlineClient};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// In the first part of the example calls are made using the static generated code
// and as a result the returned values are strongly typed.
// Create a runtime API payload that calls into
// `Core_version` function.
let runtime_api_call = polkadot::apis().core().version();
// Submit the runtime API call.
let version = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await;
println!("Core_version: {:?}", version);
// Show the supported metadata versions of the node.
// Calls into `Metadata_metadata_versions` runtime function.
let runtime_api_call = polkadot::apis().metadata().metadata_versions();
// Submit the runtime API call.
let versions = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await?;
println!("Metadata_metadata_versions: {:?}", versions);
// Create a runtime API payload that calls into
// `AccountNonceApi_account_nonce` function.
let account = AccountKeyring::Alice.to_account_id().into();
let runtime_api_call = polkadot::apis().account_nonce_api().account_nonce(account);
// Submit the runtime API call.
let nonce = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await;
println!("AccountNonceApi_account_nonce for Alice: {:?}", nonce);
// Dynamic calls.
let runtime_api_call =
subxt::dynamic::runtime_api_call("Metadata_metadata_versions", Vec::<Value<()>>::new());
let versions = api
.runtime_api()
.at_latest()
.await?
.call(runtime_api_call)
.await?;
println!(
" dynamic Metadata_metadata_versions: {:#?}",
versions.to_value()
);
Ok(())
}
-62
View File
@@ -1,62 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! In some cases we are interested only in the `RuntimeCall` enum (or more generally, only in some
//! runtime types). We can ask `subxt` to generate only runtime types by passing a corresponding
//! flag.
//!
//! Here we present how to correctly create `Block` type for the Polkadot chain.
use sp_core::H256;
use sp_runtime::{
generic,
traits::{BlakeTwo256, Block as _, Header as _},
Digest,
};
use subxt::PolkadotConfig;
#[subxt::subxt(
runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
derive_for_all_types = "Clone, PartialEq, Eq",
runtime_types_only
)]
pub mod polkadot {}
type RuntimeCall = polkadot::runtime_types::polkadot_runtime::RuntimeCall;
type UncheckedExtrinsic = generic::UncheckedExtrinsic<
<PolkadotConfig as subxt::Config>::Address,
RuntimeCall,
<PolkadotConfig as subxt::Config>::Signature,
// Usually we are not interested in `SignedExtra`.
(),
>;
type Header = generic::Header<u32, BlakeTwo256>;
type Block = generic::Block<Header, UncheckedExtrinsic>;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Although we could build an online client, we do not have access to the full runtime API. For
// that, we would have to specify `runtime_types_only = false` (or just skipping it).
//
// let api = subxt::OnlineClient::<PolkadotConfig>::new().await?;
// let address = polkadot::constants().balances().existential_deposit(); <- this won't compile!
let polkadot_header = Header::new(
41,
H256::default(),
H256::default(),
H256::default(),
Digest::default(),
);
let polkadot_block = Block::new(polkadot_header, vec![]);
println!("{polkadot_block:?}");
Ok(())
}
@@ -0,0 +1,30 @@
use subxt::{
config::{substrate::SubstrateExtrinsicParams, Config, SubstrateConfig},
OnlineClient,
};
/// Define a custom config type (see the `subxt::config::Config` docs for
/// more information about each type):
enum MyConfig {}
impl Config for MyConfig {
// This is different from the default `u32`:
type Index = u64;
// We can point to the default types if we don't need to change things:
type Hash = <SubstrateConfig as Config>::Hash;
type Hasher = <SubstrateConfig as Config>::Hasher;
type Header = <SubstrateConfig as Config>::Header;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Signature = <SubstrateConfig as Config>::Signature;
// ExtrinsicParams makes use of the index type, so we need to tweak it
// too to align with our modified index type, above:
type ExtrinsicParams = SubstrateExtrinsicParams<Self>;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client which uses the custom config:
let _api = OnlineClient::<MyConfig>::new().await?;
Ok(())
}
@@ -1,7 +1,3 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use std::{
fmt::Write,
pin::Pin,
@@ -64,13 +60,8 @@ impl RpcClientT for MyLoggingClient {
}
}
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Instantiate our replacement RPC client.
let log = Arc::default();
let rpc_client = MyLoggingClient {
+37
View File
@@ -0,0 +1,37 @@
use subxt::ext::codec::Decode;
use subxt::ext::frame_metadata::RuntimeMetadataPrefixed;
use subxt::metadata::Metadata;
use subxt::utils::H256;
use subxt::{config::PolkadotConfig, OfflineClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// We need to obtain the following details for an OfflineClient to be instantiated:
// 1. Genesis hash (RPC call: chain_getBlockHash(0)):
let genesis_hash = {
let h = "91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3";
let bytes = hex::decode(h).unwrap();
H256::from_slice(&bytes)
};
// 2. A runtime version (system_version constant on a Substrate node has these):
let runtime_version = subxt::rpc::types::RuntimeVersion {
spec_version: 9370,
transaction_version: 20,
other: Default::default(),
};
// 3. Metadata (I'll load it from the downloaded metadata, but you can use
// `subxt metadata > file.scale` to download it):
let metadata = {
let bytes = std::fs::read("./artifacts/polkadot_metadata.scale").unwrap();
let metadata = RuntimeMetadataPrefixed::decode(&mut &*bytes).unwrap();
Metadata::try_from(metadata).unwrap()
};
// Create an offline client using the details obtained above:
let _api = OfflineClient::<PolkadotConfig>::new(genesis_hash, runtime_version, metadata);
Ok(())
}
+29
View File
@@ -0,0 +1,29 @@
use sp_keyring::AccountKeyring;
use subxt::{OnlineClient, PolkadotConfig};
// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a storage query to access account information.
let account = AccountKeyring::Alice.to_account_id().into();
let storage_query = polkadot::storage().system().account(&account);
// Use that query to `fetch` a result. This returns an `Option<_>`, which will be
// `None` if no value exists at the given address. You can also use `fetch_default`
// where applicable, which will return the default value if none exists.
let result = api
.storage()
.at_latest()
.await?
.fetch(&storage_query)
.await?;
println!("Alice has free balance: {}", result.unwrap().data.free);
Ok(())
}
@@ -0,0 +1,27 @@
use sp_keyring::AccountKeyring;
use subxt::dynamic::{At, Value};
use subxt::{OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a dynamic storage query to access account information.
let account = AccountKeyring::Alice.to_account_id();
let storage_query =
subxt::dynamic::storage("System", "Account", vec![Value::from_bytes(account)]);
// Use that query to `fetch` a result. Because the query is dynamic, we don't know what the result
// type will be either, and so we get a type back that can be decoded into a dynamic Value type.
let result = api
.storage()
.at_latest()
.await?
.fetch(&storage_query)
.await?;
let value = result.unwrap().to_value()?;
println!("Alice has free balance: {:?}", value.at("data").at("free"));
Ok(())
}
+15 -94
View File
@@ -1,108 +1,29 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use codec::{Decode, Encode};
use subxt::{OnlineClient, PolkadotConfig};
// Generate an interface that we can use from the node's metadata.
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Example 1. Iterate over (keys, value) using the storage client.
// This is the standard and most ergonomic approach.
{
let key_addr = polkadot::storage().xcm_pallet().version_notifiers_root();
// Build a storage query to iterate over account information.
let storage_query = polkadot::storage().system().account_root();
let mut iter = api.storage().at_latest().await?.iter(key_addr, 10).await?;
// Get back an iterator of results (here, we are fetching 10 items at
// a time from the node, but we always iterate over oen at a time).
let mut results = api
.storage()
.at_latest()
.await?
.iter(storage_query, 10)
.await?;
println!("\nExample 1. Obtained keys:");
while let Some((key, value)) = iter.next().await? {
println!("Key: 0x{}", hex::encode(key));
println!(" Value: {value}");
}
}
// Example 2. Iterate over fetched keys manually. Here, you forgo any static type
// safety and work directly with the bytes on either side.
{
let key_addr = polkadot::storage().xcm_pallet().version_notifiers_root();
// Fetch at most 10 keys from below the prefix XcmPallet' VersionNotifiers.
let keys = api
.storage()
.at_latest()
.await?
.fetch_keys(&key_addr.to_root_bytes(), 10, None)
.await?;
println!("Example 2. Obtained keys:");
for key in keys.iter() {
println!("Key: 0x{}", hex::encode(key));
if let Some(storage_data) = api.storage().at_latest().await?.fetch_raw(&key.0).await? {
// We know the return value to be `QueryId` (`u64`) from inspecting either:
// - polkadot code
// - polkadot.rs generated file under `version_notifiers()` fn
// - metadata in json format
let value = u64::decode(&mut &*storage_data)?;
println!(" Value: {value}");
}
}
}
// Example 3. Custom iteration over double maps. Here, we manually append one lookup
// key to the root and just iterate over the values underneath that.
{
let key_addr = polkadot::storage().xcm_pallet().version_notifiers_root();
// Obtain the root bytes (`twox_128("XcmPallet") ++ twox_128("VersionNotifiers")`).
let mut query_key = key_addr.to_root_bytes();
// We know that the first key is a u32 (the `XcmVersion`) and is hashed by twox64_concat.
// twox64_concat is just the result of running the twox_64 hasher on some value and concatenating
// the value itself after it:
query_key.extend(subxt::ext::sp_core::twox_64(&2u32.encode()));
query_key.extend(&2u32.encode());
// The final query key is essentially the result of:
// `twox_128("XcmPallet") ++ twox_128("VersionNotifiers") ++ twox_64(scale_encode(2u32)) ++ scale_encode(2u32)`
println!("\nExample 3\nQuery key: 0x{}", hex::encode(&query_key));
let keys = api
.storage()
.at_latest()
.await?
.fetch_keys(&query_key, 10, None)
.await?;
println!("Obtained keys:");
for key in keys.iter() {
println!("Key: 0x{}", hex::encode(key));
if let Some(storage_data) = api.storage().at_latest().await?.fetch_raw(&key.0).await? {
// We know the return value to be `QueryId` (`u64`) from inspecting either:
// - polkadot code
// - polkadot.rs generated file under `version_notifiers()` fn
// - metadata in json format
let value = u64::decode(&mut &storage_data[..])?;
println!(" Value: {value}");
}
}
while let Some((key, value)) = results.next().await? {
println!("Key: 0x{}", hex::encode(&key));
println!("Value: {:?}", value);
}
Ok(())
@@ -0,0 +1,25 @@
use subxt::{OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new API client, configured to talk to Polkadot nodes.
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Build a dynamic storage query to iterate account information.
let storage_query = subxt::dynamic::storage_root("System", "Account");
// Use that query to return an iterator over the results.
let mut results = api
.storage()
.at_latest()
.await?
.iter(storage_query, 10)
.await?;
while let Some((key, value)) = results.next().await? {
println!("Key: 0x{}", hex::encode(&key));
println!("Value: {:?}", value.to_value()?);
}
Ok(())
}
-166
View File
@@ -1,166 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
simple_transfer().await?;
simple_transfer_separate_events().await?;
handle_transfer_events().await?;
Ok(())
}
/// This is the highest level approach to using this API. We use `wait_for_finalized_success`
/// to wait for the transaction to make it into a finalized block, and also ensure that the
/// transaction was successful according to the associated events.
async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let balance_transfer = api
.tx()
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?
.wait_for_finalized_success()
.await?;
let transfer_event = balance_transfer.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
Ok(())
}
/// This is very similar to `simple_transfer`, except to show that we can handle
/// waiting for the transaction to be finalized separately from obtaining and checking
/// for success on the events.
async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let balance_transfer = api
.tx()
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?
.wait_for_finalized()
.await?;
// Now we know it's been finalized, we can get hold of a couple of
// details, including events. Calling `wait_for_finalized_success` is
// equivalent to calling `wait_for_finalized` and then `wait_for_success`:
let _events = balance_transfer.wait_for_success().await?;
// Alternately, we could just `fetch_events`, which grabs all of the events like
// the above, but does not check for success, and leaves it up to you:
let events = balance_transfer.fetch_events().await?;
let failed_event = events.find_first::<polkadot::system::events::ExtrinsicFailed>()?;
if let Some(_ev) = failed_event {
// We found a failed event; the transfer didn't succeed.
println!("Balance transfer failed");
} else {
// We didn't find a failed event; the transfer succeeded. Find
// more details about it to report..
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
}
Ok(())
}
/// If we need more visibility into the state of the transaction, we can also ditch
/// `wait_for_finalized` entirely and stream the transaction progress events, handling
/// them more manually.
async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let mut balance_transfer_progress = api
.tx()
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?;
while let Some(ev) = balance_transfer_progress.next().await {
let ev = ev?;
use subxt::tx::TxStatus::*;
// Made it into a block, but not finalized.
if let InBlock(details) = ev {
println!(
"Transaction {:?} made it into block {:?}",
details.extrinsic_hash(),
details.block_hash()
);
let events = details.wait_for_success().await?;
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer is now in block (but not finalized): {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
}
// Finalized!
else if let Finalized(details) = ev {
println!(
"Transaction {:?} is finalized in block {:?}",
details.extrinsic_hash(),
details.block_hash()
);
let events = details.wait_for_success().await?;
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(event) = transfer_event {
println!("Balance transfer success: {event:?}");
} else {
println!("Failed to find Balances::Transfer Event");
}
}
// Report other statuses we see.
else {
println!("Current transaction status: {ev:?}");
}
}
Ok(())
}
@@ -1,88 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{tx::PairSigner, OnlineClient, PolkadotConfig};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
pub mod polkadot {}
/// Subscribe to all events, and then manually look through them and
/// pluck out the events that we care about.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to (in this case, finalized) blocks.
let mut block_sub = api.blocks().subscribe_finalized().await?;
// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn({
let api = api.clone();
async move {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut transfer_amount = 1_000_000_000;
// Make small balance transfers from Alice to Bob in a loop:
loop {
let transfer_tx = polkadot::tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount);
api.tx()
.sign_and_submit_default(&transfer_tx, &signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
}
});
// Get each finalized block as it arrives.
while let Some(block) = block_sub.next().await {
let block = block?;
// Ask for the events for this block.
let events = block.events().await?;
let block_hash = block.hash();
// We can dynamically decode events:
println!(" Dynamic event details: {block_hash:?}:");
for event in events.iter() {
let event = event?;
let is_balance_transfer = event
.as_event::<polkadot::balances::events::Transfer>()?
.is_some();
let pallet = event.pallet_name();
let variant = event.variant_name();
println!(" {pallet}::{variant} (is balance transfer? {is_balance_transfer})");
}
// Or we can find the first transfer event, ignoring any others:
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
if let Some(ev) = transfer_event {
println!(" - Balance transfer success: value: {:?}", ev.amount);
} else {
println!(" - No balance transfer event found in this block");
}
}
Ok(())
}
@@ -1,36 +0,0 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! To run this example, a local polkadot node should be running. Example verified against polkadot v0.9.28-9ffe6e9e3da.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.28/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use std::time::Duration;
use subxt::{OnlineClient, PolkadotConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Start a new tokio task to perform the runtime updates while
// utilizing the API for other use cases.
let update_client = api.updater();
tokio::spawn(async move {
let result = update_client.perform_runtime_updates().await;
println!("Runtime update failed with result={result:?}");
});
// If this client is kept in use a while, it'll update its metadata and such
// as needed when the node it's pointed at updates.
tokio::time::sleep(Duration::from_secs(10_000)).await;
Ok(())
}
+1 -107
View File
@@ -2,113 +2,6 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Generate a strongly typed API for interacting with a Substrate runtime from its metadata.
//!
//! Usage:
//!
//! Download metadata from a running Substrate node using `subxt-cli`:
//!
//! ```bash
//! subxt metadata > polkadot_metadata.scale
//! ```
//!
//! Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file.
//!
//! ```ignore
//! #[subxt::subxt(
//! runtime_metadata_path = "polkadot_metadata.scale",
//! )]
//! pub mod polkadot {}
//! ```
//!
//! The `subxt` macro will populate the annotated module with all of the methods and types required
//! for submitting extrinsics and reading from storage for the given runtime.
//!
//! ## Substituting types
//!
//! In order to replace a generated type by a user-defined type, use `substitute_type`:
//!
//! ```ignore
//! #[subxt::subxt(
//! runtime_metadata_path = "polkadot_metadata.scale",
//! substitute_type(path = "sp_arithmetic::per_things::Perbill", with = "sp_runtime::Perbill")
//! )]
//! pub mod polkadot {}
//! ```
//!
//! This will replace the generated type and any usages with the specified type at the `use` import.
//! It is useful for using custom decoding for specific types, or to provide a type with foreign
//! trait implementations, or other specialized functionality.
//! ## Custom Derives
//!
//! By default all generated types are annotated with `scale::Encode` and `scale::Decode` derives.
//! However when using the generated types in the client, they may require additional derives to be
//! useful.
//!
//! ### Adding derives for all types
//!
//! Add `derive_for_all_types` with a comma separated list of the derives to apply to *all* types
//!
//! ```ignore
//! #[subxt::subxt(
//! runtime_metadata_path = "polkadot_metadata.scale",
//! derive_for_all_types = "Eq, PartialEq"
//! )]
//! pub mod polkadot {}
//! ```
//!
//! ### Adding derives for specific types
//!
//! Add `derive_for_type` for each specific type with a comma separated list of the derives to
//! apply for that type only.
//!
//! ```ignore
//! #[subxt::subxt(
//! runtime_metadata_path = "polkadot_metadata.scale",
//! derive_for_all_types = "Eq, PartialEq",
//! derive_for_type(path = "frame_support::PalletId", derive = "Ord, PartialOrd"),
//! derive_for_type(path = "sp_runtime::ModuleError", derive = "Hash"),
//! )]
//! pub mod polkadot {}
//! ```
//!
//! ### Custom crate path
//!
//! In order to specify a custom crate path to be used for the code generation:
//!
//! ```ignore
//! #[subxt::subxt(crate = "crate::path::to::subxt")]
//! pub mod polkadot {}
//! ```
//!
//! By default the path `::subxt` is used.
//!
//! ### Expose documentation
//!
//! In order to expose the documentation from the runtime metadata on the generated
//! code, users must specify the `generate_docs` flag:
//!
//! ```ignore
//! #[subxt::subxt(generate_docs)]
//! pub mod polkadot {}
//! ```
//!
//! By default the documentation is not generated.
//!
//! ### Runtime types generation
//!
//! In some cases, you may be interested only in the runtime types, like `RuntimeCall` enum. You can
//! limit code generation to just `runtime_types` module with `runtime_types_only` flag:
//!
//! ```ignore
//! #[subxt::subxt(runtime_types_only)]
//! // or equivalently
//! #[subxt::subxt(runtime_types_only = true)]
//! ```
#![deny(unused_crate_dependencies)]
extern crate proc_macro;
use std::str::FromStr;
@@ -174,6 +67,7 @@ struct SubstituteType {
with: syn::Path,
}
// Note: docs for this are in the subxt library; don't add any here as they will be appended.
#[proc_macro_attribute]
#[proc_macro_error]
pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream {
+1 -1
View File
@@ -7,5 +7,5 @@
mod block_types;
mod blocks_client;
pub use block_types::{Block, Extrinsic, ExtrinsicEvents};
pub use block_types::{Block, BlockBody, Extrinsic, ExtrinsicEvents};
pub use blocks_client::{subscribe_to_block_headers_filling_in_gaps, BlocksClient};
+96
View File
@@ -0,0 +1,96 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
// Dev note; I used the following command to normalize and wrap comments:
// rustfmt +nightly --config wrap_comments=true,comment_width=100,normalize_comments=true subxt/src/book/mod.rs
// It messed up comments in code blocks though, so be prepared to go and fix those.
//! # The Subxt Guide
//!
//! Subxt is a library for interacting with Substrate based nodes. It has a focus on **sub**mitting
//! e**xt**rinsics, hence the name, however it's also capable of reading blocks, storage, events and
//! constants from a node. The aim of this guide is to explain key concepts and get you started with
//! using Subxt.
//!
//! 1. [Features](#features-at-a-glance)
//! 2. [Limitations](#limitations)
//! 3. [Quick start](#quick-start)
//! 4. [Usage](#usage)
//!
//! ## Features at a glance
//!
//! Here's a quick overview of the features that Subxt has to offer:
//!
//! - Subxt allows you to generate a static, type safe interface to a node given some metadata; this
//! allows you to catch many errors at compile time rather than runtime.
//! - Subxt also makes heavy use of node metadata to encode/decode the data sent to/from it. This
//! allows it to target almost any node which can output the correct metadata, and allows it some
//! flexibility in encoding and decoding things to account for cross-node differences.
//! - Subxt has a pallet-oriented interface, meaning that code you write to talk to some pallet on
//! one node will often "Just Work" when pointed at different nodes that use the same pallet.
//! - Subxt can work offline; you can generate and sign transactions, access constants from node
//! metadata and more, without a network connection. This is all checked at compile time, so you
//! can be certain it won't try to establish a network connection if you don't want it to.
//! - Subxt can forego the statically generated interface and build transactions, storage queries
//! and constant queries using data provided at runtime, rather than queries constructed
//! statically.
//! - Subxt can be compiled to WASM to run in the browser, allowing it to back Rust based browser
//! apps, or even bind to JS apps.
//!
//! ## Limitations
//!
//! In various places, you can provide a block hash to access data at a particular block, for
//! instance:
//!
//! - [`crate::storage::StorageClient::at`]
//! - [`crate::events::EventsClient::at`]
//! - [`crate::blocks::BlocksClient::at`]
//! - [`crate::runtime_api::RuntimeApiClient::at`]
//!
//! However, Subxt is (by default) only capable of properly working with blocks that were produced
//! after the most recent runtime update. This is because it uses the most recent metadata given
//! back by a node to encode and decode things. It's possible to decode older blocks produced by a
//! runtime that emits compatible (currently, V14) metadata by manually setting the metadata used by
//! the client using [`crate::client::OnlineClient::set_metadata()`].
//!
//! Subxt does not support working with blocks produced prior to the runtime update that introduces
//! V14 metadata. It may have some success decoding older blocks using newer metadata, but may also
//! completely fail to do so.
//!
//! ## Quick start
//!
//! Here is a simple but complete example of using Subxt to transfer some tokens from the example
//! accounts, Alice to Bob:
//!
//! ```rust,ignore
#![doc = include_str!("../../../examples/examples/balance_transfer_basic.rs")]
//! ```
//!
//! This example assumes that a Polkadot node is running locally (Subxt endeavors to support all
//! recent releases). Typically, to use Subxt to talk to some custom Substrate node (for example a
//! parachain node), you'll want to:
//!
//! 1. [Generate an interface](setup::codegen).
//! 2. [Configure and instantiate the client](setup::client).
//!
//! Follow the above links to learn more about each step.
//!
//! ## Usage
//!
//! Once Subxt is configured, the next step is interacting with a node. Follow the links
//! below to learn more about how to use Subxt for each of the following things:
//!
//! - [Extrinsics](usage::extrinsics): Subxt can build and submit extrinsics, wait until they are in
//! blocks, and retrieve the associated events.
//! - [Storage](usage::storage): Subxt can query the node storage.
//! - [Events](usage::events): Subxt can read the events emitted for recent blocks.
//! - [Constants](usage::constants): Subxt can access the constant values stored in a node, which
//! remain the same for a given runtime version.
//! - [Blocks](usage::blocks): Subxt can load recent blocks or subscribe to new/finalized blocks,
//! reading the extrinsics, events and storage at these blocks.
//! - [Runtime APIs](usage::runtime_apis): Subxt can make calls into pallet runtime APIs to retrieve
//! data.
//!
pub mod setup;
pub mod usage;
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Configuring the Subxt client
//!
//! Subxt ships with two clients, an [offline client](crate::client::OfflineClient) and an [online
//! client](crate::client::OnlineClient). These are backed by the traits
//! [`crate::client::OfflineClientT`] and [`crate::client::OnlineClientT`], so in theory it's
//! possible for users to implement their own clients, although this isn't generally expected.
//!
//! Both clients are generic over a [`crate::config::Config`] trait, which is the way that we give
//! the client certain information about how to interact with a node that isn't otherwise available
//! or possible to include in the node metadata. Subxt ships out of the box with two default
//! implementations:
//!
//! - [`crate::config::PolkadotConfig`] for talking to Polkadot nodes, and
//! - [`crate::config::SubstrateConfig`] for talking to generic nodes built with Substrate.
//!
//! The latter will generally work in many cases, but will need modifying if the chain you'd like to
//! connect to has altered any of the details mentioned in [the trait](`crate::config::Config`).
//!
//! In the case of the [`crate::OnlineClient`], we have a few options to instantiate it:
//!
//! - [`crate::OnlineClient::new()`] to connect to a node running locally.
//! - [`crate::OnlineClient::from_url()`] to connect to a node at a specific URL.
//! - [`crate::OnlineClient::from_rpc_client()`] to instantiate the client with a custom RPC
//! implementation.
//!
//! The latter accepts anything that implements the low level [`crate::rpc::RpcClientT`] trait; this
//! allows you to decide how Subxt will attempt to talk to a node if you'd prefer something other
//! than the provided interfaces.
//!
//! ## Examples
//!
//! Defining some custom config based off the default Substrate config:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_custom_config.rs")]
//! ```
//! Writing a custom [`crate::rpc::RpcClientT`] implementation:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_custom_rpc.rs")]
//! ```
//! Creating an [`crate::OfflineClient`]:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/setup_client_offline.rs")]
//! ```
//!
+66
View File
@@ -0,0 +1,66 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Generating an interface
//!
//! The simplest way to use Subxt is to generate an interface to a chain that you'd like to interact
//! with. This generated interface allows you to build transactions and construct queries to access
//! data while leveraging the full type safety of the Rust compiler.
//!
//! ## The `#[subxt]` macro
//!
//! The most common way to generate the interface is to use the [`#[subxt]`](crate::subxt) macro.
//! Using this macro looks something like:
//!
//! ```rust,no_run
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//! ```
//!
//! The macro takes a path to some node metadata, and uses that to generate the interface you'll use
//! to talk to it. [Go here](crate::subxt) to learn more about the options available to the macro.
//!
//! To obtain this metadata you'll need for the above, you can use the `subxt` CLI tool to download it
//! from a node. The tool can be installed via `cargo`:
//!
//! ```shell
//! cargo install subxt-cli
//! ```
//!
//! And then it can be used to fetch metadata and save it to a file:
//!
//! ```shell
//! # Download and save all of the metadata:
//! subxt metadata > metadata.scale
//! # Download and save only the pallets you want to generate an interface for:
//! subxt metadata --pallets Balances,System > metadata.scale
//! ```
//!
//! Explicitly specifying pallets will cause the tool to strip out all unnecessary metadata and type
//! information, making the bundle much smaller in the event that you only need to generate an
//! interface for a subset of the available pallets on the node.
//!
//! ## The CLI tool
//!
//! Using the [`#[subxt]`](crate::subxt) macro carries some downsides:
//!
//! - Using it to generate an interface will have a small impact on compile times (though much less of
//! one if you only need a few pallets).
//! - IDE support for autocompletion and documentation when using the macro interface can be poor.
//! - It's impossible to manually look at the generated code to understand and debug things.
//!
//! If these are an issue, you can manually generate the same code that the macro generates under the hood
//! by using the `subxt codegen` command:
//!
//! ```shell
//! # Install the CLI tool if you haven't already:
//! cargo install subxt-cli
//! # Generate and format rust code, saving it to `interface.rs`:
//! subxt codegen | rustfmt > interface.rs
//! ```
//!
//! Use `subxt codegen --help` for more options; many of the options available via the macro are
//! also available via the CLI tool, such as the ability to substitute generated types for others,
//! or strip out docs from the generated code.
//!
+13
View File
@@ -0,0 +1,13 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This modules contains details on setting up Subxt:
//!
//! - [Codegen](codegen)
//! - [Client](client)
//!
//! Alternately, [go back](super).
pub mod client;
pub mod codegen;
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Blocks
//!
//! The [blocks API](crate::blocks::BlocksClient) in Subxt unifies many of the other interfaces, and
//! allows you to:
//!
//! - Access information about specific blocks (see [`crate::blocks::BlocksClient::at()`] and
//! [`crate::blocks::BlocksClient::at_latest()`]).
//! - Subscribe to [all](crate::blocks::BlocksClient::subscribe_all()),
//! [best](crate::blocks::BlocksClient::subscribe_best()) or
//! [finalized](crate::blocks::BlocksClient::subscribe_finalized()) blocks as they are produced.
//! Prefer to subscribe to finalized blocks unless you know what you're doing.
//!
//! In either case, you'll end up with [`crate::blocks::Block`]'s, from which you can access various
//! information about the block, such a the [header](crate::blocks::Block::header()), [block
//! number](crate::blocks::Block::number()) and [body](crate::blocks::Block::body()).
//! [`crate::blocks::Block`]'s also provide shortcuts to other Subxt APIs that will operate at the
//! given block:
//!
//! - [storage](crate::blocks::Block::storage()),
//! - [events](crate::blocks::Block::events())
//! - [runtime APIs](crate::blocks::Block::runtime_api())
//!
//! ## Example
//!
//! Given a block, you can [download the block body](crate::blocks::Block::body()) and iterate over
//! the extrinsics stored within it using [`crate::blocks::BlockBody::extrinsics()`].
//!
//! Here's an example in which we subscribe to blocks and print a bunch of information about each
//! one:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/blocks_subscribing.rs")]
//! ```
//!
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Constants
//!
//! There are various constants stored in a node; the types and values of these are defined in a
//! runtime, and can only change when the runtime is updated. Much like [`super::storage`], we can
//! query these using Subxt by taking the following steps:
//!
//! 1. [Constructing a constant query](#constructing-a-query).
//! 2. [Submitting the query to get back the associated value](#submitting-it).
//!
//! ## Constructing a constant query
//!
//! We can use the statically generated interface to build constant queries:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! let constant_query = polkadot::constants().system().block_length();
//! ```
//!
//! Alternately, we can dynamically construct a constant query:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//! use subxt::dynamic::Value;
//!
//! let account = AccountKeyring::Alice.to_account_id();
//! let storage_query = subxt::dynamic::constant("System", "BlockLength");
//! ```
//!
//! Static queries also have a static return type, so the constant is decoded appropriately. In
//! addition, they are validated at runtime to ensure that they align with the current node state.
//! Dynamic queries must be decoded into some static type manually, or into the dynamic
//! [`crate::dynamic::Value`] type.
//!
//! ## Submitting it
//!
//! Constant queries are handed to Subxt via [`crate::constants::ConstantsClient::at()`]. It's worth
//! noting that constant values are pulled directly out of the node metadata which Subxt has
//! already acquired, and so this function requires no network access and is available from a
//! [`crate::OfflineClient`].
//!
//! Here's an example using a static query:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/constants_static.rs")]
//! ```
//! And here's one using a dynamic query:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/constants_dynamic.rs")]
//! ```
//!
+50
View File
@@ -0,0 +1,50 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Events
//!
//! In the process of adding extrinsics to a block, they are executed. When extrinsics are executed,
//! they normally produce events describing what's happening (at the very least, an event dictating whether
//! the extrinsic has succeeded or failed). The node may also emit some events of its own as the block is
//! processed.
//!
//! Events live in a single location in node storage which is overwritten at each block. Normal nodes tend to
//! keep a snapshot of the state at a small number of previous blocks, so you can sometimes access
//! older events by using [`crate::events::EventsClient::at()`] and providing an older block hash.
//!
//! When we submit extrinsics using Subxt, methods like [`crate::tx::TxProgress::wait_for_finalized_success()`]
//! return [`crate::blocks::ExtrinsicEvents`], which can be used to iterate and inspect the events produced
//! for a specific extrinsic. We can also access _all_ of the events produced in a single block using one of these
//! two interfaces:
//!
//! ```rust,no_run
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use subxt::client::OnlineClient;
//! use subxt::config::PolkadotConfig;
//!
//! // Create client:
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
//!
//! // Get events from the latest block (use .at() to specify a block hash):
//! let events = client.blocks().at_latest().await?.events().await?;
//! // We can use this shorthand too:
//! let events = client.events().at_latest().await?;
//! # Ok(())
//! # }
//! ```
//!
//! Once we've loaded our events, we can iterate all events or search for specific events via
//! methods like [`crate::events::Events::iter()`] and [`crate::events::Events::find()`]. See
//! [`crate::events::Events`] and [`crate::events::EventDetails`] for more information.
//!
//! ## Example
//!
//! Here's an example which puts this all together:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/events.rs")]
//! ```
//!
+175
View File
@@ -0,0 +1,175 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Extrinsics
//!
//! Extrinsics define function calls and their parameters, and are the only way that you can change
//! the state of the blockchain. Submitting extrinsics to a node is one of the core features of
//! Subxt, and generally consists of the following steps:
//!
//! 1. [Constructing an extrinsic payload to submit](#constructing-an-extrinsic-payload).
//! 2. [Signing it](#signing-it).
//! 3. [Submitting it (optionally with some additional parameters)](#submitting-it).
//!
//! We'll look at each of these steps in turn.
//!
//! > As a side note, an _extrinsic_ is anything that can be added to a block, and a _transaction_
//! > is an extrinsic that is submitted from a particular user (and is therefore also signed). Subxt
//! > can construct unsigned extrinsics, but overwhelmingly you'll need to sign them, and so the
//! > documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably.
//!
//! ## Constructing an extrinsic payload
//!
//! We can use the statically generated interface to build extrinsic payloads:
//!
//! ```rust,no_run
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! let remark = "Hello there".as_bytes().to_vec();
//! let extrinsic_payload = polkadot::tx().system().remark(remark);
//! ```
//!
//! > If you're not sure what types to import and use to build a given payload, you can use the
//! > `subxt` CLI tool to generate the interface by using something like `subxt codegen | rustfmt >
//! > interface.rs`, to see what types and things are available (or even just to use directly
//! > instead of the [`#[subxt]`](crate::subxt) macro).
//!
//! Alternately, we can dynamically construct an extrinsic payload. This will not be type checked or
//! validated until it's submitted:
//!
//! ```rust,no_run
//! use subxt::dynamic::Value;
//!
//! let extrinsic_payload = subxt::dynamic::tx("System", "remark", vec![
//! Value::from_bytes("Hello there")
//! ]);
//! ```
//!
//! The [`crate::dynamic::Value`] type is a dynamic type much like a `serde_json::Value` but instead
//! represents any type of data that can be SCALE encoded or decoded. It can be serialized,
//! deserialized and parsed from/to strings.
//!
//! A valid extrinsic payload is just something that implements the [`crate::tx::TxPayload`] trait;
//! you can implement this trait on your own custom types if the built-in ones are not suitable for
//! your needs.
//!
//! ## Signing it
//!
//! You'll normally need to sign an extrinsic to prove that it originated from an account that you
//! control. To do this, you will typically first create an [`crate::tx::Signer`], which tells Subxt
//! who the extrinsic is from, and takes care of signing the relevant details to prove this.
//!
//! Subxt provides a [`crate::tx::PairSigner`] which implements this trait (if the
//! `substrate-compat` feature is enabled) which accepts any valid [`sp_core::Pair`] and uses that
//! to sign transactions:
//!
//! ```rust
//! use subxt::tx::PairSigner;
//! use sp_core::Pair;
//! use subxt::config::PolkadotConfig;
//!
//! // Get hold of a `Signer` given a test account:
//! let pair = sp_keyring::AccountKeyring::Alice.pair();
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
//!
//! // Or generate an `sr25519` keypair to use:
//! let (pair, _, _) = sp_core::sr25519::Pair::generate_with_phrase(Some("password"));
//! let signer = PairSigner::<PolkadotConfig,_>::new(pair);
//! ```
//!
//! See the [`sp_core::Pair`] docs for more ways to generate them.
//!
//! If this isn't suitable/available, you can either implement [`crate::tx::Signer`] yourself to use
//! custom signing logic, or you can use some external signing logic, like so:
//!
//! ```rust,no_run
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use subxt::client::OnlineClient;
//! use subxt::config::PolkadotConfig;
//! use subxt::dynamic::Value;
//!
//! // Create client:
//! let client = OnlineClient::<PolkadotConfig>::new().await?;
//!
//! // Create a dummy extrinsic payload to sign:
//! let payload = subxt::dynamic::tx("System", "remark", vec![
//! Value::from_bytes("Hello there")
//! ]);
//!
//! // Construct the extrinsic but don't sign it. You need to provide the nonce
//! // here, or can use `create_partial_signed` to fetch the correct nonce.
//! let partial_extrinsic = client.tx().create_partial_signed_with_nonce(
//! &payload,
//! 0,
//! Default::default()
//! )?;
//!
//! // Fetch the payload that needs to be signed:
//! let signer_payload = partial_extrinsic.signer_payload();
//!
//! // ... At this point, we can hand off the `signer_payload` to be signed externally.
//! // Ultimately we need to be given back a `signature` (or really, anything
//! // that can be SCALE encoded) and an `address`:
//! let signature;
//! let address;
//! # use subxt::tx::Signer;
//! # let pair = sp_keyring::AccountKeyring::Alice.pair();
//! # let signer = subxt::tx::PairSigner::<PolkadotConfig,_>::new(pair);
//! # signature = signer.sign(&signer_payload);
//! # address = signer.address();
//!
//! // Now we can build an extrinsic, which one can call `submit` or `submit_and_watch`
//! // on to submit to a node and optionally watch the status.
//! let extrinsic = partial_extrinsic.sign_with_address_and_signature(
//! &address,
//! &signature
//! );
//! # Ok(())
//! # }
//! ```
//!
//! ## Submitting it
//!
//! Once we are able to sign the extrinsic, we need to submit it.
//!
//! ### The high level API
//!
//! The highest level approach to doing this is to call
//! [`crate::tx::TxClient::sign_and_submit_then_watch_default`]. This hands back a
//! [`crate::tx::TxProgress`] struct which will monitor the transaction status. We can then call
//! [`crate::tx::TxProgress::wait_for_finalized_success()`] to wait for this transaction to make it
//! into a finalized block, check for an `ExtrinsicSuccess` event, and then hand back the events for
//! inspection. This looks like:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_basic.rs")]
//! ```
//! ### Providing extrinsic parameters
//!
//! If you'd like to provide extrinsic parameters (such as mortality), you can use
//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead, and provide parameters for your
//! chain:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_with_params.rs")]
//! ```
//! This example doesn't wait for the extrinsic to be included in a block; it just submits it and
//! hopes for the best!
//!
//! ### Custom handling of transaction status updates
//!
//! If you'd like more control or visibility over exactly which status updates are being emitted for
//! the transaction, you can monitor them as they are emitted and react however you choose:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/balance_transfer_status_stream.rs")]
//! ```
//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and
//! [`crate::tx::TxInBlock`] for more options.
//!
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! This modules contains examples of using Subxt; follow the links for more:
//!
//! - [Extrinsics](extrinsics)
//! - [Storage](storage)
//! - [Events](events)
//! - [Constants](constants)
//! - [Blocks](blocks)
//! - [Runtime APIs](runtime_apis)
//!
//! Alternately, [go back](super).
pub mod blocks;
pub mod constants;
pub mod events;
pub mod extrinsics;
pub mod runtime_apis;
pub mod storage;
+81
View File
@@ -0,0 +1,81 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Runtime API interface
//!
//! The Runtime API interface allows Subxt to call runtime APIs exposed by certain pallets in order
//! to obtain information. Much like [`super::storage`] and [`super::extrinsics`], Making a runtime
//! call to a node and getting the response back takes the following steps:
//!
//! 1. [Constructing a runtime call](#constructing-a-runtime-call)
//! 2. [Submitting it to get back the response](#submitting-it)
//!
//! **Note:** Runtime APIs are only available when using V15 metadata, which is currently unstable.
//! You'll need to use `subxt metadata --version unstable` command to download the unstable V15 metadata,
//! and activate the `unstable-metadata` feature in Subxt for it to also use this metadata from a node. The
//! metadata format is unstable because it may change and break compatibility with Subxt at any moment, so
//! use at your own risk.
//!
//! ## Constructing a runtime call
//!
//! We can use the statically generated interface to build runtime calls:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! let runtime_call = polkadot::apis().metadata().metadata_versions();
//! ```
//!
//! Alternately, we can dynamically construct a runtime call:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//! use subxt::dynamic::Value;
//!
//! let account = AccountKeyring::Alice.to_account_id();
//! let runtime_call = subxt::dynamic::runtime_api_call(
//! "Metadata_metadata_versions",
//! Vec::<Value<()>>::new()
//! );
//! ```
//!
//! All valid runtime calls implement [`crate::runtime_api::RuntimeApiPayload`], a trait which
//! describes how to encode the runtime call arguments and what return type to decode from the
//! response.
//!
//! ## Submitting it
//!
//! Runtime calls can be handed to [`crate::runtime_api::RuntimeApi::call()`], which will submit
//! them and hand back the associated response.
//!
//! ### Making a static Runtime API call
//!
//! The easiest way to make a runtime API call is to use the statically generated interface.
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/runtime_apis_static.rs")]
//! ```
//!
//! ### Making a dynamic Runtime API call
//!
//! If you'd prefer to construct the call at runtime, you can do this using the
//! [`crate::dynamic::runtime_api_call`] method.
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/runtime_apis_dynamic.rs")]
//! ```
//!
//! ### Making a raw call
//!
//! This is generally discouraged in favour of one of the above, but may be necessary (especially if
//! the node you're talking to does not yet serve V15 metadata). Here, you must manually encode
//! the argument bytes and manually provide a type for the response bytes to be decoded into.
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/runtime_apis_raw.rs")]
//! ```
//!
+110
View File
@@ -0,0 +1,110 @@
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! # Storage
//!
//! A Substrate based chain has storage, whose values are determined by the extrinsics added to past
//! blocks. Subxt allows you to query the storage of a node, which consists of the following steps:
//!
//! 1. [Constructing a storage query](#constructing-a-storage-query).
//! 2. [Submitting the query to get back the associated values](#submitting-it).
//!
//! ## Constructing a storage query
//!
//! We can use the statically generated interface to build storage queries:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! let account = AccountKeyring::Alice.to_account_id().into();
//! let storage_query = polkadot::storage().system().account(&account);
//! ```
//!
//! Alternately, we can dynamically construct a storage query. This will not be type checked or
//! validated until it's submitted:
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//! use subxt::dynamic::Value;
//!
//! let account = AccountKeyring::Alice.to_account_id();
//! let storage_query = subxt::dynamic::storage("System", "Account", vec![
//! Value::from_bytes(account)
//! ]);
//! ```
//!
//! As well as accessing specific entries, some storage locations can also be iterated over (such as
//! the map of account information). To do this, suffix `_root` onto the query constructor (this
//! will only be available on static constructors when iteration is actually possible):
//!
//! ```rust,no_run
//! use sp_keyring::AccountKeyring;
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! // A static query capable of iterating over accounts:
//! let storage_query = polkadot::storage().system().account_root();
//! // A dynamic query to do the same:
//! let storage_query = subxt::dynamic::storage_root("System", "Account");
//! ```
//!
//! All valid storage queries implement [`crate::storage::StorageAddress`]. As well as describing
//! how to build a valid storage query, this trait also has some associated types that determine the
//! shape of the result you'll get back, and determine what you can do with it (ie, can you iterate
//! over storage entries using it).
//!
//! Static queries set appropriate values for these associated types, and can therefore only be used
//! where it makes sense. Dynamic queries don't know any better and can be used in more places, but
//! may fail at runtime instead if they are invalid in those places.
//!
//! ## Submitting it
//!
//! Storage queries can be handed to various functions in [`crate::storage::Storage`] in order to
//! obtain the associated values (also referred to as storage entries) back.
//!
//! ### Fetching storage entries
//!
//! The simplest way to access storage entries is to construct a query and then call either
//! [`crate::storage::Storage::fetch()`] or [`crate::storage::Storage::fetch_or_default()`] (the
//! latter will only work for storage queries that have a default value when empty):
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_fetch.rs")]
//! ```
//! For completeness, below is an example using a dynamic query instead. The return type from a
//! dynamic query is a [`crate::dynamic::DecodedValueThunk`], which can be decoded into a
//! [`crate::dynamic::Value`], or else the raw bytes can be accessed instead.
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_fetch_dynamic.rs")]
//! ```
//! ### Iterating storage entries
//!
//! Many storage entries are maps of values; as well as fetching individual values, it's possible to
//! iterate over all of the values stored at that location:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_iterating.rs")]
//! ```
//! Here's the same logic but using dynamically constructed values instead:
//!
//!
//! ```rust,ignore
#![doc = include_str!("../../../../examples/examples/storage_iterating_dynamic.rs")]
//! ```
//! ### Advanced
//!
//! For more advanced use cases, have a look at [`crate::storage::Storage::fetch_raw`] and
//! [`crate::storage::Storage::fetch_keys`]. Both of these take raw bytes as arguments, which can be
//! obtained from a [`crate::storage::StorageAddress`] by using
//! [`crate::storage::StorageClient::address_bytes()`] or
//! [`crate::storage::StorageClient::address_root_bytes()`].
//!
+1 -2
View File
@@ -29,7 +29,7 @@ pub trait Config: 'static {
/// transactions associated with a sender account.
type Index: Debug + Copy + DeserializeOwned + Into<u64>;
/// The output of the `Hashing` function.
/// The output of the `Hasher` function.
type Hash: Debug
+ Copy
+ Send
@@ -95,7 +95,6 @@ pub trait Header: Sized + Encode {
/// Take a type implementing [`Config`] (eg [`SubstrateConfig`]), and some type which describes the
/// additional and extra parameters to pass to an extrinsic (see [`ExtrinsicParams`]),
/// and returns a type implementing [`Config`] with those new [`ExtrinsicParams`].
/// ```
pub struct WithExtrinsicParams<T: Config, E: extrinsic_params::ExtrinsicParams<T::Index, T::Hash>> {
_marker: std::marker::PhantomData<(T, E)>,
}
+1 -1
View File
@@ -11,7 +11,7 @@ use crate::{
};
use scale_decode::DecodeAsType;
pub use scale_value::Value;
pub use scale_value::{At, Value};
/// A [`scale_value::Value`] type endowed with contextual information
/// regarding what type was used to decode each part of it. This implements
+207 -105
View File
@@ -2,112 +2,13 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Subxt is a library to **sub**mit e**xt**rinsics to a [substrate](https://github.com/paritytech/substrate) node via RPC.
//! Subxt is a library for interacting with Substrate based nodes. Using it looks something like this:
//!
//! The generated Subxt API exposes the ability to:
//! - [Submit extrinsics](https://docs.substrate.io/v3/concepts/extrinsics/) (Calls)
//! - [Query storage](https://docs.substrate.io/v3/runtime/storage/) (Storage)
//! - [Query constants](https://docs.substrate.io/how-to-guides/v3/basics/configurable-constants/) (Constants)
//! - [Subscribe to events](https://docs.substrate.io/v3/runtime/events-and-errors/) (Events)
//!
//! # Initializing the API client
//!
//! To interact with a node, you'll need to construct a client.
//!
//! ```no_run
//! use subxt::{OnlineClient, PolkadotConfig};
//!
//! # #[tokio::main]
//! # async fn main() {
//! let api = OnlineClient::<PolkadotConfig>::new().await.unwrap();
//! # }
//! ```rust,ignore
#![doc = include_str!("../../examples/examples/balance_transfer_basic.rs")]
//! ```
//!
//! This default client connects to a locally running node, but can be configured to point anywhere.
//! Additionally, an [`crate::OfflineClient`] is available to perform operations that don't require a
//! network connection to a node.
//!
//! The client takes a type parameter, here [`crate::PolkadotConfig`], which bakes in assumptions about
//! the structure of extrinsics and the underlying types used by the node for things like block numbers.
//! If the node you'd like to interact with deviates from Polkadot or the default Substrate node in these
//! areas, you'll need to configure them by implementing the [`crate::config::Config`] type yourself.
//!
//! # Generating runtime types
//!
//! Subxt can optionally generate types at compile time to help you interact with a node. These types are
//! generated using metadata which can be downloaded from a node using the [subxt-cli](https://crates.io/crates/subxt-cli)
//! tool. These generated types provide a degree of type safety when interacting with a node that is compatible with
//! the metadata that they were generated using. We also do runtime checks in case the node you're talking to has
//! deviated from the types you're using to communicate with it (see below).
//!
//! To generate the types, use the `subxt` macro and point it at the metadata you've downloaded, like so:
//!
//! ```ignore
//! #[subxt::subxt(runtime_metadata_path = "metadata.scale")]
//! pub mod node_runtime { }
//! ```
//!
//! For more information, please visit the [subxt-codegen](https://docs.rs/subxt-codegen/latest/subxt_codegen/)
//! documentation.
//!
//! You can opt to skip this step and use dynamic queries to talk to nodes instead, which can be useful in some cases,
//! but doesn't provide any type safety.
//!
//! # Interacting with the API
//!
//! Once instantiated, a client exposes four core functions:
//! - `.tx()` for submitting extrinsics/transactions. See [`crate::tx::TxClient`] for more details, or see
//! the [balance_transfer](../examples/examples/balance_transfer.rs) example.
//! - `.storage()` for fetching and iterating over storage entries. See [`crate::storage::StorageClient`] for more details, or see
//! the [fetch_staking_details](../examples/examples/fetch_staking_details.rs) example.
//! - `.constants()` for getting hold of constants. See [`crate::constants::ConstantsClient`] for more details, or see
//! the [fetch_constants](../examples/examples/fetch_constants.rs) example.
//! - `.events()` for subscribing/obtaining events. See [`crate::events::EventsClient`] for more details, or see:
//! - [subscribe_all_events](../examples/examples/subscribe_all_events.rs): Subscribe to events emitted from blocks.
//! - [subscribe_one_event](../examples/examples/subscribe_one_event.rs): Subscribe and filter by one event.
//! - [subscribe_some_events](../examples/examples/subscribe_some_events.rs): Subscribe and filter event.
//!
//! # Static Metadata Validation
//!
//! If you use types generated by the [`crate::subxt`] macro, there is a chance that they will fall out of sync
//! with the actual state of the node you're trying to interact with.
//!
//! When you attempt to use any of these static types to interact with a node, Subxt will validate that they are
//! still compatible and issue an error if they have deviated.
//!
//! Additionally, you can validate that the entirety of the statically generated code aligns with a node like so:
//!
//! ```no_run
//! use subxt::{OnlineClient, PolkadotConfig};
//!
//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
//! pub mod polkadot {}
//!
//! # #[tokio::main]
//! # async fn main() {
//! let api = OnlineClient::<PolkadotConfig>::new().await.unwrap();
//!
//! if let Err(_e) = polkadot::validate_codegen(&api) {
//! println!("Generated code is not up to date with node we're connected to");
//! }
//! # }
//! ```
//! ## Opting out of static validation
//!
//! The static types that are used to query/access information are validated by default, to make sure that they are
//! compatible with the node being queried. You can generally call `.unvalidated()` on these static types to
//! disable this validation.
//!
//! # Runtime Updates
//!
//! The node you're connected to may occasionally perform runtime updates while you're connected, which would ordinarily
//! leave the runtime state of the node out of sync with the information Subxt requires to do things like submit
//! transactions.
//!
//! If this is a concern, you can use the `UpdateClient` API to keep the `RuntimeVersion` and `Metadata` of the client
//! synced with the target node.
//!
//! Please visit the [subscribe_runtime_updates](../examples/examples/subscribe_runtime_updates.rs) example for more details.
//! Take a look at [the Subxt guide](book) to learn more about how to use Subxt.
#![deny(
bad_style,
@@ -132,13 +33,14 @@
)]
#![allow(clippy::type_complexity)]
// The guide is here.
pub mod book;
// Suppress an unused dependency warning because tokio is
// only used in example code snippets at the time of writing.
#[cfg(test)]
use tokio as _;
pub use subxt_macro::subxt;
// Used to enable the js feature for wasm.
#[cfg(target_arch = "wasm32")]
pub use getrandom as _;
@@ -184,3 +86,203 @@ pub mod ext {
#[cfg(feature = "substrate-compat")]
pub use sp_runtime;
}
/// Generate a strongly typed API for interacting with a Substrate runtime from its metadata.
///
/// # Metadata
///
/// First, you'll need to get hold of some metadata for the node you'd like to interact with. One
/// way to do this is by using the `subxt` CLI tool:
///
/// ```bash
/// # Install the CLI tool:
/// cargo install subxt-cli
/// # Use it to download metadata (in this case, from a node running locally)
/// subxt metadata > polkadot_metadata.scale
/// ```
///
/// Run `subxt metadata --help` for more options.
///
/// # Basic usage
///
/// Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// )]
/// mod polkadot {}
/// ```
///
/// The `subxt` macro will populate the annotated module with all of the methods and types required
/// for interacting with the runtime that the metadata came from via Subxt.
///
/// # Configuration
///
/// This macro supports a number of attributes to configure what is generated:
///
/// ## `crate = "..."`
///
/// Use this attribute to specify a custom path to the `subxt` crate:
///
/// ```rust
/// # pub extern crate subxt;
/// # pub mod path { pub mod to { pub use subxt; } }
/// # fn main() {}
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// crate = "crate::path::to::subxt"
/// )]
/// mod polkadot {}
/// ```
///
/// This is useful if you write a library which uses this macro, but don't want to force users to depend on `subxt`
/// at the top level too. By default the path `::subxt` is used.
///
/// ## `substitute_type(path = "...", with = "...")`
///
/// This attribute replaces any reference to the generated type at the path given by `path` with a
/// reference to the path given by `with`.
///
/// ```rust
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// substitute_type(path = "sp_arithmetic::per_things::Perbill", with = "crate::Foo")
/// )]
/// mod polkadot {}
///
/// # #[derive(
/// # scale_encode::EncodeAsType,
/// # scale_decode::DecodeAsType,
/// # codec::Encode,
/// # codec::Decode,
/// # Clone,
/// # Debug,
/// # )]
/// // In reality this needs some traits implementing on
/// // it to allow it to be used in place of Perbill:
/// pub struct Foo(u32);
/// # impl codec::CompactAs for Foo {
/// # type As = u32;
/// # fn encode_as(&self) -> &Self::As {
/// # &self.0
/// # }
/// # fn decode_from(x: Self::As) -> Result<Self, codec::Error> {
/// # Ok(Foo(x))
/// # }
/// # }
/// # impl From<codec::Compact<Foo>> for Foo {
/// # fn from(v: codec::Compact<Foo>) -> Foo {
/// # v.0
/// # }
/// # }
/// # fn main() {}
/// ```
///
/// If the type you're substituting contains generic parameters, you can "pattern match" on those, and
/// make use of them in the substituted type, like so:
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// substitute_type(
/// path = "sp_runtime::multiaddress::MultiAddress<A, B>",
/// with = "::subxt::utils::Static<::sp_runtime::MultiAddress<A, B>>"
/// )
/// )]
/// mod polkadot {}
/// ```
///
/// The above is also an example of using the [`crate::utils::Static`] type to wrap some type which doesn't
/// on it's own implement [`scale_encode::EncodeAsType`] or [`scale_decode::DecodeAsType`], which are required traits
/// for any substitute type to implement by default.
///
/// ## `derive_for_all_types = "..."`
///
/// By default, all generated types derive a small set of traits. This attribute allows you to derive additional
/// traits on all generated types:
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// derive_for_all_types = "Eq, PartialEq"
/// )]
/// mod polkadot {}
/// ```
///
/// Any substituted types (including the default substitutes) must also implement these traits in order to avoid errors
/// here.
///
/// ## `derive_for_type(path = "...", derive = "...")`
///
/// Unlike the above, which derives some trait on every generated type, this attribute allows you to derive traits only
/// for specific types. Note that any types which are used inside the specified type may also need to derive the same traits.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// derive_for_all_types = "Eq, PartialEq",
/// derive_for_type(path = "frame_support::PalletId", derive = "Ord, PartialOrd"),
/// derive_for_type(path = "sp_runtime::ModuleError", derive = "Hash"),
/// )]
/// mod polkadot {}
/// ```
///
/// ## `runtime_metadata_url = "..."`
///
/// This attribute can be used instead of `runtime_metadata_path` and will tell the macro to download metadata from a node running
/// at the provided URL, rather than a node running locally. This can be useful in CI, but is **not recommended** in production code,
/// since it runs at compile time and will cause compilation to fail if the node at the given address is unavailable or unresponsive.
///
/// ```rust,ignore
/// #[subxt::subxt(
/// runtime_metadata_url = "wss://rpc.polkadot.io:443"
/// )]
/// mod polkadot {}
/// ```
///
/// ## `generate_docs`
///
/// By default, documentation is not generated via the macro, since IDEs do not typically make use of it. This attribute
/// forces documentation to be generated, too.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// generate_docs
/// )]
/// mod polkadot {}
/// ```
///
/// ## `runtime_types_only`
///
/// By default, the macro will generate various interfaces to make using Subxt simpler in addition with any types that need
/// generating to make this possible. This attribute makes the codegen only generate the types and not the Subxt interface.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// runtime_types_only
/// )]
/// mod polkadot {}
/// ```
/// ## `no_default_derives`
///
/// By default, the macro will add all derives necessary for the generated code to play nicely with Subxt. Adding this attribute
/// removes all default derives.
///
/// ```rust,no_run
/// #[subxt::subxt(
/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale",
/// runtime_types_only,
/// no_default_derives,
/// derive_for_all_types="codec::Encode, codec::Decode"
/// )]
/// mod polkadot {}
/// ```
///
/// **Note**: At the moment, you must derive at least one of `codec::Encode` or `codec::Decode` or `scale_encode::EncodeAsType` or
/// `scale_decode::DecodeAsType` (because we add `#[codec(..)]` attributes on some fields/types during codegen), and you must use this
/// feature in conjunction with `runtime_types_only` (or manually specify a bunch of defaults to make codegen work properly when
/// generating the subxt interfaces).
pub use subxt_macro::subxt;
+1
View File
@@ -13,6 +13,7 @@ pub use metadata_location::MetadataLocation;
pub use metadata_type::{
ErrorMetadata, EventMetadata, InvalidMetadataError, Metadata, MetadataError, PalletMetadata,
RuntimeFnMetadata,
};
pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata};