Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Test for pallet_xcm::reserve_transfer_assets with paid XcmRouter
Browse files Browse the repository at this point in the history
  • Loading branch information
bkontur committed Aug 7, 2023
1 parent 6f4a8bb commit 80f31ec
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 7 deletions.
57 changes: 50 additions & 7 deletions xcm/pallet-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@

use codec::Encode;
use frame_support::{
construct_runtime, parameter_types,
traits::{ConstU32, Everything, Nothing},
construct_runtime, match_types, parameter_types,
traits::{ConstU32, Everything, EverythingBut, Nothing},
weights::Weight,
};
use frame_system::EnsureRoot;
use polkadot_parachain::primitives::Id as ParaId;
use polkadot_runtime_parachains::origin;
use sp_core::H256;
use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage};
pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData};
pub use sp_std::{
cell::RefCell, collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData,
};
use xcm::prelude::*;
use xcm_builder::{
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia,
ChildSystemParachainAsSuperuser, CurrencyAdapter as XcmCurrencyAdapter, FixedRateOfFungible,
FixedWeightBounds, IsConcrete, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit,
SovereignSignedViaLocation, TakeWeightCredit, XcmFeesToAccount,
};
use xcm_executor::XcmExecutor;

Expand Down Expand Up @@ -143,6 +145,7 @@ construct_runtime!(

thread_local! {
pub static SENT_XCM: RefCell<Vec<(MultiLocation, Xcm<()>)>> = RefCell::new(Vec::new());
pub static XCM_ROUTER_FEES: RefCell<BTreeMap<MultiLocation, MultiAssets>> = RefCell::new(BTreeMap::new());
}
pub(crate) fn sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
SENT_XCM.with(|q| (*q.borrow()).clone())
Expand All @@ -154,7 +157,24 @@ pub(crate) fn take_sent_xcm() -> Vec<(MultiLocation, Xcm<()>)> {
r
})
}
/// Sender that never returns error, always sends
pub(crate) fn set_router_fee_for_destination(dest: MultiLocation, fees: Option<MultiAssets>) {
XCM_ROUTER_FEES.with(|q| {
if let Some(fees) = fees {
q.borrow_mut()
.entry(dest)
.and_modify(|old_value| *old_value = fees.clone())
.or_insert(fees);
} else {
let _ = q.borrow_mut().remove_entry(&dest);
}
})
}
pub(crate) fn get_router_fee_for_destination(dest: &MultiLocation) -> Option<MultiAssets> {
XCM_ROUTER_FEES.with(|q| q.borrow().get(dest).map(Clone::clone))
}

