More examples and get compiling with deny(missing_docs)

This commit is contained in:
James Wilson
2025-12-15 11:37:08 +00:00
parent 37d4cf7524
commit 34eeea2c7e
26 changed files with 494 additions and 74 deletions
+2 -2
View File
@@ -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 {
+74
View File
@@ -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::<polkadot::Call>()?;
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::<Value>()?;
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::<Value>()?;
println!(" {name}: {value}");
}
}
}
}
Ok(())
}
+55
View File
@@ -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::<Value>()?;
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::<Value>()?;
println!(" #{event_idx}: {pallet_name}.{event_name}");
println!(" {event_values}");
}
}
}
Ok(())
}
+85
View File
@@ -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::<Value>()?;
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::<Value>()?;
// 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::<AccountInfo>()?;
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::<AccountId32>()?
.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::<scale_value::Value>()?;
println!(" {account_id} => {balance_info}");
}
Ok(())
}
@@ -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> {
+3
View File
@@ -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<T: Config> {
archive: BackendChoice<ArchiveBackend<T>>,
chainhead: BackendChoice<ChainHeadBackend<T>>,
@@ -183,6 +184,8 @@ pub struct CombinedBackendDriver<T: Config> {
}
impl<T: Config> CombinedBackendDriver<T> {
/// 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()
}
+19 -4
View File
@@ -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<T, Client> {
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)
}
+2
View File
@@ -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<T: Config> {
/// The configuration for this client.
+1 -1
View File
@@ -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<T: Config> {
inner: Arc<OnlineClientInner<T>>,
@@ -22,6 +22,12 @@ impl<T: Config> From<u64> for BlockNumberOrRef<T> {
}
}
impl<T: Config> From<usize> for BlockNumberOrRef<T> {
fn from(value: usize) -> Self {
BlockNumberOrRef::Number(value as u64)
}
}
impl<T: Config> From<BlockRef<HashFor<T>>> for BlockNumberOrRef<T> {
fn from(block_ref: BlockRef<HashFor<T>>) -> Self {
BlockNumberOrRef::BlockRef(block_ref)
+6 -1
View File
@@ -19,6 +19,11 @@ impl<T: Config> Blocks<T> {
) -> Self {
Blocks { client, stream }
}
/// Return the next block in the stream when it is produced.
pub async fn next(&mut self) -> Option<Result<Block<T>, BlocksError>> {
StreamExt::next(self).await
}
}
impl<T: Config> Stream for Blocks<T> {
@@ -68,7 +73,7 @@ impl<T: Config> Block<T> {
}
/// Instantiate a client at this block.
pub async fn client(
pub async fn at(
&self,
) -> Result<ClientAtBlock<T, OnlineClientAtBlockImpl<T>>, OnlineClientAtBlockError> {
self.client.at_block(self.block_ref.clone()).await
+9 -5
View File
@@ -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;
+30 -10
View File
@@ -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<Item = (u32, ArcMetadata)>,
) -> 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<Item = SpecVersionForRange>,
) -> 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()
}
}
+13 -1
View File
@@ -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,
+13 -1
View File
@@ -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,
+13 -1
View File
@@ -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<T>,
+13 -1
View File
@@ -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<T>,
+2 -5
View File
@@ -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;
+13 -1
View File
@@ -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,
+13 -1
View File
@@ -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,
+85 -34
View File
@@ -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<Vec<u8>, 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<Vec<u8>, 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<KeyParts: PrefixOf<Addr::KeyParts>>(
&self,
key_parts: KeyParts,
) -> Result<
impl futures::Stream<Item = Result<StorageKeyValue<'atblock, Addr>, StorageError>>
+ use<'atblock, Addr, Client, T, KeyParts>,
StorageError,
> {
) -> Result<StorageEntries<'atblock, Addr>, 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<Result<StorageResponse, BackendError>>,
// things we need to convert this into what we want:
info: Arc<StorageInfo<'atblock, u32>>,
types: &'atblock PortableRegistry,
marker: PhantomData<Addr>,
}
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<Result<StorageKeyValue<'atblock, Addr>, 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<StorageKeyValue<'atblock, Addr>, StorageError>;
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
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,
))))
}
}
+15 -2
View File
@@ -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<T>>
}
// Create a V4 "signed" or a V5 "general" transaction.
pub fn create_signable_at_version<Call>(
fn create_signable_at_version<Call>(
&self,
call: &Call,
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
@@ -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<StreamOfResults<BackendTransactionStatus<HashFor<T>>>>,
+1 -1
View File
@@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
use thiserror::Error as DeriveError;
#[derive(
Copy,
Clone,
Copy,
Eq,
PartialEq,
Ord,
+13 -1
View File
@@ -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,
+1
View File
@@ -23,6 +23,7 @@ use thiserror::Error as DeriveError;
/// that type.
#[derive(
Clone,
Copy,
Eq,
PartialEq,
Ord,