From 41bed52f512ef61ba1b9e0a75c1aaf1ca057b330 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 12 Dec 2024 01:56:40 +0200 Subject: [PATCH 01/10] convert interpret as ETH for null address --- .../primitives/router/src/inbound/mock.rs | 48 ++++++++++ .../primitives/router/src/inbound/mod.rs | 14 ++- .../primitives/router/src/inbound/tests.rs | 88 ++++++++++++++++--- 3 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 bridges/snowbridge/primitives/router/src/inbound/mock.rs diff --git a/bridges/snowbridge/primitives/router/src/inbound/mock.rs b/bridges/snowbridge/primitives/router/src/inbound/mock.rs new file mode 100644 index 000000000000..537853b324f6 --- /dev/null +++ b/bridges/snowbridge/primitives/router/src/inbound/mock.rs @@ -0,0 +1,48 @@ +use crate::inbound::{MessageToXcm, TokenId}; +use frame_support::parameter_types; +use sp_runtime::{ + traits::{IdentifyAccount, MaybeEquivalence, Verify}, + MultiSignature, +}; +use xcm::{latest::WESTEND_GENESIS_HASH, prelude::*}; + +pub const CHAIN_ID: u64 = 11155111; +pub const NETWORK: NetworkId = Ethereum { chain_id: CHAIN_ID }; + +parameter_types! { + pub EthereumNetwork: NetworkId = NETWORK; + + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetExecutionFee: u128 = 2_000_000_000; + pub const CreateAssetDeposit: u128 = 100_000_000_000; + pub const SendTokenExecutionFee: u128 = 1_000_000_000; + pub const InboundQueuePalletInstance: u8 = 80; + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1002)].into(); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)),Parachain(1000)]); +} + +type Signature = MultiSignature; +type AccountId = <::Signer as IdentifyAccount>::AccountId; +type Balance = u128; + +pub(crate) struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::parent()) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + +pub(crate) type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + MockTokenIdConvert, + UniversalLocation, + AssetHubFromEthereum, +>; diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index bc5d401cd4f7..bd0fbfa2178c 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -2,6 +2,8 @@ // SPDX-FileCopyrightText: 2023 Snowfork //! Converts messages from Ethereum to XCM messages +#[cfg(test)] +mod mock; #[cfg(test)] mod tests; @@ -394,10 +396,14 @@ where // Convert ERC20 token address to a location that can be understood by Assets Hub. fn convert_token_address(network: NetworkId, token: H160) -> Location { - Location::new( - 2, - [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], - ) + if token == H160([0; 20]) { + Location::new(2, [GlobalConsensus(network)]) + } else { + Location::new( + 2, + [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], + ) + } } /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 786aa594f653..11d7928602c6 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -1,21 +1,12 @@ use super::EthereumLocationsConverterFor; -use crate::inbound::CallIndex; -use frame_support::{assert_ok, parameter_types}; +use crate::inbound::{ + mock::*, Command, ConvertMessage, Destination, MessageV1, VersionedMessage, H160, +}; +use frame_support::assert_ok; use hex_literal::hex; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; -const NETWORK: NetworkId = Ethereum { chain_id: 11155111 }; - -parameter_types! { - pub EthereumNetwork: NetworkId = NETWORK; - - pub const CreateAssetCall: CallIndex = [1, 1]; - pub const CreateAssetExecutionFee: u128 = 123; - pub const CreateAssetDeposit: u128 = 891; - pub const SendTokenExecutionFee: u128 = 592; -} - #[test] fn test_ethereum_network_converts_successfully() { let expected_account: [u8; 32] = @@ -81,3 +72,74 @@ fn test_reanchor_all_assets() { assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); } } + +#[test] +fn test_convert_send_token_with_weth() { + const WETH: H160 = H160([0xff; 20]); + const AMOUNT: u128 = 1_000_000; + const FEE: u128 = 1_000; + const ACCOUNT_ID: [u8; 32] = [0xBA; 32]; + const MESSAGE: VersionedMessage = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: WETH, + destination: Destination::AccountId32 { id: ACCOUNT_ID }, + amount: AMOUNT, + fee: FEE, + }, + }); + let result = MessageConverter::convert([1; 32].into(), MESSAGE); + assert_ok!(&result); + let (xcm, fee) = result.unwrap(); + assert_eq!(FEE, fee); + + let expected_assets = ReserveAssetDeposited( + vec![Asset { + id: AssetId(Location { + parents: 2, + interior: Junctions::X2( + [GlobalConsensus(NETWORK), AccountKey20 { network: None, key: WETH.into() }] + .into(), + ), + }), + fun: Fungible(AMOUNT), + }] + .into(), + ); + let actual_assets = xcm.into_iter().find(|x| matches!(x, ReserveAssetDeposited(..))); + assert_eq!(actual_assets, Some(expected_assets)) +} + +#[test] +fn test_convert_send_token_with_eth() { + const ETH: H160 = H160([0x00; 20]); + const AMOUNT: u128 = 1_000_000; + const FEE: u128 = 1_000; + const ACCOUNT_ID: [u8; 32] = [0xBA; 32]; + const MESSAGE: VersionedMessage = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendToken { + token: ETH, + destination: Destination::AccountId32 { id: ACCOUNT_ID }, + amount: AMOUNT, + fee: FEE, + }, + }); + let result = MessageConverter::convert([1; 32].into(), MESSAGE); + assert_ok!(&result); + let (xcm, fee) = result.unwrap(); + assert_eq!(FEE, fee); + + let expected_assets = ReserveAssetDeposited( + vec![Asset { + id: AssetId(Location { + parents: 2, + interior: Junctions::X1([GlobalConsensus(NETWORK)].into()), + }), + fun: Fungible(AMOUNT), + }] + .into(), + ); + let actual_assets = xcm.into_iter().find(|x| matches!(x, ReserveAssetDeposited(..))); + assert_eq!(actual_assets, Some(expected_assets)) +} From dd51d232192a6e6a1f0c2488e7f1da99dcab52e5 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 12 Dec 2024 02:06:51 +0200 Subject: [PATCH 02/10] support native eth in outbound queue --- .../primitives/router/src/outbound/mod.rs | 1 + .../primitives/router/src/outbound/tests.rs | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 3b5dbdb77c89..03a16eb35e79 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -291,6 +291,7 @@ where match inner_location.unpack() { (0, [AccountKey20 { network, key }]) if self.network_matches(network) => Some((H160(*key), *amount)), + (0, []) => Some((H160([0; 20]), *amount)), _ => None, }, _ => None, diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 44f81ce31b3a..d713de324db1 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -515,6 +515,41 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { assert_eq!(result, Ok((expected_payload, [0; 32]))); } +#[test] +fn xcm_converter_convert_with_native_eth_succeeds() { + let network = BridgedNetwork::get(); + + let token_address: [u8; 20] = [0; 20]; + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let assets: Assets = vec![Asset { id: AssetId([].into()), fun: Fungible(1000) }].into(); + let filter: AssetFilter = Wild(All); + + let message: Xcm<()> = vec![ + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, + }; + let result = converter.convert(); + assert_eq!(result, Ok((expected_payload, [0; 32]))); +} + #[test] fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { let network = BridgedNetwork::get(); From e5a6eb7579af5e47d9c0a0cd4f90ead571e6f65e Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 21:49:26 +0200 Subject: [PATCH 03/10] add pr doc --- bridges/snowbridge/primitives/router/Cargo.toml | 2 +- prdoc/pr_6855.prdoc | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 prdoc/pr_6855.prdoc diff --git a/bridges/snowbridge/primitives/router/Cargo.toml b/bridges/snowbridge/primitives/router/Cargo.toml index 1f7f489c6b18..62c8eb4f8497 100644 --- a/bridges/snowbridge/primitives/router/Cargo.toml +++ b/bridges/snowbridge/primitives/router/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-router-primitives" description = "Snowbridge Router Primitives" -version = "0.9.0" +version = "0.10.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/prdoc/pr_6855.prdoc b/prdoc/pr_6855.prdoc new file mode 100644 index 000000000000..12683d14856b --- /dev/null +++ b/prdoc/pr_6855.prdoc @@ -0,0 +1,14 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Snowbridge: Support Native ETH + +doc: + - audience: Runtime User + description: + Support Native ETH as an asset type instead of only supporting WETH. WETH is still supported, but adds ETH + support for ETH in the inbound and outbound routers. + +crates: + - name: snowbridge-router-primitives + bump: minor From 0d6fbd4feaa8f1debe7e2e38b5b9cdda3c32ebbb Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 21:52:12 +0200 Subject: [PATCH 04/10] fix pr doc --- Cargo.lock | 20 ++++++++++---------- prdoc/pr_6855.prdoc | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98f6873f7b3f..a8856ebe3db9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1008,7 +1008,7 @@ dependencies = [ "rococo-runtime-constants 7.0.0", "scale-info", "serde_json", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-api 26.0.0", "sp-block-builder 26.0.0", "sp-consensus-aura 0.32.0", @@ -1145,7 +1145,7 @@ dependencies = [ "primitive-types 0.13.1", "scale-info", "serde_json", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-api 26.0.0", "sp-block-builder 26.0.0", "sp-consensus-aura 0.32.0", @@ -2632,7 +2632,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures 0.10.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-core 28.0.0", "sp-runtime 31.0.1", "staging-xcm 7.0.0", @@ -2716,7 +2716,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue 0.2.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "snowbridge-runtime-common 0.2.0", "snowbridge-runtime-test-common 0.2.0", "snowbridge-system-runtime-api 0.2.0", @@ -2873,7 +2873,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures 0.10.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-core 28.0.0", "sp-runtime 31.0.1", "staging-xcm 7.0.0", @@ -2954,7 +2954,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue 0.2.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "snowbridge-runtime-common 0.2.0", "snowbridge-runtime-test-common 0.2.0", "snowbridge-system-runtime-api 0.2.0", @@ -16704,7 +16704,7 @@ dependencies = [ "primitive-types 0.12.2", "scale-info", "smallvec", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-api 26.0.0", "sp-block-builder 26.0.0", "sp-consensus-aura 0.32.0", @@ -18932,7 +18932,7 @@ dependencies = [ "snowbridge-pallet-inbound-queue-fixtures 0.10.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "snowbridge-runtime-common 0.2.0", "snowbridge-runtime-test-common 0.2.0", "snowbridge-system-runtime-api 0.2.0", @@ -25246,7 +25246,7 @@ dependencies = [ "snowbridge-core 0.2.0", "snowbridge-pallet-ethereum-client 0.2.0", "snowbridge-pallet-inbound-queue-fixtures 0.10.0", - "snowbridge-router-primitives 0.9.0", + "snowbridge-router-primitives 0.10.0", "sp-core 28.0.0", "sp-io 30.0.0", "sp-keyring 31.0.0", @@ -25403,7 +25403,7 @@ dependencies = [ [[package]] name = "snowbridge-router-primitives" -version = "0.9.0" +version = "0.10.0" dependencies = [ "frame-support 28.0.0", "hex-literal", diff --git a/prdoc/pr_6855.prdoc b/prdoc/pr_6855.prdoc index 12683d14856b..3e2115f44201 100644 --- a/prdoc/pr_6855.prdoc +++ b/prdoc/pr_6855.prdoc @@ -1,7 +1,7 @@ # Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 # See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json -title: Snowbridge: Support Native ETH +title: Snowbridge - Support Native ETH doc: - audience: Runtime User From 7c838e5bd81e35204d64b77d4371300a1fa6f665 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 22:24:08 +0200 Subject: [PATCH 05/10] docs, comments and renames --- .../primitives/router/src/outbound/tests.rs | 9 ++++++-- .../bridge-hub-rococo/src/tests/snowbridge.rs | 22 +++++++++---------- prdoc/pr_6855.prdoc | 4 ++-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index d713de324db1..2a60f9f3e0ea 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -519,9 +519,9 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { fn xcm_converter_convert_with_native_eth_succeeds() { let network = BridgedNetwork::get(); - let token_address: [u8; 20] = [0; 20]; let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + // The asset is `{ parents: 0, interior: X1(Here) }` relative to ethereum. let assets: Assets = vec![Asset { id: AssetId([].into()), fun: Fungible(1000) }].into(); let filter: AssetFilter = Wild(All); @@ -536,12 +536,17 @@ fn xcm_converter_convert_with_native_eth_succeeds() { SetTopic([0; 32]), ] .into(); + let mut converter = XcmConverter::::new(&message, network, Default::default()); + + // The token address that is expected to be sent should be + // `0x0000000000000000000000000000000000000000`. The solidity will + // interpret this as a transfer of ETH. let expected_payload = Command::AgentExecute { agent_id: Default::default(), command: AgentExecuteCommand::TransferToken { - token: token_address.into(), + token: H160([0; 20]), recipient: beneficiary_address.into(), amount: 1000, }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index c72d5045ddc0..e0beac5f9eb6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -238,7 +238,7 @@ fn register_weth_token_from_ethereum_to_asset_hub() { /// Tests the registering of a token as an asset on AssetHub, and then subsequently sending /// a token from Ethereum to AssetHub. #[test] -fn send_token_from_ethereum_to_asset_hub() { +fn send_weth_from_ethereum_to_asset_hub() { BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id().into(), INITIAL_FUND); // Fund ethereum sovereign on AssetHub @@ -278,7 +278,7 @@ fn send_token_from_ethereum_to_asset_hub() { /// Tests sending a token to a 3rd party parachain, called PenPal. The token reserve is /// still located on AssetHub. #[test] -fn send_token_from_ethereum_to_penpal() { +fn send_weth_from_ethereum_to_penpal() { let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new( 1, [Parachain(AssetHubRococo::para_id().into())], @@ -565,7 +565,7 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { }); } -fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) { +fn send_weth_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) { let ethereum_network_v5: NetworkId = EthereumNetwork::get().into(); let weth_asset_location: Location = Location::new(2, [ethereum_network_v5.into(), AccountKey20 { network: None, key: WETH }]); @@ -623,8 +623,8 @@ fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u12 } #[test] -fn send_token_from_ethereum_to_existent_account_on_asset_hub() { - send_token_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE); +fn send_weth_from_ethereum_to_existent_account_on_asset_hub() { + send_weth_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -640,8 +640,8 @@ fn send_token_from_ethereum_to_existent_account_on_asset_hub() { } #[test] -fn send_token_from_ethereum_to_non_existent_account_on_asset_hub() { - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE); +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub() { + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -657,8 +657,8 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub() { } #[test] -fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() { - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE); +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() { + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -675,10 +675,10 @@ fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficie } #[test] -fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( +fn send_weth_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed( ) { // On AH the xcm fee is 26_789_690 and the ED is 3_300_000 - send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); + send_weth_from_ethereum_to_asset_hub_with_fee([1; 32], 30_000_000); AssetHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; diff --git a/prdoc/pr_6855.prdoc b/prdoc/pr_6855.prdoc index 3e2115f44201..515ac841b869 100644 --- a/prdoc/pr_6855.prdoc +++ b/prdoc/pr_6855.prdoc @@ -1,12 +1,12 @@ # Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 # See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json -title: Snowbridge - Support Native ETH +title: Snowbridge - Support bridging native ETH doc: - audience: Runtime User description: - Support Native ETH as an asset type instead of only supporting WETH. WETH is still supported, but adds ETH + Support Native ETH as an asset type instead of only supporting WETH. WETH is still supported, but adds support for ETH in the inbound and outbound routers. crates: From a98d3e76d46dcb58e8d5361fe8c10f608afcc920 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 20 Dec 2024 18:00:57 +0200 Subject: [PATCH 06/10] add fixture data --- .../fixtures/src/send_native_eth.rs | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs new file mode 100755 index 000000000000..5a96ecfc211c --- /dev/null +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use hex_literal::hex; +use snowbridge_beacon_primitives::{ +types::deneb, AncestryProof, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader, +}; +use snowbridge_core::inbound::{InboundQueueFixture, Log, Message, Proof}; +use sp_core::U256; +use sp_std::vec; + +pub fn make_send_native_eth_message() -> InboundQueueFixture { + InboundQueueFixture { + message: Message { + event_log: Log { + address: hex!("87d1f7fdfee7f651fabc8bfcb6e086c278b77a7d").into(), + topics: vec![ + hex!("7153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84f").into(), + hex!("c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539").into(), + hex!("5f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0").into(), + ], + data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f00a736aa0000000000010000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e8764817000000000000000000000000").into(), + }, + proof: Proof { + block_hash: hex!("").into(), + tx_index: , + receipt_proof: (vec![ + hex!("17cd4d05dde30703008a4f213205923630cff8e6bc9d5d95a52716bfb5551fd7").to_vec(), + ], vec![ + hex!("f903b4822080b903ae02f903aa018301a7fcb9010000000000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000080000000000020000000000000000000800010100000000000000000000000000000000000200000000000000000000000000001000000040080008000000000000000000040000000021000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000200000000000000f9029ff9015d9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df884a024c5d2de620c6e25186ae16f6919eba93b6e2c1a33857cc419d9f3a00d6967e9a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000090a987b944cb1dcce5564e5fdecd7a54d3de27fea000000000000000000000000000000000000000000000000000000000000003e8b8c000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000208eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48f9013c9487d1f7fdfee7f651fabc8bfcb6e086c278b77a7df863a07153f9357c8ea496bba60bf82e67143e27b64462b49041f8e689e1b05728f84fa0c173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539a05f7060e971b0dc81e63f0aa41831091847d97c1a4693ac450cc128c7214e65e0b8c000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f00a736aa0000000000010000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e8764817000000000000000000000000").to_vec(), + ]), + execution_proof: ExecutionProof { + header: BeaconHeader { + slot: 380, + proposer_index: 7, + parent_root: hex!("d182a88e47c6bc1f464932ba2b4c4b788323b072803e55512a2cf2b86faae937").into(), + state_root: hex!("6081e032097ef589526e746f95f58f473c11c97006ecd79387eb28031e034503").into(), + body_root: hex!("9a41fdf298fddf122a2df85ec57bd9fe47056f822b1a3cc49e250b5d4dc16bf3").into(), + }, + ancestry_proof: Some(AncestryProof { + header_branch: vec![ + hex!("9898c57506072a548cdb850bd1221ee8d9cf9d360935f48dd885cdcfba08fcb5").into(), + hex!("f4e1452d4f5bd001da128001f82c5be8f5169572f859a16e7ffe8dd4e5664621").into(), + hex!("0d7a2309d4f4a6e10ccd5eea81f60063d1a6fe3b0e4ecc7ada8c6608a0d3b74d").into(), + hex!("603650056fe7fb545650069af07c2b1b105703212b3f39116f51fed4fcec3340").into(), + hex!("2825ae5d9dba9222678fd1c373c9bdb55863a2e255f96b8f7b53606ca5f3eb6a").into(), + hex!("8885d60a7fd15dff37d71134afb462ba8b7c80f357e12cbc71d5c294ee5d5eb3").into(), + hex!("9abd6358b9f31637f5e0141e9595b925990a8b06a295879c4cb0b59167994533").into(), + hex!("9ecdc48631af6a1113e6501a628a0024688eca83e09a5b21fbb079239818f3b8").into(), + hex!("70acaaddde04e8d9f1ba8dd619796b3f0490d81cfd7ffad787629dfd25f4a9f2").into(), + hex!("a957f01020ea38b84734c8db6eb317f32e273cd8cc6c02a8e6bd355a8c07b740").into(), + hex!("a548b0d8101da45c8c282ebe2996b4a4d99ef7a57ea83f551614df1d5da75341").into(), + hex!("df4a4c32b083708e19d8bc54d5454550ca81428cbf1cb3679af04e4d3a67580f").into(), + hex!("b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f").into(), + ], + finalized_block_root: hex!("e77b95404d59273547bc5658f8b8a48fbd41f18ba4055d032943169819247678").into(), + }), + execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { + parent_hash: hex!("03db431c4f2051c50fdbc3139176fee0d48660542fc919a048785f60272f5b32").into(), + fee_recipient: hex!("0000000000000000000000000000000000000000").into(), + state_root: hex!("a1c9de4075c97619aa832ba482359d5f4ee84c352be10149c503e2fa8796abbb").into(), + receipts_root: hex!("17cd4d05dde30703008a4f213205923630cff8e6bc9d5d95a52716bfb5551fd7").into(), + logs_bloom: hex!("00000000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000080000000000020000000000000000000800010100000000000000000000000000000000000200000000000000000000000000001000000040080008000000000000000000040000000021000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000200000000000000").into(), + prev_randao: hex!("ef05ef41eee23d0e3a4d645acfab9ce655329c6822ffddd676fa7a50ca99d6cf").into(), + block_number: 380, + gas_limit: 55188803, + gas_used: 108540, + timestamp: 1734691273, + extra_data: hex!("d983010e08846765746888676f312e32322e358664617277696e").into(), + base_fee_per_gas: U256::from(7u64), + block_hash: hex!("363f901a301715c650ff850d3fa00dba87b11e7c293f69eaa47faf8033eec9f7").into(), + transactions_root: hex!("909139b3137666b4551b629ce6d9fb7e5e6f6def8a48d078448ec6600fe63c7f").into(), + withdrawals_root: hex!("792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535").into(), + blob_gas_used: 0, + excess_blob_gas: 0, + }), + execution_branch: vec![ + hex!("07d0ab1abd8bbdb7f11ccb74448522a60b12d8ad33226e744de8a1bdfffe661f").into(), + hex!("b46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bb").into(), + hex!("db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71").into(), + hex!("130334e22c80c742e9d921554f0f1e98eff6e1534b403c6820b55f5995344157").into(), + ], + } + }, + }, + finalized_header: BeaconHeader { + slot: 2560, + proposer_index: 5, + parent_root: hex!("1f3db43bd898476297221188c6b894174087aa28092071bbcd99e4b7343ffa1a").into(), + state_root: hex!("e304226a3a8ee48daac2919074bb64cb05d83bd65a4ee5850a63287706d9916c").into(), + body_root: hex!("abc8c9b1bc6deba6e73595deaf11702ebaf275c3aff52de83c6ad7cc94865eaf").into(), + }, + block_roots_root: hex!("b80debd862759ac203178cabc2a26ce69fff085add19419f9bef85da2645ec64").into(), + } +} From f1747197e5c852b5c64427f7dcbdc497bd1512d6 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 20 Dec 2024 20:21:56 +0200 Subject: [PATCH 07/10] fix fixtures --- .../pallets/inbound-queue/fixtures/src/lib.rs | 1 + .../fixtures/src/send_native_eth.rs | 66 +++++++++---------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs index 00adcdfa186a..6f429f19f70c 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs @@ -4,4 +4,5 @@ pub mod register_token; pub mod send_token; +pub mod send_native_eth; pub mod send_token_to_penpal; diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs index 5a96ecfc211c..23b670168143 100755 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs @@ -24,8 +24,6 @@ pub fn make_send_native_eth_message() -> InboundQueueFixture { data: hex!("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005f00a736aa0000000000010000000000000000000000000000000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000064a7b3b6e00d000000000000000000e8764817000000000000000000000000").into(), }, proof: Proof { - block_hash: hex!("").into(), - tx_index: , receipt_proof: (vec![ hex!("17cd4d05dde30703008a4f213205923630cff8e6bc9d5d95a52716bfb5551fd7").to_vec(), ], vec![ @@ -33,65 +31,65 @@ pub fn make_send_native_eth_message() -> InboundQueueFixture { ]), execution_proof: ExecutionProof { header: BeaconHeader { - slot: 380, + slot: 246, proposer_index: 7, - parent_root: hex!("d182a88e47c6bc1f464932ba2b4c4b788323b072803e55512a2cf2b86faae937").into(), - state_root: hex!("6081e032097ef589526e746f95f58f473c11c97006ecd79387eb28031e034503").into(), - body_root: hex!("9a41fdf298fddf122a2df85ec57bd9fe47056f822b1a3cc49e250b5d4dc16bf3").into(), + parent_root: hex!("4faaac5d2fa0b8884fe1175c7cac1c92aac9eba5a20b4302edb98a56428c5974").into(), + state_root: hex!("882c13f1d56df781e3444a78cae565bfa1c89822c86cdb0daea71f5351231580").into(), + body_root: hex!("c47eb72204b1ca567396dacef8b0214027eb7f0789330b55166085d1f9cb4c65").into(), }, ancestry_proof: Some(AncestryProof { header_branch: vec![ - hex!("9898c57506072a548cdb850bd1221ee8d9cf9d360935f48dd885cdcfba08fcb5").into(), - hex!("f4e1452d4f5bd001da128001f82c5be8f5169572f859a16e7ffe8dd4e5664621").into(), - hex!("0d7a2309d4f4a6e10ccd5eea81f60063d1a6fe3b0e4ecc7ada8c6608a0d3b74d").into(), - hex!("603650056fe7fb545650069af07c2b1b105703212b3f39116f51fed4fcec3340").into(), - hex!("2825ae5d9dba9222678fd1c373c9bdb55863a2e255f96b8f7b53606ca5f3eb6a").into(), - hex!("8885d60a7fd15dff37d71134afb462ba8b7c80f357e12cbc71d5c294ee5d5eb3").into(), - hex!("9abd6358b9f31637f5e0141e9595b925990a8b06a295879c4cb0b59167994533").into(), - hex!("9ecdc48631af6a1113e6501a628a0024688eca83e09a5b21fbb079239818f3b8").into(), - hex!("70acaaddde04e8d9f1ba8dd619796b3f0490d81cfd7ffad787629dfd25f4a9f2").into(), - hex!("a957f01020ea38b84734c8db6eb317f32e273cd8cc6c02a8e6bd355a8c07b740").into(), - hex!("a548b0d8101da45c8c282ebe2996b4a4d99ef7a57ea83f551614df1d5da75341").into(), - hex!("df4a4c32b083708e19d8bc54d5454550ca81428cbf1cb3679af04e4d3a67580f").into(), + hex!("38e2454bc93c4cfafcea772b8531e4802bbd2561366620699096dd4e591bc488").into(), + hex!("3d7389fb144ccaeca8b8e1667ce1d1538dfceb50bf1e49c4b368a223f051fda3").into(), + hex!("0d49c9c24137ad4d86ebca2f36a159573a68b5d5d60e317776c77cc8b6093034").into(), + hex!("0fadc6735bcdc2793a5039a806fbf39984c39374ed4d272c1147e1c23df88983").into(), + hex!("3a058ad4b169eebb4c754c8488d41e56a7a0e5f8b55b5ec67452a8d326585c69").into(), + hex!("de200426caa9bc03f8e0033b4ef4df1db6501924b5c10fb7867e76db942b903c").into(), + hex!("48b578632bc40eebb517501f179ffdd06d762c03e9383df16fc651eeddd18806").into(), + hex!("98d9d6904b2a6a285db4c4ae59a07100cd38ec4d9fb7a16a10fe83ec99e6ba1d").into(), + hex!("1b2bbae6e684864b714654a60778664e63ba6c3c9bed8074ec1a0380fe5042e6").into(), + hex!("eb907a888eadf5a7e2bd0a3a5a9369e409c7aa688bd4cde758d5b608c6c82785").into(), + hex!("ffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b").into(), + hex!("6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220").into(), hex!("b7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f").into(), ], - finalized_block_root: hex!("e77b95404d59273547bc5658f8b8a48fbd41f18ba4055d032943169819247678").into(), + finalized_block_root: hex!("440615588532ce496a93d189cb0ef1df7cf67d529faee0fd03213ce26ea115e5").into(), }), execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { - parent_hash: hex!("03db431c4f2051c50fdbc3139176fee0d48660542fc919a048785f60272f5b32").into(), + parent_hash: hex!("a8c89213b7d7d2ac76462d89e6a7384374db905b657ad803d3c86f88f86c39df").into(), fee_recipient: hex!("0000000000000000000000000000000000000000").into(), - state_root: hex!("a1c9de4075c97619aa832ba482359d5f4ee84c352be10149c503e2fa8796abbb").into(), + state_root: hex!("a1e8175213a6a43da17fae65109245867cbc60e3ada16b8ac28c6b208761c772").into(), receipts_root: hex!("17cd4d05dde30703008a4f213205923630cff8e6bc9d5d95a52716bfb5551fd7").into(), logs_bloom: hex!("00000000000000000000000020000000000000000000004000000000000000000400000000000000000000001000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000080000000000020000000000000000000800010100000000000000000000000000000000000200000000000000000000000000001000000040080008000000000000000000040000000021000000002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000200000000000000").into(), - prev_randao: hex!("ef05ef41eee23d0e3a4d645acfab9ce655329c6822ffddd676fa7a50ca99d6cf").into(), - block_number: 380, - gas_limit: 55188803, + prev_randao: hex!("b9b26dc14ea8c57d069fde0c94ad31c2558365c3986a0c06558470f8c02e62ce").into(), + block_number: 246, + gas_limit: 62908420, gas_used: 108540, - timestamp: 1734691273, + timestamp: 1734718384, extra_data: hex!("d983010e08846765746888676f312e32322e358664617277696e").into(), base_fee_per_gas: U256::from(7u64), - block_hash: hex!("363f901a301715c650ff850d3fa00dba87b11e7c293f69eaa47faf8033eec9f7").into(), + block_hash: hex!("878195e2ea83c74d475363d03d41a7fbfc4026d6e5bcffb713928253984a64a7").into(), transactions_root: hex!("909139b3137666b4551b629ce6d9fb7e5e6f6def8a48d078448ec6600fe63c7f").into(), withdrawals_root: hex!("792930bbd5baac43bcc798ee49aa8185ef76bb3b44ba62b91d86ae569e4bb535").into(), blob_gas_used: 0, excess_blob_gas: 0, }), execution_branch: vec![ - hex!("07d0ab1abd8bbdb7f11ccb74448522a60b12d8ad33226e744de8a1bdfffe661f").into(), + hex!("5d78e26ea639df17c2194ff925f782b9522009d58cfc60e3d34ba79a19f8faf1").into(), hex!("b46f0c01805fe212e15907981b757e6c496b0cb06664224655613dcec82505bb").into(), hex!("db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71").into(), - hex!("130334e22c80c742e9d921554f0f1e98eff6e1534b403c6820b55f5995344157").into(), + hex!("3d84b2809a36450186e5169995a5e3cab55d751aee90fd8456b33d871ccaa463").into(), ], } }, }, finalized_header: BeaconHeader { - slot: 2560, - proposer_index: 5, - parent_root: hex!("1f3db43bd898476297221188c6b894174087aa28092071bbcd99e4b7343ffa1a").into(), - state_root: hex!("e304226a3a8ee48daac2919074bb64cb05d83bd65a4ee5850a63287706d9916c").into(), - body_root: hex!("abc8c9b1bc6deba6e73595deaf11702ebaf275c3aff52de83c6ad7cc94865eaf").into(), + slot: 608, + proposer_index: 3, + parent_root: hex!("f10c2349530dbd339a72886270e2e304bb68155af68c918c850acd9ab341350f").into(), + state_root: hex!("6df0ef4cbb4986a84ff0763727402b88636e6b5535022cd3ad6967b8dd799402").into(), + body_root: hex!("f66fc1c022f07f91c777ad5c464625fc0b43d3e7a45650567dce60011210f574").into(), }, - block_roots_root: hex!("b80debd862759ac203178cabc2a26ce69fff085add19419f9bef85da2645ec64").into(), + block_roots_root: hex!("1c0dbf54db070770f5e573b72afe0aac2b0e3cf312107d1cd73bf64d7a2ed90c").into(), } } From e1a6f18505cffe7248a7b03b579794da83e4ac2b Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Sat, 21 Dec 2024 23:48:20 +0200 Subject: [PATCH 08/10] add round trip test --- .../bridges/bridge-hub-rococo/src/lib.rs | 3 +- .../bridges/bridge-hub-rococo/src/lib.rs | 1 + .../bridge-hub-rococo/src/tests/snowbridge.rs | 174 +++++++++++++++++- 3 files changed, 175 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/lib.rs index 5ef0993f70a1..43398eb8bd48 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/lib.rs @@ -16,7 +16,8 @@ pub mod genesis; pub use bridge_hub_rococo_runtime::{ - xcm_config::XcmConfig as BridgeHubRococoXcmConfig, EthereumBeaconClient, EthereumInboundQueue, + self as bridge_hub_rococo_runtime, xcm_config::XcmConfig as BridgeHubRococoXcmConfig, + EthereumBeaconClient, EthereumInboundQueue, ExistentialDeposit as BridgeHubRococoExistentialDeposit, RuntimeOrigin as BridgeHubRococoRuntimeOrigin, }; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index 54bc395c86f0..f84d42cb29f8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -50,6 +50,7 @@ mod imports { AssetHubWestendParaPallet as AssetHubWestendPallet, }, bridge_hub_rococo_emulated_chain::{ + bridge_hub_rococo_runtime::bridge_to_ethereum_config::EthereumGatewayAddress, genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoExistentialDeposit, BridgeHubRococoParaPallet as BridgeHubRococoPallet, BridgeHubRococoRuntimeOrigin, BridgeHubRococoXcmConfig, EthereumBeaconClient, EthereumInboundQueue, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index e0beac5f9eb6..0fe549f46b6d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -20,8 +20,8 @@ use hex_literal::hex; use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode}; use snowbridge_pallet_inbound_queue_fixtures::{ - register_token::make_register_token_message, send_token::make_send_token_message, - send_token_to_penpal::make_send_token_to_penpal_message, + register_token::make_register_token_message, send_native_eth::make_send_native_eth_message, + send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, }; use snowbridge_pallet_system; use snowbridge_router_primitives::inbound::{ @@ -515,6 +515,176 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { }); } +/// Tests the full cycle of eth transfers: +/// - sending a token to AssetHub +/// - returning the token to Ethereum +#[test] +fn send_eth_asset_from_asset_hub_to_ethereum_and_back() { + let ethereum_network: NetworkId = EthereumNetwork::get().into(); + let origin_location = (Parent, Parent, ethereum_network).into(); + + use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; + let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); + let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); + let ethereum_sovereign: AccountId = + EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + + AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubRococo::force_xcm_version(origin_location.clone(), XCM_VERSION); + + BridgeHubRococo::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + AssetHubRococo::fund_accounts(vec![ + (AssetHubRococoReceiver::get(), INITIAL_FUND), + (ethereum_sovereign.clone(), INITIAL_FUND), + ]); + + // Register ETH + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + origin_location.clone(), + ethereum_sovereign.into(), + true, + 1000, + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::ForceCreated { .. }) => {}, + ] + ); + }); + const ETH_AMOUNT: u128 = 1_000_000_000_000_000_000; + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Set the gateway. This is needed because new fixtures use a different gateway address. + assert_ok!(::System::set_storage( + RuntimeOrigin::root(), + vec![( + EthereumGatewayAddress::key().to_vec(), + sp_core::H160(hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d")).encode(), + )], + )); + + // Construct SendToken message and sent to inbound queue + assert_ok!(send_inbound_message(make_send_native_eth_message())); + + // Check that the send token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + let _issued_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { + asset_id: origin_location.clone(), + owner: AssetHubRococoReceiver::get().into(), + amount: ETH_AMOUNT, + }); + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubRococo, + vec![ + _issued_event => {}, + ] + ); + let assets = + vec![Asset { id: AssetId(origin_location.clone()), fun: Fungible(ETH_AMOUNT) }]; + let multi_assets = VersionedAssets::from(Assets::from(assets)); + + let destination = origin_location.clone().into(); + + let beneficiary = VersionedLocation::from(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + let free_balance_before = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Send the Weth back to Ethereum + ::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubRococoReceiver::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(multi_assets), + 0, + Unlimited, + ) + .unwrap(); + + let _burned_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned { + asset_id: origin_location.clone(), + owner: AssetHubRococoReceiver::get().into(), + balance: ETH_AMOUNT, + }); + // Check that AssetHub has issued the foreign asset + let _destination = origin_location.clone(); + assert_expected_events!( + AssetHubRococo, + vec![ + _burned_event => {}, + RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent { + destination: _destination, .. + }) => {}, + ] + ); + + let free_balance_after = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageAccepted {..}) => {}, + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] + ); + + let events = BridgeHubRococo::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() && *amount == 16903333 + )), + "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 && *amount == 2680000000000, + )), + "AssetHub sovereign takes remote fee." + ); + }); +} + #[test] fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { // Insufficient fund From e2723916e46b6f50c9215af9b50465b6b71e0b0f Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Sun, 22 Dec 2024 00:02:21 +0200 Subject: [PATCH 09/10] update prdoc and versions --- Cargo.lock | 10 +++++----- .../pallets/inbound-queue/fixtures/Cargo.toml | 2 +- prdoc/pr_6855.prdoc | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5039c89d99ee..24d8ba60c752 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2629,7 +2629,7 @@ dependencies = [ "rococo-westend-system-emulated-network", "scale-info", "snowbridge-core 0.2.0", - "snowbridge-pallet-inbound-queue-fixtures 0.10.0", + "snowbridge-pallet-inbound-queue-fixtures 0.11.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", "snowbridge-router-primitives 0.10.0", @@ -2870,7 +2870,7 @@ dependencies = [ "scale-info", "snowbridge-core 0.2.0", "snowbridge-pallet-inbound-queue 0.2.0", - "snowbridge-pallet-inbound-queue-fixtures 0.10.0", + "snowbridge-pallet-inbound-queue-fixtures 0.11.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", "snowbridge-router-primitives 0.10.0", @@ -18910,7 +18910,7 @@ dependencies = [ "snowbridge-pallet-ethereum-client 0.2.0", "snowbridge-pallet-ethereum-client-fixtures 0.9.0", "snowbridge-pallet-inbound-queue 0.2.0", - "snowbridge-pallet-inbound-queue-fixtures 0.10.0", + "snowbridge-pallet-inbound-queue-fixtures 0.11.0", "snowbridge-pallet-outbound-queue 0.2.0", "snowbridge-pallet-system 0.2.0", "snowbridge-router-primitives 0.10.0", @@ -25141,7 +25141,7 @@ dependencies = [ "snowbridge-beacon-primitives 0.2.0", "snowbridge-core 0.2.0", "snowbridge-pallet-ethereum-client 0.2.0", - "snowbridge-pallet-inbound-queue-fixtures 0.10.0", + "snowbridge-pallet-inbound-queue-fixtures 0.11.0", "snowbridge-router-primitives 0.10.0", "sp-core 28.0.0", "sp-io 30.0.0", @@ -25182,7 +25182,7 @@ dependencies = [ [[package]] name = "snowbridge-pallet-inbound-queue-fixtures" -version = "0.10.0" +version = "0.11.0" dependencies = [ "hex-literal", "snowbridge-beacon-primitives 0.2.0", diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml index c698dbbf1003..b3954bfcdde1 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snowbridge-pallet-inbound-queue-fixtures" description = "Snowbridge Inbound Queue Test Fixtures" -version = "0.10.0" +version = "0.11.0" authors = ["Snowfork "] edition.workspace = true repository.workspace = true diff --git a/prdoc/pr_6855.prdoc b/prdoc/pr_6855.prdoc index 515ac841b869..a665115ce6c7 100644 --- a/prdoc/pr_6855.prdoc +++ b/prdoc/pr_6855.prdoc @@ -12,3 +12,5 @@ doc: crates: - name: snowbridge-router-primitives bump: minor + - name: snowbridge-pallet-inbound-queue-fixtures + bump: minor From 005e62c1b6eb67f6ef4729eabff31d0ea8e2c77c Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Sun, 22 Dec 2024 00:22:15 +0200 Subject: [PATCH 10/10] cargo fmt --- bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs | 2 +- .../pallets/inbound-queue/fixtures/src/send_native_eth.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs index 6f429f19f70c..cb4232376c6f 100644 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/lib.rs @@ -3,6 +3,6 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod register_token; -pub mod send_token; pub mod send_native_eth; +pub mod send_token; pub mod send_token_to_penpal; diff --git a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs index 23b670168143..d3e8d76e6b39 100755 --- a/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs +++ b/bridges/snowbridge/pallets/inbound-queue/fixtures/src/send_native_eth.rs @@ -5,14 +5,14 @@ use hex_literal::hex; use snowbridge_beacon_primitives::{ -types::deneb, AncestryProof, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader, + types::deneb, AncestryProof, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader, }; use snowbridge_core::inbound::{InboundQueueFixture, Log, Message, Proof}; use sp_core::U256; use sp_std::vec; pub fn make_send_native_eth_message() -> InboundQueueFixture { - InboundQueueFixture { + InboundQueueFixture { message: Message { event_log: Log { address: hex!("87d1f7fdfee7f651fabc8bfcb6e086c278b77a7d").into(),