1#![cfg_attr(not(feature = "std"), no_std)]
17
18#[cfg(feature = "runtime-benchmarks")]
19pub mod benchmarks;
20mod erc20_transactor;
21pub mod foreign_creators;
22pub mod fungible_conversion;
23pub mod local_and_foreign_assets;
24pub mod matching;
25pub mod runtime_api;
26pub use erc20_transactor::ERC20Transactor;
27
28extern crate alloc;
29extern crate core;
30
31use crate::matching::{LocalLocationPattern, ParentLocation};
32use alloc::vec::Vec;
33use codec::{Decode, EncodeLike};
34use core::{cmp::PartialEq, marker::PhantomData};
35use frame_support::traits::{Contains, Equals, EverythingBut};
36use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId};
37use sp_core::H160;
38use sp_runtime::traits::{MaybeEquivalence, TryConvertInto};
39use xcm::prelude::*;
40use xcm_builder::{
41 AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith, WithLatestLocationConverter,
42};
43use xcm_executor::traits::JustTry;
44
45pub type AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, L = Location> =
47 AsPrefixedGeneralIndex<
48 TrustBackedAssetsPalletLocation,
49 AssetIdForTrustBackedAssets,
50 TryConvertInto,
51 L,
52 >;
53
54pub type CollectionIdForUniquesConvert<UniquesPalletLocation> =
56 AsPrefixedGeneralIndex<UniquesPalletLocation, CollectionId, TryConvertInto>;
57
58pub type TrustBackedAssetsConvertedConcreteId<
60 TrustBackedAssetsPalletLocation,
61 Balance,
62 L = Location,
63> = MatchedConvertedConcreteId<
64 AssetIdForTrustBackedAssets,
65 Balance,
66 StartsWith<TrustBackedAssetsPalletLocation>,
67 AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, L>,
68 TryConvertInto,
69>;
70
71pub type UniquesConvertedConcreteId<UniquesPalletLocation> = MatchedConvertedConcreteId<
73 CollectionId,
74 ItemId,
75 StartsWith<UniquesPalletLocation>,
78 CollectionIdForUniquesConvert<UniquesPalletLocation>,
79 TryConvertInto,
80>;
81
82pub type TrustBackedAssetsAsLocation<
87 TrustBackedAssetsPalletLocation,
88 Balance,
89 L,
90 LocationConverter = WithLatestLocationConverter<L>,
91> = MatchedConvertedConcreteId<
92 L,
93 Balance,
94 StartsWith<TrustBackedAssetsPalletLocation>,
95 LocationConverter,
96 TryConvertInto,
97>;
98
99pub type ForeignAssetsConvertedConcreteId<
108 AdditionalLocationExclusionFilter,
109 Balance,
110 AssetId,
111 LocationToAssetIdConverter = WithLatestLocationConverter<AssetId>,
112 BalanceConverter = TryConvertInto,
113> = MatchedConvertedConcreteId<
114 AssetId,
115 Balance,
116 EverythingBut<(
117 Equals<ParentLocation>,
119 StartsWith<LocalLocationPattern>,
124 AdditionalLocationExclusionFilter,
126 )>,
127 LocationToAssetIdConverter,
128 BalanceConverter,
129>;
130
131pub struct IsLocalAccountKey20;
134impl Contains<Location> for IsLocalAccountKey20 {
135 fn contains(location: &Location) -> bool {
136 matches!(location.unpack(), (0, [AccountKey20 { .. }]))
137 }
138}
139
140pub struct AccountKey20ToH160;
143impl MaybeEquivalence<Location, H160> for AccountKey20ToH160 {
144 fn convert(location: &Location) -> Option<H160> {
145 match location.unpack() {
146 (0, [AccountKey20 { key, .. }]) => Some((*key).into()),
147 _ => None,
148 }
149 }
150
151 fn convert_back(key: &H160) -> Option<Location> {
152 Some(Location::new(0, [AccountKey20 { key: (*key).into(), network: None }]))
153 }
154}
155
156pub type ERC20Matcher =
159 MatchedConvertedConcreteId<H160, u128, IsLocalAccountKey20, AccountKey20ToH160, JustTry>;
160
161pub type AssetIdForPoolAssets = u32;
162
163pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, L = Location> =
165 AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto, L>;
166pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
168 MatchedConvertedConcreteId<
169 AssetIdForPoolAssets,
170 Balance,
171 StartsWith<PoolAssetsPalletLocation>,
172 AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation>,
173 TryConvertInto,
174 >;
175
176pub struct PoolAdapter<Runtime>(PhantomData<Runtime>);
179impl<
180 Runtime: pallet_asset_conversion::Config<PoolId = (L, L), AssetKind = L>,
181 L: TryFrom<Location> + TryInto<Location> + Clone + Decode + EncodeLike + PartialEq,
182 > PoolAdapter<Runtime>
183{
184 pub fn get_assets_in_pool_with(asset: Location) -> Result<Vec<AssetId>, ()> {
195 let asset: L = asset.try_into().map_err(|_| ())?;
197 Self::iter_assets_in_pool_with(&asset)
198 .map(|location| {
199 location.try_into().map_err(|_| ()).map(AssetId)
201 })
202 .collect::<Result<Vec<_>, _>>()
203 }
204
205 pub fn quote_price_tokens_for_exact_tokens(
211 asset_1: Location,
212 asset_2: Location,
213 amount: Runtime::Balance,
214 include_fees: bool,
215 ) -> Result<Option<Runtime::Balance>, ()> {
216 let asset_1: L = asset_1.try_into().map_err(|_| ())?;
218 let asset_2: L = asset_2.try_into().map_err(|_| ())?;
219
220 Ok(pallet_asset_conversion::Pallet::<Runtime>::quote_price_tokens_for_exact_tokens(
222 asset_1,
223 asset_2,
224 amount,
225 include_fees,
226 ))
227 }
228
229 pub fn iter_assets_in_pool_with(asset: &L) -> impl Iterator<Item = L> + '_ {
231 pallet_asset_conversion::Pools::<Runtime>::iter_keys().filter_map(|(asset_1, asset_2)| {
232 if asset_1 == *asset {
233 Some(asset_2)
234 } else if asset_2 == *asset {
235 Some(asset_1)
236 } else {
237 None
238 }
239 })
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246 use sp_runtime::traits::MaybeEquivalence;
247 use xcm_builder::{StartsWithExplicitGlobalConsensus, WithLatestLocationConverter};
248 use xcm_executor::traits::{Error as MatchError, MatchesFungibles};
249
250 #[test]
251 fn asset_id_for_trust_backed_assets_convert_works() {
252 frame_support::parameter_types! {
253 pub TrustBackedAssetsPalletLocation: Location = Location::new(5, [PalletInstance(13)]);
254 }
255 let local_asset_id = 123456789 as AssetIdForTrustBackedAssets;
256 let expected_reverse_ref =
257 Location::new(5, [PalletInstance(13), GeneralIndex(local_asset_id.into())]);
258
259 assert_eq!(
260 AssetIdForTrustBackedAssetsConvert::<TrustBackedAssetsPalletLocation>::convert_back(
261 &local_asset_id
262 )
263 .unwrap(),
264 expected_reverse_ref
265 );
266 assert_eq!(
267 AssetIdForTrustBackedAssetsConvert::<TrustBackedAssetsPalletLocation>::convert(
268 &expected_reverse_ref
269 )
270 .unwrap(),
271 local_asset_id
272 );
273 }
274
275 #[test]
276 fn trust_backed_assets_match_fungibles_works() {
277 frame_support::parameter_types! {
278 pub TrustBackedAssetsPalletLocation: Location = Location::new(0, [PalletInstance(13)]);
279 }
280 type TrustBackedAssetsConvert =
282 TrustBackedAssetsConvertedConcreteId<TrustBackedAssetsPalletLocation, u128>;
283
284 let test_data = vec![
285 (ma_1000(0, [PalletInstance(13)].into()), Err(MatchError::AssetIdConversionFailed)),
287 (
288 ma_1000(0, [PalletInstance(13), GeneralKey { data: [0; 32], length: 32 }].into()),
289 Err(MatchError::AssetIdConversionFailed),
290 ),
291 (
292 ma_1000(0, [PalletInstance(13), Parachain(1000)].into()),
293 Err(MatchError::AssetIdConversionFailed),
294 ),
295 (ma_1000(0, [PalletInstance(13), GeneralIndex(1234)].into()), Ok((1234, 1000))),
297 (
298 ma_1000(0, [PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222)].into()),
299 Ok((1234, 1000)),
300 ),
301 (
302 ma_1000(
303 0,
304 [
305 PalletInstance(13),
306 GeneralIndex(1234),
307 GeneralIndex(2222),
308 GeneralKey { data: [0; 32], length: 32 },
309 ]
310 .into(),
311 ),
312 Ok((1234, 1000)),
313 ),
314 (
316 ma_1000(0, [PalletInstance(77), GeneralIndex(1234)].into()),
317 Err(MatchError::AssetNotHandled),
318 ),
319 (
320 ma_1000(0, [PalletInstance(77), GeneralIndex(1234), GeneralIndex(2222)].into()),
321 Err(MatchError::AssetNotHandled),
322 ),
323 (
325 ma_1000(1, [PalletInstance(13), GeneralIndex(1234)].into()),
326 Err(MatchError::AssetNotHandled),
327 ),
328 (
329 ma_1000(1, [PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222)].into()),
330 Err(MatchError::AssetNotHandled),
331 ),
332 (
333 ma_1000(1, [PalletInstance(77), GeneralIndex(1234)].into()),
334 Err(MatchError::AssetNotHandled),
335 ),
336 (
337 ma_1000(1, [PalletInstance(77), GeneralIndex(1234), GeneralIndex(2222)].into()),
338 Err(MatchError::AssetNotHandled),
339 ),
340 (
342 ma_1000(2, [PalletInstance(13), GeneralIndex(1234)].into()),
343 Err(MatchError::AssetNotHandled),
344 ),
345 (
346 ma_1000(2, [PalletInstance(13), GeneralIndex(1234), GeneralIndex(2222)].into()),
347 Err(MatchError::AssetNotHandled),
348 ),
349 (ma_1000(0, [PalletInstance(77)].into()), Err(MatchError::AssetNotHandled)),
351 (ma_1000(1, [PalletInstance(13)].into()), Err(MatchError::AssetNotHandled)),
352 (ma_1000(2, [PalletInstance(13)].into()), Err(MatchError::AssetNotHandled)),
353 ];
354
355 for (asset, expected_result) in test_data {
356 assert_eq!(
357 <TrustBackedAssetsConvert as MatchesFungibles<AssetIdForTrustBackedAssets, u128>>::matches_fungibles(&asset.clone().try_into().unwrap()),
358 expected_result, "asset: {:?}", asset);
359 }
360 }
361
362 #[test]
363 fn foreign_assets_converted_concrete_id_converter_works() {
364 frame_support::parameter_types! {
365 pub Parachain100Pattern: Location = Location::new(1, [Parachain(100)]);
366 pub UniversalLocationNetworkId: NetworkId = NetworkId::ByGenesis([9; 32]);
367 }
368
369 type Convert = ForeignAssetsConvertedConcreteId<
371 (
372 StartsWith<Parachain100Pattern>,
373 StartsWithExplicitGlobalConsensus<UniversalLocationNetworkId>,
374 ),
375 u128,
376 xcm::v4::Location,
377 WithLatestLocationConverter<xcm::v4::Location>,
378 >;
379
380 let test_data = vec![
381 (ma_1000(0, Here), Err(MatchError::AssetNotHandled)),
383 (ma_1000(0, [Parachain(100)].into()), Err(MatchError::AssetNotHandled)),
384 (
385 ma_1000(0, [PalletInstance(13), GeneralIndex(1234)].into()),
386 Err(MatchError::AssetNotHandled),
387 ),
388 (ma_1000(1, Here), Err(MatchError::AssetNotHandled)),
390 (ma_1000(1, [Parachain(100)].into()), Err(MatchError::AssetNotHandled)),
392 (
393 ma_1000(1, [Parachain(100), GeneralIndex(1234)].into()),
394 Err(MatchError::AssetNotHandled),
395 ),
396 (
397 ma_1000(1, [Parachain(100), PalletInstance(13), GeneralIndex(1234)].into()),
398 Err(MatchError::AssetNotHandled),
399 ),
400 (
402 ma_1000(1, [GlobalConsensus(NetworkId::ByGenesis([9; 32]))].into()),
403 Err(MatchError::AssetNotHandled),
404 ),
405 (
406 ma_1000(2, [GlobalConsensus(NetworkId::ByGenesis([9; 32]))].into()),
407 Err(MatchError::AssetNotHandled),
408 ),
409 (
410 ma_1000(
411 2,
412 [
413 GlobalConsensus(NetworkId::ByGenesis([9; 32])),
414 Parachain(200),
415 GeneralIndex(1234),
416 ]
417 .into(),
418 ),
419 Err(MatchError::AssetNotHandled),
420 ),
421 (
423 ma_1000(1, [Parachain(200)].into()),
424 Ok((xcm::v4::Location::new(1, [xcm::v4::Junction::Parachain(200)]), 1000)),
425 ),
426 (
427 ma_1000(2, [Parachain(200)].into()),
428 Ok((xcm::v4::Location::new(2, [xcm::v4::Junction::Parachain(200)]), 1000)),
429 ),
430 (
431 ma_1000(1, [Parachain(200), GeneralIndex(1234)].into()),
432 Ok((
433 xcm::v4::Location::new(
434 1,
435 [xcm::v4::Junction::Parachain(200), xcm::v4::Junction::GeneralIndex(1234)],
436 ),
437 1000,
438 )),
439 ),
440 (
441 ma_1000(2, [Parachain(200), GeneralIndex(1234)].into()),
442 Ok((
443 xcm::v4::Location::new(
444 2,
445 [xcm::v4::Junction::Parachain(200), xcm::v4::Junction::GeneralIndex(1234)],
446 ),
447 1000,
448 )),
449 ),
450 (
451 ma_1000(2, [GlobalConsensus(NetworkId::ByGenesis([7; 32]))].into()),
452 Ok((
453 xcm::v4::Location::new(
454 2,
455 [xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::ByGenesis(
456 [7; 32],
457 ))],
458 ),
459 1000,
460 )),
461 ),
462 (
463 ma_1000(
464 2,
465 [
466 GlobalConsensus(NetworkId::ByGenesis([7; 32])),
467 Parachain(200),
468 GeneralIndex(1234),
469 ]
470 .into(),
471 ),
472 Ok((
473 xcm::v4::Location::new(
474 2,
475 [
476 xcm::v4::Junction::GlobalConsensus(xcm::v4::NetworkId::ByGenesis(
477 [7; 32],
478 )),
479 xcm::v4::Junction::Parachain(200),
480 xcm::v4::Junction::GeneralIndex(1234),
481 ],
482 ),
483 1000,
484 )),
485 ),
486 ];
487
488 for (asset, expected_result) in test_data {
489 assert_eq!(
490 <Convert as MatchesFungibles<xcm::v4::Location, u128>>::matches_fungibles(
491 &asset.clone().try_into().unwrap()
492 ),
493 expected_result,
494 "asset: {:?}",
495 asset
496 );
497 }
498 }
499
500 fn ma_1000(parents: u8, interior: Junctions) -> Asset {
502 (Location::new(parents, interior), 1000).into()
503 }
504}