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 50629d3af15ba..19228e82ffd67 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -66,7 +66,7 @@ use frame_support::{ AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, ConstantStoragePrice, EitherOfDiverse, Equals, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, }; use frame_system::{ @@ -1555,33 +1555,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let native_asset = xcm_config::TokenLocation::get(); - let fee_in_native = WeightToFee::weight_to_fee(&weight); - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == native_asset => { - // for native token - Ok(fee_in_native) - }, - Ok(asset_id) => { - // Try to get current price of `asset_id` in `native_asset`. - if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::::quote_price_tokens_for_exact_tokens( - asset_id.0.clone(), - native_asset, - fee_in_native, - true, // We include the fee. - ) { - Ok(swapped_in_native) - } else { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - } - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index ffdecbc2dd47c..fd91ce7d2f428 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -34,7 +34,6 @@ use asset_test_utils::{ ExtBuilder, GovernanceOrigin, SlotDurations, }; use codec::{Decode, Encode}; -use core::ops::Mul; use frame_support::{ assert_noop, assert_ok, parameter_types, traits::{ @@ -91,52 +90,6 @@ fn slot_durations() -> SlotDurations { } } -fn setup_pool_for_paying_fees_with_foreign_assets( - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( - AccountId, - Location, - Balance, - ), -) { - let existential_deposit = ExistentialDeposit::get(); - - // setup a pool to pay fees with `foreign_asset_id_location` tokens - let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = Location::parent(); - let pool_liquidity: Balance = - existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); - - let _ = Balances::force_set_balance( - RuntimeOrigin::root(), - pool_owner.clone().into(), - (existential_deposit + pool_liquidity).mul(2).into(), - ); - - assert_ok!(ForeignAssets::mint( - RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.clone().into(), - pool_owner.clone().into(), - (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), - )); - - assert_ok!(AssetConversion::create_pool( - RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.clone().into()), - Box::new(foreign_asset_id_location.clone().into()) - )); - - assert_ok!(AssetConversion::add_liquidity( - RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()), - pool_liquidity, - pool_liquidity, - 1, - 1, - pool_owner, - )); -} - #[test] fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() @@ -845,7 +798,7 @@ mod asset_hub_rococo_tests { 1000000000000, || { // setup pool for paying fees to touch `SwapFirstAssetTrader` - setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::(ExistentialDeposit::get(), foreign_asset_create_params); // staking pot account for collecting local native fees from `BuyExecution` let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get()); // prepare bridge configuration @@ -920,7 +873,7 @@ mod asset_hub_rococo_tests { foreign_asset_create_params.clone(), 1000000000000, || { - setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::(ExistentialDeposit::get(), foreign_asset_create_params); bridging_to_asset_hub_westend() }, ( @@ -1365,11 +1318,22 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); asset_test_utils::test_cases::xcm_payment_api_with_pools_works::< Runtime, RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); + + asset_test_utils::test_cases::xcm_payment_api_foreign_asset_pool_works::< + Runtime, + RuntimeCall, + RuntimeOrigin, + LocationToAccountId, + Block, + WeightToFee, + >(ExistentialDeposit::get(), WESTEND_GENESIS_HASH); } 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 db8198db4df6c..adead0bc83192 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -53,7 +53,7 @@ use frame_support::{ AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, ConstantStoragePrice, Equals, InstanceFilter, Nothing, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, BoundedVec, PalletId, }; use frame_system::{ @@ -1692,33 +1692,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let native_asset = xcm_config::WestendLocation::get(); - let fee_in_native = WeightToFee::weight_to_fee(&weight); - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == native_asset => { - // for native asset - Ok(fee_in_native) - }, - Ok(asset_id) => { - // Try to get current price of `asset_id` in `native_asset`. - if let Ok(Some(swapped_in_native)) = assets_common::PoolAdapter::::quote_price_tokens_for_exact_tokens( - asset_id.0.clone(), - native_asset, - fee_in_native, - true, // We include the fee. - ) { - Ok(swapped_in_native) - } else { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - } - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { 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 951b33124404e..6a6a62f6f7f81 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -50,7 +50,7 @@ use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance} use sp_consensus_aura::SlotDuration; use sp_core::crypto::Ss58Codec; use sp_runtime::{traits::MaybeEquivalence, Either}; -use std::{convert::Into, ops::Mul}; +use std::convert::Into; use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::{ prelude::{Assets as XcmAssets, *}, @@ -91,52 +91,6 @@ fn slot_durations() -> SlotDurations { } } -fn setup_pool_for_paying_fees_with_foreign_assets( - (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( - AccountId, - xcm::v5::Location, - Balance, - ), -) { - let existential_deposit = ExistentialDeposit::get(); - - // setup a pool to pay fees with `foreign_asset_id_location` tokens - let pool_owner: AccountId = [14u8; 32].into(); - let native_asset = xcm::v5::Location::parent(); - let pool_liquidity: Balance = - existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); - - let _ = Balances::force_set_balance( - RuntimeOrigin::root(), - pool_owner.clone().into(), - (existential_deposit + pool_liquidity).mul(2).into(), - ); - - assert_ok!(ForeignAssets::mint( - RuntimeOrigin::signed(foreign_asset_owner), - foreign_asset_id_location.clone().into(), - pool_owner.clone().into(), - (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), - )); - - assert_ok!(AssetConversion::create_pool( - RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.clone().into()), - Box::new(foreign_asset_id_location.clone().into()) - )); - - assert_ok!(AssetConversion::add_liquidity( - RuntimeOrigin::signed(pool_owner.clone()), - Box::new(native_asset.into()), - Box::new(foreign_asset_id_location.into()), - pool_liquidity, - pool_liquidity, - 1, - 1, - pool_owner, - )); -} - #[test] fn test_buy_and_refund_weight_in_native() { ExtBuilder::::default() @@ -944,7 +898,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_pool_s 1000000000000, || { // setup pool for paying fees to touch `SwapFirstAssetTrader` - setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::(ExistentialDeposit::get(), foreign_asset_create_params); // staking pot account for collecting local native fees from `BuyExecution` let _ = Balances::force_set_balance(RuntimeOrigin::root(), StakingPot::get().into(), ExistentialDeposit::get()); // prepare bridge configuration @@ -1014,7 +968,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic foreign_asset_create_params.clone(), 1000000000000, || { - setup_pool_for_paying_fees_with_foreign_assets(foreign_asset_create_params); + asset_test_utils::test_cases::setup_pool_for_paying_fees_with_foreign_assets::(ExistentialDeposit::get(), foreign_asset_create_params); bridging_to_asset_hub_rococo() }, ( @@ -1423,13 +1377,24 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); asset_test_utils::test_cases::xcm_payment_api_with_pools_works::< Runtime, RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); + + asset_test_utils::test_cases::xcm_payment_api_foreign_asset_pool_works::< + Runtime, + RuntimeCall, + RuntimeOrigin, + LocationToAccountId, + Block, + WeightToFee, + >(ExistentialDeposit::get(), ROCOCO_GENESIS_HASH); } #[test] 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 8466af761a4e4..3279aba88d5cc 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -18,6 +18,7 @@ use super::xcm_helpers; use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees}; use codec::Encode; +use core::ops::Mul; use cumulus_primitives_core::XcmpMessageSource; use frame_support::{ assert_noop, assert_ok, @@ -35,7 +36,7 @@ use parachains_runtimes_test_utils::{ }; use sp_runtime::{ traits::{Block as BlockT, MaybeEquivalence, StaticLookup, Zero}, - DispatchError, Saturating, + DispatchError, SaturatedConversion, Saturating, }; use xcm::{latest::prelude::*, VersionedAssets}; use xcm_executor::{traits::ConvertLocation, XcmExecutor}; @@ -1598,7 +1599,7 @@ pub fn reserve_transfer_native_asset_to_non_teleport_para_works< }) } -pub fn xcm_payment_api_with_pools_works() +pub fn xcm_payment_api_with_pools_works() where Runtime: XcmPaymentApiV1 + frame_system::Config @@ -1623,6 +1624,7 @@ where <::Lookup as StaticLookup>::Source: From<::AccountId>, Block: BlockT, + WeightToFee: frame_support::weights::WeightToFee, { use xcm::prelude::*; @@ -1636,13 +1638,18 @@ where .build(); let versioned_xcm_to_weigh = VersionedXcm::from(xcm_to_weigh.clone().into()); - let xcm_weight = Runtime::query_xcm_weight(versioned_xcm_to_weigh); - assert!(xcm_weight.is_ok()); + let xcm_weight = + Runtime::query_xcm_weight(versioned_xcm_to_weigh).expect("xcm weight must be computed"); + + let expected_weight_native_fee: u128 = + WeightToFee::weight_to_fee(&xcm_weight).saturated_into(); + let native_token: Location = Parent.into(); let native_token_versioned = VersionedAssetId::from(AssetId(native_token.clone())); - let execution_fees = - Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), native_token_versioned); - assert!(execution_fees.is_ok()); + let execution_fees = Runtime::query_weight_to_asset_fee(xcm_weight, native_token_versioned) + .expect("weight must be converted to native fee"); + + assert_eq!(execution_fees, expected_weight_native_fee); // We need some balance to create an asset. assert_ok!( @@ -1651,30 +1658,25 @@ where // Now we try to use an asset that's not in a pool. let asset_id = 1984u32; // USDT. - let asset_not_in_pool: Location = - (PalletInstance(50), GeneralIndex(asset_id.into())).into(); + let usdt_token: Location = (PalletInstance(50), GeneralIndex(asset_id.into())).into(); assert_ok!(pallet_assets::Pallet::::create( RuntimeOrigin::signed(test_account.clone()), asset_id.into(), test_account.clone().into(), 1000 )); - let execution_fees = Runtime::query_weight_to_asset_fee( - xcm_weight.unwrap(), - asset_not_in_pool.clone().into(), - ); + let execution_fees = + Runtime::query_weight_to_asset_fee(xcm_weight, usdt_token.clone().into()); assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound)); // We add it to a pool with native. assert_ok!(pallet_asset_conversion::Pallet::::create_pool( RuntimeOrigin::signed(test_account.clone()), native_token.clone().try_into().unwrap(), - asset_not_in_pool.clone().try_into().unwrap() + usdt_token.clone().try_into().unwrap() )); - let execution_fees = Runtime::query_weight_to_asset_fee( - xcm_weight.unwrap(), - asset_not_in_pool.clone().into(), - ); + let execution_fees = + Runtime::query_weight_to_asset_fee(xcm_weight, usdt_token.clone().into()); // Still not enough because it doesn't have any liquidity. assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound)); @@ -1688,17 +1690,191 @@ where // ...so we can add liquidity to the pool. assert_ok!(pallet_asset_conversion::Pallet::::add_liquidity( RuntimeOrigin::signed(test_account.clone()), - native_token.try_into().unwrap(), - asset_not_in_pool.clone().try_into().unwrap(), + native_token.clone().try_into().unwrap(), + usdt_token.clone().try_into().unwrap(), 1_000_000_000_000, 2_000_000_000_000, 0, 0, test_account )); - let execution_fees = - Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), asset_not_in_pool.into()); - // Now it works! - assert_ok!(execution_fees); + + let expected_weight_usdt_fee: u128 = + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + usdt_token.clone(), + native_token, + expected_weight_native_fee, + true, + ) + .expect("the quote price must work") + .saturated_into(); + + assert_ne!(expected_weight_usdt_fee, expected_weight_native_fee); + + let execution_fees = Runtime::query_weight_to_asset_fee(xcm_weight, usdt_token.into()) + .expect("weight must be converted to native fee"); + + assert_eq!(execution_fees, expected_weight_usdt_fee); + }); +} + +pub fn setup_pool_for_paying_fees_with_foreign_assets( + existential_deposit: Balance, + (foreign_asset_owner, foreign_asset_id_location, foreign_asset_id_minimum_balance): ( + AccountId, + Location, + Balance, + ), +) where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_assets::Config< + pallet_assets::Instance2, + AssetId = xcm::v5::Location, + Balance = ::Balance, + > + pallet_asset_conversion::Config< + AssetKind = xcm::v5::Location, + Balance = ::Balance, + >, + RuntimeOrigin: OriginTrait::AccountId>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, +{ + // setup a pool to pay fees with `foreign_asset_id_location` tokens + let pool_owner: AccountId = [14u8; 32].into(); + let native_asset = Location::parent(); + let pool_liquidity: Balance = + existential_deposit.max(foreign_asset_id_minimum_balance).mul(100_000); + + let _ = pallet_balances::Pallet::::force_set_balance( + RuntimeOrigin::root(), + pool_owner.clone().into(), + (existential_deposit + pool_liquidity).mul(2).into(), + ); + + assert_ok!(pallet_assets::Pallet::::mint( + RuntimeOrigin::signed(foreign_asset_owner), + foreign_asset_id_location.clone().into(), + pool_owner.clone().into(), + (foreign_asset_id_minimum_balance + pool_liquidity).mul(2).into(), + )); + + assert_ok!(pallet_asset_conversion::Pallet::::create_pool( + RuntimeOrigin::signed(pool_owner.clone()), + Box::new(native_asset.clone().into()), + Box::new(foreign_asset_id_location.clone().into()) + )); + + assert_ok!(pallet_asset_conversion::Pallet::::add_liquidity( + RuntimeOrigin::signed(pool_owner.clone()), + Box::new(native_asset.into()), + Box::new(foreign_asset_id_location.into()), + pool_liquidity, + pool_liquidity, + 1, + 1, + pool_owner, + )); +} + +pub fn xcm_payment_api_foreign_asset_pool_works< + Runtime, + RuntimeCall, + RuntimeOrigin, + LocationToAccountId, + Block, + WeightToFee, +>( + existential_deposit: Balance, + another_network_genesis_hash: [u8; 32], +) where + Runtime: XcmPaymentApiV1 + + frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + cumulus_pallet_xcmp_queue::Config + + pallet_timestamp::Config + + pallet_assets::Config< + pallet_assets::Instance2, + AssetId = xcm::v5::Location, + Balance = ::Balance, + > + pallet_asset_conversion::Config< + AssetKind = xcm::v5::Location, + Balance = ::Balance, + >, + RuntimeOrigin: OriginTrait::AccountId>, + ValidatorIdOf: From>, + <::Lookup as StaticLookup>::Source: + From<::AccountId>, + LocationToAccountId: ConvertLocation, + Block: BlockT, + WeightToFee: frame_support::weights::WeightToFee, +{ + use xcm::prelude::*; + + ExtBuilder::::default().build().execute_with(|| { + let foreign_asset_owner = + LocationToAccountId::convert_location(&Location::parent()).unwrap(); + let foreign_asset_id_location = Location::new( + 2, + [Junction::GlobalConsensus(NetworkId::ByGenesis(another_network_genesis_hash))], + ); + let native_asset_location = Location::parent(); + let foreign_asset_id_minimum_balance = 1_000_000_000; + + pallet_assets::Pallet::::force_create( + RuntimeHelper::::root_origin(), + foreign_asset_id_location.clone().into(), + foreign_asset_owner.clone().into(), + true, // is_sufficient=true + foreign_asset_id_minimum_balance.into(), + ) + .unwrap(); + + setup_pool_for_paying_fees_with_foreign_assets::( + existential_deposit, + ( + foreign_asset_owner, + foreign_asset_id_location.clone(), + foreign_asset_id_minimum_balance, + ), + ); + + let transfer_amount = 100u128; + let xcm_to_weigh = Xcm::::builder_unsafe() + .withdraw_asset((Here, transfer_amount)) + .buy_execution((Here, transfer_amount), Unlimited) + .deposit_asset(AllCounted(1), [1u8; 32]) + .build(); + let versioned_xcm_to_weigh = VersionedXcm::from(xcm_to_weigh.into()); + + let xcm_weight = + Runtime::query_xcm_weight(versioned_xcm_to_weigh).expect("xcm weight must be computed"); + + let weight_native_fee: u128 = WeightToFee::weight_to_fee(&xcm_weight).saturated_into(); + + let expected_weight_foreign_asset_fee: u128 = + pallet_asset_conversion::Pallet::::quote_price_tokens_for_exact_tokens( + foreign_asset_id_location.clone(), + native_asset_location, + weight_native_fee, + true, + ) + .expect("the quote price must work") + .saturated_into(); + + assert_ne!(expected_weight_foreign_asset_fee, weight_native_fee); + + let execution_fees = Runtime::query_weight_to_asset_fee( + xcm_weight, + foreign_asset_id_location.clone().into(), + ) + .expect("weight must be converted to foreign asset fee"); + + assert_eq!(execution_fees, expected_weight_foreign_asset_fee); }); } 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 aaacda6d383c7..0f80d62e62d59 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 @@ -64,7 +64,7 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ConstBool, ConstU32, ConstU64, ConstU8, Get, TransformOrigin}, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -863,21 +863,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index b6530d8e8a18d..87857224bced5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -998,5 +998,6 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } 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 fb341bf74e97d..89dfc8976bd34 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 @@ -64,7 +64,7 @@ use frame_support::{ genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ConstBool, ConstU32, ConstU64, ConstU8, Get, TransformOrigin}, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -814,21 +814,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::WestendLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - tracing::trace!(target: "xcm::xcm_runtime_apis", ?asset_id, "query_weight_to_asset_fee - unhandled asset_id!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - tracing::trace!(target: "xcm::xcm_runtime_apis", ?asset, "query_weight_to_asset_fee - failed to convert asset!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index 9332da3194c05..0ea6e9f37c73f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -683,6 +683,7 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 8c1ad33ccea55..450fa857aa0aa 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -77,7 +77,7 @@ use frame_support::{ fungible::HoldConsideration, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, LinearStoragePrice, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -984,21 +984,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::WndLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs index 016538c35715f..fec4357be795d 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs @@ -25,6 +25,7 @@ use parachains_common::AccountId; use parachains_runtimes_test_utils::GovernanceOrigin; use sp_core::crypto::Ss58Codec; use sp_runtime::Either; +use testnet_parachains_constants::westend::fee::WeightToFee; use xcm::latest::prelude::*; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -146,6 +147,7 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index afc223570dadb..4b453432a4355 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -48,7 +48,7 @@ use frame_support::{ traits::{ ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -851,21 +851,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::RocRelayLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs index dea41c093fb36..10568ea77ae55 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs @@ -21,6 +21,7 @@ use coretime_rococo_runtime::{ }; use parachains_common::AccountId; use sp_core::crypto::Ss58Codec; +use testnet_parachains_constants::rococo::fee::WeightToFee; use xcm::latest::prelude::*; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -142,5 +143,6 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 421ded32ab08f..83e4d12194888 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -48,7 +48,7 @@ use frame_support::{ traits::{ ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -852,21 +852,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::TokenRelayLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs index b8e51f2d8e781..d08e273eef506 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs @@ -25,6 +25,7 @@ use parachains_common::AccountId; use parachains_runtimes_test_utils::GovernanceOrigin; use sp_core::crypto::Ss58Codec; use sp_runtime::Either; +use testnet_parachains_constants::westend::fee::WeightToFee; use xcm::latest::prelude::*; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -146,6 +147,7 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index fdadbacfc1a88..0c2e79042b373 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -38,7 +38,7 @@ use frame_support::{ ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -799,21 +799,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::RelayLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs b/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs index ba331216db526..066fc62129c06 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs @@ -21,6 +21,7 @@ use people_rococo_runtime::{ xcm_config::LocationToAccountId, Block, Runtime, RuntimeCall, RuntimeOrigin, }; use sp_core::crypto::Ss58Codec; +use testnet_parachains_constants::rococo::fee::WeightToFee; use xcm::latest::prelude::*; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -142,5 +143,6 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index dc415b7c1b7b0..40bc84eaa70a0 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -38,7 +38,7 @@ use frame_support::{ ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight, WeightToFee as _}, + weights::{ConstantMultiplier, Weight}, PalletId, }; use frame_system::{ @@ -797,21 +797,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::RelayLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs b/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs index 3d06ab8368a65..3bc9957e4d5ed 100644 --- a/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs @@ -25,6 +25,7 @@ use people_westend_runtime::{ }; use sp_core::crypto::Ss58Codec; use sp_runtime::Either; +use testnet_parachains_constants::westend::fee::WeightToFee; use xcm::latest::prelude::*; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -146,6 +147,7 @@ fn xcm_payment_api_works() { RuntimeCall, RuntimeOrigin, Block, + WeightToFee, >(); } diff --git a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs index c6b1ccdac3c12..afad2a80d4e48 100644 --- a/cumulus/parachains/runtimes/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/test-utils/src/test_cases.rs @@ -24,10 +24,11 @@ use codec::Encode; use frame_support::{ assert_ok, traits::{Get, OriginTrait}, + weights::WeightToFee as WeightToFeeT, }; use parachains_common::AccountId; use sp_runtime::{ - traits::{Block as BlockT, StaticLookup}, + traits::{Block as BlockT, SaturatedConversion, StaticLookup}, DispatchError, Either, }; use xcm::prelude::XcmError; @@ -148,7 +149,13 @@ pub fn set_storage_keys_by_governance_works( }); } -pub fn xcm_payment_api_with_native_token_works() +pub fn xcm_payment_api_with_native_token_works< + Runtime, + RuntimeCall, + RuntimeOrigin, + Block, + WeightToFee, +>() where Runtime: XcmPaymentApiV1 + frame_system::Config @@ -165,6 +172,7 @@ where <::Lookup as StaticLookup>::Source: From<::AccountId>, Block: BlockT, + WeightToFee: WeightToFeeT, { use xcm::prelude::*; ExtBuilder::::default().build().execute_with(|| { @@ -179,28 +187,37 @@ where // We first try calling it with a lower XCM version. let lower_version_xcm_to_weigh = versioned_xcm_to_weigh.clone().into_version(XCM_VERSION - 1).unwrap(); - let xcm_weight = Runtime::query_xcm_weight(lower_version_xcm_to_weigh); - assert!(xcm_weight.is_ok()); + let xcm_weight = Runtime::query_xcm_weight(lower_version_xcm_to_weigh) + .expect("xcm weight must be computed"); + + let expected_weight_fee: u128 = WeightToFee::weight_to_fee(&xcm_weight).saturated_into(); + let native_token: Location = Parent.into(); let native_token_versioned = VersionedAssetId::from(AssetId(native_token)); let lower_version_native_token = native_token_versioned.clone().into_version(XCM_VERSION - 1).unwrap(); let execution_fees = - Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), lower_version_native_token); - assert!(execution_fees.is_ok()); + Runtime::query_weight_to_asset_fee(xcm_weight, lower_version_native_token) + .expect("weight must be converted to native fee"); + + assert_eq!(execution_fees, expected_weight_fee); // Now we call it with the latest version. - let xcm_weight = Runtime::query_xcm_weight(versioned_xcm_to_weigh); - assert!(xcm_weight.is_ok()); - let execution_fees = - Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), native_token_versioned); - assert!(execution_fees.is_ok()); + let xcm_weight = + Runtime::query_xcm_weight(versioned_xcm_to_weigh).expect("xcm weight must be computed"); + + let expected_weight_fee: u128 = WeightToFee::weight_to_fee(&xcm_weight).saturated_into(); + + let execution_fees = Runtime::query_weight_to_asset_fee(xcm_weight, native_token_versioned) + .expect("weight must be converted to native fee"); + + assert_eq!(execution_fees, expected_weight_fee); // If we call it with anything other than the native token it will error. let non_existent_token: Location = Here.into(); let non_existent_token_versioned = VersionedAssetId::from(AssetId(non_existent_token)); let execution_fees = - Runtime::query_weight_to_asset_fee(xcm_weight.unwrap(), non_existent_token_versioned); + Runtime::query_weight_to_asset_fee(xcm_weight, non_existent_token_versioned); assert_eq!(execution_fees, Err(XcmPaymentApiError::AssetNotFound)); }); } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index eb21aafd02f93..73188bff81d8f 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -67,7 +67,7 @@ use frame_support::{ Nothing, TransformOrigin, }, weights::{ - constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }, PalletId, @@ -1088,21 +1088,11 @@ impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::RelayLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + PolkadotXcm::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 37a0b21bf5865..5dd3dc3cf65fc 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -103,7 +103,7 @@ use frame_support::{ KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, + weights::{ConstantMultiplier, WeightMeter}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -1897,21 +1897,11 @@ sp_api::impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + XcmPallet::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index 6f935b0573fc1..d401e7c6c9906 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -41,7 +41,7 @@ use frame_support::{ KeyOwnerProofSystem, LinearStoragePrice, Nothing, ProcessMessage, ProcessMessageError, VariantCountOf, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, + weights::{ConstantMultiplier, WeightMeter}, PalletId, }; use frame_system::{EnsureRoot, EnsureSigned}; @@ -2651,21 +2651,11 @@ sp_api::impl_runtime_apis! { } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { - let latest_asset_id: Result = asset.clone().try_into(); - match latest_asset_id { - Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => { - // for native token - Ok(WeightToFee::weight_to_fee(&weight)) - }, - Ok(asset_id) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); - Err(XcmPaymentApiError::AssetNotFound) - }, - Err(_) => { - log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); - Err(XcmPaymentApiError::VersionedConversionFailed) - } - } + use crate::xcm_config::XcmConfig; + + type Trader = ::Trader; + + XcmPallet::query_weight_to_asset_fee::(weight, asset) } fn query_xcm_weight(message: VersionedXcm<()>) -> Result { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index e55cdd235b8d7..e83fefc3231cd 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -54,6 +54,7 @@ use sp_runtime::{ }, Either, RuntimeDebug, SaturatedConversion, }; +use storage::{with_transaction, TransactionOutcome}; use xcm::{latest::QueryResponseInfo, prelude::*}; use xcm_builder::{ ExecuteController, ExecuteControllerWeightInfo, InspectMessageQueues, QueryController, @@ -2954,6 +2955,60 @@ impl Pallet { }) } + /// Computes the weight cost using the provided `WeightTrader`. + /// This function is supposed to be used ONLY in `XcmPaymentApi::query_weight_to_asset_fee`. + /// + /// The provided `WeightTrader` must be the same as the one used in the XcmExecutor to ensure + /// uniformity in the weight cost calculation. + /// + /// NOTE: Currently this function uses a workaround that should be good enough for all practical + /// uses: passes `u128::MAX / 2 == 2^127` of the specified asset to the `WeightTrader` as + /// payment and computes the weight cost as the difference between this and the unspent amount. + /// + /// Some weight traders could add the provided payment to some account's balance. However, + /// it should practically never result in overflow because even currencies with a lot of decimal + /// digits (say 18) usually have the total issuance of billions (`x * 10^9`) or trillions (`x * + /// 10^12`) at max, much less than `2^127 / 10^18 =~ 1.7 * 10^20` (170 billion billion). Thus, + /// any account's balance most likely holds less than `2^127`, so adding `2^127` won't result in + /// `u128` overflow. + pub fn query_weight_to_asset_fee( + weight: Weight, + asset: VersionedAssetId, + ) -> Result { + let asset: AssetId = asset.clone().try_into() + .map_err(|e| { + tracing::debug!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to convert versioned asset"); + XcmPaymentApiError::VersionedConversionFailed + })?; + + let max_amount = u128::MAX / 2; + let max_payment: Asset = (asset.clone(), max_amount).into(); + let context = XcmContext::with_message_id(XcmHash::default()); + + // We return the unspent amount without affecting the state + // as we used a big amount of the asset without any check. + let unspent = with_transaction(|| { + let mut trader = Trader::new(); + let result = trader.buy_weight(weight, max_payment.into(), &context) + .map_err(|e| { + tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?e, ?asset, "Failed to buy weight"); + + // Return something convertible to `DispatchError` as required by the `with_transaction` fn. + DispatchError::Other("Failed to buy weight") + }); + + TransactionOutcome::Rollback(result) + }).map_err(|_| XcmPaymentApiError::AssetNotFound)?; + + let Some(unspent) = unspent.fungible.get(&asset) else { + tracing::error!(target: "xcm::pallet::query_weight_to_asset_fee", ?asset, "The trader didn't return the needed fungible asset"); + return Err(XcmPaymentApiError::AssetNotFound); + }; + + let paid = max_amount - unspent; + Ok(paid) + } + /// Given a `destination` and XCM `message`, return assets to be charged as XCM delivery fees. pub fn query_delivery_fees( destination: VersionedLocation, diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 3c792838b8ba7..c37d0bf1c77e3 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -488,6 +488,8 @@ pub type Barrier = ( pub type XcmRouter = EnsureDecodableXcm<(TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm)>; +pub type Trader = FixedRateOfFungible; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -506,7 +508,7 @@ impl xcm_executor::Config for XcmConfig { type UniversalLocation = UniversalLocation; type Barrier = Barrier; type Weigher = FixedWeightBounds; - type Trader = FixedRateOfFungible; + type Trader = Trader; type ResponseHandler = XcmPallet; type AssetTrap = XcmPallet; type AssetLocker = (); diff --git a/polkadot/xcm/pallet-xcm/src/tests/mod.rs b/polkadot/xcm/pallet-xcm/src/tests/mod.rs index a49f6345375ed..8897fa1763631 100644 --- a/polkadot/xcm/pallet-xcm/src/tests/mod.rs +++ b/polkadot/xcm/pallet-xcm/src/tests/mod.rs @@ -30,7 +30,7 @@ use crate::{ }; use bounded_collections::BoundedVec; use frame_support::{ - assert_err_ignore_postinfo, assert_noop, assert_ok, + assert_err_ignore_postinfo, assert_noop, assert_ok, assert_storage_noop, traits::{ContainsPair, Currency, Hooks}, weights::Weight, }; @@ -1743,3 +1743,17 @@ fn deliver_failure_with_expect_error() { }) }); } + +#[test] +fn query_weight_to_asset_fee_noop() { + new_test_ext_with_balances(vec![]).execute_with(|| { + let weight = Weight::from_parts(4_000_000_000, 3800); + let asset_id = AssetId(Location::here()); + + assert_storage_noop!(XcmPallet::query_weight_to_asset_fee::( + weight, + asset_id.into() + ) + .unwrap()); + }) +} diff --git a/prdoc/pr_8281.prdoc b/prdoc/pr_8281.prdoc new file mode 100644 index 0000000000000..9f987592c7999 --- /dev/null +++ b/prdoc/pr_8281.prdoc @@ -0,0 +1,42 @@ +title: "workaround: XcmPaymentApi::query_weight_to_asset_fee simple common impl" +doc: +- audience: Runtime Dev + description: |- + This PR adds a common implementation for `XcmPaymentApi::query_weight_to_asset_fee` to `pallet-xcm`. + + This PR is a simple alternative to #8202 (which could still be useful for other reasons). + It uses a workaround instead of a big refactoring. + + The workaround is described in the doc comment `query_weight_to_asset_fee` in `pallet-xcm` + +crates: +- name: pallet-xcm + bump: patch +- name: parachains-runtimes-test-utils + bump: minor +- name: asset-test-utils + bump: minor +- name: asset-hub-rococo-runtime + bump: minor +- name: asset-hub-westend-runtime + bump: minor +- name: bridge-hub-rococo-runtime + bump: minor +- name: bridge-hub-westend-runtime + bump: minor +- name: collectives-westend-runtime + bump: minor +- name: coretime-rococo-runtime + bump: minor +- name: coretime-westend-runtime + bump: minor +- name: people-rococo-runtime + bump: minor +- name: people-westend-runtime + bump: minor +- name: penpal-runtime + bump: minor +- name: rococo-runtime + bump: minor +- name: westend-runtime + bump: minor