1use core::marker::PhantomData;
21use frame_support::{
22 ensure, parameter_types,
23 traits::{
24 tokens::asset_ops::{
25 common_strategies::{
26 AutoId, ConfigValue, ConfigValueMarker, DeriveAndReportId, Owner, WithConfig,
27 },
28 Create,
29 },
30 Incrementable,
31 },
32};
33use sp_runtime::{
34 traits::{Convert, TypedGet},
35 DispatchError, DispatchResult,
36};
37use xcm::latest::prelude::*;
38use xcm_builder::unique_instances::NonFungibleAsset;
39use xcm_executor::traits::{ConvertLocation, Error, MatchesInstance};
40
41pub trait DerivativesRegistry<Original, Derivative> {
49 fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult;
50
51 fn try_deregister_derivative_of(original: &Original) -> DispatchResult;
52
53 fn get_derivative(original: &Original) -> Result<Derivative, DispatchError>;
54
55 fn get_original(derivative: &Derivative) -> Result<Original, DispatchError>;
56}
57
58pub struct OriginalToDerivativeConvert<R>(PhantomData<R>);
61impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
62 Convert<Original, Result<Derivative, DispatchError>> for OriginalToDerivativeConvert<R>
63{
64 fn convert(a: Original) -> Result<Derivative, DispatchError> {
65 R::get_derivative(&a)
66 }
67}
68
69pub struct DerivativeToOriginalConvert<R>(PhantomData<R>);
72impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
73 Convert<Derivative, Result<Original, DispatchError>> for DerivativeToOriginalConvert<R>
74{
75 fn convert(a: Derivative) -> Result<Original, DispatchError> {
76 R::get_original(&a)
77 }
78}
79
80pub struct RegisterDerivative<R, CreateOp>(PhantomData<(R, CreateOp)>);
85impl<Original, Derivative, R, CreateOp> Create<DeriveAndReportId<Original, Derivative>>
86 for RegisterDerivative<R, CreateOp>
87where
88 Original: Clone,
89 R: DerivativesRegistry<Original, Derivative>,
90 CreateOp: Create<DeriveAndReportId<Original, Derivative>>,
91{
92 fn create(
93 id_assignment: DeriveAndReportId<Original, Derivative>,
94 ) -> Result<Derivative, DispatchError> {
95 let original = id_assignment.params;
96 let derivative = CreateOp::create(DeriveAndReportId::from(original.clone()))?;
97 R::try_register_derivative(&original, &derivative)?;
98
99 Ok(derivative)
100 }
101}
102impl<Original, Derivative, R, Config, CreateOp>
103 Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>
104 for RegisterDerivative<R, CreateOp>
105where
106 Original: Clone,
107 R: DerivativesRegistry<Original, Derivative>,
108 Config: ConfigValueMarker,
109 CreateOp: Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>,
110{
111 fn create(
112 strategy: WithConfig<Config, DeriveAndReportId<Original, Derivative>>,
113 ) -> Result<Derivative, DispatchError> {
114 let WithConfig { config, extra: id_assignment } = strategy;
115 let original = id_assignment.params;
116 let derivative =
117 CreateOp::create(WithConfig::new(config, DeriveAndReportId::from(original.clone())))?;
118 R::try_register_derivative(&original, &derivative)?;
119
120 Ok(derivative)
121 }
122}
123
124pub trait IterDerivativesRegistry<Original, Derivative> {
126 fn iter_originals() -> impl Iterator<Item = Original>;
127
128 fn iter_derivatives() -> impl Iterator<Item = Derivative>;
129
130 fn iter() -> impl Iterator<Item = (Original, Derivative)>;
131}
132
133pub trait DerivativesExtra<Derivative, Extra> {
135 fn get_derivative_extra(derivative: &Derivative) -> Option<Extra>;
136
137 fn set_derivative_extra(derivative: &Derivative, extra: Option<Extra>) -> DispatchResult;
138}
139
140pub struct ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>(
148 PhantomData<(Derivative, Extra, Registry, CreateOp)>,
149);
150impl<Derivative, Extra, ReportedId, Registry, CreateOp>
151 Create<DeriveAndReportId<Derivative, ReportedId>>
152 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
153where
154 Extra: Incrementable,
155 Registry: DerivativesExtra<Derivative, Extra>,
156 CreateOp: Create<DeriveAndReportId<(Derivative, Extra), ReportedId>>,
157{
158 fn create(
159 id_assignment: DeriveAndReportId<Derivative, ReportedId>,
160 ) -> Result<ReportedId, DispatchError> {
161 let derivative = id_assignment.params;
162
163 let id = Registry::get_derivative_extra(&derivative).or(Extra::initial_value()).ok_or(
164 DispatchError::Other(
165 "ConcatIncrementalExtra: unable to initialize incremental derivative extra",
166 ),
167 )?;
168 let next_id = id
169 .increment()
170 .ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
171
172 Registry::set_derivative_extra(&derivative, Some(next_id))?;
173
174 CreateOp::create(DeriveAndReportId::from((derivative, id)))
175 }
176}
177impl<Config, Derivative, Extra, ReportedId, Registry, CreateOp>
178 Create<WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>>
179 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
180where
181 Config: ConfigValueMarker,
182 Extra: Incrementable,
183 Registry: DerivativesExtra<Derivative, Extra>,
184 CreateOp: Create<WithConfig<Config, DeriveAndReportId<(Derivative, Extra), ReportedId>>>,
185{
186 fn create(
187 strategy: WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>,
188 ) -> Result<ReportedId, DispatchError> {
189 let WithConfig { config, extra: id_assignment } = strategy;
190 let derivative = id_assignment.params;
191
192 let id = Registry::get_derivative_extra(&derivative)
193 .or(Extra::initial_value())
194 .ok_or(DispatchError::Other("ConcatIncrementalExtra: no derivative extra is found"))?;
195 let next_id = id
196 .increment()
197 .ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
198
199 Registry::set_derivative_extra(&derivative, Some(next_id))?;
200
201 CreateOp::create(WithConfig::new(config, DeriveAndReportId::from((derivative, id))))
202 }
203}
204
205pub struct MatchDerivativeInstances<Registry>(PhantomData<Registry>);
209impl<Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>, DerivativeId>
210 MatchesInstance<DerivativeId> for MatchDerivativeInstances<Registry>
211{
212 fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
213 match asset.fun {
214 Fungibility::NonFungible(asset_instance) =>
215 Registry::get_derivative(&(asset.id.clone(), asset_instance))
216 .map_err(|_| Error::AssetNotHandled),
217 Fungibility::Fungible(_) => Err(Error::AssetNotHandled),
218 }
219 }
220}
221
222pub struct EnsureNotDerivativeInstance<Registry, Matcher>(PhantomData<(Registry, Matcher)>);
242impl<
243 Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>,
244 Matcher: MatchesInstance<DerivativeId>,
245 DerivativeId,
246 > MatchesInstance<DerivativeId> for EnsureNotDerivativeInstance<Registry, Matcher>
247{
248 fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
249 let instance_id = Matcher::matches_instance(asset)?;
250
251 ensure!(Registry::get_original(&instance_id).is_err(), Error::AssetNotHandled);
252
253 Ok(instance_id)
254 }
255}
256
257parameter_types! {
258 pub OwnerConvertedLocationDefaultErr: DispatchError = DispatchError::Other("OwnerConvertedLocation: failed to convert the location");
259}
260
261pub struct OwnerConvertedLocation<CL, IdAssignment, Err = OwnerConvertedLocationDefaultErr>(
264 PhantomData<(CL, IdAssignment, Err)>,
265);
266impl<AccountId, CL, Err, ReportedId>
267 Convert<
268 AssetId,
269 Result<
270 WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
271 DispatchError,
272 >,
273 > for OwnerConvertedLocation<CL, DeriveAndReportId<AssetId, ReportedId>, Err>
274where
275 CL: ConvertLocation<AccountId>,
276 Err: TypedGet,
277 Err::Type: Into<DispatchError>,
278{
279 fn convert(
280 AssetId(location): AssetId,
281 ) -> Result<
282 WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
283 DispatchError,
284 > {
285 CL::convert_location(&location)
286 .map(|account| {
287 WithConfig::new(ConfigValue(account), DeriveAndReportId::from(AssetId(location)))
288 })
289 .ok_or(Err::get().into())
290 }
291}
292impl<AccountId, CL, Err, ReportedId>
293 Convert<
294 AssetId,
295 Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError>,
296 > for OwnerConvertedLocation<CL, AutoId<ReportedId>, Err>
297where
298 CL: ConvertLocation<AccountId>,
299 Err: TypedGet,
300 Err::Type: Into<DispatchError>,
301{
302 fn convert(
303 AssetId(location): AssetId,
304 ) -> Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError> {
305 CL::convert_location(&location)
306 .map(|account| WithConfig::new(ConfigValue(account), AutoId::auto()))
307 .ok_or(Err::get().into())
308 }
309}