mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 07:01:03 +00:00
Asset Transaction Payment (#488)
* use new pallet name based genesis config names * use custom substrate and update polkadot * add initial asset-tx-payment pallet * update cargo.toml * add (failing) tests * dispatch Calls instead of using Pallet functions * fix fee-refund split * add test for transaction payment with tip * update cargo.lock * update cargo.lock * remove mint workaround and use Mutable trait * extract fee charging logic into OnChargeAssetTransaction trait * use asset-tx-payment in statemint runtime * make extrinsics public * make extrinsics public * use ChargeAssetIdOf type alias * update deps * move back to AssetIdOf * remove extra rpc_http_threads * use different substrate branch * Update pallets/asset-tx-payment/src/payment.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * Update pallets/asset-tx-payment/src/payment.rs Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * remove overrides * override substrate deps (again) * increment spec_version and transaction_version (because we change transaction signing) * remove direct dependency on pallet-balances from asset-tx-payment * remove Assets pallet visibility workaround * add docs and comments * remove unused imports * more docs * add more debug asserts to document assumptions * add test for tx payment from account with only assets * add test for missing asset case * extend test to cover non-sufficient assets * add a test for Pays::No (refunded transaction) * add type alias comments * add more doc comments * add asset-tx-payment to statemine and westmint * improve formatting * update license headers * add default implementation of HandleCredit for () * update doc comments and format imports * adjust Cargo.toml * update cargo.lock * cargo fmt * cargo fmt * cargo fmt * cargo +nightly fmt * add type alias for OnChargeTransaction * cargo +nightly fmt * convert ChargeAssetTxPayment from tuple struct to regular struct * add more comments * formatting * adjust imports and comment * cargo +nightly fmt * reformat comment * use ChargeTransactionPayment's own get_priority + update Substrate * update Substrate and Polkadot * cargo fmt * cargo fmt * add OperationalFeeMultiplier to asset tx payment tests * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * add doc links * charge a minimum converted asset fee of 1 if the input fee is greater zero * cargo +nightly fmt * bump spec and transaction version Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,8 @@ sp-io = { git = 'https://github.com/paritytech/substrate', branch = "master", de
|
||||
frame-executive = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
frame-support = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
frame-system = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
pallet-assets = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
pallet-authorship = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
pallet-balances = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
sp-runtime = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
sp-core = { git = 'https://github.com/paritytech/substrate', branch = "master", default-features = false }
|
||||
@@ -32,6 +34,7 @@ xcm = { git = 'https://github.com/paritytech/polkadot', branch = "master", defau
|
||||
xcm-executor = { git = 'https://github.com/paritytech/polkadot', branch = "master", default-features = false }
|
||||
|
||||
# Local dependencies
|
||||
pallet-asset-tx-payment = { path = '../../pallets/asset-tx-payment', default-features = false }
|
||||
pallet-collator-selection = { path = '../../pallets/collator-selection', default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -52,7 +55,10 @@ std = [
|
||||
'frame-support/std',
|
||||
'frame-executive/std',
|
||||
'frame-system/std',
|
||||
'pallet-asset-tx-payment/std',
|
||||
'pallet-collator-selection/std',
|
||||
'pallet-assets/std',
|
||||
'pallet-authorship/std',
|
||||
'pallet-balances/std',
|
||||
'node-primitives/std',
|
||||
'polkadot-runtime-common/std',
|
||||
|
||||
@@ -16,23 +16,31 @@
|
||||
//! Auxillary struct/enums for parachain runtimes.
|
||||
//! Taken from polkadot/runtime/common (at a21cd64) and adapted for parachains.
|
||||
|
||||
use frame_support::traits::{fungibles, Contains, Currency, Get, Imbalance, OnUnbalanced};
|
||||
use frame_support::traits::{
|
||||
fungibles::{self, Balanced, CreditOf},
|
||||
Contains, Currency, Get, Imbalance, OnUnbalanced,
|
||||
};
|
||||
use pallet_asset_tx_payment::HandleCredit;
|
||||
use sp_runtime::traits::Zero;
|
||||
use sp_std::marker::PhantomData;
|
||||
use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation};
|
||||
use xcm_executor::traits::FilterAssetLocation;
|
||||
|
||||
/// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type.
|
||||
pub type NegativeImbalance<T> = <pallet_balances::Pallet<T> as Currency<
|
||||
<T as frame_system::Config>::AccountId,
|
||||
>>::NegativeImbalance;
|
||||
|
||||
/// Logic for the author to get a portion of fees.
|
||||
pub struct ToStakingPot<R>(sp_std::marker::PhantomData<R>);
|
||||
/// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`.
|
||||
pub type AccountIdOf<R> = <R as frame_system::Config>::AccountId;
|
||||
|
||||
/// Implementation of `OnUnbalanced` that deposits the fees into a staking pot for later payout.
|
||||
pub struct ToStakingPot<R>(PhantomData<R>);
|
||||
impl<R> OnUnbalanced<NegativeImbalance<R>> for ToStakingPot<R>
|
||||
where
|
||||
R: pallet_balances::Config + pallet_collator_selection::Config,
|
||||
<R as frame_system::Config>::AccountId: From<polkadot_primitives::v1::AccountId>,
|
||||
<R as frame_system::Config>::AccountId: Into<polkadot_primitives::v1::AccountId>,
|
||||
AccountIdOf<R>:
|
||||
From<polkadot_primitives::v1::AccountId> + Into<polkadot_primitives::v1::AccountId>,
|
||||
<R as frame_system::Config>::Event: From<pallet_balances::Event<R>>,
|
||||
{
|
||||
fn on_nonzero_unbalanced(amount: NegativeImbalance<R>) {
|
||||
@@ -46,13 +54,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge the fees into one item and pass them on to the staking pot.
|
||||
pub struct DealWithFees<R>(sp_std::marker::PhantomData<R>);
|
||||
/// Implementation of `OnUnbalanced` that deals with the fees by combining tip and fee and passing
|
||||
/// the result on to `ToStakingPot`.
|
||||
pub struct DealWithFees<R>(PhantomData<R>);
|
||||
impl<R> OnUnbalanced<NegativeImbalance<R>> for DealWithFees<R>
|
||||
where
|
||||
R: pallet_balances::Config + pallet_collator_selection::Config,
|
||||
<R as frame_system::Config>::AccountId: From<polkadot_primitives::v1::AccountId>,
|
||||
<R as frame_system::Config>::AccountId: Into<polkadot_primitives::v1::AccountId>,
|
||||
AccountIdOf<R>:
|
||||
From<polkadot_primitives::v1::AccountId> + Into<polkadot_primitives::v1::AccountId>,
|
||||
<R as frame_system::Config>::Event: From<pallet_balances::Event<R>>,
|
||||
{
|
||||
fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance<R>>) {
|
||||
@@ -65,6 +74,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A `HandleCredit` implementation that naively transfers the fees to the block author.
|
||||
/// Will drop and burn the assets in case the transfer fails.
|
||||
pub struct AssetsToBlockAuthor<R>(PhantomData<R>);
|
||||
impl<R> HandleCredit<AccountIdOf<R>, pallet_assets::Pallet<R>> for AssetsToBlockAuthor<R>
|
||||
where
|
||||
R: pallet_authorship::Config + pallet_assets::Config,
|
||||
AccountIdOf<R>:
|
||||
From<polkadot_primitives::v1::AccountId> + Into<polkadot_primitives::v1::AccountId>,
|
||||
{
|
||||
fn handle_credit(credit: CreditOf<AccountIdOf<R>, pallet_assets::Pallet<R>>) {
|
||||
let author = pallet_authorship::Pallet::<R>::author();
|
||||
// In case of error: Will drop the result triggering the `OnDrop` of the imbalance.
|
||||
let _ = pallet_assets::Pallet::<R>::resolve(&author, credit);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow checking in assets that have issuance > 0.
|
||||
pub struct NonZeroIssuance<AccountId, Assets>(PhantomData<(AccountId, Assets)>);
|
||||
impl<AccountId, Assets> Contains<<Assets as fungibles::Inspect<AccountId>>::AssetId>
|
||||
|
||||
@@ -62,6 +62,7 @@ cumulus-pallet-xcmp-queue = { path = "../../pallets/xcmp-queue", default-feature
|
||||
cumulus-pallet-xcm = { path = "../../pallets/xcm", default-features = false }
|
||||
cumulus-pallet-session-benchmarking = {path = "../../pallets/session-benchmarking", default-features = false, version = "3.0.0"}
|
||||
cumulus-ping = { path = "../pallets/ping", default-features = false }
|
||||
pallet-asset-tx-payment = { path = "../../pallets/asset-tx-payment", default-features = false }
|
||||
pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false }
|
||||
parachains-common = { path = "../parachains-common", default-features = false }
|
||||
|
||||
@@ -136,6 +137,7 @@ std = [
|
||||
"pallet-utility/std",
|
||||
"parachain-info/std",
|
||||
"cumulus-pallet-aura-ext/std",
|
||||
"pallet-asset-tx-payment/std",
|
||||
"pallet-collator-selection/std",
|
||||
"cumulus-pallet-dmp-queue/std",
|
||||
"cumulus-pallet-parachain-system/std",
|
||||
|
||||
@@ -29,7 +29,7 @@ use sp_api::impl_runtime_apis;
|
||||
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
|
||||
use sp_runtime::{
|
||||
create_runtime_str, generic, impl_opaque_keys,
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto},
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult,
|
||||
};
|
||||
@@ -56,7 +56,7 @@ use frame_system::{
|
||||
};
|
||||
pub use parachains_common as common;
|
||||
use parachains_common::{
|
||||
impls::{DealWithFees, NonZeroIssuance},
|
||||
impls::{AssetsToBlockAuthor, DealWithFees, NonZeroIssuance},
|
||||
opaque, AccountId, AssetId, AuraId, Balance, BlockNumber, Hash, Header, Index, Signature,
|
||||
AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION,
|
||||
};
|
||||
@@ -90,10 +90,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("statemine"),
|
||||
impl_name: create_runtime_str!("statemine"),
|
||||
authoring_version: 1,
|
||||
spec_version: 4,
|
||||
spec_version: 5,
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 2,
|
||||
transaction_version: 3,
|
||||
};
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
@@ -693,6 +693,14 @@ impl pallet_collator_selection::Config for Runtime {
|
||||
type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_asset_tx_payment::Config for Runtime {
|
||||
type Fungibles = Assets;
|
||||
type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
|
||||
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto>,
|
||||
AssetsToBlockAuthor<Runtime>,
|
||||
>;
|
||||
}
|
||||
|
||||
// Create the runtime by composing the FRAME pallets that were previously configured.
|
||||
construct_runtime!(
|
||||
pub enum Runtime where
|
||||
@@ -712,6 +720,7 @@ construct_runtime!(
|
||||
// Monetary stuff.
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
|
||||
TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 11,
|
||||
AssetTxPayment: pallet_asset_tx_payment::{Pallet} = 12,
|
||||
|
||||
// Collator support. the order of these 4 are important and shall not change.
|
||||
Authorship: pallet_authorship::{Pallet, Call, Storage} = 20,
|
||||
@@ -753,7 +762,7 @@ pub type SignedExtra = (
|
||||
frame_system::CheckEra<Runtime>,
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>,
|
||||
);
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
|
||||
|
||||
@@ -62,6 +62,7 @@ cumulus-pallet-xcmp-queue = { path = "../../pallets/xcmp-queue", default-feature
|
||||
cumulus-pallet-xcm = { path = "../../pallets/xcm", default-features = false }
|
||||
cumulus-pallet-session-benchmarking = { path = "../../pallets/session-benchmarking", default-features = false, version = "3.0.0" }
|
||||
cumulus-ping = { path = "../pallets/ping", default-features = false }
|
||||
pallet-asset-tx-payment = { path = "../../pallets/asset-tx-payment", default-features = false }
|
||||
pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false }
|
||||
parachains-common = { path = "../parachains-common", default-features = false }
|
||||
|
||||
@@ -136,6 +137,7 @@ std = [
|
||||
"pallet-utility/std",
|
||||
"parachain-info/std",
|
||||
"cumulus-pallet-aura-ext/std",
|
||||
"pallet-asset-tx-payment/std",
|
||||
"pallet-collator-selection/std",
|
||||
"cumulus-pallet-dmp-queue/std",
|
||||
"cumulus-pallet-parachain-system/std",
|
||||
|
||||
@@ -29,7 +29,7 @@ use sp_api::impl_runtime_apis;
|
||||
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
|
||||
use sp_runtime::{
|
||||
create_runtime_str, generic, impl_opaque_keys,
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto},
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult,
|
||||
};
|
||||
@@ -56,7 +56,7 @@ use frame_system::{
|
||||
};
|
||||
pub use parachains_common as common;
|
||||
use parachains_common::{
|
||||
impls::{DealWithFees, NonZeroIssuance},
|
||||
impls::{AssetsToBlockAuthor, DealWithFees, NonZeroIssuance},
|
||||
opaque, AccountId, AssetId, AuraId, Balance, BlockNumber, Hash, Header, Index, Signature,
|
||||
AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION,
|
||||
};
|
||||
@@ -90,10 +90,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("statemint"),
|
||||
impl_name: create_runtime_str!("statemint"),
|
||||
authoring_version: 1,
|
||||
spec_version: 1,
|
||||
spec_version: 2,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 1,
|
||||
transaction_version: 2,
|
||||
};
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
@@ -656,6 +656,14 @@ impl pallet_collator_selection::Config for Runtime {
|
||||
type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_asset_tx_payment::Config for Runtime {
|
||||
type Fungibles = Assets;
|
||||
type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
|
||||
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto>,
|
||||
AssetsToBlockAuthor<Runtime>,
|
||||
>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ClassDeposit: Balance = UNITS; // 1 UNIT deposit to create asset class
|
||||
pub const InstanceDeposit: Balance = UNITS / 100; // 1/100 UNIT deposit to create asset instance
|
||||
@@ -703,6 +711,7 @@ construct_runtime!(
|
||||
// Monetary stuff.
|
||||
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 10,
|
||||
TransactionPayment: pallet_transaction_payment::{Pallet, Storage} = 11,
|
||||
AssetTxPayment: pallet_asset_tx_payment::{Pallet} = 12,
|
||||
|
||||
// Collator support. the order of these 4 are important and shall not change.
|
||||
Authorship: pallet_authorship::{Pallet, Call, Storage} = 20,
|
||||
@@ -744,7 +753,7 @@ pub type SignedExtra = (
|
||||
frame_system::CheckEra<Runtime>,
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>,
|
||||
);
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
|
||||
|
||||
@@ -62,6 +62,7 @@ cumulus-pallet-xcmp-queue = { path = "../../pallets/xcmp-queue", default-feature
|
||||
cumulus-pallet-xcm = { path = "../../pallets/xcm", default-features = false }
|
||||
cumulus-pallet-session-benchmarking = {path = "../../pallets/session-benchmarking", default-features = false, version = "3.0.0"}
|
||||
cumulus-ping = { path = "../pallets/ping", default-features = false }
|
||||
pallet-asset-tx-payment = { path = "../../pallets/asset-tx-payment", default-features = false }
|
||||
pallet-collator-selection = { path = "../../pallets/collator-selection", default-features = false }
|
||||
parachains-common = { path = "../parachains-common", default-features = false }
|
||||
|
||||
@@ -136,6 +137,7 @@ std = [
|
||||
"pallet-utility/std",
|
||||
"parachain-info/std",
|
||||
"cumulus-pallet-aura-ext/std",
|
||||
"pallet-asset-tx-payment/std",
|
||||
"pallet-collator-selection/std",
|
||||
"cumulus-pallet-dmp-queue/std",
|
||||
"cumulus-pallet-parachain-system/std",
|
||||
|
||||
@@ -29,7 +29,7 @@ use sp_api::impl_runtime_apis;
|
||||
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
|
||||
use sp_runtime::{
|
||||
create_runtime_str, generic, impl_opaque_keys,
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT},
|
||||
traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto},
|
||||
transaction_validity::{TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult,
|
||||
};
|
||||
@@ -56,7 +56,7 @@ use frame_system::{
|
||||
};
|
||||
pub use parachains_common as common;
|
||||
use parachains_common::{
|
||||
impls::{DealWithFees, NonZeroIssuance},
|
||||
impls::{AssetsToBlockAuthor, DealWithFees, NonZeroIssuance},
|
||||
opaque, AccountId, AssetId, AuraId, Balance, BlockNumber, Hash, Header, Index, Signature,
|
||||
AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION,
|
||||
};
|
||||
@@ -90,10 +90,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("westmint"),
|
||||
impl_name: create_runtime_str!("westmint"),
|
||||
authoring_version: 1,
|
||||
spec_version: 4,
|
||||
spec_version: 5,
|
||||
impl_version: 0,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
transaction_version: 2,
|
||||
transaction_version: 3,
|
||||
};
|
||||
|
||||
/// The version information used to identify this runtime when compiled natively.
|
||||
@@ -648,6 +648,14 @@ impl pallet_collator_selection::Config for Runtime {
|
||||
type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_asset_tx_payment::Config for Runtime {
|
||||
type Fungibles = Assets;
|
||||
type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
|
||||
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto>,
|
||||
AssetsToBlockAuthor<Runtime>,
|
||||
>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ClassDeposit: Balance = UNITS; // 1 UNIT deposit to create asset class
|
||||
pub const InstanceDeposit: Balance = UNITS / 100; // 1/100 UNIT deposit to create asset instance
|
||||
@@ -718,6 +726,9 @@ construct_runtime!(
|
||||
|
||||
// More things for the main stage
|
||||
Uniques: pallet_uniques::{Pallet, Call, Storage, Event<T>},
|
||||
|
||||
// More Monetary stuff
|
||||
AssetTxPayment: pallet_asset_tx_payment::{Pallet},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -737,7 +748,7 @@ pub type SignedExtra = (
|
||||
frame_system::CheckEra<Runtime>,
|
||||
frame_system::CheckNonce<Runtime>,
|
||||
frame_system::CheckWeight<Runtime>,
|
||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||
pallet_asset_tx_payment::ChargeAssetTxPayment<Runtime>,
|
||||
);
|
||||
/// Unchecked extrinsic type as expected by this runtime.
|
||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
|
||||
|
||||
Reference in New Issue
Block a user