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