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:
Alexander Popiak
2021-10-11 11:57:37 +02:00
committed by GitHub
parent 2647053fd3
commit 3b1446d791
14 changed files with 1268 additions and 24 deletions
@@ -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>;