pallet-xcm: enhance reserve_transfer_assets to support remote reserves (#1672)

## Motivation

`pallet-xcm` is the main user-facing interface for XCM functionality,
including assets manipulation functions like `teleportAssets()` and
`reserve_transfer_assets()` calls.

While `teleportAsset()` works both ways, `reserve_transfer_assets()`
works only for sending reserve-based assets to a remote destination and
beneficiary when the reserve is the _local chain_.

## Solution

This PR enhances `pallet_xcm::(limited_)reserve_withdraw_assets` to
support transfers when reserves are other chains.
This will allow complete, **bi-directional** reserve-based asset
transfers user stories using `pallet-xcm`.

Enables following scenarios:
- transferring assets with local reserve (was previously supported iff
asset used as fee also had local reserve - now it works in all cases),
- transferring assets with reserve on destination,
- transferring assets with reserve on remote/third-party chain (iff
assets and fees have same remote reserve),
- transferring assets with reserve different than the reserve of the
asset to be used as fees - meaning can be used to transfer random asset
with local/dest reserve while using DOT for fees on all involved chains,
even if DOT local/dest reserve doesn't match asset reserve,
- transferring assets with any type of local/dest reserve while using
fees which can be teleported between involved chains.

All of the above is done by pallet inner logic without the user having
to specify which scenario/reserves/teleports/etc. The correct scenario
and corresponding XCM programs are identified, and respectively, built
automatically based on runtime configuration of trusted teleporters and
trusted reserves.

#### Current limitations:
- while `fees` and "non-fee" `assets` CAN have different reserves (or
fees CAN be teleported), the remaining "non-fee" `assets` CANNOT, among
themselves, have different reserve locations (this is also implicitly
enforced by `MAX_ASSETS_FOR_TRANSFER=2`, but this can be safely
increased in the future).
- `fees` and "non-fee" `assets` CANNOT have **different remote**
reserves (this could also be supported in the future, but adds even more
complexity while possibly not being worth it - we'll see what the future
holds).

Fixes https://github.com/paritytech/polkadot-sdk/issues/1584
Fixes https://github.com/paritytech/polkadot-sdk/issues/2055

---------

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
This commit is contained in:
Adrian Catangiu
2023-11-13 17:16:55 +02:00
committed by GitHub
parent 29654a4d71
commit 18257373b3
80 changed files with 3679 additions and 1466 deletions
@@ -27,6 +27,21 @@ use std::fmt::Debug;
use xcm::latest::prelude::*;
use xcm_builder::{CreateMatcher, MatchXcm};
/// Given a message, a sender, and a destination, it returns the delivery fees
fn get_fungible_delivery_fees<S: SendXcm>(destination: MultiLocation, message: Xcm<()>) -> u128 {
let Ok((_, delivery_fees)) = validate_send::<S>(destination, message) else {
unreachable!("message can be sent; qed")
};
if let Some(delivery_fee) = delivery_fees.inner().first() {
let Fungible(delivery_fee_amount) = delivery_fee.fun else {
unreachable!("asset is fungible; qed");
};
delivery_fee_amount
} else {
0
}
}
/// Helper function to verify `xcm` contains all relevant instructions expected on destination
/// chain as part of a reserve-asset-transfer.
pub(crate) fn assert_matches_reserve_asset_deposited_instructions<RuntimeCall: Debug>(
@@ -16,25 +16,28 @@
//! Module contains predefined test-case scenarios for `Runtime` with various assets.
use super::xcm_helpers;
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
use codec::Encode;
use cumulus_primitives_core::XcmpMessageSource;
use frame_support::{
assert_noop, assert_ok,
traits::{
fungible::Mutate, fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait,
fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
OriginTrait,
},
weights::Weight,
};
use frame_system::pallet_prelude::BlockNumberFor;
use parachains_common::{AccountId, Balance};
use parachains_runtimes_test_utils::{
assert_metadata, assert_total, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
ValidatorIdOf, XcmReceivedFrom,
assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom,
};
use sp_runtime::{
traits::{MaybeEquivalence, StaticLookup, Zero},
DispatchError, Saturating,
};
use xcm::latest::prelude::*;
use xcm::{latest::prelude::*, VersionedMultiAssets};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
@@ -43,8 +46,8 @@ type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
// Re-export test_case from `parachains-runtimes-test-utils`
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
/// Test-case makes sure that `Runtime` can receive native asset from relay chain
/// and can teleport it back and to the other parachains
/// Test-case makes sure that `Runtime` can receive native asset from relay chain and can teleport
/// it back
pub fn teleports_for_native_asset_works<
Runtime,
AllPalletsWithoutSystem,
@@ -57,9 +60,6 @@ pub fn teleports_for_native_asset_works<
existential_deposit: BalanceOf<Runtime>,
target_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
runtime_para_id: u32,
) where
Runtime: frame_system::Config
@@ -164,12 +164,13 @@ pub fn teleports_for_native_asset_works<
// 2. try to teleport asset back to the relaychain
{
let dest = MultiLocation::parent();
let dest_beneficiary = MultiLocation::parent()
let mut dest_beneficiary = MultiLocation::parent()
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
@@ -223,65 +224,53 @@ pub fn teleports_for_native_asset_works<
);
}
// 3. try to teleport asset away to other parachain (1234)
// 3. try to teleport assets away to other parachain (2345): should not work as we don't
// trust `IsTeleporter` for `(relay-native-asset, para(2345))` pair
{
let other_para_id = 1234;
let other_para_id = 2345;
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
assert!(
native_asset_to_teleport_away <
target_account_balance_before_teleport - existential_deposit
);
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
dest_beneficiary,
(native_asset_id, native_asset_to_teleport_away.into()),
Some((runtime_para_id, other_para_id)),
included_head,
&alice,
));
let delivery_fees =
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
(native_asset_id, native_asset_to_teleport_away.into()).into(),
0,
Unlimited,
dest_beneficiary,
assert_eq!(
RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
dest,
);
dest_beneficiary,
(native_asset_id, native_asset_to_teleport_away.into()),
Some((runtime_para_id, other_para_id)),
included_head,
&alice,
),
Err(DispatchError::Module(sp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0,],
message: Some("Filtered",),
},),)
);
// check balances
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
target_account_balance_before_teleport -
native_asset_to_teleport_away -
delivery_fees.into()
target_account_balance_before_teleport
);
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
0.into()
);
// check events
RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
.is_some());
}
})
}
@@ -298,7 +287,6 @@ macro_rules! include_teleports_for_native_asset_works(
$collator_session_key:expr,
$existential_deposit:expr,
$unwrap_pallet_xcm_event:expr,
$unwrap_xcmp_queue_event:expr,
$runtime_para_id:expr
) => {
#[test]
@@ -318,15 +306,14 @@ macro_rules! include_teleports_for_native_asset_works(
$existential_deposit,
target_account,
$unwrap_pallet_xcm_event,
$unwrap_xcmp_queue_event,
$runtime_para_id
)
}
}
);
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain relay
/// chain
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain, and
/// can teleport it back
pub fn teleports_for_foreign_assets_works<
Runtime,
AllPalletsWithoutSystem,
@@ -381,7 +368,7 @@ pub fn teleports_for_foreign_assets_works<
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
ForeignAssetsPalletInstance: 'static,
{
// foreign parachain with the same consenus currency as asset
// foreign parachain with the same consensus currency as asset
let foreign_para_id = 2222;
let foreign_asset_id_multilocation = MultiLocation {
parents: 1,
@@ -473,7 +460,7 @@ pub fn teleports_for_foreign_assets_works<
>(foreign_asset_id_multilocation, 0, 0);
assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
// 1. process received teleported assets from relaychain
// 1. process received teleported assets from sibling parachain (foreign_para_id)
let xcm = Xcm(vec![
// BuyExecution with relaychain native token
WithdrawAsset(buy_execution_fee.clone().into()),
@@ -551,12 +538,13 @@ pub fn teleports_for_foreign_assets_works<
// 2. try to teleport asset back to source parachain (foreign_para_id)
{
let dest = MultiLocation::new(1, X1(Parachain(foreign_para_id)));
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let target_account_balance_before_teleport =
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
@@ -1108,7 +1096,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor
AssetId: Clone + Copy,
AssetIdConverter: MaybeEquivalence<MultiLocation, AssetId>,
{
// foreign parachain with the same consenus currency as asset
// foreign parachain with the same consensus currency as asset
let foreign_asset_id_multilocation =
MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) };
let asset_id = AssetIdConverter::convert(&foreign_asset_id_multilocation).unwrap();
@@ -1388,3 +1376,199 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach
}
}
);
/// Test-case makes sure that `Runtime` can reserve-transfer asset to other parachains (where
/// teleport is not trusted)
pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
Runtime,
AllPalletsWithoutSystem,
XcmConfig,
HrmpChannelOpener,
HrmpChannelSource,
LocationToAccountId,
>(
collator_session_keys: CollatorSessionKeys<Runtime>,
existential_deposit: BalanceOf<Runtime>,
alice_account: AccountIdOf<Runtime>,
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
unwrap_xcmp_queue_event: Box<
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
>,
weight_limit: WeightLimit,
) where
Runtime: frame_system::Config
+ pallet_balances::Config
+ pallet_session::Config
+ pallet_xcm::Config
+ parachain_info::Config
+ pallet_collator_selection::Config
+ cumulus_pallet_parachain_system::Config
+ cumulus_pallet_xcmp_queue::Config,
AllPalletsWithoutSystem:
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
AccountIdOf<Runtime>: Into<[u8; 32]>,
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
BalanceOf<Runtime>: From<Balance>,
<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
XcmConfig: xcm_executor::Config,
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
<Runtime as frame_system::Config>::AccountId:
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
From<<Runtime as frame_system::Config>::AccountId>,
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
Call = cumulus_pallet_parachain_system::Call<Runtime>,
>,
HrmpChannelSource: XcmpMessageSource,
{
let runtime_para_id = 1000;
ExtBuilder::<Runtime>::default()
.with_collators(collator_session_keys.collators())
.with_session_keys(collator_session_keys.session_keys())
.with_tracing()
.with_safe_xcm_version(3)
.with_para_id(runtime_para_id.into())
.build()
.execute_with(|| {
let mut alice = [0u8; 32];
alice[0] = 1;
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
2,
AccountId::from(alice).into(),
);
// reserve-transfer native asset with local reserve to remote parachain (2345)
let other_para_id = 2345;
let native_asset = MultiLocation::parent();
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
.appended_with(AccountId32 {
network: None,
id: sp_runtime::AccountId32::new([3; 32]).into(),
})
.unwrap();
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
let reserve_account = LocationToAccountId::convert_location(&dest)
.expect("Sovereign account for reserves");
let balance_to_transfer = 1_000_000_000_000_u128;
// open HRMP to other parachain
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
runtime_para_id.into(),
other_para_id.into(),
included_head,
&alice,
);
// we calculate exact delivery fees _after_ sending the message by weighing the sent
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
// then verify the arithmetics check out on final balance.
let delivery_fees_buffer = 40_000_000_000u128;
// drip 2xED + transfer_amount + delivery_fees_buffer to Alice account
let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
balance_to_transfer.into() +
delivery_fees_buffer.into();
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&alice_account,
alice_account_init_balance,
);
// SA of target location needs to have at least ED, otherwise making reserve fails
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&reserve_account,
existential_deposit,
);
// we just check here, that user retains enough balance after withdrawal
// and also we check if `balance_to_transfer` is more than `existential_deposit`,
assert!(
(<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
balance_to_transfer.into()) >=
existential_deposit
);
// SA has just ED
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit
);
// local native asset (pallet_balances)
let asset_to_transfer = MultiAsset {
fun: Fungible(balance_to_transfer.into()),
id: Concrete(native_asset),
};
// pallet_xcm call reserve transfer
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
Box::new(dest.into_versioned()),
Box::new(dest_beneficiary.into_versioned()),
Box::new(VersionedMultiAssets::from(MultiAssets::from(asset_to_transfer))),
0,
weight_limit,
));
// check events
// check pallet_xcm attempted
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
&unwrap_pallet_xcm_event,
|outcome| {
assert_ok!(outcome.ensure_complete());
},
);
// check that xcm was sent
let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
.into_iter()
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
.find_map(|e| match e {
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
Some(message_hash),
_ => None,
});
// read xcm
let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
other_para_id.into(),
)
.unwrap();
let delivery_fees = get_fungible_delivery_fees::<
<XcmConfig as xcm_executor::Config>::XcmSender,
>(dest, Xcm::try_from(xcm_sent.clone()).unwrap());
assert_eq!(
xcm_sent_message_hash,
Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
);
let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
// check sent XCM Program to other parachain
println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
let reserve_assets_deposited = MultiAssets::from(vec![MultiAsset {
id: Concrete(MultiLocation { parents: 1, interior: Here }),
fun: Fungible(1000000000000),
}]);
assert_matches_reserve_asset_deposited_instructions(
&mut xcm_sent,
&reserve_assets_deposited,
&dest_beneficiary,
);
// check alice account decreased by balance_to_transfer ( + delivery_fees)
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
);
// check reserve account
// check reserve account increased by balance_to_transfer
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
existential_deposit + balance_to_transfer.into()
);
})
}
@@ -16,7 +16,7 @@
//! Module contains predefined test-case scenarios for `Runtime` with various assets transferred
//! over a bridge.
use crate::assert_matches_reserve_asset_deposited_instructions;
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
use codec::Encode;
use cumulus_primitives_core::XcmpMessageSource;
use frame_support::{
@@ -32,10 +32,7 @@ use parachains_runtimes_test_utils::{
use sp_runtime::{traits::StaticLookup, Saturating};
use xcm::{latest::prelude::*, VersionedMultiAssets};
use xcm_builder::{CreateMatcher, MatchXcm};
use xcm_executor::{
traits::{ConvertLocation, TransactAsset},
XcmExecutor,
};
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
pub struct TestBridgingConfig {
pub bridged_network: NetworkId,
@@ -129,8 +126,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
&alice,
);
// drip ED to account
let alice_account_init_balance = existential_deposit + balance_to_transfer.into();
// we calculate exact delivery fees _after_ sending the message by weighing the sent
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
// then verify the arithmetics check out on final balance.
let delivery_fees_buffer = 8_000_000_000_000u128;
// drip ED + transfer_amount + delivery_fees_buffer to Alice account
let alice_account_init_balance =
existential_deposit + balance_to_transfer.into() + delivery_fees_buffer.into();
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
&alice_account,
alice_account_init_balance,
@@ -183,56 +185,6 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
let expected_beneficiary = target_destination_account;
// Make sure sender has enough funds for paying delivery fees
let handling_delivery_fees = {
// Probable XCM with `ReserveAssetDeposited`.
let mut expected_reserve_asset_deposited_message = Xcm(vec![
ReserveAssetDeposited(MultiAssets::from(expected_assets.clone())),
ClearOrigin,
BuyExecution {
fees: MultiAsset {
id: Concrete(Default::default()),
fun: Fungible(balance_to_transfer),
},
weight_limit: Unlimited,
},
DepositAsset { assets: Wild(AllCounted(1)), beneficiary: expected_beneficiary },
SetTopic([
220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, 112,
140, 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, 237, 173,
]),
]);
assert_matches_reserve_asset_deposited_instructions(
&mut expected_reserve_asset_deposited_message,
&expected_assets,
&expected_beneficiary,
);
// Call `SendXcm::validate` to get delivery fees.
let (_, delivery_fees): (_, MultiAssets) = XcmConfig::XcmSender::validate(
&mut Some(target_location_from_different_consensus),
&mut Some(expected_reserve_asset_deposited_message),
)
.expect("validate passes");
// Drip delivery fee to Alice account.
let mut delivery_fees_added = false;
for delivery_fee in delivery_fees.inner() {
assert_ok!(<XcmConfig::AssetTransactor as TransactAsset>::deposit_asset(
&delivery_fee,
&MultiLocation {
parents: 0,
interior: X1(AccountId32 {
network: None,
id: alice_account.clone().into(),
}),
},
None,
));
delivery_fees_added = true;
}
delivery_fees_added
};
// do pallet_xcm call reserve transfer
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
@@ -275,6 +227,7 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
// check sent XCM ExportMessage to BridgeHub
let mut delivery_fees = 0;
// 1. check paid or unpaid
if let Some(expected_fee_asset_id) = maybe_paid_export_message {
xcm_sent
@@ -315,6 +268,10 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
.split_global()
.expect("split works");
assert_eq!(destination, &target_location_junctions_without_global_consensus);
// Call `SendXcm::validate` to get delivery fees.
delivery_fees = get_fungible_delivery_fees::<
<XcmConfig as xcm_executor::Config>::XcmSender,
>(target_location_from_different_consensus, inner_xcm.clone());
assert_matches_reserve_asset_deposited_instructions(
inner_xcm,
&expected_assets,
@@ -330,8 +287,8 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
assert_eq!(
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
alice_account_init_balance
.saturating_sub(existential_deposit)
.saturating_sub(balance_to_transfer.into())
.saturating_sub(delivery_fees.into())
);
// check reserve account increased by balance_to_transfer
@@ -341,14 +298,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
);
// check dedicated account increased by delivery fees (if configured)
if handling_delivery_fees {
if let Some(delivery_fees_account) = delivery_fees_account {
let delivery_fees_account_balance_after =
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
assert!(
delivery_fees_account_balance_after > delivery_fees_account_balance_before
);
}
if let Some(delivery_fees_account) = delivery_fees_account {
let delivery_fees_account_balance_after =
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
assert!(
delivery_fees_account_balance_after - delivery_fees.into() >=
delivery_fees_account_balance_before
);
}
})
}