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/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/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 fdad733aa5fcb..09920ed00f2cd 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 @@ -65,8 +65,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)] @@ -425,26 +423,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." - ); }); } @@ -730,7 +708,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 @@ -738,25 +715,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." - ); }); } @@ -1332,8 +1290,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(); @@ -1460,7 +1416,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 @@ -1468,25 +1423,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 b6a53ad2612b8..c1e20eb7675a0 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 @@ -55,9 +55,9 @@ use xcm_builder::{ LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, + UnpaidRemoteExporter, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -481,14 +481,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, + >, + UnpaidRemoteExporter< bridging::to_ethereum::EthereumNetworkExportTable, - ), - XcmpQueue, - UniversalLocation, - >, + 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..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 @@ -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}, @@ -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, @@ -220,19 +222,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-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/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 5a00ae852886b..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(); @@ -165,6 +160,7 @@ pub type Barrier = TrailingSetTopicAsId< ParentOrParentsPlurality, Equals, Equals, + Equals, )>, // Subscriptions for version tracking are OK. AllowSubscriptionsFrom, @@ -230,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, @@ -325,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); - } -} 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!( 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 { diff --git a/prdoc/pr_8599.prdoc b/prdoc/pr_8599.prdoc new file mode 100644 index 0000000000000..aeb13a9ce687c --- /dev/null +++ b/prdoc/pr_8599.prdoc @@ -0,0 +1,21 @@ +title: 'Snowbridge: Unpaid execution when bridging to Ethereum' +doc: +- audience: Runtime Dev + description: |- + 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: staging-xcm-builder + bump: patch +- name: snowbridge-runtime-common + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: asset-hub-westend-runtime + bump: patch +- name: bridge-hub-westend-integration-tests + bump: none +- name: snowbridge-runtime-test-common + bump: none