1use alloc::vec::Vec;
21use core::marker::PhantomData;
22use frame_support::traits::{Contains, ContainsPair, Get};
23use xcm::latest::{Asset, AssetFilter, AssetId, Location, WildAsset};
24
25pub struct NativeAsset;
27impl ContainsPair<Asset, Location> for NativeAsset {
28 fn contains(asset: &Asset, origin: &Location) -> bool {
29 tracing::trace!(target: "xcm::contains", ?asset, ?origin, "NativeAsset");
30 matches!(asset.id, AssetId(ref id) if id == origin)
31 }
32}
33
34pub struct Case<T>(PhantomData<T>);
36impl<T: Get<(AssetFilter, Location)>> ContainsPair<Asset, Location> for Case<T> {
37 fn contains(asset: &Asset, origin: &Location) -> bool {
38 tracing::trace!(target: "xcm::contains", ?asset, ?origin, "Case asset");
39 let (a, o) = T::get();
40 a.matches(asset) && &o == origin
41 }
42}
43
44pub struct LocationWithAssetFilters<LocationFilter, AssetFilters>(
48 core::marker::PhantomData<(LocationFilter, AssetFilters)>,
49);
50impl<LocationFilter: Contains<Location>, AssetFilters: Get<Vec<AssetFilter>>>
51 Contains<(Location, Vec<Asset>)> for LocationWithAssetFilters<LocationFilter, AssetFilters>
52{
53 fn contains((location, assets): &(Location, Vec<Asset>)) -> bool {
54 tracing::trace!(target: "xcm::contains", ?location, ?assets, "LocationWithAssetFilters");
55
56 if !LocationFilter::contains(location) {
58 return false
59 }
60
61 let filters = AssetFilters::get();
63 assets.iter().all(|asset| {
64 for filter in &filters {
65 if filter.matches(asset) {
66 return true
67 }
68 }
69 false
70 })
71 }
72}
73
74pub struct AllAssets;
77impl Get<Vec<AssetFilter>> for AllAssets {
78 fn get() -> Vec<AssetFilter> {
79 alloc::vec![AssetFilter::Wild(WildAsset::All)]
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use frame_support::traits::Equals;
87 use xcm::latest::prelude::*;
88
89 #[test]
90 fn location_with_asset_filters_works() {
91 frame_support::parameter_types! {
92 pub ParaA: Location = Location::new(1, [Parachain(1001)]);
93 pub ParaB: Location = Location::new(1, [Parachain(1002)]);
94 pub ParaC: Location = Location::new(1, [Parachain(1003)]);
95
96 pub AssetXLocation: Location = Location::new(1, [GeneralIndex(1111)]);
97 pub AssetYLocation: Location = Location::new(1, [GeneralIndex(2222)]);
98 pub AssetZLocation: Location = Location::new(1, [GeneralIndex(3333)]);
99
100 pub OnlyAssetXOrAssetY: alloc::vec::Vec<AssetFilter> = alloc::vec![
101 Wild(AllOf { fun: WildFungible, id: AssetId(AssetXLocation::get()) }),
102 Wild(AllOf { fun: WildFungible, id: AssetId(AssetYLocation::get()) }),
103 ];
104 pub OnlyAssetZ: alloc::vec::Vec<AssetFilter> = alloc::vec![
105 Wild(AllOf { fun: WildFungible, id: AssetId(AssetZLocation::get()) })
106 ];
107 }
108
109 let test_data: Vec<(Location, Vec<Asset>, bool)> = vec![
110 (ParaA::get(), vec![(AssetXLocation::get(), 1).into()], true),
111 (ParaA::get(), vec![(AssetYLocation::get(), 1).into()], true),
112 (ParaA::get(), vec![(AssetZLocation::get(), 1).into()], false),
113 (
114 ParaA::get(),
115 vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
116 true,
117 ),
118 (
119 ParaA::get(),
120 vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
121 false,
122 ),
123 (
124 ParaA::get(),
125 vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
126 false,
127 ),
128 (
129 ParaA::get(),
130 vec![
131 (AssetXLocation::get(), 1).into(),
132 (AssetYLocation::get(), 1).into(),
133 (AssetZLocation::get(), 1).into(),
134 ],
135 false,
136 ),
137 (ParaB::get(), vec![(AssetXLocation::get(), 1).into()], false),
138 (ParaB::get(), vec![(AssetYLocation::get(), 1).into()], false),
139 (ParaB::get(), vec![(AssetZLocation::get(), 1).into()], true),
140 (
141 ParaB::get(),
142 vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
143 false,
144 ),
145 (
146 ParaB::get(),
147 vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
148 false,
149 ),
150 (
151 ParaB::get(),
152 vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
153 false,
154 ),
155 (
156 ParaB::get(),
157 vec![
158 (AssetXLocation::get(), 1).into(),
159 (AssetYLocation::get(), 1).into(),
160 (AssetZLocation::get(), 1).into(),
161 ],
162 false,
163 ),
164 (ParaC::get(), vec![(AssetXLocation::get(), 1).into()], true),
165 (ParaC::get(), vec![(AssetYLocation::get(), 1).into()], true),
166 (ParaC::get(), vec![(AssetZLocation::get(), 1).into()], true),
167 (
168 ParaC::get(),
169 vec![(AssetXLocation::get(), 1).into(), (AssetYLocation::get(), 1).into()],
170 true,
171 ),
172 (
173 ParaC::get(),
174 vec![(AssetXLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
175 true,
176 ),
177 (
178 ParaC::get(),
179 vec![(AssetYLocation::get(), 1).into(), (AssetZLocation::get(), 1).into()],
180 true,
181 ),
182 (
183 ParaC::get(),
184 vec![
185 (AssetXLocation::get(), 1).into(),
186 (AssetYLocation::get(), 1).into(),
187 (AssetZLocation::get(), 1).into(),
188 ],
189 true,
190 ),
191 ];
192
193 type Filter = (
194 LocationWithAssetFilters<Equals<ParaA>, OnlyAssetXOrAssetY>,
196 LocationWithAssetFilters<Equals<ParaB>, OnlyAssetZ>,
198 LocationWithAssetFilters<Equals<ParaC>, AllAssets>,
200 );
201
202 for (location, assets, expected_result) in test_data {
203 assert_eq!(
204 Filter::contains(&(location.clone(), assets.clone())),
205 expected_result,
206 "expected_result: {expected_result} not matched for (location, assets): ({:?}, {:?})!", location, assets,
207 )
208 }
209 }
210}