diff --git a/parachain/primitives/router/src/outbound/mod.rs b/parachain/primitives/router/src/outbound/mod.rs index a2b8620fde..15814616b2 100644 --- a/parachain/primitives/router/src/outbound/mod.rs +++ b/parachain/primitives/router/src/outbound/mod.rs @@ -7,23 +7,23 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::outbound::{AgentExecuteCommand, Command, Message, SendMessage}; -use sp_core::H256; -use sp_std::{marker::PhantomData, prelude::*}; +use sp_core::{H160, H256}; +use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::v3::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; pub struct EthereumBlobExporter< UniversalLocation, - GatewayLocation, + EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, GatewayLocation, OutboundQueue, AgentHashedDescription)>); +>(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); -impl ExportXcm - for EthereumBlobExporter +impl ExportXcm + for EthereumBlobExporter where UniversalLocation: Get, - GatewayLocation: Get, + EthereumNetwork: Get, OutboundQueue: SendMessage, OutboundQueue::Ticket: Encode + Decode, AgentHashedDescription: ConvertLocation, @@ -37,16 +37,10 @@ where destination: &mut Option, message: &mut Option>, ) -> SendResult { - let gateway_location = GatewayLocation::get(); + let expected_network = EthereumNetwork::get(); let universal_location = UniversalLocation::get(); - let (gateway_network, gateway_junctions) = gateway_location.interior().split_global() - .map_err(|_| { - log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to bridge location not being remote. universal_location: {universal_location:?}, {gateway_location:?}"); - SendError::NotApplicable - })?; - - if network != gateway_network { + if network != expected_network { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched bridge network {network:?}."); return Err(SendError::NotApplicable) } @@ -57,16 +51,6 @@ where return Err(SendError::NotApplicable) } - let gateway_address = match gateway_junctions { - X1(AccountKey20 { network, key }) - if network.is_none() || network == Some(gateway_network) => - key, - _ => { - log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched registry contract {gateway_junctions:?}."); - return Err(SendError::NotApplicable) - }, - }; - let (local_net, local_sub) = universal_source .take() .ok_or_else(|| { @@ -97,17 +81,12 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &gateway_network, &gateway_address); - let (agent_execute_command, max_target_fee) = converter.convert().map_err(|err|{ + let mut converter = XcmConverter::new(&message, &expected_network); + let agent_execute_command = converter.convert().map_err(|err|{ log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); SendError::Unroutable })?; - if max_target_fee.is_some() { - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due not supporting max target fee."); - return Err(SendError::Unroutable) - } - // local_sub is relative to the relaychain. No conversion needed. let local_sub_location: MultiLocation = local_sub.into(); let agent_id = match AgentHashedDescription::convert_location(&local_sub_location) { @@ -156,151 +135,141 @@ where #[derive(PartialEq, Debug)] enum XcmConverterError { UnexpectedEndOfXcm, - TargetFeeExpected, - BuyExecutionExpected, EndOfXcmMessageExpected, - WithdrawExpected, - DepositExpected, + WithdrawAssetExpected, + DepositAssetExpected, NoReserveAssets, FilterDoesNotConsumeAllAssets, TooManyAssets, - AssetNotConcreteFungible, ZeroAssetTransfer, BeneficiaryResolutionFailed, AssetResolutionFailed, + InvalidFeeAsset, SetTopicExpected, } +macro_rules! match_expression { + ($expression:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $value:expr $(,)?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => Some($value), + _ => None, + } + }; +} + struct XcmConverter<'a, Call> { - iter: Iter<'a, Instruction>, + iter: Peekable>>, ethereum_network: &'a NetworkId, - gateway_address: &'a [u8; 20], } impl<'a, Call> XcmConverter<'a, Call> { - fn new( - message: &'a Xcm, - ethereum_network: &'a NetworkId, - gateway_address: &'a [u8; 20], - ) -> Self { - Self { iter: message.inner().iter(), ethereum_network, gateway_address } + fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { + Self { iter: message.inner().iter().peekable(), ethereum_network } } - fn convert( - &mut self, - ) -> Result<(AgentExecuteCommand, Option<&'a MultiAsset>), XcmConverterError> { - // Get target fees if specified. - let max_target_fee = self.fee_info()?; - + fn convert(&mut self) -> Result { // Get withdraw/deposit and make native tokens create message. let result = self.native_tokens_unlock_message()?; - // Match last set topic. Later could use message id for replies - let _ = match self.next()? { - SetTopic(id) => id, - _ => return Err(XcmConverterError::SetTopicExpected), - }; - // All xcm instructions must be consumed before exit. if self.next().is_ok() { return Err(XcmConverterError::EndOfXcmMessageExpected) } - Ok((result, max_target_fee)) - } - - fn fee_info(&mut self) -> Result, XcmConverterError> { - use XcmConverterError::*; - let execution_fee = match self.next()? { - WithdrawAsset(fee_asset) => match self.next()? { - BuyExecution { fees: execution_fee, weight_limit: Unlimited } - if fee_asset.len() == 1 && fee_asset.contains(execution_fee) => - Some(execution_fee), - _ => return Err(BuyExecutionExpected), - }, - UnpaidExecution { check_origin: None, weight_limit: Unlimited } => None, - _ => return Err(TargetFeeExpected), - }; - Ok(execution_fee) + Ok(result) } fn native_tokens_unlock_message(&mut self) -> Result { use XcmConverterError::*; - let (assets, beneficiary) = if let WithdrawAsset(reserved_assets) = self.next()? { - if reserved_assets.len() == 0 { - return Err(NoReserveAssets) - } - if let DepositAsset { assets, beneficiary } = self.next()? { - if reserved_assets.inner().iter().any(|asset| !assets.matches(asset)) { - return Err(FilterDoesNotConsumeAllAssets) - } - (reserved_assets, beneficiary) - } else { - return Err(DepositExpected) - } - } else { - return Err(WithdrawExpected) - }; - // assert that the beneficiary is AccountKey20 - let destination = { - if let MultiLocation { parents: 0, interior: X1(AccountKey20 { network, key }) } = - beneficiary - { - if network.is_some() && network != &Some(*self.ethereum_network) { - return Err(BeneficiaryResolutionFailed) - } - key.into() - } else { - return Err(BeneficiaryResolutionFailed) - } - }; + // Get the reserve assets from WithdrawAsset. + let reserve_assets = + match_expression!(self.next()?, WithdrawAsset(reserve_assets), reserve_assets) + .ok_or(WithdrawAssetExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution or continue parsing. + let fee_asset = match_expression!(self.peek(), Ok(BuyExecution { fees, .. }), fees); + if fee_asset.is_some() { + let _ = self.next(); + } - let (asset, amount) = { - // We only support a single asset at a time. - ensure!(assets.len() == 1, TooManyAssets); - - // Ensure asset is concrete and fungible. - let asset = assets.get(0).ok_or(AssetResolutionFailed)?; - let (asset_location, amount) = - if let MultiAsset { id: Concrete(location), fun: Fungible(amount) } = asset { - (location, amount) - } else { - return Err(AssetNotConcreteFungible) - }; - - ensure!(*amount > 0, ZeroAssetTransfer); - - // extract ERC20 contract address - if let MultiLocation { - parents: 0, - interior: - X2( - AccountKey20 { network: gateway_network, key: gateway_address }, - AccountKey20 { network: token_network, key: token_address }, - ), - } = asset_location - { - if gateway_network.is_some() && gateway_network != &Some(*self.ethereum_network) { - return Err(AssetResolutionFailed) - } - if gateway_address != self.gateway_address { - return Err(AssetResolutionFailed) - } - if token_network.is_some() && token_network != &Some(*self.ethereum_network) { - return Err(AssetResolutionFailed) - } - (token_address.into(), *amount) - } else { - return Err(AssetResolutionFailed) + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary, + MultiLocation { parents: 0, interior: X1(AccountKey20 { network, key }) } + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + // We only support a single asset at a time. + ensure!(reserve_assets.len() == 1, TooManyAssets); + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) } - }; + } + + let (token, amount) = match_expression!( + reserve_asset, + MultiAsset { + id: Concrete(MultiLocation { parents: 0, interior: X1(AccountKey20 { network , key })}), + fun: Fungible(amount) + } if self.network_matches(network), + (H160(*key), *amount) + ) + .ok_or(AssetResolutionFailed)?; - Ok(AgentExecuteCommand::TransferToken { token: asset, recipient: destination, amount }) + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + // Check if there is a SetTopic and skip over it if found. + let _topic_id = + match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok(AgentExecuteCommand::TransferToken { token, recipient, amount }) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { self.iter.next().ok_or(XcmConverterError::UnexpectedEndOfXcm) } + + fn peek(&mut self) -> Result<&&'a Instruction, XcmConverterError> { + self.iter.peek().ok_or(XcmConverterError::UnexpectedEndOfXcm) + } + + fn network_matches(&self, network: &Option) -> bool { + if let Some(network) = network { + network == self.ethereum_network + } else { + true + } + } } #[cfg(test)] @@ -319,13 +288,9 @@ mod tests { const RelayNetwork: NetworkId = Polkadot; const UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(RelayNetwork::get()), Parachain(1013)); const BridgedNetwork: NetworkId = Ethereum{ chain_id: 1 }; - BridgedLocation: MultiLocation = MultiLocation::new(0, (GlobalConsensus(BridgedNetwork::get()), AccountKey20 { network: None, key: GATEWAY })); - BridgedLocationWithoutRegistry: MultiLocation = MultiLocation::new(0, GlobalConsensus(BridgedNetwork::get())); - BridgedLocationWithoutGlobalConsensus: MultiLocation = MultiLocation::new(0, AccountKey20 { network: None, key: GATEWAY }); + const NonBridgedNetwork: NetworkId = Ethereum{ chain_id: 2 }; } - const GATEWAY: [u8; 20] = hex!("D184c103F7acc340847eEE82a0B909E3358bc28d"); - struct MockOkOutboundQueue; impl SendMessage for MockOkOutboundQueue { type Ticket = (); @@ -361,13 +326,14 @@ mod tests { let mut destination: Option = None; let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -379,13 +345,14 @@ mod tests { let mut destination: Option = None; let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -399,13 +366,14 @@ mod tests { )); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -417,13 +385,14 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -435,49 +404,33 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::Unroutable)); } #[test] fn exporter_validate_without_global_bridge_location_yields_not_applicable() { - let network = BridgedNetwork::get(); - let channel: u32 = 0; - let mut universal_source: Option = Here.into(); - let mut destination: Option = Here.into(); - let mut message: Option> = None; - - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocationWithoutGlobalConsensus, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); - assert_eq!(result, Err(XcmSendError::NotApplicable)); - } - - #[test] - fn exporter_validate_without_registry_bridge_location_yields_not_applicable() { - let network = BridgedNetwork::get(); + let network = NonBridgedNetwork::get(); let channel: u32 = 0; let mut universal_source: Option = Here.into(); let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocationWithoutRegistry, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -490,13 +443,14 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -509,13 +463,14 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -528,13 +483,14 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -547,13 +503,14 @@ mod tests { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -572,13 +529,7 @@ mod tests { let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; let fees: MultiAssets = vec![fee.clone()].into(); let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); @@ -602,13 +553,14 @@ mod tests { .into(), ); - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -629,13 +581,14 @@ mod tests { vec![WithdrawAsset(fees), BuyExecution { fees: fee, weight_limit: Unlimited }].into(), ); - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -653,42 +606,36 @@ mod tests { let channel: u32 = 0; let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); + let fee = assets.clone().get(0).unwrap().clone(); let filter: MultiAssetFilter = assets.clone().into(); let mut message: Option> = Some( vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee, weight_limit: Unlimited }, DepositAsset { assets: filter, - beneficiary: X1(AccountKey20 { - network: Some(network), - key: beneficiary_address, - }) - .into(), + beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }) + .into(), }, SetTopic([0; 32]), ] .into(), ); - let result = - EthereumBlobExporter::< - UniversalLocation, - BridgedLocation, - MockOkOutboundQueue, - AgentIdOf, - >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + let result = EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + >::validate( + network, channel, &mut universal_source, &mut destination, &mut message + ); assert!(result.is_ok()); } @@ -697,7 +644,7 @@ mod tests { fn exporter_deliver_with_submit_failure_yields_unroutable() { let result = EthereumBlobExporter::< UniversalLocation, - BridgedLocation, + BridgedNetwork, MockErrOutboundQueue, AgentIdOf, >::deliver((hex!("deadbeef").to_vec(), XcmHash::default())); @@ -705,32 +652,23 @@ mod tests { } #[test] - fn xcm_converter_convert_success_with_max_target_fee() { + fn xcm_converter_convert_success() { let network = BridgedNetwork::get(); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -738,39 +676,32 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let expected_payload = AgentExecuteCommand::TransferToken { token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, }; let result = converter.convert(); - assert_eq!(result, Ok((expected_payload, Some(&fee)))); + assert_eq!(result, Ok(expected_payload)); } #[test] - fn xcm_converter_convert_success_without_max_target_fee() { + fn xcm_converter_convert_without_buy_execution_yields_success() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -778,14 +709,14 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let expected_payload = AgentExecuteCommand::TransferToken { token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, }; let result = converter.convert(); - assert_eq!(result, Ok((expected_payload, None))); + assert_eq!(result, Ok(expected_payload)); } #[test] @@ -796,21 +727,16 @@ mod tests { let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = Wild(All); let message: Xcm<()> = vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -818,78 +744,120 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let expected_payload = AgentExecuteCommand::TransferToken { token: token_address.into(), recipient: beneficiary_address.into(), amount: 1000, }; let result = converter.convert(); - assert_eq!(result, Ok((expected_payload, None))); + assert_eq!(result, Ok(expected_payload)); } #[test] - fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { + fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let asset_location = X1(AccountKey20 { network: None, key: token_address }).into(); + let fee_asset = MultiAsset { id: Concrete(asset_location), fun: Fungible(500) }; + + let assets: MultiAssets = + vec![MultiAsset { id: Concrete(asset_location), fun: Fungible(1000) }].into(); + + let filter: MultiAssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee_asset, weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = XcmConverter::new(&message, &network); + let expected_payload = AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }; + let result = converter.convert(); + assert_eq!(result, Ok(expected_payload)); + } + + #[test] + fn xcm_converter_convert_without_clear_origin_yields_set_topic_expected() { + let network = BridgedNetwork::get(); + + let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); + let filter: MultiAssetFilter = assets.clone().into(); + let message: Xcm<()> = vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), + }, + ClearTopic, ] .into(); - - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); + assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } #[test] - fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { + fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { let network = BridgedNetwork::get(); - let message: Xcm<()> = vec![].into(); - - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); + let assets: MultiAssets = vec![MultiAsset { + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), + fun: Fungible(1000), + }] + .into(); + let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } #[test] - fn xcm_converter_convert_without_max_target_fee_yields_target_fee_expected() { + fn xcm_converter_with_different_fee_asset_fails() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + let asset_location = X1(AccountKey20 { network: None, key: token_address }).into(); + let fee_asset = MultiAsset { + id: Concrete(MultiLocation { parents: 0, interior: Here.into() }), fun: Fungible(1000), - }] - .into(); + }; + + let assets: MultiAssets = + vec![MultiAsset { id: Concrete(asset_location), fun: Fungible(1000) }].into(); + let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - ClearTopic, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee_asset, weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -897,50 +865,52 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); - + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::TargetFeeExpected)); + assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } #[test] - fn xcm_converter_convert_without_set_topic_suffix_yields_set_topic_expected() { + fn xcm_converter_with_fees_greater_than_reserve_fails() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); + let asset_location = X1(AccountKey20 { network: None, key: token_address }).into(); + let fee_asset = MultiAsset { id: Concrete(asset_location), fun: Fungible(1001) }; + + let assets: MultiAssets = + vec![MultiAsset { id: Concrete(asset_location), fun: Fungible(1000) }].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), - fun: Fungible(1000), - }] - .into(); let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee_asset, weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), }, - ClearTopic, + SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); + let result = converter.convert(); + assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); + } + + #[test] + fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { + let network = BridgedNetwork::get(); + + let message: Xcm<()> = vec![].into(); + + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } #[test] @@ -950,35 +920,26 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), }, SetTopic([0; 32]), - ClearOrigin, + ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -991,25 +952,16 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1017,10 +969,10 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawExpected)); + assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); } #[test] @@ -1029,50 +981,45 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::DepositExpected)); + assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); } #[test] fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { let network = BridgedNetwork::get(); - let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); let assets: MultiAssets = vec![].into(); let filter: MultiAssetFilter = assets.clone().into(); + let fee = MultiAsset { + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), + fun: Fungible(1000), + }; + let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee, weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1080,7 +1027,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -1094,9 +1041,6 @@ mod tests { let token_address_2: [u8; 20] = hex!("1100000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![ MultiAsset { id: Concrete(X1(AccountKey20 { network: None, key: token_address_1 }).into()), @@ -1111,9 +1055,9 @@ mod tests { let filter: MultiAssetFilter = assets.clone().into(); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1121,7 +1065,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -1134,26 +1078,17 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(0)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1161,52 +1096,12 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); } - #[test] - fn xcm_converter_convert_with_non_fungible_asset_yields_asset_not_concrete_fungible() { - let network = BridgedNetwork::get(); - - let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), - fun: NonFungible(AssetInstance::Index(0)), - }] - .into(); - let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); - - let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), - DepositAsset { - assets: filter, - beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), - }, - SetTopic([0; 32]), - ] - .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); - - let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::AssetNotConcreteFungible)); - } - #[test] fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { let network = BridgedNetwork::get(); @@ -1214,26 +1109,17 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(0), }] .into(); let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1241,7 +1127,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -1253,9 +1139,6 @@ mod tests { let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { id: Concrete(X3(GlobalConsensus(Polkadot), Parachain(1000), GeneralIndex(0)).into()), fun: Fungible(1000), @@ -1264,9 +1147,9 @@ mod tests { let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1274,7 +1157,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -1287,58 +1170,10 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: Some(network), key: GATEWAY }, - AccountKey20 { network: Some(Ethereum { chain_id: 2 }), key: token_address }, - ) - .into(), - ), - fun: Fungible(1000), - }] - .into(); - let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); - - let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), - DepositAsset { - assets: filter, - beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), - }, - SetTopic([0; 32]), - ] - .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); - - let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); - } - - #[test] - fn xcm_converter_convert_bad_registry_yields_asset_resolution_failed() { - let network = BridgedNetwork::get(); - - let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - - const BAD_REGISTRY: [u8; 20] = hex!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { id: Concrete( - X2( - AccountKey20 { network: Some(network), key: BAD_REGISTRY }, - AccountKey20 { network: Some(network), key: token_address }, - ) - .into(), + X1(AccountKey20 { network: Some(Ethereum { chain_id: 2 }), key: token_address }) + .into(), ), fun: Fungible(1000), }] @@ -1346,9 +1181,9 @@ mod tests { let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1356,29 +1191,23 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); } #[test] - fn xcm_converter_convert_non_ethereum_chain_registry_asset_yields_asset_resolution_failed() { + fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { id: Concrete( - X2( - AccountKey20 { network: Some(Ethereum { chain_id: 2 }), key: GATEWAY }, - AccountKey20 { network: Some(network), key: token_address }, - ) - .into(), + X1(AccountKey20 { network: Some(NonBridgedNetwork::get()), key: token_address }) + .into(), ), fun: Fungible(1000), }] @@ -1386,9 +1215,9 @@ mod tests { let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { network: None, key: beneficiary_address }).into(), @@ -1396,7 +1225,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -1411,26 +1240,16 @@ mod tests { let beneficiary_address: [u8; 32] = hex!("2000000000000000000000000000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); - let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X3( @@ -1443,7 +1262,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1457,26 +1276,17 @@ mod tests { let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - let fee = MultiAsset { id: Concrete(Here.into()), fun: Fungible(1000) }; - let fees: MultiAssets = vec![fee.clone()].into(); - let assets: MultiAssets = vec![MultiAsset { - id: Concrete( - X2( - AccountKey20 { network: None, key: GATEWAY }, - AccountKey20 { network: None, key: token_address }, - ) - .into(), - ), + id: Concrete(X1(AccountKey20 { network: None, key: token_address }).into()), fun: Fungible(1000), }] .into(); let filter: MultiAssetFilter = Wild(WildMultiAsset::AllCounted(1)); let message: Xcm<()> = vec![ - WithdrawAsset(fees), - BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, - WithdrawAsset(assets), + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, DepositAsset { assets: filter, beneficiary: X1(AccountKey20 { @@ -1488,7 +1298,7 @@ mod tests { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network, &GATEWAY); + let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); diff --git a/polkadot-sdk b/polkadot-sdk index dab8646cfd..0378a75a1f 160000 --- a/polkadot-sdk +++ b/polkadot-sdk @@ -1 +1 @@ -Subproject commit dab8646cfdd5fe8ffe82335feda064cd4b8a5f2c +Subproject commit 0378a75a1f7d32da9529bb1bdf860f1c9f2ed090