1use core::{marker::PhantomData, result};
20use frame_support::traits::{Contains, Get};
21use sp_runtime::traits::MaybeEquivalence;
22use xcm::latest::prelude::*;
23use xcm_executor::traits::{
24 Error as MatchError, MatchesFungibles, MatchesInstance, MatchesNonFungible, MatchesNonFungibles,
25};
26
27pub struct AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId, L = Location>(
31 PhantomData<(Prefix, AssetId, ConvertAssetId, L)>,
32);
33impl<
34 Prefix: Get<L>,
35 AssetId: Clone,
36 ConvertAssetId: MaybeEquivalence<u128, AssetId>,
37 L: TryInto<Location> + TryFrom<Location> + Clone,
38 > MaybeEquivalence<L, AssetId> for AsPrefixedGeneralIndex<Prefix, AssetId, ConvertAssetId, L>
39{
40 fn convert(id: &L) -> Option<AssetId> {
41 let prefix = Prefix::get();
42 let latest_prefix: Location = prefix.try_into().ok()?;
43 let latest_id: Location = (*id).clone().try_into().ok()?;
44 if latest_prefix.parent_count() != latest_id.parent_count() ||
45 latest_prefix
46 .interior()
47 .iter()
48 .enumerate()
49 .any(|(index, junction)| latest_id.interior().at(index) != Some(junction))
50 {
51 return None;
52 }
53 match latest_id.interior().at(latest_prefix.interior().len()) {
54 Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert(&id),
55 _ => None,
56 }
57 }
58 fn convert_back(what: &AssetId) -> Option<L> {
59 let location = Prefix::get();
60 let mut latest_location: Location = location.try_into().ok()?;
61 let id = ConvertAssetId::convert_back(what)?;
62 latest_location.push_interior(Junction::GeneralIndex(id)).ok()?;
63 latest_location.try_into().ok()
64 }
65}
66
67pub struct ConvertedConcreteId<AssetId, Balance, ConvertAssetId, ConvertOther>(
68 PhantomData<(AssetId, Balance, ConvertAssetId, ConvertOther)>,
69);
70impl<
71 AssetId: Clone,
72 Balance: Clone,
73 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
74 ConvertBalance: MaybeEquivalence<u128, Balance>,
75 > MatchesFungibles<AssetId, Balance>
76 for ConvertedConcreteId<AssetId, Balance, ConvertAssetId, ConvertBalance>
77{
78 fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), MatchError> {
79 let (amount, id) = match (&a.fun, &a.id) {
80 (Fungible(ref amount), AssetId(ref id)) => (amount, id),
81 _ => return Err(MatchError::AssetNotHandled),
82 };
83 let what = ConvertAssetId::convert(id).ok_or(MatchError::AssetIdConversionFailed)?;
84 let amount =
85 ConvertBalance::convert(amount).ok_or(MatchError::AmountToBalanceConversionFailed)?;
86 Ok((what, amount))
87 }
88}
89impl<
90 ClassId: Clone,
91 InstanceId: Clone,
92 ConvertClassId: MaybeEquivalence<Location, ClassId>,
93 ConvertInstanceId: MaybeEquivalence<AssetInstance, InstanceId>,
94 > MatchesNonFungibles<ClassId, InstanceId>
95 for ConvertedConcreteId<ClassId, InstanceId, ConvertClassId, ConvertInstanceId>
96{
97 fn matches_nonfungibles(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> {
98 let (instance, class) = match (&a.fun, &a.id) {
99 (NonFungible(ref instance), AssetId(ref class)) => (instance, class),
100 _ => return Err(MatchError::AssetNotHandled),
101 };
102 let what = ConvertClassId::convert(class).ok_or(MatchError::AssetIdConversionFailed)?;
103 let instance =
104 ConvertInstanceId::convert(instance).ok_or(MatchError::InstanceConversionFailed)?;
105 Ok((what, instance))
106 }
107}
108
109#[deprecated = "Use `ConvertedConcreteId` instead"]
110pub type ConvertedConcreteAssetId<A, B, C, O> = ConvertedConcreteId<A, B, C, O>;
111
112pub struct MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther>(
113 PhantomData<(AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther)>,
114);
115impl<
116 AssetId: Clone,
117 Balance: Clone,
118 MatchAssetId: Contains<Location>,
119 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
120 ConvertBalance: MaybeEquivalence<u128, Balance>,
121 > MatchesFungibles<AssetId, Balance>
122 for MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
123{
124 fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), MatchError> {
125 let (amount, id) = match (&a.fun, &a.id) {
126 (Fungible(ref amount), AssetId(ref id)) if MatchAssetId::contains(id) => (amount, id),
127 _ => return Err(MatchError::AssetNotHandled),
128 };
129 let what = ConvertAssetId::convert(id).ok_or(MatchError::AssetIdConversionFailed)?;
130 let amount =
131 ConvertBalance::convert(amount).ok_or(MatchError::AmountToBalanceConversionFailed)?;
132 Ok((what, amount))
133 }
134}
135impl<
136 ClassId: Clone,
137 InstanceId: Clone,
138 MatchClassId: Contains<Location>,
139 ConvertClassId: MaybeEquivalence<Location, ClassId>,
140 ConvertInstanceId: MaybeEquivalence<AssetInstance, InstanceId>,
141 > MatchesNonFungibles<ClassId, InstanceId>
142 for MatchedConvertedConcreteId<
143 ClassId,
144 InstanceId,
145 MatchClassId,
146 ConvertClassId,
147 ConvertInstanceId,
148 >
149{
150 fn matches_nonfungibles(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> {
151 let (instance, class) = match (&a.fun, &a.id) {
152 (NonFungible(ref instance), AssetId(ref class)) if MatchClassId::contains(class) => {
153 (instance, class)
154 },
155 _ => return Err(MatchError::AssetNotHandled),
156 };
157 let what = ConvertClassId::convert(class).ok_or(MatchError::AssetIdConversionFailed)?;
158 let instance =
159 ConvertInstanceId::convert(instance).ok_or(MatchError::InstanceConversionFailed)?;
160 Ok((what, instance))
161 }
162}
163
164pub struct MatchInClassInstances<Matcher>(PhantomData<Matcher>);
173
174impl<ClassId, InstanceId, Matcher: MatchesNonFungibles<ClassId, InstanceId>>
175 MatchesInstance<(ClassId, InstanceId)> for MatchInClassInstances<Matcher>
176{
177 fn matches_instance(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> {
178 Matcher::matches_nonfungibles(a)
179 }
180}
181
182pub struct MatchClasslessInstances<Matcher>(PhantomData<Matcher>);
190
191impl<InstanceId, Matcher: MatchesNonFungible<InstanceId>> MatchesInstance<InstanceId>
192 for MatchClasslessInstances<Matcher>
193{
194 fn matches_instance(a: &Asset) -> result::Result<InstanceId, MatchError> {
195 Matcher::matches_nonfungible(a).ok_or(MatchError::AssetNotHandled)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 use xcm_executor::traits::JustTry;
204
205 struct OnlyParentZero;
206 impl Contains<Location> for OnlyParentZero {
207 fn contains(a: &Location) -> bool {
208 match a {
209 Location { parents: 0, .. } => true,
210 _ => false,
211 }
212 }
213 }
214
215 #[test]
216 fn matched_converted_concrete_id_for_fungibles_works() {
217 type AssetIdForTrustBackedAssets = u32;
218 type Balance = u128;
219 frame_support::parameter_types! {
220 pub TrustBackedAssetsPalletLocation: Location = PalletInstance(50).into();
221 }
222
223 type Converter = MatchedConvertedConcreteId<
225 AssetIdForTrustBackedAssets,
226 Balance,
227 OnlyParentZero,
228 AsPrefixedGeneralIndex<
229 TrustBackedAssetsPalletLocation,
230 AssetIdForTrustBackedAssets,
231 JustTry,
232 >,
233 JustTry,
234 >;
235 assert_eq!(
236 TrustBackedAssetsPalletLocation::get(),
237 Location { parents: 0, interior: [PalletInstance(50)].into() }
238 );
239
240 assert_eq!(
242 Converter::matches_fungibles(&Asset {
243 id: AssetId(Location::new(1, [PalletInstance(50), GeneralIndex(1)])),
244 fun: Fungible(12345),
245 }),
246 Err(MatchError::AssetNotHandled)
247 );
248
249 assert_eq!(
251 Converter::matches_fungibles(&Asset {
252 id: AssetId(Location::new(
253 0,
254 [PalletInstance(50), GeneralKey { length: 1, data: [1; 32] }]
255 )),
256 fun: Fungible(12345),
257 }),
258 Err(MatchError::AssetIdConversionFailed)
259 );
260
261 assert_eq!(
263 Converter::matches_fungibles(&Asset {
264 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
265 fun: NonFungible(Index(54321)),
266 }),
267 Err(MatchError::AssetNotHandled)
268 );
269
270 assert_eq!(
272 Converter::matches_fungibles(&Asset {
273 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
274 fun: Fungible(12345),
275 }),
276 Ok((1, 12345))
277 );
278 }
279
280 #[test]
281 fn matched_converted_concrete_id_for_nonfungibles_works() {
282 type ClassId = u32;
283 type ClassInstanceId = u64;
284 frame_support::parameter_types! {
285 pub TrustBackedAssetsPalletLocation: Location = PalletInstance(50).into();
286 }
287
288 struct ClassInstanceIdConverter;
290 impl MaybeEquivalence<AssetInstance, ClassInstanceId> for ClassInstanceIdConverter {
291 fn convert(value: &AssetInstance) -> Option<ClassInstanceId> {
292 (*value).try_into().ok()
293 }
294
295 fn convert_back(value: &ClassInstanceId) -> Option<AssetInstance> {
296 Some(AssetInstance::from(*value))
297 }
298 }
299
300 type Converter = MatchedConvertedConcreteId<
301 ClassId,
302 ClassInstanceId,
303 OnlyParentZero,
304 AsPrefixedGeneralIndex<TrustBackedAssetsPalletLocation, ClassId, JustTry>,
305 ClassInstanceIdConverter,
306 >;
307 assert_eq!(
308 TrustBackedAssetsPalletLocation::get(),
309 Location { parents: 0, interior: [PalletInstance(50)].into() }
310 );
311
312 assert_eq!(
314 Converter::matches_nonfungibles(&Asset {
315 id: AssetId(Location::new(1, [PalletInstance(50), GeneralIndex(1)])),
316 fun: NonFungible(Index(54321)),
317 }),
318 Err(MatchError::AssetNotHandled)
319 );
320
321 assert_eq!(
323 Converter::matches_nonfungibles(&Asset {
324 id: AssetId(Location::new(
325 0,
326 [PalletInstance(50), GeneralKey { length: 1, data: [1; 32] }]
327 )),
328 fun: NonFungible(Index(54321)),
329 }),
330 Err(MatchError::AssetIdConversionFailed)
331 );
332
333 assert_eq!(
335 Converter::matches_nonfungibles(&Asset {
336 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
337 fun: Fungible(12345),
338 }),
339 Err(MatchError::AssetNotHandled)
340 );
341
342 assert_eq!(
344 Converter::matches_nonfungibles(&Asset {
345 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
346 fun: NonFungible(Index(54321)),
347 }),
348 Ok((1, 54321))
349 );
350 }
351}