Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xcm-executor: take transport fee from transferred assets if necessary #4834

Merged
4 changes: 3 additions & 1 deletion bridges/snowbridge/primitives/router/src/inbound/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,9 @@ where
}])),
// Perform a deposit reserve to send to destination chain.
DepositReserveAsset {
assets: Definite(vec![dest_para_fee_asset.clone(), asset].into()),
// Send over assets and unspent fees, XCM delivery fee will be charged from
// here.
assets: Wild(AllCounted(2)),
Comment on lines +361 to +363
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, should work for us!

dest: Location::new(1, [Parachain(dest_para_id)]),
xcm: vec![
// Buy execution on target.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubRococo,
vec![
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(pallet_xcm::Event::FeesPaid { .. }) => {},
]
);
Expand Down Expand Up @@ -274,7 +274,7 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) {
t.args.dest.clone()
),
},
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(
pallet_xcm::Event::FeesPaid { .. }
) => {},
Expand Down Expand Up @@ -305,7 +305,7 @@ fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) {
owner: *owner == t.sender.account_id,
balance: *balance == t.args.amount,
},
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(
pallet_xcm::Event::FeesPaid { .. }
) => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ mod imports {
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Westend>;
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubWestend>;
pub type RelayToParaThroughAHTest = Test<Westend, PenpalA, AssetHubWestend>;
pub type PenpalToRelayThroughAHTest = Test<PenpalA, Westend, AssetHubWestend>;
}

#[cfg(test)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -658,13 +658,13 @@ fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explici
}

// ===============================================================
// ===== Transfer - Native Asset - Relay->AssetHub->Parachain ====
// ====== Transfer - Native Asset - Relay->AssetHub->Penpal ======
// ===============================================================
/// Transfers of native asset Relay to Parachain (using AssetHub reserve). Parachains want to avoid
/// Transfers of native asset Relay to Penpal (using AssetHub reserve). Parachains want to avoid
/// managing SAs on all system chains, thus want all their DOT-in-reserve to be held in their
/// Sovereign Account on Asset Hub.
#[test]
fn transfer_native_asset_from_relay_to_para_through_asset_hub() {
fn transfer_native_asset_from_relay_to_penpal_through_asset_hub() {
// Init values for Relay
let destination = Westend::child_location_of(PenpalA::para_id());
let sender = WestendSender::get();
Expand Down Expand Up @@ -820,6 +820,137 @@ fn transfer_native_asset_from_relay_to_para_through_asset_hub() {
assert!(receiver_assets_after < receiver_assets_before + amount_to_send);
}

// ===============================================================
// ===== Transfer - Native Asset - Penpal->AssetHub->Relay =======
// ===============================================================
/// Transfers of native asset Penpal to Relay (using AssetHub reserve). Parachains want to avoid
/// managing SAs on all system chains, thus want all their DOT-in-reserve to be held in their
/// Sovereign Account on Asset Hub.
#[test]
fn transfer_native_asset_from_penpal_to_relay_through_asset_hub() {
// Init values for Penpal
let destination = RelayLocation::get();
let sender = PenpalASender::get();
let amount_to_send: Balance = WESTEND_ED * 100;

// Init values for Penpal
let relay_native_asset_location = RelayLocation::get();
let receiver = WestendReceiver::get();

// Init Test
let test_args = TestContext {
sender: sender.clone(),
receiver: receiver.clone(),
args: TestArgs::new_para(
destination.clone(),
receiver.clone(),
amount_to_send,
(Parent, amount_to_send).into(),
None,
0,
),
};
let mut test = PenpalToRelayThroughAHTest::new(test_args);

let sov_penpal_on_ah = AssetHubWestend::sovereign_account_id_of(
AssetHubWestend::sibling_location_of(PenpalA::para_id()),
);
// fund Penpal's sender account
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
relay_native_asset_location.clone(),
sender.clone(),
amount_to_send * 2,
);
// fund Penpal's SA on AssetHub with the assets held in reserve
AssetHubWestend::fund_accounts(vec![(sov_penpal_on_ah.clone().into(), amount_to_send * 2)]);

// prefund Relay checking account so we accept teleport "back" from AssetHub
let check_account =
Westend::execute_with(|| <Westend as WestendPallet>::XcmPallet::check_account());
Westend::fund_accounts(vec![(check_account, amount_to_send)]);

// Query initial balances
let sender_balance_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
});
let sov_penpal_on_ah_before = AssetHubWestend::execute_with(|| {
<AssetHubWestend as AssetHubWestendPallet>::Balances::free_balance(sov_penpal_on_ah.clone())
});
let receiver_balance_before = Westend::execute_with(|| {
<Westend as WestendPallet>::Balances::free_balance(receiver.clone())
});

