Skip to content

Commit

Permalink
Tx Payment: drop ED requirements for tx payments with exchangeable as…
Browse files Browse the repository at this point in the history
…set (paritytech#4488)

Drop the Existential Deposit (ED) requirement for the asset amount
exchangeable for the fee asset (eg. DOT/KSM) during transaction
payments.

This achieved by using `SwapCredit` implementation of swap, which works
with imbalances and does not require a temporary balance account within
the transaction payment.

### Problem
Currently, every swap during transaction payment, processed with asset
`A` for native asset, must be for an amount greater than the ED of a
native asset if the user lacks a native asset account. Since fees are
typically smaller, the current implementation necessitates additional
swaps to meet the ED during `pre_dispatch`, with refunds for excess ED
swap occurring during `post_dispatch`. Further details can be found
[here](https://github.com/paritytech/polkadot-sdk/blob/115c2477eb287df55107cd95594100ba395ed239/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs#L115).

This setup presents an issue where a user is unable to transfer their
entire balance and close the account. Instead, the user must transfer
slightly less of asset `A` to ensure there is enough not only for the
fee payment but also some extra to meet the ED requirement for their
native account during `pre_dispatch`. In some cases during
`post_dispatch`, the user will have the excess ED swapped back to asset
`A`, while in other cases, it may not be sufficient to meet the ED
requirement for asset `A`, leading it being left in the user's
'temporary' native asset account.

---------

Co-authored-by: Oliver Tale-Yazdi <[email protected]>
  • Loading branch information
2 people authored and TarekkMA committed Aug 2, 2024
1 parent 77c12c0 commit 0db6ed2
Show file tree
Hide file tree
Showing 10 changed files with 541 additions and 273 deletions.
16 changes: 12 additions & 4 deletions cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureRoot, EnsureSigned, EnsureSignedBy,
};
use pallet_asset_conversion_tx_payment::AssetConversionAdapter;
use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
use pallet_nfts::PalletFeatures;
use parachains_common::{
impls::DealWithFees,
Expand Down Expand Up @@ -798,11 +798,19 @@ impl pallet_collator_selection::Config for Runtime {
type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
}

parameter_types! {
pub StakingPot: AccountId = CollatorSelection::account_id();
}

impl pallet_asset_conversion_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Fungibles = LocalAndForeignAssets;
type OnChargeAssetTransaction =
AssetConversionAdapter<Balances, AssetConversion, TokenLocationV3>;
type AssetId = xcm::v3::Location;
type OnChargeAssetTransaction = SwapAssetAdapter<
TokenLocationV3,
NativeAndAssets,
AssetConversion,
ResolveAssetTo<StakingPot, NativeAndAssets>,
>;
}

parameter_types! {
Expand Down
16 changes: 12 additions & 4 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use frame_system::{
limits::{BlockLength, BlockWeights},
EnsureRoot, EnsureSigned, EnsureSignedBy,
};
use pallet_asset_conversion_tx_payment::AssetConversionAdapter;
use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
use pallet_nfts::{DestroyWitness, PalletFeatures};
use pallet_xcm::EnsureXcm;
use parachains_common::{
Expand Down Expand Up @@ -787,11 +787,19 @@ impl pallet_collator_selection::Config for Runtime {
type WeightInfo = weights::pallet_collator_selection::WeightInfo<Runtime>;
}

parameter_types! {
pub StakingPot: AccountId = CollatorSelection::account_id();
}

impl pallet_asset_conversion_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Fungibles = LocalAndForeignAssets;
type OnChargeAssetTransaction =
AssetConversionAdapter<Balances, AssetConversion, WestendLocationV3>;
type AssetId = xcm::v3::Location;
type OnChargeAssetTransaction = SwapAssetAdapter<
WestendLocationV3,
NativeAndAssets,
AssetConversion,
ResolveAssetTo<StakingPot, NativeAndAssets>,
>;
}

parameter_types! {
Expand Down
25 changes: 25 additions & 0 deletions prdoc/pr_4488.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
title: "Tx Payment: drop ED requirements for tx payments with exchangeable asset"

doc:
- audience: Runtime Dev
description: |
Drop the Existential Deposit requirement for the asset amount exchangeable for the fee asset
(eg. DOT/KSM) during transaction payments.

This achieved by using `SwapCredit` implementation of asset conversion, which works with
imbalances and does not require a temporary balance account within the transaction payment.

This is a breaking change for the `pallet-asset-conversion-tx-payment` pallet, use examples
from PR for the migration.

crates:
- name: pallet-asset-conversion-tx-payment
bump: major
- name: pallet-transaction-payment
bump: patch
- name: pallet-asset-conversion
bump: patch
- name: asset-hub-rococo-runtime
bump: patch
- name: asset-hub-westend-runtime
bump: patch
29 changes: 11 additions & 18 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ use frame_system::{
pub use node_primitives::{AccountId, Signature};
use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Moment, Nonce};
use pallet_asset_conversion::{AccountIdConverter, Ascending, Chain, WithFirstAsset};
use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
use pallet_broker::{CoreAssignment, CoreIndex, CoretimeInterface, PartsOf57600};
use pallet_election_provider_multi_phase::{GeometricDepositBase, SolutionAccuracyOf};
use pallet_identity::legacy::IdentityInfo;
Expand Down Expand Up @@ -123,7 +124,7 @@ pub use sp_runtime::BuildStorage;
pub mod impls;
#[cfg(not(feature = "runtime-benchmarks"))]
use impls::AllianceIdentityVerifier;
use impls::{AllianceProposalProvider, Author, CreditToBlockAuthor};
use impls::{AllianceProposalProvider, Author};

/// Constant values used within the runtime.
pub mod constants;
Expand Down Expand Up @@ -574,22 +575,14 @@ impl pallet_transaction_payment::Config for Runtime {
>;
}

impl pallet_asset_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Fungibles = Assets;
type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
pallet_assets::BalanceToAssetBalance<Balances, Runtime, ConvertInto, Instance1>,
CreditToBlockAuthor,
>;
}

impl pallet_asset_conversion_tx_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Fungibles = Assets;
type OnChargeAssetTransaction = pallet_asset_conversion_tx_payment::AssetConversionAdapter<
Balances,
AssetConversion,
type AssetId = NativeOrWithId<u32>;
type OnChargeAssetTransaction = SwapAssetAdapter<
Native,
NativeAndAssets,
AssetConversion,
ResolveAssetTo<TreasuryAccount, NativeAndAssets>,
>;
}

Expand Down Expand Up @@ -1705,12 +1698,15 @@ parameter_types! {
pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native;
}

pub type NativeAndAssets =
UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;

impl pallet_asset_conversion::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Balance = u128;
type HigherPrecisionBalance = sp_core::U256;
type AssetKind = NativeOrWithId<u32>;
type Assets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
type Assets = NativeAndAssets;
type PoolId = (Self::AssetKind, Self::AssetKind);
type PoolLocator = Chain<
WithFirstAsset<
Expand Down Expand Up @@ -2259,9 +2255,6 @@ mod runtime {
#[runtime::pallet_index(7)]
pub type TransactionPayment = pallet_transaction_payment::Pallet<Runtime>;

#[runtime::pallet_index(8)]
pub type AssetTxPayment = pallet_asset_tx_payment::Pallet<Runtime>;

#[runtime::pallet_index(9)]
pub type AssetConversionTxPayment = pallet_asset_conversion_tx_payment::Pallet<Runtime>;

Expand Down
52 changes: 52 additions & 0 deletions substrate/frame/asset-conversion/src/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,37 @@ pub trait SwapCredit<AccountId> {
) -> Result<(Self::Credit, Self::Credit), (Self::Credit, DispatchError)>;
}

/// Trait providing methods to quote swap prices between asset classes.
///
/// The quoted price is only guaranteed if no other swaps are made after the price is quoted and
/// before the target swap (e.g., the swap is made immediately within the same transaction).
pub trait QuotePrice {
/// Measurement units of the asset classes for pricing.
type Balance: Balance;
/// Type representing the kind of assets for which the price is being quoted.
type AssetKind;
/// Quotes the amount of `asset1` required to obtain the exact `amount` of `asset2`.
///
/// If `include_fee` is set to `true`, the price will include the pool's fee.
/// If the pool does not exist or the swap cannot be made, `None` is returned.
fn quote_price_tokens_for_exact_tokens(
asset1: Self::AssetKind,
asset2: Self::AssetKind,
amount: Self::Balance,
include_fee: bool,
) -> Option<Self::Balance>;
/// Quotes the amount of `asset2` resulting from swapping the exact `amount` of `asset1`.
///
/// If `include_fee` is set to `true`, the price will include the pool's fee.
/// If the pool does not exist or the swap cannot be made, `None` is returned.
fn quote_price_exact_tokens_for_tokens(
asset1: Self::AssetKind,
asset2: Self::AssetKind,
amount: Self::Balance,
include_fee: bool,
) -> Option<Self::Balance>;
}

impl<T: Config> Swap<T::AccountId> for Pallet<T> {
type Balance = T::Balance;
type AssetKind = T::AssetKind;
Expand Down Expand Up @@ -210,3 +241,24 @@ impl<T: Config> SwapCredit<T::AccountId> for Pallet<T> {
.map_err(|_| (Self::Credit::zero(credit_asset), DispatchError::Corruption))?
}
}

impl<T: Config> QuotePrice for Pallet<T> {
type Balance = T::Balance;
type AssetKind = T::AssetKind;
fn quote_price_exact_tokens_for_tokens(
asset1: Self::AssetKind,
asset2: Self::AssetKind,
amount: Self::Balance,
include_fee: bool,
) -> Option<Self::Balance> {
Self::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee)
}
fn quote_price_tokens_for_exact_tokens(
asset1: Self::AssetKind,
asset2: Self::AssetKind,
amount: Self::Balance,
include_fee: bool,
) -> Option<Self::Balance> {
Self::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee)
}
}
Loading

0 comments on commit 0db6ed2

Please sign in to comment.