diff --git a/README.md b/README.md index 797546ec89..1758b1f2dc 100644 --- a/README.md +++ b/README.md @@ -22,81 +22,19 @@ subxt metadata -f bytes > metadata.scale This defaults to querying the metadata of a locally running node on the default `http://localhost:9933/`. If querying a different node then the `metadata` command accepts a `--url` argument. -### Generating the runtime API from the downloaded metadata +## Subxt Documentation -Declare a module and decorate it with the `subxt` attribute which points at the downloaded metadata for the -target runtime: - -```rust -#[subxt::subxt(runtime_metadata_path = "metadata.scale")] -pub mod node_runtime { } -``` - -**Important:** `runtime_metadata_path` resolves to a path relative to the directory where your crate's `Cargo.toml` -resides ([`CARGO_MANIFEST_DIR`](https://doc.rust-lang.org/cargo/reference/environment-variables.html)), *not* relative to the source file. - -### Initializing the API client - -```rust -use subxt::{ClientBuilder, DefaultConfig, SubstrateExtrinsicParams}; - -let api = ClientBuilder::new() - .set_url("wss://rpc.polkadot.io:443") - .build() - .await? - .to_runtime_api::>>(); -``` - -The `RuntimeApi` type is generated by the `subxt` macro from the supplied metadata. This can be parameterized with user -supplied implementations for the `Config` and `Extra` types, if the default implementations differ from the target -chain. - -### Querying Storage - -Call the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and then the `storage_item_name()`. - -So in order to query `Balances::TotalIssuance`: - -```rust -let total_issuance = api - .storage() - .balances() - .total_issuance(None) - .await - .unwrap() -``` - -### Submitting Extrinsics - -Submit an extrinsic, returning success once the transaction is validated and accepted into the pool: - -```rust -use sp_keyring::AccountKeyring; -use subxt::PairSigner; - -let signer = PairSigner::new(AccountKeyring::Alice.pair()); -let dest = AccountKeyring::Bob.to_account_id().into(); - -let tx_hash = api - .tx() - .balances() - .transfer(dest, 10_000) - .sign_and_submit(&signer) - .await?; -``` - -For more advanced usage, which can wait for block inclusion and return any events triggered by the extrinsic, see the -[balance transfer example](./examples/examples/balance_transfer.rs). +For more details regarding utilizing subxt, please visit the [documentation](docs/subxt.md). ## Integration Testing Most tests require a running substrate node to communicate with. This is done by spawning an instance of the -substrate node per test. It requires an executable binary `substrate` at [`polkadot-v0.9.10`](https://github.com/paritytech/substrate/releases/tag/polkadot-v0.9.10) on your path. +substrate node per test. It requires an up-to-date `substrate` executable on your path. This can be installed from source via cargo: ```bash -cargo install --git https://github.com/paritytech/substrate node-cli --tag=polkadot-v0.9.10 --force +cargo install --git https://github.com/paritytech/substrate node-cli --force ``` ## Real world usage diff --git a/cli/src/main.rs b/cli/src/main.rs index afc99586c3..78cf2fc53b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -63,7 +63,7 @@ enum Command { /// Download metadata from a substrate node, for use with `subxt` codegen. #[structopt(name = "metadata")] Metadata { - /// the url of the substrate node to query for metadata + /// The url of the substrate node to query for metadata. #[structopt( name = "url", long, @@ -71,7 +71,7 @@ enum Command { default_value = "http://localhost:9933" )] url: url::Url, - /// the format of the metadata to display: `json`, `hex` or `bytes` + /// The format of the metadata to display: `json`, `hex` or `bytes`. #[structopt(long, short, default_value = "bytes")] format: String, }, @@ -81,10 +81,10 @@ enum Command { /// /// `subxt codegen | rustfmt --edition=2018 --emit=stdout` Codegen { - /// the url of the substrate node to query for metadata for codegen. + /// The url of the substrate node to query for metadata for codegen. #[structopt(name = "url", long, parse(try_from_str))] url: Option, - /// the path to the encoded metadata file. + /// The path to the encoded metadata file. #[structopt(short, long, parse(from_os_str))] file: Option, /// Additional derives diff --git a/codegen/src/api/calls.rs b/codegen/src/api/calls.rs index 47f39fa57a..a5d01fbdd5 100644 --- a/codegen/src/api/calls.rs +++ b/codegen/src/api/calls.rs @@ -20,7 +20,6 @@ use crate::types::{ }; use frame_metadata::{ v14::RuntimeMetadataV14, - PalletCallMetadata, PalletMetadata, }; use heck::{ @@ -35,13 +34,51 @@ use quote::{ }; use scale_info::form::PortableForm; +/// Generate calls from the provided pallet's metadata. +/// +/// The function creates a new module named `calls` under the pallet's module. +/// ```ignore +/// pub mod PalletName { +/// pub mod calls { +/// ... +/// } +/// } +/// ``` +/// +/// The function generates the calls as rust structs that implement the `subxt::Call` trait +/// to uniquely identify the call's identity when creating the extrinsic. +/// +/// ```ignore +/// pub struct CallName { +/// pub call_param: type, +/// } +/// impl ::subxt::Call for CallName { +/// ... +/// } +/// ``` +/// +/// Calls are extracted from the API and wrapped into the generated `TransactionApi` of +/// each module. +/// +/// # Arguments +/// +/// - `metadata` - Runtime metadata from which the calls are generated. +/// - `type_gen` - The type generator containing all types defined by metadata. +/// - `pallet` - Pallet metadata from which the calls are generated. +/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from. pub fn generate_calls( metadata: &RuntimeMetadataV14, type_gen: &TypeGenerator, pallet: &PalletMetadata, - call: &PalletCallMetadata, types_mod_ident: &syn::Ident, ) -> TokenStream2 { + // Early return if the pallet has no calls. + let call = if let Some(ref calls) = pallet.calls { + calls + } else { + return quote!() + }; + let mut struct_defs = super::generate_structs_from_variants( type_gen, call.ty.id(), diff --git a/codegen/src/api/constants.rs b/codegen/src/api/constants.rs index 4750ed59ba..8039e89db6 100644 --- a/codegen/src/api/constants.rs +++ b/codegen/src/api/constants.rs @@ -17,7 +17,6 @@ use crate::types::TypeGenerator; use frame_metadata::{ v14::RuntimeMetadataV14, - PalletConstantMetadata, PalletMetadata, }; use heck::ToSnakeCase as _; @@ -29,13 +28,41 @@ use quote::{ }; use scale_info::form::PortableForm; +/// Generate constants from the provided pallet's metadata. +/// +/// The function creates a new module named `constants` under the pallet's module. +/// ```ignore +/// pub mod PalletName { +/// pub mod constants { +/// ... +/// } +/// } +/// ``` +/// +/// The constants are exposed via the `ConstantsApi` wrapper. +/// +/// Although the constants are defined in the provided static metadata, the API +/// ensures that the constants are returned from the runtime metadata of the node. +/// This ensures that if the node's constants change value, we'll always see the latest values. +/// +/// # Arguments +/// +/// - `metadata` - Runtime metadata from which the calls are generated. +/// - `type_gen` - The type generator containing all types defined by metadata +/// - `pallet` - Pallet metadata from which the calls are generated. +/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from. pub fn generate_constants( metadata: &RuntimeMetadataV14, type_gen: &TypeGenerator, pallet: &PalletMetadata, - constants: &[PalletConstantMetadata], types_mod_ident: &syn::Ident, ) -> TokenStream2 { + // Early return if the pallet has no constants. + if pallet.constants.is_empty() { + return quote!() + } + let constants = &pallet.constants; + let constant_fns = constants.iter().map(|constant| { let fn_name = format_ident!("{}", constant.name.to_snake_case()); let pallet_name = &pallet.name; diff --git a/codegen/src/api/events.rs b/codegen/src/api/events.rs index 05a0eaef18..b63e7ba31f 100644 --- a/codegen/src/api/events.rs +++ b/codegen/src/api/events.rs @@ -15,20 +15,51 @@ // along with subxt. If not, see . use crate::types::TypeGenerator; -use frame_metadata::{ - PalletEventMetadata, - PalletMetadata, -}; +use frame_metadata::PalletMetadata; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use scale_info::form::PortableForm; +/// Generate events from the provided pallet metadata. +/// +/// The function creates a new module named `events` under the pallet's module. +/// ```ignore +/// pub mod PalletName { +/// pub mod events { +/// ... +/// } +/// } +/// ``` +/// +/// The function generates the events as rust structs that implement the `subxt::Event` trait +/// to uniquely identify the event's identity when creating the extrinsic. +/// +/// ```ignore +/// pub struct EventName { +/// pub event_param: type, +/// } +/// impl ::subxt::Event for EventName { +/// ... +/// } +/// ``` +/// +/// # Arguments +/// +/// - `type_gen` - The type generator containing all types defined by metadata. +/// - `pallet` - Pallet metadata from which the events are generated. +/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from. pub fn generate_events( type_gen: &TypeGenerator, pallet: &PalletMetadata, - event: &PalletEventMetadata, types_mod_ident: &syn::Ident, ) -> TokenStream2 { + // Early return if the pallet has no events. + let event = if let Some(ref event) = pallet.event { + event + } else { + return quote!() + }; + let struct_defs = super::generate_structs_from_variants( type_gen, event.ty.id(), diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index 01b3a2df6c..0ca62712a9 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -55,6 +55,15 @@ use std::{ }; use syn::parse_quote; +/// Generates the API for interacting with a Substrate runtime. +/// +/// # Arguments +/// +/// * `item_mod` - The module declaration for which the API is implemented. +/// * `path` - The path to the scale encoded metadata of the runtime node. +/// * `derives` - Provide custom derives for the generated types. +/// +/// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases. pub fn generate_runtime_api

