diff --git a/Cargo.lock b/Cargo.lock index 145ae9f8e976d..52f3f5de3adef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14158,6 +14158,7 @@ dependencies = [ "pallet-xcm-bridge-router", "parity-scale-codec", "polkadot-parachain-primitives", + "polkadot-runtime-common", "scale-info", "sp-core 28.0.0", "sp-io 30.0.0", @@ -14226,6 +14227,7 @@ dependencies = [ "frame-system", "log", "parity-scale-codec", + "polkadot-runtime-common", "scale-info", "sp-core 28.0.0", "sp-io 30.0.0", diff --git a/bridges/modules/xcm-bridge-router/Cargo.toml b/bridges/modules/xcm-bridge-router/Cargo.toml index 2aa585c8b5571..0afb79c21c22d 100644 --- a/bridges/modules/xcm-bridge-router/Cargo.toml +++ b/bridges/modules/xcm-bridge-router/Cargo.toml @@ -28,6 +28,7 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # Polkadot Dependencies +polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } @@ -45,6 +46,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "polkadot-runtime-common/std", "scale-info/std", "sp-core/std", "sp-runtime/std", @@ -56,6 +58,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm/runtime-benchmarks", @@ -63,5 +66,6 @@ runtime-benchmarks = [ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] diff --git a/bridges/modules/xcm-bridge-router/src/impls.rs b/bridges/modules/xcm-bridge-router/src/impls.rs index c0ef3f370fce3..b1169bbc7bf81 100644 --- a/bridges/modules/xcm-bridge-router/src/impls.rs +++ b/bridges/modules/xcm-bridge-router/src/impls.rs @@ -69,6 +69,39 @@ impl, I: 'static> bp_xcm_bridge::LocalXcmChannelManager(PhantomData<(T, I, BasePrice)>); +impl, I: 'static, BasePrice: Get> + polkadot_runtime_common::xcm_sender::PriceForMessageDelivery + for GetPriceForBridge +{ + type Id = BridgeIdOf; + + fn price_for_delivery(bridge_id: Self::Id, message: &Xcm<()>) -> Assets { + // Get a base price. + let mut price = BasePrice::get(); + + // Apply message-size-based fees (if configured). + if let Some(message_size_fees) = + Pallet::::calculate_message_size_fee(|| message.encoded_size() as _) + { + price.push(message_size_fees); + } + + // Apply dynamic congestion fees based on bridge state (if needed). + if let Some(bridge_state) = Bridges::::get(bridge_id) { + let mut dynamic_fees = price.into_inner(); + for fee in dynamic_fees.iter_mut() { + Pallet::::apply_dynamic_fee_factor(&bridge_state, fee); + } + price = Assets::from(dynamic_fees); + } + + price + } +} + /// Adapter implementation for [`ExporterFor`] that allows exporting message size fee and/or dynamic /// fees based on the `BridgeId` resolved by the `T::BridgeIdResolver` resolver, if and only if the /// `E` exporter supports bridging. This adapter acts as an [`ExporterFor`], for example, for the diff --git a/bridges/modules/xcm-bridge/Cargo.toml b/bridges/modules/xcm-bridge/Cargo.toml index 3bcff067015de..6ab92e38a4599 100644 --- a/bridges/modules/xcm-bridge/Cargo.toml +++ b/bridges/modules/xcm-bridge/Cargo.toml @@ -30,6 +30,7 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # Polkadot Dependencies +polkadot-runtime-common = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } @@ -60,6 +61,7 @@ std = [ "pallet-bridge-messages/std", "pallet-xcm-bridge-router/std", "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", "scale-info/std", "sp-core/std", "sp-io/std", @@ -77,6 +79,7 @@ runtime-benchmarks = [ "pallet-bridge-messages/runtime-benchmarks", "pallet-xcm-bridge-router/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -88,5 +91,6 @@ try-runtime = [ "pallet-balances/try-runtime", "pallet-bridge-messages/try-runtime", "pallet-xcm-bridge-router/try-runtime", + "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", ] diff --git a/bridges/modules/xcm-bridge/src/exporter.rs b/bridges/modules/xcm-bridge/src/exporter.rs index 45c99a115956c..f91c14c314f15 100644 --- a/bridges/modules/xcm-bridge/src/exporter.rs +++ b/bridges/modules/xcm-bridge/src/exporter.rs @@ -33,16 +33,19 @@ use frame_support::{ensure, traits::Get}; use pallet_bridge_messages::{ Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet, }; +use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; use xcm::prelude::*; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::ExportXcm; /// An easy way to access `HaulBlobExporter`. +/// +/// Note: Set no price for `HaulBlobExporter`, because `ExportXcm for Pallet` handles the fees. pub type PalletAsHaulBlobExporter = HaulBlobExporter< DummyHaulBlob, >::BridgedNetwork, >::DestinationVersion, - >::MessageExportPrice, + (), >; /// An easy way to access associated messages pallet. type MessagesPallet = BridgeMessagesPallet>::BridgeMessagesPalletInstance>; @@ -133,10 +136,15 @@ where SendError::NotApplicable })?; - // check if we are able to route the message. We use existing `HaulBlobExporter` for that. - // It will make all required changes and will encode message properly, so that the - // `DispatchBlob` at the bridged bridge hub will be able to decode it - let ((blob, id), price) = PalletAsHaulBlobExporter::::validate( + // Get the potential price for a message over the bridge. + let price_for_delivery = message.as_ref().map(|msg| { + T::MessageExportPrice::price_for_delivery(locations.bridge_id().clone(), msg) + }); + + // check if we are able to route the message. We use the existing ` HaulBlobExporter ` for that. + // It will make all required changes and will encode a message properly, so that the + // `DispatchBlob` at the bridged xcm-bridge will be able to decode it. + let ((blob, id), mut price) = PalletAsHaulBlobExporter::::validate( network, channel, universal_source, @@ -144,6 +152,13 @@ where message, )?; + // Add `price_for_delivery` to the `price`. + if let Some(delivery_prices) = price_for_delivery { + for dp in delivery_prices.into_inner() { + price.push(dp); + } + } + // Here, we know that the message is relevant to this pallet instance, so let's check for // congestion defense. if bridge.state == BridgeState::HardSuspended { diff --git a/bridges/modules/xcm-bridge/src/lib.rs b/bridges/modules/xcm-bridge/src/lib.rs index cd6190f9b71ac..054d084430125 100644 --- a/bridges/modules/xcm-bridge/src/lib.rs +++ b/bridges/modules/xcm-bridge/src/lib.rs @@ -153,6 +153,7 @@ use bp_xcm_bridge::{ use frame_support::{traits::fungible::MutateHold, DefaultNoBound}; use frame_system::Config as SystemConfig; use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError}; +use polkadot_runtime_common::xcm_sender::PriceForMessageDelivery; use sp_std::{boxed::Box, vec::Vec}; use xcm::prelude::*; use xcm_builder::DispatchBlob; @@ -211,22 +212,22 @@ pub mod pallet { // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and // replace it with the `NetworkId` - then we'll be able to use // `T as pallet_bridge_messages::Config::BridgedChain::NetworkId` - /// Bridged network as relative location of bridged `GlobalConsensus`. + /// Bridged network as a relative location of bridged `GlobalConsensus`. #[pallet::constant] type BridgedNetwork: Get; /// Associated messages pallet instance that bridges us with the /// `BridgedNetworkId` consensus. type BridgeMessagesPalletInstance: 'static; - /// Price of single message export to the bridged consensus (`Self::BridgedNetwork`). - type MessageExportPrice: Get; + /// Price of a single message export to the bridged consensus (`Self::BridgedNetwork`). + type MessageExportPrice: PriceForMessageDelivery; /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; /// The origin that is allowed to call privileged operations on the pallet, e.g. open/close /// bridge for locations. type ForceOrigin: EnsureOrigin<::RuntimeOrigin>; - /// A set of XCM locations within local consensus system that are allowed to open + /// A set of XCM locations within a local consensus system that are allowed to open /// bridges with remote destinations. type OpenBridgeOrigin: EnsureOrigin< ::RuntimeOrigin, diff --git a/bridges/modules/xcm-bridge/src/mock.rs b/bridges/modules/xcm-bridge/src/mock.rs index 95e43a79d6e9e..382ce8b02dee5 100644 --- a/bridges/modules/xcm-bridge/src/mock.rs +++ b/bridges/modules/xcm-bridge/src/mock.rs @@ -35,6 +35,7 @@ use pallet_xcm_bridge::congestion::{ UpdateBridgeStatusXcmChannelManager, }; use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, @@ -217,7 +218,7 @@ impl pallet_xcm_bridge::Config for TestRuntime { type BridgedNetwork = BridgedRelayNetworkLocation; type BridgeMessagesPalletInstance = (); - type MessageExportPrice = (); + type MessageExportPrice = NoPriceForMessageDelivery; type DestinationVersion = AlwaysLatest; type ForceOrigin = EnsureNever<()>;