/// Sender that never returns error, always sends.
/// By default does not return **fees**, **fees** can be managed per destination with `set_router_fee_for_destination`.
pub struct TestSendXcm;
impl SendXcm for TestSendXcm {
type Ticket = (MultiLocation, Xcm<()>);
Expand All @@ -163,7 +183,17 @@ impl SendXcm for TestSendXcm {
msg: &mut Option<Xcm<()>>,
) -> SendResult<(MultiLocation, Xcm<()>)> {
let pair = (dest.take().unwrap(), msg.take().unwrap());
Ok((pair, MultiAssets::new()))

// Check if fees are configured for dest
let maybe_payment = match get_router_fee_for_destination(&pair.0) {
Some(fees) => fees,
None => {
// can be change to None, but this was here before.
MultiAssets::new()
},
};

Ok((pair, maybe_payment))
}
fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<XcmHash, SendError> {
let hash = fake_message_hash(&pair.1);
Expand Down Expand Up @@ -271,6 +301,14 @@ parameter_types! {
pub TrustedAssets: (MultiAssetFilter, MultiLocation) = (All.into(), Here.into());
pub const MaxInstructions: u32 = 100;
pub const MaxAssetsIntoHolding: u32 = 64;
pub XcmFeesTargetAccount: AccountId = AccountId::new([167u8; 32]);
}

pub const XCM_FEES_NOT_WAIVED_USER_ACCOUNT: [u8; 32] = [37u8; 32];
match_types! {
pub type XcmFeesNotWaivedLocations: impl Contains<MultiLocation> = {
MultiLocation { parents: 0, interior: X1(Junction::AccountId32 {network: None, id: XCM_FEES_NOT_WAIVED_USER_ACCOUNT})}
};
}

pub type Barrier = (
Expand Down Expand Up @@ -300,7 +338,12 @@ impl xcm_executor::Config for XcmConfig {
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = ();
type FeeManager = XcmFeesToAccount<
Self,
EverythingBut<XcmFeesNotWaivedLocations>,
AccountId,
XcmFeesTargetAccount,
>;
type MessageExporter = ();
type UniversalAliases = Nothing;
type CallDispatcher = RuntimeCall;
Expand Down
74 changes: 74 additions & 0 deletions xcm/pallet-xcm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,80 @@ fn reserve_transfer_assets_works() {
});
}

/// Test `reserve_transfer_assets_with_paid_router_works`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
/// is increased. Verifies the correct message is sent and event is emitted.
/// Verifies that XCM router fees (`SendXcm::validate` -> `MultiAssets`) are withdrawn from correct user account
/// and deposited to a correct target account (`XcmFeesTargetAccount`).
#[test]
fn reserve_transfer_assets_with_paid_router_works() {
// set up router fees for destination
let xcm_router_fee_amount = 1;
set_router_fee_for_destination(
Parachain(PARA_ID).into(),
Some(MultiAssets::from((Here, xcm_router_fee_amount))),
);

let user_account = AccountId::from(XCM_FEES_NOT_WAIVED_USER_ACCOUNT);

let balances = vec![
(user_account.clone(), INITIAL_BALANCE),
(ParaId::from(PARA_ID).into_account_truncating(), INITIAL_BALANCE),
(XcmFeesTargetAccount::get(), INITIAL_BALANCE),
];
new_test_ext_with_balances(balances).execute_with(|| {
let weight = BaseXcmWeight::get() * 2;
let dest: MultiLocation =
Junction::AccountId32 { network: None, id: user_account.clone().into() }.into();
assert_eq!(Balances::total_balance(&user_account), INITIAL_BALANCE);
assert_ok!(XcmPallet::reserve_transfer_assets(
RuntimeOrigin::signed(user_account.clone()),
Box::new(Parachain(PARA_ID).into()),
Box::new(dest.clone().into()),
Box::new((Here, SEND_AMOUNT).into()),
0,
));
// check event
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);

// XCM_FEES_NOT_WAIVED_USER_ACCOUNT spent amount
assert_eq!(
Balances::free_balance(user_account),
INITIAL_BALANCE - SEND_AMOUNT - xcm_router_fee_amount
);
// Destination account (parachain account) has amount
let para_acc: AccountId = ParaId::from(PARA_ID).into_account_truncating();
assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE + SEND_AMOUNT);
// XcmFeesTargetAccount where should lend xcm_router_fee_amount
assert_eq!(
Balances::free_balance(XcmFeesTargetAccount::get()),
INITIAL_BALANCE + xcm_router_fee_amount
);
assert_eq!(
sent_xcm(),
vec![(
Parachain(PARA_ID).into(),
Xcm(vec![
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
ClearOrigin,
buy_limited_execution((Parent, SEND_AMOUNT), Weight::from_parts(4000, 4000)),
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
]),
)]
);
let versioned_sent = VersionedXcm::from(sent_xcm().into_iter().next().unwrap().1);
let _check_v2_ok: xcm::v2::Xcm<()> = versioned_sent.try_into().unwrap();
assert_eq!(
last_event(),
RuntimeEvent::XcmPallet(crate::Event::Attempted { outcome: Outcome::Complete(weight) })
);
});
}

/// Test `limited_reserve_transfer_assets`
///
/// Asserts that the sender's balance is decreased and the beneficiary's balance
Expand Down

0 comments on commit 80f31ec

Please sign in to comment.