1#![cfg_attr(not(feature = "std"), no_std)]
20
21mod asset_id_conversions;
22pub use asset_id_conversions::*;
23
24mod constants;
25pub use constants::*;
26
27mod ethereum_xcm;
28pub use ethereum_xcm::*;
29
30mod filter_asset_max_fee;
31pub use filter_asset_max_fee::*;
32
33mod origin_conversion;
34pub use origin_conversion::*;
35
36mod transactor_traits;
37pub use transactor_traits::*;
38
39mod fee_trader;
40pub use fee_trader::*;
41
42use sp_std::sync::Arc;
43use sp_std::vec::Vec;
44use xcm::latest::{Junction, Junctions, Location};
45
46fn junctions_from_slice(junctions: &[Junction]) -> Option<Junctions> {
48 match junctions.len() {
49 0 => Some(Junctions::Here),
50 1 => Some(Junctions::X1(Arc::new([junctions[0].clone()]))),
51 2 => Some(Junctions::X2(Arc::new([
52 junctions[0].clone(),
53 junctions[1].clone(),
54 ]))),
55 3 => Some(Junctions::X3(Arc::new([
56 junctions[0].clone(),
57 junctions[1].clone(),
58 junctions[2].clone(),
59 ]))),
60 4 => Some(Junctions::X4(Arc::new([
61 junctions[0].clone(),
62 junctions[1].clone(),
63 junctions[2].clone(),
64 junctions[3].clone(),
65 ]))),
66 5 => Some(Junctions::X5(Arc::new([
67 junctions[0].clone(),
68 junctions[1].clone(),
69 junctions[2].clone(),
70 junctions[3].clone(),
71 junctions[4].clone(),
72 ]))),
73 6 => Some(Junctions::X6(Arc::new([
74 junctions[0].clone(),
75 junctions[1].clone(),
76 junctions[2].clone(),
77 junctions[3].clone(),
78 junctions[4].clone(),
79 junctions[5].clone(),
80 ]))),
81 7 => Some(Junctions::X7(Arc::new([
82 junctions[0].clone(),
83 junctions[1].clone(),
84 junctions[2].clone(),
85 junctions[3].clone(),
86 junctions[4].clone(),
87 junctions[5].clone(),
88 junctions[6].clone(),
89 ]))),
90 8 => Some(Junctions::X8(Arc::new([
91 junctions[0].clone(),
92 junctions[1].clone(),
93 junctions[2].clone(),
94 junctions[3].clone(),
95 junctions[4].clone(),
96 junctions[5].clone(),
97 junctions[6].clone(),
98 junctions[7].clone(),
99 ]))),
100 _ => None,
101 }
102}
103
104pub fn split_location_into_chain_part_and_beneficiary(
105 mut location: Location,
106) -> Option<(Location, Location)> {
107 let mut beneficiary_junctions_vec = Vec::new();
108
109 while let Some(j) = location.last() {
111 if matches!(j, Junction::Parachain(_) | Junction::GlobalConsensus(_)) {
112 beneficiary_junctions_vec.reverse();
115 let beneficiary_junctions = junctions_from_slice(&beneficiary_junctions_vec)?;
116 return Some((location, beneficiary_junctions.into_location()));
117 } else {
118 let (location_prefix, maybe_last_junction) = location.split_last_interior();
119 location = location_prefix;
120 if let Some(junction) = maybe_last_junction {
121 beneficiary_junctions_vec.push(junction);
122 }
123 }
124 }
125
126 beneficiary_junctions_vec.reverse();
128 let beneficiary_junctions = junctions_from_slice(&beneficiary_junctions_vec)?;
129
130 if location.parent_count() == 1 {
131 Some((Location::parent(), beneficiary_junctions.into_location()))
132 } else {
133 None
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use xcm::latest::prelude::*;
141
142 #[test]
143 fn test_split_location_single_beneficiary_junction() {
144 let location = Location {
146 parents: 1,
147 interior: [
148 Parachain(2),
149 AccountKey20 {
150 network: None,
151 key: [1u8; 20],
152 },
153 ]
154 .into(),
155 };
156
157 let (chain_part, beneficiary) =
158 split_location_into_chain_part_and_beneficiary(location).unwrap();
159
160 assert_eq!(
162 chain_part,
163 Location {
164 parents: 1,
165 interior: [Parachain(2)].into()
166 }
167 );
168
169 assert_eq!(
171 beneficiary,
172 Location {
173 parents: 0,
174 interior: [AccountKey20 {
175 network: None,
176 key: [1u8; 20]
177 }]
178 .into()
179 }
180 );
181 }
182
183 #[test]
184 fn test_split_location_multiple_beneficiary_junctions_order_preserved() {
185 let account_id = AccountId32 {
188 network: None,
189 id: [2u8; 32],
190 };
191 let general_index = GeneralIndex(42);
192
193 let location = Location {
194 parents: 1,
195 interior: [Parachain(100), account_id, general_index].into(),
196 };
197
198 let (chain_part, beneficiary) =
199 split_location_into_chain_part_and_beneficiary(location).unwrap();
200
201 assert_eq!(
203 chain_part,
204 Location {
205 parents: 1,
206 interior: [Parachain(100)].into()
207 }
208 );
209
210 assert_eq!(
212 beneficiary,
213 Location {
214 parents: 0,
215 interior: [account_id, general_index].into()
216 }
217 );
218 }
219
220 #[test]
221 fn test_split_location_three_beneficiary_junctions_order_preserved() {
222 let pallet = PalletInstance(5);
224 let account_id = AccountId32 {
225 network: None,
226 id: [3u8; 32],
227 };
228 let general_index = GeneralIndex(10);
229
230 let location = Location {
231 parents: 1,
232 interior: [Parachain(200), pallet, account_id, general_index].into(),
233 };
234
235 let (chain_part, beneficiary) =
236 split_location_into_chain_part_and_beneficiary(location).unwrap();
237
238 assert_eq!(
240 chain_part,
241 Location {
242 parents: 1,
243 interior: [Parachain(200)].into()
244 }
245 );
246
247 assert_eq!(
249 beneficiary,
250 Location {
251 parents: 0,
252 interior: [pallet, account_id, general_index].into()
253 }
254 );
255 }
256
257 #[test]
258 fn test_split_location_with_global_consensus() {
259 let account_id = AccountId32 {
261 network: None,
262 id: [4u8; 32],
263 };
264
265 let location = Location {
266 parents: 1,
267 interior: [
268 GlobalConsensus(NetworkId::Polkadot),
269 Parachain(1),
270 account_id,
271 ]
272 .into(),
273 };
274
275 let (chain_part, beneficiary) =
276 split_location_into_chain_part_and_beneficiary(location).unwrap();
277
278 assert_eq!(
282 chain_part,
283 Location {
284 parents: 1,
285 interior: [GlobalConsensus(NetworkId::Polkadot), Parachain(1)].into()
286 }
287 );
288
289 assert_eq!(
291 beneficiary,
292 Location {
293 parents: 0,
294 interior: [account_id].into()
295 }
296 );
297 }
298
299 #[test]
300 fn test_split_location_parent_only() {
301 let account_id = AccountId32 {
303 network: None,
304 id: [5u8; 32],
305 };
306
307 let location = Location {
308 parents: 1,
309 interior: [account_id].into(),
310 };
311
312 let (chain_part, beneficiary) =
313 split_location_into_chain_part_and_beneficiary(location).unwrap();
314
315 assert_eq!(chain_part, Location::parent());
317
318 assert_eq!(
320 beneficiary,
321 Location {
322 parents: 0,
323 interior: [account_id].into()
324 }
325 );
326 }
327
328 #[test]
329 fn test_split_location_multiple_junctions_order_verification() {
330 let junction_a = AccountKey20 {
334 network: None,
335 key: [10u8; 20],
336 };
337 let junction_b = AccountId32 {
338 network: None,
339 id: [20u8; 32],
340 };
341 let junction_c = GeneralIndex(30);
342
343 let location = Location {
344 parents: 1,
345 interior: [Parachain(300), junction_a, junction_b, junction_c].into(),
346 };
347
348 let (chain_part, beneficiary) =
349 split_location_into_chain_part_and_beneficiary(location).unwrap();
350
351 assert_eq!(
353 chain_part,
354 Location {
355 parents: 1,
356 interior: [Parachain(300)].into()
357 }
358 );
359
360 let beneficiary_interior = beneficiary.interior;
362 match beneficiary_interior {
363 Junctions::X3(junctions) => {
364 assert_eq!(
365 junctions[0],
366 Junction::AccountKey20 {
367 network: None,
368 key: [10u8; 20]
369 }
370 );
371 assert_eq!(
372 junctions[1],
373 Junction::AccountId32 {
374 network: None,
375 id: [20u8; 32]
376 }
377 );
378 assert_eq!(junctions[2], Junction::GeneralIndex(30));
379 }
380 _ => panic!("Expected X3 junctions"),
381 }
382 }
383
384 #[test]
385 fn test_split_location_no_beneficiary() {
386 let location = Location {
388 parents: 1,
389 interior: [Parachain(400)].into(),
390 };
391
392 let (chain_part, beneficiary) =
393 split_location_into_chain_part_and_beneficiary(location).unwrap();
394
395 assert_eq!(
397 chain_part,
398 Location {
399 parents: 1,
400 interior: [Parachain(400)].into()
401 }
402 );
403
404 assert_eq!(
406 beneficiary,
407 Location {
408 parents: 0,
409 interior: Junctions::Here
410 }
411 );
412 }
413
414 #[test]
415 fn test_split_location_invalid_no_chain_identifier() {
416 let location = Location {
418 parents: 0,
419 interior: [AccountId32 {
420 network: None,
421 id: [6u8; 32],
422 }]
423 .into(),
424 };
425
426 let result = split_location_into_chain_part_and_beneficiary(location);
427 assert!(result.is_none());
428 }
429
430 #[test]
431 fn test_split_location_invalid_wrong_parent_count() {
432 let location = Location {
434 parents: 2,
435 interior: [AccountId32 {
436 network: None,
437 id: [7u8; 32],
438 }]
439 .into(),
440 };
441
442 let result = split_location_into_chain_part_and_beneficiary(location);
443 assert!(result.is_none());
444 }
445}