XCM WeightTrader: Swap Fee Asset for Native Asset (#1845)

Implements an XCM executor `WeightTrader`, facilitating fee payments in
any asset that can be exchanged for a native asset.

A few constraints need to be observed:
- `buy_weight` and `refund` operations must be atomic, as another weight
trader implementation might be attempted in case of failure.
- swap credit must be utilized since there isn’t an account to which an
asset of some class can be deposited with a guarantee to meet the
existential deposit requirement. Also, operating with credits enhances
the efficiency of the weight trader -
https://github.com/paritytech/polkadot-sdk/pull/1677

related PRs:
- (depends) https://github.com/paritytech/polkadot-sdk/pull/2031
- (depends) https://github.com/paritytech/polkadot-sdk/pull/1677
- (caused) https://github.com/paritytech/polkadot-sdk/pull/1847
- (caused) https://github.com/paritytech/polkadot-sdk/pull/1876

// DONE: impl `OnUnbalanced` for a `fungible/s` credit
// DONE: make the trader free from a concept of a native currency and
drop few fallible conversions. related issue -
https://github.com/paritytech/polkadot-sdk/issues/1842
// DONE: tests

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: Liam Aharon <liam.aharon@hotmail.com>
This commit is contained in:
Muharem
2024-01-16 15:34:48 +08:00
committed by GitHub
parent 4c4963a192
commit 2cb39f8dc9
25 changed files with 1769 additions and 861 deletions
@@ -30,6 +30,7 @@ use parachains_runtimes_test_utils::{
ValidatorIdOf, XcmReceivedFrom,
};
use sp_runtime::{traits::StaticLookup, Saturating};
use sp_std::ops::Mul;
use xcm::{latest::prelude::*, VersionedMultiAssets};
use xcm_builder::{CreateMatcher, MatchXcm};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
@@ -336,12 +337,13 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config
+ pallet_assets::Config<ForeignAssetsPalletInstance>,
+ pallet_assets::Config<ForeignAssetsPalletInstance>
+ pallet_asset_conversion::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
AccountIdOf<Runtime>: Into<[u8; 32]> + From<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
BalanceOf<Runtime>: From<Balance> + Into<Balance>,
XcmConfig: xcm_executor::Config,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as pallet_assets::Config<ForeignAssetsPalletInstance>>::AssetId:
@@ -354,6 +356,9 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
+ Into<AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as pallet_asset_conversion::Config>::AssetKind:
From<MultiLocation> + Into<MultiLocation>,
<Runtime as pallet_asset_conversion::Config>::Balance: From<Balance>,
ForeignAssetsPalletInstance: 'static,
{
ExtBuilder::<Runtime>::default()
@@ -400,6 +405,43 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
)
);
// setup a pool to pay fees with `foreign_asset_id_multilocation` tokens
let pool_owner: AccountIdOf<Runtime> = [1u8; 32].into();
let native_asset = MultiLocation::parent();
let pool_liquidity: u128 =
existential_deposit.into().max(foreign_asset_id_minimum_balance).mul(100_000);
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&pool_owner,
(existential_deposit.into() + pool_liquidity).mul(2).into(),
);
assert_ok!(<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::mint(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(
sovereign_account_as_owner_of_foreign_asset
),
foreign_asset_id_multilocation.into(),
pool_owner.clone().into(),
(foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(),
));
assert_ok!(<pallet_asset_conversion::Pallet<Runtime>>::create_pool(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(pool_owner.clone()),
Box::new(native_asset.into()),
Box::new(foreign_asset_id_multilocation.into())
));
assert_ok!(<pallet_asset_conversion::Pallet<Runtime>>::add_liquidity(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(pool_owner.clone()),
Box::new(native_asset.into()),
Box::new(foreign_asset_id_multilocation.into()),
pool_liquidity.into(),
pool_liquidity.into(),
1.into(),
1.into(),
pool_owner,
));
// Balances before
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
@@ -485,14 +527,12 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
);
assert_ok!(outcome.ensure_complete());
// author actual balance after (received fees from Trader for ForeignAssets)
let author_received_fees =
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_multilocation.into(),
&block_author_account,
);
// Balances after (untouched)
// Balances after
// staking pot receives xcm fees in dot
assert!(
<pallet_balances::Pallet<Runtime>>::free_balance(&staking_pot) !=
existential_deposit
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
existential_deposit.clone()
@@ -501,25 +541,13 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
<pallet_balances::Pallet<Runtime>>::free_balance(&block_author_account),
0.into()
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&staking_pot),
existential_deposit.clone()
);
// ForeignAssets balances after
assert_eq!(
assert!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_multilocation.into(),
&target_account
),
(transfered_foreign_asset_id_amount - author_received_fees.into()).into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_multilocation.into(),
&block_author_account
),
author_received_fees
) > 0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
@@ -528,6 +556,13 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works<
),
0.into()
);
assert_eq!(
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
foreign_asset_id_multilocation.into(),
&block_author_account
),
0.into()
);
})
}