From 7df0417bcdcbc62f8b12b631076cb824bc917f87 Mon Sep 17 00:00:00 2001
From: Serban Iorga <serban@parity.io>
Date: Thu, 2 Nov 2023 16:02:41 +0200
Subject: [PATCH] XCM MultiAssets: sort after reanchoring (#2129)

Fixes https://github.com/paritytech/polkadot-sdk/issues/2123
---
 polkadot/xcm/src/v3/multiasset.rs | 42 ++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs
index 9d86fb8deff..454120a1a7b 100644
--- a/polkadot/xcm/src/v3/multiasset.rs
+++ b/polkadot/xcm/src/v3/multiasset.rs
@@ -689,12 +689,16 @@ impl MultiAssets {
 
 	/// Mutate the location of the asset identifier if concrete, giving it the same location
 	/// relative to a `target` context. The local context is provided as `context`.
+	///
+	/// This will also re-sort the inner assets to preserve ordering guarantees.
 	pub fn reanchor(
 		&mut self,
 		target: &MultiLocation,
 		context: InteriorMultiLocation,
 	) -> Result<(), ()> {
-		self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))
+		self.0.iter_mut().try_for_each(|i| i.reanchor(target, context))?;
+		self.0.sort();
+		Ok(())
 	}
 
 	/// Return a reference to an item at a specific index or `None` if it doesn't exist.
@@ -985,6 +989,42 @@ mod tests {
 		assert!(r.is_err());
 	}
 
+	#[test]
+	fn reanchor_preserves_sorting() {
+		use super::*;
+		use alloc::vec;
+
+		let reanchor_context = X1(Parachain(2000));
+		let dest = MultiLocation::new(1, Here);
+
+		let asset_1: MultiAsset =
+			(MultiLocation::new(0, X2(PalletInstance(50), GeneralIndex(1))), 10).into();
+		let mut asset_1_reanchored = asset_1.clone();
+		assert!(asset_1_reanchored.reanchor(&dest, reanchor_context).is_ok());
+		assert_eq!(
+			asset_1_reanchored,
+			(MultiLocation::new(0, X3(Parachain(2000), PalletInstance(50), GeneralIndex(1))), 10)
+				.into()
+		);
+
+		let asset_2: MultiAsset = (MultiLocation::new(1, Here), 10).into();
+		let mut asset_2_reanchored = asset_2.clone();
+		assert!(asset_2_reanchored.reanchor(&dest, reanchor_context).is_ok());
+		assert_eq!(asset_2_reanchored, (MultiLocation::new(0, Here), 10).into());
+
+		let asset_3: MultiAsset = (MultiLocation::new(1, X1(Parachain(1000))), 10).into();
+		let mut asset_3_reanchored = asset_3.clone();
+		assert!(asset_3_reanchored.reanchor(&dest, reanchor_context).is_ok());
+		assert_eq!(asset_3_reanchored, (MultiLocation::new(0, X1(Parachain(1000))), 10).into());
+
+		let mut assets: MultiAssets =
+			vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into();
+		assert_eq!(assets.clone(), vec![asset_1.clone(), asset_2.clone(), asset_3.clone()].into());
+
+		assert!(assets.reanchor(&dest, reanchor_context).is_ok());
+		assert_eq!(assets, vec![asset_2_reanchored, asset_3_reanchored, asset_1_reanchored].into());
+	}
+
 	#[test]
 	fn decoding_respects_limit() {
 		use super::*;
-- 
GitLab