diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index a799f10d0e..5235f68271 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -83,7 +83,7 @@ impl RuntimeGenerator { Ok(quote! { #( #item_mod_attrs )* - #[allow(dead_code, unused_imports, non_camel_case_types, unreachable_patterns)] + #[allow(dead_code, missing_docs, unused_imports, non_camel_case_types, unreachable_patterns)] #[allow(clippy::all)] #[allow(rustdoc::broken_intra_doc_links)] pub mod #mod_ident { @@ -247,7 +247,7 @@ impl RuntimeGenerator { Ok(quote! { #( #item_mod_attrs )* - #[allow(dead_code, unused_imports, non_camel_case_types, unreachable_patterns)] + #[allow(dead_code, missing_docs, unused_imports, non_camel_case_types, unreachable_patterns)] #[allow(clippy::all)] #[allow(rustdoc::broken_intra_doc_links)] pub mod #mod_ident { diff --git a/subxt/examples/blocks.rs b/subxt/examples/blocks.rs new file mode 100644 index 0000000000..cc83314c01 --- /dev/null +++ b/subxt/examples/blocks.rs @@ -0,0 +1,74 @@ +//! Subscribe to blocks. +use subxt::dynamic::Value; +use subxt::{Error, OnlineClient, PolkadotConfig}; + +#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] +mod polkadot {} + +#[tokio::main] +async fn main() -> Result<(), Error> { + // Create a new API client, configured to talk to Polkadot nodes. + let config = PolkadotConfig::new(); + let api = OnlineClient::new(config).await?; + + // Stream the finalized blocks. See the OnlineClient docs for how to + // stream best blocks or all new blocks. + let mut blocks = api.stream_blocks().await?; + + while let Some(block) = blocks.next().await { + let block = block?; + + let block_number = block.number(); + let block_hash = block.hash(); + + println!("Block #{block_number}:"); + println!(" Hash: {block_hash}"); + println!(" Extrinsics:"); + + // Create a client to work at this block. From here you have the + // same interface as `api.block(block.hash()).await`. + let at_block = block.at().await?; + + // Here we'll obtain and display the extrinsics: + let extrinsics = at_block.extrinsics().fetch().await?; + for ext in extrinsics.iter() { + let ext = ext?; + + let idx = ext.index(); + let pallet_name = ext.pallet_name(); + let call_name = ext.call_name(); + let bytes_hex = format!("0x{}", hex::encode(ext.bytes())); + let events = ext.events().await?; + + // See the API docs for more ways to decode extrinsics: + let decoded_ext = ext.decode_call_data_as::()?; + + println!(" #{idx}: {pallet_name}.{call_name}:"); + println!(" Bytes: {bytes_hex}"); + println!(" Decoded: {decoded_ext:?}"); + + for evt in events.iter() { + println!(" Events:"); + let evt = evt?; + let event_idx = evt.event_index(); + let pallet_name = evt.pallet_name(); + let event_name = evt.event_name(); + let event_values = evt.decode_fields_unchecked_as::()?; + + println!(" #{event_idx}: {pallet_name}.{event_name}"); + println!(" {event_values}"); + } + + if let Some(transaction_extensions) = ext.transaction_extensions() { + println!(" Transaction Extensions:"); + for transaction_extension in transaction_extensions.iter() { + let name = transaction_extension.name(); + let value = transaction_extension.decode_unchecked_as::()?; + println!(" {name}: {value}"); + } + } + } + } + + Ok(()) +} diff --git a/subxt/examples/historic_blocks.rs b/subxt/examples/historic_blocks.rs new file mode 100644 index 0000000000..7c3de62f8f --- /dev/null +++ b/subxt/examples/historic_blocks.rs @@ -0,0 +1,55 @@ +//! Working with historic blocks. +use subxt::dynamic::Value; +use subxt::{Error, OnlineClient, PolkadotConfig}; + +#[tokio::main] +async fn main() -> Result<(), Error> { + // Point at an archive node since we want to obtain old blocks. + let config = PolkadotConfig::new(); + let api = OnlineClient::from_url(config, "wss://rpc.polkadot.io").await?; + + for block_number in 1234567u32.. { + let at_block = api.at_block(block_number).await?; + + let block_number = at_block.block_number(); + let block_hash = at_block.block_hash(); + + println!("Block #{block_number}:"); + println!(" Hash: {block_hash}"); + println!(" Extrinsics:"); + + // Here we'll obtain and display the extrinsics: + let extrinsics = at_block.extrinsics().fetch().await?; + for ext in extrinsics.iter() { + let ext = ext?; + + let idx = ext.index(); + let pallet_name = ext.pallet_name(); + let call_name = ext.call_name(); + let bytes_hex = format!("0x{}", hex::encode(ext.bytes())); + let events = ext.events().await?; + + // See the API docs for more ways to decode extrinsics. Here, since we're + // accessing a historic block, we don't use any generated types to help us. + let decoded_ext = ext.decode_call_data_fields_unchecked_as::()?; + + println!(" #{idx}: {pallet_name}.{call_name}:"); + println!(" Bytes: {bytes_hex}"); + println!(" Decoded: {decoded_ext}"); + + for evt in events.iter() { + println!(" Events:"); + let evt = evt?; + let event_idx = evt.event_index(); + let pallet_name = evt.pallet_name(); + let event_name = evt.event_name(); + let event_values = evt.decode_fields_unchecked_as::()?; + + println!(" #{event_idx}: {pallet_name}.{event_name}"); + println!(" {event_values}"); + } + } + } + + Ok(()) +} diff --git a/subxt/examples/storage_entries.rs b/subxt/examples/storage_entries.rs new file mode 100644 index 0000000000..0296b83db8 --- /dev/null +++ b/subxt/examples/storage_entries.rs @@ -0,0 +1,85 @@ +//! Fetching and iterating over storage entries. +use subxt::dynamic::Value; +use subxt::utils::AccountId32; +use subxt::{Error, OnlineClient, PolkadotConfig}; + +#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] +mod polkadot {} + +#[tokio::main] +async fn main() -> Result<(), Error> { + let config = PolkadotConfig::new(); + let api = OnlineClient::new(config).await?; + let at_block = api.at_current_block().await?; + + // Here we use a statically generated address to fetch the storage entry, which + // gives us type information for future actions on it. + let account_balances = at_block + .storage() + .entry(polkadot::storage().system().account())?; + + // We can see the default value for this entry at this block, if one exists. + if let Some(default_value) = account_balances.default_value() { + let default_balance_info = default_value.decode_as::()?; + println!("Default balance info: {default_balance_info}"); + } + + // We can fetch a specific account balance by its key, like so (here I just picked a random key + // I knew to exist from iterating over storage entries): + let account_id = { + let hex = "9a4d0faa2ba8c3cc5711852960940793acf55bf195b6eecf88fa78e961d0ce4a"; + let bytes: [u8; 32] = hex::decode(hex).unwrap().try_into().unwrap(); + AccountId32::from(bytes) + }; + let entry = account_balances.fetch((account_id,)).await?; + + // We can decode the value into our generic `Value` type, which can + // represent any SCALE-encoded value, like so: + let _balance_info = entry.decode_as::()?; + + // Or we can decode into a known shape. Any type implementing DecodeAsType can + // be used here to extract fields you're interested in, or we can use the generated + // type which already implements this: + type AccountInfo = polkadot::system::storage::account::output::Output; + let balance_info = entry.decode_as::()?; + + println!( + "Single balance info from {account_id} => free: {} reserved: {} frozen: {} flags: {:?}", + balance_info.data.free, + balance_info.data.reserved, + balance_info.data.frozen, + balance_info.data.flags, + ); + + // Or we can iterate over all of the account balances and print them out. Here we provide an + // empty tuple, indicating that we want to iterate over everything and not only things under a certain key + // (in the case of account balances, there is only one key anyway, but other storage entries may map from + // several keys to a value, and for those we can choose which depth we iterate at by providing as many keys + // as we want and leaving the rest). + let mut all_balances = account_balances.iter(()).await?; + while let Some(entry) = all_balances.next().await { + let entry = entry?; + let key = entry.key()?; + + // Because we provided a statically typed Address when we originally obtained this + // storage entry (ie `polkadot::storage().system().account()`), we can statically + // decode the key into its well typed constituent parts: + let key_parts = key.decode()?; + println!("Account ID: {}", key_parts.0); + + // Alternately, if we don't have type information available (or just want to decode + // into some different type), we can do something like this instead: + let account_id = key + .part(0) + .unwrap() + .decode_as::()? + .expect("We expect this key to decode into a 32 byte AccountId"); + + // Decode these values into our generic scale_value::Value type. Less efficient than + // defining a static type as above, but easier for the sake of the example. + let balance_info = entry.value().decode_as::()?; + println!(" {account_id} => {balance_info}"); + } + + Ok(()) +} diff --git a/subxt/examples/transactions_basic.rs b/subxt/examples/submit_transaction.rs similarity index 96% rename from subxt/examples/transactions_basic.rs rename to subxt/examples/submit_transaction.rs index 2867247886..2fbcd14690 100644 --- a/subxt/examples/transactions_basic.rs +++ b/subxt/examples/submit_transaction.rs @@ -1,10 +1,10 @@ -#![allow(missing_docs)] +//! Construct and submit a transaction. use subxt::{Error, OnlineClient, PolkadotConfig}; use subxt_signer::sr25519::dev; // Generate an interface that we can use from the node's metadata. #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] -pub mod polkadot {} +mod polkadot {} #[tokio::main] async fn main() -> Result<(), Error> { diff --git a/subxt/src/backend/combined.rs b/subxt/src/backend/combined.rs index 85ca7dc684..a835822549 100644 --- a/subxt/src/backend/combined.rs +++ b/subxt/src/backend/combined.rs @@ -18,6 +18,7 @@ use futures::StreamExt; use std::task::Poll; use subxt_rpcs::RpcClient; +/// A builder to configure and build a [`CombinedBackend`]. pub struct CombinedBackendBuilder { archive: BackendChoice>, chainhead: BackendChoice>, @@ -183,6 +184,8 @@ pub struct CombinedBackendDriver { } impl CombinedBackendDriver { + /// this returns true if this [`CombinedBackendDriver`] needs polling in + /// order to drive the [`CombinedBackend`], and false if it does not. pub fn needs_polling(&self) -> bool { self.chainhead_driver.is_some() } diff --git a/subxt/src/client.rs b/subxt/src/client.rs index db5240f4c3..5e263b109c 100644 --- a/subxt/src/client.rs +++ b/subxt/src/client.rs @@ -1,3 +1,15 @@ +//! This module exposes the entrypoint to connect and interact with chains. +//! +//! - See [`OnlineClient`] for instantiating a standard client which is connected to +//! a chain and capable of interacting with it. +//! - See [`OfflineClient`] if you have no network connection but want to perform certain +//! actions against some chain. +//! +//! After instantiating a client, you'll typically then select a block to work against +//! via something like [`OnlineClient::at_block`] or [`OfflineClient::at_block`]. +//! These hand back [`OnlineClientAtBlock`] or [`OfflineClientAtBlock`], which expose +//! various methods available online or offline at the given block. + mod offline_client; mod online_client; @@ -19,10 +31,12 @@ pub use online_client::{ BlockNumberOrRef, OnlineClient, OnlineClientAtBlockImpl, OnlineClientAtBlockT, }; -/// This represents a client at a specific block number. This wraps a client impl -/// which will either be [`OfflineClientAtBlockImpl`] or [`OnlineClientAtBlockImpl`]. -/// Prefer to use the type aliases [`OfflineClientAtBlock`] and [`OnlineClientAtBlock`] -/// if you need to refer to the concrete instances of this. +/// This represents a client at a specific block number, and is created by calling either +/// [`OnlineClient::at_block`] or [`OfflineClient::at_block`]. +/// +/// This wraps a client implementation, which will either be [`OfflineClientAtBlockImpl`] +/// or [`OnlineClientAtBlockImpl`]. Prefer to use the type aliases [`OfflineClientAtBlock`] +/// and [`OnlineClientAtBlock`] if you need to refer to the concrete instances of this. #[derive(Clone, Debug)] pub struct ClientAtBlock { pub(crate) client: Client, @@ -85,6 +99,7 @@ where RuntimeApisClient::new(&self.client) } + /// Access Pallet View Functions at this block. pub fn view_functions(&self) -> ViewFunctionsClient<'_, T, Client> { ViewFunctionsClient::new(&self.client) } diff --git a/subxt/src/client/offline_client.rs b/subxt/src/client/offline_client.rs index 713739b19f..af5c45c8e0 100644 --- a/subxt/src/client/offline_client.rs +++ b/subxt/src/client/offline_client.rs @@ -3,6 +3,8 @@ use crate::config::{Config, HashFor, Hasher}; use crate::error::OfflineClientAtBlockError; use crate::metadata::{ArcMetadata, Metadata}; +/// A client which does not require a connection to a chain, and can perform certain +/// actions which don't require a network connection. #[derive(Clone, Debug)] pub struct OfflineClient { /// The configuration for this client. diff --git a/subxt/src/client/online_client.rs b/subxt/src/client/online_client.rs index 93c73f9da6..dc02d251b8 100644 --- a/subxt/src/client/online_client.rs +++ b/subxt/src/client/online_client.rs @@ -22,7 +22,7 @@ use crate::error::OnlineClientError; pub use block_number_or_ref::BlockNumberOrRef; -/// A client which exposes the means to decode historic data on a chain online. +/// A client which requires a connection to a chain, and allows interacting with it. #[derive(Clone, Debug)] pub struct OnlineClient { inner: Arc>, diff --git a/subxt/src/client/online_client/block_number_or_ref.rs b/subxt/src/client/online_client/block_number_or_ref.rs index cc35f95649..0f14125a51 100644 --- a/subxt/src/client/online_client/block_number_or_ref.rs +++ b/subxt/src/client/online_client/block_number_or_ref.rs @@ -22,6 +22,12 @@ impl From for BlockNumberOrRef { } } +impl From for BlockNumberOrRef { + fn from(value: usize) -> Self { + BlockNumberOrRef::Number(value as u64) + } +} + impl From>> for BlockNumberOrRef { fn from(block_ref: BlockRef>) -> Self { BlockNumberOrRef::BlockRef(block_ref) diff --git a/subxt/src/client/online_client/blocks.rs b/subxt/src/client/online_client/blocks.rs index 6a89b00688..be5de1c574 100644 --- a/subxt/src/client/online_client/blocks.rs +++ b/subxt/src/client/online_client/blocks.rs @@ -19,6 +19,11 @@ impl Blocks { ) -> Self { Blocks { client, stream } } + + /// Return the next block in the stream when it is produced. + pub async fn next(&mut self) -> Option, BlocksError>> { + StreamExt::next(self).await + } } impl Stream for Blocks { @@ -68,7 +73,7 @@ impl Block { } /// Instantiate a client at this block. - pub async fn client( + pub async fn at( &self, ) -> Result>, OnlineClientAtBlockError> { self.client.at_block(self.block_ref.clone()).await diff --git a/subxt/src/config.rs b/subxt/src/config.rs index 3cec090d5d..ab77732b42 100644 --- a/subxt/src/config.rs +++ b/subxt/src/config.rs @@ -2,11 +2,15 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -//! This module provides a [`Config`] type, which is used to define various -//! types that are important in order to speak to a particular chain. -//! [`SubstrateConfig`] provides a default set of these types suitable for the -//! default Substrate node implementation, and [`PolkadotConfig`] for a -//! Polkadot node. +//! This module provides a [`Config`] type, which provides a way to configure Subxt to work with a +//! specific chain. +//! +//! - A generic [`SubstrateConfig`] implementation is provided that will work against modern +//! blocks with most Substrate based chains automatically, and can be configured to work with +//! historic blocks. +//! - A [`PolkadotConfig`] implementation is provided which is specialized towards the Polkadot +//! Relay Chain, and comes pre-configured to work against historic Polkadot RC blocks. +//! mod default_extrinsic_params; diff --git a/subxt/src/config/polkadot.rs b/subxt/src/config/polkadot.rs index a9cdf1129c..a6d2a8fb27 100644 --- a/subxt/src/config/polkadot.rs +++ b/subxt/src/config/polkadot.rs @@ -15,7 +15,10 @@ pub use crate::utils::{AccountId32, MultiAddress, MultiSignature}; pub use primitive_types::{H256, U256}; /// Construct a [`PolkadotConfig`] using this. -pub struct PolkadotConfigBuilder(SubstrateConfigBuilder); +pub struct PolkadotConfigBuilder { + use_historic_types: bool, + inner: SubstrateConfigBuilder, +} impl Default for PolkadotConfigBuilder { fn default() -> Self { @@ -26,10 +29,19 @@ impl Default for PolkadotConfigBuilder { impl PolkadotConfigBuilder { /// Create a new [`PolkadotConfigBuilder`]. pub fn new() -> Self { - let inner = SubstrateConfigBuilder::new() - .set_legacy_types(frame_decode::legacy_types::polkadot::relay_chain()); + let inner = SubstrateConfigBuilder::new(); + PolkadotConfigBuilder { + use_historic_types: true, + inner, + } + } - PolkadotConfigBuilder(inner) + /// Use historic types. These are enabled by default, but can be disabled to avoid + /// loading them in if they are not needed (ie you do not need to access any block + /// using a runtime which has V14 metadata). + pub fn use_historic_types(mut self, b: bool) -> Self { + self.use_historic_types = b; + self } /// Set the metadata to be used for decoding blocks at the given spec versions. @@ -37,7 +49,7 @@ impl PolkadotConfigBuilder { mut self, ranges: impl IntoIterator, ) -> Self { - self = Self(self.0.set_metadata_for_spec_versions(ranges)); + self.inner = self.inner.set_metadata_for_spec_versions(ranges); self } @@ -47,17 +59,25 @@ impl PolkadotConfigBuilder { mut self, ranges: impl IntoIterator, ) -> Self { - self = Self(self.0.set_spec_version_for_block_ranges(ranges)); + self.inner = self.inner.set_spec_version_for_block_ranges(ranges); self } /// Construct the [`PolkadotConfig`] from this builder. - pub fn build(self) -> PolkadotConfig { - PolkadotConfig(self.0.build()) + pub fn build(mut self) -> PolkadotConfig { + if self.use_historic_types { + self.inner = self + .inner + .set_legacy_types(frame_decode::legacy_types::polkadot::relay_chain()); + } + + PolkadotConfig(self.inner.build()) } } -/// Configuration that's suitable for the Polkadot Relay Chain. +/// Configuration for the Polkadot Relay Chain. This should not be used to connect +/// to any other chain; instead use [`crate::config::SubstrateConfig`] and configure +/// that as needed to support the chain you're connecting to. #[derive(Debug, Clone)] pub struct PolkadotConfig(SubstrateConfig); @@ -69,7 +89,7 @@ impl PolkadotConfig { /// Build a new [`PolkadotConfig`]. pub fn builder() -> PolkadotConfigBuilder { - PolkadotConfigBuilder(SubstrateConfig::builder()) + PolkadotConfigBuilder::new() } } diff --git a/subxt/src/constants.rs b/subxt/src/constants.rs index a790d398ba..ef28d469d5 100644 --- a/subxt/src/constants.rs +++ b/subxt/src/constants.rs @@ -1,3 +1,15 @@ +//! This module exposes [`ConstantsClient`], which has methods for working with constants. It's +//! created by calling [`crate::client::ClientAtBlock::constants()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let constants = at_block.constants(); +//! ``` + mod address; use crate::client::OfflineClientAtBlockT; @@ -9,7 +21,7 @@ use std::marker::PhantomData; pub use address::{Address, DynamicAddress, StaticAddress, dynamic}; -/// A client for working with storage entries. +/// A client for working with constants. See [the module docs](crate::constants) for more. #[derive(Clone)] pub struct ConstantsClient<'atblock, T, Client> { client: &'atblock Client, diff --git a/subxt/src/custom_values.rs b/subxt/src/custom_values.rs index 4698f3fea9..76b19244f2 100644 --- a/subxt/src/custom_values.rs +++ b/subxt/src/custom_values.rs @@ -1,3 +1,15 @@ +//! This module exposes [`CustomValuesClient`], which has methods for working with custom values. +//! It's created by calling [`crate::client::ClientAtBlock::custom_values()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let custom_values = at_block.custom_values(); +//! ``` + mod address; use crate::client::OfflineClientAtBlockT; @@ -10,7 +22,7 @@ use scale_decode::IntoVisitor; pub use address::{Address, DynamicAddress, StaticAddress, dynamic}; -/// A client for accessing custom values stored in the metadata. +/// A client for working with custom values. See [the module docs](crate::custom_values) for more. #[derive_where(Clone; Client)] pub struct CustomValuesClient<'atblock, T, Client> { client: &'atblock Client, diff --git a/subxt/src/events.rs b/subxt/src/events.rs index 8b944f2cc2..1479758c2d 100644 --- a/subxt/src/events.rs +++ b/subxt/src/events.rs @@ -1,3 +1,15 @@ +//! This module exposes [`EventsClient`], which has methods for working with eventts. +//! It's created by calling [`crate::client::ClientAtBlock::events()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let events = at_block.events(); +//! ``` + mod decode_as_event; use crate::backend::BackendExt; @@ -12,7 +24,7 @@ use std::sync::Arc; pub use decode_as_event::DecodeAsEvent; -/// A client for working with events. +/// A client for working with events. See [the module docs](crate::events) for more. pub struct EventsClient<'atblock, T, Client> { client: &'atblock Client, marker: PhantomData, diff --git a/subxt/src/extrinsics.rs b/subxt/src/extrinsics.rs index 144573cfa4..222df77d5a 100644 --- a/subxt/src/extrinsics.rs +++ b/subxt/src/extrinsics.rs @@ -1,3 +1,15 @@ +//! This module exposes [`ExtrinsicsClient`], which has methods for working with extrinsics. +//! It's created by calling [`crate::client::ClientAtBlock::extrinsics()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let extrinsics = at_block.extrinsics(); +//! ``` + mod decode_as_extrinsic; mod extrinsic_transaction_extensions; @@ -18,7 +30,7 @@ pub use extrinsic_transaction_extensions::{ ExtrinsicTransactionExtension, ExtrinsicTransactionExtensions, }; -/// A client for working with extrinsics. +/// A client for working with extrinsics. See [the module docs](crate::extrinsics) for more. pub struct ExtrinsicsClient<'atblock, T, Client> { client: &'atblock Client, marker: PhantomData, diff --git a/subxt/src/lib.rs b/subxt/src/lib.rs index c2f2d74b48..bf499f7f69 100644 --- a/subxt/src/lib.rs +++ b/subxt/src/lib.rs @@ -2,13 +2,10 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -// TODO: REMOVE BEFORE MERGING. -#![allow(missing_docs)] - //! Subxt is a library for interacting with Substrate based nodes. Using it looks something like this: //! //! ```rust,ignore -#![doc = include_str!("../examples/transactions_basic.rs")] +#![doc = include_str!("../examples/submit_transaction.rs")] //! ``` //! //! Take a look at [the Subxt guide](book) to learn more about how to use Subxt. @@ -76,8 +73,8 @@ pub use subxt_lightclient as lightclient; /// Re-export external crates that are made use of in the subxt API. pub mod ext { pub use codec; + pub use frame_decode; pub use frame_metadata; - pub use futures; pub use scale_bits; pub use scale_decode; pub use scale_encode; diff --git a/subxt/src/runtime_apis.rs b/subxt/src/runtime_apis.rs index 9a3d855790..a9442cad4a 100644 --- a/subxt/src/runtime_apis.rs +++ b/subxt/src/runtime_apis.rs @@ -2,6 +2,18 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +//! This module exposes [`RuntimeApisClient`], which has methods for calling Runtime APIs. +//! It's created by calling [`crate::client::ClientAtBlock::runtime_apis()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let runtime_apis = at_block.runtime_apis(); +//! ``` + mod payload; use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT}; @@ -13,7 +25,7 @@ use std::marker::PhantomData; pub use payload::{DynamicPayload, Payload, StaticPayload, dynamic}; -/// Execute runtime API calls. +/// A client for working with Runtime APIs. See [the module docs](crate::runtime_apis) for more. #[derive_where(Clone; Client)] pub struct RuntimeApisClient<'atblock, T: Config, Client> { client: &'atblock Client, diff --git a/subxt/src/storage.rs b/subxt/src/storage.rs index 636529fb4f..9edbb47acf 100644 --- a/subxt/src/storage.rs +++ b/subxt/src/storage.rs @@ -1,3 +1,15 @@ +//! This module exposes [`StorageClient`], which has methods for fetching and iterating over +//! storage entries. It's created by calling [`crate::client::ClientAtBlock::storage()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let storage = at_block.storage(); +//! ``` + mod address; mod prefix_of; mod storage_entry; @@ -21,7 +33,7 @@ pub use storage_key::{StorageKey, StorageKeyPart}; pub use storage_key_value::StorageKeyValue; pub use storage_value::StorageValue; -/// A client for working with storage entries. +/// A client for working with storage entries. See [the module docs](crate::storage) for more. #[derive(Clone)] pub struct StorageClient<'atblock, T, Client> { client: &'atblock Client, diff --git a/subxt/src/storage/storage_entry.rs b/subxt/src/storage/storage_entry.rs index dbf7cd0f73..7270b4fc0b 100644 --- a/subxt/src/storage/storage_entry.rs +++ b/subxt/src/storage/storage_entry.rs @@ -1,14 +1,17 @@ -use crate::backend::BackendExt; +use crate::backend::{BackendExt, StorageResponse, StreamOf}; use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT}; use crate::config::Config; -use crate::error::StorageError; +use crate::error::{BackendError, StorageError}; use crate::storage::address::Address; use crate::storage::{PrefixOf, StorageKeyValue, StorageValue}; use crate::utils::YesMaybe; use core::marker::PhantomData; use frame_decode::storage::{IntoEncodableValues, StorageInfo, StorageTypeInfo}; -use futures::StreamExt; +use futures::{Stream, StreamExt}; +use scale_info::PortableRegistry; +use std::pin::Pin; use std::sync::Arc; +use std::task::Poll; /// This represents a single storage entry (be it a plain value or map) /// and the operations that can be performed on it. @@ -99,6 +102,14 @@ where /// This returns a full key to a single value in this storage entry. pub fn fetch_key(&self, key_parts: Addr::KeyParts) -> Result, StorageError> { + let num_keys = self.inner.info.keys.len(); + if key_parts.num_encodable_values() != num_keys { + return Err(StorageError::WrongNumberOfKeyPartsProvidedForFetching { + expected: num_keys, + got: key_parts.num_encodable_values(), + }); + } + self.key_from_any_parts(key_parts) } @@ -128,24 +139,14 @@ where &self, key_parts: impl IntoEncodableValues, ) -> Result, StorageError> { - let num_keys = self.inner.info.keys.len(); - if key_parts.num_encodable_values() != num_keys { - return Err(StorageError::WrongNumberOfKeyPartsProvidedForFetching { - expected: num_keys, - got: key_parts.num_encodable_values(), - }); - } - - let key = frame_decode::storage::encode_storage_key_with_info( + frame_decode::storage::encode_storage_key_with_info( self.pallet_name(), self.entry_name(), key_parts, &self.inner.info, self.inner.client.metadata_ref().types(), ) - .map_err(StorageError::StorageKeyEncodeError)?; - - Ok(key) + .map_err(StorageError::StorageKeyEncodeError) } } @@ -227,11 +228,15 @@ where pub async fn iter>( &self, key_parts: KeyParts, - ) -> Result< - impl futures::Stream, StorageError>> - + use<'atblock, Addr, Client, T, KeyParts>, - StorageError, - > { + ) -> Result, StorageError> { + let num_keys = self.inner.info.keys.len(); + if key_parts.num_encodable_values() >= num_keys { + return Err(StorageError::WrongNumberOfKeyPartsProvidedForIterating { + max_expected: num_keys - 1, + got: key_parts.num_encodable_values(), + }); + } + let info = self.inner.info.clone(); let types = self.inner.client.metadata_ref().types(); let key_bytes = self.key_from_any_parts(key_parts)?; @@ -243,20 +248,66 @@ where .backend() .storage_fetch_descendant_values(key_bytes, block_hash) .await - .map_err(StorageError::CannotIterateValues)? - .map(move |kv| { - let kv = match kv { - Ok(kv) => kv, - Err(e) => return Err(StorageError::StreamFailure(e)), - }; - Ok(StorageKeyValue::new( - info.clone(), - types, - kv.key.into(), - kv.value, - )) - }); + .map_err(StorageError::CannotIterateValues)?; - Ok(Box::pin(stream)) + // .map(move |kv| { + // let kv = match kv { + // Ok(kv) => kv, + // Err(e) => return Err(StorageError::StreamFailure(e)), + // }; + // Ok(StorageKeyValue::new( + // info.clone(), + // types, + // kv.key.into(), + // kv.value, + // )) + // }); + + Ok(StorageEntries { + info, + stream, + types, + marker: PhantomData, + }) + } +} + +/// A stream of storage entries. +pub struct StorageEntries<'atblock, Addr> { + // The raw underlying stream: + stream: StreamOf>, + // things we need to convert this into what we want: + info: Arc>, + types: &'atblock PortableRegistry, + marker: PhantomData, +} + +impl<'atblock, Addr: Address> StorageEntries<'atblock, Addr> { + /// Get the next storage entry. This is an alias for `futures::StreamExt::next(self)`. + pub async fn next(&mut self) -> Option, StorageError>> { + StreamExt::next(self).await + } +} + +impl<'atblock, Addr> std::marker::Unpin for StorageEntries<'atblock, Addr> {} +impl<'atblock, Addr: Address> Stream for StorageEntries<'atblock, Addr> { + type Item = Result, StorageError>; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + let val = match futures::ready!(self.stream.poll_next_unpin(cx)) { + Some(Ok(val)) => val, + Some(Err(e)) => return Poll::Ready(Some(Err(StorageError::StreamFailure(e)))), + None => return Poll::Ready(None), + }; + + Poll::Ready(Some(Ok(StorageKeyValue::new( + self.info.clone(), + self.types, + val.key.into(), + val.value, + )))) } } diff --git a/subxt/src/transactions.rs b/subxt/src/transactions.rs index f081e86e11..9edc61fed3 100644 --- a/subxt/src/transactions.rs +++ b/subxt/src/transactions.rs @@ -1,3 +1,16 @@ +//! This module exposes [`TransactionsClient`], which has methods for constructing and submitting +//! transactions. It's created by calling [`crate::client::ClientAtBlock::transactions()`], or +//! [`crate::client::ClientAtBlock::tx()`] for short. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let transactions = at_block.transactions(); +//! ``` + mod account_nonce; mod default_params; mod payload; @@ -26,7 +39,7 @@ pub use validation_result::{ TransactionInvalid, TransactionUnknown, TransactionValid, ValidationResult, }; -/// A client for working with transactions. +/// A client for working with transactions. See [the module docs](crate::transactions) for more. #[derive(Clone)] pub struct TransactionsClient<'atblock, T, Client> { client: &'atblock Client, @@ -257,7 +270,7 @@ impl<'atblock, T: Config, Client: OfflineClientAtBlockT> } // Create a V4 "signed" or a V5 "general" transaction. - pub fn create_signable_at_version( + fn create_signable_at_version( &self, call: &Call, params: >::Params, diff --git a/subxt/src/transactions/transaction_progress.rs b/subxt/src/transactions/transaction_progress.rs index d84710a5a0..559e7b9fb9 100644 --- a/subxt/src/transactions/transaction_progress.rs +++ b/subxt/src/transactions/transaction_progress.rs @@ -11,6 +11,11 @@ use futures::{Stream, StreamExt}; use std::pin::Pin; use std::task::{Context, Poll}; +/// A stream representing the progress of some transaction. Events can be +/// streamed and acted on using [`TransactionProgress::next()`], or the helper +/// functions [`TransactionProgress::wait_for_finalized`] and +/// [`TransactionProgress::wait_for_finalized_success`] can be used to wait +/// for completion. #[derive(Debug)] pub struct TransactionProgress<'atblock, T: Config, C> { sub: Option>>>, diff --git a/subxt/src/utils/account_id20.rs b/subxt/src/utils/account_id20.rs index 5eb2f815b0..997a1d6a75 100644 --- a/subxt/src/utils/account_id20.rs +++ b/subxt/src/utils/account_id20.rs @@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize}; use thiserror::Error as DeriveError; #[derive( - Copy, Clone, + Copy, Eq, PartialEq, Ord, diff --git a/subxt/src/view_functions.rs b/subxt/src/view_functions.rs index 1f9e3dff03..985a800fa6 100644 --- a/subxt/src/view_functions.rs +++ b/subxt/src/view_functions.rs @@ -2,6 +2,18 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. +//! This module exposes [`ViewFunctionsClient`], which has methods for calling View Functions. +//! It's created by calling [`crate::client::ClientAtBlock::view_functions()`]. +//! +//! ```rust,no_run +//! pub use subxt::{OnlineClient, PolkadotConfig}; +//! +//! let client = OnlineClient::new().await?; +//! let at_block = client.at_current_block().await?; +//! +//! let view_functions = at_block.view_functions(); +//! ``` + mod payload; use crate::client::{OfflineClientAtBlockT, OnlineClientAtBlockT}; @@ -16,7 +28,7 @@ pub use payload::{DynamicPayload, Payload, StaticPayload, dynamic}; /// The name of the Runtime API call which can execute const CALL_NAME: &str = "RuntimeViewFunction_execute_view_function"; -/// Execute View Function calls. +/// A client for working with View Functions. See [the module docs](crate::view_functions) for more. #[derive_where(Clone; Client)] pub struct ViewFunctionsClient<'atblock, T: Config, Client> { client: &'atblock Client, diff --git a/utils/accountid32/src/lib.rs b/utils/accountid32/src/lib.rs index 4be6268362..debe4fc04c 100644 --- a/utils/accountid32/src/lib.rs +++ b/utils/accountid32/src/lib.rs @@ -23,6 +23,7 @@ use thiserror::Error as DeriveError; /// that type. #[derive( Clone, + Copy, Eq, PartialEq, Ord,