diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 1509be680975e..9d0e595c47a3c 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -55,7 +55,7 @@ pub use frame_support::{ }; pub use xcm_runtime_apis::{ dry_run::runtime_decl_for_dry_run_api::DryRunApiV2, - fees::{runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError}, + fees::{runtime_decl_for_xcm_payment_api::XcmPaymentApiV2, Error as XcmPaymentApiError}, }; pub use frame_support::traits::{fungible::Mutate, fungibles::Inspect, Currency}; @@ -124,9 +124,15 @@ macro_rules! test_parachain_is_trusted_teleporter { .unwrap(); assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); + // The native asset of every system chain. + // We want to get the delivery fees in this asset. + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::new(1, [])); let delivery_fees = - >::query_delivery_fees(destination_to_query.clone(), - remote_message.clone()).unwrap(); + >::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees + ).unwrap(); let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap(); let $crate::macros::Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else { unreachable!("asset is non-fungible"); @@ -261,9 +267,15 @@ macro_rules! test_relay_is_trusted_teleporter { .unwrap(); assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); + // The native asset of every system chain. + // We want to get the delivery fees in this asset. + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::new(0, [])); let delivery_fees = - >::query_delivery_fees(destination_to_query.clone(), - remote_message.clone()).unwrap(); + >::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees + ).unwrap(); let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap(); let $crate::macros::Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else { unreachable!("asset is non-fungible"); @@ -406,9 +418,15 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { .unwrap(); assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); + // The native asset of every system chain. + // We want to get the delivery fees in this asset. + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); let delivery_fees = - >::query_delivery_fees(destination_to_query.clone(), - remote_message.clone()).unwrap(); + >::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees + ).unwrap(); let latest_delivery_fees: $crate::macros::Assets = delivery_fees.clone().try_into().unwrap(); delivery_fees_amount = if let Some(first_asset) = latest_delivery_fees.inner().first() { let $crate::macros::Fungible(inner_delivery_fees_amount) = first_asset.fun else { @@ -689,8 +707,8 @@ macro_rules! test_can_estimate_and_pay_exact_fees { let result = >::dry_run_call(origin, call.clone(), $crate::macros::XCM_VERSION).unwrap(); let local_xcm = result.local_xcm.unwrap().clone(); - let local_xcm_weight = >::query_xcm_weight(local_xcm).unwrap(); - local_execution_fees = >::query_weight_to_asset_fee( + let local_xcm_weight = >::query_xcm_weight(local_xcm).unwrap(); + local_execution_fees = >::query_weight_to_asset_fee( local_xcm_weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::parent())), ) @@ -706,9 +724,13 @@ macro_rules! test_can_estimate_and_pay_exact_fees { .unwrap(); assert_eq!(messages_to_query.len(), 1); remote_message = messages_to_query[0].clone(); + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); let delivery_fees = - >::query_delivery_fees(destination_to_query.clone(), - remote_message.clone()).unwrap(); + >::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees + ).unwrap(); local_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees); }); @@ -721,8 +743,8 @@ macro_rules! test_can_estimate_and_pay_exact_fees { type RuntimeCall = <$asset_hub as $crate::macros::Chain>::RuntimeCall; // First we get the execution fees. - let weight = >::query_xcm_weight(remote_message.clone()).unwrap(); - intermediate_execution_fees = >::query_weight_to_asset_fee( + let weight = >::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = >::query_weight_to_asset_fee( weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::new(1, []))), ) @@ -750,9 +772,11 @@ macro_rules! test_can_estimate_and_pay_exact_fees { // 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 = >::query_delivery_fees( + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); + let delivery_fees = >::query_delivery_fees( destination_to_query.clone(), intermediate_remote_message.clone(), + asset_id_for_delivery_fees, ) .unwrap(); intermediate_delivery_fees = $crate::xcm_helpers::get_amount_from_versioned_assets(delivery_fees); @@ -763,10 +787,10 @@ macro_rules! test_can_estimate_and_pay_exact_fees { <$receiver_para as $crate::macros::TestExt>::execute_with(|| { type Runtime = <$sender_para as $crate::macros::Chain>::Runtime; - let weight = >::query_xcm_weight( + let weight = >::query_xcm_weight( intermediate_remote_message.clone()).unwrap(); final_execution_fees = - >::query_weight_to_asset_fee(weight, + >::query_weight_to_asset_fee(weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId($crate::macros::Location::parent()))) .unwrap(); }); @@ -903,7 +927,7 @@ macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { )); type Runtime = <$asset_hub as $crate::macros::Chain>::Runtime; - let acceptable_payment_assets = >::query_acceptable_payment_assets( + let acceptable_payment_assets = >::query_acceptable_payment_assets( $crate::macros::XCM_VERSION).unwrap(); assert_eq!(acceptable_payment_assets, vec![ $crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone())), @@ -915,12 +939,12 @@ macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { .buy_execution(($crate::macros::Parent, 10u128), $crate::macros::Unlimited) .deposit_asset($crate::macros::All, [0u8; 32]) .build(); - let weight = >::query_xcm_weight( + let weight = >::query_xcm_weight( $crate::macros::VersionedXcm::from(program)).unwrap(); - let fee_in_wnd = >::query_weight_to_asset_fee(weight, + let fee_in_wnd = >::query_weight_to_asset_fee(weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId(wnd.clone()))).unwrap(); // Assets not in a pool don't work. - assert!(>::query_weight_to_asset_fee(weight, + assert!(>::query_weight_to_asset_fee(weight, $crate::macros::VersionedAssetId::from( $crate::macros::AssetId($crate::macros::Location::new(0, [$crate::macros::PalletInstance($crate::macros::ASSETS_PALLET_ID), @@ -929,7 +953,7 @@ macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { ) ) ).is_err()); - let fee_in_usdt_fail = >::query_weight_to_asset_fee(weight, + let fee_in_usdt_fail = >::query_weight_to_asset_fee(weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt.clone()))); // Weight to asset fee fails because there's not enough asset in the pool. // We just created it, there's none. @@ -953,7 +977,7 @@ macro_rules! test_xcm_fee_querying_apis_work_for_asset_hub { sender.into() )); // Now it works. - let fee_in_usdt = >::query_weight_to_asset_fee(weight, + let fee_in_usdt = >::query_weight_to_asset_fee(weight, $crate::macros::VersionedAssetId::from($crate::macros::AssetId(usdt)) ); $crate::macros::assert_ok!(fee_in_usdt); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs index 6e3b893e3f6c7..843d1b38c0395 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/xcm_fee_estimation.rs @@ -20,7 +20,7 @@ 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::DryRunApiV2, - fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2, }; fn sender_assertions(test: ParaToParaThroughAHTest) { @@ -157,9 +157,13 @@ fn multi_hop_works() { .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(); + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees, + ) + .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); }); @@ -200,9 +204,11 @@ fn multi_hop_works() { // 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 asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); let delivery_fees = Runtime::query_delivery_fees( destination_to_query.clone(), intermediate_remote_message.clone(), + asset_id_for_delivery_fees, ) .unwrap(); intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs index 7deaaa9a984f9..3fc49ae4e6f20 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/xcm_fee_estimation.rs @@ -15,15 +15,66 @@ //! Tests to ensure correct XCM fee estimation for cross-chain asset transfers. -use crate::imports::*; +use crate::{create_pool_with_wnd_on, imports::*}; 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::DryRunApiV2, - fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2, }; +fn usdt_transfer_call( + destination: Location, + beneficiary: Location, + amount_to_send: u128, + usdt_location_on_penpal: Location, + usdt_location_on_ah: Location, +) -> ::RuntimeCall { + let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + + // Create the XCM to transfer USDT to PenpalB via Asset Hub using InitiateTransfer + let remote_xcm_on_penpal_b = + Xcm::<()>(vec![DepositAsset { assets: Wild(AllCounted(1)), beneficiary }]); + + let xcm_on_asset_hub = Xcm::<()>(vec![InitiateTransfer { + destination, + remote_fees: Some(AssetTransferFilter::ReserveDeposit( + Definite((usdt_location_on_ah, 1_000_000u128).into()), // 1 USDT for fees + )), + preserve_origin: false, + assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Wild(All))]), + remote_xcm: remote_xcm_on_penpal_b, + }]); + + let xcm = Xcm::<::RuntimeCall>(vec![ + WithdrawAsset((usdt_location_on_penpal.clone(), amount_to_send).into()), + PayFees { + asset: Asset { + id: AssetId(usdt_location_on_penpal.clone()), + fun: Fungible(1_000_000u128), // 1 USDT for local fees + }, + }, + InitiateTransfer { + destination: asset_hub_location, + remote_fees: Some(AssetTransferFilter::ReserveWithdraw( + Definite((usdt_location_on_penpal.clone(), 1_000_000u128).into()), /* 1 USDT for + * Asset Hub fees */ + )), + preserve_origin: false, + assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(Wild( + All, + ))]), + remote_xcm: xcm_on_asset_hub, + }, + ]); + + ::RuntimeCall::PolkadotXcm(pallet_xcm::Call::execute { + message: bx!(VersionedXcm::from(xcm)), + max_weight: Weight::MAX, + }) +} + fn sender_assertions(test: ParaToParaThroughAHTest) { type RuntimeEvent = ::RuntimeEvent; PenpalA::assert_xcm_pallet_attempted_complete(None); @@ -85,11 +136,20 @@ fn transfer_assets_para_to_para_through_ah_call( assets: Wild(AllCounted(test.args.assets.len() as u32)), beneficiary: test.args.beneficiary, }]); + let remote_fee_id: AssetId = test + .args + .assets + .clone() + .into_inner() + .get(test.args.fee_asset_item as usize) + .expect("asset in index fee_asset_item should exist") + .clone() + .id; RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets_using_type_and_then { 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::from(AssetId(Location::parent()))), + remote_fees_id: bx!(VersionedAssetId::from(remote_fee_id)), 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, @@ -159,9 +219,13 @@ fn multi_hop_works() { .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(); + let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + asset_id_for_delivery_fees, + ) + .unwrap(); delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); }); @@ -202,9 +266,11 @@ fn multi_hop_works() { // 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 asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent()); let delivery_fees = Runtime::query_delivery_fees( destination_to_query.clone(), intermediate_remote_message.clone(), + asset_id_for_delivery_fees, ) .unwrap(); intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); @@ -287,3 +353,132 @@ fn multi_hop_pay_fees_works() { Penpal ); } + +/// We are able to estimate delivery fees in USDT for a USDT transfer from PenpalA to PenpalB via +/// Asset Hub. Scenario: Alice on PenpalA has some USDT and wants to send them to PenpalB. +/// We want to estimate the delivery fees in USDT using the new `asset_id` parameter in +/// `query_delivery_fees`. +#[test] +fn usdt_fee_estimation_in_usdt_works() { + use emulated_integration_tests_common::USDT_ID; + + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send = 10_000_000; // 10 USDT (6 decimals) + + // USDT location from PenpalA's perspective + let usdt_location_on_penpal = PenpalUsdtFromAssetHub::get(); + + // USDT location from Asset Hub's perspective + let usdt_location_on_ah = + Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]); + + let penpal_as_seen_by_ah = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_penpal_on_ah = + AssetHubWestend::sovereign_account_id_of(penpal_as_seen_by_ah.clone()); + + // fund PenpalA's sender account with USDT + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_location_on_penpal.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund PenpalA's sovereign account on AssetHub with USDT + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + USDT_ID, + sov_of_penpal_on_ah.clone(), + amount_to_send * 2, + ); + + // Create a liquidity pool between WND (relay token) and USDT on AssetHub + // This is needed for the asset conversion in fee estimation + create_pool_with_wnd_on!( + AssetHubWestend, + usdt_location_on_ah.clone(), + false, + AssetHubWestendSender::get(), + 1_000_000_000_000, // 1 WND + 2_000_000 // 2 USDT (1:2 ratio) + ); + + // Create a liquidity pool between WND and USDT on PenpalA as well + // This is needed for PenpalA to perform asset conversion for fee estimation + create_pool_with_wnd_on!( + PenpalA, + usdt_location_on_penpal.clone(), + true, + PenpalAssetOwner::get(), + 1_000_000_000_000, // 1 WND + 2_000_000 // 2 USDT (1:2 ratio) + ); + + let beneficiary_id = PenpalBReceiver::get(); + + // We get the delivery fees from the PenpalA closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::from(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type OriginCaller = ::OriginCaller; + + let call = usdt_transfer_call( + destination.clone(), + beneficiary_id.clone().into(), + amount_to_send, + usdt_location_on_penpal.clone(), + usdt_location_on_ah.clone(), + ); + + let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubWestend::para_id()); + + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap(); + + // Find the message sent to Asset Hub + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::from(asset_hub_location.clone()) + }) + .unwrap(); + + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + + // Query delivery fees in USDT using the new asset_id parameter + let usdt_asset_id = VersionedAssetId::from(AssetId(usdt_location_on_penpal.clone())); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + remote_message.clone(), + usdt_asset_id, + ) + .unwrap(); + + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees.clone()); + + // Verify the fees are quoted in USDT (the delivery fees should be converted from native to + // USDT) + let fee_assets = match delivery_fees { + VersionedAssets::V5(assets) => assets, + _ => panic!("Expected V5 assets"), + }; + + // Should have one asset (USDT) + assert_eq!(fee_assets.len(), 1); + let fee_asset = fee_assets.get(0).unwrap(); + + // Verify it's USDT + assert_eq!(fee_asset.id.0, usdt_location_on_penpal); + + // Verify we get a reasonable USDT amount (delivery fees should be > 0) + if let Fungible(amount) = fee_asset.fun { + assert!(amount > 0, "Delivery fees should be greater than 0"); + } else { + panic!("Expected fungible delivery fees"); + } + }); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 302db52e4945f..20d974af4b91b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -86,7 +86,7 @@ use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::Weigh use xcm_config::{ ForeignAssetsConvertedConcreteId, GovernanceLocation, LocationToAccountId, PoolAssetsConvertedConcreteId, PoolAssetsPalletLocation, TokenLocation, - TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, + TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, XcmConfig, }; #[cfg(test)] @@ -1565,10 +1565,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -1576,8 +1573,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index c39a2dab7fbab..19aead366c0ce 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -96,7 +96,7 @@ use westend_runtime_constants::time::DAYS as RC_DAYS; use xcm_config::{ ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId, PoolAssetsPalletLocation, TrustBackedAssetsConvertedConcreteId, - TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin, + TrustBackedAssetsPalletLocation, WestendLocation, XcmConfig, XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -1940,10 +1940,7 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!( } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -1951,8 +1948,9 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!( PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index e7a98c0385990..c1a549382f80a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -1642,7 +1642,7 @@ fn governance_authorize_upgrade_works() { #[test] fn weight_of_message_increases_when_dealing_with_erc20s() { use xcm::VersionedXcm; - use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1; + use xcm_runtime_apis::fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2; let message = Xcm::<()>::builder_unsafe().withdraw_asset((Parent, 100u128)).build(); let versioned = VersionedXcm::<()>::V5(message); let regular_asset_weight = Runtime::query_xcm_weight(versioned).unwrap(); diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index e6a0aea3605e3..400f7e5028dbc 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -44,7 +44,7 @@ use xcm_executor::{ XcmExecutor, }; use xcm_runtime_apis::fees::{ - runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError, + runtime_decl_for_xcm_payment_api::XcmPaymentApiV2, Error as XcmPaymentApiError, }; type RuntimeHelper = @@ -1634,7 +1634,7 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< pub fn xcm_payment_api_with_pools_works() where - Runtime: XcmPaymentApiV1 + Runtime: XcmPaymentApiV2 + frame_system::Config + pallet_balances::Config + pallet_session::Config @@ -1821,7 +1821,7 @@ pub fn xcm_payment_api_foreign_asset_pool_works< existential_deposit: Balance, another_network_genesis_hash: [u8; 32], ) where - Runtime: XcmPaymentApiV1 + Runtime: XcmPaymentApiV2 + frame_system::Config + pallet_balances::Config + pallet_session::Config diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 8b037a77a8445..071f54c485e86 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -84,7 +84,7 @@ pub use sp_runtime::{MultiAddress, Perbill, Permill}; #[cfg(feature = "runtime-benchmarks")] use xcm::latest::WESTEND_GENESIS_HASH; use xcm::VersionedLocation; -use xcm_config::{TreasuryAccount, XcmOriginToTransactDispatchOrigin, XcmRouter}; +use xcm_config::{TreasuryAccount, XcmConfig, XcmOriginToTransactDispatchOrigin, XcmRouter}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -874,10 +874,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -885,8 +882,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 23f0275ee32d6..ab6ffefa6d1e0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -73,7 +73,7 @@ use frame_system::{ }; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; -use xcm_config::{XcmOriginToTransactDispatchOrigin, XcmRouter}; +use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin, XcmRouter}; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -825,10 +825,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -836,8 +833,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 2dc06e50aade3..99d3572488694 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -99,7 +99,8 @@ use testnet_parachains_constants::westend::{ account::*, consensus::*, currency::*, fee::WeightToFee, time::*, }; use xcm_config::{ - GovernanceLocation, LocationToAccountId, TreasurerBodyId, XcmOriginToTransactDispatchOrigin, + GovernanceLocation, LocationToAccountId, TreasurerBodyId, XcmConfig, + XcmOriginToTransactDispatchOrigin, }; #[cfg(any(feature = "std", test))] @@ -1013,10 +1014,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -1024,8 +1022,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 736e6dd6ead14..0201c8ba0fe12 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -80,7 +80,8 @@ use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::Weigh use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{prelude::*, Version as XcmVersion}; use xcm_config::{ - FellowshipLocation, GovernanceLocation, RocRelayLocation, XcmOriginToTransactDispatchOrigin, + FellowshipLocation, GovernanceLocation, RocRelayLocation, XcmConfig, + XcmOriginToTransactDispatchOrigin, }; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -860,10 +861,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -871,8 +869,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs index 3dcc258abb654..8cf14d103f1c5 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -249,7 +249,7 @@ pub type PriceForParentDelivery = /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 4579b78ab4715..78c6239d48b46 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -80,7 +80,8 @@ use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::Weig use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{prelude::*, Version as XcmVersion}; use xcm_config::{ - FellowshipLocation, GovernanceLocation, TokenRelayLocation, XcmOriginToTransactDispatchOrigin, + FellowshipLocation, GovernanceLocation, TokenRelayLocation, XcmConfig, + XcmOriginToTransactDispatchOrigin, }; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, @@ -860,10 +861,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -871,8 +869,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs index 7061f9cb1338a..391972f24572c 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -285,7 +285,7 @@ pub type PriceForParentDelivery = /// queues. pub type XcmRouter = WithUniqueTopic<( // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 1ed6cedffd806..9560534e29d4b 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -808,10 +808,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -819,8 +816,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs index 4adbf02d9e586..8f2a89a268ee2 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -247,7 +247,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 4966b8783ea60..a139fd2530f07 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -810,10 +810,7 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -821,8 +818,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index 9cb1a96036b47..e5203f39c8814 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -288,7 +288,7 @@ pub type LocalOriginToLocation = SignedToAccountId32, + cumulus_primitives_utility::ParentAsUmp, // ..and XCMP to communicate with the sibling chains. XcmpQueue, )>; diff --git a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs index 5e4d073c9c2da..bc8a9f003b0ef 100644 --- a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs @@ -33,7 +33,7 @@ use sp_runtime::{ }; use xcm::prelude::InstructionError; use xcm_runtime_apis::fees::{ - runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, Error as XcmPaymentApiError, + runtime_decl_for_xcm_payment_api::XcmPaymentApiV2, Error as XcmPaymentApiError, }; type RuntimeHelper = @@ -157,7 +157,7 @@ pub fn xcm_payment_api_with_native_token_works< WeightToFee, >() where - Runtime: XcmPaymentApiV1 + Runtime: XcmPaymentApiV2 + frame_system::Config + pallet_balances::Config + pallet_session::Config diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 6a4b555d8bc04..9295f9b272b6d 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -97,7 +97,9 @@ pub use sp_runtime::{traits::ConvertInto, MultiAddress, Perbill, Permill}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm_config::{ForeignAssetsAssetId, LocationToAccountId, XcmOriginToTransactDispatchOrigin}; +use xcm_config::{ + ForeignAssetsAssetId, LocationToAccountId, XcmConfig, XcmOriginToTransactDispatchOrigin, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -1070,10 +1072,7 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!( } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } @@ -1081,8 +1080,9 @@ pallet_revive::impl_runtime_apis_plus_revive_traits!( PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 52efd6a742935..f43940c8474f2 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -440,7 +440,7 @@ sp_api::impl_runtime_apis! { unimplemented!() } - fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>) -> Result { + fn query_delivery_fees(_: VersionedLocation, _: VersionedXcm<()>, _: VersionedAssetId) -> Result { unimplemented!() } } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 1c08a1c6b3e1d..80426212d4c29 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -150,6 +150,7 @@ use governance::{ pallet_custom_origins, AuctionAdmin, Fellows, GeneralAdmin, LeaseAdmin, Treasurer, TreasurySpender, }; +use xcm_config::XcmConfig; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, @@ -1922,10 +1923,7 @@ sp_api::impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - XcmPallet::query_weight_to_asset_fee::(weight, asset) } @@ -1933,8 +1931,9 @@ sp_api::impl_runtime_apis! { XcmPallet::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - XcmPallet::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + XcmPallet::query_delivery_fees::(destination, message, asset_id) } } diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 99e02b1053320..88d0ee00531d1 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -151,6 +151,7 @@ use governance::{ pallet_custom_origins, AuctionAdmin, FellowshipAdmin, GeneralAdmin, LeaseAdmin, StakingAdmin, Treasurer, TreasurySpender, }; +use xcm_config::XcmConfig; #[cfg(test)] mod tests; @@ -2724,10 +2725,7 @@ sp_api::impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - use crate::xcm_config::XcmConfig; - type Trader = ::Trader; - XcmPallet::query_weight_to_asset_fee::(weight, asset) } @@ -2735,8 +2733,9 @@ sp_api::impl_runtime_apis! { XcmPallet::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - XcmPallet::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + XcmPallet::query_delivery_fees::(destination, message, asset_id) } } diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index cec618e3603a2..7d286b88493ea 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -3273,9 +3273,15 @@ impl Pallet { } /// Given a `destination` and XCM `message`, return assets to be charged as XCM delivery fees. - pub fn query_delivery_fees( + /// + /// Meant to be called by the `XcmPaymentApi`. + /// It's necessary to specify the asset in which fees are desired. + /// + /// NOTE: Only use this if delivery fees consist of only 1 asset, else this function will error. + pub fn query_delivery_fees( destination: VersionedLocation, message: VersionedXcm<()>, + versioned_asset_id: VersionedAssetId, ) -> Result { let result_version = destination.identify_version().max(message.identify_version()); @@ -3298,12 +3304,43 @@ impl Pallet { XcmPaymentApiError::Unroutable })?; - VersionedAssets::from(fees) - .into_version(result_version) - .map_err(|e| { - tracing::error!(target: "xcm::pallet_xcm::query_delivery_fees", ?e, ?result_version, "Failed to convert fees into version"); - XcmPaymentApiError::VersionedConversionFailed - }) + // This helper only works for routers that return 1 and only 1 asset for delivery fees. + if fees.len() != 1 { + return Err(XcmPaymentApiError::Unimplemented); + } + + let fee = fees.get(0).ok_or(XcmPaymentApiError::Unimplemented)?; + + let asset_id = versioned_asset_id.clone().try_into().map_err(|()| { + tracing::trace!( + target: "xcm::xcm_runtime_apis::query_delivery_fees", + "Failed to convert asset id: {versioned_asset_id:?}!" + ); + XcmPaymentApiError::VersionedConversionFailed + })?; + + let assets_to_pay = if fee.id == asset_id { + // If the fee asset is the same as the desired one, just return that. + fees + } else { + // We get the fees in the desired asset. + AssetExchanger::quote_exchange_price( + &fees.into(), + &(asset_id, Fungible(1)).into(), + true, // Maximal. + ) + .ok_or(XcmPaymentApiError::AssetNotFound)? + }; + + VersionedAssets::from(assets_to_pay).into_version(result_version).map_err(|e| { + tracing::trace!( + target: "xcm::pallet_xcm::query_delivery_fees", + ?e, + ?result_version, + "Failed to convert fees into desired version" + ); + XcmPaymentApiError::VersionedConversionFailed + }) } /// Given an Asset and a Location, returns if the provided location is a trusted reserve for the diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index b4efab9349a4b..0fea337a4ef36 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -666,7 +666,7 @@ impl XcmExecutor { /// charged for swapping to `asset_needed_for_fees`. /// /// The calculation is done by `Config::AssetExchanger`. - /// If neither `PayFees` or `BuyExecution` were not used, or no swap is required, + /// If neither `PayFees` or `BuyExecution` were used, or no swap is required, /// it will just return `asset_needed_for_fees`. fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset { let Some(asset_wanted_for_fees) = diff --git a/polkadot/xcm/xcm-runtime-apis/src/fees.rs b/polkadot/xcm/xcm-runtime-apis/src/fees.rs index a0e2dc912934e..250a9237b7ce7 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/fees.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/fees.rs @@ -34,6 +34,7 @@ sp_api::decl_runtime_apis! { /// /// To determine the execution weight of the calls required for /// [`xcm::latest::Instruction::Transact`] instruction, `TransactionPaymentCallApi` can be used. + #[api_version(2)] pub trait XcmPaymentApi { /// Returns a list of acceptable payment assets. /// @@ -57,6 +58,8 @@ sp_api::decl_runtime_apis! { /// * `asset`: `VersionedAssetId`. fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result; + /// Query delivery fees V1. + /// /// Get delivery fees for sending a specific `message` to a `destination`. /// These always come in a specific asset, defined by the chain. /// @@ -65,7 +68,20 @@ sp_api::decl_runtime_apis! { /// size of the message. /// * `destination`: The destination to send the message to. Different destinations may use /// different senders that charge different fees. + #[changed_in(2)] fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result; + + /// Query delivery fees V2. + /// + /// Get delivery fees for sending a specific `message` to a `destination`. + /// These always come in a specific asset, defined by the chain. + /// + /// # Arguments + /// * `message`: The message that'll be sent, necessary because most delivery fees are based on the + /// size of the message. + /// * `destination`: The destination to send the message to. Different destinations may use + /// different senders that charge different fees. + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result; } } diff --git a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs index 1307c7adaa9cb..17d3ae1c06b5f 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs @@ -29,6 +29,7 @@ mod mock; use mock::{ new_test_ext_with_balances, new_test_ext_with_balances_and_assets, DeliveryFees, ExistentialDeposit, HereLocation, OriginCaller, RuntimeCall, RuntimeEvent, TestClient, + ASSET_HUB_ASSETS_PALLET_INSTANCE, ASSET_HUB_PARA_ID, USDT_ID, }; use xcm_simulator::fake_message_hash; @@ -143,13 +144,10 @@ fn fee_estimation_for_teleport() { // results. let weight = runtime_api.query_xcm_weight(H256::zero(), local_xcm.clone()).unwrap().unwrap(); + let asset_id = VersionedAssetId::from(AssetId(HereLocation::get())); assert_eq!(weight, Weight::from_parts(400, 40)); let execution_fees = runtime_api - .query_weight_to_asset_fee( - H256::zero(), - weight, - VersionedAssetId::from(AssetId(HereLocation::get())), - ) + .query_weight_to_asset_fee(H256::zero(), weight, asset_id.clone()) .unwrap() .unwrap(); assert_eq!(execution_fees, 440); @@ -160,7 +158,12 @@ fn fee_estimation_for_teleport() { let remote_message = &remote_messages[0]; let delivery_fees = runtime_api - .query_delivery_fees(H256::zero(), destination.clone(), remote_message.clone()) + .query_delivery_fees( + H256::zero(), + destination.clone(), + remote_message.clone(), + asset_id, + ) .unwrap() .unwrap(); assert_eq!(delivery_fees, VersionedAssets::from((Here, 20u128))); @@ -465,3 +468,104 @@ fn calling_payment_api_with_a_lower_version_works() { .unwrap(); assert!(execution_fees.is_ok()); } + +// Test fee estimation for USDT reserve transfer with delivery fees in USDT. +// In this scenario, we're sending USDT from parachain 2000 to parachain 1000 (AssetHub). +// Since USDT is native to AssetHub, this will be a destination reserve transfer. +// We request the delivery fees to be quoted in USDT using the asset_id parameter in +// `query_delivery_fees`. +// +// Reserve Asset Transfer USDT +// Delivery fees in USDT +// Parachain(2000) -------------------------------------------> Parachain(1000) +#[test] +fn fee_estimation_for_usdt_reserve_transfer_in_usdt() { + sp_tracing::init_for_tests(); + let who = 1; // AccountId = u64. + let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; // Just enough for fees + let assets = vec![ + (1984, who, 1000), // USDT (asset ID 1984) - amount to transfer plus fees + ]; + new_test_ext_with_balances_and_assets(balances, assets).execute_with(|| { + let client = TestClient; + let runtime_api = client.runtime_api(); + + // USDT location from our parachain (2000) perspective + let usdt_location = Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), + GeneralIndex(USDT_ID.into()), + ], + ); + // USDT location from Asset Hub's perspective + let usdt_location_ah_pov = Location::new( + 0, + [PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), GeneralIndex(USDT_ID.into())], + ); + + let call = RuntimeCall::XcmPallet(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::from((Parent, Parachain(1000)))), + beneficiary: Box::new(VersionedLocation::from(AccountId32 { + id: [0u8; 32], + network: None, + })), + assets: Box::new(VersionedAssets::from(vec![ + (usdt_location.clone(), 100u128).into(), // Send 100 USDT + ])), + fee_asset_item: 0, // Fees are paid with USDT (the only asset) + weight_limit: Unlimited, + }); + let origin = OriginCaller::system(RawOrigin::Signed(who)); + let dry_run_effects = runtime_api + .dry_run_call(H256::zero(), origin, call, XCM_VERSION) + .unwrap() + .unwrap(); + + // For destination reserve transfer, we burn the assets locally + assert_eq!( + dry_run_effects.local_xcm, + Some(VersionedXcm::from( + Xcm::builder_unsafe() + .withdraw_asset((usdt_location.clone(), 100u128)) + .burn_asset((usdt_location.clone(), 100u128)) + .build() + )), + ); + + let send_destination = Location::new(1, [Parachain(1000)]); + // For destination reserve, the remote message withdraws the assets from the sovereign + // account + let send_message = Xcm::<()>::builder_unsafe() + .withdraw_asset((usdt_location_ah_pov.clone(), 100u128)) + .clear_origin() + .buy_execution((usdt_location_ah_pov.clone(), 100u128), Unlimited) + .deposit_asset(AllCounted(1), [0u8; 32]) + .build(); + assert_eq!( + dry_run_effects.forwarded_xcms, + vec![( + VersionedLocation::from(send_destination.clone()), + vec![VersionedXcm::from(send_message.clone())], + ),], + ); + + let mut forwarded_xcms_iter = dry_run_effects.forwarded_xcms.into_iter(); + let (destination, remote_messages) = forwarded_xcms_iter.next().unwrap(); + let remote_message = &remote_messages[0]; + + // Query delivery fees in USDT asset using the `asset_id` parameter. + let usdt_asset_id = VersionedAssetId::from(AssetId(usdt_location.clone())); + + let delivery_fees_usdt = runtime_api + .query_delivery_fees(H256::zero(), destination, remote_message.clone(), usdt_asset_id) + .unwrap() + .unwrap(); + + // With our MockAssetExchanger, we expect fees in USDT at a 1:2 conversion rate. + // Native delivery fee is `DeliveryFees::get()`, so USDT fee should be twice that. + let expected_usdt_fees = VersionedAssets::from((usdt_location, DeliveryFees::get() * 2)); + assert_eq!(delivery_fees_usdt, expected_usdt_fees); + }); +} diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index a4cb839e07767..90f45a362c895 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -90,6 +90,7 @@ impl pallet_balances::Config for TestRuntime { type ExistentialDeposit = ExistentialDeposit; } +// Assets instance #[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)] impl pallet_assets::Config for TestRuntime { type AssetId = AssetIdForAssetsPallet; @@ -153,9 +154,9 @@ impl InspectMessageQueues for TestXcmSender { pub type XcmRouter = TestXcmSender; parameter_types! { - pub const DeliveryFees: u128 = 20; // Random value. - pub const ExistentialDeposit: u128 = 1; // Random value. - pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Random value. + pub const DeliveryFees: u128 = 20; // Arbitrary value. + pub const ExistentialDeposit: u128 = 1; // Arbitrary value. + pub const BaseXcmWeight: Weight = Weight::from_parts(100, 10); // Arbitrary value. pub const MaxInstructions: u32 = 100; pub const NativeTokenPerSecondPerByte: (AssetId, u128, u128) = (AssetId(HereLocation::get()), 1, 1); pub UniversalLocation: InteriorLocation = [GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(2000)].into(); @@ -185,17 +186,29 @@ type Weigher = FixedWeightBounds; pub struct NativeTokenToAssetHub; impl ContainsPair for NativeTokenToAssetHub { fn contains(asset: &Asset, origin: &Location) -> bool { - matches!(asset.id.0.unpack(), (0, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + matches!(asset.id.0.unpack(), (0, [])) && + matches!(origin.unpack(), (1, [Parachain(ASSET_HUB_PARA_ID)])) } } -/// Matches the pair (RelayToken, AssetHub). +/// Parachain id of Asset Hub. +pub const ASSET_HUB_PARA_ID: u32 = 1000; +/// The instance index of the trust-backed assets pallet in Asset Hub. +pub const ASSET_HUB_ASSETS_PALLET_INSTANCE: u8 = 50; +/// Id of USDT in Asset Hub. +pub const USDT_ID: u32 = 1984; + +/// Matches the pairs (RelayToken, AssetHub) and (UsdtToken, AssetHub). /// This is used in the `IsReserve` configuration item, meaning we accept the relay token /// coming from AssetHub as a reserve asset transfer. -pub struct RelayTokenToAssetHub; -impl ContainsPair for RelayTokenToAssetHub { +pub struct RelayTokenAndUsdtToAssetHub; +impl ContainsPair for RelayTokenAndUsdtToAssetHub { fn contains(asset: &Asset, origin: &Location) -> bool { - matches!(asset.id.0.unpack(), (1, [])) && matches!(origin.unpack(), (1, [Parachain(1000)])) + let is_asset_hub = matches!(origin.unpack(), (1, [Parachain(ASSET_HUB_PARA_ID)])); + let is_relay_token = matches!(asset.id.0.unpack(), (1, [])); + let is_usdt = matches!(asset.id.0.unpack(), (1, [Parachain(ASSET_HUB_PARA_ID), PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), GeneralIndex(asset_id)]) if *asset_id == USDT_ID.into()); + + is_asset_hub && (is_relay_token || is_usdt) } } @@ -243,11 +256,16 @@ pub type NativeTokenTransactor = FungibleAdapter< LocalCheckAccount, >; +/// Converter from Location to local asset id and viceversa. pub struct LocationToAssetIdForAssetsPallet; impl MaybeEquivalence for LocationToAssetIdForAssetsPallet { fn convert(location: &Location) -> Option { match location.unpack() { (1, []) => Some(1 as AssetIdForAssetsPallet), + ( + 1, + [Parachain(ASSET_HUB_PARA_ID), PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), GeneralIndex(asset_id)], + ) if *asset_id == USDT_ID.into() => Some(USDT_ID as AssetIdForAssetsPallet), _ => None, } } @@ -255,13 +273,21 @@ impl MaybeEquivalence for LocationToAssetIdFor fn convert_back(id: &AssetIdForAssetsPallet) -> Option { match id { 1 => Some(Location::new(1, [])), + asset_id if *asset_id == USDT_ID => Some(Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), + GeneralIndex(USDT_ID.into()), + ], + )), _ => None, } } } -/// AssetTransactor for handling the relay chain token. -pub type RelayTokenTransactor = FungiblesAdapter< +/// AssetTransactor for handling assets other than the native one. +pub type AssetsTransactor = FungiblesAdapter< // We use pallet-assets for handling the relay token. AssetsPallet, // Matches the relay token. @@ -275,7 +301,7 @@ pub type RelayTokenTransactor = FungiblesAdapter< (), >; -pub type AssetTransactors = (NativeTokenTransactor, RelayTokenTransactor); +pub type AssetTransactors = (NativeTokenTransactor, AssetsTransactor); pub struct HereAndInnerLocations; impl Contains for HereAndInnerLocations { @@ -299,7 +325,7 @@ impl xcm_executor::Config for XcmConfig { type XcmEventEmitter = XcmPallet; type AssetTransactor = AssetTransactors; type OriginConverter = (); - type IsReserve = RelayTokenToAssetHub; + type IsReserve = RelayTokenAndUsdtToAssetHub; type IsTeleporter = NativeTokenToAssetHub; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -308,7 +334,7 @@ impl xcm_executor::Config for XcmConfig { type ResponseHandler = (); type AssetTrap = (); type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = MockAssetExchanger; type AssetClaims = (); type SubscriptionService = (); type PalletInstancesInfo = AllPalletsWithSystem; @@ -326,6 +352,69 @@ impl xcm_executor::Config for XcmConfig { type XcmRecorder = XcmPallet; } +/// Mock AssetExchanger that recognizes USDT and provides a 1:2 exchange rate +/// (1 native token = 2 USDT tokens) +pub struct MockAssetExchanger; +impl xcm_executor::traits::AssetExchange for MockAssetExchanger { + fn exchange_asset( + _origin: Option<&Location>, + give: xcm_executor::AssetsInHolding, + want: &Assets, + _maximal: bool, + ) -> Result { + let usdt_location = Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), + GeneralIndex(USDT_ID.into()), + ], + ); + + // Check if we're trying to exchange native asset for USDT + if let Some(give_asset) = give.fungible.get(&AssetId(HereLocation::get())) { + if let Some(want_asset) = want.get(0) { + if want_asset.id.0 == usdt_location { + // Convert native asset to USDT at 1:2 rate + let usdt_amount = give_asset.saturating_mul(2); + let mut result = xcm_executor::AssetsInHolding::new(); + result.subsume((AssetId(usdt_location), usdt_amount).into()); + return Ok(result); + } + } + } + + // If we can't handle the exchange, return the original assets + Err(give) + } + + fn quote_exchange_price(give: &Assets, want: &Assets, _maximal: bool) -> Option { + let usdt_location = Location::new( + 1, + [ + Parachain(ASSET_HUB_PARA_ID), + PalletInstance(ASSET_HUB_ASSETS_PALLET_INSTANCE), + GeneralIndex(USDT_ID.into()), + ], + ); + + // Check if we're trying to quote native asset for USDT + if let Some(give_asset) = give.get(0) { + if let Some(want_asset) = want.get(0) { + if give_asset.id.0 == HereLocation::get() && want_asset.id.0 == usdt_location { + if let Fungible(amount) = give_asset.fun { + // Return the USDT amount we'd get at 1:2 rate + let usdt_amount = amount.saturating_mul(2); + return Some((AssetId(usdt_location), usdt_amount).into()); + } + } + } + } + + None + } +} + /// Converts a signed origin of a u64 account into a location with only the `AccountIndex64` /// junction. pub struct SignedToAccountIndex64( @@ -408,10 +497,12 @@ pub fn new_test_ext_with_balances_and_assets( // We don't actually need this to be sufficient, since we use the native assets in // tests for the existential deposit. (1, 0, true, 1), + (1984, 0, true, 1), ], metadata: vec![ // id, name, symbol, decimals. (1, "Relay Token".into(), "RLY".into(), 12), + (1984, "Tether".into(), "USDT".into(), 6), ], accounts: assets, next_asset_id: None, @@ -495,8 +586,8 @@ sp_api::mock_impl_runtime_apis! { } } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - XcmPallet::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + XcmPallet::query_delivery_fees::<::AssetExchanger>(destination, message, asset_id) } } diff --git a/prdoc/pr_9963.prdoc b/prdoc/pr_9963.prdoc new file mode 100644 index 0000000000000..e8614adb84054 --- /dev/null +++ b/prdoc/pr_9963.prdoc @@ -0,0 +1,48 @@ +title: Query delivery fees v2 +doc: +- audience: Runtime Dev + description: |- + BREAKING CHANGE + + Adds a new parameter to `XcmPaymentApi.query_delivery_fees` to specify the asset id in which you'd like to get the delivery fees. This makes it possible to estimate how much to pay for fees for assets other than the native asset. + + Fixes https://github.com/paritytech/polkadot-sdk/issues/7061 + + Continuation of https://github.com/paritytech/polkadot-sdk/pull/8297 +crates: +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: asset-test-utils + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: collectives-westend-runtime + bump: major +- name: coretime-rococo-runtime + bump: major +- name: coretime-westend-runtime + bump: major +- name: people-rococo-runtime + bump: major +- name: people-westend-runtime + bump: major +- name: parachains-runtimes-test-utils + bump: major +- name: penpal-runtime + bump: major +- name: rococo-runtime + bump: major +- name: westend-runtime + bump: major +- name: xcm-runtime-apis + bump: major +- name: emulated-integration-tests-common + bump: major +- name: pallet-xcm + bump: major +- name: staging-xcm-executor + bump: major diff --git a/substrate/frame/staking-async/runtimes/parachain/src/lib.rs b/substrate/frame/staking-async/runtimes/parachain/src/lib.rs index 38f70633d6a8d..b0cc3c03c2ae8 100644 --- a/substrate/frame/staking-async/runtimes/parachain/src/lib.rs +++ b/substrate/frame/staking-async/runtimes/parachain/src/lib.rs @@ -1609,8 +1609,9 @@ impl_runtime_apis! { PolkadotXcm::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - PolkadotXcm::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + PolkadotXcm::query_delivery_fees::(destination, message, asset_id) } } diff --git a/substrate/frame/staking-async/runtimes/rc/src/lib.rs b/substrate/frame/staking-async/runtimes/rc/src/lib.rs index 8f0f6b4f33096..53edb28ceccd9 100644 --- a/substrate/frame/staking-async/runtimes/rc/src/lib.rs +++ b/substrate/frame/staking-async/runtimes/rc/src/lib.rs @@ -2676,8 +2676,9 @@ sp_api::impl_runtime_apis! { XcmPallet::query_xcm_weight(message) } - fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { - XcmPallet::query_delivery_fees(destination, message) + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>, asset_id: VersionedAssetId) -> Result { + type AssetExchanger = ::AssetExchanger; + XcmPallet::query_delivery_fees::(destination, message, asset_id) } }