diff --git a/subxt/src/blocks/block_types.rs b/subxt/src/blocks/block_types.rs index a1cb01ec25..b7158025b4 100644 --- a/subxt/src/blocks/block_types.rs +++ b/subxt/src/blocks/block_types.rs @@ -167,3 +167,5 @@ where }; Ok(account_nonce) } + +pub struct LatestBlockHeader {} diff --git a/subxt/src/client/client_capabilities.rs b/subxt/src/client/client_capabilities.rs new file mode 100644 index 0000000000..a9e9706481 --- /dev/null +++ b/subxt/src/client/client_capabilities.rs @@ -0,0 +1,72 @@ +// use crate::{backend::Backend, Config}; + +// use super::OfflineClientT; +use crate::{Config, Error, OfflineClient, OnlineClient}; +use core::future::Future; + +use super::OfflineClientT; + +pub type FetchBlockHeader
= + Box> + Send + 'static>; + +/// An object-safe trait abstracting over capabilities of offline and online-clients. +/// +/// Implemented by [`subxt::OfflineClient`], [`subxt::OnlineClient`] and [`subxt::LightClient`] the like. +pub trait ClientCapabilities { + fn latest_block_header(&self) -> Option> + where + T::Header: Clone; +} + +impl ClientCapabilities for OfflineClient { + fn latest_block_header(&self) -> Option> + where + T::Header: Clone, + { + None + } +} + +impl ClientCapabilities for OnlineClient { + fn latest_block_header(&self) -> Option> + where + T::Header: Clone, + { + let client = self.clone(); + Some(Box::new(async move { + let block = client.blocks().at_latest().await?; + let header: T::Header = block.header().clone(); + Ok(header) + })) + } +} + +crate::macros::cfg_unstable_light_client! { + use super::LightClient; + + impl ClientCapabilities for LightClient { + fn latest_block_header(&self) -> Option> + where + T::Header: Clone, + { + let client = self.clone(); + Some(Box::new(async move { + let block = client.blocks().at_latest().await?; + let header: T::Header = block.header().clone(); + Ok(header) + })) + } + } +} + +#[cfg(test)] +mod tests { + use super::ClientCapabilities; + use crate::PolkadotConfig; + use core::panic; + + fn is_object_safe(client: Box>) { + _ = client.latest_block_header(); + unreachable!("Do not call this function, it just needs to compile.") + } +} diff --git a/subxt/src/client/mod.rs b/subxt/src/client/mod.rs index c764af4b59..e3a35dc87a 100644 --- a/subxt/src/client/mod.rs +++ b/subxt/src/client/mod.rs @@ -8,6 +8,7 @@ //! require network access. The [`OnlineClient`] requires network //! access. +mod client_capabilities; mod offline_client; mod online_client; diff --git a/subxt/src/config/extrinsic_params.rs b/subxt/src/config/extrinsic_params.rs index 42f3b620af..f8d48c97d7 100644 --- a/subxt/src/config/extrinsic_params.rs +++ b/subxt/src/config/extrinsic_params.rs @@ -79,3 +79,57 @@ pub trait ExtrinsicParamsEncoder: 'static { /// signing it, meaning the client and node must agree on their values. fn encode_additional_to(&self, _v: &mut Vec) {} } + +/// Like the `From` trait, if value is Some(_), but it lets us circumvent the orphan rule, because we want to implement +/// `DefaultOrFrom
` for `()`, such that DefaultOrFrom
is implemented by `((),(),(),CheckMortalityParams)`, if `CheckMortalityParams` implements `DefaultFrom
` +pub trait DefaultOrFrom { + /// If value + fn default_or_from(value: Option<&T>) -> Self; +} + +impl DefaultOrFrom for () { + fn default_or_from(_value: Option<&T>) -> () {} +} + +macro_rules! impl_default_from_tuples { + ($($ident:ident),+) => { + // We do some magic when the tuple is wrapped in AnyOf. We + // look at the metadata, and use this to select and make use of only the extensions + // that we actually need for the chain we're dealing with. + impl DefaultOrFrom for ($($ident,)+) + where + $($ident: DefaultOrFrom,)+ + { + fn default_or_from(value: Option<&T>) -> Self { + ($( + (<$ident as DefaultOrFrom>::default_or_from(value)), + )+) + } + } + } +} + +#[rustfmt::skip] +const _: () = { + impl_default_from_tuples!(A); + impl_default_from_tuples!(A, B); + impl_default_from_tuples!(A, B, C); + impl_default_from_tuples!(A, B, C, D); + impl_default_from_tuples!(A, B, C, D, E); + impl_default_from_tuples!(A, B, C, D, E, F); + impl_default_from_tuples!(A, B, C, D, E, F, G); + impl_default_from_tuples!(A, B, C, D, E, F, G, H); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U); + impl_default_from_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, U, V); +}; diff --git a/subxt/src/config/mod.rs b/subxt/src/config/mod.rs index ddfa2466c7..b028d0b77c 100644 --- a/subxt/src/config/mod.rs +++ b/subxt/src/config/mod.rs @@ -23,7 +23,9 @@ use scale_encode::EncodeAsType; use serde::{de::DeserializeOwned, Serialize}; pub use default_extrinsic_params::{DefaultExtrinsicParams, DefaultExtrinsicParamsBuilder}; -pub use extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +pub use extrinsic_params::{ + DefaultOrFrom, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError, +}; pub use polkadot::{PolkadotConfig, PolkadotExtrinsicParams, PolkadotExtrinsicParamsBuilder}; pub use signed_extensions::SignedExtension; pub use substrate::{SubstrateConfig, SubstrateExtrinsicParams, SubstrateExtrinsicParamsBuilder}; diff --git a/subxt/src/config/signed_extensions.rs b/subxt/src/config/signed_extensions.rs index f0864cee61..9751a57f0c 100644 --- a/subxt/src/config/signed_extensions.rs +++ b/subxt/src/config/signed_extensions.rs @@ -7,7 +7,10 @@ //! [`AnyOf`] to configure the set of signed extensions which are known about //! when interacting with a chain. -use super::extrinsic_params::{ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError}; +use super::extrinsic_params::{ + DefaultOrFrom, ExtrinsicParams, ExtrinsicParamsEncoder, ExtrinsicParamsError, +}; +use crate::config::Header; use crate::utils::Era; use crate::{client::OfflineClientT, Config}; use codec::{Compact, Encode}; @@ -166,6 +169,17 @@ impl Default for CheckMortalityParams { } } +impl DefaultOrFrom for CheckMortalityParams { + fn default_or_from(value: Option<&T::Header>) -> Self { + if let Some(header) = value { + const FOR_N_BLOCKS: u64 = 32; + CheckMortalityParams::mortal(FOR_N_BLOCKS, header.number().into(), header.hash()) + } else { + Default::default() + } + } +} + impl CheckMortalityParams { /// Configure a mortal transaction. The `period` is (roughly) how many /// blocks the transaction will be valid for. The `block_number` and @@ -253,6 +267,12 @@ impl Default for ChargeAssetTxPaymentParams { } } +impl DefaultOrFrom for ChargeAssetTxPaymentParams { + fn default_or_from(_value: Option<&T::Header>) -> Self { + Default::default() + } +} + impl ChargeAssetTxPaymentParams { /// Don't provide a tip to the extrinsic author. pub fn no_tip() -> Self { @@ -324,6 +344,12 @@ pub struct ChargeTransactionPaymentParams { tip: u128, } +impl DefaultOrFrom for ChargeTransactionPaymentParams { + fn default_or_from(_value: Option<&T>) -> Self { + Default::default() + } +} + impl ChargeTransactionPaymentParams { /// Don't provide a tip to the extrinsic author. pub fn no_tip() -> Self { diff --git a/subxt/src/tx/tx_client.rs b/subxt/src/tx/tx_client.rs index 6bf6c9ab57..dd40567087 100644 --- a/subxt/src/tx/tx_client.rs +++ b/subxt/src/tx/tx_client.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use crate::{ backend::{BackendExt, BlockRef, TransactionStatus}, client::{OfflineClientT, OnlineClientT}, - config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher}, + config::{Config, DefaultOrFrom, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher}, error::{Error, MetadataError}, tx::{Signer as SignerT, TxPayload, TxProgress}, utils::{Encoded, PhantomDataSendSync}, @@ -213,9 +213,13 @@ where where Call: TxPayload, Signer: SignerT, - >::OtherParams: Default, + >::OtherParams: DefaultOrFrom, { - self.sign_and_submit_then_watch(call, signer, Default::default()) + let latest_block = self.client.blocks().at_latest().await.ok(); + let latest_header = latest_block.as_ref().map(|b| b.header()); + let other_params = + >::OtherParams::default_or_from(latest_header); + self.sign_and_submit_then_watch(call, signer, other_params) .await } @@ -257,9 +261,13 @@ where where Call: TxPayload, Signer: SignerT, - >::OtherParams: Default, + >::OtherParams: DefaultOrFrom, { - self.sign_and_submit(call, signer, Default::default()).await + let latest_block = self.client.blocks().at_latest().await.ok(); + let latest_header = latest_block.as_ref().map(|b| b.header()); + let other_params = + >::OtherParams::default_or_from(latest_header); + self.sign_and_submit(call, signer, other_params).await } /// Creates and signs an extrinsic and submits to the chain for block inclusion.