fn transfer_assets_dispatchable(t: PenpalToRelayThroughAHTest) -> DispatchResult {
let fee_idx = t.args.fee_asset_item as usize;
let fee: Asset = t.args.assets.inner().get(fee_idx).cloned().unwrap();
let asset_hub_location = PenpalA::sibling_location_of(AssetHubWestend::para_id());
let context = PenpalUniversalLocation::get();

// reanchor fees to the view of destination (Westend Relay)
let mut remote_fees = fee.clone().reanchored(&t.args.dest, &context).unwrap();
if let Fungible(ref mut amount) = remote_fees.fun {
// we already spent some fees along the way, just use half of what we started with
*amount = *amount / 2;
}
let xcm_on_final_dest = Xcm::<()>(vec![
BuyExecution { fees: remote_fees, weight_limit: t.args.weight_limit.clone() },
DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
},
]);

// reanchor final dest (Westend Relay) to the view of hop (Asset Hub)
let mut dest = t.args.dest.clone();
dest.reanchor(&asset_hub_location, &context).unwrap();
// on Asset Hub
let xcm_on_hop = Xcm::<()>(vec![InitiateTeleport {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
dest,
xcm: xcm_on_final_dest,
}]);

// First leg is a reserve-withdraw, from there a teleport to final dest
<PenpalA as PenpalAPallet>::PolkadotXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(asset_hub_location.into()),
bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(xcm_on_hop)),
t.args.weight_limit,
)
}
test.set_dispatchable::<PenpalA>(transfer_assets_dispatchable);
test.assert();

// Query final balances
let sender_balance_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
});
let sov_penpal_on_ah_after = AssetHubWestend::execute_with(|| {
<AssetHubWestend as AssetHubWestendPallet>::Balances::free_balance(sov_penpal_on_ah.clone())
});
let receiver_balance_after = Westend::execute_with(|| {
<Westend as WestendPallet>::Balances::free_balance(receiver.clone())
});

// Sender's asset balance is reduced by amount sent plus delivery fees
acatangiu marked this conversation as resolved.
Show resolved Hide resolved
assert!(sender_balance_after < sender_balance_before - amount_to_send);
// SA on AH balance is decreased by `amount_to_send`
assert_eq!(sov_penpal_on_ah_after, sov_penpal_on_ah_before - amount_to_send);
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
// Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_balance_after < receiver_balance_before + amount_to_send);
}

