diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs index d923e4211ccb..080ee1c3a6bd 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/lib.rs @@ -44,8 +44,7 @@ use scale_info::TypeInfo; use sp_core::H160; use sp_std::vec; use xcm::{ - prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm}, - VersionedXcm, MAX_XCM_DECODE_DEPTH, + prelude::{send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm}, }; use snowbridge_core::{ @@ -53,12 +52,15 @@ use snowbridge_core::{ BasicOperatingMode, }; use snowbridge_router_primitives::inbound::v2::Message as MessageV2; +use snowbridge_router_primitives::inbound::v2::ConvertMessage; pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] use snowbridge_beacon_primitives::BeaconHeader; +use snowbridge_router_primitives::inbound::v2::ConvertMessageError; + pub use pallet::*; pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; @@ -66,11 +68,9 @@ pub const LOG_TARGET: &str = "snowbridge-inbound-queue:v2"; #[frame_support::pallet] pub mod pallet { use super::*; - use codec::DecodeLimit; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); @@ -97,6 +97,7 @@ pub mod pallet { type WeightInfo: WeightInfo; /// AssetHub parachain ID type AssetHubParaId: Get; + type MessageConverter: ConvertMessage; #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -207,7 +208,7 @@ pub mod pallet { let message = MessageV2::decode_all(&mut envelope.payload.as_ref()) .map_err(|_| Error::::InvalidPayload)?; - let xcm = convert_message(message)?; + let xcm = T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; // Todo: Deposit fee(in Ether) to RewardLeger which should cover all of: // T::RewardLeger::deposit(who, envelope.fee.into())?; @@ -218,7 +219,7 @@ pub mod pallet { // e. The reward // Attempt to forward XCM to AH - let dest = Location::new(1, [Parachain(T::AssetHubParaId)]); + let dest = Location::new(1, [Parachain(T::AssetHubParaId::get())]); let (message_id, _) = send_xcm::(dest, xcm).map_err(Error::::from)?; Self::deposit_event(Event::MessageReceived { nonce: envelope.nonce, message_id }); diff --git a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs index f08ce202f9bc..7f754fe20874 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/mock.rs @@ -18,7 +18,7 @@ use sp_runtime::{ }; use sp_std::{convert::From, default::Default}; use xcm::{latest::SendXcm, prelude::*}; - +use snowbridge_router_primitives::inbound::v2::MessageToXcm; use crate::{self as inbound_queue}; type Block = frame_system::mocking::MockBlock; @@ -100,20 +100,6 @@ impl Verifier for MockVerifier { const GATEWAY_ADDRESS: [u8; 20] = hex!["eda338e4dc46038493b885327842fd3e301cab39"]; -parameter_types! { - pub const EthereumNetwork: xcm::v3::NetworkId = xcm::v3::NetworkId::Ethereum { chain_id: 11155111 }; - pub const GatewayAddress: H160 = H160(GATEWAY_ADDRESS); - 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 InitialFund: u128 = 1_000_000_000_000; - pub const InboundQueuePalletInstance: u8 = 80; - pub UniversalLocation: InteriorLocation = - [GlobalConsensus(Westend), Parachain(1002)].into(); - pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); -} - #[cfg(feature = "runtime-benchmarks")] impl BenchmarkHelper for Test { // not implemented since the MockVerifier is used for tests @@ -158,12 +144,25 @@ 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 InboundQueuePalletInstance: u8 = 80; + pub AssetHubLocation: InteriorLocation = Parachain(1000).into(); +} + impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; type XcmSender = MockXcmSender; type WeightInfo = (); type GatewayAddress = GatewayAddress; + type AssetHubParaId = ConstU32<1000>; + type MessageConverter = MessageToXcm< + EthereumNetwork, + AssetHubLocation, + InboundQueuePalletInstance, + >; #[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 abf9a3f8a823..475539e6e1a9 100644 --- a/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue-v2/src/test.rs @@ -10,12 +10,16 @@ use sp_runtime::DispatchError; use crate::{mock::*, Error, Event as InboundQueueEvent}; use codec::DecodeLimit; -use snowbridge_router_primitives::inbound::v2::Asset as InboundAsset; +use snowbridge_router_primitives::inbound::v2::InboundAsset; use sp_core::H256; use xcm::opaque::latest::{ prelude::{ClearOrigin, ReceiveTeleportedAsset}, Asset, AssetId, Assets, }; +use xcm::VersionedXcm; +use xcm::MAX_XCM_DECODE_DEPTH; +use snowbridge_router_primitives::inbound::v2::ConvertMessage; +use xcm::prelude::{Junction::AccountKey20, *}; #[test] fn test_submit_happy_path() { @@ -246,9 +250,6 @@ fn test_register_token_inbound_message_with_xcm_and_claimer() { } }; - let total_fee_asset: Asset = (Location::parent(), 1_000_000_000).into(); - let first_instruction = ReceiveTeleportedAsset(total_fee_asset.into()); - let mut decoded_instructions = decoded_instructions.into_iter(); let decoded_first = decoded_instructions.next().take(); assert!(decoded_first.is_some()); diff --git a/bridges/snowbridge/primitives/router/src/inbound/v2.rs b/bridges/snowbridge/primitives/router/src/inbound/v2.rs index 08bbae3c7c6e..444b1f9a7ee5 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/v2.rs @@ -6,15 +6,12 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::PalletError; use scale_info::TypeInfo; -use snowbridge_core::TokenId; use sp_core::{Get, RuntimeDebug, H160, H256}; -use sp_runtime::MultiAddress; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm::MAX_XCM_DECODE_DEPTH; use codec::DecodeLimit; -const MINIMUM_DEPOSIT: u128 = 1; const LOG_TARGET: &str = "snowbridge-router-primitives"; /// Messages from Ethereum are versioned. This is because in future, @@ -32,7 +29,7 @@ pub struct Message { /// The origin address pub origin: H160, /// The assets - pub assets: Vec, + pub assets: Vec, // The command originating from the Gateway contract pub xcm: Vec, // The claimer in the case that funds get trapped. @@ -40,15 +37,15 @@ pub struct Message { } #[derive(Clone, Encode, Decode, RuntimeDebug)] -pub enum Asset { +pub enum InboundAsset { NativeTokenERC20 { - /// The token ID, native or foreign + /// The native token ID token_id: H160, /// The monetary value of the asset value: u128 }, ForeignTokenERC20 { - /// The token ID, native or foreign + /// The foreign token ID token_id: H256, /// The monetary value of the asset value: u128 @@ -71,29 +68,35 @@ pub trait ConvertMessage { } pub struct MessageToXcm< - EthereumUniversalLocation, + EthereumNetwork, AssetHubLocation, + InboundQueuePalletInstance, > where - EthereumUniversalLocation: Get, + EthereumNetwork: Get, AssetHubLocation: Get, + InboundQueuePalletInstance: Get, { _phantom: PhantomData<( - EthereumUniversalLocation, + EthereumNetwork, AssetHubLocation, + InboundQueuePalletInstance, )>, } impl< - EthereumUniversalLocation, + EthereumNetwork, AssetHubLocation, + InboundQueuePalletInstance, > ConvertMessage for MessageToXcm< - EthereumUniversalLocation, + EthereumNetwork, AssetHubLocation, + InboundQueuePalletInstance, > where - EthereumUniversalLocation: Get, + EthereumNetwork: Get, AssetHubLocation: Get, + InboundQueuePalletInstance: Get, { fn convert(message: Message) -> Result, ConvertMessageError> { // Decode xcm @@ -105,17 +108,54 @@ for MessageToXcm< log::debug!(target: LOG_TARGET,"xcm decoded as {:?}", message_xcm); - let origin_location: Location = Location::new(2, [EthereumUniversalLocation::get().into(), Junction::AccountKey20{ key: message.origin.into(), network: None}.into()]); - let instructions = vec![ - AliasOrigin(origin_location), + let network = EthereumNetwork::get(); + + let mut origin_location = Location::new(2, GlobalConsensus(network)).push_interior(AccountKey20 { + key: message.origin.into(), network: None + }).map_err(|_| ConvertMessageError::InvalidXCM)?; + + let network = EthereumNetwork::get(); + + let fee_asset = Location::new(1, Here); + let fee_value = 1_000_000_000u128; // TODO configure + let fee: Asset = (fee_asset, fee_value).into(); + let mut instructions = vec![ + ReceiveTeleportedAsset(fee.clone().into()), + BuyExecution{fees: fee, weight_limit: Unlimited}, + DescendOrigin(PalletInstance(InboundQueuePalletInstance::get()).into()), + UniversalOrigin(GlobalConsensus(network)), + AliasOrigin(origin_location.into()), ]; + for asset in &message.assets { + match asset { + InboundAsset::NativeTokenERC20 { token_id, value } => { + let token_location: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get()), AccountKey20{network: None, key: (*token_id).into()}]); + instructions.push(ReserveAssetDeposited((token_location, *value).into())); + } + InboundAsset::ForeignTokenERC20 { token_id, value } => { + // TODO check how token is represented as H256 on AH, assets pallet? + let token_location: Location = Location::new(0, [AccountId32 {network: None, id: (*token_id).into()}]); + // TODO Is this token always on AH? Would probably need to distinguish between tokens on other parachains eventually + instructions.push(WithdrawAsset((token_location, *value).into())); + } + } + } + if let Some(claimer) = message.claimer { - let claimer = MultiAddress::decode(&mut claimer.as_ref()).map_err(|_| ConvertMessageError::InvalidClaimer)?; - let claimer_location: Location = Location::new(1, [AssetHubLocation::get().into(), claimer.into()]); + let claimer = Junction::decode(&mut claimer.as_ref()).map_err(|_| ConvertMessageError::InvalidClaimer)?; + let claimer_location: Location = Location::new(0, [claimer.into()]); instructions.push(SetAssetClaimer { location: claimer_location }); } + // TODO not sure this is correct, should the junction be prefixed with GlobalConsensus(EthereumNetwork::get()? + instructions.push(DescendOrigin(AccountKey20 { + key: message.origin.into(), network: None + }.into())); + + // Add the XCM the user specified to the end of the XCM + instructions.extend(message_xcm.0); + Ok(instructions.into()) } }