diff --git a/Cargo.lock b/Cargo.lock index 70a8e72fe36f..f2d0c3eed0e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19667,6 +19667,7 @@ dependencies = [ "snowbridge-core", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", + "snowbridge-pallet-inbound-queue", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "sp-core", diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 171a26621c08..5312171eda8a 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -53,6 +53,7 @@ use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, + *, }; use xcm_executor::traits::TransactAsset; @@ -61,9 +62,8 @@ use snowbridge_core::{ sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, StaticLookup, }; -use snowbridge_router_primitives::{ - inbound, - inbound::{ConvertMessage, ConvertMessageError}, +use snowbridge_router_primitives::inbound::{ + ConvertMessage, ConvertMessageError, VersionedMessage, }; use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; @@ -141,6 +141,9 @@ pub mod pallet { /// To withdraw and deposit an asset. type AssetTransactor: TransactAsset; + + /// The most expensive xcm here only used to estimate send cost + type MaxSendCost: Get>; } #[pallet::hooks] @@ -278,12 +281,11 @@ pub mod pallet { } // Decode message into XCM - let (xcm, fee) = - match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { - Ok(message) => T::MessageConverter::convert(envelope.message_id, message) - .map_err(|e| Error::::ConvertMessage(e))?, - Err(_) => return Err(Error::::InvalidPayload.into()), - }; + let (xcm, fee) = match VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { + Ok(message) => T::MessageConverter::convert(envelope.message_id, message) + .map_err(|e| Error::::ConvertMessage(e))?, + Err(_) => return Err(Error::::InvalidPayload.into()), + }; log::info!( target: LOG_TARGET, @@ -329,12 +331,20 @@ pub mod pallet { Ok(xcm_hash) } + pub fn calculate_send_cost(xcm: Xcm<()>, dest: ParaId) -> Result> { + let dest = Location::new(1, [Parachain(dest.into())]); + let (_, fee) = T::XcmSender::validate(&mut Some(dest), &mut Some(xcm)) + .map_err(Error::::from)?; + Ok(fee) + } + pub fn calculate_delivery_cost(length: u32) -> BalanceOf { let weight_fee = T::WeightToFee::weight_to_fee(&T::WeightInfo::submit()); let len_fee = T::LengthToFee::weight_to_fee(&Weight::from_parts(length as u64, 0)); weight_fee .saturating_add(len_fee) .saturating_add(T::PricingParameters::get().rewards.local) + .saturating_add(T::MaxSendCost::get()) } /// Burn the amount of the fee embedded into the XCM for teleports @@ -365,7 +375,6 @@ pub mod pallet { /// API for accessing the delivery cost of a message impl Get> for Pallet { fn get() -> BalanceOf { - // Cost here based on MaxMessagePayloadSize(the worst case) Self::calculate_delivery_cost(T::MaxMessageSize::get()) } } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index a031676c6076..95e0f1eb45ad 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -153,6 +153,8 @@ parameter_types! { rewards: Rewards { local: DOT, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub DefaultFee: Asset = (Parent, 1_000_000_000).into(); + pub MaxSendCost: u128 = 1; } pub const DOT: u128 = 10_000_000_000; @@ -226,6 +228,7 @@ impl inbound_queue::Config for Test { type LengthToFee = IdentityFee; type MaxMessageSize = ConstU32<1024>; type AssetTransactor = SuccessfulTransactor; + type MaxSendCost = MaxSendCost; } pub fn last_events(n: usize) -> Vec { diff --git a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs index 0037f537c480..85648e2ba024 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/lib.rs @@ -410,7 +410,7 @@ pub mod pallet { } /// The local component of the message processing fees in native currency - pub(crate) fn calculate_local_fee() -> T::Balance { + pub fn calculate_local_fee() -> T::Balance { T::WeightToFee::weight_to_fee( &T::WeightInfo::do_process_message().saturating_add(T::WeightInfo::commit_single()), ) diff --git a/bridges/snowbridge/runtime/test-common/Cargo.toml b/bridges/snowbridge/runtime/test-common/Cargo.toml index 6f8e586bf5ff..54977d297cbd 100644 --- a/bridges/snowbridge/runtime/test-common/Cargo.toml +++ b/bridges/snowbridge/runtime/test-common/Cargo.toml @@ -41,6 +41,7 @@ parachains-runtimes-test-utils = { workspace = true } snowbridge-core = { workspace = true } snowbridge-pallet-ethereum-client = { workspace = true } snowbridge-pallet-ethereum-client-fixtures = { workspace = true } +snowbridge-pallet-inbound-queue = { workspace = true } snowbridge-pallet-outbound-queue = { workspace = true } snowbridge-pallet-system = { workspace = true } @@ -63,6 +64,7 @@ std = [ "snowbridge-core/std", "snowbridge-pallet-ethereum-client-fixtures/std", "snowbridge-pallet-ethereum-client/std", + "snowbridge-pallet-inbound-queue/std", "snowbridge-pallet-outbound-queue/std", "snowbridge-pallet-system/std", "sp-core/std", @@ -86,6 +88,7 @@ runtime-benchmarks = [ "snowbridge-core/runtime-benchmarks", "snowbridge-pallet-ethereum-client-fixtures/runtime-benchmarks", "snowbridge-pallet-ethereum-client/runtime-benchmarks", + "snowbridge-pallet-inbound-queue/runtime-benchmarks", "snowbridge-pallet-outbound-queue/runtime-benchmarks", "snowbridge-pallet-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index 8f36313e360f..f484f84c1f58 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -12,7 +12,7 @@ use parachains_runtimes_test_utils::{ }; use snowbridge_core::{ChannelId, ParaId}; use snowbridge_pallet_ethereum_client_fixtures::*; -use sp_core::{H160, U256}; +use sp_core::{Get, H160, U256}; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; use xcm::{ @@ -561,3 +561,101 @@ pub fn ethereum_to_polkadot_message_extrinsics_work( assert_ok!(sync_committee_outcome); }); } + +pub fn fetch_delivery_cost(collator_session_key: CollatorSessionKeys) +where + Runtime: frame_system::Config + + pallet_balances::Config + + pallet_session::Config + + pallet_xcm::Config + + parachain_info::Config + + pallet_collator_selection::Config + + cumulus_pallet_parachain_system::Config + + snowbridge_pallet_outbound_queue::Config + + snowbridge_pallet_inbound_queue::Config + + pallet_timestamp::Config, + XcmConfig: xcm_executor::Config, + ValidatorIdOf: From>, + ::AccountId: From + AsRef<[u8]>, +{ + ExtBuilder::::default() + .with_collators(collator_session_key.collators()) + .with_session_keys(collator_session_key.session_keys()) + .with_para_id(1013.into()) + .with_tracing() + .build() + .execute_with(|| { + let _ = >::force_xcm_version( + RuntimeHelper::::root_origin(), + Box::new(Location { parents: 1, interior: [Parachain(1000)].into() }), + XCM_VERSION, + ); + let inbound_delivery_cost = + >::calculate_delivery_cost( + ::MaxMessageSize::get(), + ); + let inbound_send_cost = + ::MaxSendCost::get(); + let outbound_delivery_cost = + >::calculate_local_fee(); + + println!("inbound_delivery_cost:{:?}", inbound_delivery_cost); + println!("inbound_send_cost:{:?}", inbound_send_cost); + println!("outbound_delivery_cost:{:?}", outbound_delivery_cost); + + const CHAIN_ID: u64 = 11155111; + const WETH: [u8; 20] = [1; 20]; + + let total_fee_amount: u32 = 2_000_000_000; + let asset_hub_fee_amount: u32 = 1_000_000_000; + let dest_fee_amount: u32 = total_fee_amount - asset_hub_fee_amount; + + let total_fee_asset: Asset = (Location::parent(), total_fee_amount).into(); + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee_amount).into(); + let dest_para_fee_asset: Asset = (Location::parent(), dest_fee_amount).into(); + + let weth_asset_id = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH.into() }, + ], + ); + let weth_asset = Asset::from((weth_asset_id.clone(), 1_000_000)); + + let xcm_worst_case = Xcm(vec![ + ReceiveTeleportedAsset(total_fee_asset.into()), + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(80).into()), + UniversalOrigin(GlobalConsensus(Ethereum { chain_id: CHAIN_ID })), + ReserveAssetDeposited(weth_asset.clone().into()), + ClearOrigin, + DepositReserveAsset { + assets: Definite(vec![dest_para_fee_asset.clone(), weth_asset.clone()].into()), + dest: Location::new(1, [Parachain(2000)]), + xcm: vec![ + BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, + DepositAsset { + assets: Definite(weth_asset.into()), + beneficiary: [1; 32].into(), + }, + SetTopic([1; 32]), + ] + .into(), + }, + ]); + + let delivery_cost_worst_case = + >::calculate_send_cost( + xcm_worst_case, + ParaId::from(1000), + ) + .unwrap() + .into_inner(); + // println!("{:?}", delivery_cost_worst_case[0]); + + let send_cost_max: Asset = + (Location::parent(), inbound_send_cost.saturated_into::()).into(); + assert!(send_cost_max.gt(&delivery_cost_worst_case[0])) + }) +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 53a536655408..96aae0e50eb9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -63,6 +63,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub MaxSendCost: Balance = UNITS / 10; } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -90,6 +91,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type AssetTransactor = ::AssetTransactor; + type MaxSendCost = MaxSendCost; } impl snowbridge_pallet_outbound_queue::Config for Runtime { 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 5960ab7b5505..b2952b2f2c65 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 @@ -202,3 +202,10 @@ fn construct_and_apply_extrinsic( let r = Executive::apply_extrinsic(xt); r.unwrap() } + +#[test] +fn fetch_delivery_cost() { + snowbridge_runtime_test_common::fetch_delivery_cost::( + collator_session_keys(), + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 7922d3ed02b1..34b74935b5ef 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -64,6 +64,7 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub MaxSendCost: Balance = UNITS / 10; } impl snowbridge_pallet_inbound_queue::Config for Runtime { @@ -91,6 +92,7 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; type PricingParameters = EthereumSystem; type AssetTransactor = ::AssetTransactor; + type MaxSendCost = MaxSendCost; } impl snowbridge_pallet_outbound_queue::Config for Runtime {