mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 22:37:57 +00:00
4f832ea865
closes https://github.com/paritytech/polkadot-sdk/issues/1842 Decoupling Pallet from the Concept of Native Currency Currently, the pallet is intrinsically linked with the concept of native currency, requiring users to provide implementations of the `fungible::*` and `fungibles::*` traits to interact with native and non native assets. This incapsulates some non-related to the pallet complexity and makes it less adaptable in contexts where the native currency concept is absent. With this PR, the dependence on `fungible::*` for liquidity-supplying assets has been removed. Instead, the native and non-native currencies' handling is now overseen by a single type that implements the `fungibles::*` traits. To simplify this integration, types have been introduced to facilitate the creation of a union between `fungible::*` and `fungibles::*` implementations, producing a unified `fungibles::*` type. One of the reasons driving these changes is the ambition to create a more user-friendly API for the `SwapCredit` implementation. Given that it interacts with two distinct credit types from `fungible` and `fungibles`, a unified type was introduced. Clients now manage potential conversion failures for those credit types. In certain contexts, it's vital to guarantee that operations are fail-safe, like in this impl - [PR](https://github.com/paritytech/polkadot-sdk/pull/1845), place in [code](https://github.com/paritytech/polkadot-sdk/blob/20b85a5fada8f55c98ba831964f5866ffeadf4da/cumulus/primitives/utility/src/lib.rs#L429). Additional Updates: - abstracted the pool ID and its account derivation logic via trait bounds, along with common implementation offerings; - removed `inc_providers` on a pool creation for the pool account; - benchmarks: -- swap complexity is N, not const; -- removed `From<u128> + Into<u128>` bound from `T::Balance`; -- removed swap/liquidity/.. amount constants, resolve them dynamically based on pallet configuration; -- migrated to v2 API; - `OnUnbalanced` handler for the pool creation fee, replacing direct transfers to a specified account ID; - renamed `MultiAssetId` to `AssetKind` aligning with naming across frame crates; related PRs: - (depends) https://github.com/paritytech/polkadot-sdk/pull/1677 - (caused) https://github.com/paritytech/polkadot-sdk/pull/2033 - (caused) https://github.com/paritytech/polkadot-sdk/pull/1876 --------- Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
199 lines
6.8 KiB
Rust
199 lines
6.8 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
///! Traits and default implementation for paying transaction fees in assets.
|
|
use super::*;
|
|
use crate::Config;
|
|
|
|
use frame_support::{
|
|
ensure,
|
|
traits::{fungible::Inspect, tokens::Balance},
|
|
unsigned::TransactionValidityError,
|
|
};
|
|
use pallet_asset_conversion::Swap;
|
|
use sp_runtime::{
|
|
traits::{DispatchInfoOf, Get, PostDispatchInfoOf, Zero},
|
|
transaction_validity::InvalidTransaction,
|
|
Saturating,
|
|
};
|
|
use sp_std::marker::PhantomData;
|
|
|
|
/// Handle withdrawing, refunding and depositing of transaction fees.
|
|
pub trait OnChargeAssetTransaction<T: Config> {
|
|
/// The underlying integer type in which fees are calculated.
|
|
type Balance: Balance;
|
|
/// The type used to identify the assets used for transaction payment.
|
|
type AssetId: AssetId;
|
|
/// The type used to store the intermediate values between pre- and post-dispatch.
|
|
type LiquidityInfo;
|
|
|
|
/// Secure the payment of the transaction fees before the transaction is executed.
|
|
///
|
|
/// Note: The `fee` already includes the `tip`.
|
|
fn withdraw_fee(
|
|
who: &T::AccountId,
|
|
call: &T::RuntimeCall,
|
|
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
|
|
asset_id: Self::AssetId,
|
|
fee: Self::Balance,
|
|
tip: Self::Balance,
|
|
) -> Result<
|
|
(LiquidityInfoOf<T>, Self::LiquidityInfo, AssetBalanceOf<T>),
|
|
TransactionValidityError,
|
|
>;
|
|
|
|
/// Refund any overpaid fees and deposit the corrected amount.
|
|
/// The actual fee gets calculated once the transaction is executed.
|
|
///
|
|
/// Note: The `fee` already includes the `tip`.
|
|
///
|
|
/// Returns the fee and tip in the asset used for payment as (fee, tip).
|
|
fn correct_and_deposit_fee(
|
|
who: &T::AccountId,
|
|
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
|
|
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
|
|
corrected_fee: Self::Balance,
|
|
tip: Self::Balance,
|
|
fee_paid: LiquidityInfoOf<T>,
|
|
received_exchanged: Self::LiquidityInfo,
|
|
asset_id: Self::AssetId,
|
|
initial_asset_consumed: AssetBalanceOf<T>,
|
|
) -> Result<AssetBalanceOf<T>, TransactionValidityError>;
|
|
}
|
|
|
|
/// Implements the asset transaction for a balance to asset converter (implementing [`Swap`]).
|
|
///
|
|
/// The converter is given the complete fee in terms of the asset used for the transaction.
|
|
pub struct AssetConversionAdapter<C, CON, N>(PhantomData<(C, CON, N)>);
|
|
|
|
/// Default implementation for a runtime instantiating this pallet, an asset to native swapper.
|
|
impl<T, C, CON, N> OnChargeAssetTransaction<T> for AssetConversionAdapter<C, CON, N>
|
|
where
|
|
N: Get<CON::AssetKind>,
|
|
T: Config,
|
|
C: Inspect<<T as frame_system::Config>::AccountId>,
|
|
CON: Swap<T::AccountId, Balance = BalanceOf<T>, AssetKind = T::AssetKind>,
|
|
BalanceOf<T>: Into<AssetBalanceOf<T>>,
|
|
T::AssetKind: From<AssetIdOf<T>>,
|
|
BalanceOf<T>: IsType<<C as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
|
|
{
|
|
type Balance = BalanceOf<T>;
|
|
type AssetId = AssetIdOf<T>;
|
|
type LiquidityInfo = BalanceOf<T>;
|
|
|
|
/// Swap & withdraw the predicted fee from the transaction origin.
|
|
///
|
|
/// Note: The `fee` already includes the `tip`.
|
|
///
|
|
/// Returns the total amount in native currency received by exchanging the `asset_id` and the
|
|
/// amount in native currency used to pay the fee.
|
|
fn withdraw_fee(
|
|
who: &T::AccountId,
|
|
call: &T::RuntimeCall,
|
|
info: &DispatchInfoOf<T::RuntimeCall>,
|
|
asset_id: Self::AssetId,
|
|
fee: BalanceOf<T>,
|
|
tip: BalanceOf<T>,
|
|
) -> Result<
|
|
(LiquidityInfoOf<T>, Self::LiquidityInfo, AssetBalanceOf<T>),
|
|
TransactionValidityError,
|
|
> {
|
|
// convert the asset into native currency
|
|
let ed = C::minimum_balance();
|
|
let native_asset_required =
|
|
if C::balance(&who) >= ed.saturating_add(fee.into()) { fee } else { fee + ed.into() };
|
|
|
|
let asset_consumed = CON::swap_tokens_for_exact_tokens(
|
|
who.clone(),
|
|
vec![asset_id.into(), N::get()],
|
|
native_asset_required,
|
|
None,
|
|
who.clone(),
|
|
true,
|
|
)
|
|
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
|
|
|
|
ensure!(asset_consumed > Zero::zero(), InvalidTransaction::Payment);
|
|
|
|
// charge the fee in native currency
|
|
<T::OnChargeTransaction>::withdraw_fee(who, call, info, fee, tip)
|
|
.map(|r| (r, native_asset_required, asset_consumed.into()))
|
|
}
|
|
|
|
/// Correct the fee and swap the refund back to asset.
|
|
///
|
|
/// Note: The `corrected_fee` already includes the `tip`.
|
|
/// Note: Is the ED wasn't needed, the `received_exchanged` will be equal to `fee_paid`, or
|
|
/// `fee_paid + ed` otherwise.
|
|
fn correct_and_deposit_fee(
|
|
who: &T::AccountId,
|
|
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
|
|
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
|
|
corrected_fee: BalanceOf<T>,
|
|
tip: BalanceOf<T>,
|
|
fee_paid: LiquidityInfoOf<T>,
|
|
received_exchanged: Self::LiquidityInfo,
|
|
asset_id: Self::AssetId,
|
|
initial_asset_consumed: AssetBalanceOf<T>,
|
|
) -> Result<AssetBalanceOf<T>, TransactionValidityError> {
|
|
// Refund the native asset to the account that paid the fees (`who`).
|
|
// The `who` account will receive the "fee_paid - corrected_fee" refund.
|
|
<T::OnChargeTransaction>::correct_and_deposit_fee(
|
|
who,
|
|
dispatch_info,
|
|
post_info,
|
|
corrected_fee,
|
|
tip,
|
|
fee_paid,
|
|
)?;
|
|
|
|
// calculate the refund in native asset, to swap back to the desired `asset_id`
|
|
let swap_back = received_exchanged.saturating_sub(corrected_fee);
|
|
let mut asset_refund = Zero::zero();
|
|
if !swap_back.is_zero() {
|
|
// If this fails, the account might have dropped below the existential balance or there
|
|
// is not enough liquidity left in the pool. In that case we don't throw an error and
|
|
// the account will keep the native currency.
|
|
match CON::swap_exact_tokens_for_tokens(
|
|
who.clone(), // we already deposited the native to `who`
|
|
vec![
|
|
N::get(), // we provide the native
|
|
asset_id.into(), // we want asset_id back
|
|
],
|
|
swap_back, // amount of the native asset to convert to `asset_id`
|
|
None, // no minimum amount back
|
|
who.clone(), // we will refund to `who`
|
|
false, // no need to keep alive
|
|
)
|
|
.ok()
|
|
{
|
|
Some(acquired) => {
|
|
asset_refund = acquired
|
|
.try_into()
|
|
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
|
|
},
|
|
None => {
|
|
Pallet::<T>::deposit_event(Event::<T>::AssetRefundFailed {
|
|
native_amount_kept: swap_back,
|
|
});
|
|
},
|
|
}
|
|
}
|
|
|
|
let actual_paid = initial_asset_consumed.saturating_sub(asset_refund);
|
|
Ok(actual_paid)
|
|
}
|
|
}
|