From f40cfd4f6db05793cfc59ab51b127dace97dd10e Mon Sep 17 00:00:00 2001 From: claravanstaden Date: Mon, 2 Dec 2024 21:43:56 +0200 Subject: [PATCH] use weth for execution fees on AH --- .../pallets/inbound-queue-v2/src/api.rs | 6 +- .../pallets/inbound-queue-v2/src/lib.rs | 12 --- .../pallets/inbound-queue-v2/src/mock.rs | 10 +- .../pallets/inbound-queue-v2/src/test.rs | 31 ++---- .../primitives/router/src/inbound/v2.rs | 27 +++--- .../src/tests/snowbridge_v2.rs | 96 +------------------ .../src/bridge_to_ethereum_config.rs | 9 +- 7 files changed, 34 insertions(+), 157 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs index 241d1372f7ea..f54f4a3a0de0 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/api.rs @@ -5,8 +5,8 @@ use crate::{weights::WeightInfo, Config, Error, Junction::AccountId32, Location}; use frame_support::weights::WeightToFee; use snowbridge_router_primitives::inbound::v2::{ConvertMessage, Message}; -use sp_core::{Get, H256}; -use sp_runtime::{DispatchError, Saturating}; +use sp_core::H256; +use sp_runtime::{DispatchError}; use xcm::latest::Xcm; pub fn dry_run(message: Message) -> Result<(Xcm<()>, T::Balance), DispatchError> @@ -21,9 +21,7 @@ where // Calculate fee. Consists of the cost of the "submit" extrinsic as well as the XCM execution // prologue fee (static XCM part of the message that is execution on AH). let weight_fee = T::WeightToFee::weight_to_fee(&T::WeightInfo::submit()); - let xcm_prologue_fee = T::XcmPrologueFee::get(); let fee: u128 = weight_fee - .saturating_add(xcm_prologue_fee) .try_into() .map_err(|_| Error::::InvalidFee)?; diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index 6da0ddcdb673..00273ff107e0 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -52,7 +52,6 @@ use frame_support::{ use frame_system::{ensure_signed, pallet_prelude::*}; use scale_info::TypeInfo; use snowbridge_core::{ - fees::burn_fees, inbound::{Message, VerificationError, Verifier}, sparse_bitmap::SparseBitmap, BasicOperatingMode, @@ -65,7 +64,6 @@ use sp_std::vec; use types::Nonce; pub use weights::WeightInfo; use xcm::prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, *}; -use xcm_executor::traits::TransactAsset; #[cfg(feature = "runtime-benchmarks")] use snowbridge_beacon_primitives::BeaconHeader; @@ -108,14 +106,10 @@ pub mod pallet { type AssetHubParaId: Get; /// Convert a command from Ethereum to an XCM message. type MessageConverter: ConvertMessage; - /// The AH XCM execution fee for the static part of the XCM message. - type XcmPrologueFee: Get>; /// Used to burn fees from the origin account (the relayer), which will be teleported to AH. type Token: Mutate + Inspect; /// Used for the dry run API implementation. type Balance: Balance + From; - /// Used to burn fees. - type AssetTransactor: TransactAsset; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -234,12 +228,6 @@ pub mod pallet { let xcm = Self::do_convert(message, origin_account_location.clone())?; - // Burn the required fees for the static XCM message part - burn_fees::>( - origin_account_location, - T::XcmPrologueFee::get(), - )?; - // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: // T::RewardLeger::deposit(who, envelope.fee.into())?; // a. The submit extrinsic cost on BH diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index b069c352ae8a..69eaa473fff7 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -5,7 +5,7 @@ use super::*; use crate::{self as inbound_queue_v2}; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32}, + traits::ConstU32, weights::IdentityFee, }; use hex_literal::hex; @@ -104,6 +104,7 @@ impl Verifier for MockVerifier { } const GATEWAY_ADDRESS: [u8; 20] = hex!["eda338e4dc46038493b885327842fd3e301cab39"]; +const WETH_ADDRESS: [u8; 20] = hex!["fff9976782d46cc05630d1f6ebab18b2324d6b14"]; #[cfg(feature = "runtime-benchmarks")] impl BenchmarkHelper for Test { @@ -149,12 +150,11 @@ impl MaybeEquivalence for MockTokenIdConvert { parameter_types! { pub const EthereumNetwork: xcm::v5::NetworkId = xcm::v5::NetworkId::Ethereum { chain_id: 11155111 }; pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS); + pub const WethAddress: H160 = H160(WETH_ADDRESS); pub const InboundQueuePalletInstance: u8 = 80; pub AssetHubLocation: InteriorLocation = Parachain(1000).into(); } -const XCM_PROLOGUE_FEE: u128 = 1_000_000_000_000; - impl inbound_queue_v2::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; @@ -167,12 +167,10 @@ impl inbound_queue_v2::Config for Test { EthereumNetwork, InboundQueuePalletInstance, MockTokenIdConvert, - ConstU128, + WethAddress, >; type Token = Balances; type Balance = u128; - type XcmPrologueFee = ConstU128; - type AssetTransactor = SuccessfulTransactor; #[cfg(feature = "runtime-benchmarks")] type Helper = Test; } diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs index 0b9e49ca43b3..cdaf95e4b267 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -46,27 +46,6 @@ fn test_submit_happy_path() { }); } -#[test] -fn test_submit_xcm_invalid_channel() { - new_tester().execute_with(|| { - let relayer: AccountId = Keyring::Bob.into(); - let origin = RuntimeOrigin::signed(relayer); - - // Submit message - let message = Message { - event_log: mock_event_log_invalid_channel(), - proof: Proof { - receipt_proof: Default::default(), - execution_proof: mock_execution_proof(), - }, - }; - assert_noop!( - InboundQueue::submit(origin.clone(), message.clone()), - Error::::InvalidChannel, - ); - }); -} - #[test] fn test_submit_with_invalid_gateway() { new_tester().execute_with(|| { @@ -151,7 +130,7 @@ fn test_set_operating_mode_root_only() { fn test_send_native_erc20_token_payload() { new_tester().execute_with(|| { // To generate test data: forge test --match-test testSendEther -vvvv - let payload = hex!("29e3b139f4393adda86303fcdaa35f60bb7092bf04005615deb798bb3e4dfa0139dfa1b3d433cc23b72f0000b2d3595bf00600000000000000000000").to_vec(); + let payload = hex!("29e3b139f4393adda86303fcdaa35f60bb7092bf0030ef7dba020000000000000000000004005615deb798bb3e4dfa0139dfa1b3d433cc23b72f0000b2d3595bf00600000000000000000000").to_vec(); let message = MessageV2::decode(&mut payload.as_ref()); assert_ok!(message.clone()); @@ -179,12 +158,13 @@ fn test_send_native_erc20_token_payload() { #[test] fn test_send_foreign_erc20_token_payload() { new_tester().execute_with(|| { - let payload = hex!("29e3b139f4393adda86303fcdaa35f60bb7092bf040197874824853fb4ad04794ccfd1cc8d2a7463839cfcbc6a315a1045c60ab85f400000b2d3595bf00600000000000000000000").to_vec(); + let payload = hex!("29e3b139f4393adda86303fcdaa35f60bb7092bf0030ef7dba0200000000000000000000040197874824853fb4ad04794ccfd1cc8d2a7463839cfcbc6a315a1045c60ab85f400000b2d3595bf00600000000000000000000").to_vec(); let message = MessageV2::decode(&mut payload.as_ref()); assert_ok!(message.clone()); - let inbound_message = message.unwrap(); + let inbound_message = message.unwrap(); + let expected_fee = 3_000_000_000_000u128; let expected_origin: H160 = hex!("29e3b139f4393adda86303fcdaa35f60bb7092bf").into(); let expected_token_id: H256 = hex!("97874824853fb4ad04794ccfd1cc8d2a7463839cfcbc6a315a1045c60ab85f40").into(); let expected_value = 500000000000000000u128; @@ -192,6 +172,7 @@ fn test_send_foreign_erc20_token_payload() { let expected_claimer: Option> = None; assert_eq!(expected_origin, inbound_message.origin); + assert_eq!(expected_fee, inbound_message.fee); assert_eq!(1, inbound_message.assets.len()); if let Asset::ForeignTokenERC20 { token_id, value } = &inbound_message.assets[0] { assert_eq!(expected_token_id, *token_id); @@ -207,7 +188,7 @@ fn test_send_foreign_erc20_token_payload() { #[test] fn test_register_token_inbound_message_with_xcm_and_claimer() { new_tester().execute_with(|| { - let payload = hex!("5991a2df15a8f6a256d3ec51e99254cd3fb576a904005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000300508020401000002286bee0a015029e3b139f4393adda86303fcdaa35f60bb7092bf").to_vec(); + let payload = hex!("5991a2df15a8f6a256d3ec51e99254cd3fb576a90030ef7dba020000000000000000000004005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000300508020401000002286bee0a015029e3b139f4393adda86303fcdaa35f60bb7092bf").to_vec(); let message = MessageV2::decode(&mut payload.as_ref()); assert_ok!(message.clone()); diff --git a/bridges/snowbridge/primitives/router/src/inbound/v2.rs b/bridges/snowbridge/primitives/router/src/inbound/v2.rs index 59ab2ea9ee0a..c9c8edb28921 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v2.rs @@ -11,7 +11,7 @@ use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_runtime::traits::MaybeEquivalence; use sp_std::prelude::*; use xcm::{ - prelude::{Junction::AccountKey20, *}, + prelude::{Asset as XcmAsset, Junction::AccountKey20, *}, MAX_XCM_DECODE_DEPTH, }; @@ -23,6 +23,8 @@ const LOG_TARGET: &str = "snowbridge-router-primitives"; pub struct Message { /// The origin address pub origin: H160, + /// Fee in weth to cover the xcm execution on AH. + pub fee: u128, /// The assets pub assets: Vec, /// The command originating from the Gateway contract @@ -67,24 +69,24 @@ pub trait ConvertMessage { fn convert(message: Message, origin_account: Location) -> Result, ConvertMessageError>; } -pub struct MessageToXcm +pub struct MessageToXcm where EthereumNetwork: Get, InboundQueuePalletInstance: Get, ConvertAssetId: MaybeEquivalence, - XcmPrologueFee: Get, + WethAddress: Get, { _phantom: - PhantomData<(EthereumNetwork, InboundQueuePalletInstance, ConvertAssetId, XcmPrologueFee)>, + PhantomData<(EthereumNetwork, InboundQueuePalletInstance, ConvertAssetId, WethAddress)>, } -impl ConvertMessage - for MessageToXcm +impl ConvertMessage + for MessageToXcm where EthereumNetwork: Get, InboundQueuePalletInstance: Get, ConvertAssetId: MaybeEquivalence, - XcmPrologueFee: Get, + WethAddress: Get, { fn convert( message: Message, @@ -112,13 +114,14 @@ where let network = EthereumNetwork::get(); - let fee_asset = Location::new(1, Here); - let fee: xcm::prelude::Asset = (fee_asset.clone(), XcmPrologueFee::get()).into(); + // use weth as asset + let fee_asset = Location::new(2, [GlobalConsensus(EthereumNetwork::get()), AccountKey20 { network: None, key: WethAddress::get().into() } ]); + let fee: XcmAsset = (fee_asset.clone(), message.fee).into(); let mut instructions = vec![ - ReceiveTeleportedAsset(fee.clone().into()), - PayFees { asset: fee }, DescendOrigin(PalletInstance(InboundQueuePalletInstance::get()).into()), UniversalOrigin(GlobalConsensus(network)), + ReserveAssetDeposited(fee.clone().into()), + PayFees { asset: fee }, ]; for asset in &message.assets { @@ -150,6 +153,7 @@ where // Set the alias origin to the original sender on Ethereum. Important to be before the // arbitrary XCM that is appended to the message on the next line. + // TODO allow address from Ethereum to create foreign assets on AH // instructions.push(AliasOrigin(origin_location.into())); // Add the XCM sent in the message to the end of the xcm instruction @@ -158,7 +162,6 @@ where let appendix = vec![ RefundSurplus, // Refund excess fees to the relayer - // TODO maybe refund all fees to the relayer instead of just DOT? DepositAsset { assets: Wild(AllOf { id: AssetId(fee_asset.into()), fun: WildFungible }), beneficiary: origin_account_location, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index 0f2e0f118ad0..18b48b3dbc7e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -32,7 +32,6 @@ use testnet_parachains_constants::westend::fee::WeightToFee as WeightCalculator; const INITIAL_FUND: u128 = 5_000_000_000_000; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; const WETH: [u8; 20] = hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14"); -const WETH_FEE: u128 = 1_000_000_000_000; pub fn weth_location() -> Location { Location::new( @@ -147,6 +146,7 @@ pub(crate) fn set_up_weth_pool_with_wnd_on_ah_westend(asset: v5::Location) { #[test] fn register_token_v2() { + // Whole register token fee is 374_851_000_000 let relayer = BridgeHubWestendSender::get(); let receiver = AssetHubWestendReceiver::get(); BridgeHubWestend::fund_accounts(vec![(relayer.clone(), INITIAL_FUND)]); @@ -171,8 +171,9 @@ fn register_token_v2() { let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); - let weth_fee: xcm::prelude::Asset = (weth_location(), WETH_FEE).into(); - let weth_transact_fee: xcm::prelude::Asset = (weth_location(), WETH_FEE / 2).into(); + let weth_fee_value: u128 = 1_000_000_000_000; + let weth_fee: xcm::prelude::Asset = (weth_location(), weth_fee_value).into(); + let weth_transact_fee: xcm::prelude::Asset = (weth_location(), weth_fee_value).into(); let asset_id = Location::new( 2, @@ -191,7 +192,6 @@ fn register_token_v2() { // will be deducted from) DepositAsset { assets: dot_fee.into(), beneficiary: bridge_owner.into() }, // Pay for the transact execution - //PayFees { asset: weth_transact_fee.into() }, Transact { origin_kind: OriginKind::Xcm, call: ( @@ -210,6 +210,7 @@ fn register_token_v2() { let message = Message { origin, + fee: 1_500_000_000_000u128, assets, xcm: versioned_message_xcm.encode(), claimer: Some(claimer_bytes), @@ -233,93 +234,6 @@ fn register_token_v2() { }); } -#[test] -fn xcm_prologue_fee() { - BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id().into(), INITIAL_FUND); - - let asset_hub_sovereign = BridgeHubWestend::sovereign_account_id_of(Location::new( - 1, - [Parachain(AssetHubWestend::para_id().into())], - )); - - let relayer = BridgeHubWestendSender::get(); - let receiver = AssetHubWestendReceiver::get(); - BridgeHubWestend::fund_accounts(vec![(relayer.clone(), INITIAL_FUND)]); - - let mut token_ids = Vec::new(); - for _ in 0..8 { - token_ids.push(H160::random()); - } - - let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); - - AssetHubWestend::execute_with(|| { - type RuntimeOrigin = ::RuntimeOrigin; - - for token_id in token_ids.iter() { - let token_id = *token_id; - - let asset_location = Location::new( - 2, - [ - GlobalConsensus(ethereum_network_v5), - AccountKey20 { network: None, key: token_id.into() }, - ], - ); - - assert_ok!(::ForeignAssets::force_create( - RuntimeOrigin::root(), - asset_location.clone(), - asset_hub_sovereign.clone().into(), - false, - 1, - )); - - assert!(::ForeignAssets::asset_exists( - asset_location.clone().try_into().unwrap(), - )); - } - }); - - let native_tokens: Vec = token_ids - .iter() - .map(|token_id| NativeTokenERC20 { token_id: *token_id, value: 3_000_000_000 }) - .collect(); - - BridgeHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - let claimer = AccountId32 { network: None, id: receiver.clone().into() }; - let claimer_bytes = claimer.encode(); - let origin = H160::random(); - let relayer_location = - Location::new(0, AccountId32 { network: None, id: relayer.clone().into() }); - - let message_xcm_instructions = - vec![DepositAsset { assets: Wild(AllCounted(8)), beneficiary: receiver.into() }]; - let message_xcm: Xcm<()> = message_xcm_instructions.into(); - let versioned_message_xcm = VersionedXcm::V5(message_xcm); - - let message = Message { - origin, - assets: native_tokens, - xcm: versioned_message_xcm.encode(), - claimer: Some(claimer_bytes), - }; - let xcm = EthereumInboundQueueV2::do_convert(message, relayer_location).unwrap(); - let _ = EthereumInboundQueueV2::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); - - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] - ); - }); - - let execution_fee = WeightCalculator::weight_to_fee(&Weight::from_parts(1410450000, 33826)); - let buffered_fee = execution_fee * 2; - println!("buffered execution fee for prologue for 8 assets: {}", buffered_fee); -} - #[test] fn register_token_xcm() { BridgeHubWestend::execute_with(|| { 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 4d236b801384..e998909fd2da 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 @@ -71,12 +71,9 @@ parameter_types! { }; pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); + pub WethAddress: H160 = H160(hex_literal::hex!("fff9976782d46cc05630d1f6ebab18b2324d6b14")); } -/// The XCM execution fee on AH for the static part of the XCM message (not the user provided -/// xcm). Teleported from BH. Calculated with integration test snowbridge_v2::xcm_prologue_fee. -const XCM_PROLOGUE_FEE: u128 = 67_652_000_000; - impl snowbridge_pallet_inbound_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; @@ -122,13 +119,11 @@ impl snowbridge_pallet_inbound_queue_v2::Config for Runtime { type AssetHubParaId = ConstU32<1000>; type Token = Balances; type Balance = Balance; - type XcmPrologueFee = ConstU128; - type AssetTransactor = ::AssetTransactor; type MessageConverter = snowbridge_router_primitives::inbound::v2::MessageToXcm< EthereumNetwork, ConstU8, EthereumSystem, - ConstU128, + WethAddress, >; }