From 832c864eab9c728ae8c6a73f5d89e745d0cdfd5d Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 May 2025 23:28:27 +0800 Subject: [PATCH 01/16] Unpaid execution to Ethereum --- Cargo.lock | 1 + .../asset-hub-westend/src/xcm_config.rs | 27 +++++++------ .../bridge-hub-westend/src/xcm_config.rs | 8 +++- .../runtimes/bridge-hubs/common/Cargo.toml | 2 + .../bridge-hubs/common/src/barriers.rs | 38 ++++++++++++++++++- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d4eddf4f954d..87d41532e8554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2378,6 +2378,7 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "tracing", ] [[package]] diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 5187e0e69a4fb..13698483bde06 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -56,8 +56,8 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, + WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -454,14 +454,18 @@ pub type XcmRouter = WithUniqueTopic<( // GlobalConsensus with a pausable flag, if the flag is set true then the Router is paused PausableExporter< crate::SnowbridgeSystemFrontend, - SovereignPaidRemoteExporter< - ( + ( + UnpaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTableV2, + XcmpQueue, + UniversalLocation, + >, + SovereignPaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTable, - ), - XcmpQueue, - UniversalLocation, - >, + XcmpQueue, + UniversalLocation, + >, + ), >, )>; @@ -630,9 +634,7 @@ pub mod bridging { /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs /// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals. pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; - pub const DefaultBridgeHubEthereumBaseFeeV2: Balance = 100_000_000_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); - pub storage BridgeHubEthereumBaseFeeV2: Balance = DefaultBridgeHubEthereumBaseFeeV2::get(); pub SiblingBridgeHubWithEthereumInboundQueueV1Instance: Location = Location::new( 1, [ @@ -667,10 +669,7 @@ pub mod bridging { EthereumNetwork::get(), Some(sp_std::vec![Junctions::Here]), SiblingBridgeHub::get(), - Some(( - XcmBridgeHubRouterFeeAssetId::get(), - BridgeHubEthereumBaseFeeV2::get(), - ).into()) + None ), ]; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 5a00ae852886b..fd1d00b22aeaf 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -20,7 +20,9 @@ use super::{ RuntimeOrigin, TransactionByteFee, WeightToFee, XcmOverBridgeHubRococo, XcmpQueue, }; use crate::bridge_to_ethereum_config::{AssetHubLocation, SnowbridgeFrontendLocation}; -use bridge_hub_common::DenyExportMessageFrom; +use bridge_hub_common::{ + barriers::AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum, DenyExportMessageFrom, +}; use frame_support::{ parameter_types, traits::{ @@ -166,6 +168,10 @@ pub type Barrier = TrailingSetTopicAsId< Equals, Equals, )>, + AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum< + Equals, + EthereumNetwork, + >, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, // HRMP notifications from the relay chain are OK. diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index 83f8171f3c404..cc413453b7f6d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml @@ -21,6 +21,7 @@ snowbridge-core = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +tracing = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } @@ -37,6 +38,7 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", + "tracing/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs index 6b5dee3de3842..fa5ae70909a38 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs @@ -16,10 +16,12 @@ use core::{marker::PhantomData, ops::ControlFlow}; use cumulus_primitives_core::Weight; use frame_support::traits::{Contains, ProcessMessageError}; -use xcm::prelude::{ExportMessage, Instruction, Location, NetworkId}; +use xcm::prelude::{ExportMessage, Instruction, Location, NetworkId, UnpaidExecution}; +use frame_support::ensure; +use sp_core::Get; use xcm_builder::{CreateMatcher, MatchXcm}; -use xcm_executor::traits::{DenyExecution, Properties}; +use xcm_executor::traits::{DenyExecution, Properties, ShouldExecute}; /// Deny execution if the message contains instruction `ExportMessage` with /// a. origin is contained in `FromOrigin` (i.e.`FromOrigin::Contains(origin)`) @@ -55,3 +57,35 @@ where Ok(()) } } + +/// Allow Unpaid execution from Location L and exported to N +pub struct AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum(PhantomData<(L, N)>); +impl, N: Get> ShouldExecute + for AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum +{ + fn should_execute( + origin: &Location, + instructions: &mut [Instruction], + max_weight: Weight, + properties: &mut Properties, + ) -> Result<(), ProcessMessageError> { + tracing::trace!( + target: "xcm::barriers", + ?origin, ?instructions, ?max_weight, ?properties, + "AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum", + ); + ensure!(L::contains(origin), ProcessMessageError::Unsupported); + instructions + .matcher() + .match_next_inst(|inst| match inst { + UnpaidExecution { .. } => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + })? + .match_next_inst(|inst| match inst { + ExportMessage { network, .. } if network.clone().eq(&N::get()) => Ok(()), + _ => Err(ProcessMessageError::BadFormat), + })?; + + Ok(()) + } +} From b70ad21e945fda2c784d8968d1fc5acb8369f4ef Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 22 May 2025 19:12:09 +0800 Subject: [PATCH 02/16] Add a custom SnowbridgeUnpaidRemoteExporter --- .../runtime/runtime-common/src/v2/exporter.rs | 81 +++++++++++++++++++ .../runtime/runtime-common/src/v2/mod.rs | 1 + .../asset-hub-westend/src/xcm_config.rs | 7 +- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs new file mode 100644 index 0000000000000..f6610eb66e94b --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs @@ -0,0 +1,81 @@ +use core::marker::PhantomData; +use frame_support::traits::Get; +use sp_std::vec; +use xcm::prelude::{ + validate_send, ExportMessage, InteriorLocation, Location, SendError, + SendError::{MissingArgument, NotApplicable}, + SendResult, SendXcm, SetTopic, Unlimited, UnpaidExecution, Xcm, XcmHash, +}; +use xcm_builder::{ensure_is_remote, ExporterFor}; + +/// A custom UnpaidRemoteExporter in Snowbridge with a minor tweak allowes some payment to be +/// attached, which is not permitted by the original UnpaidRemoteExporter. +pub struct SnowbridgeUnpaidRemoteExporter( + PhantomData<(Bridges, Router, UniversalLocation)>, +); +impl> SendXcm + for SnowbridgeUnpaidRemoteExporter +{ + type Ticket = Router::Ticket; + + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + // This `clone` ensures that `dest` is not consumed in any case. + let d = dest.clone().ok_or(MissingArgument)?; + let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?; + let (remote_network, remote_location) = devolved; + let xcm = msg.take().ok_or(MissingArgument)?; + + // find exporter + let Some((bridge, maybe_payment)) = + Bridges::exporter_for(&remote_network, &remote_location, &xcm) + else { + // We need to make sure that msg is not consumed in case of `NotApplicable`. + *msg = Some(xcm); + return Err(NotApplicable) + }; + + // `xcm` should already end with `SetTopic` - if it does, then extract and derive into + // an onward topic ID. + let maybe_forward_id = match xcm.last() { + Some(SetTopic(t)) => Some(*t), + _ => None, + }; + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. This will only work if the bridge will do the message + // export for free. Common-good chains will typically be afforded this. + let mut message = Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + ExportMessage { + network: remote_network, + destination: remote_location, + xcm: xcm.clone(), + }, + ]); + if let Some(forward_id) = maybe_forward_id { + message.0.push(SetTopic(forward_id)); + } + let (v, mut cost) = validate_send::(bridge, message).inspect_err(|err| { + if let NotApplicable = err { + // We need to make sure that msg is not consumed in case of `NotApplicable`. + *msg = Some(xcm); + } + })?; + if let Some(bridge_payment) = maybe_payment { + cost.push(bridge_payment); + } + Ok((v, cost)) + } + + fn deliver(validation: Self::Ticket) -> Result { + Router::deliver(validation) + } + + #[cfg(feature = "runtime-benchmarks")] + fn ensure_successful_delivery(location: Option) { + Router::ensure_successful_delivery(location); + } +} diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs index 7b882430af59d..f8ccdd053cdd0 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs @@ -1,3 +1,4 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork +pub mod exporter; pub mod register_token; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 13698483bde06..e6508c7e0f62f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -42,6 +42,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_outbound_queue_primitives::v2::exporter::PausableExporter; +use snowbridge_runtime_common::v2::exporter::SnowbridgeUnpaidRemoteExporter; use sp_runtime::traits::{AccountIdConversion, TryConvertInto}; use westend_runtime_constants::system_parachain::COLLECTIVES_ID; use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; @@ -56,8 +57,8 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, - WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -455,7 +456,7 @@ pub type XcmRouter = WithUniqueTopic<( PausableExporter< crate::SnowbridgeSystemFrontend, ( - UnpaidRemoteExporter< + SnowbridgeUnpaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTableV2, XcmpQueue, UniversalLocation, From 14f37be0aa280821829bd9446baeea1d6130a68d Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 23 May 2025 13:10:55 +0800 Subject: [PATCH 03/16] Add base_fee back --- .../runtimes/assets/asset-hub-westend/src/xcm_config.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index e6508c7e0f62f..90503a6dea664 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -635,7 +635,9 @@ pub mod bridging { /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs /// Polkadot uses 10 decimals, Kusama,Rococo,Westend 12 decimals. pub const DefaultBridgeHubEthereumBaseFee: Balance = 3_833_568_200_000; + pub const DefaultBridgeHubEthereumBaseFeeV2: Balance = 100_000_000_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); + pub storage BridgeHubEthereumBaseFeeV2: Balance = DefaultBridgeHubEthereumBaseFeeV2::get(); pub SiblingBridgeHubWithEthereumInboundQueueV1Instance: Location = Location::new( 1, [ @@ -670,7 +672,10 @@ pub mod bridging { EthereumNetwork::get(), Some(sp_std::vec![Junctions::Here]), SiblingBridgeHub::get(), - None + Some(( + XcmBridgeHubRouterFeeAssetId::get(), + BridgeHubEthereumBaseFeeV2::get(), + ).into()) ), ]; From 85343c025ac6b1e3e41253ccb0480c19658b7079 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 23 May 2025 13:16:25 +0800 Subject: [PATCH 04/16] Polish comment --- cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs index fa5ae70909a38..4926eae298a09 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs @@ -58,7 +58,7 @@ where } } -/// Allow Unpaid execution from Location L and exported to N +/// Allow unpaid execution from location L when exporting to network N pub struct AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum(PhantomData<(L, N)>); impl, N: Get> ShouldExecute for AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum From 069b814fb9ea5d43d2396238b514a40a8fd8bf13 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 23 May 2025 13:27:27 +0800 Subject: [PATCH 05/16] Add prdoc --- prdoc/pr_8599.prdoc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 prdoc/pr_8599.prdoc diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc new file mode 100644 index 0000000000000..8f7d35074c845 --- /dev/null +++ b/prdoc/pr_8599.prdoc @@ -0,0 +1,16 @@ +title: Snowbridge: Unpaid execution when bridging to Ethereum +doc: +- audience: Runtime Dev + description: |- + Since all fees in Snowbridge V2 will be estimated on the fly and injected into the XCM, there is + no need to preconfigure a bridge fee using SovereignPaidRemoteExporter. Additionally, we want to + avoid maintaining Asset Hub’s sovereign account on Bridge Hub. +crates: +- name: snowbridge-runtime-common + bump: patch +- name: bridge-hub-common + bump: patch +- name: bridge-hub-westend-runtime + bump: patch +- name: asset-hub-westend-runtime + bump: patch From b984fdb59d323a63167953b898cc1a091090bef7 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 23 May 2025 19:19:00 +0800 Subject: [PATCH 06/16] Add delivery fees to UnpaidRemoteExporter and remove the custom one --- .../runtime/runtime-common/src/v2/exporter.rs | 81 ------------------- .../runtime/runtime-common/src/v2/mod.rs | 1 - .../asset-hub-westend/src/xcm_config.rs | 7 +- .../xcm/xcm-builder/src/universal_exports.rs | 9 ++- 4 files changed, 9 insertions(+), 89 deletions(-) delete mode 100644 bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs deleted file mode 100644 index f6610eb66e94b..0000000000000 --- a/bridges/snowbridge/runtime/runtime-common/src/v2/exporter.rs +++ /dev/null @@ -1,81 +0,0 @@ -use core::marker::PhantomData; -use frame_support::traits::Get; -use sp_std::vec; -use xcm::prelude::{ - validate_send, ExportMessage, InteriorLocation, Location, SendError, - SendError::{MissingArgument, NotApplicable}, - SendResult, SendXcm, SetTopic, Unlimited, UnpaidExecution, Xcm, XcmHash, -}; -use xcm_builder::{ensure_is_remote, ExporterFor}; - -/// A custom UnpaidRemoteExporter in Snowbridge with a minor tweak allowes some payment to be -/// attached, which is not permitted by the original UnpaidRemoteExporter. -pub struct SnowbridgeUnpaidRemoteExporter( - PhantomData<(Bridges, Router, UniversalLocation)>, -); -impl> SendXcm - for SnowbridgeUnpaidRemoteExporter -{ - type Ticket = Router::Ticket; - - fn validate( - dest: &mut Option, - msg: &mut Option>, - ) -> SendResult { - // This `clone` ensures that `dest` is not consumed in any case. - let d = dest.clone().ok_or(MissingArgument)?; - let devolved = ensure_is_remote(UniversalLocation::get(), d).map_err(|_| NotApplicable)?; - let (remote_network, remote_location) = devolved; - let xcm = msg.take().ok_or(MissingArgument)?; - - // find exporter - let Some((bridge, maybe_payment)) = - Bridges::exporter_for(&remote_network, &remote_location, &xcm) - else { - // We need to make sure that msg is not consumed in case of `NotApplicable`. - *msg = Some(xcm); - return Err(NotApplicable) - }; - - // `xcm` should already end with `SetTopic` - if it does, then extract and derive into - // an onward topic ID. - let maybe_forward_id = match xcm.last() { - Some(SetTopic(t)) => Some(*t), - _ => None, - }; - - // We then send a normal message to the bridge asking it to export the prepended - // message to the remote chain. This will only work if the bridge will do the message - // export for free. Common-good chains will typically be afforded this. - let mut message = Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - ExportMessage { - network: remote_network, - destination: remote_location, - xcm: xcm.clone(), - }, - ]); - if let Some(forward_id) = maybe_forward_id { - message.0.push(SetTopic(forward_id)); - } - let (v, mut cost) = validate_send::(bridge, message).inspect_err(|err| { - if let NotApplicable = err { - // We need to make sure that msg is not consumed in case of `NotApplicable`. - *msg = Some(xcm); - } - })?; - if let Some(bridge_payment) = maybe_payment { - cost.push(bridge_payment); - } - Ok((v, cost)) - } - - fn deliver(validation: Self::Ticket) -> Result { - Router::deliver(validation) - } - - #[cfg(feature = "runtime-benchmarks")] - fn ensure_successful_delivery(location: Option) { - Router::ensure_successful_delivery(location); - } -} diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs index f8ccdd053cdd0..7b882430af59d 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs @@ -1,4 +1,3 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -pub mod exporter; pub mod register_token; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 90503a6dea664..f5923e0bf162a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -42,7 +42,6 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_outbound_queue_primitives::v2::exporter::PausableExporter; -use snowbridge_runtime_common::v2::exporter::SnowbridgeUnpaidRemoteExporter; use sp_runtime::traits::{AccountIdConversion, TryConvertInto}; use westend_runtime_constants::system_parachain::COLLECTIVES_ID; use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; @@ -57,8 +56,8 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, - WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, + WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -456,7 +455,7 @@ pub type XcmRouter = WithUniqueTopic<( PausableExporter< crate::SnowbridgeSystemFrontend, ( - SnowbridgeUnpaidRemoteExporter< + UnpaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTableV2, XcmpQueue, UniversalLocation, diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index da718ea1b6ed3..573165928b33c 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -278,7 +278,6 @@ impl(bridge, message).inspect_err(|err| { + let (v, mut cost) = validate_send::(bridge, message).inspect_err(|err| { if let NotApplicable = err { // We need to make sure that msg is not consumed in case of `NotApplicable`. *msg = Some(xcm); } - }) + })?; + if let Some(bridge_payment) = maybe_payment { + cost.push(bridge_payment); + } + Ok((v, cost)) } fn deliver(validation: Self::Ticket) -> Result { From d0cadac0235d2fa586323476b8ce966104c0ab3b Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 23 May 2025 21:35:19 +0800 Subject: [PATCH 07/16] AllowExplicitUnpaidExecution from AH root location --- .../bridge-hub-westend/src/xcm_config.rs | 9 +---- .../bridge-hubs/common/src/barriers.rs | 38 +------------------ 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index fd1d00b22aeaf..4f7384c2a585b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -20,9 +20,7 @@ use super::{ RuntimeOrigin, TransactionByteFee, WeightToFee, XcmOverBridgeHubRococo, XcmpQueue, }; use crate::bridge_to_ethereum_config::{AssetHubLocation, SnowbridgeFrontendLocation}; -use bridge_hub_common::{ - barriers::AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum, DenyExportMessageFrom, -}; +use bridge_hub_common::DenyExportMessageFrom; use frame_support::{ parameter_types, traits::{ @@ -167,11 +165,8 @@ pub type Barrier = TrailingSetTopicAsId< ParentOrParentsPlurality, Equals, Equals, - )>, - AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum< Equals, - EthereumNetwork, - >, + )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, // HRMP notifications from the relay chain are OK. diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs index 4926eae298a09..6b5dee3de3842 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/barriers.rs @@ -16,12 +16,10 @@ use core::{marker::PhantomData, ops::ControlFlow}; use cumulus_primitives_core::Weight; use frame_support::traits::{Contains, ProcessMessageError}; -use xcm::prelude::{ExportMessage, Instruction, Location, NetworkId, UnpaidExecution}; +use xcm::prelude::{ExportMessage, Instruction, Location, NetworkId}; -use frame_support::ensure; -use sp_core::Get; use xcm_builder::{CreateMatcher, MatchXcm}; -use xcm_executor::traits::{DenyExecution, Properties, ShouldExecute}; +use xcm_executor::traits::{DenyExecution, Properties}; /// Deny execution if the message contains instruction `ExportMessage` with /// a. origin is contained in `FromOrigin` (i.e.`FromOrigin::Contains(origin)`) @@ -57,35 +55,3 @@ where Ok(()) } } - -/// Allow unpaid execution from location L when exporting to network N -pub struct AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum(PhantomData<(L, N)>); -impl, N: Get> ShouldExecute - for AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum -{ - fn should_execute( - origin: &Location, - instructions: &mut [Instruction], - max_weight: Weight, - properties: &mut Properties, - ) -> Result<(), ProcessMessageError> { - tracing::trace!( - target: "xcm::barriers", - ?origin, ?instructions, ?max_weight, ?properties, - "AllowExplicitUnpaidExecutionFromAssetHubExportToEthereum", - ); - ensure!(L::contains(origin), ProcessMessageError::Unsupported); - instructions - .matcher() - .match_next_inst(|inst| match inst { - UnpaidExecution { .. } => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - })? - .match_next_inst(|inst| match inst { - ExportMessage { network, .. } if network.clone().eq(&N::get()) => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - })?; - - Ok(()) - } -} From e136ea8cf2ba1d75ff92acb2b3dc20050e1c2879 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 May 2025 15:47:24 +0200 Subject: [PATCH 08/16] Apply suggestions from code review --- cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml | 2 -- prdoc/pr_8599.prdoc | 2 -- 2 files changed, 4 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index cc413453b7f6d..83f8171f3c404 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml @@ -21,7 +21,6 @@ snowbridge-core = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } -tracing = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } @@ -38,7 +37,6 @@ std = [ "sp-core/std", "sp-runtime/std", "sp-std/std", - "tracing/std", "xcm-builder/std", "xcm-executor/std", "xcm/std", diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index 8f7d35074c845..1fcaeec07756f 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -8,8 +8,6 @@ doc: crates: - name: snowbridge-runtime-common bump: patch -- name: bridge-hub-common - bump: patch - name: bridge-hub-westend-runtime bump: patch - name: asset-hub-westend-runtime From 4f27913a34619f1cc4d613144cfcd99ffb22f2d2 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 May 2025 15:47:59 +0200 Subject: [PATCH 09/16] Update Cargo.lock --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index db088a092e9a2..f999d987754b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2378,7 +2378,6 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", - "tracing", ] [[package]] From 1413bb1d55b2c2865a9be7dc2e469de33f230b44 Mon Sep 17 00:00:00 2001 From: Branislav Kontur Date: Fri, 23 May 2025 15:49:11 +0200 Subject: [PATCH 10/16] Update prdoc/pr_8599.prdoc --- prdoc/pr_8599.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index 1fcaeec07756f..d77ccb0f6fbf9 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -6,7 +6,7 @@ doc: no need to preconfigure a bridge fee using SovereignPaidRemoteExporter. Additionally, we want to avoid maintaining Asset Hub’s sovereign account on Bridge Hub. crates: -- name: snowbridge-runtime-common +- name: staging-xcm-builder bump: patch - name: bridge-hub-westend-runtime bump: patch From b48fecf588e5aa4a5bfdc04f842d373017cdf9e4 Mon Sep 17 00:00:00 2001 From: Ron Date: Thu, 29 May 2025 06:08:46 +0800 Subject: [PATCH 11/16] Snowbridge V1: unpaid execution (#20) * Apply UnpaidExporter to Snowbridge V1 as well * Remove the unused XcmFeeManagerFromComponentsBridgeHub * Revert changes in EthereumBlobExporter --- .../runtime/runtime-common/src/lib.rs | 4 - .../runtime/runtime-common/src/tests.rs | 171 ------------------ .../runtime-common/src/v1/fee_handler.rs | 146 --------------- .../runtime/runtime-common/src/v1/mod.rs | 3 - .../src/tests/snowbridge.rs | 64 ------- .../asset-hub-westend/src/xcm_config.rs | 10 +- .../bridge-hub-rococo/src/xcm_config.rs | 16 +- .../bridge-hub-westend/src/xcm_config.rs | 53 +----- 8 files changed, 16 insertions(+), 451 deletions(-) delete mode 100644 bridges/snowbridge/runtime/runtime-common/src/tests.rs delete mode 100644 bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs delete mode 100644 bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index 479a1b34008ee..72fd22e0c3988 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -4,9 +4,5 @@ //! //! Common traits and types shared by runtimes. #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(test)] -mod tests; -pub mod v1; pub mod v2; -pub use v1::fee_handler::XcmExportFeeToSibling; pub use v2::register_token::{ForeignAssetOwner, LocalAssetOwner}; diff --git a/bridges/snowbridge/runtime/runtime-common/src/tests.rs b/bridges/snowbridge/runtime/runtime-common/src/tests.rs deleted file mode 100644 index fce73e67184a6..0000000000000 --- a/bridges/snowbridge/runtime/runtime-common/src/tests.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::XcmExportFeeToSibling; -use frame_support::{parameter_types, sp_runtime::testing::H256}; -use snowbridge_outbound_queue_primitives::{ - v1::{Fee, Message, SendMessage}, - SendError, SendMessageFeeProvider, -}; -use xcm::prelude::{ - Asset, Assets, Here, Kusama, Location, NetworkId, Parachain, XcmContext, XcmError, XcmHash, - XcmResult, -}; -use xcm_builder::HandleFee; -use xcm_executor::{ - traits::{FeeReason, TransactAsset}, - AssetsInHolding, -}; - -parameter_types! { - pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; - pub TokenLocation: Location = Location::parent(); -} - -struct MockOkOutboundQueue; -impl SendMessage for MockOkOutboundQueue { - type Ticket = (); - - fn validate(_: &Message) -> Result<(Self::Ticket, Fee), SendError> { - Ok(((), Fee { local: 1, remote: 1 })) - } - - fn deliver(_: Self::Ticket) -> Result { - Ok(H256::zero()) - } -} - -impl SendMessageFeeProvider for MockOkOutboundQueue { - type Balance = u128; - - fn local_fee() -> Self::Balance { - 1 - } -} -struct MockErrOutboundQueue; -impl SendMessage for MockErrOutboundQueue { - type Ticket = (); - - fn validate(_: &Message) -> Result<(Self::Ticket, Fee), SendError> { - Err(SendError::MessageTooLarge) - } - - fn deliver(_: Self::Ticket) -> Result { - Err(SendError::MessageTooLarge) - } -} - -impl SendMessageFeeProvider for MockErrOutboundQueue { - type Balance = u128; - - fn local_fee() -> Self::Balance { - 1 - } -} - -pub struct MockAssetTransactor; -impl TransactAsset for MockAssetTransactor { - fn can_check_in(_origin: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn can_check_out(_dest: &Location, _what: &Asset, _context: &XcmContext) -> XcmResult { - Ok(()) - } - - fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult { - Ok(()) - } - - fn withdraw_asset( - _what: &Asset, - _who: &Location, - _context: Option<&XcmContext>, - ) -> Result { - Ok(Assets::default().into()) - } - - fn internal_transfer_asset( - _what: &Asset, - _from: &Location, - _to: &Location, - _context: &XcmContext, - ) -> Result { - Ok(Assets::default().into()) - } -} - -#[test] -fn handle_fee_success() { - let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); - let ctx = XcmContext { - origin: Some(Location::new(1, Parachain(1000))), - message_id: XcmHash::default(), - topic: None, - }; - let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; - let result = XcmExportFeeToSibling::< - u128, - u64, - TokenLocation, - EthereumNetwork, - MockAssetTransactor, - MockOkOutboundQueue, - >::handle_fee(fee, Some(&ctx), reason); - let local_fee = Asset::from((Location::parent(), MockOkOutboundQueue::local_fee())).into(); - // assert only local fee left - assert_eq!(result, local_fee) -} - -#[test] -fn handle_fee_success_but_not_for_ethereum() { - let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - // invalid network not for ethereum - let reason = FeeReason::Export { network: Kusama, destination: Here }; - let result = XcmExportFeeToSibling::< - u128, - u64, - TokenLocation, - EthereumNetwork, - MockAssetTransactor, - MockOkOutboundQueue, - >::handle_fee(fee.clone(), Some(&ctx), reason); - // assert fee not touched and just forward to the next handler - assert_eq!(result, fee) -} - -#[test] -fn handle_fee_success_even_from_an_invalid_none_origin_location() { - let fee: Assets = Asset::from((Location::parent(), 10_u128)).into(); - // invalid origin None here not from a sibling chain - let ctx = XcmContext { origin: None, message_id: XcmHash::default(), topic: None }; - let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; - let result = XcmExportFeeToSibling::< - u128, - u64, - TokenLocation, - EthereumNetwork, - MockAssetTransactor, - MockOkOutboundQueue, - >::handle_fee(fee.clone(), Some(&ctx), reason); - assert_eq!(result, fee) -} - -#[test] -fn handle_fee_success_even_when_fee_insufficient() { - // insufficient fee not cover the (local_fee + remote_fee) required - let fee: Assets = Asset::from((Location::parent(), 1_u128)).into(); - let ctx = XcmContext { - origin: Some(Location::new(1, Parachain(1000))), - message_id: XcmHash::default(), - topic: None, - }; - let reason = FeeReason::Export { network: EthereumNetwork::get(), destination: Here }; - let result = XcmExportFeeToSibling::< - u128, - u64, - TokenLocation, - EthereumNetwork, - MockAssetTransactor, - MockOkOutboundQueue, - >::handle_fee(fee.clone(), Some(&ctx), reason); - assert_eq!(result, fee) -} diff --git a/bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs b/bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs deleted file mode 100644 index fa2b9486f2cd8..0000000000000 --- a/bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -//! Handling of fees for `ExportMessage` on BridgeHub in V1. - -use codec::FullCodec; -use core::marker::PhantomData; -use frame_support::traits::Get; -use snowbridge_outbound_queue_primitives::SendMessageFeeProvider; -use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; -use sp_std::fmt::Debug; -use xcm::prelude::*; -use xcm_builder::HandleFee; -use xcm_executor::traits::{FeeReason, TransactAsset}; - -pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling"; - -/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions -/// to Snowbridge and splits off the remote fee and deposits it to the origin -/// parachain sovereign account. The local fee is then returned back to be handled by -/// the next fee handler in the chain. Most likely the treasury account. -pub struct XcmExportFeeToSibling< - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, ->( - PhantomData<( - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, - )>, -); - -impl HandleFee - for XcmExportFeeToSibling< - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, - > -where - Balance: BaseArithmetic + Unsigned + Copy + From + Into + Debug, - AccountId: Clone + FullCodec, - FeeAssetLocation: Get, - EthereumNetwork: Get, - AssetTransactor: TransactAsset, - FeeProvider: SendMessageFeeProvider, -{ - fn handle_fee(fees: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets { - let token_location = FeeAssetLocation::get(); - - // Check the reason to see if this export is for snowbridge. - if !matches!( - reason, - FeeReason::Export { network: bridged_network, ref destination } - if bridged_network == EthereumNetwork::get() && destination == &Here - ) { - return fees - } - - // Get the parachain sovereign from the `context`. - let maybe_para_id: Option = - if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) = - context - { - if let Some(Parachain(sibling_para_id)) = interior.first() { - Some(*sibling_para_id) - } else { - None - } - } else { - None - }; - if maybe_para_id.is_none() { - log::error!( - target: LOG_TARGET, - "invalid location in context {:?}", - context, - ); - return fees - } - let para_id = maybe_para_id.unwrap(); - - // Get the total fee offered by export message. - let maybe_total_supplied_fee: Option<(usize, Balance)> = fees - .inner() - .iter() - .enumerate() - .filter_map(|(index, asset)| { - if let Asset { id: location, fun: Fungible(amount) } = asset { - if location.0 == token_location { - return Some((index, (*amount).into())) - } - } - None - }) - .next(); - if maybe_total_supplied_fee.is_none() { - log::error!( - target: LOG_TARGET, - "could not find fee asset item in fees: {:?}", - fees, - ); - return fees - } - let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap(); - let local_fee = FeeProvider::local_fee(); - let remote_fee = total_fee.saturating_sub(local_fee); - if local_fee == Balance::zero() || remote_fee == Balance::zero() { - log::error!( - target: LOG_TARGET, - "calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}", - local_fee, - remote_fee, - ); - return fees - } - // Refund remote component of fee to physical origin - let result = AssetTransactor::deposit_asset( - &Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) }, - &Location::new(1, [Parachain(para_id)]), - context, - ); - if result.is_err() { - log::error!( - target: LOG_TARGET, - "transact fee asset failed: {:?}", - result.unwrap_err() - ); - return fees - } - - // Return remaining fee to the next fee handler in the chain. - let mut modified_fees = fees.inner().clone(); - modified_fees.remove(fee_index); - modified_fees.push(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) }); - modified_fees.into() - } -} diff --git a/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs b/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs deleted file mode 100644 index eee91c7a38d90..0000000000000 --- a/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2023 Snowfork -pub mod fee_handler; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b6ba1257a64a6..aa77b4cb0dcdc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -64,8 +64,6 @@ const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EB const XCM_FEE: u128 = 100_000_000_000; const INSUFFICIENT_XCM_FEE: u128 = 1000; const TOKEN_AMOUNT: u128 = 100_000_000_000; -const TREASURY_ACCOUNT: [u8; 32] = - hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); const BRIDGE_FEE: u128 = 4_000_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -424,26 +422,6 @@ fn send_eth_asset_from_asset_hub_to_ethereum_and_back() { RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, ] ); - - let events = BridgeHubWestend::events(); - // Check that the local fee was credited to the Snowbridge sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _ }) - if *who == TREASURY_ACCOUNT.into() - )), - "Snowbridge sovereign takes local fee." - ); - // Check that the remote fee was credited to the AssetHub sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _ }) - if *who == assethub_sovereign - )), - "AssetHub sovereign takes remote fee." - ); }); } @@ -729,7 +707,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { }); BridgeHubWestend::execute_with(|| { - use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; type RuntimeEvent = ::RuntimeEvent; // Check that the transfer token back to Ethereum message was queue in the Ethereum // Outbound Queue @@ -737,25 +714,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { BridgeHubWestend, vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] ); - let events = BridgeHubWestend::events(); - // Check that the local fee was credited to the Snowbridge sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) - if *who == TreasuryAccount::get().into() - )), - "Snowbridge sovereign takes local fee." - ); - // Check that the remote fee was credited to the AssetHub sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) - if *who == assethub_sovereign - )), - "AssetHub sovereign takes remote fee." - ); }); } @@ -1331,8 +1289,6 @@ fn send_weth_from_ethereum_to_ahw_to_ahr_back_to_ahw_and_ethereum() { let fees_asset: AssetId = fee.clone().into(); let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset { assets: Wild(AllCounted(2)), beneficiary }]); - let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); - let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); let assets: Assets = vec![(weth_location.clone(), TOKEN_AMOUNT).into(), (fee, XCM_FEE).into()].into(); @@ -1459,7 +1415,6 @@ fn send_weth_from_ethereum_to_ahw_to_ahr_back_to_ahw_and_ethereum() { }); BridgeHubWestend::execute_with(|| { - use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; type RuntimeEvent = ::RuntimeEvent; // Check that the transfer token back to Ethereum message was queue in the Ethereum // Outbound Queue @@ -1467,25 +1422,6 @@ fn send_weth_from_ethereum_to_ahw_to_ahr_back_to_ahw_and_ethereum() { BridgeHubWestend, vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] ); - let events = BridgeHubWestend::events(); - // Check that the local fee was credited to the Snowbridge sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) - if *who == TreasuryAccount::get().into() - )), - "Snowbridge sovereign takes local fee." - ); - // Check that the remote fee was credited to the AssetHub sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount: _amount }) - if *who == assethub_sovereign - )), - "AssetHub sovereign takes remote fee." - ); }); } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index f5923e0bf162a..a75025557e75a 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -54,10 +54,10 @@ use xcm_builder::{ LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, - WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, + UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -460,7 +460,7 @@ pub type XcmRouter = WithUniqueTopic<( XcmpQueue, UniversalLocation, >, - SovereignPaidRemoteExporter< + UnpaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTable, XcmpQueue, UniversalLocation, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index dab4ff8b7779c..60282e83698d3 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -41,7 +41,6 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH}; @@ -55,6 +54,7 @@ use xcm_builder::{ SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::{ traits::{FeeManager, FeeReason, FeeReason::Export}, @@ -220,19 +220,9 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeeManagerFromComponentsBridgeHub< + type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, - ( - XcmExportFeeToSibling< - bp_rococo::Balance, - AccountId, - TokenLocation, - EthereumNetwork, - Self::AssetTransactor, - crate::EthereumOutboundQueue, - >, - SendXcmFeeToAccount, - ), + SendXcmFeeToAccount, >; type MessageExporter = ( XcmOverBridgeHubWestend, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 4f7384c2a585b..fb9974a608609 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -40,9 +40,7 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_runtime::traits::AccountIdConversion; -use sp_std::marker::PhantomData; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ @@ -50,16 +48,13 @@ use xcm_builder::{ AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyRecursively, DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, - FrameTransactionalProcessor, FungibleAdapter, HandleFee, HashedDescription, IsConcrete, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, -}; -use xcm_executor::{ - traits::{FeeManager, FeeReason, FeeReason::Export}, - XcmExecutor, + FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; +use xcm_executor::XcmExecutor; parameter_types! { pub const RootLocation: Location = Location::here(); @@ -231,19 +226,9 @@ impl xcm_executor::Config for XcmConfig { type SubscriptionService = PolkadotXcm; type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type FeeManager = XcmFeeManagerFromComponentsBridgeHub< + type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, - ( - XcmExportFeeToSibling< - bp_westend::Balance, - AccountId, - WestendLocation, - EthereumNetwork, - Self::AssetTransactor, - crate::EthereumOutboundQueue, - >, - SendXcmFeeToAccount, - ), + SendXcmFeeToAccount, >; type MessageExporter = ( XcmOverBridgeHubRococo, @@ -326,25 +311,3 @@ impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; } - -pub struct XcmFeeManagerFromComponentsBridgeHub( - PhantomData<(WaivedLocations, HandleFee)>, -); -impl, FeeHandler: HandleFee> FeeManager - for XcmFeeManagerFromComponentsBridgeHub -{ - fn is_waived(origin: Option<&Location>, fee_reason: FeeReason) -> bool { - let Some(loc) = origin else { return false }; - if let Export { network, destination: Here } = fee_reason { - if network == EthereumNetwork::get().into() { - return false - } - } - WaivedLocations::contains(loc) - } - - fn handle_fee(fee: Assets, context: Option<&XcmContext>, reason: FeeReason) { - tracing::trace!(target: "xcm::handle_fee", ?fee, ?context, ?reason, "FeeManager handle_fee"); - FeeHandler::handle_fee(fee, context, reason); - } -} From c51cbcdd2557cfae0a8124cccc55c1a88b3f9e6e Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 May 2025 06:21:23 +0800 Subject: [PATCH 12/16] Update prdoc --- prdoc/pr_8599.prdoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index d77ccb0f6fbf9..46a4f3cfc15b1 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -2,13 +2,17 @@ title: Snowbridge: Unpaid execution when bridging to Ethereum doc: - audience: Runtime Dev description: |- - Since all fees in Snowbridge V2 will be estimated on the fly and injected into the XCM, there is - no need to preconfigure a bridge fee using SovereignPaidRemoteExporter. Additionally, we want to - avoid maintaining Asset Hub’s sovereign account on Bridge Hub. + Since in Snowbridge V2 the execution fee on Ethereum will be estimated on the fly + and injected into the XCM, there is no need to preconfigure the bridge fee. + Additionally, we want to avoid maintaining Asset Hub’s sovereign account on Bridge Hub. crates: +- name: snowbridge-runtime-common + bump: patch - name: staging-xcm-builder bump: patch - name: bridge-hub-westend-runtime bump: patch - name: asset-hub-westend-runtime bump: patch +- name: bridge-hub-westend-integration-tests + bump: none From 4cf2753068317413fc0af867ec8ec22f1b4dda8f Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 May 2025 06:46:32 +0800 Subject: [PATCH 13/16] Polish prdoc --- prdoc/pr_8599.prdoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index 46a4f3cfc15b1..700834db2f8b3 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -2,9 +2,8 @@ title: Snowbridge: Unpaid execution when bridging to Ethereum doc: - audience: Runtime Dev description: |- - Since in Snowbridge V2 the execution fee on Ethereum will be estimated on the fly - and injected into the XCM, there is no need to preconfigure the bridge fee. - Additionally, we want to avoid maintaining Asset Hub’s sovereign account on Bridge Hub. + In Snowbridge V2, the execution fee on Ethereum is estimated dynamically and injected into the XCM, eliminating the need to preconfigure the bridge fee. + Additionally, we also aim to avoid maintaining the Asset Hub’s sovereign account on the Bridge Hub. crates: - name: snowbridge-runtime-common bump: patch From 2a3c13909f35eb74d38bbe4d20b80c3788a268c9 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 May 2025 06:49:30 +0800 Subject: [PATCH 14/16] Fix prdoc --- prdoc/pr_8599.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index 700834db2f8b3..fa6cd0fce0226 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -1,4 +1,4 @@ -title: Snowbridge: Unpaid execution when bridging to Ethereum +title: 'Snowbridge: Unpaid execution when bridging to Ethereum' doc: - audience: Runtime Dev description: |- From 354264955a55be1ed6a1e6458fc40488fe0f8592 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 May 2025 22:05:09 +0800 Subject: [PATCH 15/16] Fix breaking tests --- .../snowbridge/runtime/test-common/src/lib.rs | 13 +++++------- .../bridge-hub-rococo/src/xcm_config.rs | 2 ++ .../bridge-hub-rococo/tests/snowbridge.rs | 20 ++----------------- .../bridge-hub-westend/tests/snowbridge.rs | 20 ++----------------- .../bridge-hub-westend/tests/tests.rs | 2 +- 5 files changed, 12 insertions(+), 45 deletions(-) diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index 5441dd822caca..0e5bf409a1f08 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -306,6 +306,7 @@ pub fn send_unpaid_transfer_token_message( + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_system::Config + pallet_timestamp::Config, XcmConfig: xcm_executor::Config, ValidatorIdOf: From>, @@ -319,12 +320,9 @@ pub fn send_unpaid_transfer_token_message( .with_tracing() .build() .execute_with(|| { - let asset_hub_sovereign_account = - snowbridge_core::sibling_sovereign_account::(assethub_parachain_id.into()); - - >::mint_into( - &asset_hub_sovereign_account, - 4000000000u32.into(), + >::initialize( + runtime_para_id.into(), + assethub_parachain_id.into(), ) .unwrap(); @@ -370,8 +368,7 @@ pub fn send_unpaid_transfer_token_message( RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), Weight::zero(), ); - // check error is barrier - assert_err!(outcome.ensure_complete(), XcmError::Barrier); + assert_ok!(outcome.ensure_complete()); }); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 60282e83698d3..8a661ed53236e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -73,6 +73,7 @@ parameter_types! { pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); pub RelayTreasuryLocation: Location = (Parent, PalletInstance(rococo_runtime_constants::TREASURY_PALLET_ID)).into(); pub SiblingPeople: Location = (Parent, Parachain(rococo_runtime_constants::system_parachain::PEOPLE_ID)).into(); + pub AssetHubRococoLocation: Location = Location::new(1, [Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)]); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -155,6 +156,7 @@ pub type Barrier = TrailingSetTopicAsId< ParentOrParentsPlurality, Equals, Equals, + Equals, )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index 332977b672839..7643cf3efcc93 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -24,7 +24,7 @@ use bridge_hub_rococo_runtime::{ TxExtension, UncheckedExtrinsic, }; use codec::{Decode, Encode}; -use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use cumulus_primitives_core::XcmError::FailedToTransactAsset; use frame_support::parameter_types; use parachains_common::{AccountId, AuraId, Balance}; use snowbridge_pallet_ethereum_client::WeightInfo; @@ -67,7 +67,7 @@ pub fn transfer_token_to_ethereum_works() { } #[test] -pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { +pub fn unpaid_transfer_token_to_ethereum_should_work() { snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( 11155111, collator_session_keys(), @@ -78,22 +78,6 @@ pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { ) } -#[test] -pub fn transfer_token_to_ethereum_fee_not_enough() { - snowbridge_runtime_test_common::send_transfer_token_message_failure::( - 11155111, - collator_session_keys(), - 1013, - 1000, - DefaultBridgeHubEthereumBaseFee::get() + 1_000_000_000, - H160::random(), - H160::random(), - // fee not enough - 1_000_000_000, - NotHoldingFees, - ) -} - #[test] pub fn transfer_token_to_ethereum_insufficient_fund() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index 39227bfadfc1e..427ecefa53f24 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -25,7 +25,7 @@ use bridge_hub_westend_runtime::{ RuntimeCall, RuntimeEvent, SessionKeys, TxExtension, UncheckedExtrinsic, }; use codec::{Decode, Encode}; -use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use cumulus_primitives_core::XcmError::FailedToTransactAsset; use frame_support::parameter_types; use parachains_common::{AccountId, AuraId, Balance}; use snowbridge_pallet_ethereum_client::WeightInfo; @@ -68,7 +68,7 @@ pub fn transfer_token_to_ethereum_works() { } #[test] -pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { +pub fn unpaid_transfer_token_to_ethereum_should_work() { snowbridge_runtime_test_common::send_unpaid_transfer_token_message::( 11155111, collator_session_keys(), @@ -79,22 +79,6 @@ pub fn unpaid_transfer_token_to_ethereum_fails_with_barrier() { ) } -#[test] -pub fn transfer_token_to_ethereum_fee_not_enough() { - snowbridge_runtime_test_common::send_transfer_token_message_failure::( - 11155111, - collator_session_keys(), - BRIDGE_HUB_WESTEND_PARACHAIN_ID, - ASSET_HUB_WESTEND_PARACHAIN_ID, - DefaultBridgeHubEthereumBaseFee::get() + 20_000_000_000, - H160::random(), - H160::random(), - // fee not enough - 20_000_000_000, - NotHoldingFees, - ) -} - #[test] pub fn transfer_token_to_ethereum_insufficient_fund() { snowbridge_runtime_test_common::send_transfer_token_message_failure::( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index 7f5efbf295de0..45aaee3339628 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -789,7 +789,7 @@ fn governance_authorize_upgrade_works() { Runtime, RuntimeOrigin, >(GovernanceOrigin::Location(Location::new(1, Parachain(ASSET_HUB_ID)))), - Either::Right(XcmError::Barrier) + Either::Right(XcmError::BadOrigin) ); // no - Collectives assert_err!( From e1e6ee4c8e8c566dae69abea915d29031686f003 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 May 2025 22:45:26 +0800 Subject: [PATCH 16/16] Update prdoc --- prdoc/pr_8599.prdoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc index fa6cd0fce0226..aeb13a9ce687c 100644 --- a/prdoc/pr_8599.prdoc +++ b/prdoc/pr_8599.prdoc @@ -5,13 +5,17 @@ doc: In Snowbridge V2, the execution fee on Ethereum is estimated dynamically and injected into the XCM, eliminating the need to preconfigure the bridge fee. Additionally, we also aim to avoid maintaining the Asset Hub’s sovereign account on the Bridge Hub. crates: -- name: snowbridge-runtime-common - bump: patch - name: staging-xcm-builder bump: patch +- name: snowbridge-runtime-common + bump: major +- name: bridge-hub-rococo-runtime + bump: major - name: bridge-hub-westend-runtime - bump: patch + bump: major - name: asset-hub-westend-runtime bump: patch - name: bridge-hub-westend-integration-tests bump: none +- name: snowbridge-runtime-test-common + bump: none