diff --git a/bridges/snowbridge/pallets/system-frontend/src/backend_weights.rs b/bridges/snowbridge/pallets/system-frontend/src/backend_weights.rs index 201e28029a96a..75c188df91b3a 100644 --- a/bridges/snowbridge/pallets/system-frontend/src/backend_weights.rs +++ b/bridges/snowbridge/pallets/system-frontend/src/backend_weights.rs @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! XCM Execution weights for invoking the backend implementation -use frame_support::weights::Weight; +use frame_support::weights::{constants::RocksDbWeight, Weight}; /// XCM Execution weights for invoking the backend implementation pub trait BackendWeightInfo { @@ -10,6 +10,9 @@ pub trait BackendWeightInfo { /// using `Transact`. fn transact_register_token() -> Weight; fn transact_add_tip() -> Weight; + fn do_process_message() -> Weight; + fn commit_single() -> Weight; + fn submit_delivery_receipt() -> Weight; } impl BackendWeightInfo for () { @@ -19,4 +22,21 @@ impl BackendWeightInfo for () { fn transact_add_tip() -> Weight { Weight::from_parts(100_000_000, 10000) } + fn do_process_message() -> Weight { + Weight::from_parts(39_000_000, 3485) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + fn commit_single() -> Weight { + Weight::from_parts(9_000_000, 1586) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + + fn submit_delivery_receipt() -> Weight { + Weight::from_parts(70_000_000, 0) + .saturating_add(Weight::from_parts(0, 3601)) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(2)) + } } diff --git a/bridges/snowbridge/pallets/system-frontend/src/benchmarking.rs b/bridges/snowbridge/pallets/system-frontend/src/benchmarking.rs index f7d206879e151..c5fc62cc6f678 100644 --- a/bridges/snowbridge/pallets/system-frontend/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system-frontend/src/benchmarking.rs @@ -7,6 +7,7 @@ use crate::Pallet as SnowbridgeControlFrontend; use frame_benchmarking::v2::*; use frame_system::RawOrigin; use xcm::prelude::{Location, *}; +use xcm_executor::traits::ConvertLocation; #[benchmarks(where ::AccountId: Into)] mod benchmarks { @@ -18,7 +19,11 @@ mod benchmarks { let asset_location: Location = Location::new(1, [Parachain(2000), GeneralIndex(1)]); let asset_id = Box::new(VersionedLocation::from(asset_location.clone())); - T::Helper::initialize_storage(asset_location, origin_location); + T::Helper::initialize_storage(asset_location, origin_location.clone()); + + let ether = T::EthereumLocation::get(); + let asset_owner = T::AccountIdConverter::convert_location(&origin_location).unwrap(); + T::Helper::setup_pools(asset_owner, ether.clone()); let asset_metadata = AssetMetadata { name: "pal".as_bytes().to_vec().try_into().unwrap(), @@ -26,8 +31,10 @@ mod benchmarks { decimals: 12, }; + let fee_asset = Asset::from((Location::parent(), 1_000_000u128)); + #[extrinsic_call] - _(origin as T::RuntimeOrigin, asset_id, asset_metadata); + _(origin as T::RuntimeOrigin, asset_id, asset_metadata, fee_asset); Ok(()) } diff --git a/bridges/snowbridge/pallets/system-frontend/src/lib.rs b/bridges/snowbridge/pallets/system-frontend/src/lib.rs index d59464fa5cde7..af95fbcb0320d 100644 --- a/bridges/snowbridge/pallets/system-frontend/src/lib.rs +++ b/bridges/snowbridge/pallets/system-frontend/src/lib.rs @@ -62,6 +62,7 @@ pub enum EthereumSystemCall { sender: Box, asset_id: Box, metadata: AssetMetadata, + amount: u128, }, #[codec(index = 3)] AddTip { sender: AccountIdOf, message_id: MessageId, amount: u128 }, @@ -80,6 +81,7 @@ where #[frame_support::pallet] pub mod pallet { use super::*; + use xcm_executor::traits::ConvertLocation; #[pallet::pallet] pub struct Pallet(_); @@ -118,6 +120,8 @@ pub mod pallet { /// InteriorLocation of this pallet. type PalletLocation: Get; + type AccountIdConverter: ConvertLocation; + /// Weights for dispatching XCM to backend implementation of `register_token` type BackendWeightInfo: BackendWeightInfo; @@ -215,11 +219,15 @@ pub mod pallet { #[pallet::weight( T::WeightInfo::register_token() .saturating_add(T::BackendWeightInfo::transact_register_token()) + .saturating_add(T::BackendWeightInfo::do_process_message()) + .saturating_add(T::BackendWeightInfo::commit_single()) + .saturating_add(T::BackendWeightInfo::submit_delivery_receipt()) )] pub fn register_token( origin: OriginFor, asset_id: Box, metadata: AssetMetadata, + fee_asset: Asset, ) -> DispatchResult { ensure!(!Self::export_operating_mode().is_halted(), Error::::Halted); @@ -227,21 +235,21 @@ pub mod pallet { (*asset_id).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; let origin_location = T::RegisterTokenOrigin::ensure_origin(origin, &asset_location)?; - let dest = T::BridgeHubLocation::get(); - let call = - Self::build_register_token_call(origin_location.clone(), asset_location, metadata)?; - let remote_xcm = Self::build_remote_xcm(&call); - let message_id = Self::send_xcm(origin_location, dest.clone(), remote_xcm.clone()) - .map_err(|error| Error::::from(error))?; + let ether_gained = if origin_location.is_here() { + // Root origin/location does not pay any fees/tip. + 0 + } else { + Self::swap_fee_asset_and_burn(origin_location.clone(), fee_asset)? + }; - Self::deposit_event(Event::::MessageSent { - origin: T::PalletLocation::get().into(), - destination: dest, - message: remote_xcm, - message_id, - }); + let call = Self::build_register_token_call( + origin_location.clone(), + asset_location, + metadata, + ether_gained, + )?; - Ok(()) + Self::send_transact_call(origin_location, call) } /// Add an additional relayer tip for a committed message identified by `message_id`. @@ -257,49 +265,12 @@ pub mod pallet { { let who = ensure_signed(origin)?; - let ether_location = T::EthereumLocation::get(); - let (tip_asset_location, tip_amount) = match asset { - Asset { id: AssetId(ref loc), fun: Fungibility::Fungible(amount) } => (loc, amount), - _ => { - tracing::debug!(target: LOG_TARGET, ?asset, "error matching tip asset"); - return Err(Error::::UnsupportedAsset.into()) - }, - }; - - ensure!(tip_amount > 0, Error::::TipAmountZero); - - let ether_gained = if *tip_asset_location != ether_location { - Self::swap_and_burn( - who.clone(), - tip_asset_location.clone(), - ether_location, - tip_amount, - ) - .inspect_err(|&e| { - tracing::debug!(target: LOG_TARGET, ?e, "error swapping asset"); - })? - } else { - tip_amount - }; + let ether_gained = Self::swap_fee_asset_and_burn(who.clone().into(), asset)?; // Send the tip details to BH to be allocated to the reward in the Inbound/Outbound // pallet - let dest = T::BridgeHubLocation::get(); let call = Self::build_add_tip_call(who.clone(), message_id.clone(), ether_gained); - let remote_xcm = Self::build_remote_xcm(&call); - let who_location: Location = who.into(); - - let xcm_message_id = Self::send_xcm(who_location, dest.clone(), remote_xcm.clone()) - .map_err(|error| Error::::from(error))?; - - Self::deposit_event(Event::::MessageSent { - origin: T::PalletLocation::get().into(), - destination: dest, - message: remote_xcm, - message_id: xcm_message_id, - }); - - Ok(()) + Self::send_transact_call(who.into(), call) } } @@ -318,17 +289,15 @@ pub mod pallet { /// teleportation. Returns the amount of Ether gained if successful, or a DispatchError if /// any step fails. fn swap_and_burn( - who: AccountIdOf, + origin: Location, tip_asset_location: Location, ether_location: Location, tip_amount: u128, - ) -> Result - where - ::AccountId: Into, - { + ) -> Result { // Swap tip asset to ether let swap_path = vec![tip_asset_location.clone(), ether_location.clone()]; - let who_location: Location = who.clone().into(); + let who = T::AccountIdConverter::convert_location(&origin) + .ok_or(Error::::LocationConversionFailed)?; let ether_gained = T::Swap::swap_exact_tokens_for_tokens( who.clone(), @@ -342,7 +311,7 @@ pub mod pallet { // Burn the ether let ether_asset = Asset::from((ether_location.clone(), ether_gained)); - burn_for_teleport::(&who_location, ðer_asset) + burn_for_teleport::(&origin, ðer_asset) .map_err(|_| Error::::BurnError)?; Ok(ether_gained) @@ -353,6 +322,7 @@ pub mod pallet { sender: Location, asset: Location, metadata: AssetMetadata, + amount: u128, ) -> Result, Error> { // reanchor locations relative to BH let sender = Self::reanchored(sender)?; @@ -362,6 +332,7 @@ pub mod pallet { sender: Box::new(VersionedLocation::from(sender)), asset_id: Box::new(VersionedLocation::from(asset)), metadata, + amount, }); Ok(call) @@ -398,6 +369,59 @@ pub mod pallet { .reanchored(&T::BridgeHubLocation::get(), &T::UniversalLocation::get()) .map_err(|_| Error::::LocationConversionFailed) } + + fn swap_fee_asset_and_burn( + origin: Location, + fee_asset: Asset, + ) -> Result { + let ether_location = T::EthereumLocation::get(); + let (fee_asset_location, fee_amount) = match fee_asset { + Asset { id: AssetId(ref loc), fun: Fungible(amount) } => (loc, amount), + _ => { + tracing::debug!(target: LOG_TARGET, ?fee_asset, "error matching fee asset"); + return Err(Error::::UnsupportedAsset.into()) + }, + }; + if fee_amount == 0 { + return Ok(0) + } + + let ether_gained = if *fee_asset_location != ether_location { + Self::swap_and_burn( + origin.clone(), + fee_asset_location.clone(), + ether_location, + fee_amount, + ) + .inspect_err(|&e| { + tracing::debug!(target: LOG_TARGET, ?e, "error swapping asset"); + })? + } else { + burn_for_teleport::(&origin, &fee_asset) + .map_err(|_| Error::::BurnError)?; + fee_amount + }; + Ok(ether_gained) + } + + fn send_transact_call( + origin_location: Location, + call: BridgeHubRuntime, + ) -> DispatchResult { + let dest = T::BridgeHubLocation::get(); + let remote_xcm = Self::build_remote_xcm(&call); + let message_id = Self::send_xcm(origin_location, dest.clone(), remote_xcm.clone()) + .map_err(|error| Error::::from(error))?; + + Self::deposit_event(Event::::MessageSent { + origin: T::PalletLocation::get().into(), + destination: dest, + message: remote_xcm, + message_id, + }); + + Ok(()) + } } impl ExportPausedQuery for Pallet { diff --git a/bridges/snowbridge/pallets/system-frontend/src/mock.rs b/bridges/snowbridge/pallets/system-frontend/src/mock.rs index 38026b9253186..d4f6562d6a33a 100644 --- a/bridges/snowbridge/pallets/system-frontend/src/mock.rs +++ b/bridges/snowbridge/pallets/system-frontend/src/mock.rs @@ -7,11 +7,12 @@ use frame_support::{ derive_impl, parameter_types, traits::{AsEnsureOriginWithArg, Everything}, }; +use snowbridge_core::ParaId; use snowbridge_test_utils::mock_swap_executor::SwapExecutor; pub use snowbridge_test_utils::{mock_origin::pallet_xcm_origin, mock_xcm::*}; use sp_core::H256; use sp_runtime::{ - traits::{BlakeTwo256, IdentityLookup}, + traits::{AccountIdConversion, BlakeTwo256, IdentityLookup}, AccountId32, BuildStorage, }; use xcm::prelude::*; @@ -75,6 +76,18 @@ parameter_types! { pub PalletLocation: InteriorLocation = [PalletInstance(36)].into(); } +pub struct AccountIdConverter; +impl xcm_executor::traits::ConvertLocation for AccountIdConverter { + fn convert_location(ml: &Location) -> Option { + match ml.unpack() { + (0, [Junction::AccountId32 { id, .. }]) => + Some(::decode(&mut &*id.to_vec()).unwrap()), + (1, [Parachain(id)]) => Some(ParaId::from(*id).into_account_truncating()), + _ => None, + } + } +} + impl crate::Config for Test { type RuntimeEvent = RuntimeEvent; type RegisterTokenOrigin = AsEnsureOriginWithArg>; @@ -90,6 +103,7 @@ impl crate::Config for Test { type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); + type AccountIdConverter = AccountIdConverter; } // Build genesis storage according to the mock runtime. diff --git a/bridges/snowbridge/pallets/system-frontend/src/tests.rs b/bridges/snowbridge/pallets/system-frontend/src/tests.rs index 16170387c3917..53412fe9557c2 100644 --- a/bridges/snowbridge/pallets/system-frontend/src/tests.rs +++ b/bridges/snowbridge/pallets/system-frontend/src/tests.rs @@ -26,10 +26,15 @@ fn register_token() { decimals: 12, }; + let ether_location = Ether::get(); + let fee_amount = 1000; + let asset = Asset::from((ether_location.clone(), fee_amount)); + assert_ok!(EthereumSystemFrontend::register_token( origin.clone(), asset_id.clone(), - asset_metadata.clone() + asset_metadata.clone(), + asset.clone(), )); }); } @@ -46,11 +51,14 @@ fn register_token_fails_delivery_fees_not_met() { symbol: "pal".as_bytes().to_vec().try_into().unwrap(), decimals: 12, }; + let ether_location = Ether::get(); + let fee_amount = 1000; + let asset = Asset::from((ether_location.clone(), fee_amount)); set_charge_fees_override(|_, _| Err(XcmError::FeesNotMet)); assert_err!( - EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata), + EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata, asset.clone()), Error::::FeesNotMet, ); }); @@ -68,6 +76,9 @@ fn register_token_fails_unroutable() { symbol: "pal".as_bytes().to_vec().try_into().unwrap(), decimals: 12, }; + let ether_location = Ether::get(); + let fee_amount = 1000; + let asset = Asset::from((ether_location.clone(), fee_amount)); // Send XCM with overrides for `SendXcm` behavior to return `Unroutable` error on // validate @@ -79,7 +90,8 @@ fn register_token_fails_unroutable() { EthereumSystemFrontend::register_token( origin.clone(), asset_id.clone(), - asset_metadata.clone() + asset_metadata.clone(), + asset.clone(), ), Error::::SendFailure ); @@ -92,7 +104,7 @@ fn register_token_fails_unroutable() { ); assert_err!( - EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata), + EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata, asset.clone()), Error::::SendFailure ); }); @@ -114,11 +126,15 @@ fn test_switch_operating_mode() { symbol: "pal".as_bytes().to_vec().try_into().unwrap(), decimals: 12, }; + let ether_location = Ether::get(); + let fee_amount = 1000; + let asset = Asset::from((ether_location.clone(), fee_amount)); assert_noop!( EthereumSystemFrontend::register_token( origin.clone(), asset_id.clone(), - asset_metadata.clone() + asset_metadata.clone(), + asset.clone(), ), crate::Error::::Halted ); @@ -126,7 +142,7 @@ fn test_switch_operating_mode() { RawOrigin::Root.into(), BasicOperatingMode::Normal, )); - assert_ok!(EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata),); + assert_ok!(EthereumSystemFrontend::register_token(origin, asset_id, asset_metadata, asset)); }); } @@ -249,3 +265,28 @@ fn tip_fails_due_to_swap_error() { ); }); } + +#[test] +fn register_token_with_non_ether_fee_asset_succeeds() { + new_test_ext().execute_with(|| { + let origin_location = Location::new(1, [Parachain(2000)]); + let origin = make_xcm_origin(origin_location.clone()); + let asset_location: Location = Location::new(1, [Parachain(2000), GeneralIndex(1)]); + let asset_id = Box::new(VersionedLocation::from(asset_location)); + let asset_metadata = AssetMetadata { + name: "pal".as_bytes().to_vec().try_into().unwrap(), + symbol: "pal".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }; + + let fee_amount = 1000; + let fee_asset = Asset::from((Location::parent(), fee_amount)); + + assert_ok!(EthereumSystemFrontend::register_token( + origin.clone(), + asset_id.clone(), + asset_metadata.clone(), + fee_asset.clone(), + )); + }); +} diff --git a/bridges/snowbridge/pallets/system-v2/src/benchmarking.rs b/bridges/snowbridge/pallets/system-v2/src/benchmarking.rs index d967c851a92ce..7350baa919770 100644 --- a/bridges/snowbridge/pallets/system-v2/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system-v2/src/benchmarking.rs @@ -27,7 +27,7 @@ mod benchmarks { }; #[extrinsic_call] - _(origin as T::RuntimeOrigin, creator, asset, asset_metadata); + _(origin as T::RuntimeOrigin, creator, asset, asset_metadata, 1); Ok(()) } diff --git a/bridges/snowbridge/pallets/system-v2/src/lib.rs b/bridges/snowbridge/pallets/system-v2/src/lib.rs index 100727cae752c..095b263f8f257 100644 --- a/bridges/snowbridge/pallets/system-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/system-v2/src/lib.rs @@ -213,6 +213,7 @@ pub mod pallet { sender: Box, asset_id: Box, metadata: AssetMetadata, + amount: u128, ) -> DispatchResult { T::FrontendOrigin::ensure_origin(origin)?; @@ -237,7 +238,7 @@ pub mod pallet { }; let message_origin = Self::location_to_message_origin(sender_location)?; - Self::send(message_origin, command, 0)?; + Self::send(message_origin, command, amount)?; Self::deposit_event(Event::::RegisterToken { location: location.into(), diff --git a/bridges/snowbridge/pallets/system-v2/src/tests.rs b/bridges/snowbridge/pallets/system-v2/src/tests.rs index 6edf9e2243708..2475dfa67c399 100644 --- a/bridges/snowbridge/pallets/system-v2/src/tests.rs +++ b/bridges/snowbridge/pallets/system-v2/src/tests.rs @@ -17,6 +17,7 @@ fn register_tokens_succeeds() { Box::new(versioned_location.clone()), Box::new(versioned_location), Default::default(), + 1 )); }); } @@ -129,7 +130,8 @@ fn register_all_tokens_succeeds() { origin, Box::new(versioned_location.clone()), Box::new(versioned_location), - Default::default() + Default::default(), + 1 )); let reanchored_location = EthereumSystemV2::reanchor(tc.native.clone()).unwrap(); @@ -162,7 +164,8 @@ fn register_ethereum_native_token_fails() { origin, versioned_location.clone(), versioned_location.clone(), - Default::default() + Default::default(), + 1 ), Error::::LocationConversionFailed ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs index 090614d3351ef..2f9f06b46c5b8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs @@ -33,7 +33,7 @@ use xcm::v5::AssetTransferFilter; #[derive(Encode, Decode, Debug, PartialEq, Clone, TypeInfo)] pub enum EthereumSystemFrontendCall { #[codec(index = 1)] - RegisterToken { asset_id: Box, metadata: AssetMetadata }, + RegisterToken { asset_id: Box, metadata: AssetMetadata, fee_asset: Asset }, } #[allow(clippy::large_enum_variant)] @@ -132,6 +132,8 @@ pub fn register_relay_token_from_asset_hub_with_sudo() { AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; + let fees_asset = Asset { id: AssetId(ethereum()), fun: Fungible(1) }; + assert_ok!( ::SnowbridgeSystemFrontend::register_token( RuntimeOrigin::root(), @@ -140,10 +142,19 @@ pub fn register_relay_token_from_asset_hub_with_sudo() { name: "wnd".as_bytes().to_vec().try_into().unwrap(), symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), decimals: 12, - } + }, + fees_asset ) ); }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueueV2(snowbridge_pallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},] + ); + }); } #[test] @@ -151,8 +162,13 @@ pub fn register_usdt_from_owner_on_asset_hub() { fund_on_bh(); register_assets_on_ah(); fund_on_ah(); + set_up_eth_and_dot_pool(); AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let fees_asset = + Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000u128) }; assert_ok!( ::SnowbridgeSystemFrontend::register_token( @@ -162,9 +178,14 @@ pub fn register_usdt_from_owner_on_asset_hub() { name: "usdt".as_bytes().to_vec().try_into().unwrap(), symbol: "usdt".as_bytes().to_vec().try_into().unwrap(), decimals: 6, - } + }, + fees_asset ) ); + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::SwapExecuted { .. }) => {},] + ); }); BridgeHubWestend::execute_with(|| { @@ -714,6 +735,7 @@ fn register_token_from_penpal() { EthereumSystemFrontendCall::RegisterToken { asset_id: Box::new(VersionedLocation::from(foreign_asset_at_asset_hub)), metadata: Default::default(), + fee_asset: remote_fee_asset_on_ethereum.clone(), }, ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs index 807fe76c02f45..495cf6f6f5601 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs @@ -67,6 +67,7 @@ fn register_penpal_a_asset_from_penpal_b_will_fail() { EthereumSystemFrontendCall::RegisterToken { asset_id: Box::new(VersionedLocation::from(penpal_a_asset_at_asset_hub)), metadata: Default::default(), + fee_asset: remote_fee_asset_on_ethereum.clone(), }, ); @@ -181,6 +182,9 @@ pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() { AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; + let fees_asset = + Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + assert_noop!( ::SnowbridgeSystemFrontend::register_token( // The owner is Alice, while AssetHubWestendReceiver is Bob, so it should fail @@ -190,7 +194,8 @@ pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() { name: "usdt".as_bytes().to_vec().try_into().unwrap(), symbol: "usdt".as_bytes().to_vec().try_into().unwrap(), decimals: 6, - } + }, + fees_asset ), BadOrigin ); @@ -205,6 +210,9 @@ pub fn register_relay_token_from_asset_hub_user_origin_will_fail() { AssetHubWestend::execute_with(|| { type RuntimeOrigin = ::RuntimeOrigin; + let fees_asset = + Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + assert_noop!( ::SnowbridgeSystemFrontend::register_token( RuntimeOrigin::signed(AssetHubWestendSender::get()), @@ -214,6 +222,7 @@ pub fn register_relay_token_from_asset_hub_user_origin_will_fail() { symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), decimals: 12, }, + fees_asset ), BadOrigin ); diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs index 8ed0fd3048e12..b7348ed1e1a60 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs @@ -303,10 +303,13 @@ fn register_rococo_asset_on_ethereum_from_rah() { vec![], ); + let fee_asset = Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) }; + let call = EthereumSystemFrontend::EthereumSystemFrontend(EthereumSystemFrontendCall::RegisterToken { asset_id: Box::new(VersionedLocation::from(bridged_asset_at_wah.clone())), metadata: Default::default(), + fee_asset, }) .encode(); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index 1cede206ccb92..3fd8cd8aa7872 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -24,8 +24,6 @@ use crate::{ AccountId, AssetConversion, Assets, ForeignAssets, Runtime, RuntimeEvent, }; use assets_common::{matching::FromSiblingParachain, AssetIdForTrustBackedAssetsConvert}; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, traits::EitherOf}; use frame_system::EnsureRootWithSuccess; use parachains_common::AssetIdForTrustBackedAssets; @@ -66,24 +64,28 @@ pub mod benchmark_helpers { RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) } - fn initialize_storage(asset_location: Location, asset_owner: Location) { - let asset_owner = LocationToAccountId::convert_location(&asset_owner).unwrap(); + fn initialize_storage(asset_location: Location, asset_owner_location: Location) { + let asset_owner = LocationToAccountId::convert_location(&asset_owner_location).unwrap(); ForeignAssets::force_create( RuntimeOrigin::root(), asset_location, - asset_owner.into(), + asset_owner.clone().into(), true, 1, ) - .unwrap() + .unwrap(); } fn setup_pools(caller: AccountId, asset: Location) { // Prefund the caller's account with DOT - Balances::force_set_balance(RuntimeOrigin::root(), caller.into(), 10_000_000_000_000) - .unwrap(); + Balances::force_set_balance( + RuntimeOrigin::root(), + caller.clone().into(), + 10_000_000_000_000, + ) + .unwrap(); - let asset_owner = LocationToAccountId::convert_location(&asset).unwrap(); + let asset_owner = caller.clone(); ForeignAssets::force_create( RuntimeOrigin::root(), asset.clone(), @@ -176,7 +178,7 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { #[cfg(not(feature = "runtime-benchmarks"))] type XcmSender = XcmRouter; #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; + type XcmSender = benchmark_helpers::DoNothingRouter; type AssetTransactor = AssetTransactors; type EthereumLocation = FeeAsset; type XcmExecutor = XcmExecutor; @@ -185,4 +187,5 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { type PalletLocation = SystemFrontendPalletLocation; type Swap = AssetConversion; type BackendWeightInfo = weights::snowbridge_pallet_system_backend::WeightInfo; + type AccountIdConverter = xcm_config::LocationToAccountId; } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/snowbridge_pallet_system_backend.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/snowbridge_pallet_system_backend.rs index 4170cc5483cde..1cb91f2dcfdf2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/snowbridge_pallet_system_backend.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/snowbridge_pallet_system_backend.rs @@ -37,4 +37,24 @@ impl snowbridge_pallet_system_frontend::BackendWeightIn .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn do_process_message() -> Weight { + Weight::from_parts(19_000_000, 0) + .saturating_add(Weight::from_parts(0, 1527)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(4)) + } + + fn commit_single() -> Weight { + Weight::from_parts(10_000_000, 0) + .saturating_add(Weight::from_parts(0, 1620)) + .saturating_add(T::DbWeight::get().reads(1)) + } + + fn submit_delivery_receipt() -> Weight { + Weight::from_parts(68_000_000, 0) + .saturating_add(Weight::from_parts(0, 3785)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(1)) + } } diff --git a/prdoc/pr_8725.prdoc b/prdoc/pr_8725.prdoc new file mode 100644 index 0000000000000..0d1ecdef17d3e --- /dev/null +++ b/prdoc/pr_8725.prdoc @@ -0,0 +1,15 @@ +title: 'Snowbridge: register polkadot native asset with fee' +doc: +- audience: Runtime Dev + description: To enforce a fee for PNA registration. +crates: +- name: snowbridge-pallet-system-frontend + bump: patch + validate: false +- name: snowbridge-pallet-system-v2 + bump: patch + validate: false +- name: asset-hub-westend-runtime + bump: patch +- name: bridge-hub-westend-integration-tests + bump: none