// ==============================================================================================
// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Parachain<->AssetHub ====
// ==============================================================================================
Expand All @@ -839,7 +970,7 @@ fn bidirectional_transfer_multiple_assets_between_penpal_and_asset_hub() {
// xcm to be executed at dest
let xcm_on_dest = Xcm(vec![
// since this is the last hop, we don't need to further use any assets previously
// reserved for fees (there are no further hops to cover transport fees for); we
// reserved for fees (there are no further hops to cover delivery fees for); we
// RefundSurplus to get back any unspent fees
RefundSurplus,
DepositAsset { assets: Wild(All), beneficiary: t.args.beneficiary },
Expand Down Expand Up @@ -875,7 +1006,7 @@ fn bidirectional_transfer_multiple_assets_between_penpal_and_asset_hub() {
// xcm to be executed at dest
let xcm_on_dest = Xcm(vec![
// since this is the last hop, we don't need to further use any assets previously
// reserved for fees (there are no further hops to cover transport fees for); we
// reserved for fees (there are no further hops to cover delivery fees for); we
// RefundSurplus to get back any unspent fees
RefundSurplus,
DepositAsset { assets: Wild(All), beneficiary: t.args.beneficiary },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) {
assert_expected_events!(
AssetHubWestend,
vec![
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(pallet_xcm::Event::FeesPaid { .. }) => {},
]
);
Expand Down Expand Up @@ -274,7 +274,7 @@ fn system_para_to_para_assets_sender_assertions(t: SystemParaToParaTest) {
t.args.dest.clone()
),
},
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(
pallet_xcm::Event::FeesPaid { .. }
) => {},
Expand Down Expand Up @@ -305,7 +305,7 @@ fn para_to_system_para_assets_sender_assertions(t: ParaToSystemParaTest) {
owner: *owner == t.sender.account_id,
balance: *balance == t.args.amount,
},
// Transport fees are paid
// Delivery fees are paid
RuntimeEvent::PolkadotXcm(
pallet_xcm::Event::FeesPaid { .. }
) => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn transfer_and_transact_in_same_xcm(
Transact { origin_kind: OriginKind::Xcm, call, fallback_max_weight: None },
ExpectTransactStatus(MaybeErrorCode::Success),
// since this is the last hop, we don't need to further use any assets previously
// reserved for fees (there are no further hops to cover transport fees for); we
// reserved for fees (there are no further hops to cover delivery fees for); we
// RefundSurplus to get back any unspent fees
RefundSurplus,
DepositAsset { assets: Wild(All), beneficiary },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use crate::tests::*;

fn send_assets_over_bridge<F: FnOnce()>(send_fn: F) {
// fund the AHR's SA on BHR for paying bridge transport fees
// fund the AHR's SA on BHR for paying bridge delivery fees
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), 10_000_000_000_000u128);

// set XCM versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn register_rococo_asset_on_wah_from_rah() {

let destination = asset_hub_westend_location();

// fund the RAH's SA on RBH for paying bridge transport fees
// fund the RAH's SA on RBH for paying bridge delivery fees
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), 10_000_000_000_000u128);

// set XCM versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
let native_token = Location::parent();
let amount = ASSET_HUB_ROCOCO_ED * 1_000;

// fund the AHR's SA on BHR for paying bridge transport fees
// fund the AHR's SA on BHR for paying bridge delivery fees
BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), 10_000_000_000_000u128);
// fund sender
AssetHubRococo::fund_accounts(vec![(AssetHubRococoSender::get().into(), amount * 10)]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{create_pool_with_native_on, tests::*};
use xcm::latest::AssetTransferFilter;

fn send_assets_over_bridge<F: FnOnce()>(send_fn: F) {
// fund the AHW's SA on BHW for paying bridge transport fees
// fund the AHW's SA on BHW for paying bridge delivery fees
BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), 10_000_000_000_000u128);

// set XCM versions
Expand Down Expand Up @@ -592,7 +592,7 @@ fn do_send_pens_and_wnds_from_penpal_westend_via_ahw_to_asset_hub_rococo(
// XCM to be executed at dest (Rococo Asset Hub)
let xcm_on_dest = Xcm(vec![
// since this is the last hop, we don't need to further use any assets previously
// reserved for fees (there are no further hops to cover transport fees for); we
// reserved for fees (there are no further hops to cover delivery fees for); we
// RefundSurplus to get back any unspent fees
RefundSurplus,
// deposit everything to final beneficiary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fn register_asset_on_rah_from_wah(bridged_asset_at_rah: Location) {

let destination = asset_hub_rococo_location();

// fund the WAH's SA on WBH for paying bridge transport fees
// fund the WAH's SA on WBH for paying bridge delivery fees
BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), 10_000_000_000_000u128);

// set XCM versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
let native_token = Location::parent();
let amount = ASSET_HUB_WESTEND_ED * 1_000;

// fund the AHR's SA on BHR for paying bridge transport fees
// fund the AHR's SA on BHR for paying bridge delivery fees
BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), 10_000_000_000_000u128);
// fund sender
AssetHubWestend::fund_accounts(vec![(AssetHubWestendSender::get().into(), amount * 10)]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn transfer_and_transact_in_same_xcm(
Transact { origin_kind: OriginKind::Xcm, call, fallback_max_weight: None },
ExpectTransactStatus(MaybeErrorCode::Success),
// since this is the last hop, we don't need to further use any assets previously
// reserved for fees (there are no further hops to cover transport fees for); we
// reserved for fees (there are no further hops to cover delivery fees for); we
// RefundSurplus to get back any unspent fees
RefundSurplus,
DepositAsset { assets: Wild(All), beneficiary },
Expand Down
Loading
Loading