pallet-asset-conversion: Swap Credit (#1677)

Introduces a swap implementation that allows the exchange of a credit
(aka Negative Imbalance) of one asset for a credit of another asset.

This is particularly useful when a credit swap is required but may not
have sufficient value to meet the ED constraint, hence cannot be
deposited to temp account before. An example use case is when XCM fees
are paid using an asset held in the XCM executor registry and has to be
swapped for native currency.

Additional Updates:
- encapsulates the existing `Swap` trait impl within a transactional
context, since partial storage mutation is possible when an error
occurs;
- supplied `Currency` and `Assets` impls must be implemented over the
same `Balance` type, the `AssetBalance` generic type is dropped. This
helps to avoid numerous type conversion and overflow cases. If those
types are different it should be handled outside of the pallet;
- `Box` asset kind on a pallet level, unbox on a runtime level - here
[why](https://substrate.stackexchange.com/questions/10039/boxed-argument-of-a-dispatchable/10103#10103);
- `path` uses `Vec` now, instead of `BoundedVec` since it is never used
in PoV;
- removes the `Transfer` event due to it's redundancy with the events
emitted by `fungible/s` implementations;
- modifies the `SwapExecuted` event type;

related issue: 
- https://github.com/paritytech/polkadot-sdk/issues/105

related PRs:
- (required for) https://github.com/paritytech/polkadot-sdk/pull/1845
- (caused) https://github.com/paritytech/polkadot-sdk/pull/1717

// DONE make the pallet work only with `fungibles` trait and make it
free from the concept of a `native` asset -
https://github.com/paritytech/polkadot-sdk/issues/1842

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
This commit is contained in:
Muharem
2023-12-19 17:31:18 +01:00
committed by GitHub
parent 84d6342cd2
commit 5ce04514eb
20 changed files with 3932 additions and 647 deletions
@@ -238,7 +238,6 @@ ord_parameter_types! {
impl pallet_asset_conversion::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type AssetBalance = <Self as pallet_balances::Config>::Balance;
type AssetId = u32;
type PoolAssetId = u32;
type Assets = Assets;
@@ -83,8 +83,8 @@ impl<T, C, CON> OnChargeAssetTransaction<T> for AssetConversionAdapter<C, CON>
where
T: Config,
C: Inspect<<T as frame_system::Config>::AccountId>,
CON: Swap<T::AccountId, T::HigherPrecisionBalance, T::MultiAssetId>,
T::HigherPrecisionBalance: From<BalanceOf<T>> + TryInto<AssetBalanceOf<T>>,
CON: Swap<T::AccountId, Balance = BalanceOf<T>, MultiAssetId = T::MultiAssetId>,
BalanceOf<T>: Into<AssetBalanceOf<T>>,
T::MultiAssetId: From<AssetIdOf<T>>,
BalanceOf<T>: IsType<<C as Inspect<<T as frame_system::Config>::AccountId>>::Balance>,
{
@@ -117,22 +117,18 @@ where
let asset_consumed = CON::swap_tokens_for_exact_tokens(
who.clone(),
vec![asset_id.into(), T::MultiAssetIdConverter::get_native()],
T::HigherPrecisionBalance::from(native_asset_required),
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
<T::OnChargeTransaction>::withdraw_fee(who, call, info, fee, tip)
.map(|r| (r, native_asset_required, asset_consumed))
.map(|r| (r, native_asset_required, asset_consumed.into()))
}
/// Correct the fee and swap the refund back to asset.
@@ -175,8 +171,7 @@ where
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` */
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
@@ -19,7 +19,10 @@ use frame_support::{
assert_ok,
dispatch::{DispatchInfo, PostDispatchInfo},
pallet_prelude::*,
traits::{fungible::Inspect, fungibles::Mutate},
traits::{
fungible::Inspect,
fungibles::{Inspect as FungiblesInspect, Mutate},
},
weights::Weight,
};
use frame_system as system;
@@ -110,22 +113,32 @@ fn default_post_info() -> PostDispatchInfo {
fn setup_lp(asset_id: u32, balance_factor: u64) {
let lp_provider = 5;
let ed = Balances::minimum_balance();
let ed_asset = Assets::minimum_balance(asset_id);
assert_ok!(Balances::force_set_balance(
RuntimeOrigin::root(),
lp_provider,
10_000 * balance_factor
10_000 * balance_factor + ed,
));
let lp_provider_account = <Runtime as system::Config>::Lookup::unlookup(lp_provider);
assert_ok!(Assets::mint_into(asset_id.into(), &lp_provider_account, 10_000 * balance_factor));
assert_ok!(Assets::mint_into(
asset_id.into(),
&lp_provider_account,
10_000 * balance_factor + ed_asset
));
let token_1 = NativeOrAssetId::Native;
let token_2 = NativeOrAssetId::Asset(asset_id);
assert_ok!(AssetConversion::create_pool(RuntimeOrigin::signed(lp_provider), token_1, token_2));
assert_ok!(AssetConversion::create_pool(
RuntimeOrigin::signed(lp_provider),
Box::new(token_1),
Box::new(token_2)
));
assert_ok!(AssetConversion::add_liquidity(
RuntimeOrigin::signed(lp_provider),
token_1,
token_2,
Box::new(token_1),
Box::new(token_2),
1_000 * balance_factor, // 1 desired
10_000 * balance_factor, // 2 desired
1, // 1 min