From 7723274a2c5cbb10213379271094d5180716ca7d Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Mon, 22 Jul 2024 12:37:05 +0200 Subject: [PATCH] Add XcmPaymentApi and DryRunApi to all runtimes (#380) Adds the https://github.com/paritytech/polkadot-sdk/pull/3607 and https://github.com/paritytech/polkadot-sdk/pull/3872 to all runtimes. These APIs work together to allow dry running and estimating execution and delivery fees for XCMs. This PR doesn't allow querying the price in assets different than the relay token for asset hubs. Can be done in a following PR. Also tracking https://github.com/polkadot-fellows/runtimes/issues/388 and https://github.com/polkadot-fellows/runtimes/issues/389 for future improvemens. Old PR was https://github.com/polkadot-fellows/runtimes/pull/359, this one targets main already updated to Polkadot SDK 1.13. ## Dear reviewer Although there are a lot of changes, the main one is the addition of the two aforementioned APIs to all relays and system parachains. The rest of the files are tests, which all have the same format. --- CHANGELOG.md | 2 + Cargo.lock | 59 ++++ Cargo.toml | 4 + .../coretime/coretime-kusama/Cargo.toml | 22 ++ .../coretime/coretime-kusama/src/genesis.rs | 66 ++++ .../coretime/coretime-kusama/src/lib.rs | 50 +++ integration-tests/emulated/helpers/src/lib.rs | 160 ++++++++-- .../networks/kusama-system/Cargo.toml | 1 + .../networks/kusama-system/src/lib.rs | 6 +- .../tests/assets/asset-hub-kusama/Cargo.toml | 1 + .../assets/asset-hub-kusama/src/tests/mod.rs | 1 + .../src/tests/xcm_fee_estimation.rs | 301 +++++++++++++++++ .../assets/asset-hub-polkadot/Cargo.toml | 1 + .../asset-hub-polkadot/src/tests/mod.rs | 1 + .../src/tests/xcm_fee_estimation.rs | 302 ++++++++++++++++++ .../bridges/bridge-hub-kusama/Cargo.toml | 1 + .../bridges/bridge-hub-kusama/src/lib.rs | 2 +- .../src/tests/asset_transfers.rs | 128 +++++++- .../bridge-hub-kusama/src/tests/mod.rs | 31 +- .../bridge-hub-kusama/src/tests/teleport.rs | 30 ++ .../bridges/bridge-hub-polkadot/Cargo.toml | 1 + .../bridges/bridge-hub-polkadot/src/lib.rs | 1 + .../bridge-hub-polkadot/src/tests/teleport.rs | 30 ++ .../collectives-polkadot/Cargo.toml | 1 + .../src/tests/teleport.rs | 9 +- .../tests/coretime/coretime-kusama/Cargo.toml | 38 +++ .../tests/coretime/coretime-kusama/src/lib.rs | 55 ++++ .../coretime/coretime-kusama/src/tests/mod.rs | 16 + .../coretime-kusama/src/tests/teleport.rs | 46 +++ .../tests/people/people-kusama/Cargo.toml | 4 +- .../people-kusama/src/tests/teleport.rs | 172 ++-------- .../tests/people/people-polkadot/Cargo.toml | 4 +- .../people-polkadot/src/tests/teleport.rs | 172 ++-------- relay/kusama/Cargo.toml | 3 + relay/kusama/src/lib.rs | 53 ++- relay/polkadot/Cargo.toml | 3 + relay/polkadot/src/impls.rs | 1 - relay/polkadot/src/lib.rs | 53 ++- .../asset-hubs/asset-hub-kusama/Cargo.toml | 3 + .../asset-hubs/asset-hub-kusama/src/lib.rs | 53 ++- .../asset-hubs/asset-hub-polkadot/Cargo.toml | 3 + .../asset-hubs/asset-hub-polkadot/src/lib.rs | 53 ++- .../bridge-hubs/bridge-hub-kusama/Cargo.toml | 5 +- .../bridge-hubs/bridge-hub-kusama/src/lib.rs | 48 ++- .../bridge-hub-polkadot/Cargo.toml | 5 +- .../bridge-hub-polkadot/src/lib.rs | 48 ++- .../collectives-polkadot/Cargo.toml | 3 + .../collectives-polkadot/src/lib.rs | 48 ++- .../coretime/coretime-kusama/Cargo.toml | 3 + .../coretime/coretime-kusama/src/lib.rs | 50 ++- system-parachains/encointer/Cargo.toml | 3 + system-parachains/encointer/src/lib.rs | 53 ++- .../people/people-kusama/Cargo.toml | 7 +- .../people/people-kusama/src/lib.rs | 55 +++- .../people/people-polkadot/Cargo.toml | 3 + .../people/people-polkadot/src/lib.rs | 53 ++- 56 files changed, 1950 insertions(+), 377 deletions(-) create mode 100644 integration-tests/emulated/chains/parachains/coretime/coretime-kusama/Cargo.toml create mode 100644 integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/genesis.rs create mode 100644 integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/lib.rs create mode 100644 integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/xcm_fee_estimation.rs create mode 100644 integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/xcm_fee_estimation.rs create mode 100644 integration-tests/emulated/tests/coretime/coretime-kusama/Cargo.toml create mode 100644 integration-tests/emulated/tests/coretime/coretime-kusama/src/lib.rs create mode 100644 integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/mod.rs create mode 100644 integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/teleport.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index bfc50064b4..1c05a8ca37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +- All runtimes: XcmPaymentApi and DryRunApi ([polkadot-fellows/runtimes#380](https://github.com/polkadot-fellows/runtimes/pull/380)) + #### From [#322](https://github.com/polkadot-fellows/runtimes/pull/322): - Add `claim_assets` extrinsic to `pallet-xcm` ([SDK v1.9 #3403](https://github.com/paritytech/polkadot-sdk/pull/3403)). diff --git a/Cargo.lock b/Cargo.lock index 34ff7ec38c..94caf224ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,6 +575,7 @@ dependencies = [ "staging-xcm", "staging-xcm-executor", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -663,6 +664,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -709,6 +711,7 @@ dependencies = [ "staging-xcm", "staging-xcm-executor", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -794,6 +797,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -1668,6 +1672,7 @@ dependencies = [ "staging-xcm", "staging-xcm-executor", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -1772,6 +1777,7 @@ dependencies = [ "substrate-wasm-builder", "system-parachains-constants", "tuplex", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -1821,6 +1827,7 @@ dependencies = [ "staging-xcm", "staging-xcm-executor", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -1923,6 +1930,7 @@ dependencies = [ "substrate-wasm-builder", "system-parachains-constants", "tuplex", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -2357,6 +2365,7 @@ dependencies = [ "staging-xcm", "staging-xcm-executor", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -2432,6 +2441,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -2570,6 +2580,44 @@ dependencies = [ "memchr", ] +[[package]] +name = "coretime-kusama-emulated-chain" +version = "0.0.0" +dependencies = [ + "coretime-kusama-runtime", + "cumulus-primitives-core", + "emulated-integration-tests-common", + "frame-support", + "parachains-common", + "sp-core 34.0.0", +] + +[[package]] +name = "coretime-kusama-integration-tests" +version = "1.0.0" +dependencies = [ + "asset-test-utils", + "coretime-kusama-runtime", + "cumulus-pallet-parachain-system", + "emulated-integration-tests-common", + "frame-support", + "integration-tests-helpers", + "kusama-runtime-constants", + "kusama-system-emulated-network", + "pallet-balances", + "pallet-identity", + "pallet-message-queue", + "pallet-xcm", + "parachains-common", + "parity-scale-codec", + "polkadot-runtime-common", + "sp-runtime 38.0.0", + "staging-kusama-runtime", + "staging-xcm", + "staging-xcm-executor", + "xcm-fee-payment-runtime-api", +] + [[package]] name = "coretime-kusama-runtime" version = "1.0.0" @@ -2636,6 +2684,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -3826,6 +3875,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -5744,6 +5794,7 @@ version = "1.0.0" dependencies = [ "asset-hub-kusama-emulated-chain", "bridge-hub-kusama-emulated-chain", + "coretime-kusama-emulated-chain", "emulated-integration-tests-common", "kusama-emulated-chain", "penpal-emulated-chain", @@ -9282,6 +9333,7 @@ dependencies = [ "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", + "integration-tests-helpers", "kusama-runtime-constants", "kusama-system-emulated-network", "pallet-balances", @@ -9296,6 +9348,7 @@ dependencies = [ "staging-kusama-runtime", "staging-xcm", "staging-xcm-executor", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -9364,6 +9417,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -9387,6 +9441,7 @@ dependencies = [ "cumulus-pallet-parachain-system", "emulated-integration-tests-common", "frame-support", + "integration-tests-helpers", "pallet-balances", "pallet-identity", "pallet-message-queue", @@ -9401,6 +9456,7 @@ dependencies = [ "sp-runtime 38.0.0", "staging-xcm", "staging-xcm-executor", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -9467,6 +9523,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "system-parachains-constants", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -9787,6 +9844,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "tokio", + "xcm-fee-payment-runtime-api", ] [[package]] @@ -14027,6 +14085,7 @@ dependencies = [ "staging-xcm-executor", "substrate-wasm-builder", "tokio", + "xcm-fee-payment-runtime-api", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 505e163b92..6cfc0c6c6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,7 @@ codec = { package = "parity-scale-codec", version = "3.6.9", default-features = collectives-polkadot-emulated-chain = { path = "integration-tests/emulated/chains/parachains/collectives/collectives-polkadot" } collectives-polkadot-runtime = { path = "system-parachains/collectives/collectives-polkadot" } collectives-polkadot-runtime-constants = { path = "system-parachains/collectives/collectives-polkadot/constants" } +coretime-kusama-emulated-chain = { path = "integration-tests/emulated/chains/parachains/coretime/coretime-kusama" } coretime-kusama-runtime = { path = "system-parachains/coretime/coretime-kusama" } cumulus-pallet-aura-ext = { version = "0.14.0", default-features = false } cumulus-pallet-dmp-queue = { version = "0.14.0", default-features = false } @@ -235,6 +236,7 @@ xcm = { version = "14.0.0", default-features = false, package = "staging-xcm" } xcm-builder = { version = "14.0.0", default-features = false, package = "staging-xcm-builder" } xcm-emulator = { version = "0.12.0" } xcm-executor = { version = "14.0.0", default-features = false, package = "staging-xcm-executor" } +xcm-fee-payment-runtime-api = { version = "0.4.0", default-features = false } anyhow = { version = "1.0.82" } subxt = { version = "0.35.0", default-features = false } tracing-subscriber = { version = "0.3.18" } @@ -251,6 +253,7 @@ members = [ "integration-tests/emulated/chains/parachains/bridges/bridge-hub-kusama", "integration-tests/emulated/chains/parachains/bridges/bridge-hub-polkadot", "integration-tests/emulated/chains/parachains/collectives/collectives-polkadot", + "integration-tests/emulated/chains/parachains/coretime/coretime-kusama", "integration-tests/emulated/chains/parachains/people/people-kusama", "integration-tests/emulated/chains/parachains/people/people-polkadot", "integration-tests/emulated/chains/parachains/testing/penpal", @@ -265,6 +268,7 @@ members = [ "integration-tests/emulated/tests/bridges/bridge-hub-kusama", "integration-tests/emulated/tests/bridges/bridge-hub-polkadot", "integration-tests/emulated/tests/collectives/collectives-polkadot", + "integration-tests/emulated/tests/coretime/coretime-kusama", "integration-tests/emulated/tests/people/people-kusama", "integration-tests/emulated/tests/people/people-polkadot", "relay/kusama", diff --git a/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/Cargo.toml b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/Cargo.toml new file mode 100644 index 0000000000..0c24f8320b --- /dev/null +++ b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "coretime-kusama-emulated-chain" +version = "0.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +description = "Coretime Kusama emulated chain used for integration tests" +publish = false + +[dependencies] + +# Substrate +sp-core = { workspace = true, default-features = true } +frame-support = { workspace = true, default-features = true } + +# Cumulus +parachains-common = { workspace = true, default-features = true } +cumulus-primitives-core = { workspace = true, default-features = true } +emulated-integration-tests-common = { workspace = true } + +# Runtimes +coretime-kusama-runtime = { workspace = true } diff --git a/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/genesis.rs b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/genesis.rs new file mode 100644 index 0000000000..1144e62497 --- /dev/null +++ b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/genesis.rs @@ -0,0 +1,66 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Substrate +use sp_core::storage::Storage; + +// Cumulus +use emulated_integration_tests_common::{ + accounts, build_genesis_storage, collators, SAFE_XCM_VERSION, +}; +use parachains_common::Balance; + +pub const PARA_ID: u32 = 1001; +pub const ED: Balance = coretime_kusama_runtime::ExistentialDeposit::get(); + +pub fn genesis() -> Storage { + let genesis_config = coretime_kusama_runtime::RuntimeGenesisConfig { + system: coretime_kusama_runtime::SystemConfig::default(), + balances: coretime_kusama_runtime::BalancesConfig { + balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(), + }, + parachain_info: coretime_kusama_runtime::ParachainInfoConfig { + parachain_id: PARA_ID.into(), + ..Default::default() + }, + collator_selection: coretime_kusama_runtime::CollatorSelectionConfig { + invulnerables: collators::invulnerables().iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: ED * 16, + ..Default::default() + }, + session: coretime_kusama_runtime::SessionConfig { + keys: collators::invulnerables() + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + coretime_kusama_runtime::SessionKeys { aura }, // session keys + ) + }) + .collect(), + }, + polkadot_xcm: coretime_kusama_runtime::PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + build_genesis_storage( + &genesis_config, + coretime_kusama_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), + ) +} diff --git a/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/lib.rs b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/lib.rs new file mode 100644 index 0000000000..2876d1886b --- /dev/null +++ b/integration-tests/emulated/chains/parachains/coretime/coretime-kusama/src/lib.rs @@ -0,0 +1,50 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod genesis; + +// Substrate +use frame_support::traits::OnInitialize; + +// Cumulus +use emulated_integration_tests_common::{ + impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, + impls::Parachain, xcm_emulator::decl_test_parachains, +}; + +// CollectivesPolkadot Parachain declaration +decl_test_parachains! { + pub struct CoretimeKusama { + genesis = genesis::genesis(), + on_init = { + coretime_kusama_runtime::AuraExt::on_initialize(1); + }, + runtime = coretime_kusama_runtime, + core = { + XcmpMessageHandler: coretime_kusama_runtime::XcmpQueue, + LocationToAccountId: coretime_kusama_runtime::xcm_config::LocationToAccountId, + ParachainInfo: coretime_kusama_runtime::ParachainInfo, + MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin, + }, + pallets = { + PolkadotXcm: coretime_kusama_runtime::PolkadotXcm, + Balances: coretime_kusama_runtime::Balances, + } + }, +} + +// CoretimeKusama implementation +impl_accounts_helpers_for_parachain!(CoretimeKusama); +impl_assert_events_helpers_for_parachain!(CoretimeKusama); diff --git a/integration-tests/emulated/helpers/src/lib.rs b/integration-tests/emulated/helpers/src/lib.rs index 7f29c0e16b..096e91b3b7 100644 --- a/integration-tests/emulated/helpers/src/lib.rs +++ b/integration-tests/emulated/helpers/src/lib.rs @@ -40,7 +40,6 @@ macro_rules! test_relay_is_trusted_teleporter { let sender = [<$sender_relay Sender>]::get(); let mut relay_sender_balance_before = <$sender_relay as $crate::Chain>::account_data_of(sender.clone()).free; - let origin = <$sender_relay as $crate::Chain>::RuntimeOrigin::signed(sender.clone()); let fee_asset_item = 0; let weight_limit = $crate::WeightLimit::Unlimited; @@ -55,16 +54,50 @@ macro_rules! test_relay_is_trusted_teleporter { let beneficiary: Location = $crate::AccountId32 { network: None, id: receiver.clone().into() }.into(); - // Send XCM message from Relay + // Dry-run first. + let call = <$sender_relay as Chain>::RuntimeCall::XcmPallet(pallet_xcm::Call::limited_teleport_assets { + dest: bx!(para_destination.clone().into()), + beneficiary: bx!(beneficiary.clone().into()), + assets: bx!($assets.clone().into()), + fee_asset_item: fee_asset_item, + weight_limit: weight_limit.clone(), + }); + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + <$sender_relay>::execute_with(|| { + type Runtime = <$sender_relay as Chain>::Runtime; + type OriginCaller = <$sender_relay as Chain>::OriginCaller; + + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call.clone()).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(0, [Parachain(<$receiver_para>::para_id().into())])) + }) + .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 latest_delivery_fees: Assets = delivery_fees.clone().try_into().unwrap(); + let Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + delivery_fees_amount = inner_delivery_fees_amount; + }); + + // Reset to send actual message. + <$sender_relay>::reset_ext(); + <$receiver_para>::reset_ext(); + + // Send XCM message from Relay. <$sender_relay>::execute_with(|| { - assert_ok!(<$sender_relay as [<$sender_relay Pallet>]>::XcmPallet::limited_teleport_assets( - origin.clone(), - bx!(para_destination.clone().into()), - bx!(beneficiary.clone().into()), - bx!($assets.clone().into()), - fee_asset_item, - weight_limit.clone(), - )); + let origin = <$sender_relay as Chain>::RuntimeOrigin::signed(sender.clone()); + assert_ok!(call.dispatch(origin)); type RuntimeEvent = <$sender_relay as $crate::Chain>::RuntimeEvent; @@ -106,13 +139,8 @@ macro_rules! test_relay_is_trusted_teleporter { <$sender_relay as $crate::Chain>::account_data_of(sender.clone()).free; let para_receiver_balance_after = <$receiver_para as $crate::Chain>::account_data_of(receiver.clone()).free; - let delivery_fees = <$sender_relay>::execute_with(|| { - $crate::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::< - <$sender_xcm_config as xcm_executor::Config>::XcmSender, - >($assets.clone(), fee_asset_item, weight_limit.clone(), beneficiary, para_destination) - }); - assert_eq!(relay_sender_balance_before - $amount - delivery_fees, relay_sender_balance_after); + assert_eq!(relay_sender_balance_before - $amount - delivery_fees_amount, relay_sender_balance_after); assert!(para_receiver_balance_after > para_receiver_balance_before); // Update sender balance @@ -129,6 +157,13 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { $crate::paste::paste! { // init Origin variables let sender = [<$sender_para Sender>]::get(); + // Mint assets to `$sender_para` to succeed with teleport. + <$sender_para>::execute_with(|| { + assert_ok!(<$sender_para as [<$sender_para Pallet>]>::Balances::mint_into( + &sender, + $amount + 10_000_000_000, // Some extra for delivery fees. + )); + }); let mut para_sender_balance_before = <$sender_para as $crate::Chain>::account_data_of(sender.clone()).free; let origin = <$sender_para as $crate::Chain>::RuntimeOrigin::signed(sender.clone()); @@ -136,7 +171,19 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { let fee_asset_item = 0; let weight_limit = $crate::WeightLimit::Unlimited; - // init Destination variables + // We need to mint funds into the checking account of `$receiver_relay` + // for it to accept a teleport from `$sender_para`. + // Else we'd get a `NotWithdrawable` error since it tries to reduce the check account balance, which + // would be 0. + <$receiver_relay>::execute_with(|| { + let check_account = <$receiver_relay as [<$receiver_relay Pallet>]>::XcmPallet::check_account(); + assert_ok!(<$receiver_relay as [<$receiver_relay Pallet>]>::Balances::mint_into( + &check_account, + $amount, + )); + }); + + // Init destination variables. let receiver = [<$receiver_relay Receiver>]::get(); let relay_receiver_balance_before = <$receiver_relay as $crate::Chain>::account_data_of(receiver.clone()).free; @@ -144,16 +191,72 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { let beneficiary: Location = $crate::AccountId32 { network: None, id: receiver.clone().into() }.into(); - // Send XCM message from Parachain + // Dry-run first. + let call = <$sender_para as Chain>::RuntimeCall::PolkadotXcm(pallet_xcm::Call::limited_teleport_assets { + dest: bx!(relay_destination.clone().into()), + beneficiary: bx!(beneficiary.clone().into()), + assets: bx!(assets.clone().into()), + fee_asset_item: fee_asset_item, + weight_limit: weight_limit.clone(), + }); + // These will be filled in the closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); <$sender_para>::execute_with(|| { - assert_ok!(<$sender_para as [<$sender_para Pallet>]>::PolkadotXcm::limited_teleport_assets( - origin.clone(), - bx!(relay_destination.clone().into()), - bx!(beneficiary.clone().into()), - bx!(assets.clone().into()), - fee_asset_item, - weight_limit.clone(), + type Runtime = <$sender_para as Chain>::Runtime; + type OriginCaller = <$sender_para as Chain>::OriginCaller; + + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call.clone()).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [])) + }) + .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 latest_delivery_fees: Assets = delivery_fees.clone().try_into().unwrap(); + delivery_fees_amount = if let Some(first_asset) = latest_delivery_fees.inner().first() { + let Fungible(inner_delivery_fees_amount) = first_asset.fun else { + unreachable!("asset is fungible"); + }; + inner_delivery_fees_amount + } else { + 0 + } + }); + + // Reset to send actual message. + <$sender_para>::reset_ext(); + <$receiver_relay>::reset_ext(); + + // Mint assets to `$sender_para` to succeed with teleport. + <$sender_para>::execute_with(|| { + assert_ok!(<$sender_para as [<$sender_para Pallet>]>::Balances::mint_into( + &sender, + $amount + 10_000_000_000, // Some extra for delivery fees. + )); + }); + + // Since we reset everything, we need to mint funds into the checking account again. + <$receiver_relay>::execute_with(|| { + let check_account = <$receiver_relay as [<$receiver_relay Pallet>]>::XcmPallet::check_account(); + assert_ok!(<$receiver_relay as [<$receiver_relay Pallet>]>::Balances::mint_into( + &check_account, + $amount, )); + }); + + // Send XCM message from Parachain. + <$sender_para>::execute_with(|| { + let origin = <$sender_para as Chain>::RuntimeOrigin::signed(sender.clone()); + assert_ok!(call.dispatch(origin)); type RuntimeEvent = <$sender_para as $crate::Chain>::RuntimeEvent; @@ -195,13 +298,8 @@ macro_rules! test_parachain_is_trusted_teleporter_for_relay { <$sender_para as $crate::Chain>::account_data_of(sender.clone()).free; let relay_receiver_balance_after = <$receiver_relay as $crate::Chain>::account_data_of(receiver.clone()).free; - let delivery_fees = <$sender_para>::execute_with(|| { - $crate::asset_test_utils::xcm_helpers::teleport_assets_delivery_fees::< - <$sender_xcm_config as xcm_executor::Config>::XcmSender, - >(assets, fee_asset_item, weight_limit.clone(), beneficiary, relay_destination) - }); - assert_eq!(para_sender_balance_before - $amount - delivery_fees, para_sender_balance_after); + assert_eq!(para_sender_balance_before - $amount - delivery_fees_amount, para_sender_balance_after); assert!(relay_receiver_balance_after > relay_receiver_balance_before); // Update sender balance diff --git a/integration-tests/emulated/networks/kusama-system/Cargo.toml b/integration-tests/emulated/networks/kusama-system/Cargo.toml index 50f09c6512..4f8c90bded 100644 --- a/integration-tests/emulated/networks/kusama-system/Cargo.toml +++ b/integration-tests/emulated/networks/kusama-system/Cargo.toml @@ -18,3 +18,4 @@ bridge-hub-kusama-emulated-chain = { workspace = true } kusama-emulated-chain = { workspace = true } penpal-emulated-chain = { workspace = true } people-kusama-emulated-chain = { workspace = true } +coretime-kusama-emulated-chain = { workspace = true } diff --git a/integration-tests/emulated/networks/kusama-system/src/lib.rs b/integration-tests/emulated/networks/kusama-system/src/lib.rs index 775d7ece4e..ed785d60af 100644 --- a/integration-tests/emulated/networks/kusama-system/src/lib.rs +++ b/integration-tests/emulated/networks/kusama-system/src/lib.rs @@ -15,12 +15,14 @@ pub use asset_hub_kusama_emulated_chain; pub use bridge_hub_kusama_emulated_chain; +pub use coretime_kusama_emulated_chain; pub use kusama_emulated_chain; pub use penpal_emulated_chain; pub use people_kusama_emulated_chain; use asset_hub_kusama_emulated_chain::AssetHubKusama; use bridge_hub_kusama_emulated_chain::BridgeHubKusama; +use coretime_kusama_emulated_chain::CoretimeKusama; use kusama_emulated_chain::Kusama; use penpal_emulated_chain::{PenpalA, PenpalB}; use people_kusama_emulated_chain::PeopleKusama; @@ -40,6 +42,7 @@ decl_test_networks! { PenpalA, PenpalB, PeopleKusama, + CoretimeKusama, ], bridge = () }, @@ -51,5 +54,6 @@ decl_test_sender_receiver_accounts_parameter_types! { BridgeHubKusamaPara { sender: ALICE, receiver: BOB }, PenpalAPara { sender: ALICE, receiver: BOB }, PenpalBPara { sender: ALICE, receiver: BOB }, - PeopleKusamaPara { sender: ALICE, receiver: BOB } + PeopleKusamaPara { sender: ALICE, receiver: BOB }, + CoretimeKusamaPara { sender: ALICE, receiver: BOB } } diff --git a/integration-tests/emulated/tests/assets/asset-hub-kusama/Cargo.toml b/integration-tests/emulated/tests/assets/asset-hub-kusama/Cargo.toml index 18fdb9675a..e858c2ffe2 100644 --- a/integration-tests/emulated/tests/assets/asset-hub-kusama/Cargo.toml +++ b/integration-tests/emulated/tests/assets/asset-hub-kusama/Cargo.toml @@ -26,6 +26,7 @@ xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true } pallet-xcm = { workspace = true, default-features = true } polkadot-runtime-common = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus parachains-common = { workspace = true, default-features = true } diff --git a/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/mod.rs b/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/mod.rs index 8fffec23d7..88fa379c40 100644 --- a/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/mod.rs +++ b/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/mod.rs @@ -21,3 +21,4 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; +mod xcm_fee_estimation; diff --git a/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/xcm_fee_estimation.rs b/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/xcm_fee_estimation.rs new file mode 100644 index 0000000000..eebb6b9cb4 --- /dev/null +++ b/integration-tests/emulated/tests/assets/asset-hub-kusama/src/tests/xcm_fee_estimation.rs @@ -0,0 +1,301 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for XCM fee estimation in the runtime. + +use crate::{ + assert_expected_events, bx, AssetHubKusama, Chain, ParaToParaThroughAHTest, PenpalA, + PenpalAPallet, PenpalASender, PenpalAssetOwner, PenpalB, PenpalBPallet, PenpalBReceiver, + TestArgs, TestContext, TransferType, +}; +use emulated_integration_tests_common::impls::{Parachain, TestExt}; +use frame_support::{ + dispatch::RawOrigin, + sp_runtime::{traits::Dispatchable, DispatchResult}, + traits::fungibles::Inspect, +}; +use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; + +/// We are able to dry-run and estimate the fees for a multi-hop XCM journey. +/// Scenario: Alice on PenpalA has some DOTs and wants to send them to PenpalB. +/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`. +#[test] +fn multi_hop_works() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let amount_to_send = 1_000_000_000_000; + let asset_owner = PenpalAssetOwner::get(); + let assets: Assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = Location::parent(); + let sender_as_seen_by_ah = AssetHubKusama::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_ah = AssetHubKusama::sovereign_account_id_of(sender_as_seen_by_ah.clone()); + + // fund Parachain's sender account + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve. + AssetHubKusama::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]); + + // Init values for Parachain Destination + let beneficiary_id = PenpalBReceiver::get(); + + let test_args = TestContext { + sender: PenpalASender::get(), // Bob in PenpalB. + receiver: PenpalBReceiver::get(), // Alice. + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // We get them from the PenpalA closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type OriginCaller = ::OriginCaller; + + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + }) + .unwrap(); + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // These are set in the AssetHub closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Location::new(1, []).into()), + ) + .unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let result = + Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(2001)])) + }) + .unwrap(); + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ) + .unwrap(); + intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + }); + + // Dry-running is done. + PenpalA::reset_ext(); + AssetHubKusama::reset_ext(); + PenpalB::reset_ext(); + + // Fund accounts again. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + AssetHubKusama::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]); + + // Actually run the extrinsic. + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &beneficiary_id) + }); + + test.set_assertion::(sender_assertions); + test.set_assertion::(hop_assertions); + test.set_assertion::(receiver_assertions); + test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); + test.assert(); + + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!( + sender_assets_after, + sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly + * from the sender's + * account. */ + ); + assert_eq!( + receiver_assets_after, + receiver_assets_before + amount_to_send - + intermediate_execution_fees - + intermediate_delivery_fees_amount - + final_execution_fees + ); +} + +fn sender_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcm_pallet_attempted_complete(None); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.sender.account_id, + balance: *balance == test.args.amount, + }, + ] + ); +} + +fn hop_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + AssetHubKusama::assert_xcmp_queue_success(None); + + assert_expected_events!( + AssetHubKusama, + vec![ + RuntimeEvent::Balances( + pallet_balances::Event::Burned { amount, .. } + ) => { + amount: *amount == test.args.amount, + }, + ] + ); +} + +fn receiver_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcmp_queue_success(None); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, .. } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.receiver.account_id, + }, + ] + ); +} + +fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { + let latest_assets: Assets = assets.try_into().unwrap(); + let Fungible(amount) = latest_assets.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + amount +} + +fn transfer_assets_para_to_para_through_ah_dispatchable( + test: ParaToParaThroughAHTest, +) -> DispatchResult { + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + match call.dispatch(test.signed_origin) { + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } +} + +fn transfer_assets_para_to_para_through_ah_call( + test: ParaToParaThroughAHTest, +) -> ::RuntimeCall { + type RuntimeCall = ::RuntimeCall; + + let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubKusama::para_id()); + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(test.args.assets.len() as u32)), + beneficiary: test.args.beneficiary, + }]); + 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::V4(AssetId(Location::new(1, [])))), + 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, + }) +} diff --git a/integration-tests/emulated/tests/assets/asset-hub-polkadot/Cargo.toml b/integration-tests/emulated/tests/assets/asset-hub-polkadot/Cargo.toml index 3c15a06834..3b6420f7a6 100644 --- a/integration-tests/emulated/tests/assets/asset-hub-polkadot/Cargo.toml +++ b/integration-tests/emulated/tests/assets/asset-hub-polkadot/Cargo.toml @@ -25,6 +25,7 @@ polkadot-runtime-common = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true, default-features = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus asset-test-utils = { workspace = true } diff --git a/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/mod.rs b/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/mod.rs index 564257ab0d..73b73b239a 100644 --- a/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/mod.rs +++ b/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/mod.rs @@ -22,3 +22,4 @@ mod set_xcm_versions; mod swap; mod teleport; mod treasury; +mod xcm_fee_estimation; diff --git a/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/xcm_fee_estimation.rs b/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/xcm_fee_estimation.rs new file mode 100644 index 0000000000..8588e60846 --- /dev/null +++ b/integration-tests/emulated/tests/assets/asset-hub-polkadot/src/tests/xcm_fee_estimation.rs @@ -0,0 +1,302 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for XCM fee estimation in the runtime. + +use crate::{ + assert_expected_events, bx, AssetHubPolkadot, Chain, ParaToParaThroughAHTest, PenpalA, + PenpalAPallet, PenpalAReceiver, PenpalAssetOwner, PenpalB, PenpalBPallet, PenpalBSender, + TestArgs, TestContext, TransferType, +}; +use emulated_integration_tests_common::impls::{Parachain, TestExt}; +use frame_support::{ + dispatch::RawOrigin, + sp_runtime::{traits::Dispatchable, DispatchResult}, + traits::fungibles::Inspect, +}; +use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; + +/// We are able to dry-run and estimate the fees for a multi-hop XCM journey. +/// Scenario: Alice on PenpalB has some DOTs and wants to send them to PenpalA. +/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`. +#[test] +fn multi_hop_works() { + let destination = PenpalB::sibling_location_of(PenpalA::para_id()); + let sender = PenpalBSender::get(); + let amount_to_send = 1_000_000_000_000; // One DOT is 10 decimals but it's configured in Penpal as 12. + let asset_owner = PenpalAssetOwner::get(); + let assets: Assets = (Parent, amount_to_send).into(); + let relay_native_asset_location = Location::parent(); + let sender_as_seen_by_ah = AssetHubPolkadot::sibling_location_of(PenpalB::para_id()); + let sov_of_sender_on_ah = + AssetHubPolkadot::sovereign_account_id_of(sender_as_seen_by_ah.clone()); + + // fund Parachain's sender account + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner.clone()), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + + // fund the Parachain Origin's SA on AssetHub with the native tokens held in reserve. + AssetHubPolkadot::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]); + + // Init values for Parachain Destination + let beneficiary_id = PenpalAReceiver::get(); + + let test_args = TestContext { + sender: PenpalBSender::get(), // Bob in PenpalB. + receiver: PenpalAReceiver::get(), // Alice. + args: TestArgs::new_para( + destination, + beneficiary_id.clone(), + amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // We get them from the PenpalB closure. + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type OriginCaller = ::OriginCaller; + + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + let origin = OriginCaller::system(RawOrigin::Signed(sender.clone())); + let result = Runtime::dry_run_call(origin, call).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1000)])) + }) + .unwrap(); + assert_eq!(messages_to_query.len(), 1); + remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // These are set in the AssetHub closure. + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm::<()>(Vec::new())); + ::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = Runtime::query_weight_to_asset_fee( + weight, + VersionedAssetId::V4(Location::new(1, []).into()), + ) + .unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let result = + Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap(); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(2000)])) + }) + .unwrap(); + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = Runtime::query_delivery_fees( + destination_to_query.clone(), + intermediate_remote_message.clone(), + ) + .unwrap(); + intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees); + }); + + // Get the final execution fees in the destination. + let mut final_execution_fees = 0; + ::execute_with(|| { + type Runtime = ::Runtime; + + let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap(); + final_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + }); + + // Dry-running is done. + PenpalB::reset_ext(); + AssetHubPolkadot::reset_ext(); + PenpalA::reset_ext(); + + // Fund accounts again. + PenpalB::mint_foreign_asset( + ::RuntimeOrigin::signed(asset_owner), + relay_native_asset_location.clone(), + sender.clone(), + amount_to_send * 2, + ); + AssetHubPolkadot::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]); + + // Actually run the extrinsic. + let sender_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &beneficiary_id) + }); + + test.set_assertion::(sender_assertions); + test.set_assertion::(hop_assertions); + test.set_assertion::(receiver_assertions); + test.set_dispatchable::(transfer_assets_para_to_para_through_ah_dispatchable); + test.assert(); + + let sender_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location.clone(), &sender) + }); + let receiver_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(relay_native_asset_location, &beneficiary_id) + }); + + // We know the exact fees on every hop. + assert_eq!( + sender_assets_after, + sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly + * from the sender's + * account. */ + ); + assert_eq!( + receiver_assets_after, + receiver_assets_before + amount_to_send - + intermediate_execution_fees - + intermediate_delivery_fees_amount - + final_execution_fees + ); +} + +fn sender_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalB::assert_xcm_pallet_attempted_complete(None); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Burned { asset_id, owner, balance } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.sender.account_id, + balance: *balance == test.args.amount, + }, + ] + ); +} + +fn hop_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + AssetHubPolkadot::assert_xcmp_queue_success(None); + + assert_expected_events!( + AssetHubPolkadot, + vec![ + RuntimeEvent::Balances( + pallet_balances::Event::Burned { amount, .. } + ) => { + amount: *amount == test.args.amount, + }, + ] + ); +} + +fn receiver_assertions(test: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + PenpalA::assert_xcmp_queue_success(None); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::ForeignAssets( + pallet_assets::Event::Issued { asset_id, owner, .. } + ) => { + asset_id: *asset_id == Location::new(1, []), + owner: *owner == test.receiver.account_id, + }, + ] + ); +} + +fn get_amount_from_versioned_assets(assets: VersionedAssets) -> u128 { + let latest_assets: Assets = assets.try_into().unwrap(); + let Fungible(amount) = latest_assets.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + amount +} + +fn transfer_assets_para_to_para_through_ah_dispatchable( + test: ParaToParaThroughAHTest, +) -> DispatchResult { + let call = transfer_assets_para_to_para_through_ah_call(test.clone()); + match call.dispatch(test.signed_origin) { + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } +} + +fn transfer_assets_para_to_para_through_ah_call( + test: ParaToParaThroughAHTest, +) -> ::RuntimeCall { + type RuntimeCall = ::RuntimeCall; + + let asset_hub_location: Location = PenpalB::sibling_location_of(AssetHubPolkadot::para_id()); + let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { + assets: Wild(AllCounted(test.args.assets.len() as u32)), + beneficiary: test.args.beneficiary, + }]); + 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::V4(AssetId(Location::new(1, [])))), + 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, + }) +} diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml index b3385f8dfb..7076d34d39 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/Cargo.toml @@ -25,6 +25,7 @@ pallet-message-queue = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true, default-features = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus emulated-integration-tests-common = { workspace = true } diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/lib.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/lib.rs index c930e54f2c..674bf32bb9 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/lib.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/lib.rs @@ -15,7 +15,7 @@ // Substrate pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; -pub use sp_runtime::DispatchError; +pub use sp_runtime::{traits::Dispatchable, DispatchError}; // Polkadot pub use xcm::{ diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/asset_transfers.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/asset_transfers.rs index 5c94dde5b2..9304093e39 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/asset_transfers.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/asset_transfers.rs @@ -14,7 +14,11 @@ // limitations under the License. use crate::tests::*; -use frame_support::traits::fungible::Mutate; +use frame_support::{dispatch::RawOrigin, traits::fungible::Mutate}; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; fn send_asset_from_asset_hub_kusama_to_asset_hub_polkadot(id: Location, amount: u128) { let destination = asset_hub_polkadot_location(); @@ -32,6 +36,113 @@ fn send_asset_from_asset_hub_kusama_to_asset_hub_polkadot(id: Location, amount: assert_bridge_hub_polkadot_message_received(); } +fn dry_run_send_asset_from_asset_hub_kusama_to_asset_hub_polkadot( + id: Location, + amount: u128, +) -> u128 { + let destination = asset_hub_polkadot_location(); + + // fund the AHK's SA on BHK for paying bridge transport fees + BridgeHubKusama::fund_para_sovereign(AssetHubKusama::para_id(), 10_000_000_000_000u128); + + // set XCM versions + AssetHubKusama::force_xcm_version(destination.clone(), XCM_VERSION); + BridgeHubKusama::force_xcm_version(bridge_hub_polkadot_location(), XCM_VERSION); + + let beneficiary: Location = + AccountId32Junction { id: AssetHubPolkadotReceiver::get().into(), network: None }.into(); + let assets: Assets = (id, amount).into(); + let fee_asset_item = 0; + let call = + send_asset_from_asset_hub_kusama_call(destination, beneficiary, assets, fee_asset_item); + let mut delivery_fees_amount = 0; + let mut remote_message = VersionedXcm::V4(Xcm(Vec::new())); + AssetHubKusama::execute_with(|| { + type Runtime = ::Runtime; + type OriginCaller = ::OriginCaller; + + let origin = OriginCaller::system(RawOrigin::Signed(AssetHubKusamaSender::get())); + let result = Runtime::dry_run_call(origin, call).unwrap(); + // We filter the result to get only the messages we are interested in. + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1002)])) + }) + .unwrap(); + dbg!(&result.forwarded_xcms); + 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 latest_delivery_fees: Assets = delivery_fees.clone().try_into().unwrap(); + let Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + delivery_fees_amount = inner_delivery_fees_amount; + }); + + let mut intermediate_execution_fees = 0; + let mut intermediate_delivery_fees_amount = 0; + let mut intermediate_remote_message = VersionedXcm::V4(Xcm(Vec::new())); + BridgeHubKusama::execute_with(|| { + type Runtime = ::Runtime; + type RuntimeCall = ::RuntimeCall; + + // First we get the execution fees. + let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap(); + intermediate_execution_fees = + Runtime::query_weight_to_asset_fee(weight, VersionedAssetId::V4(Parent.into())) + .unwrap(); + + // We have to do this to turn `VersionedXcm<()>` into `VersionedXcm`. + let xcm_program = + VersionedXcm::V4(Xcm::::from(remote_message.clone().try_into().unwrap())); + + // Now we get the delivery fees to the final destination. + let asset_hub_as_seen_by_bridge_hub: Location = Location::new(1, [Parachain(1000)]); + let result = + Runtime::dry_run_xcm(asset_hub_as_seen_by_bridge_hub.clone().into(), xcm_program) + .unwrap(); + + // We filter the result to get only the messages we are interested in. + // dbg!(&result.forwarded_xcms); + let (destination_to_query, messages_to_query) = &result + .forwarded_xcms + .iter() + .find(|(destination, _)| { + *destination == VersionedLocation::V4(Location::new(1, [Parachain(1002)])) + }) + .unwrap(); + // There's actually two messages here. + // One created when the message we sent from PenpalA arrived and was executed. + // The second one when we dry-run the xcm. + // We could've gotten the message from the queue without having to dry-run, but + // offchain applications would have to dry-run, so we do it here as well. + intermediate_remote_message = messages_to_query[0].clone(); + let delivery_fees = + Runtime::query_delivery_fees(destination_to_query.clone(), remote_message.clone()) + .unwrap(); + let latest_delivery_fees: Assets = delivery_fees.clone().try_into().unwrap(); + let Fungible(inner_delivery_fees_amount) = latest_delivery_fees.inner()[0].fun else { + unreachable!("asset is fungible"); + }; + intermediate_delivery_fees_amount = inner_delivery_fees_amount; + }); + + dbg!(&delivery_fees_amount); + dbg!(&intermediate_execution_fees); + dbg!(&intermediate_delivery_fees_amount); + + // After dry-running we reset. + AssetHubKusama::reset_ext(); + BridgeHubKusama::reset_ext(); + + delivery_fees_amount +} + #[test] fn send_ksms_from_asset_hub_kusama_to_asset_hub_polkadot() { let ksm_at_asset_hub_kusama: v3::Location = v3::Parent.into(); @@ -61,6 +172,12 @@ fn send_ksms_from_asset_hub_kusama_to_asset_hub_polkadot() { let ksm_at_asset_hub_kusama_latest: Location = ksm_at_asset_hub_kusama.try_into().unwrap(); let amount = ASSET_HUB_KUSAMA_ED * 1_000; + // First dry-run. + let delivery_fees = dry_run_send_asset_from_asset_hub_kusama_to_asset_hub_polkadot( + ksm_at_asset_hub_kusama_latest.clone(), + amount, + ); + // Then send. send_asset_from_asset_hub_kusama_to_asset_hub_polkadot(ksm_at_asset_hub_kusama_latest, amount); AssetHubPolkadot::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -89,8 +206,15 @@ fn send_ksms_from_asset_hub_kusama_to_asset_hub_polkadot() { let ksms_in_reserve_on_ahk_after = ::account_data_of(sov_ahp_on_ahk.clone()).free; + dbg!(&sender_ksms_after); + dbg!(&sender_ksms_before); + dbg!(&amount); + dbg!(&delivery_fees); + dbg!(&receiver_ksms_after); + dbg!(&receiver_ksms_before); + // Sender's balance is reduced - assert!(sender_ksms_before > sender_ksms_after); + assert_eq!(sender_ksms_after, sender_ksms_before - amount - delivery_fees); // Receiver's balance is increased assert!(receiver_ksms_after > receiver_ksms_before); // Reserve balance is reduced by sent amount diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs index 9d5d7a9f38..fee51267b6 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/mod.rs @@ -49,17 +49,32 @@ pub(crate) fn send_asset_from_asset_hub_kusama( let fee_asset_item = 0; AssetHubKusama::execute_with(|| { - ::PolkadotXcm::limited_reserve_transfer_assets( - signed_origin, - bx!(destination.into()), - bx!(beneficiary.into()), - bx!(assets.into()), - fee_asset_item, - WeightLimit::Unlimited, - ) + let call = + send_asset_from_asset_hub_kusama_call(destination, beneficiary, assets, fee_asset_item); + match call.dispatch(signed_origin) { + Ok(_) => Ok(()), + Err(error_with_post_info) => Err(error_with_post_info.error), + } }) } +pub(crate) fn send_asset_from_asset_hub_kusama_call( + destination: Location, + beneficiary: Location, + assets: Assets, + fee_asset_item: u32, +) -> ::RuntimeCall { + ::RuntimeCall::PolkadotXcm( + pallet_xcm::Call::limited_reserve_transfer_assets { + dest: bx!(destination.into()), + beneficiary: bx!(beneficiary.into()), + assets: bx!(assets.into()), + fee_asset_item, + weight_limit: WeightLimit::Unlimited, + }, + ) +} + pub(crate) fn assert_bridge_hub_kusama_message_accepted(expected_processed: bool) { BridgeHubKusama::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/teleport.rs b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/teleport.rs index c1aebaabfc..58179e8c53 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/teleport.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-kusama/src/tests/teleport.rs @@ -15,6 +15,16 @@ use crate::*; use bridge_hub_kusama_runtime::xcm_config::XcmConfig; +use frame_support::{ + dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; +use integration_tests_helpers::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; #[test] fn teleport_to_other_system_parachains_works() { @@ -28,3 +38,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = KUSAMA_ED * 1000; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Kusama, + KusamaXcmConfig, + vec![BridgeHubKusama], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubKusama, + BridgeHubKusamaXcmConfig, + Kusama, + amount + ); +} diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml index c4034d7913..bb9ffdf9bb 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/Cargo.toml @@ -25,6 +25,7 @@ pallet-message-queue = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true, default-features = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus emulated-integration-tests-common = { workspace = true } diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs index 9aefaed933..5ab2ff4e60 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/lib.rs @@ -60,6 +60,7 @@ pub use kusama_polkadot_system_emulated_network::{ AssetHubPolkadotParaSender as AssetHubPolkadotSender, BridgeHubKusamaPara as BridgeHubKusama, BridgeHubPolkadotPara as BridgeHubPolkadot, BridgeHubPolkadotParaSender as BridgeHubPolkadotSender, PolkadotRelay as Polkadot, + PolkadotRelayReceiver as PolkadotReceiver, PolkadotRelaySender as PolkadotSender, }; pub use parachains_common::{AccountId, Balance}; pub use polkadot_system_emulated_network::{ diff --git a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/teleport.rs b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/teleport.rs index 76eec63125..07664c0d28 100644 --- a/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/teleport.rs +++ b/integration-tests/emulated/tests/bridges/bridge-hub-polkadot/src/tests/teleport.rs @@ -15,6 +15,16 @@ use crate::*; use bridge_hub_polkadot_runtime::xcm_config::XcmConfig; +use frame_support::{ + dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; +use integration_tests_helpers::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; #[test] fn teleport_to_other_system_parachains_works() { @@ -28,3 +38,23 @@ fn teleport_to_other_system_parachains_works() { (native_asset, amount) ); } + +#[test] +fn teleport_from_and_to_relay() { + let amount = BRIDGE_HUB_POLKADOT_ED * 1000; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Polkadot, + PolkadotXcmConfig, + vec![BridgeHubPolkadot], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + BridgeHubPolkadot, + BridgeHubPolkadotXcmConfig, + Polkadot, + amount + ); +} diff --git a/integration-tests/emulated/tests/collectives/collectives-polkadot/Cargo.toml b/integration-tests/emulated/tests/collectives/collectives-polkadot/Cargo.toml index 9888f770f9..6a9e80de9b 100644 --- a/integration-tests/emulated/tests/collectives/collectives-polkadot/Cargo.toml +++ b/integration-tests/emulated/tests/collectives/collectives-polkadot/Cargo.toml @@ -28,6 +28,7 @@ polkadot-runtime-common = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true, default-features = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus asset-test-utils = { workspace = true } diff --git a/integration-tests/emulated/tests/collectives/collectives-polkadot/src/tests/teleport.rs b/integration-tests/emulated/tests/collectives/collectives-polkadot/src/tests/teleport.rs index 27ccd27b55..d292fd195b 100644 --- a/integration-tests/emulated/tests/collectives/collectives-polkadot/src/tests/teleport.rs +++ b/integration-tests/emulated/tests/collectives/collectives-polkadot/src/tests/teleport.rs @@ -16,11 +16,16 @@ use crate::*; use asset_hub_polkadot_runtime::xcm_config::XcmConfig as AssetHubPolkadotXcmConfig; use collectives_polkadot_runtime::xcm_config::XcmConfig as CollectivesPolkadotXcmConfig; -use frame_support::assert_ok; +use frame_support::{ + assert_ok, dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; use integration_tests_helpers::{ test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, }; -use polkadot_runtime::xcm_config::XcmConfig as PolkadotXcmConfig; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; #[test] fn teleport_from_and_to_relay() { diff --git a/integration-tests/emulated/tests/coretime/coretime-kusama/Cargo.toml b/integration-tests/emulated/tests/coretime/coretime-kusama/Cargo.toml new file mode 100644 index 0000000000..45acad6728 --- /dev/null +++ b/integration-tests/emulated/tests/coretime/coretime-kusama/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "coretime-kusama-integration-tests" +version.workspace = true +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +description = "Coretime Kusama runtime integration tests with xcm-emulator" +publish = false + +[dependencies] +codec = { workspace = true, default-features = true } + +# Substrate +sp-runtime = { workspace = true, default-features = true } +frame-support = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-message-queue = { workspace = true, default-features = true } +pallet-identity = { workspace = true, default-features = true } + +# Polkadot +polkadot-runtime-common = { workspace = true, default-features = true } +pallet-xcm = { workspace = true, default-features = true } +xcm = { workspace = true, default-features = true } +xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } + +# Cumulus +parachains-common = { workspace = true, default-features = true } +emulated-integration-tests-common = { workspace = true } +asset-test-utils = { workspace = true } +cumulus-pallet-parachain-system = { workspace = true, default-features = true } + +# Local +kusama-runtime-constants = { workspace = true, default-features = true } +kusama-runtime = { workspace = true } +integration-tests-helpers = { workspace = true } +coretime-kusama-runtime = { workspace = true } +kusama-system-emulated-network = { workspace = true } diff --git a/integration-tests/emulated/tests/coretime/coretime-kusama/src/lib.rs b/integration-tests/emulated/tests/coretime/coretime-kusama/src/lib.rs new file mode 100644 index 0000000000..0d3e3ac75c --- /dev/null +++ b/integration-tests/emulated/tests/coretime/coretime-kusama/src/lib.rs @@ -0,0 +1,55 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use codec::Encode; + +// Substrate +pub use frame_support::{ + assert_err, assert_ok, + pallet_prelude::Weight, + sp_runtime::{AccountId32, DispatchError, DispatchResult}, + traits::fungibles::Inspect, +}; + +// Polkadot +pub use xcm::{ + prelude::{AccountId32 as AccountId32Junction, *}, + v3::{Error, NetworkId::Kusama as KusamaId}, +}; + +// Cumulus +pub use asset_test_utils::xcm_helpers; +pub use emulated_integration_tests_common::{ + xcm_emulator::{ + assert_expected_events, bx, helpers::weight_within_threshold, Chain, Parachain as Para, + RelayChain as Relay, Test, TestArgs, TestContext, TestExt, + }, + xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, + PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, XCM_V3, +}; +pub use kusama_system_emulated_network::{ + coretime_kusama_emulated_chain::{ + genesis::ED as CORETIME_KUSAMA_ED, CoretimeKusamaParaPallet as CoretimeKusamaPallet, + }, + kusama_emulated_chain::{genesis::ED as KUSAMA_ED, KusamaRelayPallet as KusamaPallet}, + CoretimeKusamaPara as CoretimeKusama, CoretimeKusamaParaReceiver as CoretimeKusamaReceiver, + CoretimeKusamaParaSender as CoretimeKusamaSender, KusamaRelay as Kusama, + KusamaRelayReceiver as KusamaReceiver, KusamaRelaySender as KusamaSender, + PenpalAPara as PenpalA, +}; +pub use parachains_common::{AccountId, Balance}; + +#[cfg(test)] +mod tests; diff --git a/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/mod.rs b/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/mod.rs new file mode 100644 index 0000000000..516ec37cc1 --- /dev/null +++ b/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/mod.rs @@ -0,0 +1,16 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod teleport; diff --git a/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/teleport.rs b/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/teleport.rs new file mode 100644 index 0000000000..0e36821279 --- /dev/null +++ b/integration-tests/emulated/tests/coretime/coretime-kusama/src/tests/teleport.rs @@ -0,0 +1,46 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::{ + dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; +use integration_tests_helpers::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; + +#[test] +fn teleport_from_and_to_relay() { + let amount = KUSAMA_ED * 1000; + let native_asset: Assets = (Here, amount).into(); + + test_relay_is_trusted_teleporter!( + Kusama, + KusamaXcmConfig, + vec![CoretimeKusama], + (native_asset, amount) + ); + + test_parachain_is_trusted_teleporter_for_relay!( + CoretimeKusama, + CoretimeKusamaXcmConfig, + Kusama, + amount + ); +} diff --git a/integration-tests/emulated/tests/people/people-kusama/Cargo.toml b/integration-tests/emulated/tests/people/people-kusama/Cargo.toml index 58b1a66d8b..140732df73 100644 --- a/integration-tests/emulated/tests/people/people-kusama/Cargo.toml +++ b/integration-tests/emulated/tests/people/people-kusama/Cargo.toml @@ -19,9 +19,10 @@ pallet-identity = { workspace = true, default-features = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } -xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } +xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus parachains-common = { workspace = true, default-features = true } @@ -32,5 +33,6 @@ cumulus-pallet-parachain-system = { workspace = true, default-features = true } # Local kusama-runtime-constants = { workspace = true, default-features = true } kusama-runtime = { workspace = true } +integration-tests-helpers = { workspace = true } people-kusama-runtime = { workspace = true } kusama-system-emulated-network = { workspace = true } diff --git a/integration-tests/emulated/tests/people/people-kusama/src/tests/teleport.rs b/integration-tests/emulated/tests/people/people-kusama/src/tests/teleport.rs index 33647a3c43..33eb8d2bb8 100644 --- a/integration-tests/emulated/tests/people/people-kusama/src/tests/teleport.rs +++ b/integration-tests/emulated/tests/people/people-kusama/src/tests/teleport.rs @@ -14,52 +14,35 @@ // limitations under the License. use crate::*; -use kusama_runtime::xcm_config::XcmConfig as KusamaXcmConfig; +use frame_support::{ + dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; +use integration_tests_helpers::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; use people_kusama_runtime::xcm_config::XcmConfig as PeopleKusamaXcmConfig; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Kusama::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); +#[test] +fn teleport_from_and_to_relay() { + let amount = KUSAMA_ED * 1000; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Kusama, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Kusama::assert_ump_queue_processed( - true, - Some(PeopleKusama::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), + KusamaXcmConfig, + vec![PeopleKusama], + (native_asset, amount) ); - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeopleKusama, + PeopleKusamaXcmConfig, Kusama, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } @@ -93,33 +76,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeopleKusama::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); - - assert_expected_events!( - PeopleKusama, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -131,90 +87,6 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = KUSAMA_ED * 1000; - let dest = Kusama::child_location_of(PeopleKusama::para_id()); - let beneficiary_id = PeopleKusamaReceiver::get(); - let test_args = TestContext { - sender: KusamaSender::get(), - receiver: PeopleKusamaReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Kusama::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_KUSAMA_ED * 1000; - let destination = PeopleKusama::parent_location(); - let beneficiary_id = KusamaReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeopleKusama::fund_accounts(vec![(PeopleKusamaSender::get(), KUSAMA_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeopleKusamaSender::get(), - receiver: KusamaReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeopleKusama::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain /// should't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] diff --git a/integration-tests/emulated/tests/people/people-polkadot/Cargo.toml b/integration-tests/emulated/tests/people/people-polkadot/Cargo.toml index 5276c94e56..de541e552b 100644 --- a/integration-tests/emulated/tests/people/people-polkadot/Cargo.toml +++ b/integration-tests/emulated/tests/people/people-polkadot/Cargo.toml @@ -19,9 +19,10 @@ pallet-identity = { workspace = true, default-features = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } -xcm = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } +xcm = { workspace = true, default-features = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true, default-features = true } # Cumulus parachains-common = { workspace = true, default-features = true } @@ -32,5 +33,6 @@ cumulus-pallet-parachain-system = { workspace = true, default-features = true } # Local polkadot-runtime-constants = { workspace = true, default-features = true } polkadot-runtime = { workspace = true } +integration-tests-helpers = { workspace = true } people-polkadot-runtime = { workspace = true } polkadot-system-emulated-network = { workspace = true } diff --git a/integration-tests/emulated/tests/people/people-polkadot/src/tests/teleport.rs b/integration-tests/emulated/tests/people/people-polkadot/src/tests/teleport.rs index 3907a941a5..1904e53405 100644 --- a/integration-tests/emulated/tests/people/people-polkadot/src/tests/teleport.rs +++ b/integration-tests/emulated/tests/people/people-polkadot/src/tests/teleport.rs @@ -14,52 +14,35 @@ // limitations under the License. use crate::*; +use frame_support::{ + dispatch::RawOrigin, sp_runtime::traits::Dispatchable, traits::fungible::Mutate, +}; +use integration_tests_helpers::{ + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, +}; use people_polkadot_runtime::xcm_config::XcmConfig as PeoplePolkadotXcmConfig; -use polkadot_runtime::xcm_config::XcmConfig as PolkadotXcmConfig; +use xcm_fee_payment_runtime_api::{ + dry_run::runtime_decl_for_dry_run_api::DryRunApiV1, + fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV1, +}; -fn relay_origin_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - Polkadot::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(627_959_000, 7_200))); +#[test] +fn teleport_from_and_to_relay() { + let amount = KUSAMA_ED * 1000; + let native_asset: Assets = (Here, amount).into(); - assert_expected_events!( + test_relay_is_trusted_teleporter!( Polkadot, - vec![ - // Amount to teleport is withdrawn from Sender - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == t.sender.account_id, - amount: *amount == t.args.amount, - }, - // Amount to teleport is deposited in Relay's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - ] - ); -} - -fn relay_dest_assertions(t: SystemParaToRelayTest) { - type RuntimeEvent = ::RuntimeEvent; - - Polkadot::assert_ump_queue_processed( - true, - Some(PeoplePolkadot::para_id()), - Some(Weight::from_parts(304_266_000, 7_186)), + PolkadotXcmConfig, + vec![PeoplePolkadot], + (native_asset, amount) ); - assert_expected_events!( + test_parachain_is_trusted_teleporter_for_relay!( + PeoplePolkadot, + PeoplePolkadotXcmConfig, Polkadot, - vec![ - // Amount is withdrawn from Relay Chain's `CheckAccount` - RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) => { - who: *who == ::XcmPallet::check_account(), - amount: *amount == t.args.amount, - }, - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] + amount ); } @@ -93,33 +76,6 @@ fn para_origin_assertions(t: SystemParaToRelayTest) { ); } -fn para_dest_assertions(t: RelayToSystemParaTest) { - type RuntimeEvent = ::RuntimeEvent; - - PeoplePolkadot::assert_dmp_queue_complete(Some(Weight::from_parts(162_456_000, 0))); - - assert_expected_events!( - PeoplePolkadot, - vec![ - // Amount minus fees are deposited in Receiver's account - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, .. }) => { - who: *who == t.receiver.account_id, - }, - ] - ); -} - -fn relay_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult { - ::XcmPallet::limited_teleport_assets( - t.signed_origin, - bx!(t.args.dest.into()), - bx!(t.args.beneficiary.into()), - bx!(t.args.assets.into()), - t.args.fee_asset_item, - t.args.weight_limit, - ) -} - fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult { ::PolkadotXcm::limited_teleport_assets( t.signed_origin, @@ -131,90 +87,6 @@ fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResu ) } -/// Limited Teleport of native asset from Relay Chain to the System Parachain should work -#[test] -fn limited_teleport_native_assets_from_relay_to_system_para_works() { - // Init values for Relay Chain - let amount_to_send: Balance = KUSAMA_ED * 1000; - let dest = Polkadot::child_location_of(PeoplePolkadot::para_id()); - let beneficiary_id = PeoplePolkadotReceiver::get(); - let test_args = TestContext { - sender: PolkadotSender::get(), - receiver: PeoplePolkadotReceiver::get(), - args: TestArgs::new_relay(dest, beneficiary_id, amount_to_send), - }; - - let mut test = RelayToSystemParaTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(relay_origin_assertions); - test.set_assertion::(para_dest_assertions); - test.set_dispatchable::(relay_limited_teleport_assets); - test.assert(); - - let delivery_fees = Polkadot::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - -/// Limited Teleport of native asset from System Parachain to Relay Chain -/// should work when there is enough balance in Relay Chain's `CheckAccount` -#[test] -fn limited_teleport_native_assets_back_from_system_para_to_relay_works() { - // Dependency - Relay Chain's `CheckAccount` should have enough balance - limited_teleport_native_assets_from_relay_to_system_para_works(); - - let amount_to_send: Balance = PEOPLE_KUSAMA_ED * 1000; - let destination = PeoplePolkadot::parent_location(); - let beneficiary_id = PolkadotReceiver::get(); - let assets = (Parent, amount_to_send).into(); - - // Fund a sender - PeoplePolkadot::fund_accounts(vec![(PeoplePolkadotSender::get(), KUSAMA_ED * 2_000u128)]); - - let test_args = TestContext { - sender: PeoplePolkadotSender::get(), - receiver: PolkadotReceiver::get(), - args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0), - }; - - let mut test = SystemParaToRelayTest::new(test_args); - - let sender_balance_before = test.sender.balance; - let receiver_balance_before = test.receiver.balance; - - test.set_assertion::(para_origin_assertions); - test.set_assertion::(relay_dest_assertions); - test.set_dispatchable::(system_para_limited_teleport_assets); - test.assert(); - - let sender_balance_after = test.sender.balance; - let receiver_balance_after = test.receiver.balance; - - let delivery_fees = PeoplePolkadot::execute_with(|| { - xcm_helpers::teleport_assets_delivery_fees::< - ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) - }); - - // Sender's balance is reduced - assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after); - // Receiver's balance is increased - assert!(receiver_balance_after > receiver_balance_before); -} - /// Limited Teleport of native asset from System Parachain to Relay Chain /// should't work when there is not enough balance in Relay Chain's `CheckAccount` #[test] diff --git a/relay/kusama/Cargo.toml b/relay/kusama/Cargo.toml index 7c0cf5d39b..e5c7b53bcb 100644 --- a/relay/kusama/Cargo.toml +++ b/relay/kusama/Cargo.toml @@ -101,6 +101,7 @@ polkadot-primitives = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } xcm-builder = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } sp-debug-derive = { workspace = true } @@ -210,6 +211,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std" ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -261,6 +263,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks" ] try-runtime = [ "frame-election-provider-support/try-runtime", diff --git a/relay/kusama/src/lib.rs b/relay/kusama/src/lib.rs index b2949c5585..a37f74740e 100644 --- a/relay/kusama/src/lib.rs +++ b/relay/kusama/src/lib.rs @@ -81,7 +81,7 @@ use frame_support::{ InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, StorageMapShim, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::EnsureRoot; @@ -102,11 +102,12 @@ use sp_staking::SessionIndex; #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{ - latest::{InteriorLocation, Junction, Junction::PalletInstance}, - VersionedLocation, -}; +use xcm::prelude::*; use xcm_builder::PayOverXcm; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -2237,6 +2238,48 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::TokenLocation::get())]; + XcmPallet::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + 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_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + XcmPallet::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + XcmPallet::dry_run_xcm::(origin_location, xcm) + } + } + impl pallet_nomination_pools_runtime_api::NominationPoolsApi< Block, AccountId, diff --git a/relay/polkadot/Cargo.toml b/relay/polkadot/Cargo.toml index f11edec300..968bac44d0 100644 --- a/relay/polkadot/Cargo.toml +++ b/relay/polkadot/Cargo.toml @@ -99,6 +99,7 @@ polkadot-primitives = { workspace = true } xcm = { workspace = true } xcm-executor = { workspace = true } xcm-builder = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } sp-debug-derive = { workspace = true } @@ -207,6 +208,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -256,6 +258,7 @@ runtime-benchmarks = [ "sp-staking/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "frame-election-provider-support/try-runtime", diff --git a/relay/polkadot/src/impls.rs b/relay/polkadot/src/impls.rs index efce64c99a..fa690a1a8d 100644 --- a/relay/polkadot/src/impls.rs +++ b/relay/polkadot/src/impls.rs @@ -26,7 +26,6 @@ use frame_system::RawOrigin; use polkadot_primitives::Id as ParaId; use polkadot_runtime_common::identity_migrator::{OnReapIdentity, WeightInfo}; use polkadot_runtime_constants::system_parachain::PEOPLE_ID; -use xcm::{latest::prelude::*, VersionedXcm}; use xcm_builder::IsChildSystemParachain; use xcm_executor::traits::TransactAsset; diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index b45360a8b7..631fcc9cdc 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -65,7 +65,7 @@ use frame_support::{ Get, InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, ProcessMessageError, WithdrawReasons, }, - weights::{ConstantMultiplier, WeightMeter}, + weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::EnsureRoot; @@ -102,11 +102,12 @@ use sp_std::{ #[cfg(any(feature = "std", test))] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use xcm::{ - latest::{InteriorLocation, Junction, Junction::PalletInstance}, - VersionedLocation, -}; +use xcm::prelude::*; use xcm_builder::PayOverXcm; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; @@ -2336,6 +2337,48 @@ sp_api::impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::TokenLocation::get())]; + XcmPallet::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + 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_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + XcmPallet::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + XcmPallet::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + XcmPallet::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + XcmPallet::dry_run_xcm::(origin_location, xcm) + } + } + impl sp_genesis_builder::GenesisBuilder for Runtime { fn build_state(config: Vec) -> sp_genesis_builder::Result { build_state::(config) diff --git a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml index 1184dfc2f2..2e7bd9d438 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml @@ -81,6 +81,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -157,6 +158,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -272,6 +274,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] # Enable metadata hash generation at compile time for the `CheckMetadataHash` extension. diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs index 09be6931df..42b4befc18 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs @@ -63,7 +63,7 @@ use frame_support::{ ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, EnsureOrigin, EnsureOriginWithArg, Equals, InstanceFilter, TransformOrigin, WithdrawReasons, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, }; use frame_system::{ @@ -81,12 +81,19 @@ use system_parachains_constants::{ kusama::{consensus::*, currency::*, fee::WeightToFee}, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, }; -use xcm::latest::prelude::{AssetId, BodyId}; +use xcm::{ + latest::prelude::{AssetId, BodyId}, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; use xcm_config::{ FellowshipLocation, ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, KsmLocation, KsmLocationV3, PoolAssetsConvertedConcreteId, TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocationV3, }; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -1283,6 +1290,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::KsmLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::KsmLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl assets_common::runtime_api::FungiblesApi< Block, AccountId, diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index 4dce9f6a45..081e61c224 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -81,6 +81,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -145,6 +146,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -254,6 +256,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] # Enable metadata hash generation at compile time for the `CheckMetadataHash` extension. diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs index f163a6bcfa..db4537a508 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/src/lib.rs @@ -80,6 +80,10 @@ use sp_runtime::{ ApplyExtrinsicResult, Perbill, Permill, }; use xcm_config::TrustBackedAssetsPalletLocationV3; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -97,7 +101,7 @@ use frame_support::{ ConstU32, ConstU64, ConstU8, EitherOfDiverse, EnsureOrigin, EnsureOriginWithArg, Equals, InstanceFilter, NeverEnsureOrigin, TransformOrigin, WithdrawReasons, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -116,7 +120,10 @@ use system_parachains_constants::{ polkadot::{consensus::*, currency::*, fee::WeightToFee}, AVERAGE_ON_INITIALIZE_RATIO, DAYS, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, }; -use xcm::latest::prelude::{AssetId, BodyId}; +use xcm::{ + latest::prelude::{AssetId, BodyId}, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; use xcm_config::{ DotLocation, DotLocationV3, FellowshipLocation, ForeignAssetsConvertedConcreteId, ForeignCreatorsSovereignAccountOf, GovernanceLocation, PoolAssetsConvertedConcreteId, @@ -1244,6 +1251,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::DotLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::DotLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl assets_common::runtime_api::FungiblesApi< Block, AccountId, diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml index 6eb8dea2a9..3d972459a8 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -70,6 +70,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -221,7 +222,8 @@ std = [ "xcm-executor/std", "xcm/std", "bp-polkadot-bulletin/std", - "tuplex/std" + "tuplex/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -264,6 +266,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs index 48fa7da454..96784c165d 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -60,7 +60,7 @@ use frame_support::{ tokens::imbalance::ResolveTo, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -91,6 +91,10 @@ use system_parachains_constants::{ // XCM Imports use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -739,6 +743,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::KsmRelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::KsmRelayLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 7ce786eb5b..d683b0c496 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -70,6 +70,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -217,7 +218,8 @@ std = [ "xcm-executor/std", "xcm/std", "bp-polkadot-bulletin/std", - "tuplex/std" + "tuplex/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -259,6 +261,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs index 7adceeecf3..2c41fa5ee3 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -60,7 +60,7 @@ use frame_support::{ tokens::imbalance::ResolveTo, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -93,6 +93,10 @@ use system_parachains_constants::{ // XCM Imports use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -748,6 +752,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::DotRelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::DotRelayLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/collectives/collectives-polkadot/Cargo.toml b/system-parachains/collectives/collectives-polkadot/Cargo.toml index 83d9d08b1c..48e4e56133 100644 --- a/system-parachains/collectives/collectives-polkadot/Cargo.toml +++ b/system-parachains/collectives/collectives-polkadot/Cargo.toml @@ -68,6 +68,7 @@ polkadot-runtime-constants = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -125,6 +126,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", @@ -229,6 +231,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] # Enable metadata hash generation at compile time for the `CheckMetadataHash` extension. diff --git a/system-parachains/collectives/collectives-polkadot/src/lib.rs b/system-parachains/collectives/collectives-polkadot/src/lib.rs index e7b06c621f..7fb5fc5dfa 100644 --- a/system-parachains/collectives/collectives-polkadot/src/lib.rs +++ b/system-parachains/collectives/collectives-polkadot/src/lib.rs @@ -73,7 +73,7 @@ use frame_support::{ fungible::HoldConsideration, tokens::imbalance::ResolveTo, ConstBool, ConstU16, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, LinearStoragePrice, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -101,6 +101,10 @@ pub use sp_runtime::BuildStorage; use pallet_xcm::{EnsureXcm, IsVoiceOfBody}; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use xcm::prelude::*; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; @@ -951,6 +955,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::DotLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::DotLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/coretime/coretime-kusama/Cargo.toml b/system-parachains/coretime/coretime-kusama/Cargo.toml index 810e50bffd..6af63bf4e1 100644 --- a/system-parachains/coretime/coretime-kusama/Cargo.toml +++ b/system-parachains/coretime/coretime-kusama/Cargo.toml @@ -63,6 +63,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-pallet-aura-ext = { workspace = true } @@ -147,6 +148,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -175,6 +177,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/system-parachains/coretime/coretime-kusama/src/lib.rs b/system-parachains/coretime/coretime-kusama/src/lib.rs index aefd31d6f8..a2b87a2509 100644 --- a/system-parachains/coretime/coretime-kusama/src/lib.rs +++ b/system-parachains/coretime/coretime-kusama/src/lib.rs @@ -40,7 +40,7 @@ use frame_support::{ tokens::imbalance::ResolveTo, ConstBool, ConstU32, ConstU64, ConstU8, Contains, EitherOfDiverse, EverythingBut, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -72,11 +72,15 @@ use system_parachains_constants::{ AVERAGE_ON_INITIALIZE_RATIO, HOURS, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, SLOT_DURATION, }; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::*; +use xcm::prelude::*; use xcm_config::{ FellowshipLocation, GovernanceLocation, KsmRelayLocation, StakingPot, XcmOriginToTransactDispatchOrigin, }; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -796,6 +800,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::KsmRelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::KsmRelayLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/encointer/Cargo.toml b/system-parachains/encointer/Cargo.toml index 0f75e3365b..0a5082c3d9 100644 --- a/system-parachains/encointer/Cargo.toml +++ b/system-parachains/encointer/Cargo.toml @@ -84,6 +84,7 @@ polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus dependencies cumulus-pallet-aura-ext = { workspace = true } @@ -150,6 +151,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] std = [ "codec/std", @@ -224,6 +226,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] diff --git a/system-parachains/encointer/src/lib.rs b/system-parachains/encointer/src/lib.rs index 0cce8191c3..61ea918c8c 100644 --- a/system-parachains/encointer/src/lib.rs +++ b/system-parachains/encointer/src/lib.rs @@ -61,7 +61,7 @@ use frame_support::{ ConstBool, ConstU64, Contains, EitherOfDiverse, EqualPrivilegeOnly, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -103,7 +103,14 @@ use system_parachains_constants::{ SLOT_DURATION, }; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::{AssetId as XcmAssetId, BodyId}; +use xcm::{ + latest::prelude::{AssetId as XcmAssetId, BodyId}, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; use xcm_config::{KsmLocation, StakingPot, XcmOriginToTransactDispatchOrigin}; @@ -916,6 +923,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![XcmAssetId(xcm_config::KsmLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::KsmLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/people/people-kusama/Cargo.toml b/system-parachains/people/people-kusama/Cargo.toml index 9ebd65ac38..321df5d416 100644 --- a/system-parachains/people/people-kusama/Cargo.toml +++ b/system-parachains/people/people-kusama/Cargo.toml @@ -61,6 +61,7 @@ kusama-runtime-constants = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-primitives-aura = { workspace = true } @@ -143,7 +144,8 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", - "polkadot-primitives/std" + "polkadot-primitives/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -172,7 +174,8 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks" + "polkadot-primitives/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/system-parachains/people/people-kusama/src/lib.rs b/system-parachains/people/people-kusama/src/lib.rs index 5e82749ca7..0585af8e62 100644 --- a/system-parachains/people/people-kusama/src/lib.rs +++ b/system-parachains/people/people-kusama/src/lib.rs @@ -35,7 +35,9 @@ use frame_support::{ tokens::imbalance::ResolveTo, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, TransformOrigin, }, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, Weight}, + weights::{ + constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, Weight, WeightToFee as _, + }, PalletId, }; use frame_system::{ @@ -69,11 +71,18 @@ use system_parachains_constants::kusama::{ consensus::RELAY_CHAIN_SLOT_DURATION_MILLIS, currency::*, fee::WeightToFee, }; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::BodyId; +use xcm::{ + latest::prelude::{AssetId, BodyId}, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; use xcm_config::{ FellowshipLocation, GovernanceLocation, PriceForSiblingParachainDelivery, StakingPot, XcmConfig, XcmOriginToTransactDispatchOrigin, }; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// This determines the average expected block time that we are targeting. Blocks will be /// produced at a minimum duration defined by `SLOT_DURATION`. `SLOT_DURATION` is picked up by @@ -783,6 +792,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::RelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + 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_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header) diff --git a/system-parachains/people/people-polkadot/Cargo.toml b/system-parachains/people/people-polkadot/Cargo.toml index 5fbf1bc4d0..56eb9e8810 100644 --- a/system-parachains/people/people-polkadot/Cargo.toml +++ b/system-parachains/people/people-polkadot/Cargo.toml @@ -60,6 +60,7 @@ polkadot-runtime-constants = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +xcm-fee-payment-runtime-api = { workspace = true } # Cumulus cumulus-primitives-aura = { workspace = true } @@ -140,6 +141,7 @@ std = [ "xcm-builder/std", "xcm-executor/std", "xcm/std", + "xcm-fee-payment-runtime-api/std", ] runtime-benchmarks = [ @@ -168,6 +170,7 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ diff --git a/system-parachains/people/people-polkadot/src/lib.rs b/system-parachains/people/people-polkadot/src/lib.rs index 001115df32..3208474c6e 100644 --- a/system-parachains/people/people-polkadot/src/lib.rs +++ b/system-parachains/people/people-polkadot/src/lib.rs @@ -34,7 +34,7 @@ use frame_support::{ tokens::imbalance::ResolveTo, ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, TransformOrigin, }, - weights::{ConstantMultiplier, Weight}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; use frame_system::{ @@ -66,11 +66,18 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use system_parachains_constants::polkadot::{consensus::*, currency::*, fee::WeightToFee}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; -use xcm::latest::prelude::BodyId; +use xcm::{ + latest::prelude::{AssetId, BodyId}, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, +}; use xcm_config::{ FellowshipLocation, GovernanceLocation, PriceForSiblingParachainDelivery, StakingPot, XcmConfig, XcmOriginToTransactDispatchOrigin, }; +use xcm_fee_payment_runtime_api::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -735,6 +742,48 @@ impl_runtime_apis! { } } + impl xcm_fee_payment_runtime_api::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::RelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + 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_fee_payment_runtime_api", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_fee_payment_runtime_api", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_fee_payment_runtime_api::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + impl cumulus_primitives_core::CollectCollationInfo for Runtime { fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { ParachainSystem::collect_collation_info(header)