Skip to content

Commit 2cb39f8

Browse files
muharemjoepetrowskiliamaharon
authored
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 - #1677 related PRs: - (depends) #2031 - (depends) #1677 - (caused) #1847 - (caused) #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 - #1842 // DONE: tests --------- Co-authored-by: joe petrowski <[email protected]> Co-authored-by: Liam Aharon <[email protected]>
1 parent 4c4963a commit 2cb39f8

File tree

25 files changed

+1739
-831
lines changed

25 files changed

+1739
-831
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/send.rs

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -27,78 +27,3 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() {
2727
Some(Weight::from_parts(1_019_445_000, 200_000)),
2828
)
2929
}
30-
31-
/// Parachain should be able to send XCM paying its fee with sufficient asset
32-
/// in the System Parachain
33-
#[test]
34-
fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
35-
let para_sovereign_account = AssetHubRococo::sovereign_account_id_of(
36-
AssetHubRococo::sibling_location_of(PenpalA::para_id()),
37-
);
38-
39-
// Force create and mint assets for Parachain's sovereign account
40-
AssetHubRococo::force_create_and_mint_asset(
41-
ASSET_ID,
42-
ASSET_MIN_BALANCE,
43-
true,
44-
para_sovereign_account.clone(),
45-
Some(Weight::from_parts(1_019_445_000, 200_000)),
46-
ASSET_MIN_BALANCE * 1000000000,
47-
);
48-
49-
// We just need a call that can pass the `SafeCallFilter`
50-
// Call values are not relevant
51-
let call = AssetHubRococo::force_create_asset_call(
52-
ASSET_ID,
53-
para_sovereign_account.clone(),
54-
true,
55-
ASSET_MIN_BALANCE,
56-
);
57-
58-
let origin_kind = OriginKind::SovereignAccount;
59-
let fee_amount = ASSET_MIN_BALANCE * 1000000;
60-
let native_asset =
61-
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into();
62-
63-
let root_origin = <PenpalA as Chain>::RuntimeOrigin::root();
64-
let system_para_destination = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into();
65-
let xcm = xcm_transact_paid_execution(
66-
call,
67-
origin_kind,
68-
native_asset,
69-
para_sovereign_account.clone(),
70-
);
71-
72-
PenpalA::execute_with(|| {
73-
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
74-
root_origin,
75-
bx!(system_para_destination),
76-
bx!(xcm),
77-
));
78-
79-
PenpalA::assert_xcm_pallet_sent();
80-
});
81-
82-
AssetHubRococo::execute_with(|| {
83-
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
84-
85-
AssetHubRococo::assert_xcmp_queue_success(Some(Weight::from_parts(
86-
15_594_564_000,
87-
562_893,
88-
)));
89-
90-
assert_expected_events!(
91-
AssetHubRococo,
92-
vec![
93-
RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
94-
asset_id: *asset_id == ASSET_ID,
95-
owner: *owner == para_sovereign_account,
96-
balance: *balance == fee_amount,
97-
},
98-
RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
99-
asset_id: *asset_id == ASSET_ID,
100-
},
101-
]
102-
);
103-
});
104-
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/swap.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,130 @@ fn cannot_create_pool_from_pool_assets() {
270270
);
271271
});
272272
}
273+
274+
#[test]
275+
fn pay_xcm_fee_with_some_asset_swapped_for_native() {
276+
let asset_native = asset_hub_rococo_runtime::xcm_config::TokenLocation::get();
277+
let asset_one = MultiLocation {
278+
parents: 0,
279+
interior: X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())),
280+
};
281+
let penpal = AssetHubRococo::sovereign_account_id_of(AssetHubRococo::sibling_location_of(
282+
PenpalA::para_id(),
283+
));
284+
285+
AssetHubRococo::execute_with(|| {
286+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
287+
288+
// set up pool with ASSET_ID <> NATIVE pair
289+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::Assets::create(
290+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
291+
ASSET_ID.into(),
292+
AssetHubRococoSender::get().into(),
293+
ASSET_MIN_BALANCE,
294+
));
295+
assert!(<AssetHubRococo as AssetHubRococoPallet>::Assets::asset_exists(ASSET_ID));
296+
297+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::Assets::mint(
298+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
299+
ASSET_ID.into(),
300+
AssetHubRococoSender::get().into(),
301+
3_000_000_000_000,
302+
));
303+
304+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::AssetConversion::create_pool(
305+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
306+
Box::new(asset_native),
307+
Box::new(asset_one),
308+
));
309+
310+
assert_expected_events!(
311+
AssetHubRococo,
312+
vec![
313+
RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {},
314+
]
315+
);
316+
317+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::AssetConversion::add_liquidity(
318+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
319+
Box::new(asset_native),
320+
Box::new(asset_one),
321+
1_000_000_000_000,
322+
2_000_000_000_000,
323+
0,
324+
0,
325+
AssetHubRococoSender::get().into()
326+
));
327+
328+
assert_expected_events!(
329+
AssetHubRococo,
330+
vec![
331+
RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { lp_token_minted: *lp_token_minted == 1414213562273, },
332+
]
333+
);
334+
335+
// ensure `penpal` sovereign account has no native tokens and mint some `ASSET_ID`
336+
assert_eq!(
337+
<AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(penpal.clone()),
338+
0
339+
);
340+
341+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::Assets::touch_other(
342+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
343+
ASSET_ID.into(),
344+
penpal.clone().into(),
345+
));
346+
347+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::Assets::mint(
348+
<AssetHubRococo as Chain>::RuntimeOrigin::signed(AssetHubRococoSender::get()),
349+
ASSET_ID.into(),
350+
penpal.clone().into(),
351+
10_000_000_000_000,
352+
));
353+
});
354+
355+
PenpalA::execute_with(|| {
356+
// send xcm transact from `penpal` account which as only `ASSET_ID` tokens on
357+
// `AssetHubRococo`
358+
let call = AssetHubRococo::force_create_asset_call(
359+
ASSET_ID + 1000,
360+
penpal.clone(),
361+
true,
362+
ASSET_MIN_BALANCE,
363+
);
364+
365+
let penpal_root = <PenpalA as Chain>::RuntimeOrigin::root();
366+
let fee_amount = 4_000_000_000_000u128;
367+
let asset_one =
368+
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount)
369+
.into();
370+
let asset_hub_location = PenpalA::sibling_location_of(AssetHubRococo::para_id()).into();
371+
let xcm = xcm_transact_paid_execution(
372+
call,
373+
OriginKind::SovereignAccount,
374+
asset_one,
375+
penpal.clone(),
376+
);
377+
378+
assert_ok!(<PenpalA as PenpalAPallet>::PolkadotXcm::send(
379+
penpal_root,
380+
bx!(asset_hub_location),
381+
bx!(xcm),
382+
));
383+
384+
PenpalA::assert_xcm_pallet_sent();
385+
});
386+
387+
AssetHubRococo::execute_with(|| {
388+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
389+
390+
AssetHubRococo::assert_xcmp_queue_success(None);
391+
assert_expected_events!(
392+
AssetHubRococo,
393+
vec![
394+
RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapCreditExecuted { .. },) => {},
395+
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true,.. }) => {},
396+
]
397+
);
398+
});
399+
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/send.rs

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -27,78 +27,3 @@ fn send_transact_as_superuser_from_relay_to_system_para_works() {
2727
Some(Weight::from_parts(1_019_445_000, 200_000)),
2828
)
2929
}
30-
31-
/// Parachain should be able to send XCM paying its fee with sufficient asset
32-
/// in the System Parachain
33-
#[test]
34-
fn send_xcm_from_para_to_system_para_paying_fee_with_assets_works() {
35-
let para_sovereign_account = AssetHubWestend::sovereign_account_id_of(
36-
AssetHubWestend::sibling_location_of(PenpalB::para_id()),
37-
);
38-
39-
// Force create and mint assets for Parachain's sovereign account
40-
AssetHubWestend::force_create_and_mint_asset(
41-
ASSET_ID,
42-
ASSET_MIN_BALANCE,
43-
true,
44-
para_sovereign_account.clone(),
45-
Some(Weight::from_parts(1_019_445_000, 200_000)),
46-
ASSET_MIN_BALANCE * 1000000000,
47-
);
48-
49-
// We just need a call that can pass the `SafeCallFilter`
50-
// Call values are not relevant
51-
let call = AssetHubWestend::force_create_asset_call(
52-
ASSET_ID,
53-
para_sovereign_account.clone(),
54-
true,
55-
ASSET_MIN_BALANCE,
56-
);
57-
58-
let origin_kind = OriginKind::SovereignAccount;
59-
let fee_amount = ASSET_MIN_BALANCE * 1000000;
60-
let native_asset =
61-
(X2(PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())), fee_amount).into();
62-
63-
let root_origin = <PenpalB as Chain>::RuntimeOrigin::root();
64-
let system_para_destination = PenpalB::sibling_location_of(AssetHubWestend::para_id()).into();
65-
let xcm = xcm_transact_paid_execution(
66-
call,
67-
origin_kind,
68-
native_asset,
69-
para_sovereign_account.clone(),
70-
);
71-
72-
PenpalB::execute_with(|| {
73-
assert_ok!(<PenpalB as PenpalBPallet>::PolkadotXcm::send(
74-
root_origin,
75-
bx!(system_para_destination),
76-
bx!(xcm),
77-
));
78-
79-
PenpalB::assert_xcm_pallet_sent();
80-
});
81-
82-
AssetHubWestend::execute_with(|| {
83-
type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
84-
85-
AssetHubWestend::assert_xcmp_queue_success(Some(Weight::from_parts(
86-
16_290_336_000,
87-
562_893,
88-
)));
89-
90-
assert_expected_events!(
91-
AssetHubWestend,
92-
vec![
93-
RuntimeEvent::Assets(pallet_assets::Event::Burned { asset_id, owner, balance }) => {
94-
asset_id: *asset_id == ASSET_ID,
95-
owner: *owner == para_sovereign_account,
96-
balance: *balance == fee_amount,
97-
},
98-
RuntimeEvent::Assets(pallet_assets::Event::Issued { asset_id, .. }) => {
99-
asset_id: *asset_id == ASSET_ID,
100-
},
101-
]
102-
);
103-
});
104-
}

0 commit comments

Comments
 (0)