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
109pub struct MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther>(
110 PhantomData<(AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertOther)>,
111);
112impl<
113 AssetId: Clone,
114 Balance: Clone,
115 MatchAssetId: Contains<Location>,
116 ConvertAssetId: MaybeEquivalence<Location, AssetId>,
117 ConvertBalance: MaybeEquivalence<u128, Balance>,
118 > MatchesFungibles<AssetId, Balance>
119 for MatchedConvertedConcreteId<AssetId, Balance, MatchAssetId, ConvertAssetId, ConvertBalance>
120{
121 fn matches_fungibles(a: &Asset) -> result::Result<(AssetId, Balance), MatchError> {
122 let (amount, id) = match (&a.fun, &a.id) {
123 (Fungible(ref amount), AssetId(ref id)) if MatchAssetId::contains(id) => (amount, id),
124 _ => return Err(MatchError::AssetNotHandled),
125 };
126 let what = ConvertAssetId::convert(id).ok_or(MatchError::AssetIdConversionFailed)?;
127 let amount =
128 ConvertBalance::convert(amount).ok_or(MatchError::AmountToBalanceConversionFailed)?;
129 Ok((what, amount))
130 }
131}
132impl<
133 ClassId: Clone,
134 InstanceId: Clone,
135 MatchClassId: Contains<Location>,
136 ConvertClassId: MaybeEquivalence<Location, ClassId>,
137 ConvertInstanceId: MaybeEquivalence<AssetInstance, InstanceId>,
138 > MatchesNonFungibles<ClassId, InstanceId>
139 for MatchedConvertedConcreteId<
140 ClassId,
141 InstanceId,
142 MatchClassId,
143 ConvertClassId,
144 ConvertInstanceId,
145 >
146{
147 fn matches_nonfungibles(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> {
148 let (instance, class) = match (&a.fun, &a.id) {
149 (NonFungible(ref instance), AssetId(ref class)) if MatchClassId::contains(class) => {
150 (instance, class)
151 },
152 _ => return Err(MatchError::AssetNotHandled),
153 };
154 let what = ConvertClassId::convert(class).ok_or(MatchError::AssetIdConversionFailed)?;
155 let instance =
156 ConvertInstanceId::convert(instance).ok_or(MatchError::InstanceConversionFailed)?;
157 Ok((what, instance))
158 }
159}
160
161pub struct MatchInClassInstances<Matcher>(PhantomData<Matcher>);
170
171impl<ClassId, InstanceId, Matcher: MatchesNonFungibles<ClassId, InstanceId>>
172 MatchesInstance<(ClassId, InstanceId)> for MatchInClassInstances<Matcher>
173{
174 fn matches_instance(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> {
175 Matcher::matches_nonfungibles(a)
176 }
177}
178
179pub struct MatchClasslessInstances<Matcher>(PhantomData<Matcher>);
187
188impl<InstanceId, Matcher: MatchesNonFungible<InstanceId>> MatchesInstance<InstanceId>
189 for MatchClasslessInstances<Matcher>
190{
191 fn matches_instance(a: &Asset) -> result::Result<InstanceId, MatchError> {
192 Matcher::matches_nonfungible(a).ok_or(MatchError::AssetNotHandled)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 use sp_runtime::traits::TryConvertInto;
201
202 struct OnlyParentZero;
203 impl Contains<Location> for OnlyParentZero {
204 fn contains(a: &Location) -> bool {
205 match a {
206 Location { parents: 0, .. } => true,
207 _ => false,
208 }
209 }
210 }
211
212 #[test]
213 fn matched_converted_concrete_id_for_fungibles_works() {
214 type AssetIdForTrustBackedAssets = u32;
215 type Balance = u128;
216 frame_support::parameter_types! {
217 pub TrustBackedAssetsPalletLocation: Location = PalletInstance(50).into();
218 }
219
220 type Converter = MatchedConvertedConcreteId<
222 AssetIdForTrustBackedAssets,
223 Balance,
224 OnlyParentZero,
225 AsPrefixedGeneralIndex<
226 TrustBackedAssetsPalletLocation,
227 AssetIdForTrustBackedAssets,
228 TryConvertInto,
229 >,
230 TryConvertInto,
231 >;
232 assert_eq!(
233 TrustBackedAssetsPalletLocation::get(),
234 Location { parents: 0, interior: [PalletInstance(50)].into() }
235 );
236
237 assert_eq!(
239 Converter::matches_fungibles(&Asset {
240 id: AssetId(Location::new(1, [PalletInstance(50), GeneralIndex(1)])),
241 fun: Fungible(12345),
242 }),
243 Err(MatchError::AssetNotHandled)
244 );
245
246 assert_eq!(
248 Converter::matches_fungibles(&Asset {
249 id: AssetId(Location::new(
250 0,
251 [PalletInstance(50), GeneralKey { length: 1, data: [1; 32] }]
252 )),
253 fun: Fungible(12345),
254 }),
255 Err(MatchError::AssetIdConversionFailed)
256 );
257
258 assert_eq!(
260 Converter::matches_fungibles(&Asset {
261 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
262 fun: NonFungible(Index(54321)),
263 }),
264 Err(MatchError::AssetNotHandled)
265 );
266
267 assert_eq!(
269 Converter::matches_fungibles(&Asset {
270 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
271 fun: Fungible(12345),
272 }),
273 Ok((1, 12345))
274 );
275 }
276
277 #[test]
278 fn matched_converted_concrete_id_for_nonfungibles_works() {
279 type ClassId = u32;
280 type ClassInstanceId = u64;
281 frame_support::parameter_types! {
282 pub TrustBackedAssetsPalletLocation: Location = PalletInstance(50).into();
283 }
284
285 struct ClassInstanceIdConverter;
287 impl MaybeEquivalence<AssetInstance, ClassInstanceId> for ClassInstanceIdConverter {
288 fn convert(value: &AssetInstance) -> Option<ClassInstanceId> {
289 (*value).try_into().ok()
290 }
291
292 fn convert_back(value: &ClassInstanceId) -> Option<AssetInstance> {
293 Some(AssetInstance::from(*value))
294 }
295 }
296
297 type Converter = MatchedConvertedConcreteId<
298 ClassId,
299 ClassInstanceId,
300 OnlyParentZero,
301 AsPrefixedGeneralIndex<TrustBackedAssetsPalletLocation, ClassId, TryConvertInto>,
302 ClassInstanceIdConverter,
303 >;
304 assert_eq!(
305 TrustBackedAssetsPalletLocation::get(),
306 Location { parents: 0, interior: [PalletInstance(50)].into() }
307 );
308
309 assert_eq!(
311 Converter::matches_nonfungibles(&Asset {
312 id: AssetId(Location::new(1, [PalletInstance(50), GeneralIndex(1)])),
313 fun: NonFungible(Index(54321)),
314 }),
315 Err(MatchError::AssetNotHandled)
316 );
317
318 assert_eq!(
320 Converter::matches_nonfungibles(&Asset {
321 id: AssetId(Location::new(
322 0,
323 [PalletInstance(50), GeneralKey { length: 1, data: [1; 32] }]
324 )),
325 fun: NonFungible(Index(54321)),
326 }),
327 Err(MatchError::AssetIdConversionFailed)
328 );
329
330 assert_eq!(
332 Converter::matches_nonfungibles(&Asset {
333 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
334 fun: Fungible(12345),
335 }),
336 Err(MatchError::AssetNotHandled)
337 );
338
339 assert_eq!(
341 Converter::matches_nonfungibles(&Asset {
342 id: AssetId(Location::new(0, [PalletInstance(50), GeneralIndex(1)])),
343 fun: NonFungible(Index(54321)),
344 }),
345 Ok((1, 54321))
346 );
347 }
348}