use super::extrinsic_params::ExtrinsicParams;
use super::refine_params::RefineParamsData;
use super::RefineParams;
use crate::client::ClientState;
use crate::config::ExtrinsicParamsEncoder;
use crate::error::ExtrinsicParamsError;
use crate::utils::Era;
use crate::Config;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::vec::Vec;
use codec::{Compact, Encode};
use core::fmt::Debug;
use derive_where::derive_where;
use hashbrown::HashMap;
use scale_decode::DecodeAsType;
use scale_info::PortableRegistry;
pub trait SignedExtension<T: Config>: ExtrinsicParams<T> {
type Decoded: DecodeAsType;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool;
}
pub struct CheckMetadataHash {
}
impl<T: Config> ExtrinsicParams<T> for CheckMetadataHash {
type Params = ();
fn new(_client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckMetadataHash {})
}
}
impl ExtrinsicParamsEncoder for CheckMetadataHash {
fn encode_extra_to(&self, v: &mut Vec<u8>) {
0u8.encode_to(v);
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
None::<()>.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckMetadataHash {
type Decoded = CheckMetadataHashMode;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckMetadataHash"
}
}
#[derive(Copy, Clone, Debug, DecodeAsType)]
pub enum CheckMetadataHashMode {
Disabled,
Enabled,
}
impl CheckMetadataHashMode {
pub fn is_enabled(&self) -> bool {
match self {
CheckMetadataHashMode::Disabled => false,
CheckMetadataHashMode::Enabled => true,
}
}
}
pub struct CheckSpecVersion(u32);
impl<T: Config> ExtrinsicParams<T> for CheckSpecVersion {
type Params = ();
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckSpecVersion(client.runtime_version.spec_version))
}
}
impl ExtrinsicParamsEncoder for CheckSpecVersion {
fn encode_additional_to(&self, v: &mut Vec<u8>) {
self.0.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckSpecVersion {
type Decoded = ();
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckSpecVersion"
}
}
pub struct CheckNonce(Compact<u64>);
impl<T: Config> ExtrinsicParams<T> for CheckNonce {
type Params = CheckNonceParams;
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
let nonce = params.0.unwrap_or(0);
Ok(CheckNonce(Compact(nonce)))
}
}
impl ExtrinsicParamsEncoder for CheckNonce {
fn encode_extra_to(&self, v: &mut Vec<u8>) {
self.0.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckNonce {
type Decoded = u64;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckNonce"
}
}
#[derive(Debug, Clone, Default)]
pub struct CheckNonceParams(pub Option<u64>);
impl<T: Config> RefineParams<T> for CheckNonceParams {
fn refine(&mut self, data: &RefineParamsData<T>) {
if self.0.is_none() {
self.0 = Some(data.account_nonce());
}
}
}
pub struct CheckTxVersion(u32);
impl<T: Config> ExtrinsicParams<T> for CheckTxVersion {
type Params = ();
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckTxVersion(client.runtime_version.transaction_version))
}
}
impl ExtrinsicParamsEncoder for CheckTxVersion {
fn encode_additional_to(&self, v: &mut Vec<u8>) {
self.0.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckTxVersion {
type Decoded = ();
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckTxVersion"
}
}
pub struct CheckGenesis<T: Config>(T::Hash);
impl<T: Config> ExtrinsicParams<T> for CheckGenesis<T> {
type Params = ();
fn new(client: &ClientState<T>, _params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(CheckGenesis(client.genesis_hash))
}
}
impl<T: Config> ExtrinsicParamsEncoder for CheckGenesis<T> {
fn encode_additional_to(&self, v: &mut Vec<u8>) {
self.0.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckGenesis<T> {
type Decoded = ();
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckGenesis"
}
}
pub struct CheckMortality<T: Config> {
era: Era,
checkpoint: T::Hash,
}
pub struct CheckMortalityParams<T: Config>(Option<CheckMortalityParamsInner<T>>);
struct CheckMortalityParamsInner<T: Config> {
era: Era,
checkpoint: Option<T::Hash>,
}
impl<T: Config> Default for CheckMortalityParams<T> {
fn default() -> Self {
CheckMortalityParams(None)
}
}
impl<T: Config> RefineParams<T> for CheckMortalityParams<T> {
fn refine(&mut self, data: &RefineParamsData<T>) {
if self.0.is_none() {
const TX_VALID_FOR: u64 = 32;
*self =
CheckMortalityParams::mortal(TX_VALID_FOR, data.block_number(), data.block_hash());
}
}
}
impl<T: Config> CheckMortalityParams<T> {
pub fn mortal(period: u64, block_number: u64, block_hash: T::Hash) -> Self {
Self(Some(CheckMortalityParamsInner {
era: Era::mortal(period, block_number),
checkpoint: Some(block_hash),
}))
}
pub fn immortal() -> Self {
Self(Some(CheckMortalityParamsInner {
era: Era::Immortal,
checkpoint: None,
}))
}
}
impl<T: Config> ExtrinsicParams<T> for CheckMortality<T> {
type Params = CheckMortalityParams<T>;
fn new(client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
let check_mortality = if let Some(params) = params.0 {
CheckMortality {
era: params.era,
checkpoint: params.checkpoint.unwrap_or(client.genesis_hash),
}
} else {
CheckMortality {
era: Era::Immortal,
checkpoint: client.genesis_hash,
}
};
Ok(check_mortality)
}
}
impl<T: Config> ExtrinsicParamsEncoder for CheckMortality<T> {
fn encode_extra_to(&self, v: &mut Vec<u8>) {
self.era.encode_to(v);
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
self.checkpoint.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for CheckMortality<T> {
type Decoded = Era;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "CheckMortality"
}
}
#[derive(DecodeAsType)]
#[derive_where(Clone, Debug; T::AssetId)]
#[decode_as_type(trait_bounds = "T::AssetId: DecodeAsType")]
pub struct ChargeAssetTxPayment<T: Config> {
tip: Compact<u128>,
asset_id: Option<T::AssetId>,
}
impl<T: Config> ChargeAssetTxPayment<T> {
pub fn tip(&self) -> u128 {
self.tip.0
}
pub fn asset_id(&self) -> Option<&T::AssetId> {
self.asset_id.as_ref()
}
}
pub struct ChargeAssetTxPaymentParams<T: Config> {
tip: u128,
asset_id: Option<T::AssetId>,
}
impl<T: Config> Default for ChargeAssetTxPaymentParams<T> {
fn default() -> Self {
ChargeAssetTxPaymentParams {
tip: Default::default(),
asset_id: Default::default(),
}
}
}
impl<T: Config> ChargeAssetTxPaymentParams<T> {
pub fn no_tip() -> Self {
ChargeAssetTxPaymentParams {
tip: 0,
asset_id: None,
}
}
pub fn tip(tip: u128) -> Self {
ChargeAssetTxPaymentParams {
tip,
asset_id: None,
}
}
pub fn tip_of(tip: u128, asset_id: T::AssetId) -> Self {
ChargeAssetTxPaymentParams {
tip,
asset_id: Some(asset_id),
}
}
}
impl<T: Config> ExtrinsicParams<T> for ChargeAssetTxPayment<T> {
type Params = ChargeAssetTxPaymentParams<T>;
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(ChargeAssetTxPayment {
tip: Compact(params.tip),
asset_id: params.asset_id,
})
}
}
impl<T: Config> RefineParams<T> for ChargeAssetTxPaymentParams<T> {}
impl<T: Config> ExtrinsicParamsEncoder for ChargeAssetTxPayment<T> {
fn encode_extra_to(&self, v: &mut Vec<u8>) {
(self.tip, &self.asset_id).encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for ChargeAssetTxPayment<T> {
type Decoded = Self;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "ChargeAssetTxPayment"
}
}
#[derive(Clone, Debug, DecodeAsType)]
pub struct ChargeTransactionPayment {
tip: Compact<u128>,
}
impl ChargeTransactionPayment {
pub fn tip(&self) -> u128 {
self.tip.0
}
}
#[derive(Default)]
pub struct ChargeTransactionPaymentParams {
tip: u128,
}
impl ChargeTransactionPaymentParams {
pub fn no_tip() -> Self {
ChargeTransactionPaymentParams { tip: 0 }
}
pub fn tip(tip: u128) -> Self {
ChargeTransactionPaymentParams { tip }
}
}
impl<T: Config> ExtrinsicParams<T> for ChargeTransactionPayment {
type Params = ChargeTransactionPaymentParams;
fn new(_client: &ClientState<T>, params: Self::Params) -> Result<Self, ExtrinsicParamsError> {
Ok(ChargeTransactionPayment {
tip: Compact(params.tip),
})
}
}
impl<T: Config> RefineParams<T> for ChargeTransactionPaymentParams {}
impl ExtrinsicParamsEncoder for ChargeTransactionPayment {
fn encode_extra_to(&self, v: &mut Vec<u8>) {
self.tip.encode_to(v);
}
}
impl<T: Config> SignedExtension<T> for ChargeTransactionPayment {
type Decoded = Self;
fn matches(identifier: &str, _type_id: u32, _types: &PortableRegistry) -> bool {
identifier == "ChargeTransactionPayment"
}
}
pub struct AnyOf<T, Params> {
params: Vec<Box<dyn ExtrinsicParamsEncoder>>,
_marker: core::marker::PhantomData<(T, Params)>,
}
macro_rules! impl_tuples {
($($ident:ident $index:tt),+) => {
impl <T, $($ident),+> ExtrinsicParams<T> for AnyOf<T, ($($ident,)+)>
where
T: Config,
$($ident: SignedExtension<T>,)+
{
type Params = ($($ident::Params,)+);
fn new(
client: &ClientState<T>,
params: Self::Params,
) -> Result<Self, ExtrinsicParamsError> {
let metadata = &client.metadata;
let types = metadata.types();
let mut exts_by_index = HashMap::new();
$({
for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() {
if exts_by_index.contains_key(&idx) {
continue
}
if $ident::matches(e.identifier(), e.extra_ty(), types) {
let ext = $ident::new(client, params.$index)?;
let boxed_ext: Box<dyn ExtrinsicParamsEncoder> = Box::new(ext);
exts_by_index.insert(idx, boxed_ext);
break
}
}
})+
let mut params = Vec::new();
for (idx, e) in metadata.extrinsic().signed_extensions().iter().enumerate() {
let Some(ext) = exts_by_index.remove(&idx) else {
if is_type_empty(e.extra_ty(), types) {
continue
} else {
return Err(ExtrinsicParamsError::UnknownSignedExtension(e.identifier().to_owned()));
}
};
params.push(ext);
}
Ok(AnyOf {
params,
_marker: core::marker::PhantomData
})
}
}
impl <T, $($ident),+> ExtrinsicParamsEncoder for AnyOf<T, ($($ident,)+)>
where
T: Config,
$($ident: SignedExtension<T>,)+
{
fn encode_extra_to(&self, v: &mut Vec<u8>) {
for ext in &self.params {
ext.encode_extra_to(v);
}
}
fn encode_additional_to(&self, v: &mut Vec<u8>) {
for ext in &self.params {
ext.encode_additional_to(v);
}
}
}
}
}
#[rustfmt::skip]
const _: () = {
impl_tuples!(A 0);
impl_tuples!(A 0, B 1);
impl_tuples!(A 0, B 1, C 2);
impl_tuples!(A 0, B 1, C 2, D 3);
impl_tuples!(A 0, B 1, C 2, D 3, E 4);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19);
impl_tuples!(A 0, B 1, C 2, D 3, E 4, F 5, G 6, H 7, I 8, J 9, K 10, L 11, M 12, N 13, O 14, P 15, Q 16, R 17, S 18, U 19, V 20);
};
fn is_type_empty(type_id: u32, types: &scale_info::PortableRegistry) -> bool {
let Some(ty) = types.resolve(type_id) else {
return false;
};
use scale_info::TypeDef;
match &ty.type_def {
TypeDef::Composite(c) => c.fields.iter().all(|f| is_type_empty(f.ty.id, types)),
TypeDef::Array(a) => a.len == 0 || is_type_empty(a.type_param.id, types),
TypeDef::Tuple(t) => t.fields.iter().all(|f| is_type_empty(f.id, types)),
TypeDef::BitSequence(_)
| TypeDef::Variant(_)
| TypeDef::Sequence(_)
| TypeDef::Compact(_)
| TypeDef::Primitive(_) => false,
}
}