mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 01:11:10 +00:00
Refactor the asset-conversion-tx-payment pallet (#14558)
* Code refactoring * Fix imports * Typo * Update frame/asset-conversion/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Sync docs --------- Co-authored-by: parity-processbot <> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
This commit is contained in:
@@ -81,11 +81,10 @@ use sp_arithmetic::traits::Unsigned;
|
||||
use sp_runtime::{
|
||||
traits::{
|
||||
CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Ensure, MaybeDisplay, TrailingZeroInput,
|
||||
Zero,
|
||||
},
|
||||
DispatchError,
|
||||
};
|
||||
use sp_std::vec;
|
||||
use sp_std::prelude::*;
|
||||
pub use types::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
@@ -111,7 +110,6 @@ pub mod pallet {
|
||||
traits::{IntegerSquareRoot, One, Zero},
|
||||
Saturating,
|
||||
};
|
||||
use sp_std::prelude::*;
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
@@ -148,7 +146,7 @@ pub mod pallet {
|
||||
type AssetId: AssetId + PartialOrd;
|
||||
|
||||
/// Type that identifies either the native currency or a token class from `Assets`.
|
||||
type MultiAssetId: AssetId + Ord;
|
||||
type MultiAssetId: AssetId + Ord + From<Self::AssetId>;
|
||||
|
||||
/// Type to convert an `AssetId` into `MultiAssetId`.
|
||||
type MultiAssetIdConverter: MultiAssetIdConverter<Self::MultiAssetId, Self::AssetId>;
|
||||
@@ -644,18 +642,14 @@ pub mod pallet {
|
||||
keep_alive: bool,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
ensure!(
|
||||
amount_in > Zero::zero() && amount_out_min > Zero::zero(),
|
||||
Error::<T>::ZeroAmount
|
||||
);
|
||||
Self::validate_swap_path(&path)?;
|
||||
|
||||
let amounts = Self::get_amounts_out(&amount_in, &path)?;
|
||||
let amount_out = *amounts.last().expect("Has always more than 1 element");
|
||||
ensure!(amount_out >= amount_out_min, Error::<T>::ProvidedMinimumNotSufficientForSwap);
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Self::do_swap_exact_tokens_for_tokens(
|
||||
sender,
|
||||
path,
|
||||
amount_in,
|
||||
Some(amount_out_min),
|
||||
send_to,
|
||||
keep_alive,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -676,23 +670,95 @@ pub mod pallet {
|
||||
keep_alive: bool,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin)?;
|
||||
|
||||
ensure!(
|
||||
amount_out > Zero::zero() && amount_in_max > Zero::zero(),
|
||||
Error::<T>::ZeroAmount
|
||||
);
|
||||
Self::validate_swap_path(&path)?;
|
||||
|
||||
let amounts = Self::get_amounts_in(&amount_out, &path)?;
|
||||
let amount_in = *amounts.first().expect("Always has more than one element");
|
||||
ensure!(amount_in <= amount_in_max, Error::<T>::ProvidedMaximumNotSufficientForSwap);
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Self::do_swap_tokens_for_exact_tokens(
|
||||
sender,
|
||||
path,
|
||||
amount_out,
|
||||
Some(amount_in_max),
|
||||
send_to,
|
||||
keep_alive,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`.
|
||||
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
|
||||
/// the amount desired.
|
||||
///
|
||||
/// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`,
|
||||
/// respecting `keep_alive`.
|
||||
///
|
||||
/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
|
||||
pub fn do_swap_exact_tokens_for_tokens(
|
||||
sender: T::AccountId,
|
||||
path: BoundedVec<T::MultiAssetId, T::MaxSwapPathLength>,
|
||||
amount_in: T::AssetBalance,
|
||||
amount_out_min: Option<T::AssetBalance>,
|
||||
send_to: T::AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<T::AssetBalance, DispatchError> {
|
||||
ensure!(amount_in > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
if let Some(amount_out_min) = amount_out_min {
|
||||
ensure!(amount_out_min > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
}
|
||||
|
||||
Self::validate_swap_path(&path)?;
|
||||
|
||||
let amounts = Self::get_amounts_out(&amount_in, &path)?;
|
||||
let amount_out =
|
||||
*amounts.last().defensive_ok_or("get_amounts_out() returned an empty result")?;
|
||||
|
||||
if let Some(amount_out_min) = amount_out_min {
|
||||
ensure!(
|
||||
amount_out >= amount_out_min,
|
||||
Error::<T>::ProvidedMinimumNotSufficientForSwap
|
||||
);
|
||||
}
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Ok(amount_out)
|
||||
}
|
||||
|
||||
/// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// Withdraws `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`,
|
||||
/// respecting `keep_alive`.
|
||||
///
|
||||
/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
|
||||
pub fn do_swap_tokens_for_exact_tokens(
|
||||
sender: T::AccountId,
|
||||
path: BoundedVec<T::MultiAssetId, T::MaxSwapPathLength>,
|
||||
amount_out: T::AssetBalance,
|
||||
amount_in_max: Option<T::AssetBalance>,
|
||||
send_to: T::AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<T::AssetBalance, DispatchError> {
|
||||
ensure!(amount_out > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
if let Some(amount_in_max) = amount_in_max {
|
||||
ensure!(amount_in_max > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
}
|
||||
|
||||
Self::validate_swap_path(&path)?;
|
||||
|
||||
let amounts = Self::get_amounts_in(&amount_out, &path)?;
|
||||
let amount_in =
|
||||
*amounts.first().defensive_ok_or("get_amounts_in() returned an empty result")?;
|
||||
|
||||
if let Some(amount_in_max) = amount_in_max {
|
||||
ensure!(
|
||||
amount_in <= amount_in_max,
|
||||
Error::<T>::ProvidedMaximumNotSufficientForSwap
|
||||
);
|
||||
}
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Ok(amount_in)
|
||||
}
|
||||
|
||||
/// Transfer an `amount` of `asset_id`, respecting the `keep_alive` requirements.
|
||||
fn transfer(
|
||||
asset_id: &T::MultiAssetId,
|
||||
@@ -749,6 +815,13 @@ pub mod pallet {
|
||||
.map_err(|_| Error::<T>::Overflow)
|
||||
}
|
||||
|
||||
/// Convert a `HigherPrecisionBalance` type to an `AssetBalance`.
|
||||
pub(crate) fn convert_hpb_to_asset_balance(
|
||||
amount: T::HigherPrecisionBalance,
|
||||
) -> Result<T::AssetBalance, Error<T>> {
|
||||
amount.try_into().map_err(|_| Error::<T>::Overflow)
|
||||
}
|
||||
|
||||
/// Swap assets along a `path`, depositing in `send_to`.
|
||||
pub(crate) fn do_swap(
|
||||
sender: T::AccountId,
|
||||
@@ -1123,92 +1196,47 @@ pub mod pallet {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config>
|
||||
frame_support::traits::tokens::fungibles::SwapNative<
|
||||
T::RuntimeOrigin,
|
||||
T::AccountId,
|
||||
T::Balance,
|
||||
T::AssetBalance,
|
||||
T::AssetId,
|
||||
> for Pallet<T>
|
||||
where
|
||||
<T as pallet::Config>::Currency:
|
||||
frame_support::traits::tokens::fungible::Inspect<<T as frame_system::Config>::AccountId>,
|
||||
{
|
||||
/// Take an `asset_id` and swap some amount for `amount_out` of the chain's native asset. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// If successful returns the amount of the `asset_id` taken to provide `amount_out`.
|
||||
fn swap_tokens_for_exact_native(
|
||||
impl<T: Config> Swap<T::AccountId, T::HigherPrecisionBalance, T::MultiAssetId> for Pallet<T> {
|
||||
fn swap_exact_tokens_for_tokens(
|
||||
sender: T::AccountId,
|
||||
asset_id: T::AssetId,
|
||||
amount_out: T::Balance,
|
||||
amount_in_max: Option<T::AssetBalance>,
|
||||
path: Vec<T::MultiAssetId>,
|
||||
amount_in: T::HigherPrecisionBalance,
|
||||
amount_out_min: Option<T::HigherPrecisionBalance>,
|
||||
send_to: T::AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<T::AssetBalance, DispatchError> {
|
||||
ensure!(amount_out > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
if let Some(amount_in_max) = amount_in_max {
|
||||
ensure!(amount_in_max > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
}
|
||||
let mut path = sp_std::vec::Vec::new();
|
||||
path.push(T::MultiAssetIdConverter::into_multiasset_id(&asset_id));
|
||||
path.push(T::MultiAssetIdConverter::get_native());
|
||||
let path = path.try_into().unwrap();
|
||||
|
||||
// convert `amount_out` from native balance type, to asset balance type
|
||||
let amount_out = Self::convert_native_balance_to_asset_balance(amount_out)?;
|
||||
|
||||
// calculate the amount we need to provide
|
||||
let amounts = Self::get_amounts_in(&amount_out, &path)?;
|
||||
let amount_in =
|
||||
*amounts.first().defensive_ok_or("get_amounts_in() returned an empty result")?;
|
||||
if let Some(amount_in_max) = amount_in_max {
|
||||
ensure!(amount_in <= amount_in_max, Error::<T>::ProvidedMaximumNotSufficientForSwap);
|
||||
}
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Ok(amount_in)
|
||||
) -> Result<T::HigherPrecisionBalance, DispatchError> {
|
||||
let path = path.try_into().map_err(|_| Error::<T>::PathError)?;
|
||||
let amount_out_min = amount_out_min.map(Self::convert_hpb_to_asset_balance).transpose()?;
|
||||
let amount_out = Self::do_swap_exact_tokens_for_tokens(
|
||||
sender,
|
||||
path,
|
||||
Self::convert_hpb_to_asset_balance(amount_in)?,
|
||||
amount_out_min,
|
||||
send_to,
|
||||
keep_alive,
|
||||
)?;
|
||||
Ok(amount_out.into())
|
||||
}
|
||||
|
||||
/// Take an `asset_id` and swap `amount_in` of the chain's native asset for it. If an
|
||||
/// `amount_out_min` is specified, it will return an error if it is unable to acquire the amount
|
||||
/// desired.
|
||||
///
|
||||
/// If successful, returns the amount of `asset_id` acquired for the `amount_in`.
|
||||
fn swap_exact_native_for_tokens(
|
||||
fn swap_tokens_for_exact_tokens(
|
||||
sender: T::AccountId,
|
||||
asset_id: T::AssetId,
|
||||
amount_in: T::Balance,
|
||||
amount_out_min: Option<T::AssetBalance>,
|
||||
path: Vec<T::MultiAssetId>,
|
||||
amount_out: T::HigherPrecisionBalance,
|
||||
amount_in_max: Option<T::HigherPrecisionBalance>,
|
||||
send_to: T::AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<T::AssetBalance, DispatchError> {
|
||||
ensure!(amount_in > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
if let Some(amount_out_min) = amount_out_min {
|
||||
ensure!(amount_out_min > Zero::zero(), Error::<T>::ZeroAmount);
|
||||
}
|
||||
let mut path = sp_std::vec::Vec::new();
|
||||
path.push(T::MultiAssetIdConverter::get_native());
|
||||
path.push(T::MultiAssetIdConverter::into_multiasset_id(&asset_id));
|
||||
let path = path.try_into().expect(
|
||||
"`MaxSwapPathLength` is ensured by to be greater than 2; pushed only twice; qed",
|
||||
);
|
||||
|
||||
// convert `amount_in` from native balance type, to asset balance type
|
||||
let amount_in = Self::convert_native_balance_to_asset_balance(amount_in)?;
|
||||
|
||||
// calculate the amount we should receive
|
||||
let amounts = Self::get_amounts_out(&amount_in, &path)?;
|
||||
let amount_out =
|
||||
*amounts.last().defensive_ok_or("get_amounts_out() returned an empty result")?;
|
||||
if let Some(amount_out_min) = amount_out_min {
|
||||
ensure!(amount_out >= amount_out_min, Error::<T>::ProvidedMaximumNotSufficientForSwap);
|
||||
}
|
||||
|
||||
Self::do_swap(sender, &amounts, path, send_to, keep_alive)?;
|
||||
Ok(amount_out)
|
||||
) -> Result<T::HigherPrecisionBalance, DispatchError> {
|
||||
let path = path.try_into().map_err(|_| Error::<T>::PathError)?;
|
||||
let amount_in_max = amount_in_max.map(Self::convert_hpb_to_asset_balance).transpose()?;
|
||||
let amount_in = Self::do_swap_tokens_for_exact_tokens(
|
||||
sender,
|
||||
path,
|
||||
Self::convert_hpb_to_asset_balance(amount_out)?,
|
||||
amount_in_max,
|
||||
send_to,
|
||||
keep_alive,
|
||||
)?;
|
||||
Ok(amount_in.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ pub struct PoolInfo<PoolAssetId> {
|
||||
|
||||
/// A trait that converts between a MultiAssetId and either the native currency or an AssetId.
|
||||
pub trait MultiAssetIdConverter<MultiAssetId, AssetId> {
|
||||
/// Returns the MultiAssetId reperesenting the native currency of the chain.
|
||||
/// Returns the MultiAssetId representing the native currency of the chain.
|
||||
fn get_native() -> MultiAssetId;
|
||||
|
||||
/// Returns true if the given MultiAssetId is the native currency.
|
||||
@@ -42,7 +42,7 @@ pub trait MultiAssetIdConverter<MultiAssetId, AssetId> {
|
||||
/// If it's not native, returns the AssetId for the given MultiAssetId.
|
||||
fn try_convert(asset: &MultiAssetId) -> Result<AssetId, ()>;
|
||||
|
||||
/// Wrapps an AssetId as a MultiAssetId.
|
||||
/// Wraps an AssetId as a MultiAssetId.
|
||||
fn into_multiasset_id(asset: &AssetId) -> MultiAssetId;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,12 @@ where
|
||||
Asset(AssetId),
|
||||
}
|
||||
|
||||
impl<AssetId: Ord> From<AssetId> for NativeOrAssetId<AssetId> {
|
||||
fn from(asset: AssetId) -> Self {
|
||||
Self::Asset(asset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<AssetId: Ord> Ord for NativeOrAssetId<AssetId> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self, other) {
|
||||
@@ -126,3 +132,40 @@ impl<AssetId: Ord + Clone> MultiAssetIdConverter<NativeOrAssetId<AssetId>, Asset
|
||||
NativeOrAssetId::Asset((*asset).clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for providing methods to swap between the various asset classes.
|
||||
pub trait Swap<AccountId, Balance, MultiAssetId> {
|
||||
/// Swap exactly `amount_in` of asset `path[0]` for asset `path[1]`.
|
||||
/// If an `amount_out_min` is specified, it will return an error if it is unable to acquire
|
||||
/// the amount desired.
|
||||
///
|
||||
/// Withdraws the `path[0]` asset from `sender`, deposits the `path[1]` asset to `send_to`,
|
||||
/// respecting `keep_alive`.
|
||||
///
|
||||
/// If successful, returns the amount of `path[1]` acquired for the `amount_in`.
|
||||
fn swap_exact_tokens_for_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiAssetId>,
|
||||
amount_in: Balance,
|
||||
amount_out_min: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
|
||||
/// Take the `path[0]` asset and swap some amount for `amount_out` of the `path[1]`. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// Withdraws `path[0]` asset from `sender`, deposits `path[1]` asset to `send_to`,
|
||||
/// respecting `keep_alive`.
|
||||
///
|
||||
/// If successful returns the amount of the `path[0]` taken to provide `path[1]`.
|
||||
fn swap_tokens_for_exact_tokens(
|
||||
sender: AccountId,
|
||||
path: Vec<MultiAssetId>,
|
||||
amount_out: Balance,
|
||||
amount_in_max: Option<Balance>,
|
||||
send_to: AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<Balance, DispatchError>;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,5 @@ pub use hold::{
|
||||
pub use imbalance::{Credit, Debt, HandleImbalanceDrop, Imbalance};
|
||||
pub use lifetime::{Create, Destroy};
|
||||
pub use regular::{
|
||||
Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, SwapNative, Unbalanced,
|
||||
Balanced, DecreaseIssuance, Dust, IncreaseIssuance, Inspect, Mutate, Unbalanced,
|
||||
};
|
||||
|
||||
@@ -583,40 +583,3 @@ pub trait Balanced<AccountId>: Inspect<AccountId> + Unbalanced<AccountId> {
|
||||
fn done_deposit(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {}
|
||||
fn done_withdraw(_asset: Self::AssetId, _who: &AccountId, _amount: Self::Balance) {}
|
||||
}
|
||||
|
||||
/// Trait for providing methods to swap between the chain's native token and other asset classes.
|
||||
pub trait SwapNative<Origin, AccountId, Balance, AssetBalance, AssetId> {
|
||||
/// Take an `asset_id` and swap some amount for `amount_out` of the chain's native asset. If an
|
||||
/// `amount_in_max` is specified, it will return an error if acquiring `amount_out` would be
|
||||
/// too costly.
|
||||
///
|
||||
/// Withdraws `asset_id` from `sender`, deposits native asset to `send_to`, respecting
|
||||
/// `keep_alive`.
|
||||
///
|
||||
/// If successful returns the amount of the `asset_id` taken to provide `amount_out`.
|
||||
fn swap_tokens_for_exact_native(
|
||||
sender: AccountId,
|
||||
asset_id: AssetId,
|
||||
amount_out: Balance,
|
||||
amount_in_max: Option<AssetBalance>,
|
||||
send_to: AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<AssetBalance, DispatchError>;
|
||||
|
||||
/// Take an `asset_id` and swap `amount_in` of the chain's native asset for it. If an
|
||||
/// `amount_out_min` is specified, it will return an error if it is unable to acquire the amount
|
||||
/// desired.
|
||||
///
|
||||
/// Withdraws native asset from `sender`, deposits `asset_id` to `send_to`, respecting
|
||||
/// `keep_alive`.
|
||||
///
|
||||
/// If successful, returns the amount of `asset_id` acquired for the `amount_in`.
|
||||
fn swap_exact_native_for_tokens(
|
||||
sender: AccountId,
|
||||
asset_id: AssetId,
|
||||
amount_in: Balance,
|
||||
amount_out_min: Option<AssetBalance>,
|
||||
send_to: AccountId,
|
||||
keep_alive: bool,
|
||||
) -> Result<AssetBalance, DispatchError>;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ sp-runtime = { version = "24.0.0", default-features = false, path = "../../../pr
|
||||
sp-std = { version = "8.0.0", default-features = false, path = "../../../primitives/std" }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../../system" }
|
||||
pallet-asset-conversion = { version = "4.0.0-dev", default-features = false, path = "../../asset-conversion" }
|
||||
pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, path = ".." }
|
||||
codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
||||
@@ -28,7 +29,6 @@ sp-io = { version = "23.0.0", default-features = false, path = "../../../primiti
|
||||
sp-storage = { version = "13.0.0", default-features = false, path = "../../../primitives/storage" }
|
||||
pallet-assets = { version = "4.0.0-dev", path = "../../assets" }
|
||||
pallet-balances = { version = "4.0.0-dev", path = "../../balances" }
|
||||
pallet-asset-conversion = { version = "4.0.0-dev", path = "../../asset-conversion" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -41,6 +41,7 @@ std = [
|
||||
"frame-system/std",
|
||||
"sp-io/std",
|
||||
"sp-core/std",
|
||||
"pallet-asset-conversion/std",
|
||||
"pallet-transaction-payment/std",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
|
||||
@@ -69,6 +69,8 @@ mod mock;
|
||||
mod tests;
|
||||
|
||||
mod payment;
|
||||
use frame_support::traits::tokens::AssetId;
|
||||
use pallet_asset_conversion::MultiAssetIdConverter;
|
||||
pub use payment::*;
|
||||
|
||||
/// Type aliases used for interaction with `OnChargeTransaction`.
|
||||
@@ -116,7 +118,9 @@ pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config + pallet_transaction_payment::Config {
|
||||
pub trait Config:
|
||||
frame_system::Config + pallet_transaction_payment::Config + pallet_asset_conversion::Config
|
||||
{
|
||||
/// The overarching event type.
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
/// The fungibles instance used to pay for transactions in assets.
|
||||
@@ -187,12 +191,12 @@ where
|
||||
debug_assert!(self.tip <= fee, "tip should be included in the computed fee");
|
||||
if fee.is_zero() {
|
||||
Ok((fee, InitialPayment::Nothing))
|
||||
} else if let Some(asset_id) = self.asset_id {
|
||||
} else if let Some(asset_id) = &self.asset_id {
|
||||
T::OnChargeAssetTransaction::withdraw_fee(
|
||||
who,
|
||||
call,
|
||||
info,
|
||||
asset_id,
|
||||
asset_id.clone(),
|
||||
fee.into(),
|
||||
self.tip.into(),
|
||||
)
|
||||
@@ -324,7 +328,7 @@ where
|
||||
tip.into(),
|
||||
used_for_fee.into(),
|
||||
received_exchanged.into(),
|
||||
asset_id,
|
||||
asset_id.clone(),
|
||||
asset_consumed.into(),
|
||||
)?;
|
||||
|
||||
|
||||
@@ -17,26 +17,25 @@
|
||||
use super::*;
|
||||
use crate::Config;
|
||||
|
||||
use codec::FullCodec;
|
||||
use frame_support::{
|
||||
ensure,
|
||||
traits::{fungible::Inspect, fungibles::SwapNative, tokens::Balance},
|
||||
traits::{fungible::Inspect, tokens::Balance},
|
||||
unsigned::TransactionValidityError,
|
||||
};
|
||||
use scale_info::TypeInfo;
|
||||
use pallet_asset_conversion::Swap;
|
||||
use sp_runtime::{
|
||||
traits::{DispatchInfoOf, MaybeSerializeDeserialize, PostDispatchInfoOf, Zero},
|
||||
traits::{DispatchInfoOf, PostDispatchInfoOf, Zero},
|
||||
transaction_validity::InvalidTransaction,
|
||||
Saturating,
|
||||
};
|
||||
use sp_std::{fmt::Debug, marker::PhantomData};
|
||||
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: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo;
|
||||
type AssetId: AssetId;
|
||||
/// The type used to store the intermediate values between pre- and post-dispatch.
|
||||
type LiquidityInfo;
|
||||
|
||||
@@ -74,8 +73,7 @@ pub trait OnChargeAssetTransaction<T: Config> {
|
||||
) -> Result<AssetBalanceOf<T>, TransactionValidityError>;
|
||||
}
|
||||
|
||||
/// Implements the asset transaction for a balance to asset converter (implementing
|
||||
/// [`SwapNative`]).
|
||||
/// 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>(PhantomData<(C, CON)>);
|
||||
@@ -85,8 +83,9 @@ impl<T, C, CON> OnChargeAssetTransaction<T> for AssetConversionAdapter<C, CON>
|
||||
where
|
||||
T: Config,
|
||||
C: Inspect<<T as frame_system::Config>::AccountId>,
|
||||
CON: SwapNative<T::RuntimeOrigin, T::AccountId, BalanceOf<T>, AssetBalanceOf<T>, AssetIdOf<T>>,
|
||||
AssetIdOf<T>: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo,
|
||||
CON: Swap<T::AccountId, T::HigherPrecisionBalance, T::MultiAssetId>,
|
||||
T::HigherPrecisionBalance: From<BalanceOf<T>> + TryInto<AssetBalanceOf<T>>,
|
||||
T::MultiAssetId: From<AssetIdOf<T>>,
|
||||
BalanceOf<T>: IsType<<C as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
|
||||
{
|
||||
type Balance = BalanceOf<T>;
|
||||
@@ -115,16 +114,20 @@ where
|
||||
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_native(
|
||||
let asset_consumed = CON::swap_tokens_for_exact_tokens(
|
||||
who.clone(),
|
||||
asset_id,
|
||||
native_asset_required,
|
||||
vec![asset_id.into(), T::MultiAssetIdConverter::get_native()],
|
||||
T::HigherPrecisionBalance::from(native_asset_required),
|
||||
None,
|
||||
who.clone(),
|
||||
true,
|
||||
)
|
||||
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
|
||||
|
||||
let asset_consumed = asset_consumed
|
||||
.try_into()
|
||||
.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?;
|
||||
|
||||
ensure!(asset_consumed > Zero::zero(), InvalidTransaction::Payment);
|
||||
|
||||
// charge the fee in native currency
|
||||
@@ -166,17 +169,25 @@ where
|
||||
// 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_native_for_tokens(
|
||||
match CON::swap_exact_tokens_for_tokens(
|
||||
who.clone(), // we already deposited the native to `who`
|
||||
asset_id, // we want asset_id back
|
||||
swap_back, // amount of the native asset to convert to `asset_id`
|
||||
vec![
|
||||
T::MultiAssetIdConverter::get_native(), // we provide the native
|
||||
asset_id.into(), // we want asset_id back
|
||||
],
|
||||
T::HigherPrecisionBalance::from(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,
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user