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)) +}