Rework Subxt API to support offline and dynamic transactions (#593)

* WIP API changes

* debug impls

* Get main crate compiling with first round of changes

* Some tidy up

* Add WithExtrinsicParams, and have SubstrateConfig + PolkadotConfig, not DefaultConfig

* move transaction into extrinsic folder

* Add runtime updates back to OnlineClient

* rework to be 'client first' to fit better with storage + events

* add support for events to Client

* tidy dupe trait bound

* Wire storage into client, but need to remove static reliance

* various tidy up and start stripping codegen to remove bits we dont need now

* First pass updating calls and constants codegen

* WIP storage client updates

* First pass migrated runtime storage over to new format

* pass over codegen to generate StorageAddresses and throw other stuff out

* don't need a Call trait any more

* shuffle things around a bit

* Various proc_macro fixes to get 'cargo check' working

* organise what's exposed from subxt

* Get first example working; balance_transfer_with_params

* get balance_transfer example compiling

* get concurrent_storage_requests.rs example compiling

* get fetch_all_accounts example compiling

* get a bunch more of the examples compiling

* almost get final example working; type mismatch to look into

* wee tweaks

* move StorageAddress to separate file

* pass Defaultable/Iterable info to StorageAddress in codegen

* fix storage validation ne, and partial run through example code

* Remove static iteration and strip a generic param from everything

* fix doc tests in subxt crate

* update test utils and start fixing frame tests

* fix frame staking tests

* fix the rest of the test compile issues, Borrow on storage values

* cargo fmt

* remove extra logging during tests

* Appease clippy and no more need for into_iter on events

* cargo fmt

* fix dryRun tests by waiting for blocks

* wait for blocks instead of sleeping or other test hacks

* cargo fmt

* Fix doc links

* Traitify StorageAddress

* remove out-of-date doc comments

* optimise decoding storage a little

* cleanup tx stuff, trait for TxPayload, remove Err type param and decode at runtime

* clippy fixes

* fix doc links

* fix doc example

* constant address trait for consistency

* fix a typo and remove EncodeWithMetadata stuff

* Put EventDetails behind a proper interface and allow decoding into top level event, too

* fix docs

* tweak StorageAddress docs

* re-export StorageAddress at root for consistency

* fix clippy things

* Add support for dynamic values

* fix double encoding of storage map key after refactor

* clippy fix

* Fixes and add a dynamic usage example (needs new scale_value release)

* bump scale_value version

* cargo fmt

* Tweak event bits

* cargo fmt

* Add a test and bump scale-value to 0.4.0 to support this

* remove unnecessary vec from dynamic example

* Various typo/grammar fixes

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>

* Address PR nits

* Undo accidental rename in changelog

* Small PR nits/tidyups

* fix tests; codegen change against latest substrate

* tweak storage address util names

* move error decoding to DecodeError and expose

* impl some basic traits on the extrinsic param builder

Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
James Wilson
2022-08-08 11:55:20 +01:00
committed by GitHub
parent 7a09ac6cd7
commit e48f0e3b1d
84 changed files with 23097 additions and 35863 deletions
+15 -19
View File
@@ -2,20 +2,19 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -26,23 +25,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Submit the `transfer` extrinsic from Alice's account to Bob's.
let dest = AccountKeyring::Bob.to_account_id().into();
// Obtain an extrinsic, calling the "transfer" function in
// the "balances" pallet.
let extrinsic = api.tx().balances().transfer(dest, 123_456_789_012_345)?;
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Sign and submit the extrinsic, returning its hash.
let tx_hash = extrinsic.sign_and_submit_default(&signer).await?;
// Create a transaction to submit:
let tx = polkadot::tx()
.balances()
.transfer(dest, 123_456_789_012_345);
println!("Balance transfer extrinsic submitted: {}", tx_hash);
// Submit the transaction with default params:
let hash = api.tx().sign_and_submit_default(&tx, &signer).await?;
println!("Balance transfer extrinsic submitted: {}", hash);
Ok(())
}
@@ -2,25 +2,24 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
extrinsic::{
tx::{
Era,
PairSigner,
PlainTip,
PolkadotExtrinsicParamsBuilder as Params,
},
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
PolkadotExtrinsicParamsBuilder as Params,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -33,23 +32,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// 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);
// Configure the transaction tip and era:
let tx_params = Params::new()
.tip(PlainTip::new(20_000_000_000))
.era(Era::Immortal, *api.client.genesis());
.era(Era::Immortal, api.genesis_hash());
// Send the transaction:
let hash = api
.tx()
.balances()
.transfer(dest, 123_456_789_012_345)?
.sign_and_submit(&signer, tx_params)
.await?;
// submit the transaction:
let hash = api.tx().sign_and_submit(&tx, &signer, tx_params).await?;
println!("Balance transfer extrinsic submitted: {}", hash);
@@ -2,12 +2,19 @@
// 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 polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::join;
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -15,17 +22,18 @@ pub mod polkadot {}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let addr = AccountKeyring::Bob.to_account_id();
// 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().staking().bonded(&addr, None);
let b_fut = api.storage().staking().ledger(&addr, None);
let a_fut = api.storage().fetch(&staking_bonded, None);
let b_fut = api.storage().fetch(&staking_ledger, None);
let (a, b) = join!(a_fut, b_fut);
println!("{a:?}, {b:?}");
+31 -30
View File
@@ -2,21 +2,20 @@
// 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.18-4542a603cc-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
//! This example should compile but should aos fail to work, since we've modified the
//! config to not align with a Polkadot node.
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
Config,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
config::{
Config,
SubstrateConfig,
},
tx::{
PairSigner,
SubstrateExtrinsicParams,
},
OnlineClient,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -33,32 +32,34 @@ impl Config for MyConfig {
// 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 BlockNumber = <DefaultConfig as Config>::BlockNumber;
type Hash = <DefaultConfig as Config>::Hash;
type Hashing = <DefaultConfig as Config>::Hashing;
type AccountId = <DefaultConfig as Config>::AccountId;
type Address = <DefaultConfig as Config>::Address;
type Header = <DefaultConfig as Config>::Header;
type Signature = <DefaultConfig as Config>::Signature;
type Extrinsic = <DefaultConfig as Config>::Extrinsic;
type BlockNumber = <SubstrateConfig as Config>::BlockNumber;
type Hash = <SubstrateConfig as Config>::Hash;
type Hashing = <SubstrateConfig as Config>::Hashing;
type AccountId = <SubstrateConfig as Config>::AccountId;
type Address = <SubstrateConfig as Config>::Address;
type Header = <SubstrateConfig as Config>::Header;
type Signature = <SubstrateConfig as Config>::Signature;
type Extrinsic = <SubstrateConfig as Config>::Extrinsic;
// 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 api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<MyConfig, PolkadotExtrinsicParams<MyConfig>>>();
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let hash = api
.tx()
// Create a client to use:
let api = OnlineClient::<MyConfig>::new().await?;
// Create a transaction to submit:
let tx = polkadot::tx()
.balances()
.transfer(dest, 10_000)?
.sign_and_submit_default(&signer)
.await?;
.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);
+1 -2
View File
@@ -2,8 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Example verified against polkadot 0.9.18-4542a603cc-aarch64-macos.
//! Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
#![allow(clippy::redundant_clone)]
#[subxt::subxt(
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2019-2022 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 polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/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)?;
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()
.fetch_or_default(&storage_address, None)
.await?;
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().iter(storage_address, 10, None).await?;
while let Some((key, account)) = iter.next().await? {
println!("{}: {}", hex::encode(key), account);
}
Ok(())
}
+8 -10
View File
@@ -2,18 +2,17 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -23,12 +22,11 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let mut iter = api.storage().system().account_iter(None).await?;
let address = polkadot::storage().system().account_root();
let mut iter = api.storage().iter(address, 10, None).await?;
while let Some((key, account)) = iter.next().await? {
println!("{}: {}", hex::encode(key), account.data.free);
+11 -20
View File
@@ -2,18 +2,17 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
OnlineClient,
PolkadotConfig,
};
// Generate the API from a static metadata path.
@@ -24,22 +23,14 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Upon connecting to the target polkadot node, the node's metadata is downloaded (referred to
// as the runtime metadata).
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Constants are queried from the node's runtime metadata.
// Query the `ExistentialDeposit` constant from the `Balances` pallet.
let existential_deposit = api
// This is the constants query.
.constants()
// Constant from the `Balances` pallet.
.balances()
// Constant name.
.existential_deposit()?;
// 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);
+18 -21
View File
@@ -2,24 +2,25 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use subxt::{
sp_core::{
sr25519,
Pair,
ext::{
sp_core::{
sr25519,
Pair,
},
sp_runtime::AccountId32,
},
sp_runtime::AccountId32,
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -29,12 +30,11 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
let era = api.storage().staking().active_era(None).await?.unwrap();
let active_era_addr = polkadot::storage().staking().active_era();
let era = api.storage().fetch(&active_era_addr, None).await?.unwrap();
println!(
"Staking active era: index: {:?}, start: {:?}",
era.index, era.start
@@ -51,19 +51,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
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()
.staking()
.bonded(&alice_stash_id, None)
.fetch(&controller_acc_addr, None)
.await?
.unwrap();
println!(" account controlled by: {:?}", controller_acc);
let era_result = api
.storage()
.staking()
.eras_reward_points(&era.index, None)
.await?;
let era_reward_addr = polkadot::storage().staking().eras_reward_points(&era.index);
let era_result = api.storage().fetch(&era_reward_addr, None).await?;
println!("Era reward points: {:?}", era_result);
Ok(())
+12 -16
View File
@@ -2,18 +2,17 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -23,18 +22,15 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Full metadata validation is not enabled by default; instead, the individual calls,
// storage requests and constant accesses are runtime type checked against the node
// metadata to ensure that they are compatible with the generated code.
//
// To make sure that all of our statically generated pallets are compatible with the
// runtime node, we can run this check:
api.validate_metadata()?;
// 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(())
}
+6 -18
View File
@@ -2,40 +2,28 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
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 = ClientBuilder::new()
.set_url("wss://rpc.polkadot.io:443")
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let block_number = 1u32;
let block_hash = api
.client
.rpc()
.block_hash(Some(block_number.into()))
.await?;
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}");
+9 -16
View File
@@ -2,41 +2,34 @@
// 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.18-f6d6ab005d-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.13/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use subxt::{
rpc::Subscription,
sp_runtime::{
ext::sp_runtime::{
generic::Header,
traits::BlakeTwo256,
},
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
rpc::Subscription,
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 = ClientBuilder::new()
.set_url("wss://rpc.polkadot.io:443")
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
let api =
OnlineClient::<PolkadotConfig>::from_url("wss://rpc.polkadot.io:443").await?;
// For non-finalised blocks use `.subscribe_blocks()`
let mut blocks: Subscription<Header<u32, BlakeTwo256>> =
api.client.rpc().subscribe_finalized_blocks().await?;
api.rpc().subscribe_finalized_blocks().await?;
while let Some(Ok(block)) = blocks.next().await {
println!(
+107
View File
@@ -0,0 +1,107 @@
// Copyright 2019-2022 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 polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use codec::Decode;
use subxt::{
storage::address::{
StorageHasher,
StorageMapKey,
},
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?;
// 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();
let mut iter = api.storage().iter(key_addr, 10, None).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()
.fetch_keys(&key_addr.to_root_bytes(), 10, None, 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().fetch_raw(&key.0, None).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.
// We can build a `StorageMapKey` that replicates that, and append those bytes to the above.
StorageMapKey::new(&2u32, StorageHasher::Twox64Concat).to_bytes(&mut query_key);
// The final query key is essentially the result of:
// `twox_128("XcmPallet") ++ twox_128("VersionNotifiers") ++ twox_64(2u32) ++ 2u32`
println!("\nExample 3\nQuery key: 0x{}", hex::encode(&query_key));
let keys = api.storage().fetch_keys(&query_key, 10, None, None).await?;
println!("Obtained keys:");
for key in keys.iter() {
println!("Key: 0x{}", hex::encode(&key));
if let Some(storage_data) = api.storage().fetch_raw(&key.0, None).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);
}
}
}
Ok(())
}
-153
View File
@@ -1,153 +0,0 @@
// Copyright 2019-2022 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.18-4542a603cc-aarch64-macos.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use codec::Decode;
use subxt::{
rpc::Rpc,
storage::{
StorageClient,
StorageKeyPrefix,
},
ClientBuilder,
DefaultConfig,
PolkadotExtrinsicParams,
StorageEntryKey,
StorageMapKey,
};
#[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 = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Obtain the storage client wrapper from the API.
let storage: StorageClient<_> = api.client.storage();
// The VersionNotifiers type of the XcmPallet is defined as:
//
// ```
// All locations that we have requested version notifications from.
// #[pallet::storage]
// pub(super) type VersionNotifiers<T: Config> = StorageDoubleMap<
// _,
// Twox64Concat,
// XcmVersion,
// Blake2_128Concat,
// VersionedMultiLocation,
// QueryId,
// OptionQuery,
// >;
// ```
// Example 1. Iterate over fetched keys manually.
{
// Fetch at most 10 keys from below the prefix XcmPallet' VersionNotifiers.
let keys = storage
.fetch_keys::<polkadot::xcm_pallet::storage::VersionNotifiers>(10, None, None)
.await?;
println!("Example 1. Obtained keys:");
for key in keys.iter() {
println!("Key: 0x{}", hex::encode(&key));
if let Some(storage_data) = storage.fetch_raw(key.clone(), None).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.0[..])?;
println!(" Value: {}", value);
}
}
}
// Example 2. Iterate over (keys, value) using the storage client.
{
let mut iter = storage
.iter::<polkadot::xcm_pallet::storage::VersionNotifiers>(None)
.await?;
println!("\nExample 2. Obtained keys:");
while let Some((key, value)) = iter.next().await? {
println!("Key: 0x{}", hex::encode(&key));
println!(" Value: {}", value);
}
}
// Example 3. Iterate over (keys, value) using the polkadot API.
{
let mut iter = api
.storage()
.xcm_pallet()
.version_notifiers_iter(None)
.await?;
println!("\nExample 3. Obtained keys:");
while let Some((key, value)) = iter.next().await? {
println!("Key: 0x{}", hex::encode(&key));
println!(" Value: {}", value);
}
}
// Example 4. Custom iteration over double maps.
{
// Obtain the inner RPC from the API.
let rpc: &Rpc<_> = api.client.rpc();
// Obtain the prefixed `twox_128("XcmPallet") ++ twox_128("VersionNotifiers")`
let prefix =
StorageKeyPrefix::new::<polkadot::xcm_pallet::storage::VersionNotifiers>();
// From the VersionNotifiers definition above, the first key is represented by
// ```
// Twox64Concat,
// XcmVersion,
// ```
// while `XcmVersion` is `u32`.
// Pass `2` as `XcmVersion` and concatenate the key to the prefix.
let entry_key = StorageEntryKey::Map(vec![StorageMapKey::new(
&2u32,
::subxt::StorageHasher::Twox64Concat,
)]);
// The final query key is:
// `twox_128("XcmPallet") ++ twox_128("VersionNotifiers") ++ twox_64(2u32) ++ 2u32`
let query_key = entry_key.final_key(prefix);
println!("\nExample 4\nQuery key: 0x{}", hex::encode(&query_key));
let keys = rpc
.storage_keys_paged(Some(query_key), 10, None, None)
.await?;
println!("Obtained keys:");
for key in keys.iter() {
println!("Key: 0x{}", hex::encode(&key));
if let Some(storage_data) = storage.fetch_raw(key.clone(), None).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.0[..])?;
println!(" Value: {}", value);
}
}
}
Ok(())
}
+18 -28
View File
@@ -2,21 +2,20 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use futures::StreamExt;
use sp_keyring::AccountKeyring;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -40,16 +39,13 @@ 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 = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let balance_transfer = api
.tx()
.balances()
.transfer(dest, 10_000)?
.sign_and_submit_then_watch_default(&signer)
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?
.wait_for_finalized_success()
.await?;
@@ -72,16 +68,13 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let dest = AccountKeyring::Bob.to_account_id().into();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let balance_transfer = api
.tx()
.balances()
.transfer(dest, 10_000)?
.sign_and_submit_then_watch_default(&signer)
.sign_and_submit_then_watch_default(&balance_transfer_tx, &signer)
.await?
.wait_for_finalized()
.await?;
@@ -123,21 +116,18 @@ 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 = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<_>>>();
let api = OnlineClient::<PolkadotConfig>::new().await?;
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
let mut balance_transfer_progress = api
.tx()
.balances()
.transfer(dest, 10_000)?
.sign_and_submit_then_watch_default(&signer)
.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::TransactionStatus::*;
use subxt::tx::TxStatus::*;
// Made it into a block, but not finalized.
if let InBlock(details) = ev {
+30 -47
View File
@@ -2,11 +2,11 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
@@ -14,10 +14,9 @@ use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -29,41 +28,33 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to any events that occur:
let mut event_sub = api.events().subscribe().await?;
// While this subscription is active, balance transfers are made somewhere:
tokio::task::spawn(async {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let api =
ClientBuilder::new()
.build()
.await
.unwrap()
.to_runtime_api::<polkadot::RuntimeApi<
DefaultConfig,
PolkadotExtrinsicParams<DefaultConfig>,
>>();
tokio::task::spawn({
let api = api.clone();
async move {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let mut transfer_amount = 1_000_000_000;
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();
// Make small balance transfers from Alice to Bob in a loop:
loop {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount)
.expect("compatible transfer call on runtime node")
.sign_and_submit_default(&signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
tokio::time::sleep(Duration::from_secs(10)).await;
transfer_amount += 100_000_000;
}
}
});
@@ -72,29 +63,21 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let events = events?;
let block_hash = events.block_hash();
// We can iterate, statically decoding all events if we want:
println!("All events in block {block_hash:?}:");
println!(" Static event details:");
for event in events.iter() {
let event = event?;
println!(" {event:?}");
}
// Or we can dynamically decode events:
// We can dynamically decode events:
println!(" Dynamic event details: {block_hash:?}:");
for event in events.iter_raw() {
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;
let variant = event.variant;
let pallet = event.pallet_name();
let variant = event.variant_name();
println!(
" {pallet}::{variant} (is balance transfer? {is_balance_transfer})"
);
}
// Or we can dynamically find the first transfer event, ignoring any others:
// Or we can find the first transfer event, ignoring any others:
let transfer_event =
events.find_first::<polkadot::balances::events::Transfer>()?;
+27 -33
View File
@@ -2,11 +2,11 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
@@ -14,10 +14,9 @@ use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -29,11 +28,8 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Subscribe to any events that occur:
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to just balance transfer events, making use of `filter_events`
// to select a single event type (note the 1-tuple) to filter out and return.
@@ -43,29 +39,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.await?
.filter_events::<(polkadot::balances::events::Transfer,)>();
// While this subscription is active, we imagine some balance transfers are made somewhere else:
tokio::task::spawn(async {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let api =
ClientBuilder::new()
.build()
.await
.unwrap()
.to_runtime_api::<polkadot::RuntimeApi<
DefaultConfig,
PolkadotExtrinsicParams<DefaultConfig>,
>>();
// 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 {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
.expect("compatible transfer call on runtime node")
.sign_and_submit_default(&signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
// 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;
}
}
});
+10 -39
View File
@@ -2,67 +2,38 @@
// 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.18-f6d6ab005d-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
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 = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// 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.client.updates();
let update_client = api.subscribe_to_updates();
tokio::spawn(async move {
let result = update_client.perform_runtime_updates().await;
println!("Runtime update failed with result={:?}", result);
});
// Make multiple transfers to simulate a long running `subxt::Client` use-case.
//
// Meanwhile, the tokio task above will perform any necessary updates to keep in sync
// with the node we've connected to. Transactions submitted in the vicinity of a runtime
// update may still fail, however, owing to a race between the update happening and
// subxt synchronising its internal state with it.
let signer = PairSigner::new(AccountKeyring::Alice.pair());
// Make small balance transfers from Alice to Bob:
for _ in 0..10 {
let hash = api
.tx()
.balances()
.transfer(
AccountKeyring::Bob.to_account_id().into(),
123_456_789_012_345,
)
.unwrap()
.sign_and_submit_default(&signer)
.await
.unwrap();
println!("Balance transfer extrinsic submitted: {}", hash);
tokio::time::sleep(Duration::from_secs(30)).await;
}
// 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(())
}
+27 -33
View File
@@ -2,11 +2,11 @@
// 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.18-4542a603cc-aarch64-macos.
//! To run this example, a local polkadot node should be running. Example verified against polkadot polkadot 0.9.25-5174e9ae75b.
//!
//! E.g.
//! ```bash
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location
//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.25/polkadot" --output /usr/local/bin/polkadot --location
//! polkadot --dev --tmp
//! ```
@@ -14,10 +14,9 @@ use futures::StreamExt;
use sp_keyring::AccountKeyring;
use std::time::Duration;
use subxt::{
ClientBuilder,
DefaultConfig,
PairSigner,
PolkadotExtrinsicParams,
tx::PairSigner,
OnlineClient,
PolkadotConfig,
};
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")]
@@ -29,11 +28,8 @@ pub mod polkadot {}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Subscribe to any events that occur:
let api = ClientBuilder::new()
.build()
.await?
.to_runtime_api::<polkadot::RuntimeApi<DefaultConfig, PolkadotExtrinsicParams<DefaultConfig>>>();
// Create a client to use:
let api = OnlineClient::<PolkadotConfig>::new().await?;
// Subscribe to several balance related events. If we ask for more than one event,
// we'll be given a correpsonding tuple of `Option`'s, with exactly one
@@ -44,29 +40,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
polkadot::balances::events::Deposit,
)>();
// While this subscription is active, we imagine some balance transfers are made somewhere else:
tokio::task::spawn(async {
let signer = PairSigner::new(AccountKeyring::Alice.pair());
let api =
ClientBuilder::new()
.build()
.await
.unwrap()
.to_runtime_api::<polkadot::RuntimeApi<
DefaultConfig,
PolkadotExtrinsicParams<DefaultConfig>,
>>();
// 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 {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
.expect("compatible transfer call on runtime node")
.sign_and_submit_default(&signer)
.await
.unwrap();
tokio::time::sleep(Duration::from_secs(10)).await;
// 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;
}
}
});