mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 00:41:03 +00:00
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:
@@ -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
@@ -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.
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
Reference in New Issue
Block a user