( item_mod: syn::ItemMod, path: P, @@ -78,11 +87,16 @@ where generator.generate_runtime(item_mod, derives) } +/// Create the API for interacting with a Substrate runtime. pub struct RuntimeGenerator { metadata: RuntimeMetadataV14, } impl RuntimeGenerator { + /// Create a new runtime generator from the provided metadata. + /// + /// **Note:** If you have a path to the metadata, prefer to use [generate_runtime_api] + /// for generating the runtime API. pub fn new(metadata: RuntimeMetadataPrefixed) -> Self { match metadata.1 { RuntimeMetadata::V14(v14) => Self { metadata: v14 }, @@ -90,6 +104,12 @@ impl RuntimeGenerator { } } + /// Generate the API for interacting with a Substrate runtime. + /// + /// # Arguments + /// + /// * `item_mod` - The module declaration for which the API is implemented. + /// * `derives` - Provide custom derives for the generated types. pub fn generate_runtime( &self, item_mod: syn::ItemMod, @@ -98,7 +118,7 @@ impl RuntimeGenerator { let item_mod_ir = ir::ItemMod::from(item_mod); let default_derives = derives.default_derives(); - // some hardcoded default type substitutes, can be overridden by user + // Some hardcoded default type substitutes, can be overridden by user let mut type_substitutes = [ ( "bitvec::order::Lsb0", @@ -175,47 +195,24 @@ impl RuntimeGenerator { let metadata_hash = get_metadata_per_pallet_hash(&self.metadata, &pallet_names); let modules = pallets_with_mod_names.iter().map(|(pallet, mod_name)| { - let calls = if let Some(ref calls) = pallet.calls { - calls::generate_calls( - &self.metadata, - &type_gen, - pallet, - calls, - types_mod_ident, - ) - } else { - quote!() - }; - - let event = if let Some(ref event) = pallet.event { - events::generate_events(&type_gen, pallet, event, types_mod_ident) - } else { - quote!() - }; - - let storage_mod = if let Some(ref storage) = pallet.storage { - storage::generate_storage( - &self.metadata, - &type_gen, - pallet, - storage, - types_mod_ident, - ) - } else { - quote!() - }; - - let constants_mod = if !pallet.constants.is_empty() { - constants::generate_constants( - &self.metadata, - &type_gen, - pallet, - &pallet.constants, - types_mod_ident, - ) - } else { - quote!() - }; + let calls = + calls::generate_calls(&self.metadata, &type_gen, pallet, types_mod_ident); + + let event = events::generate_events(&type_gen, pallet, types_mod_ident); + + let storage_mod = storage::generate_storage( + &self.metadata, + &type_gen, + pallet, + types_mod_ident, + ); + + let constants_mod = constants::generate_constants( + &self.metadata, + &type_gen, + pallet, + types_mod_ident, + ); quote! { pub mod #mod_name { diff --git a/codegen/src/api/storage.rs b/codegen/src/api/storage.rs index 4a2c91c09f..faa4813c97 100644 --- a/codegen/src/api/storage.rs +++ b/codegen/src/api/storage.rs @@ -18,7 +18,6 @@ use crate::types::TypeGenerator; use frame_metadata::{ v14::RuntimeMetadataV14, PalletMetadata, - PalletStorageMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType, @@ -36,13 +35,51 @@ use scale_info::{ TypeDef, }; +/// Generate storage from the provided pallet's metadata. +/// +/// The function creates a new module named `storage` under the pallet's module. +/// +/// ```ignore +/// pub mod PalletName { +/// pub mod storage { +/// ... +/// } +/// } +/// ``` +/// +/// The function generates the storage as rust structs that implement the `subxt::StorageEntry` +/// trait to uniquely identify the storage's identity when creating the extrinsic. +/// +/// ```ignore +/// pub struct StorageName { +/// pub storage_param: type, +/// } +/// impl ::subxt::StorageEntry for StorageName { +/// ... +/// } +/// ``` +/// +/// Storages are extracted from the API and wrapped into the generated `StorageApi` of +/// each module. +/// +/// # Arguments +/// +/// - `metadata` - Runtime metadata from which the storages are generated. +/// - `type_gen` - The type generator containing all types defined by metadata. +/// - `pallet` - Pallet metadata from which the storages are generated. +/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from. pub fn generate_storage( metadata: &RuntimeMetadataV14, type_gen: &TypeGenerator, pallet: &PalletMetadata, - storage: &PalletStorageMetadata, types_mod_ident: &syn::Ident, ) -> TokenStream2 { + let storage = if let Some(ref storage) = pallet.storage { + storage + } else { + return quote!() + }; + let (storage_structs, storage_fns): (Vec<_>, Vec<_>) = storage .entries .iter() diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3ca916a380..8f4cd9e023 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -15,6 +15,38 @@ // along with subxt. If not, see . //! Library to generate an API for a Substrate runtime from its metadata. +//! +//! ## Generated Structure +//! +//! The API generator logic: +//! - At the root there is the `item_mod` provided (ie `pub mod api {}`) +//! - Pallets are represented by a child module (ie `pub mod PalletName {}`) of the root +//! - Each pallet exposes as child modules (if applicable): +//! - Calls (`pub mod calls {}`) +//! - Events (`pub mod events {}`) +//! - Storage (`pub mod storage {}`) +//! - Constants (`pub mod constants {}`) +//! +//! ## Example +//! +//! ```rust +//! use codec::Decode; +//! use frame_metadata::RuntimeMetadataPrefixed; +//! use subxt_codegen::DerivesRegistry; +//! +//! // Runtime metadata obtained from a node. +//! let metadata = ::decode(encoded)?; +//! // Module under which the API is generated. +//! let item_mod = syn::parse_quote!( +//! pub mod api {} +//! ); +//! // Default module derivatives. +//! let mut derives = DerivesRegistry::default(); +//! // Generate the Runtime API. +//! let generator = subxt_codegen::RuntimeGenerator::new(metadata); +//! let runtime_api = generator.generate_runtime(item_mod, derives); +//! println!("{}", runtime_api); +//! ``` #![deny(unused_crate_dependencies)] diff --git a/docs/subxt.md b/docs/subxt.md new file mode 100644 index 0000000000..66c825609c --- /dev/null +++ b/docs/subxt.md @@ -0,0 +1,144 @@ +Subxt is a library to **sub**mit e**xt**rinsics to a [substrate](https://github.com/paritytech/substrate) node via RPC. + +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) + + +### Generate the runtime API + +Subxt generates a runtime API from downloaded static metadata. The metadata can be downloaded using the +[subxt-cli](https://crates.io/crates/subxt-cli) tool. + +To generate the runtime API, use the `subxt` attribute which points at downloaded static metadata. + +```rust +#[subxt::subxt(runtime_metadata_path = "metadata.scale")] +pub mod node_runtime { } +``` + +The `node_runtime` has the following hierarchy: + +```rust +pub mod node_runtime { + pub mod PalletName { + pub mod calls { } + pub mod storage { } + pub mod constants { } + pub mod events { } + } +} +``` + +For more information regarding the `node_runtime` hierarchy, please visit the +[subxt-codegen](https://docs.rs/subxt-codegen/latest/subxt_codegen/) documentation. + + +### Initializing the API client + +```rust +use subxt::{ClientBuilder, DefaultConfig, PolkadotExtrinsicParams}; + +let api = ClientBuilder::new() + .set_url("wss://rpc.polkadot.io:443") + .build() + .await? + .to_runtime_api::>>(); +``` + +The `RuntimeApi` type is generated by the `subxt` macro from the supplied metadata. This can be parameterized with user +supplied implementations for the `Config` and `Extra` types, if the default implementation differs from the target +chain. + +To ensure that extrinsics are properly submitted, during the build phase of the Client the +runtime metadata of the node is downloaded. If the URL is not specified (`set_url`), the local host is used instead. + + +### Submit Extrinsics + +Extrinsics are obtained using the API's `RuntimeApi::tx()` method, followed by `pallet_name()` and then the +`call_item_name()`. + +Submit an extrinsic, returning success once the transaction is validated and accepted into the pool: + +Please visit the [balance_transfer](../examples/examples/balance_transfer.rs) example for more details. + + +### Querying Storage + +The runtime storage is queried via the generated `RuntimeApi::storage()` method, followed by the `pallet_name()` and +then the `storage_item_name()`. + +Please visit the [fetch_staking_details](../examples/examples/fetch_staking_details.rs) example for more details. + +### Query Constants + +Constants are embedded into the node's metadata. + +The subxt offers the ability to query constants from the runtime metadata (metadata downloaded when constructing +the client, *not* the one provided for API generation). + +To query constants use the generated `RuntimeApi::constants()` method, followed by the `pallet_name()` and then the +`constant_item_name()`. + +Please visit the [fetch_constants](../examples/examples/fetch_constants.rs) example for more details. + +### Subscribe to Events + +To subscribe to events, use the generated `RuntimeApi::events()` method which exposes: +- `subscribe()` - Subscribe to events emitted from blocks. These blocks haven't necessarily been finalised. +- `subscribe_finalized()` - Subscribe to events from finalized blocks. +- `at()` - Obtain events at a given block hash. + + +*Examples* +- [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 + +There are two types of metadata that the subxt is aware of: +- static metadata: Metadata used for generating the API. +- runtime metadata: Metadata downloaded from the target node when a subxt client is created. + +There are cases when the static metadata is different from the runtime metadata of a node. +Such is the case when the node performs a runtime update. + +To ensure that subxt can properly communicate with the target node the static metadata is validated +against the runtime metadata of the node. + +This validation is performed at the Call, Constant, and Storage levels, as well for the entire metadata. +The level of granularity ensures that the users can still submit a given call, even if another +call suffered changes. + +Full metadata validation: + +```rust +// To make sure that all of our statically generated pallets are compatible with the +// runtime node, we can run this check: +api.validate_metadata()?; +``` + +Call level validation: + +```rust +let extrinsic = api + .tx() + .balances() + // Constructing an extrinsic will fail if the metadata + // is not in sync with the generated API. + .transfer(dest, 123_456_789_012_345)?; +``` + +### Runtime Updates + +There are cases when the node would perform a runtime update, and the runtime node's metadata would be +out of sync with the subxt's metadata. + +The `UpdateClient` API keeps 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. diff --git a/examples/examples/balance_transfer.rs b/examples/examples/balance_transfer.rs index eb8a53c29d..b49aac0a1e 100644 --- a/examples/examples/balance_transfer.rs +++ b/examples/examples/balance_transfer.rs @@ -38,20 +38,23 @@ async fn main() -> Result<(), Box> { tracing_subscriber::fmt::init(); let signer = PairSigner::new(AccountKeyring::Alice.pair()); - let dest = AccountKeyring::Bob.to_account_id().into(); let api = ClientBuilder::new() .build() .await? .to_runtime_api::>>(); - let hash = api - .tx() - .balances() - .transfer(dest, 123_456_789_012_345)? - .sign_and_submit_default(&signer) - .await?; - - println!("Balance transfer extrinsic submitted: {}", hash); + + // Submit the `transfer` extrinsic from Alice's account to Bob's. + let dest = AccountKeyring::Bob.to_account_id().into(); + + // Obtain an extrinsic, calling the "transfer" function in + // the "balances" pallet. + let extrinsic = api.tx().balances().transfer(dest, 123_456_789_012_345)?; + + // Sign and submit the extrinsic, returning its hash. + let tx_hash = extrinsic.sign_and_submit_default(&signer).await?; + + println!("Balance transfer extrinsic submitted: {}", tx_hash); Ok(()) } diff --git a/examples/examples/fetch_constants.rs b/examples/examples/fetch_constants.rs new file mode 100644 index 0000000000..675793f98d --- /dev/null +++ b/examples/examples/fetch_constants.rs @@ -0,0 +1,59 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is part of subxt. +// +// subxt is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// subxt is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with subxt. If not, see . + +//! To run this example, a local polkadot node should be running. Example verified against polkadot 0.9.18-4542a603cc-aarch64-macos. +//! +//! E.g. +//! ```bash +//! curl "https://github.com/paritytech/polkadot/releases/download/v0.9.18/polkadot" --output /usr/local/bin/polkadot --location +//! polkadot --dev --tmp +//! ``` + +use subxt::{ + ClientBuilder, + DefaultConfig, + PolkadotExtrinsicParams, +}; + +// 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(); + + // Upon connecting to the target polkadot node, the node's metadata is downloaded (referred to + // as the runtime metadata). + let api = ClientBuilder::new() + .build() + .await? + .to_runtime_api::>>(); + + // Constants are queried from the node's runtime metadata. + // Query the `ExistentialDeposit` constant from the `Balances` pallet. + let existential_deposit = api + // This is the constants query. + .constants() + // Constant from the `Balances` pallet. + .balances() + // Constant name. + .existential_deposit()?; + + println!("Existential Deposit: {}", existential_deposit); + + Ok(()) +} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 6d4898537a..33239f18ff 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -21,7 +21,7 @@ //! Download metadata from a running Substrate node using `subxt-cli`: //! //! ```bash -//! subxt metadata -f bytes > polkadot_metadata.scale +//! subxt metadata > polkadot_metadata.scale //! ``` //! //! Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file. diff --git a/subxt/src/client.rs b/subxt/src/client.rs index 89d6cd2315..cf847de601 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -98,6 +98,17 @@ impl ClientBuilder { } /// Creates a new Client. + /// + /// # Example + /// + /// ```rust + /// use subxt::{ClientBuilder, DefaultConfig}; + /// + /// let client = ClientBuilder::new() + /// .set_url("wss://rpc.polkadot.io:443") + /// .build::() + /// .await?; + /// ``` pub async fn build(self) -> Result, BasicError> { let client = if let Some(client) = self.client { client @@ -190,6 +201,22 @@ impl Client { } /// Create a wrapper for performing runtime updates on this client. + /// + /// # Note + /// + /// The update client is intended to be used in the background for + /// performing runtime updates, while the API is still in use. + /// Without performing runtime updates the submitted extrinsics may fail. + /// + /// # Example + /// + /// ```rust + /// let update_client = client.updates(); + /// tokio::spawn(async move { + /// let result = update_client.perform_runtime_updates().await; + /// println!("Runtime update finished with result={:?}", result); + /// }); + /// ``` pub fn updates(&self) -> UpdateClient { UpdateClient::new( self.rpc.clone(), diff --git a/subxt/src/events/mod.rs b/subxt/src/events/mod.rs index a6ed76b568..c327e51230 100644 --- a/subxt/src/events/mod.rs +++ b/subxt/src/events/mod.rs @@ -14,7 +14,58 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -//! For working with events. +//! This module exposes the ability to work with events generated by a given block. +//! Subxt can either attempt to statically decode events into known types, or it +//! can hand back details of the raw event without knowing what shape its contents +//! are (this may be useful if we don't know what exactly we're looking for). +//! +//! This module is wrapped by the generated API in `RuntimeAPI::EventsApi`. +//! +//! # Example +//! +//! ## Subscribe to all events +//! +//! Users can subscribe to all emitted events from blocks using `subscribe()`. +//! +//! To subscribe to all events from just the finalized blocks use `subscribe_finalized()`. +//! +//! To obtain the events from a given block use `at()`. +//! +//! ```rust +//! let mut events = api.events().subscribe().await?; +//! +//! while let Some(ev) = events.next().await { +//! // Obtain all events from this block. +//! let ev: subxt::Events<_, _> = ev?; +//! // Print block hash. +//! println!("Event at block hash {:?}", ev.block_hash()); +//! // Iterate over all events. +//! let mut iter = ev.iter(); +//! while let Some(event_details) = iter.next() { +//! println!("Event details {:?}", event_details); +//! } +//! } +//! ``` +//! +//! ## Filter events +//! +//! The subxt exposes the ability to filter events via the `filter_events()` function. +//! +//! The function filters events from the provided tuple. If 1-tuple is provided, the events are +//! returned directly. Otherwise, we'll be given a corresponding tuple of `Option`'s, with exactly +//! one variant populated each time. +//! +//! ```rust +//! let mut events = api +//! .events() +//! .subscribe() +//! .await? +//! .filter_events::<(polkadot::balances::events::Transfer,)>(); +//! +//! while let Some(transfer_event) = transfer_events.next().await { +//! println!("Balance transfer event: {transfer_event:?}"); +//! } +//! ``` mod decoding; mod event_subscription; diff --git a/subxt/src/extrinsic/mod.rs b/subxt/src/extrinsic/mod.rs index e3b9db291c..f31a420970 100644 --- a/subxt/src/extrinsic/mod.rs +++ b/subxt/src/extrinsic/mod.rs @@ -15,6 +15,20 @@ // along with subxt. If not, see . //! Create signed or unsigned extrinsics. +//! +//! This modules exposes the extrinsic's parameters and the ability to sign an extrinsic. +//! +//! +//! An extrinsic is submitted with an "signed extra" and "additional" parameters, which can be +//! different for each chain. The trait [ExtrinsicParams] determines exactly which +//! additional and signed extra parameters are used when constructing an extrinsic. +//! +//! +//! The structure [BaseExtrinsicParams] is a base implementation of the trait which +//! configures most of the "signed extra" and "additional" parameters as needed for +//! Polkadot and Substrate nodes. Only the shape of the tip payments differs, leading to +//! [SubstrateExtrinsicParams] and [PolkadotExtrinsicParams] structs which pick an +//! appropriate shape for Substrate/Polkadot chains respectively. mod params; mod signer; diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index 5c4a2a814c..747a783f31 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -14,9 +14,7 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -//! A library to **sub**mit e**xt**rinsics to a -//! [substrate](https://github.com/paritytech/substrate) node via RPC. - +#![doc = include_str!("../../docs/subxt.md")] #![deny( bad_style, const_err, @@ -126,7 +124,13 @@ pub use crate::{ }, }; -/// Call trait. +/// Trait to uniquely identify the call (extrinsic)'s identity from the runtime metadata. +/// +/// Generated API structures that represent each of the different possible +/// calls to a node each implement this trait. +/// +/// When encoding an extrinsic, we use this information to know how to map +/// the call to the specific pallet and call index needed by a particular node. pub trait Call: Encode { /// Pallet name. const PALLET: &'static str; @@ -139,7 +143,12 @@ pub trait Call: Encode { } } -/// Event trait. +/// Trait to uniquely identify the events's identity from the runtime metadata. +/// +/// Generated API structures that represent an event implement this trait. +/// +/// The trait is utilized to decode emitted events from a block, via obtaining the +/// form of the `Event` from the metadata. pub trait Event: Decode { /// Pallet name. const PALLET: &'static str; diff --git a/subxt/src/metadata/metadata_type.rs b/subxt/src/metadata/metadata_type.rs index 2d121801e7..1b706a57d1 100644 --- a/subxt/src/metadata/metadata_type.rs +++ b/subxt/src/metadata/metadata_type.rs @@ -37,7 +37,7 @@ use std::{ sync::Arc, }; -/// Metadata error. +/// Metadata error originated from inspecting the internal representation of the runtime metadata. #[derive(Debug, thiserror::Error, PartialEq)] pub enum MetadataError { /// Module is not in metadata. @@ -97,7 +97,7 @@ struct MetadataInner { cached_storage_hashes: HashCache, } -/// Runtime metadata. +/// A representation of the runtime metadata received from a node. #[derive(Clone, Debug)] pub struct Metadata { inner: Arc, @@ -307,7 +307,10 @@ impl EventMetadata { } } -/// Metadata for specific errors. +/// Metadata for specific errors obtained from the pallet's `PalletErrorMetadata`. +/// +/// This holds in memory information regarding the Pallet's name, Error's name, and the underlying +/// metadata representation. #[derive(Clone, Debug)] pub struct ErrorMetadata { pallet: String, @@ -332,6 +335,10 @@ impl ErrorMetadata { } } +/// Error originated from converting a runtime metadata [RuntimeMetadataPrefixed] to +/// the internal [Metadata] representation. +/// +/// The runtime metadata is converted when building the [crate::client::Client]. #[derive(Debug, thiserror::Error)] pub enum InvalidMetadataError { #[error("Invalid prefix")] diff --git a/subxt/src/rpc.rs b/subxt/src/rpc.rs index 1aff88e8a9..bd78824f4c 100644 --- a/subxt/src/rpc.rs +++ b/subxt/src/rpc.rs @@ -15,6 +15,49 @@ // along with subxt. If not, see . //! RPC types and client for interacting with a substrate node. +//! +//! This is used behind the scenes by various `subxt` APIs, but can +//! also be used directly. +//! +//! # Examples +//! +//! ## Fetch Storage +//! +//! ```rust +//! use subxt::rpc::Rpc; +//! use subxt::storage::StorageKeyPrefix; +//! +//! // Storage prefix is `twox_128("System") ++ twox_128("ExtrinsicCount")`. +//! let key = StorageKeyPrefix::new::() +//! .to_storage_key(); +//! +//! // Obtain the RPC from a generated API +//! let rpc: &Rpc<_> = api +//! .client +//! .rpc(); +//! +//! let result = rpc.storage(&key, None)?; +//! println!("Storage result: {:?}", result); +//! ``` +//! +//! ## Fetch Keys +//! +//! ```rust +//! use subxt::rpc::Rpc; +//! use subxt::storage::StorageKeyPrefix; +//! let key = StorageKeyPrefix::new::(); +//! +//! // Obtain the RPC from a generated API +//! let rpc: &Rpc<_> = api +//! .client +//! .rpc(); +//! // Fetch up to 10 keys. +//! let keys = rpc +//! .storage_keys_paged(Some(key), 10, None, None); +//! for key in keys.iter() { +//! println!("Key: 0x{}", hex::encode(&key)); +//! } +//! ``` // jsonrpsee subscriptions are interminable. // Allows `while let status = subscription.next().await {}` diff --git a/subxt/src/storage.rs b/subxt/src/storage.rs index b3e2f55657..c0682f4aa1 100644 --- a/subxt/src/storage.rs +++ b/subxt/src/storage.rs @@ -14,7 +14,47 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -//! For querying runtime storage. +//! Query the runtime storage using [StorageClient]. +//! +//! This module is the core of performing runtime storage queries. While you can +//! work with it directly, it's prefer to use the generated `storage()` interface where +//! possible. +//! +//! The exposed API is performing RPC calls to `state_getStorage` and `state_getKeysPaged`. +//! +//! A runtime storage entry can be of type: +//! - [StorageEntryKey::Plain] for keys constructed just from the prefix +//! `twox_128(pallet) ++ twox_128(storage_item)` +//! - [StorageEntryKey::Map] for mapped keys constructed from the prefix, +//! plus other arguments `twox_128(pallet) ++ twox_128(storage_item) ++ hash(arg1) ++ arg1` +//! +//! # Examples +//! +//! ## Fetch Storage Keys +//! +//! ```rust +//! // Fetch just the keys, returning up to 10 keys. +//! let keys = storage +//! .fetch_keys::(10, None, None) +//! .await?; +//! // Iterate over each key +//! for key in keys.iter() { +//! println!("Key: 0x{}", hex::encode(&key)); +//! } +//! ``` +//! +//! ## Iterate over Storage +//! +//! ```rust +//! // Iterate over keys and values. +//! let mut iter = storage +//! .iter::(None) +//! .await?; +//! while let Some((key, value)) = iter.next().await? { +//! println!("Key: 0x{}", hex::encode(&key)); +//! println!("Value: {}", value); +//! } +//! ``` use codec::{ Decode, diff --git a/subxt/src/updates.rs b/subxt/src/updates.rs index e304d4f4f7..ea606e05fb 100644 --- a/subxt/src/updates.rs +++ b/subxt/src/updates.rs @@ -14,7 +14,27 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . -//! For performing runtime updates. +//! Perform runtime updates in the background using [UpdateClient]. +//! +//! There are cases when the node would perform a runtime update. As a result, the subxt's metadata +//! would be out of sync and the API would not be able to submit valid extrinsics. +//! This API keeps the `RuntimeVersion` and `Metadata` of the client synced with the target node. +//! +//! The runtime update is recommended for long-running clients, or for cases where manually +//! restarting subxt would not be feasible. Even with this, extrinsics submitted during a node +//! runtime update are at risk or failing, as it will take `subxt` a moment to catch up. +//! +//! ## Note +//! +//! Here we use tokio to check for updates in the background, but any runtime can be used. +//! +//! ```rust +//! let update_client = client.updates(); +//! tokio::spawn(async move { +//! let result = update_client.perform_runtime_updates().await; +//! println!("Runtime update finished with result={:?}", result); +//! }); +//! ``` use crate::{ rpc::{