moonbeam_runtime_common/tests/
xcm.rs1#[macro_export]
22macro_rules! generate_common_xcm_tests {
23 ($runtime: ident) => {
24 #[cfg(test)]
25 pub mod common_xcm_tests {
26 use crate::common::{ExtBuilder, ALICE};
27 use cumulus_primitives_core::ExecuteXcm;
28 use frame_support::assert_ok;
29 use frame_support::traits::fungible::Inspect;
30 use frame_support::traits::EnsureOrigin;
31 use frame_support::weights::{constants::WEIGHT_REF_TIME_PER_SECOND, WeightToFee as _};
32 use moonbeam_core_primitives::{AccountId, Balance};
33 use pallet_xcm_weight_trader::{SupportedAssets, RELATIVE_PRICE_DECIMALS};
34 use parity_scale_codec::Encode;
35 use sp_weights::Weight;
36 use xcm::latest::Location;
37 use xcm::{
38 latest::{prelude::AccountKey20, Assets as XcmAssets, Xcm},
39 VersionedAssets, VersionedLocation, VersionedXcm,
40 };
41 use $runtime::{
42 xcm_config::SelfReserve, Balances, PolkadotXcm, Runtime, RuntimeEvent,
43 RuntimeOrigin, System, XcmTransactor, XcmWeightTrader,
44 };
45
46 pub(crate) fn last_events(n: usize) -> Vec<RuntimeEvent> {
47 System::events()
48 .into_iter()
49 .map(|e| e.event)
50 .rev()
51 .take(n)
52 .rev()
53 .collect()
54 }
55
56 #[test]
57 fn dest_asset_fee_per_second_matches_configured_fee_not_relative_price() {
58 fn set_fee_per_second_for_location(
59 location: Location,
60 fee_per_second: u128,
61 ) -> Result<(), ()> {
62 let native_amount_per_second: u128 =
63 <Runtime as pallet_xcm_weight_trader::Config>::WeightToFee::weight_to_fee(
64 &Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, 0),
65 )
66 .try_into()
67 .map_err(|_| ())?;
68 let precision_factor = 10u128.pow(RELATIVE_PRICE_DECIMALS);
69 let relative_price: u128 = if fee_per_second > 0u128 {
70 native_amount_per_second
71 .saturating_mul(precision_factor)
72 .saturating_div(fee_per_second)
73 } else {
74 0u128
75 };
76 if SupportedAssets::<Runtime>::contains_key(&location) {
77 let enabled = SupportedAssets::<Runtime>::get(&location).ok_or(())?.0;
78 SupportedAssets::<Runtime>::insert(&location, (enabled, relative_price));
79 } else {
80 SupportedAssets::<Runtime>::insert(&location, (true, relative_price));
81 }
82 Ok(())
83 }
84
85 ExtBuilder::default().build().execute_with(|| {
86 let native_fee_per_second = WEIGHT_REF_TIME_PER_SECOND as u128;
89 let actual_fee_per_second = native_fee_per_second
90 .checked_div(5)
91 .expect("division by 5 should not overflow");
92
93 let location = Location::parent();
94
95 set_fee_per_second_for_location(location.clone(), actual_fee_per_second)
97 .expect("must be able to configure fee per second");
98
99 let reported = XcmTransactor::dest_asset_fee_per_second(&location)
102 .expect("fee should be set");
103
104 assert_eq!(reported, actual_fee_per_second);
105 });
106 }
107
108 #[test]
109 fn claim_assets_works() {
110 const INITIAL_BALANCE: Balance = 10_000_000_000_000_000_000;
111 const SEND_AMOUNT: Balance = 1_000_000_000_000_000_000;
112
113 let alice = AccountId::from(ALICE);
114 let balances = vec![(alice, INITIAL_BALANCE)];
115
116 ExtBuilder::default()
117 .with_balances(balances)
118 .build()
119 .execute_with(|| {
120 let assets = XcmAssets::from((SelfReserve::get(), SEND_AMOUNT));
121 let trapping_program =
123 Xcm::builder_unsafe().withdraw_asset(assets.clone()).build();
124 let origin_location =
126 <Runtime as pallet_xcm::Config>::ExecuteXcmOrigin::ensure_origin(
127 RuntimeOrigin::signed(alice),
128 )
129 .expect("qed");
130 let message = Box::new(VersionedXcm::V5(trapping_program));
131 let mut hash = message.using_encoded(sp_io::hashing::blake2_256);
132 let message = (*message).try_into().expect("qed");
133 let _ = <Runtime as pallet_xcm::Config>::XcmExecutor::prepare_and_execute(
134 origin_location,
135 message,
136 &mut hash,
137 Weight::MAX,
138 Weight::MAX,
139 );
140 assert_eq!(
141 Balances::total_balance(&alice),
142 INITIAL_BALANCE - SEND_AMOUNT
143 );
144
145 assert!(last_events(2).iter().any(|evt| matches!(
147 evt,
148 RuntimeEvent::PolkadotXcm(pallet_xcm::Event::AssetsTrapped { .. })
149 )));
150
151 assert_ok!(PolkadotXcm::claim_assets(
153 RuntimeOrigin::signed(alice),
154 Box::new(VersionedAssets::V5(assets)),
155 Box::new(VersionedLocation::V5(
156 AccountKey20 {
157 network: None,
158 key: alice.clone().into()
159 }
160 .into()
161 )),
162 ));
163 assert_eq!(Balances::total_balance(&alice), INITIAL_BALANCE);
165 });
166 }
167 }
168 };
169}