1use core::marker::PhantomData;
21use frame_support::{
22 ensure, parameter_types,
23 traits::{
24 tokens::asset_ops::{
25 common_strategies::{
26 AutoId, CanCreate, ConfigValue, ConfigValueMarker, DeriveAndReportId, Owner,
27 WithConfig,
28 },
29 AssetDefinition, Create, Inspect,
30 },
31 Incrementable,
32 },
33};
34use sp_runtime::{
35 traits::{Convert, TypedGet},
36 DispatchError, DispatchResult,
37};
38use xcm::latest::prelude::*;
39use xcm_builder::unique_instances::NonFungibleAsset;
40use xcm_executor::traits::{ConvertLocation, Error, MatchesInstance};
41
42pub trait DerivativesRegistry<Original, Derivative> {
50 fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult;
51
52 fn try_deregister_derivative_of(original: &Original) -> DispatchResult;
53
54 fn get_derivative(original: &Original) -> Result<Derivative, DispatchError>;
55
56 fn get_original(derivative: &Derivative) -> Result<Original, DispatchError>;
57}
58
59pub struct OriginalToDerivativeConvert<R>(PhantomData<R>);
62impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
63 Convert<Original, Result<Derivative, DispatchError>> for OriginalToDerivativeConvert<R>
64{
65 fn convert(a: Original) -> Result<Derivative, DispatchError> {
66 R::get_derivative(&a)
67 }
68}
69
70pub struct DerivativeToOriginalConvert<R>(PhantomData<R>);
73impl<Original, Derivative, R: DerivativesRegistry<Original, Derivative>>
74 Convert<Derivative, Result<Original, DispatchError>> for DerivativeToOriginalConvert<R>
75{
76 fn convert(a: Derivative) -> Result<Original, DispatchError> {
77 R::get_original(&a)
78 }
79}
80
81pub struct RegisterDerivative<R, CreateOp>(PhantomData<(R, CreateOp)>);
86impl<Original, Derivative, R, CreateOp> Create<DeriveAndReportId<Original, Derivative>>
87 for RegisterDerivative<R, CreateOp>
88where
89 Original: Clone,
90 R: DerivativesRegistry<Original, Derivative>,
91 CreateOp: Create<DeriveAndReportId<Original, Derivative>>,
92{
93 fn create(
94 id_assignment: DeriveAndReportId<Original, Derivative>,
95 ) -> Result<Derivative, DispatchError> {
96 let original = id_assignment.params;
97 let derivative = CreateOp::create(DeriveAndReportId::from(original.clone()))?;
98 R::try_register_derivative(&original, &derivative)?;
99
100 Ok(derivative)
101 }
102}
103impl<Original, Derivative, R, Config, CreateOp>
104 Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>
105 for RegisterDerivative<R, CreateOp>
106where
107 Original: Clone,
108 R: DerivativesRegistry<Original, Derivative>,
109 Config: ConfigValueMarker,
110 CreateOp: Create<WithConfig<Config, DeriveAndReportId<Original, Derivative>>>,
111{
112 fn create(
113 strategy: WithConfig<Config, DeriveAndReportId<Original, Derivative>>,
114 ) -> Result<Derivative, DispatchError> {
115 let WithConfig { config, extra: id_assignment } = strategy;
116 let original = id_assignment.params;
117 let derivative =
118 CreateOp::create(WithConfig::new(config, DeriveAndReportId::from(original.clone())))?;
119 R::try_register_derivative(&original, &derivative)?;
120
121 Ok(derivative)
122 }
123}
124impl<R, CreateOp: AssetDefinition> AssetDefinition for RegisterDerivative<R, CreateOp> {
125 type Id = CreateOp::Id;
126}
127impl<R, CreateOp, Condition> Inspect<CanCreate<Condition>> for RegisterDerivative<R, CreateOp>
128where
129 CreateOp: Inspect<CanCreate<Condition>>,
130{
131 fn inspect(id: &Self::Id, can_create: CanCreate<Condition>) -> Result<bool, DispatchError> {
132 CreateOp::inspect(id, can_create)
133 }
134}
135
136pub trait IterDerivativesRegistry<Original, Derivative> {
138 fn iter_originals() -> impl Iterator<Item = Original>;
139
140 fn iter_derivatives() -> impl Iterator<Item = Derivative>;
141
142 fn iter() -> impl Iterator<Item = (Original, Derivative)>;
143}
144
145pub trait DerivativesExtra<Derivative, Extra> {
147 fn get_derivative_extra(derivative: &Derivative) -> Option<Extra>;
148
149 fn set_derivative_extra(derivative: &Derivative, extra: Option<Extra>) -> DispatchResult;
150}
151
152pub struct ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>(
160 PhantomData<(Derivative, Extra, Registry, CreateOp)>,
161);
162impl<Derivative, Extra, Registry, CreateOp>
163 ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
164where
165 Extra: Incrementable,
166 Registry: DerivativesExtra<Derivative, Extra>,
167{
168 fn get_derivative_extra(derivative: &Derivative) -> Result<Extra, DispatchError> {
169 Registry::get_derivative_extra(derivative)
170 .or(Extra::initial_value())
171 .ok_or(DispatchError::Other("ConcatIncrementalExtra: no derivative extra is found"))
172 }
173}
174impl<Derivative, Extra, ReportedId, Registry, CreateOp>
175 Create<DeriveAndReportId<Derivative, ReportedId>>
176 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
177where
178 Extra: Incrementable,
179 Registry: DerivativesExtra<Derivative, Extra>,
180 CreateOp: Create<DeriveAndReportId<(Derivative, Extra), ReportedId>>,
181{
182 fn create(
183 id_assignment: DeriveAndReportId<Derivative, ReportedId>,
184 ) -> Result<ReportedId, DispatchError> {
185 let derivative = id_assignment.params;
186
187 let id = Registry::get_derivative_extra(&derivative).or(Extra::initial_value()).ok_or(
188 DispatchError::Other(
189 "ConcatIncrementalExtra: unable to initialize incremental derivative extra",
190 ),
191 )?;
192 let next_id = id
193 .increment()
194 .ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
195
196 Registry::set_derivative_extra(&derivative, Some(next_id))?;
197
198 CreateOp::create(DeriveAndReportId::from((derivative, id)))
199 }
200}
201impl<Config, Derivative, Extra, ReportedId, Registry, CreateOp>
202 Create<WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>>
203 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
204where
205 Config: ConfigValueMarker,
206 Extra: Incrementable,
207 Registry: DerivativesExtra<Derivative, Extra>,
208 CreateOp: Create<WithConfig<Config, DeriveAndReportId<(Derivative, Extra), ReportedId>>>,
209{
210 fn create(
211 strategy: WithConfig<Config, DeriveAndReportId<Derivative, ReportedId>>,
212 ) -> Result<ReportedId, DispatchError> {
213 let WithConfig { config, extra: id_assignment } = strategy;
214 let derivative = id_assignment.params;
215
216 let id = Self::get_derivative_extra(&derivative)?;
217 let next_id = id
218 .increment()
219 .ok_or(DispatchError::Other("ConcatIncrementalExtra: failed to increment the id"))?;
220
221 Registry::set_derivative_extra(&derivative, Some(next_id))?;
222
223 CreateOp::create(WithConfig::new(config, DeriveAndReportId::from((derivative, id))))
224 }
225}
226impl<Derivative, Extra, Registry, CreateOp> AssetDefinition
227 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
228{
229 type Id = Derivative;
230}
231impl<Derivative, Extra, Registry, CreateOp> Inspect<CanCreate>
232 for ConcatIncrementalExtra<Derivative, Extra, Registry, CreateOp>
233where
234 Derivative: Clone,
235 Extra: Incrementable,
236 Registry: DerivativesExtra<Derivative, Extra>,
237 CreateOp: AssetDefinition<Id = (Derivative, Extra)> + Inspect<CanCreate>,
238{
239 fn inspect(id: &Self::Id, can_create: CanCreate) -> Result<bool, DispatchError> {
240 let extra = Self::get_derivative_extra(id)?;
241
242 CreateOp::inspect(&(id.clone(), extra), can_create)
243 }
244}
245
246pub struct MatchDerivativeInstances<Registry>(PhantomData<Registry>);
250impl<Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>, DerivativeId>
251 MatchesInstance<DerivativeId> for MatchDerivativeInstances<Registry>
252{
253 fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
254 match asset.fun {
255 Fungibility::NonFungible(asset_instance) => {
256 Registry::get_derivative(&(asset.id.clone(), asset_instance))
257 .map_err(|_| Error::AssetNotHandled)
258 },
259 Fungibility::Fungible(_) => Err(Error::AssetNotHandled),
260 }
261 }
262}
263
264pub struct EnsureNotDerivativeInstance<Registry, Matcher>(PhantomData<(Registry, Matcher)>);
284impl<
285 Registry: DerivativesRegistry<NonFungibleAsset, DerivativeId>,
286 Matcher: MatchesInstance<DerivativeId>,
287 DerivativeId,
288 > MatchesInstance<DerivativeId> for EnsureNotDerivativeInstance<Registry, Matcher>
289{
290 fn matches_instance(asset: &Asset) -> Result<DerivativeId, Error> {
291 let instance_id = Matcher::matches_instance(asset)?;
292
293 ensure!(Registry::get_original(&instance_id).is_err(), Error::AssetNotHandled);
294
295 Ok(instance_id)
296 }
297}
298
299parameter_types! {
300 pub OwnerConvertedLocationDefaultErr: DispatchError = DispatchError::Other("OwnerConvertedLocation: failed to convert the location");
301}
302
303pub struct OwnerConvertedLocation<CL, IdAssignment, Err = OwnerConvertedLocationDefaultErr>(
306 PhantomData<(CL, IdAssignment, Err)>,
307);
308impl<AccountId, CL, Err, ReportedId>
309 Convert<
310 AssetId,
311 Result<
312 WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
313 DispatchError,
314 >,
315 > for OwnerConvertedLocation<CL, DeriveAndReportId<AssetId, ReportedId>, Err>
316where
317 CL: ConvertLocation<AccountId>,
318 Err: TypedGet,
319 Err::Type: Into<DispatchError>,
320{
321 fn convert(
322 AssetId(location): AssetId,
323 ) -> Result<
324 WithConfig<ConfigValue<Owner<AccountId>>, DeriveAndReportId<AssetId, ReportedId>>,
325 DispatchError,
326 > {
327 CL::convert_location(&location)
328 .map(|account| {
329 WithConfig::new(ConfigValue(account), DeriveAndReportId::from(AssetId(location)))
330 })
331 .ok_or(Err::get().into())
332 }
333}
334impl<AccountId, CL, Err, ReportedId>
335 Convert<
336 AssetId,
337 Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError>,
338 > for OwnerConvertedLocation<CL, AutoId<ReportedId>, Err>
339where
340 CL: ConvertLocation<AccountId>,
341 Err: TypedGet,
342 Err::Type: Into<DispatchError>,
343{
344 fn convert(
345 AssetId(location): AssetId,
346 ) -> Result<WithConfig<ConfigValue<Owner<AccountId>>, AutoId<ReportedId>>, DispatchError> {
347 CL::convert_location(&location)
348 .map(|account| WithConfig::new(ConfigValue(account), AutoId::auto()))
349 .ok_or(Err::get().into())
350 }
351}