diff --git a/Cargo.lock b/Cargo.lock index c58c56f890..f1bbec594c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "array-bytes" @@ -353,9 +353,9 @@ dependencies = [ [[package]] name = "bounded-collections" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a071c348a5ef6da1d3a87166b408170b46002382b1dda83992b5c2208cefb370" +checksum = "e3888522b497857eb606bf51695988dba7096941822c1bcf676e3a929a9ae7a0" dependencies = [ "log", "parity-scale-codec", @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags", "clap_lex 0.2.4", @@ -470,9 +470,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.5" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" +checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" dependencies = [ "clap_builder", "clap_derive", @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.5" +version = "4.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" +checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" dependencies = [ "anstream", "anstyle", @@ -640,7 +640,7 @@ dependencies = [ "atty", "cast", "ciborium", - "clap 3.2.23", + "clap 3.2.25", "criterion-plot", "itertools", "lazy_static", @@ -837,12 +837,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" dependencies = [ - "darling_core 0.20.0", - "darling_macro 0.20.0", + "darling_core 0.20.1", + "darling_macro 0.20.1", ] [[package]] @@ -861,9 +861,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" dependencies = [ "fnv", "ident_case", @@ -886,11 +886,11 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" dependencies = [ - "darling_core 0.20.0", + "darling_core 0.20.1", "quote", "syn 2.0.15", ] @@ -1691,7 +1691,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.14", + "rustix 0.37.19", "windows-sys 0.48.0", ] @@ -1905,9 +1905,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lock_api" @@ -1958,7 +1958,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" dependencies = [ - "rustix 0.37.14", + "rustix 0.37.19", ] [[package]] @@ -2630,15 +2630,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.14" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", - "linux-raw-sys 0.3.4", + "linux-raw-sys 0.3.7", "windows-sys 0.48.0", ] @@ -3457,9 +3457,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "ss58-registry" -version = "1.39.0" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecf0bd63593ef78eca595a7fc25e9a443ca46fe69fd472f8f09f5245cdcd769d" +checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" dependencies = [ "Inflector", "num-format", @@ -3574,7 +3574,7 @@ dependencies = [ name = "subxt-cli" version = "0.28.0" dependencies = [ - "clap 4.2.5", + "clap 4.2.7", "color-eyre", "frame-metadata", "hex", @@ -3614,20 +3614,16 @@ version = "0.28.0" dependencies = [ "futures", "hex", - "parity-scale-codec", - "sp-core", "sp-keyring", - "sp-runtime", "subxt", "tokio", - "tracing-subscriber 0.3.17", ] [[package]] name = "subxt-macro" version = "0.28.0" dependencies = [ - "darling 0.20.0", + "darling 0.20.1", "proc-macro-error", "subxt-codegen", "syn 2.0.15", @@ -3824,9 +3820,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -4638,9 +4634,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index cf87f98ec1..71c7eb4d4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,4 +86,4 @@ subxt-macro = { version = "0.28.0", path = "macro" } subxt-metadata = { version = "0.28.0", path = "metadata" } subxt-codegen = { version = "0.28.0", path = "codegen" } test-runtime = { path = "testing/test-runtime" } -substrate-runner = { path = "testing/substrate-runner" } \ No newline at end of file +substrate-runner = { path = "testing/substrate-runner" } diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index 8f112b088f..2421e711f1 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -225,8 +225,13 @@ impl RuntimeGenerator { // Preserve any Rust items that were previously defined in the adorned module #( #rust_items ) * - // Make it easy to access the root via `root_mod` at different levels: - use super::#mod_ident as root_mod; + // Make it easy to access the root items via `root_mod` at different levels + // without reaching out of this module. + #[allow(unused_imports)] + mod root_mod { + pub use super::*; + } + #types_mod } }) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 2b28de852b..d6972e640c 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -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 } diff --git a/examples/README.md b/examples/README.md index 30ae9c8b65..a7c3c186ab 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,5 @@ # Subxt Examples -Take a look in the [examples](./examples) subfolder for various `subxt` usage examples. \ No newline at end of file +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. \ No newline at end of file diff --git a/examples/examples/balance_transfer.rs b/examples/examples/balance_transfer.rs deleted file mode 100644 index 6392c686bc..0000000000 --- a/examples/examples/balance_transfer.rs +++ /dev/null @@ -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> { - 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::::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(()) -} diff --git a/examples/examples/balance_transfer_basic.rs b/examples/examples/balance_transfer_basic.rs new file mode 100644 index 0000000000..787f1f9ef2 --- /dev/null +++ b/examples/examples/balance_transfer_basic.rs @@ -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> { + // Create a new API client, configured to talk to Polkadot nodes. + let api = OnlineClient::::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::()?; + if let Some(event) = transfer_event { + println!("Balance transfer success: {event:?}"); + } + + Ok(()) +} diff --git a/examples/examples/balance_transfer_status_stream.rs b/examples/examples/balance_transfer_status_stream.rs new file mode 100644 index 0000000000..a390f9e3d4 --- /dev/null +++ b/examples/examples/balance_transfer_status_stream.rs @@ -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> { + // Create a new API client, configured to talk to Polkadot nodes. + let api = OnlineClient::::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::()?; + + 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(()) +} diff --git a/examples/examples/balance_transfer_with_params.rs b/examples/examples/balance_transfer_with_params.rs index ee9bde04ce..c94140d421 100644 --- a/examples/examples/balance_transfer_with_params.rs +++ b/examples/examples/balance_transfer_with_params.rs @@ -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> { - 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::::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(()) } diff --git a/examples/examples/subscribe_blocks.rs b/examples/examples/blocks_subscribing.rs similarity index 73% rename from examples/examples/subscribe_blocks.rs rename to examples/examples/blocks_subscribing.rs index 81a88f09b5..4c8cdb6cff 100644 --- a/examples/examples/subscribe_blocks.rs +++ b/examples/examples/blocks_subscribing.rs @@ -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> { - tracing_subscriber::fmt::init(); - // Create a client to use: let api = OnlineClient::::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> { 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> { 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); } } } diff --git a/examples/examples/concurrent_storage_requests.rs b/examples/examples/concurrent_storage_requests.rs deleted file mode 100644 index ccfc673f89..0000000000 --- a/examples/examples/concurrent_storage_requests.rs +++ /dev/null @@ -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> { - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/constants_dynamic.rs b/examples/examples/constants_dynamic.rs new file mode 100644 index 0000000000..ba23a8415d --- /dev/null +++ b/examples/examples/constants_dynamic.rs @@ -0,0 +1,17 @@ +use subxt::{OnlineClient, PolkadotConfig}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create a client to use: + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/constants_static.rs b/examples/examples/constants_static.rs new file mode 100644 index 0000000000..1dd9409c0e --- /dev/null +++ b/examples/examples/constants_static.rs @@ -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> { + // Create a client to use: + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/custom_config.rs b/examples/examples/custom_config.rs deleted file mode 100644 index 7c784d1503..0000000000 --- a/examples/examples/custom_config.rs +++ /dev/null @@ -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 = ::Hash; - type Hasher = ::Hasher; - type Header = ::Header; - type AccountId = ::AccountId; - type Address = ::Address; - type Signature = ::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; -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - // Create a client to use: - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/custom_metadata_url.rs b/examples/examples/custom_metadata_url.rs deleted file mode 100644 index 7fb1893075..0000000000 --- a/examples/examples/custom_metadata_url.rs +++ /dev/null @@ -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() {} diff --git a/examples/examples/custom_type_derives.rs b/examples/examples/custom_type_derives.rs deleted file mode 100644 index 123b9d336c..0000000000 --- a/examples/examples/custom_type_derives.rs +++ /dev/null @@ -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> { - let pallet_id = PalletId([1u8; 8]); - let _ = pallet_id.clone(); - Ok(()) -} diff --git a/examples/examples/dynamic_multisig.rs b/examples/examples/dynamic_multisig.rs deleted file mode 100644 index 68d43347a3..0000000000 --- a/examples/examples/dynamic_multisig.rs +++ /dev/null @@ -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> { - 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::::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(()) -} diff --git a/examples/examples/dynamic_queries.rs b/examples/examples/dynamic_queries.rs deleted file mode 100644 index c7c69fe66e..0000000000 --- a/examples/examples/dynamic_queries.rs +++ /dev/null @@ -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> { - let api = OnlineClient::::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. 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(()) -} diff --git a/examples/examples/events.rs b/examples/examples/events.rs new file mode 100644 index 0000000000..4c35398349 --- /dev/null +++ b/examples/examples/events.rs @@ -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> { + // Create a client to use: + let api = OnlineClient::::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::() { + println!("{ev:?}"); + } else { + println!(""); + } + } + + // 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::() { + println!("{ev:?}"); + } else { + continue; + } + } + + // Or we can look for specific events which match our statically defined ones: + let transfer_event = events.find_first::()?; + if let Some(ev) = transfer_event { + println!(" - Balance transfer success: value: {:?}", ev.amount); + } else { + println!(" - No balance transfer event found in this block"); + } + + Ok(()) +} diff --git a/examples/examples/fetch_all_accounts.rs b/examples/examples/fetch_all_accounts.rs deleted file mode 100644 index 2bc140aa06..0000000000 --- a/examples/examples/fetch_all_accounts.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/fetch_constants.rs b/examples/examples/fetch_constants.rs deleted file mode 100644 index 38762d29be..0000000000 --- a/examples/examples/fetch_constants.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/fetch_staking_details.rs b/examples/examples/fetch_staking_details.rs deleted file mode 100644 index 980b430528..0000000000 --- a/examples/examples/fetch_staking_details.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/metadata_compatibility.rs b/examples/examples/metadata_compatibility.rs deleted file mode 100644 index fab4f770c5..0000000000 --- a/examples/examples/metadata_compatibility.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/multisig.rs b/examples/examples/multisig.rs deleted file mode 100644 index c9d0f6c660..0000000000 --- a/examples/examples/multisig.rs +++ /dev/null @@ -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> { - 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::::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(()) -} diff --git a/examples/examples/rpc_call.rs b/examples/examples/rpc_call.rs deleted file mode 100644 index cdffa03854..0000000000 --- a/examples/examples/rpc_call.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/rpc_call_subscribe_blocks.rs b/examples/examples/rpc_call_subscribe_blocks.rs deleted file mode 100644 index bd5ff2dc0b..0000000000 --- a/examples/examples/rpc_call_subscribe_blocks.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - let api = OnlineClient::::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(()) -} diff --git a/examples/examples/runtime_apis_dynamic.rs b/examples/examples/runtime_apis_dynamic.rs new file mode 100644 index 0000000000..c861c53357 --- /dev/null +++ b/examples/examples/runtime_apis_dynamic.rs @@ -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> { + // Create a client to use: + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/runtime_apis_raw.rs b/examples/examples/runtime_apis_raw.rs new file mode 100644 index 0000000000..fc61ff4b3b --- /dev/null +++ b/examples/examples/runtime_apis_raw.rs @@ -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> { + // Create a client to use: + let api = OnlineClient::::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, RuntimeMetadataPrefixed) = + runtime_apis.call_raw("Metadata_metadata", None).await?; + + println!("{meta:?}"); + Ok(()) +} diff --git a/examples/examples/runtime_apis_static.rs b/examples/examples/runtime_apis_static.rs new file mode 100644 index 0000000000..5e0d256bde --- /dev/null +++ b/examples/examples/runtime_apis_static.rs @@ -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> { + // Create a client to use: + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/runtime_calls.rs b/examples/examples/runtime_calls.rs deleted file mode 100644 index e5337e5a50..0000000000 --- a/examples/examples/runtime_calls.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: - let api = OnlineClient::::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::>::new()); - let versions = api - .runtime_api() - .at_latest() - .await? - .call(runtime_api_call) - .await?; - println!( - " dynamic Metadata_metadata_versions: {:#?}", - versions.to_value() - ); - - Ok(()) -} diff --git a/examples/examples/runtime_types_only.rs b/examples/examples/runtime_types_only.rs deleted file mode 100644 index 20ceb838c3..0000000000 --- a/examples/examples/runtime_types_only.rs +++ /dev/null @@ -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< - ::Address, - RuntimeCall, - ::Signature, - // Usually we are not interested in `SignedExtra`. - (), ->; - -type Header = generic::Header; -type Block = generic::Block; - -#[tokio::main] -async fn main() -> Result<(), Box> { - 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::::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(()) -} diff --git a/examples/examples/setup_client_custom_config.rs b/examples/examples/setup_client_custom_config.rs new file mode 100644 index 0000000000..0acf71ae4a --- /dev/null +++ b/examples/examples/setup_client_custom_config.rs @@ -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 = ::Hash; + type Hasher = ::Hasher; + type Header = ::Header; + type AccountId = ::AccountId; + type Address = ::Address; + type Signature = ::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; +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create a client which uses the custom config: + let _api = OnlineClient::::new().await?; + + Ok(()) +} diff --git a/examples/examples/custom_rpc_client.rs b/examples/examples/setup_client_custom_rpc.rs similarity index 91% rename from examples/examples/custom_rpc_client.rs rename to examples/examples/setup_client_custom_rpc.rs index a5fe4e0d98..ed04f1f4fa 100644 --- a/examples/examples/custom_rpc_client.rs +++ b/examples/examples/setup_client_custom_rpc.rs @@ -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> { - tracing_subscriber::fmt::init(); - // Instantiate our replacement RPC client. let log = Arc::default(); let rpc_client = MyLoggingClient { diff --git a/examples/examples/setup_client_offline.rs b/examples/examples/setup_client_offline.rs new file mode 100644 index 0000000000..ba5d9b31ab --- /dev/null +++ b/examples/examples/setup_client_offline.rs @@ -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> { + // 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::::new(genesis_hash, runtime_version, metadata); + + Ok(()) +} diff --git a/examples/examples/storage_fetch.rs b/examples/examples/storage_fetch.rs new file mode 100644 index 0000000000..1a5b034be5 --- /dev/null +++ b/examples/examples/storage_fetch.rs @@ -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> { + // Create a new API client, configured to talk to Polkadot nodes. + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/storage_fetch_dynamic.rs b/examples/examples/storage_fetch_dynamic.rs new file mode 100644 index 0000000000..43325770ea --- /dev/null +++ b/examples/examples/storage_fetch_dynamic.rs @@ -0,0 +1,27 @@ +use sp_keyring::AccountKeyring; +use subxt::dynamic::{At, Value}; +use subxt::{OnlineClient, PolkadotConfig}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create a new API client, configured to talk to Polkadot nodes. + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/storage_iterating.rs b/examples/examples/storage_iterating.rs index ae439e0510..4a6a0159a6 100644 --- a/examples/examples/storage_iterating.rs +++ b/examples/examples/storage_iterating.rs @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: + // Create a new API client, configured to talk to Polkadot nodes. let api = OnlineClient::::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().at_latest().await?.iter(key_addr, 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}"); - } - } + // Build a storage query to iterate over account information. + let storage_query = polkadot::storage().system().account_root(); + + // 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?; + + while let Some((key, value)) = results.next().await? { + println!("Key: 0x{}", hex::encode(&key)); + println!("Value: {:?}", value); } Ok(()) diff --git a/examples/examples/storage_iterating_dynamic.rs b/examples/examples/storage_iterating_dynamic.rs new file mode 100644 index 0000000000..9deca9ad8a --- /dev/null +++ b/examples/examples/storage_iterating_dynamic.rs @@ -0,0 +1,25 @@ +use subxt::{OnlineClient, PolkadotConfig}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create a new API client, configured to talk to Polkadot nodes. + let api = OnlineClient::::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(()) +} diff --git a/examples/examples/submit_and_watch.rs b/examples/examples/submit_and_watch.rs deleted file mode 100644 index d45d629604..0000000000 --- a/examples/examples/submit_and_watch.rs +++ /dev/null @@ -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> { - 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> { - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = OnlineClient::::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::()?; - - 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> { - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = OnlineClient::::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::()?; - - 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::()?; - 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> { - let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); - - let api = OnlineClient::::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::()?; - - 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::()?; - - 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(()) -} diff --git a/examples/examples/subscribe_block_events.rs b/examples/examples/subscribe_block_events.rs deleted file mode 100644 index 02452cd4bc..0000000000 --- a/examples/examples/subscribe_block_events.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: - let api = OnlineClient::::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::()? - .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::()?; - - if let Some(ev) = transfer_event { - println!(" - Balance transfer success: value: {:?}", ev.amount); - } else { - println!(" - No balance transfer event found in this block"); - } - } - - Ok(()) -} diff --git a/examples/examples/subscribe_runtime_updates.rs b/examples/examples/subscribe_runtime_updates.rs deleted file mode 100644 index 29145f0d8c..0000000000 --- a/examples/examples/subscribe_runtime_updates.rs +++ /dev/null @@ -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> { - tracing_subscriber::fmt::init(); - - // Create a client to use: - let api = OnlineClient::::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(()) -} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index f0621a1ec4..8954679305 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -2,113 +2,6 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -//! Generate a strongly typed API for interacting with a Substrate runtime from its metadata. -//! -//! Usage: -//! -//! Download metadata from a running Substrate node using `subxt-cli`: -//! -//! ```bash -//! subxt metadata > polkadot_metadata.scale -//! ``` -//! -//! Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file. -//! -//! ```ignore -//! #[subxt::subxt( -//! runtime_metadata_path = "polkadot_metadata.scale", -//! )] -//! pub mod polkadot {} -//! ``` -//! -//! The `subxt` macro will populate the annotated module with all of the methods and types required -//! for submitting extrinsics and reading from storage for the given runtime. -//! -//! ## Substituting types -//! -//! In order to replace a generated type by a user-defined type, use `substitute_type`: -//! -//! ```ignore -//! #[subxt::subxt( -//! runtime_metadata_path = "polkadot_metadata.scale", -//! substitute_type(path = "sp_arithmetic::per_things::Perbill", with = "sp_runtime::Perbill") -//! )] -//! pub mod polkadot {} -//! ``` -//! -//! This will replace the generated type and any usages with the specified type at the `use` import. -//! It is useful for using custom decoding for specific types, or to provide a type with foreign -//! trait implementations, or other specialized functionality. - -//! ## Custom Derives -//! -//! By default all generated types are annotated with `scale::Encode` and `scale::Decode` derives. -//! However when using the generated types in the client, they may require additional derives to be -//! useful. -//! -//! ### Adding derives for all types -//! -//! Add `derive_for_all_types` with a comma separated list of the derives to apply to *all* types -//! -//! ```ignore -//! #[subxt::subxt( -//! runtime_metadata_path = "polkadot_metadata.scale", -//! derive_for_all_types = "Eq, PartialEq" -//! )] -//! pub mod polkadot {} -//! ``` -//! -//! ### Adding derives for specific types -//! -//! Add `derive_for_type` for each specific type with a comma separated list of the derives to -//! apply for that type only. -//! -//! ```ignore -//! #[subxt::subxt( -//! runtime_metadata_path = "polkadot_metadata.scale", -//! derive_for_all_types = "Eq, PartialEq", -//! derive_for_type(path = "frame_support::PalletId", derive = "Ord, PartialOrd"), -//! derive_for_type(path = "sp_runtime::ModuleError", derive = "Hash"), -//! )] -//! pub mod polkadot {} -//! ``` -//! -//! ### Custom crate path -//! -//! In order to specify a custom crate path to be used for the code generation: -//! -//! ```ignore -//! #[subxt::subxt(crate = "crate::path::to::subxt")] -//! pub mod polkadot {} -//! ``` -//! -//! By default the path `::subxt` is used. -//! -//! ### Expose documentation -//! -//! In order to expose the documentation from the runtime metadata on the generated -//! code, users must specify the `generate_docs` flag: -//! -//! ```ignore -//! #[subxt::subxt(generate_docs)] -//! pub mod polkadot {} -//! ``` -//! -//! By default the documentation is not generated. -//! -//! ### Runtime types generation -//! -//! In some cases, you may be interested only in the runtime types, like `RuntimeCall` enum. You can -//! limit code generation to just `runtime_types` module with `runtime_types_only` flag: -//! -//! ```ignore -//! #[subxt::subxt(runtime_types_only)] -//! // or equivalently -//! #[subxt::subxt(runtime_types_only = true)] -//! ``` - -#![deny(unused_crate_dependencies)] - extern crate proc_macro; use std::str::FromStr; @@ -174,6 +67,7 @@ struct SubstituteType { with: syn::Path, } +// Note: docs for this are in the subxt library; don't add any here as they will be appended. #[proc_macro_attribute] #[proc_macro_error] pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { diff --git a/subxt/src/blocks/mod.rs b/subxt/src/blocks/mod.rs index d60df23d99..9d6e62d8b3 100644 --- a/subxt/src/blocks/mod.rs +++ b/subxt/src/blocks/mod.rs @@ -7,5 +7,5 @@ mod block_types; mod blocks_client; -pub use block_types::{Block, Extrinsic, ExtrinsicEvents}; +pub use block_types::{Block, BlockBody, Extrinsic, ExtrinsicEvents}; pub use blocks_client::{subscribe_to_block_headers_filling_in_gaps, BlocksClient}; diff --git a/subxt/src/book/mod.rs b/subxt/src/book/mod.rs new file mode 100644 index 0000000000..47124df1ab --- /dev/null +++ b/subxt/src/book/mod.rs @@ -0,0 +1,96 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +// Dev note; I used the following command to normalize and wrap comments: +// rustfmt +nightly --config wrap_comments=true,comment_width=100,normalize_comments=true subxt/src/book/mod.rs +// It messed up comments in code blocks though, so be prepared to go and fix those. + +//! # The Subxt Guide +//! +//! Subxt is a library for interacting with Substrate based nodes. It has a focus on **sub**mitting +//! e**xt**rinsics, hence the name, however it's also capable of reading blocks, storage, events and +//! constants from a node. The aim of this guide is to explain key concepts and get you started with +//! using Subxt. +//! +//! 1. [Features](#features-at-a-glance) +//! 2. [Limitations](#limitations) +//! 3. [Quick start](#quick-start) +//! 4. [Usage](#usage) +//! +//! ## Features at a glance +//! +//! Here's a quick overview of the features that Subxt has to offer: +//! +//! - Subxt allows you to generate a static, type safe interface to a node given some metadata; this +//! allows you to catch many errors at compile time rather than runtime. +//! - Subxt also makes heavy use of node metadata to encode/decode the data sent to/from it. This +//! allows it to target almost any node which can output the correct metadata, and allows it some +//! flexibility in encoding and decoding things to account for cross-node differences. +//! - Subxt has a pallet-oriented interface, meaning that code you write to talk to some pallet on +//! one node will often "Just Work" when pointed at different nodes that use the same pallet. +//! - Subxt can work offline; you can generate and sign transactions, access constants from node +//! metadata and more, without a network connection. This is all checked at compile time, so you +//! can be certain it won't try to establish a network connection if you don't want it to. +//! - Subxt can forego the statically generated interface and build transactions, storage queries +//! and constant queries using data provided at runtime, rather than queries constructed +//! statically. +//! - Subxt can be compiled to WASM to run in the browser, allowing it to back Rust based browser +//! apps, or even bind to JS apps. +//! +//! ## Limitations +//! +//! In various places, you can provide a block hash to access data at a particular block, for +//! instance: +//! +//! - [`crate::storage::StorageClient::at`] +//! - [`crate::events::EventsClient::at`] +//! - [`crate::blocks::BlocksClient::at`] +//! - [`crate::runtime_api::RuntimeApiClient::at`] +//! +//! However, Subxt is (by default) only capable of properly working with blocks that were produced +//! after the most recent runtime update. This is because it uses the most recent metadata given +//! back by a node to encode and decode things. It's possible to decode older blocks produced by a +//! runtime that emits compatible (currently, V14) metadata by manually setting the metadata used by +//! the client using [`crate::client::OnlineClient::set_metadata()`]. +//! +//! Subxt does not support working with blocks produced prior to the runtime update that introduces +//! V14 metadata. It may have some success decoding older blocks using newer metadata, but may also +//! completely fail to do so. +//! +//! ## Quick start +//! +//! Here is a simple but complete example of using Subxt to transfer some tokens from the example +//! accounts, Alice to Bob: +//! +//! ```rust,ignore +#![doc = include_str!("../../../examples/examples/balance_transfer_basic.rs")] +//! ``` +//! +//! This example assumes that a Polkadot node is running locally (Subxt endeavors to support all +//! recent releases). Typically, to use Subxt to talk to some custom Substrate node (for example a +//! parachain node), you'll want to: +//! +//! 1. [Generate an interface](setup::codegen). +//! 2. [Configure and instantiate the client](setup::client). +//! +//! Follow the above links to learn more about each step. +//! +//! ## Usage +//! +//! Once Subxt is configured, the next step is interacting with a node. Follow the links +//! below to learn more about how to use Subxt for each of the following things: +//! +//! - [Extrinsics](usage::extrinsics): Subxt can build and submit extrinsics, wait until they are in +//! blocks, and retrieve the associated events. +//! - [Storage](usage::storage): Subxt can query the node storage. +//! - [Events](usage::events): Subxt can read the events emitted for recent blocks. +//! - [Constants](usage::constants): Subxt can access the constant values stored in a node, which +//! remain the same for a given runtime version. +//! - [Blocks](usage::blocks): Subxt can load recent blocks or subscribe to new/finalized blocks, +//! reading the extrinsics, events and storage at these blocks. +//! - [Runtime APIs](usage::runtime_apis): Subxt can make calls into pallet runtime APIs to retrieve +//! data. +//! +pub mod setup; +pub mod usage; diff --git a/subxt/src/book/setup/client.rs b/subxt/src/book/setup/client.rs new file mode 100644 index 0000000000..92fd621769 --- /dev/null +++ b/subxt/src/book/setup/client.rs @@ -0,0 +1,54 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Configuring the Subxt client +//! +//! Subxt ships with two clients, an [offline client](crate::client::OfflineClient) and an [online +//! client](crate::client::OnlineClient). These are backed by the traits +//! [`crate::client::OfflineClientT`] and [`crate::client::OnlineClientT`], so in theory it's +//! possible for users to implement their own clients, although this isn't generally expected. +//! +//! Both clients are generic over a [`crate::config::Config`] trait, which is the way that we give +//! the client certain information about how to interact with a node that isn't otherwise available +//! or possible to include in the node metadata. Subxt ships out of the box with two default +//! implementations: +//! +//! - [`crate::config::PolkadotConfig`] for talking to Polkadot nodes, and +//! - [`crate::config::SubstrateConfig`] for talking to generic nodes built with Substrate. +//! +//! The latter will generally work in many cases, but will need modifying if the chain you'd like to +//! connect to has altered any of the details mentioned in [the trait](`crate::config::Config`). +//! +//! In the case of the [`crate::OnlineClient`], we have a few options to instantiate it: +//! +//! - [`crate::OnlineClient::new()`] to connect to a node running locally. +//! - [`crate::OnlineClient::from_url()`] to connect to a node at a specific URL. +//! - [`crate::OnlineClient::from_rpc_client()`] to instantiate the client with a custom RPC +//! implementation. +//! +//! The latter accepts anything that implements the low level [`crate::rpc::RpcClientT`] trait; this +//! allows you to decide how Subxt will attempt to talk to a node if you'd prefer something other +//! than the provided interfaces. +//! +//! ## Examples +//! +//! Defining some custom config based off the default Substrate config: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/setup_client_custom_config.rs")] +//! ``` +//! Writing a custom [`crate::rpc::RpcClientT`] implementation: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/setup_client_custom_rpc.rs")] +//! ``` +//! Creating an [`crate::OfflineClient`]: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/setup_client_offline.rs")] +//! ``` +//! diff --git a/subxt/src/book/setup/codegen.rs b/subxt/src/book/setup/codegen.rs new file mode 100644 index 0000000000..5cf0d8032d --- /dev/null +++ b/subxt/src/book/setup/codegen.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Generating an interface +//! +//! The simplest way to use Subxt is to generate an interface to a chain that you'd like to interact +//! with. This generated interface allows you to build transactions and construct queries to access +//! data while leveraging the full type safety of the Rust compiler. +//! +//! ## The `#[subxt]` macro +//! +//! The most common way to generate the interface is to use the [`#[subxt]`](crate::subxt) macro. +//! Using this macro looks something like: +//! +//! ```rust,no_run +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! ``` +//! +//! The macro takes a path to some node metadata, and uses that to generate the interface you'll use +//! to talk to it. [Go here](crate::subxt) to learn more about the options available to the macro. +//! +//! To obtain this metadata you'll need for the above, you can use the `subxt` CLI tool to download it +//! from a node. The tool can be installed via `cargo`: +//! +//! ```shell +//! cargo install subxt-cli +//! ``` +//! +//! And then it can be used to fetch metadata and save it to a file: +//! +//! ```shell +//! # Download and save all of the metadata: +//! subxt metadata > metadata.scale +//! # Download and save only the pallets you want to generate an interface for: +//! subxt metadata --pallets Balances,System > metadata.scale +//! ``` +//! +//! Explicitly specifying pallets will cause the tool to strip out all unnecessary metadata and type +//! information, making the bundle much smaller in the event that you only need to generate an +//! interface for a subset of the available pallets on the node. +//! +//! ## The CLI tool +//! +//! Using the [`#[subxt]`](crate::subxt) macro carries some downsides: +//! +//! - Using it to generate an interface will have a small impact on compile times (though much less of +//! one if you only need a few pallets). +//! - IDE support for autocompletion and documentation when using the macro interface can be poor. +//! - It's impossible to manually look at the generated code to understand and debug things. +//! +//! If these are an issue, you can manually generate the same code that the macro generates under the hood +//! by using the `subxt codegen` command: +//! +//! ```shell +//! # Install the CLI tool if you haven't already: +//! cargo install subxt-cli +//! # Generate and format rust code, saving it to `interface.rs`: +//! subxt codegen | rustfmt > interface.rs +//! ``` +//! +//! Use `subxt codegen --help` for more options; many of the options available via the macro are +//! also available via the CLI tool, such as the ability to substitute generated types for others, +//! or strip out docs from the generated code. +//! diff --git a/subxt/src/book/setup/mod.rs b/subxt/src/book/setup/mod.rs new file mode 100644 index 0000000000..f3f3c8e109 --- /dev/null +++ b/subxt/src/book/setup/mod.rs @@ -0,0 +1,13 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! This modules contains details on setting up Subxt: +//! +//! - [Codegen](codegen) +//! - [Client](client) +//! +//! Alternately, [go back](super). + +pub mod client; +pub mod codegen; diff --git a/subxt/src/book/usage/blocks.rs b/subxt/src/book/usage/blocks.rs new file mode 100644 index 0000000000..b991500f86 --- /dev/null +++ b/subxt/src/book/usage/blocks.rs @@ -0,0 +1,39 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Blocks +//! +//! The [blocks API](crate::blocks::BlocksClient) in Subxt unifies many of the other interfaces, and +//! allows you to: +//! +//! - Access information about specific blocks (see [`crate::blocks::BlocksClient::at()`] and +//! [`crate::blocks::BlocksClient::at_latest()`]). +//! - Subscribe to [all](crate::blocks::BlocksClient::subscribe_all()), +//! [best](crate::blocks::BlocksClient::subscribe_best()) or +//! [finalized](crate::blocks::BlocksClient::subscribe_finalized()) blocks as they are produced. +//! Prefer to subscribe to finalized blocks unless you know what you're doing. +//! +//! In either case, you'll end up with [`crate::blocks::Block`]'s, from which you can access various +//! information about the block, such a the [header](crate::blocks::Block::header()), [block +//! number](crate::blocks::Block::number()) and [body](crate::blocks::Block::body()). +//! [`crate::blocks::Block`]'s also provide shortcuts to other Subxt APIs that will operate at the +//! given block: +//! +//! - [storage](crate::blocks::Block::storage()), +//! - [events](crate::blocks::Block::events()) +//! - [runtime APIs](crate::blocks::Block::runtime_api()) +//! +//! ## Example +//! +//! Given a block, you can [download the block body](crate::blocks::Block::body()) and iterate over +//! the extrinsics stored within it using [`crate::blocks::BlockBody::extrinsics()`]. +//! +//! Here's an example in which we subscribe to blocks and print a bunch of information about each +//! one: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/blocks_subscribing.rs")] +//! ``` +//! diff --git a/subxt/src/book/usage/constants.rs b/subxt/src/book/usage/constants.rs new file mode 100644 index 0000000000..94074113ee --- /dev/null +++ b/subxt/src/book/usage/constants.rs @@ -0,0 +1,61 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Constants +//! +//! There are various constants stored in a node; the types and values of these are defined in a +//! runtime, and can only change when the runtime is updated. Much like [`super::storage`], we can +//! query these using Subxt by taking the following steps: +//! +//! 1. [Constructing a constant query](#constructing-a-query). +//! 2. [Submitting the query to get back the associated value](#submitting-it). +//! +//! ## Constructing a constant query +//! +//! We can use the statically generated interface to build constant queries: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! +//! let constant_query = polkadot::constants().system().block_length(); +//! ``` +//! +//! Alternately, we can dynamically construct a constant query: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! use subxt::dynamic::Value; +//! +//! let account = AccountKeyring::Alice.to_account_id(); +//! let storage_query = subxt::dynamic::constant("System", "BlockLength"); +//! ``` +//! +//! Static queries also have a static return type, so the constant is decoded appropriately. In +//! addition, they are validated at runtime to ensure that they align with the current node state. +//! Dynamic queries must be decoded into some static type manually, or into the dynamic +//! [`crate::dynamic::Value`] type. +//! +//! ## Submitting it +//! +//! Constant queries are handed to Subxt via [`crate::constants::ConstantsClient::at()`]. It's worth +//! noting that constant values are pulled directly out of the node metadata which Subxt has +//! already acquired, and so this function requires no network access and is available from a +//! [`crate::OfflineClient`]. +//! +//! Here's an example using a static query: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/constants_static.rs")] +//! ``` +//! And here's one using a dynamic query: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/constants_dynamic.rs")] +//! ``` +//! diff --git a/subxt/src/book/usage/events.rs b/subxt/src/book/usage/events.rs new file mode 100644 index 0000000000..b64382731e --- /dev/null +++ b/subxt/src/book/usage/events.rs @@ -0,0 +1,50 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Events +//! +//! In the process of adding extrinsics to a block, they are executed. When extrinsics are executed, +//! they normally produce events describing what's happening (at the very least, an event dictating whether +//! the extrinsic has succeeded or failed). The node may also emit some events of its own as the block is +//! processed. +//! +//! Events live in a single location in node storage which is overwritten at each block. Normal nodes tend to +//! keep a snapshot of the state at a small number of previous blocks, so you can sometimes access +//! older events by using [`crate::events::EventsClient::at()`] and providing an older block hash. +//! +//! When we submit extrinsics using Subxt, methods like [`crate::tx::TxProgress::wait_for_finalized_success()`] +//! return [`crate::blocks::ExtrinsicEvents`], which can be used to iterate and inspect the events produced +//! for a specific extrinsic. We can also access _all_ of the events produced in a single block using one of these +//! two interfaces: +//! +//! ```rust,no_run +//! # #[tokio::main] +//! # async fn main() -> Result<(), Box> { +//! use subxt::client::OnlineClient; +//! use subxt::config::PolkadotConfig; +//! +//! // Create client: +//! let client = OnlineClient::::new().await?; +//! +//! // Get events from the latest block (use .at() to specify a block hash): +//! let events = client.blocks().at_latest().await?.events().await?; +//! // We can use this shorthand too: +//! let events = client.events().at_latest().await?; +//! # Ok(()) +//! # } +//! ``` +//! +//! Once we've loaded our events, we can iterate all events or search for specific events via +//! methods like [`crate::events::Events::iter()`] and [`crate::events::Events::find()`]. See +//! [`crate::events::Events`] and [`crate::events::EventDetails`] for more information. +//! +//! ## Example +//! +//! Here's an example which puts this all together: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/events.rs")] +//! ``` +//! diff --git a/subxt/src/book/usage/extrinsics.rs b/subxt/src/book/usage/extrinsics.rs new file mode 100644 index 0000000000..3d409f5772 --- /dev/null +++ b/subxt/src/book/usage/extrinsics.rs @@ -0,0 +1,175 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Extrinsics +//! +//! Extrinsics define function calls and their parameters, and are the only way that you can change +//! the state of the blockchain. Submitting extrinsics to a node is one of the core features of +//! Subxt, and generally consists of the following steps: +//! +//! 1. [Constructing an extrinsic payload to submit](#constructing-an-extrinsic-payload). +//! 2. [Signing it](#signing-it). +//! 3. [Submitting it (optionally with some additional parameters)](#submitting-it). +//! +//! We'll look at each of these steps in turn. +//! +//! > As a side note, an _extrinsic_ is anything that can be added to a block, and a _transaction_ +//! > is an extrinsic that is submitted from a particular user (and is therefore also signed). Subxt +//! > can construct unsigned extrinsics, but overwhelmingly you'll need to sign them, and so the +//! > documentation tends to use the terms _extrinsic_ and _transaction_ interchangeably. +//! +//! ## Constructing an extrinsic payload +//! +//! We can use the statically generated interface to build extrinsic payloads: +//! +//! ```rust,no_run +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! +//! let remark = "Hello there".as_bytes().to_vec(); +//! let extrinsic_payload = polkadot::tx().system().remark(remark); +//! ``` +//! +//! > If you're not sure what types to import and use to build a given payload, you can use the +//! > `subxt` CLI tool to generate the interface by using something like `subxt codegen | rustfmt > +//! > interface.rs`, to see what types and things are available (or even just to use directly +//! > instead of the [`#[subxt]`](crate::subxt) macro). +//! +//! Alternately, we can dynamically construct an extrinsic payload. This will not be type checked or +//! validated until it's submitted: +//! +//! ```rust,no_run +//! use subxt::dynamic::Value; +//! +//! let extrinsic_payload = subxt::dynamic::tx("System", "remark", vec![ +//! Value::from_bytes("Hello there") +//! ]); +//! ``` +//! +//! The [`crate::dynamic::Value`] type is a dynamic type much like a `serde_json::Value` but instead +//! represents any type of data that can be SCALE encoded or decoded. It can be serialized, +//! deserialized and parsed from/to strings. +//! +//! A valid extrinsic payload is just something that implements the [`crate::tx::TxPayload`] trait; +//! you can implement this trait on your own custom types if the built-in ones are not suitable for +//! your needs. +//! +//! ## Signing it +//! +//! You'll normally need to sign an extrinsic to prove that it originated from an account that you +//! control. To do this, you will typically first create an [`crate::tx::Signer`], which tells Subxt +//! who the extrinsic is from, and takes care of signing the relevant details to prove this. +//! +//! Subxt provides a [`crate::tx::PairSigner`] which implements this trait (if the +//! `substrate-compat` feature is enabled) which accepts any valid [`sp_core::Pair`] and uses that +//! to sign transactions: +//! +//! ```rust +//! use subxt::tx::PairSigner; +//! use sp_core::Pair; +//! use subxt::config::PolkadotConfig; +//! +//! // Get hold of a `Signer` given a test account: +//! let pair = sp_keyring::AccountKeyring::Alice.pair(); +//! let signer = PairSigner::::new(pair); +//! +//! // Or generate an `sr25519` keypair to use: +//! let (pair, _, _) = sp_core::sr25519::Pair::generate_with_phrase(Some("password")); +//! let signer = PairSigner::::new(pair); +//! ``` +//! +//! See the [`sp_core::Pair`] docs for more ways to generate them. +//! +//! If this isn't suitable/available, you can either implement [`crate::tx::Signer`] yourself to use +//! custom signing logic, or you can use some external signing logic, like so: +//! +//! ```rust,no_run +//! # #[tokio::main] +//! # async fn main() -> Result<(), Box> { +//! use subxt::client::OnlineClient; +//! use subxt::config::PolkadotConfig; +//! use subxt::dynamic::Value; +//! +//! // Create client: +//! let client = OnlineClient::::new().await?; +//! +//! // Create a dummy extrinsic payload to sign: +//! let payload = subxt::dynamic::tx("System", "remark", vec![ +//! Value::from_bytes("Hello there") +//! ]); +//! +//! // Construct the extrinsic but don't sign it. You need to provide the nonce +//! // here, or can use `create_partial_signed` to fetch the correct nonce. +//! let partial_extrinsic = client.tx().create_partial_signed_with_nonce( +//! &payload, +//! 0, +//! Default::default() +//! )?; +//! +//! // Fetch the payload that needs to be signed: +//! let signer_payload = partial_extrinsic.signer_payload(); +//! +//! // ... At this point, we can hand off the `signer_payload` to be signed externally. +//! // Ultimately we need to be given back a `signature` (or really, anything +//! // that can be SCALE encoded) and an `address`: +//! let signature; +//! let address; +//! # use subxt::tx::Signer; +//! # let pair = sp_keyring::AccountKeyring::Alice.pair(); +//! # let signer = subxt::tx::PairSigner::::new(pair); +//! # signature = signer.sign(&signer_payload); +//! # address = signer.address(); +//! +//! // Now we can build an extrinsic, which one can call `submit` or `submit_and_watch` +//! // on to submit to a node and optionally watch the status. +//! let extrinsic = partial_extrinsic.sign_with_address_and_signature( +//! &address, +//! &signature +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Submitting it +//! +//! Once we are able to sign the extrinsic, we need to submit it. +//! +//! ### The high level API +//! +//! The highest level approach to doing this is to call +//! [`crate::tx::TxClient::sign_and_submit_then_watch_default`]. This hands back a +//! [`crate::tx::TxProgress`] struct which will monitor the transaction status. We can then call +//! [`crate::tx::TxProgress::wait_for_finalized_success()`] to wait for this transaction to make it +//! into a finalized block, check for an `ExtrinsicSuccess` event, and then hand back the events for +//! inspection. This looks like: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/balance_transfer_basic.rs")] +//! ``` +//! ### Providing extrinsic parameters +//! +//! If you'd like to provide extrinsic parameters (such as mortality), you can use +//! [`crate::tx::TxClient::sign_and_submit_then_watch`] instead, and provide parameters for your +//! chain: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/balance_transfer_with_params.rs")] +//! ``` +//! This example doesn't wait for the extrinsic to be included in a block; it just submits it and +//! hopes for the best! +//! +//! ### Custom handling of transaction status updates +//! +//! If you'd like more control or visibility over exactly which status updates are being emitted for +//! the transaction, you can monitor them as they are emitted and react however you choose: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/balance_transfer_status_stream.rs")] +//! ``` +//! Take a look at the API docs for [`crate::tx::TxProgress`], [`crate::tx::TxStatus`] and +//! [`crate::tx::TxInBlock`] for more options. +//! diff --git a/subxt/src/book/usage/mod.rs b/subxt/src/book/usage/mod.rs new file mode 100644 index 0000000000..8243cf2ce2 --- /dev/null +++ b/subxt/src/book/usage/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! This modules contains examples of using Subxt; follow the links for more: +//! +//! - [Extrinsics](extrinsics) +//! - [Storage](storage) +//! - [Events](events) +//! - [Constants](constants) +//! - [Blocks](blocks) +//! - [Runtime APIs](runtime_apis) +//! +//! Alternately, [go back](super). + +pub mod blocks; +pub mod constants; +pub mod events; +pub mod extrinsics; +pub mod runtime_apis; +pub mod storage; diff --git a/subxt/src/book/usage/runtime_apis.rs b/subxt/src/book/usage/runtime_apis.rs new file mode 100644 index 0000000000..f5b4dd0799 --- /dev/null +++ b/subxt/src/book/usage/runtime_apis.rs @@ -0,0 +1,81 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Runtime API interface +//! +//! The Runtime API interface allows Subxt to call runtime APIs exposed by certain pallets in order +//! to obtain information. Much like [`super::storage`] and [`super::extrinsics`], Making a runtime +//! call to a node and getting the response back takes the following steps: +//! +//! 1. [Constructing a runtime call](#constructing-a-runtime-call) +//! 2. [Submitting it to get back the response](#submitting-it) +//! +//! **Note:** Runtime APIs are only available when using V15 metadata, which is currently unstable. +//! You'll need to use `subxt metadata --version unstable` command to download the unstable V15 metadata, +//! and activate the `unstable-metadata` feature in Subxt for it to also use this metadata from a node. The +//! metadata format is unstable because it may change and break compatibility with Subxt at any moment, so +//! use at your own risk. +//! +//! ## Constructing a runtime call +//! +//! We can use the statically generated interface to build runtime calls: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! +//! let runtime_call = polkadot::apis().metadata().metadata_versions(); +//! ``` +//! +//! Alternately, we can dynamically construct a runtime call: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! use subxt::dynamic::Value; +//! +//! let account = AccountKeyring::Alice.to_account_id(); +//! let runtime_call = subxt::dynamic::runtime_api_call( +//! "Metadata_metadata_versions", +//! Vec::>::new() +//! ); +//! ``` +//! +//! All valid runtime calls implement [`crate::runtime_api::RuntimeApiPayload`], a trait which +//! describes how to encode the runtime call arguments and what return type to decode from the +//! response. +//! +//! ## Submitting it +//! +//! Runtime calls can be handed to [`crate::runtime_api::RuntimeApi::call()`], which will submit +//! them and hand back the associated response. +//! +//! ### Making a static Runtime API call +//! +//! The easiest way to make a runtime API call is to use the statically generated interface. +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/runtime_apis_static.rs")] +//! ``` +//! +//! ### Making a dynamic Runtime API call +//! +//! If you'd prefer to construct the call at runtime, you can do this using the +//! [`crate::dynamic::runtime_api_call`] method. +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/runtime_apis_dynamic.rs")] +//! ``` +//! +//! ### Making a raw call +//! +//! This is generally discouraged in favour of one of the above, but may be necessary (especially if +//! the node you're talking to does not yet serve V15 metadata). Here, you must manually encode +//! the argument bytes and manually provide a type for the response bytes to be decoded into. +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/runtime_apis_raw.rs")] +//! ``` +//! diff --git a/subxt/src/book/usage/storage.rs b/subxt/src/book/usage/storage.rs new file mode 100644 index 0000000000..92efb13890 --- /dev/null +++ b/subxt/src/book/usage/storage.rs @@ -0,0 +1,110 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! # Storage +//! +//! A Substrate based chain has storage, whose values are determined by the extrinsics added to past +//! blocks. Subxt allows you to query the storage of a node, which consists of the following steps: +//! +//! 1. [Constructing a storage query](#constructing-a-storage-query). +//! 2. [Submitting the query to get back the associated values](#submitting-it). +//! +//! ## Constructing a storage query +//! +//! We can use the statically generated interface to build storage queries: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! +//! let account = AccountKeyring::Alice.to_account_id().into(); +//! let storage_query = polkadot::storage().system().account(&account); +//! ``` +//! +//! Alternately, we can dynamically construct a storage query. This will not be type checked or +//! validated until it's submitted: +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! use subxt::dynamic::Value; +//! +//! let account = AccountKeyring::Alice.to_account_id(); +//! let storage_query = subxt::dynamic::storage("System", "Account", vec![ +//! Value::from_bytes(account) +//! ]); +//! ``` +//! +//! As well as accessing specific entries, some storage locations can also be iterated over (such as +//! the map of account information). To do this, suffix `_root` onto the query constructor (this +//! will only be available on static constructors when iteration is actually possible): +//! +//! ```rust,no_run +//! use sp_keyring::AccountKeyring; +//! +//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] +//! pub mod polkadot {} +//! +//! // A static query capable of iterating over accounts: +//! let storage_query = polkadot::storage().system().account_root(); +//! // A dynamic query to do the same: +//! let storage_query = subxt::dynamic::storage_root("System", "Account"); +//! ``` +//! +//! All valid storage queries implement [`crate::storage::StorageAddress`]. As well as describing +//! how to build a valid storage query, this trait also has some associated types that determine the +//! shape of the result you'll get back, and determine what you can do with it (ie, can you iterate +//! over storage entries using it). +//! +//! Static queries set appropriate values for these associated types, and can therefore only be used +//! where it makes sense. Dynamic queries don't know any better and can be used in more places, but +//! may fail at runtime instead if they are invalid in those places. +//! +//! ## Submitting it +//! +//! Storage queries can be handed to various functions in [`crate::storage::Storage`] in order to +//! obtain the associated values (also referred to as storage entries) back. +//! +//! ### Fetching storage entries +//! +//! The simplest way to access storage entries is to construct a query and then call either +//! [`crate::storage::Storage::fetch()`] or [`crate::storage::Storage::fetch_or_default()`] (the +//! latter will only work for storage queries that have a default value when empty): +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/storage_fetch.rs")] +//! ``` +//! For completeness, below is an example using a dynamic query instead. The return type from a +//! dynamic query is a [`crate::dynamic::DecodedValueThunk`], which can be decoded into a +//! [`crate::dynamic::Value`], or else the raw bytes can be accessed instead. +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/storage_fetch_dynamic.rs")] +//! ``` +//! ### Iterating storage entries +//! +//! Many storage entries are maps of values; as well as fetching individual values, it's possible to +//! iterate over all of the values stored at that location: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/storage_iterating.rs")] +//! ``` +//! Here's the same logic but using dynamically constructed values instead: +//! +//! +//! ```rust,ignore +#![doc = include_str!("../../../../examples/examples/storage_iterating_dynamic.rs")] +//! ``` +//! ### Advanced +//! +//! For more advanced use cases, have a look at [`crate::storage::Storage::fetch_raw`] and +//! [`crate::storage::Storage::fetch_keys`]. Both of these take raw bytes as arguments, which can be +//! obtained from a [`crate::storage::StorageAddress`] by using +//! [`crate::storage::StorageClient::address_bytes()`] or +//! [`crate::storage::StorageClient::address_root_bytes()`]. +//! diff --git a/subxt/src/config/mod.rs b/subxt/src/config/mod.rs index ba569efa39..702a90eb07 100644 --- a/subxt/src/config/mod.rs +++ b/subxt/src/config/mod.rs @@ -29,7 +29,7 @@ pub trait Config: 'static { /// transactions associated with a sender account. type Index: Debug + Copy + DeserializeOwned + Into; - /// The output of the `Hashing` function. + /// The output of the `Hasher` function. type Hash: Debug + Copy + Send @@ -95,7 +95,6 @@ pub trait Header: Sized + Encode { /// Take a type implementing [`Config`] (eg [`SubstrateConfig`]), and some type which describes the /// additional and extra parameters to pass to an extrinsic (see [`ExtrinsicParams`]), /// and returns a type implementing [`Config`] with those new [`ExtrinsicParams`]. -/// ``` pub struct WithExtrinsicParams> { _marker: std::marker::PhantomData<(T, E)>, } diff --git a/subxt/src/dynamic.rs b/subxt/src/dynamic.rs index df65cffb13..ec318da4c9 100644 --- a/subxt/src/dynamic.rs +++ b/subxt/src/dynamic.rs @@ -11,7 +11,7 @@ use crate::{ }; use scale_decode::DecodeAsType; -pub use scale_value::Value; +pub use scale_value::{At, Value}; /// A [`scale_value::Value`] type endowed with contextual information /// regarding what type was used to decode each part of it. This implements diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 3b41f95963..f7eeca7bc3 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -2,112 +2,13 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -//! Subxt is a library to **sub**mit e**xt**rinsics to a [substrate](https://github.com/paritytech/substrate) node via RPC. +//! Subxt is a library for interacting with Substrate based nodes. Using it looks something like this: //! -//! The generated Subxt API exposes the ability to: -//! - [Submit extrinsics](https://docs.substrate.io/v3/concepts/extrinsics/) (Calls) -//! - [Query storage](https://docs.substrate.io/v3/runtime/storage/) (Storage) -//! - [Query constants](https://docs.substrate.io/how-to-guides/v3/basics/configurable-constants/) (Constants) -//! - [Subscribe to events](https://docs.substrate.io/v3/runtime/events-and-errors/) (Events) -//! -//! # Initializing the API client -//! -//! To interact with a node, you'll need to construct a client. -//! -//! ```no_run -//! use subxt::{OnlineClient, PolkadotConfig}; -//! -//! # #[tokio::main] -//! # async fn main() { -//! let api = OnlineClient::::new().await.unwrap(); -//! # } +//! ```rust,ignore +#![doc = include_str!("../../examples/examples/balance_transfer_basic.rs")] //! ``` //! -//! This default client connects to a locally running node, but can be configured to point anywhere. -//! Additionally, an [`crate::OfflineClient`] is available to perform operations that don't require a -//! network connection to a node. -//! -//! The client takes a type parameter, here [`crate::PolkadotConfig`], which bakes in assumptions about -//! the structure of extrinsics and the underlying types used by the node for things like block numbers. -//! If the node you'd like to interact with deviates from Polkadot or the default Substrate node in these -//! areas, you'll need to configure them by implementing the [`crate::config::Config`] type yourself. -//! -//! # Generating runtime types -//! -//! Subxt can optionally generate types at compile time to help you interact with a node. These types are -//! generated using metadata which can be downloaded from a node using the [subxt-cli](https://crates.io/crates/subxt-cli) -//! tool. These generated types provide a degree of type safety when interacting with a node that is compatible with -//! the metadata that they were generated using. We also do runtime checks in case the node you're talking to has -//! deviated from the types you're using to communicate with it (see below). -//! -//! To generate the types, use the `subxt` macro and point it at the metadata you've downloaded, like so: -//! -//! ```ignore -//! #[subxt::subxt(runtime_metadata_path = "metadata.scale")] -//! pub mod node_runtime { } -//! ``` -//! -//! For more information, please visit the [subxt-codegen](https://docs.rs/subxt-codegen/latest/subxt_codegen/) -//! documentation. -//! -//! You can opt to skip this step and use dynamic queries to talk to nodes instead, which can be useful in some cases, -//! but doesn't provide any type safety. -//! -//! # Interacting with the API -//! -//! Once instantiated, a client exposes four core functions: -//! - `.tx()` for submitting extrinsics/transactions. See [`crate::tx::TxClient`] for more details, or see -//! the [balance_transfer](../examples/examples/balance_transfer.rs) example. -//! - `.storage()` for fetching and iterating over storage entries. See [`crate::storage::StorageClient`] for more details, or see -//! the [fetch_staking_details](../examples/examples/fetch_staking_details.rs) example. -//! - `.constants()` for getting hold of constants. See [`crate::constants::ConstantsClient`] for more details, or see -//! the [fetch_constants](../examples/examples/fetch_constants.rs) example. -//! - `.events()` for subscribing/obtaining events. See [`crate::events::EventsClient`] for more details, or see: -//! - [subscribe_all_events](../examples/examples/subscribe_all_events.rs): Subscribe to events emitted from blocks. -//! - [subscribe_one_event](../examples/examples/subscribe_one_event.rs): Subscribe and filter by one event. -//! - [subscribe_some_events](../examples/examples/subscribe_some_events.rs): Subscribe and filter event. -//! -//! # Static Metadata Validation -//! -//! If you use types generated by the [`crate::subxt`] macro, there is a chance that they will fall out of sync -//! with the actual state of the node you're trying to interact with. -//! -//! When you attempt to use any of these static types to interact with a node, Subxt will validate that they are -//! still compatible and issue an error if they have deviated. -//! -//! Additionally, you can validate that the entirety of the statically generated code aligns with a node like so: -//! -//! ```no_run -//! use subxt::{OnlineClient, PolkadotConfig}; -//! -//! #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata.scale")] -//! pub mod polkadot {} -//! -//! # #[tokio::main] -//! # async fn main() { -//! let api = OnlineClient::::new().await.unwrap(); -//! -//! if let Err(_e) = polkadot::validate_codegen(&api) { -//! println!("Generated code is not up to date with node we're connected to"); -//! } -//! # } -//! ``` -//! ## Opting out of static validation -//! -//! The static types that are used to query/access information are validated by default, to make sure that they are -//! compatible with the node being queried. You can generally call `.unvalidated()` on these static types to -//! disable this validation. -//! -//! # Runtime Updates -//! -//! The node you're connected to may occasionally perform runtime updates while you're connected, which would ordinarily -//! leave the runtime state of the node out of sync with the information Subxt requires to do things like submit -//! transactions. -//! -//! If this is a concern, you can use the `UpdateClient` API to keep the `RuntimeVersion` and `Metadata` of the client -//! synced with the target node. -//! -//! Please visit the [subscribe_runtime_updates](../examples/examples/subscribe_runtime_updates.rs) example for more details. +//! Take a look at [the Subxt guide](book) to learn more about how to use Subxt. #![deny( bad_style, @@ -132,13 +33,14 @@ )] #![allow(clippy::type_complexity)] +// The guide is here. +pub mod book; + // Suppress an unused dependency warning because tokio is // only used in example code snippets at the time of writing. #[cfg(test)] use tokio as _; -pub use subxt_macro::subxt; - // Used to enable the js feature for wasm. #[cfg(target_arch = "wasm32")] pub use getrandom as _; @@ -184,3 +86,203 @@ pub mod ext { #[cfg(feature = "substrate-compat")] pub use sp_runtime; } + +/// Generate a strongly typed API for interacting with a Substrate runtime from its metadata. +/// +/// # Metadata +/// +/// First, you'll need to get hold of some metadata for the node you'd like to interact with. One +/// way to do this is by using the `subxt` CLI tool: +/// +/// ```bash +/// # Install the CLI tool: +/// cargo install subxt-cli +/// # Use it to download metadata (in this case, from a node running locally) +/// subxt metadata > polkadot_metadata.scale +/// ``` +/// +/// Run `subxt metadata --help` for more options. +/// +/// # Basic usage +/// +/// Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file. +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// )] +/// mod polkadot {} +/// ``` +/// +/// The `subxt` macro will populate the annotated module with all of the methods and types required +/// for interacting with the runtime that the metadata came from via Subxt. +/// +/// # Configuration +/// +/// This macro supports a number of attributes to configure what is generated: +/// +/// ## `crate = "..."` +/// +/// Use this attribute to specify a custom path to the `subxt` crate: +/// +/// ```rust +/// # pub extern crate subxt; +/// # pub mod path { pub mod to { pub use subxt; } } +/// # fn main() {} +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// crate = "crate::path::to::subxt" +/// )] +/// mod polkadot {} +/// ``` +/// +/// This is useful if you write a library which uses this macro, but don't want to force users to depend on `subxt` +/// at the top level too. By default the path `::subxt` is used. +/// +/// ## `substitute_type(path = "...", with = "...")` +/// +/// This attribute replaces any reference to the generated type at the path given by `path` with a +/// reference to the path given by `with`. +/// +/// ```rust +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// substitute_type(path = "sp_arithmetic::per_things::Perbill", with = "crate::Foo") +/// )] +/// mod polkadot {} +/// +/// # #[derive( +/// # scale_encode::EncodeAsType, +/// # scale_decode::DecodeAsType, +/// # codec::Encode, +/// # codec::Decode, +/// # Clone, +/// # Debug, +/// # )] +/// // In reality this needs some traits implementing on +/// // it to allow it to be used in place of Perbill: +/// pub struct Foo(u32); +/// # impl codec::CompactAs for Foo { +/// # type As = u32; +/// # fn encode_as(&self) -> &Self::As { +/// # &self.0 +/// # } +/// # fn decode_from(x: Self::As) -> Result { +/// # Ok(Foo(x)) +/// # } +/// # } +/// # impl From> for Foo { +/// # fn from(v: codec::Compact) -> Foo { +/// # v.0 +/// # } +/// # } +/// # fn main() {} +/// ``` +/// +/// If the type you're substituting contains generic parameters, you can "pattern match" on those, and +/// make use of them in the substituted type, like so: +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// substitute_type( +/// path = "sp_runtime::multiaddress::MultiAddress", +/// with = "::subxt::utils::Static<::sp_runtime::MultiAddress>" +/// ) +/// )] +/// mod polkadot {} +/// ``` +/// +/// The above is also an example of using the [`crate::utils::Static`] type to wrap some type which doesn't +/// on it's own implement [`scale_encode::EncodeAsType`] or [`scale_decode::DecodeAsType`], which are required traits +/// for any substitute type to implement by default. +/// +/// ## `derive_for_all_types = "..."` +/// +/// By default, all generated types derive a small set of traits. This attribute allows you to derive additional +/// traits on all generated types: +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// derive_for_all_types = "Eq, PartialEq" +/// )] +/// mod polkadot {} +/// ``` +/// +/// Any substituted types (including the default substitutes) must also implement these traits in order to avoid errors +/// here. +/// +/// ## `derive_for_type(path = "...", derive = "...")` +/// +/// Unlike the above, which derives some trait on every generated type, this attribute allows you to derive traits only +/// for specific types. Note that any types which are used inside the specified type may also need to derive the same traits. +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// derive_for_all_types = "Eq, PartialEq", +/// derive_for_type(path = "frame_support::PalletId", derive = "Ord, PartialOrd"), +/// derive_for_type(path = "sp_runtime::ModuleError", derive = "Hash"), +/// )] +/// mod polkadot {} +/// ``` +/// +/// ## `runtime_metadata_url = "..."` +/// +/// This attribute can be used instead of `runtime_metadata_path` and will tell the macro to download metadata from a node running +/// at the provided URL, rather than a node running locally. This can be useful in CI, but is **not recommended** in production code, +/// since it runs at compile time and will cause compilation to fail if the node at the given address is unavailable or unresponsive. +/// +/// ```rust,ignore +/// #[subxt::subxt( +/// runtime_metadata_url = "wss://rpc.polkadot.io:443" +/// )] +/// mod polkadot {} +/// ``` +/// +/// ## `generate_docs` +/// +/// By default, documentation is not generated via the macro, since IDEs do not typically make use of it. This attribute +/// forces documentation to be generated, too. +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// generate_docs +/// )] +/// mod polkadot {} +/// ``` +/// +/// ## `runtime_types_only` +/// +/// By default, the macro will generate various interfaces to make using Subxt simpler in addition with any types that need +/// generating to make this possible. This attribute makes the codegen only generate the types and not the Subxt interface. +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// runtime_types_only +/// )] +/// mod polkadot {} +/// ``` +/// ## `no_default_derives` +/// +/// By default, the macro will add all derives necessary for the generated code to play nicely with Subxt. Adding this attribute +/// removes all default derives. +/// +/// ```rust,no_run +/// #[subxt::subxt( +/// runtime_metadata_path = "../artifacts/polkadot_metadata.scale", +/// runtime_types_only, +/// no_default_derives, +/// derive_for_all_types="codec::Encode, codec::Decode" +/// )] +/// mod polkadot {} +/// ``` +/// +/// **Note**: At the moment, you must derive at least one of `codec::Encode` or `codec::Decode` or `scale_encode::EncodeAsType` or +/// `scale_decode::DecodeAsType` (because we add `#[codec(..)]` attributes on some fields/types during codegen), and you must use this +/// feature in conjunction with `runtime_types_only` (or manually specify a bunch of defaults to make codegen work properly when +/// generating the subxt interfaces). +pub use subxt_macro::subxt; diff --git a/subxt/src/metadata/mod.rs b/subxt/src/metadata/mod.rs index 2e80ac9c15..7ada3e62b7 100644 --- a/subxt/src/metadata/mod.rs +++ b/subxt/src/metadata/mod.rs @@ -13,6 +13,7 @@ pub use metadata_location::MetadataLocation; pub use metadata_type::{ ErrorMetadata, EventMetadata, InvalidMetadataError, Metadata, MetadataError, PalletMetadata, + RuntimeFnMetadata, }; pub use decode_encode_traits::{DecodeWithMetadata, EncodeWithMetadata};