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

[XCMv5] Better fee mechanism #5420

Merged
merged 54 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3d7e24a
feat(xcm): add PayFees instruction
franciscoaguirre Aug 20, 2024
0a88320
chore(xcm-executor): clean up executor unit test and move everything …
franciscoaguirre Aug 21, 2024
0b16ca5
Merge branch 'xcm-v5' into xcm-pay-fees
franciscoaguirre Aug 21, 2024
2393087
feat(xcm-executor): tests for delivery fees and old BuyExecution
franciscoaguirre Aug 21, 2024
b9ca9e0
feat(xcm-executor): interaction between PayFees and RefundSurplus
franciscoaguirre Aug 21, 2024
22efce5
fix(xcm): add builder(pays_fees) to old xcm versions
franciscoaguirre Aug 21, 2024
49f63a4
chore(xcm-executor): unhappy tests for PayFees
franciscoaguirre Aug 21, 2024
50b06d6
chore(xcm-executor): test that leftover fees are also trapped
franciscoaguirre Aug 23, 2024
738a8c1
chore: add made-up benchmarks to all runtimes
franciscoaguirre Aug 26, 2024
995cc09
feat(xcm-builder): allow PayFees in AllowTopLevelPaidExecutionFrom ba…
franciscoaguirre Aug 26, 2024
679c3c7
feat(xcm-executor): use fees register in DepositReserveAsset
franciscoaguirre Aug 26, 2024
d934c64
feat(xcm-emulator): add set_call method for dispatching calls
franciscoaguirre Aug 26, 2024
52f3ba1
test(asset-hub-westend-integration-tests): add xcm estimation test wi…
franciscoaguirre Aug 26, 2024
900bb46
fix: fmt
franciscoaguirre Aug 26, 2024
56bcd4d
fix(xcm-executor): remove unnecessary clones
franciscoaguirre Aug 27, 2024
c062c0c
chore(asset-hub-westend-integration-tests): refactor test to decl macro
franciscoaguirre Aug 27, 2024
94f831f
feat(asset-hub-rococo-integration-tests): add PayFees test
franciscoaguirre Aug 27, 2024
3198950
fix: fmt
franciscoaguirre Aug 27, 2024
6bf562b
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=ro…
Aug 27, 2024
15a5333
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=we…
Aug 27, 2024
fb79e69
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=co…
Aug 27, 2024
4c9dddc
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=as…
Aug 27, 2024
a8d5d1d
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=as…
Aug 27, 2024
c1c95dc
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=co…
Aug 27, 2024
0c5afe3
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=br…
Aug 27, 2024
338d8de
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=br…
Aug 27, 2024
12164f8
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=pe…
Aug 27, 2024
c3b6bd0
chore: add missing benchmark and made up benchmarks for all runtimes
franciscoaguirre Aug 27, 2024
e2c620e
fix: remove duplicated weight
franciscoaguirre Aug 27, 2024
86b151c
fix: remove unused imports
franciscoaguirre Aug 27, 2024
2937662
Merge branch 'xcm-v5' into xcm-pay-fees
franciscoaguirre Aug 27, 2024
0eaa9fb
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=we…
Aug 27, 2024
39cf7b1
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=ro…
Aug 27, 2024
abe36c3
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=co…
Aug 27, 2024
a60172c
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=as…
Aug 27, 2024
8470fca
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=as…
Aug 27, 2024
6a59c1d
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=br…
Aug 27, 2024
bdae9ec
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=co…
Aug 27, 2024
6b339a0
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=br…
Aug 27, 2024
02b0fbb
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=pe…
Aug 27, 2024
ec92154
".git/.scripts/commands/bench/bench.sh" --subcommand=xcm --runtime=pe…
Aug 27, 2024
1091ac7
doc: add prdoc
franciscoaguirre Aug 30, 2024
f0f1aac
fix: prdoc
franciscoaguirre Aug 30, 2024
d32a028
Merge branch 'xcm-v5' into xcm-pay-fees
franciscoaguirre Sep 4, 2024
62e71ca
Merge branch 'xcm-v5' into xcm-pay-fees
acatangiu Oct 1, 2024
919107e
chore: address feedback
franciscoaguirre Oct 7, 2024
f10feb0
".git/.scripts/commands/fmt/fmt.sh"
Oct 7, 2024
a9334fa
fix: if no delivery fees are defined, xcm should still work
franciscoaguirre Oct 7, 2024
adbec21
".git/.scripts/commands/fmt/fmt.sh"
Oct 7, 2024
66afb67
fix auto-swap delivery fee when using PayFees (#5955)
acatangiu Oct 7, 2024
69dd94d
fix: change some xcm licenses from Substrate to Polkadot
franciscoaguirre Oct 7, 2024
193c2bf
fix: change v4 to latest in benchmarks
franciscoaguirre Oct 7, 2024
2cc969b
chore: semver fix in prdoc
franciscoaguirre Oct 8, 2024
8b49830
Use `Versioned*::from` instead of concrete `V*` when using `xcm::late…
bkontur Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions cumulus/parachains/integration-tests/emulated/common/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,227 @@ macro_rules! test_chain_can_claim_assets {
}
};
}

#[macro_export]
macro_rules! test_can_estimate_and_pay_exact_fees {
( $sender_para:ty, $asset_hub:ty, $receiver_para:ty, ($asset_id:expr, $amount:expr), $owner_prefix:ty ) => {
$crate::macros::paste::paste! {
// We first define the call we'll use throughout the test.
fn get_call(
estimated_local_fees: impl Into<Asset>,
estimated_intermediate_fees: impl Into<Asset>,
estimated_remote_fees: impl Into<Asset>,
) -> <$sender_para as Chain>::RuntimeCall {
type RuntimeCall = <$sender_para as Chain>::RuntimeCall;

let beneficiary = [<$receiver_para Receiver>]::get();
let xcm_in_destination = Xcm::<()>::builder_unsafe()
.pay_fees(estimated_remote_fees)
.deposit_asset(AllCounted(1), beneficiary)
.build();
let ah_to_receiver = $asset_hub::sibling_location_of($receiver_para::para_id());
let xcm_in_reserve = Xcm::<()>::builder_unsafe()
.pay_fees(estimated_intermediate_fees)
.deposit_reserve_asset(
AllCounted(1),
ah_to_receiver,
xcm_in_destination,
)
.build();
let sender_to_ah = $sender_para::sibling_location_of($asset_hub::para_id());
let local_xcm = Xcm::<<$sender_para as Chain>::RuntimeCall>::builder()
.withdraw_asset(($asset_id, $amount))
.pay_fees(estimated_local_fees)
.initiate_reserve_withdraw(AllCounted(1), sender_to_ah, xcm_in_reserve)
.build();

RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute {
message: bx!(VersionedXcm::from(local_xcm)),
max_weight: Weight::from_parts(10_000_000_000, 500_000),
})
}

let destination = $sender_para::sibling_location_of($receiver_para::para_id());
let sender = [<$sender_para Sender>]::get();
let sender_as_seen_by_ah = $asset_hub::sibling_location_of($sender_para::para_id());
let sov_of_sender_on_ah = $asset_hub::sovereign_account_id_of(sender_as_seen_by_ah.clone());
let asset_owner = [<$owner_prefix AssetOwner>]::get();

// Fund parachain's sender account.
$sender_para::mint_foreign_asset(
<$sender_para as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
$asset_id.clone().into(),
sender.clone(),
$amount * 2,
);

// Fund the parachain origin's SA on Asset Hub with the native tokens.
$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah.clone(), $amount * 2)]);

let beneficiary_id = [<$receiver_para Receiver>]::get();

let test_args = TestContext {
sender: sender.clone(),
receiver: beneficiary_id.clone(),
args: TestArgs::new_para(
destination,
beneficiary_id.clone(),
$amount,
($asset_id, $amount).into(),
None,
0,
),
};
let mut test = ParaToParaThroughAHTest::new(test_args);

// We get these from the closure.
let mut local_execution_fees = 0;
let mut local_delivery_fees = 0;
let mut remote_message = VersionedXcm::from(Xcm::<()>(Vec::new()));
<$sender_para as TestExt>::execute_with(|| {
type Runtime = <$sender_para as Chain>::Runtime;
type OriginCaller = <$sender_para as Chain>::OriginCaller;

let call = get_call(
(Parent, 100_000_000_000u128),
(Parent, 100_000_000_000u128),
(Parent, 100_000_000_000u128),
);
let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
let result = Runtime::dry_run_call(origin, call).unwrap();
let local_xcm = result.local_xcm.unwrap().clone();
let local_xcm_weight = Runtime::query_xcm_weight(local_xcm).unwrap();
local_execution_fees = Runtime::query_weight_to_asset_fee(
local_xcm_weight,
VersionedAssetId::V5(Location::parent().into()),
bkontur marked this conversation as resolved.
Show resolved Hide resolved
)
.unwrap();
// We filter the result to get only the messages we are interested in.
let (destination_to_query, messages_to_query) = &result
.forwarded_xcms
.iter()
.find(|(destination, _)| {
*destination == VersionedLocation::V5(Location::new(1, [Parachain(1000)]))
})
.unwrap();
assert_eq!(messages_to_query.len(), 1);
remote_message = messages_to_query[0].clone();
let delivery_fees =
Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone())
.unwrap();
local_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
});

// These are set in the AssetHub closure.
let mut intermediate_execution_fees = 0;
let mut intermediate_delivery_fees = 0;
let mut intermediate_remote_message = VersionedXcm::V5(Xcm::<()>(Vec::new()));
<$asset_hub as TestExt>::execute_with(|| {
type Runtime = <$asset_hub as Chain>::Runtime;
type RuntimeCall = <$asset_hub as Chain>::RuntimeCall;

// First we get the execution fees.
let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
intermediate_execution_fees = Runtime::query_weight_to_asset_fee(
weight,
VersionedAssetId::V5(Location::new(1, []).into()),
)
.unwrap();

// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
let xcm_program =
VersionedXcm::V5(Xcm::<RuntimeCall>::from(remote_message.clone().try_into().unwrap()));

// Now we get the delivery fees to the final destination.
let result =
Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
let (destination_to_query, messages_to_query) = &result
.forwarded_xcms
.iter()
.find(|(destination, _)| {
*destination == VersionedLocation::V5(Location::new(1, [Parachain(2001)]))
})
.unwrap();
// There's actually two messages here.
// One created when the message we sent from `$sender_para` arrived and was executed.
// The second one when we dry-run the xcm.
// We could've gotten the message from the queue without having to dry-run, but
// offchain applications would have to dry-run, so we do it here as well.
intermediate_remote_message = messages_to_query[0].clone();
let delivery_fees = Runtime::query_delivery_fees(
destination_to_query.clone(),
intermediate_remote_message.clone(),
)
.unwrap();
intermediate_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees);
});

// Get the final execution fees in the destination.
let mut final_execution_fees = 0;
<$receiver_para as TestExt>::execute_with(|| {
type Runtime = <$sender_para as Chain>::Runtime;

let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap();
final_execution_fees =
Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V5(Parent.into()))
.unwrap();
});

// Dry-running is done.
$sender_para::reset_ext();
$asset_hub::reset_ext();
$receiver_para::reset_ext();

// Fund accounts again.
$sender_para::mint_foreign_asset(
<$sender_para as Chain>::RuntimeOrigin::signed(asset_owner),
$asset_id.clone().into(),
sender.clone(),
$amount * 2,
);
$asset_hub::fund_accounts(vec![(sov_of_sender_on_ah, $amount * 2)]);

// Actually run the extrinsic.
let sender_assets_before = $sender_para::execute_with(|| {
type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &sender)
});
let receiver_assets_before = $receiver_para::execute_with(|| {
type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &beneficiary_id)
});

test.set_assertion::<$sender_para>(sender_assertions);
test.set_assertion::<$asset_hub>(hop_assertions);
test.set_assertion::<$receiver_para>(receiver_assertions);
let call = get_call(
(Parent, local_execution_fees + local_delivery_fees),
(Parent, intermediate_execution_fees + intermediate_delivery_fees),
(Parent, final_execution_fees),
);
test.set_call(call);
test.assert();

let sender_assets_after = $sender_para::execute_with(|| {
type ForeignAssets = <$sender_para as [<$sender_para Pallet>]>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance($asset_id.clone().into(), &sender)
});
let receiver_assets_after = $receiver_para::execute_with(|| {
type ForeignAssets = <$receiver_para as [<$receiver_para Pallet>]>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance($asset_id.into(), &beneficiary_id)
});

// We know the exact fees on every hop.
assert_eq!(sender_assets_after, sender_assets_before - $amount);
assert_eq!(
receiver_assets_after,
receiver_assets_before + $amount -
local_execution_fees -
local_delivery_fees -
intermediate_execution_fees -
intermediate_delivery_fees -
final_execution_fees
);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@
//! Tests for XCM fee estimation in the runtime.

use crate::imports::*;
use frame_support::{
dispatch::RawOrigin,
sp_runtime::{traits::Dispatchable, DispatchResult},
};
use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees;
use frame_support::dispatch::RawOrigin;
use xcm_runtime_apis::{
dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
Expand Down Expand Up @@ -76,16 +74,6 @@ fn receiver_assertions(test: ParaToParaThroughAHTest) {
);
}

fn transfer_assets_para_to_para_through_ah_dispatchable(
test: ParaToParaThroughAHTest,
) -> DispatchResult {
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
match call.dispatch(test.signed_origin) {
Ok(_) => Ok(()),
Err(error_with_post_info) => Err(error_with_post_info.error),
}
}

fn transfer_assets_para_to_para_through_ah_call(
test: ParaToParaThroughAHTest,
) -> <PenpalA as Chain>::RuntimeCall {
Expand Down Expand Up @@ -257,7 +245,8 @@ fn multi_hop_works() {
test.set_assertion::<PenpalA>(sender_assertions);
test.set_assertion::<AssetHubRococo>(hop_assertions);
test.set_assertion::<PenpalB>(receiver_assertions);
test.set_dispatchable::<PenpalA>(transfer_assets_para_to_para_through_ah_dispatchable);
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
test.set_call(call);
test.assert();

let sender_assets_after = PenpalA::execute_with(|| {
Expand All @@ -284,3 +273,14 @@ fn multi_hop_works() {
final_execution_fees
);
}

#[test]
fn multi_hop_pay_fees_works() {
test_can_estimate_and_pay_exact_fees!(
PenpalA,
AssetHubRococo,
PenpalB,
(Parent, 1_000_000_000_000u128),
Penpal
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@

use crate::imports::*;

use frame_support::{
dispatch::RawOrigin,
sp_runtime::{traits::Dispatchable, DispatchResult},
};
use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees;
use frame_support::dispatch::RawOrigin;
use xcm_runtime_apis::{
dry_run::runtime_decl_for_dry_run_api::DryRunApiV1,
fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1,
Expand Down Expand Up @@ -77,22 +75,12 @@ fn receiver_assertions(test: ParaToParaThroughAHTest) {
);
}

fn transfer_assets_para_to_para_through_ah_dispatchable(
test: ParaToParaThroughAHTest,
) -> DispatchResult {
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
match call.dispatch(test.signed_origin) {
Ok(_) => Ok(()),
Err(error_with_post_info) => Err(error_with_post_info.error),
}
}

fn transfer_assets_para_to_para_through_ah_call(
test: ParaToParaThroughAHTest,
) -> <PenpalA as Chain>::RuntimeCall {
type RuntimeCall = <PenpalA as Chain>::RuntimeCall;

let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubWestend::para_id());
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id());
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(test.args.assets.len() as u32)),
beneficiary: test.args.beneficiary,
Expand All @@ -101,7 +89,7 @@ fn transfer_assets_para_to_para_through_ah_call(
dest: bx!(test.args.dest.into()),
assets: bx!(test.args.assets.clone().into()),
assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
remote_fees_id: bx!(VersionedAssetId::V5(AssetId(Location::new(1, [])))),
remote_fees_id: bx!(VersionedAssetId::V5(AssetId(Location::parent()))),
fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())),
custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)),
weight_limit: test.args.weight_limit,
Expand Down Expand Up @@ -259,7 +247,8 @@ fn multi_hop_works() {
test.set_assertion::<PenpalA>(sender_assertions);
test.set_assertion::<AssetHubWestend>(hop_assertions);
test.set_assertion::<PenpalB>(receiver_assertions);
test.set_dispatchable::<PenpalA>(transfer_assets_para_to_para_through_ah_dispatchable);
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
test.set_call(call);
test.assert();

let sender_assets_after = PenpalA::execute_with(|| {
Expand All @@ -286,3 +275,14 @@ fn multi_hop_works() {
final_execution_fees
);
}

#[test]
fn multi_hop_pay_fees_works() {
test_can_estimate_and_pay_exact_fees!(
PenpalA,
AssetHubWestend,
PenpalB,
(Parent, 1_000_000_000_000u128),
Penpal
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ impl<Call> XcmWeightInfo<Call> for AssetHubRococoXcmWeight<Call> {
fn buy_execution(_fees: &Asset, _weight_limit: &WeightLimit) -> Weight {
XcmGeneric::<Runtime>::buy_execution()
}
fn pay_fees(_asset: &Asset) -> Weight {
XcmGeneric::<Runtime>::pay_fees()
}
fn refund_surplus() -> Weight {
XcmGeneric::<Runtime>::refund_surplus()
}
Expand Down
Loading
Loading