frame_support/traits/tokens/asset_ops/common_strategies.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! This module contains the common asset ops strategies.
19
20use super::*;
21use codec::{Decode, Encode, MaxEncodedLen};
22use scale_info::TypeInfo;
23use sp_core::RuntimeDebug;
24use sp_runtime::traits::Convert;
25
26/// The `CheckState` is a strategy that accepts an `Inspect` value and the `Inner` strategy.
27///
28/// It is meant to be used when the asset state check should be performed
29/// prior to the `Inner` strategy execution.
30/// **The inspected state must be equal to the provided value.**
31///
32/// The `CheckState` implements all potentially state-mutating strategies that the `Inner`
33/// implements.
34pub struct CheckState<Inspect: InspectStrategy, Inner = NoParams>(pub Inspect::Value, pub Inner);
35impl<Inspect: InspectStrategy, Inner: Default> CheckState<Inspect, Inner> {
36 /// This function creates a `CheckState` strategy.
37 /// The operation that accepts it must check if the provided `expected` value
38 /// equals the in-storage one.
39 ///
40 /// If so, the operation must, in turn, proceed according to the default value of the `Inner`
41 /// strategy.
42 pub fn check(expected: Inspect::Value) -> Self {
43 Self(expected, Default::default())
44 }
45}
46impl<Inspect: InspectStrategy, Inner> CheckState<Inspect, Inner> {
47 /// This function creates a `CheckState` strategy.
48 /// The operation that accepts it must check if the provided `expected` value
49 /// equals the in-storage one.
50 ///
51 /// If so, the operation must, in turn, proceed according to the provided value of the `Inner`
52 /// strategy.
53 pub fn new(expected: Inspect::Value, inner: Inner) -> Self {
54 Self(expected, inner)
55 }
56}
57impl<Inspect: InspectStrategy, Inner: UpdateStrategy> UpdateStrategy
58 for CheckState<Inspect, Inner>
59{
60 type UpdateValue<'u> = Inner::UpdateValue<'u>;
61 type Success = Inner::Success;
62}
63impl<Inspect: InspectStrategy, Inner: CreateStrategy> CreateStrategy
64 for CheckState<Inspect, Inner>
65{
66 type Success = Inner::Success;
67}
68impl<Inspect: InspectStrategy, Inner: DestroyStrategy> DestroyStrategy
69 for CheckState<Inspect, Inner>
70{
71 type Success = Inner::Success;
72}
73impl<Inspect: InspectStrategy, Inner: StashStrategy> StashStrategy for CheckState<Inspect, Inner> {
74 type Success = Inner::Success;
75}
76impl<Inspect: InspectStrategy, Inner: RestoreStrategy> RestoreStrategy
77 for CheckState<Inspect, Inner>
78{
79 type Success = Inner::Success;
80}
81
82/// The `CheckOrigin` is a strategy that accepts a runtime origin and the `Inner` strategy.
83///
84/// It is meant to be used when the origin check should be performed
85/// prior to the `Inner` strategy execution.
86///
87/// The `CheckOrigin` implements all potentially state-mutating strategies that the `Inner`
88/// implements.
89pub struct CheckOrigin<RuntimeOrigin, Inner = NoParams>(pub RuntimeOrigin, pub Inner);
90impl<RuntimeOrigin, Inner: Default> CheckOrigin<RuntimeOrigin, Inner> {
91 /// This function creates a `CheckOrigin` strategy.
92 /// The operation that accepts it must check if the provided `origin` is allowed to perform it.
93 ///
94 /// If so, the operation must, in turn, proceed according to the default value of the `Inner`
95 /// strategy.
96 pub fn check(origin: RuntimeOrigin) -> Self {
97 Self(origin, Default::default())
98 }
99}
100impl<RuntimeOrigin, Inner> CheckOrigin<RuntimeOrigin, Inner> {
101 /// This function creates a `CheckOrigin` strategy.
102 /// The operation that accepts it must check if the provided `origin` is allowed to perform it.
103 ///
104 /// If so, the operation must, in turn, proceed according to the provided value of the `Inner`
105 /// strategy.
106 pub fn new(origin: RuntimeOrigin, inner: Inner) -> Self {
107 Self(origin, inner)
108 }
109}
110
111impl<RuntimeOrigin, Inner: UpdateStrategy> UpdateStrategy for CheckOrigin<RuntimeOrigin, Inner> {
112 type UpdateValue<'u> = Inner::UpdateValue<'u>;
113 type Success = Inner::Success;
114}
115impl<RuntimeOrigin, Inner: CreateStrategy> CreateStrategy for CheckOrigin<RuntimeOrigin, Inner> {
116 type Success = Inner::Success;
117}
118impl<RuntimeOrigin, Inner: DestroyStrategy> DestroyStrategy for CheckOrigin<RuntimeOrigin, Inner> {
119 type Success = Inner::Success;
120}
121impl<RuntimeOrigin, Inner: StashStrategy> StashStrategy for CheckOrigin<RuntimeOrigin, Inner> {
122 type Success = Inner::Success;
123}
124impl<RuntimeOrigin, Inner: RestoreStrategy> RestoreStrategy for CheckOrigin<RuntimeOrigin, Inner> {
125 type Success = Inner::Success;
126}
127
128/// The NoParams represents the simplest state-mutating strategy,
129/// which doesn't require any parameters to perform the operation.
130///
131/// It can be used as the following strategies:
132/// * [`destroy strategy`](DestroyStrategy)
133/// * [`stash strategy`](StashStrategy)
134/// * [`restore strategy`](RestoreStrategy)
135#[derive(Default)]
136pub struct NoParams;
137impl DestroyStrategy for NoParams {
138 type Success = ();
139}
140impl StashStrategy for NoParams {
141 type Success = ();
142}
143impl RestoreStrategy for NoParams {
144 type Success = ();
145}
146
147/// The `Bytes` strategy represents raw state bytes.
148/// It is both an [inspect](InspectStrategy) and [update](UpdateStrategy)
149/// strategy.
150///
151/// * As the inspect strategy, it returns `Vec<u8>`.
152/// * As the update strategy, it accepts `Option<&[u8]>`, where `None` means data removal.
153///
154/// By default, the `Bytes` identifies a byte blob associated with the asset (the only one
155/// blob). However, a user can define several variants of this strategy by supplying the
156/// `Request` type. The `Request` type can also contain additional data (like a byte key) to
157/// identify a certain byte data.
158/// For instance, there can be several named byte attributes. In that case, the `Request` might
159/// be something like `Attribute(/* name: */ String)`.
160pub struct Bytes<Request = ()>(pub Request);
161impl Default for Bytes<()> {
162 fn default() -> Self {
163 Self(())
164 }
165}
166impl<Request> InspectStrategy for Bytes<Request> {
167 type Value = Vec<u8>;
168}
169impl<Request> UpdateStrategy for Bytes<Request> {
170 type UpdateValue<'u> = Option<&'u [u8]>;
171 type Success = ();
172}
173
174/// The `Owner` strategy is both [inspect](InspectStrategy) and [update](UpdateStrategy) strategy
175/// allows getting and setting the owner of an asset.
176#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
177pub struct Owner<AccountId>(PhantomData<AccountId>);
178impl<AccountId> Default for Owner<AccountId> {
179 fn default() -> Self {
180 Self(PhantomData)
181 }
182}
183impl<AccountId> InspectStrategy for Owner<AccountId> {
184 type Value = AccountId;
185}
186impl<AccountId: 'static> UpdateStrategy for Owner<AccountId> {
187 type UpdateValue<'u> = &'u AccountId;
188 type Success = ();
189}
190
191/// The `Admin` strategy is both [inspect](InspectStrategy) and [update](UpdateStrategy) strategy
192/// allows getting and setting the admin of an asset.
193#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
194pub struct Admin<AccountId>(PhantomData<AccountId>);
195impl<AccountId> Default for Admin<AccountId> {
196 fn default() -> Self {
197 Self(PhantomData)
198 }
199}
200impl<AccountId> InspectStrategy for Admin<AccountId> {
201 type Value = AccountId;
202}
203impl<AccountId: 'static> UpdateStrategy for Admin<AccountId> {
204 type UpdateValue<'u> = &'u AccountId;
205 type Success = ();
206}
207
208/// The `Witness` strategy is an [inspect](InspectStrategy) strategy,
209/// which gets the specified `WitnessData` from the asset.
210///
211/// The `WitnessData` can be anything descriptive about the asset that helps perform a related
212/// operation. For instance, a witness could be required to destroy an NFT collection because the
213/// corresponding extrinsic's weight couldn't be known ahead of time without providing, for example,
214/// the number of items within the collection. In this case, the number of items is the witness
215/// data. The said extrinsic, in turn, could use the destroy operation with the `WithWitness`
216/// strategy, which will compare the provided witness with the actual chain state before attempting
217/// the collection destruction.
218#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
219pub struct Witness<WitnessData>(PhantomData<WitnessData>);
220impl<WitnessData> Default for Witness<WitnessData> {
221 fn default() -> Self {
222 Self(PhantomData)
223 }
224}
225impl<WitnessData> InspectStrategy for Witness<WitnessData> {
226 type Value = WitnessData;
227}
228
229/// The operation implementation must check
230/// if the given account owns the asset and act according to the inner strategy.
231pub type IfOwnedBy<AccountId, Inner = NoParams> = CheckState<Owner<AccountId>, Inner>;
232
233/// The operation implementation must check
234/// if the given account owns the asset and only then perform the owner update to the one supplied
235/// to the `Update::update` function.
236pub type ChangeOwnerFrom<AccountId> = CheckState<Owner<AccountId>, Owner<AccountId>>;
237
238/// The operation implementation must check
239/// if the given witness represents the correct state of the asset.
240/// If so, the operation must act according to the inner strategy.
241pub type WithWitness<WitnessData, Inner = NoParams> = CheckState<Witness<WitnessData>, Inner>;
242
243/// The `CanCreate` strategy represents the ability to create an asset.
244/// It is both an [inspect](InspectStrategy) and [update](UpdateStrategy)
245/// strategy.
246///
247/// * As the inspect strategy, it returns `bool`.
248/// * As the update strategy, it accepts `bool`.
249///
250/// By default, this strategy means the ability to create an asset "in general".
251/// However, a user can define several variants of this strategy by supplying the `Condition`
252/// type. Using the `Condition` value, we are formulating the question, "Can this be created
253/// under the given condition?". For instance, "Can **a specific user** create an asset?".
254pub struct CanCreate<Condition = ()>(pub Condition);
255impl Default for CanCreate<()> {
256 fn default() -> Self {
257 Self(())
258 }
259}
260impl<Condition> InspectStrategy for CanCreate<Condition> {
261 type Value = bool;
262}
263impl<Condition> UpdateStrategy for CanCreate<Condition> {
264 type UpdateValue<'u> = bool;
265 type Success = ();
266}
267
268/// The `CanDestroy` strategy represents the ability to destroy an asset.
269/// It is both an [inspect](InspectStrategy) and [update](UpdateStrategy)
270/// strategy.
271///
272/// * As the inspect strategy, it returns `bool`.
273/// * As the update strategy, it accepts `bool`.
274///
275/// By default, this strategy means the ability to destroy an asset "in general".
276/// However, a user can define several variants of this strategy by supplying the `Condition`
277/// type. Using the `Condition` value, we are formulating the question, "Can this be destroyed
278/// under the given condition?". For instance, "Can **a specific user** destroy an asset of
279/// **another user**?".
280pub struct CanDestroy<Condition = ()>(pub Condition);
281impl Default for CanDestroy<()> {
282 fn default() -> Self {
283 Self(())
284 }
285}
286impl<Condition> InspectStrategy for CanDestroy<Condition> {
287 type Value = bool;
288}
289impl<Condition> UpdateStrategy for CanDestroy<Condition> {
290 type UpdateValue<'u> = bool;
291 type Success = ();
292}
293
294/// The `CanUpdate` strategy represents the ability to update the state of an asset.
295/// It is both an [inspect](InspectStrategy) and [update](UpdateStrategy)
296/// strategy.
297///
298/// * As the inspect strategy, it returns `bool`.
299/// * As the update strategy is accepts `bool`.
300///
301/// By default, this strategy means the ability to update the state of an asset "in general".
302/// However, a user can define several flavors of this strategy by supplying the `Flavor` type.
303/// The `Flavor` type can add more details to the strategy.
304/// For instance, "Can **a specific user** update the state of an asset **under a certain
305/// key**?".
306pub struct CanUpdate<Flavor = ()>(pub Flavor);
307impl Default for CanUpdate<()> {
308 fn default() -> Self {
309 Self(())
310 }
311}
312impl<Flavor> InspectStrategy for CanUpdate<Flavor> {
313 type Value = bool;
314}
315impl<Flavor> UpdateStrategy for CanUpdate<Flavor> {
316 type UpdateValue<'u> = bool;
317 type Success = ();
318}
319
320/// This trait converts the given [UpdateStrategy] into the corresponding [CanUpdate] strategy
321/// representing the ability to update the asset using the provided strategy.
322pub trait AsCanUpdate: Sized + UpdateStrategy {
323 fn as_can_update(self) -> CanUpdate<Self>;
324}
325impl<T: UpdateStrategy> AsCanUpdate for T {
326 fn as_can_update(self) -> CanUpdate<Self> {
327 CanUpdate(self)
328 }
329}
330
331/// The `AutoId` is an ID assignment approach intended to be used in
332/// [`"create" strategies`](CreateStrategy).
333///
334/// It accepts the `Id` type of the asset.
335/// The "create" strategy should report the value of type `ReportedId` upon successful asset
336/// creation.
337pub type AutoId<ReportedId> = DeriveAndReportId<(), ReportedId>;
338
339/// The `PredefinedId` is an ID assignment approach intended to be used in
340/// [`"create" strategies`](CreateStrategy).
341///
342/// It accepts the `Id` that should be assigned to the newly created asset.
343///
344/// The "create" strategy should report the `Id` value upon successful asset creation.
345pub type PredefinedId<Id> = DeriveAndReportId<Id, Id>;
346
347/// The `DeriveAndReportId` is an ID assignment approach intended to be used in
348/// [`"create" strategies`](CreateStrategy).
349///
350/// It accepts the `Params` and the `Id`.
351/// The `ReportedId` value should be computed by the "create" strategy using the `Params` value.
352///
353/// The "create" strategy should report the `ReportedId` value upon successful asset creation.
354///
355/// An example of ID derivation is the creation of an NFT inside a collection using the
356/// collection ID as `Params`. The `ReportedId` in this case is the full ID of the NFT.
357#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
358pub struct DeriveAndReportId<Params, ReportedId> {
359 pub params: Params,
360 _phantom: PhantomData<ReportedId>,
361}
362impl<ReportedId> DeriveAndReportId<(), ReportedId> {
363 pub fn auto() -> AutoId<ReportedId> {
364 Self { params: (), _phantom: PhantomData }
365 }
366}
367impl<Params, ReportedId> DeriveAndReportId<Params, ReportedId> {
368 pub fn from(params: Params) -> Self {
369 Self { params, _phantom: PhantomData }
370 }
371}
372impl<Params, ReportedId> IdAssignment for DeriveAndReportId<Params, ReportedId> {
373 type ReportedId = ReportedId;
374}
375impl<Params, ReportedId> CreateStrategy for DeriveAndReportId<Params, ReportedId> {
376 type Success = ReportedId;
377}
378
379/// Represents the value of an [InspectStrategy] to be used as a configuration value in the
380/// [WithConfig] strategy.
381#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
382pub struct ConfigValue<Inspect: InspectStrategy>(pub Inspect::Value);
383
384/// This trait marks a config value to be used in the [WithConfig] strategy.
385/// It is used to make compiler error messages clearer if invalid type is supplied into the
386/// [WithConfig].
387pub trait ConfigValueMarker {}
388impl<Inspect: InspectStrategy> ConfigValueMarker for ConfigValue<Inspect> {}
389
390#[impl_trait_for_tuples::impl_for_tuples(1, 8)]
391impl ConfigValueMarker for Tuple {}
392
393/// This trait converts the given [InspectStrategy] into the config value to be used in the
394/// [WithConfig] strategy.
395pub trait WithConfigValue: Sized + InspectStrategy {
396 fn with_config_value(value: Self::Value) -> ConfigValue<Self>;
397}
398impl<T: InspectStrategy> WithConfigValue for T {
399 fn with_config_value(value: Self::Value) -> ConfigValue<Self> {
400 ConfigValue::<Self>(value)
401 }
402}
403
404/// The `WithConfig` is a [create](CreateStrategy) and [restore](RestoreStrategy) strategy.
405/// It facilitates setting the asset's properties that can be later inspected via the corresponding
406/// [inspect strategies](InspectStrategy). The provided asset's properties are considered its
407/// config. Every inspect strategy can be used to create a config value.
408///
409/// For instance, one can use `WithConfig` to restore an asset to the given owner using the [Owner]
410/// inspect strategy:
411///
412/// ```rust,ignore
413/// NftEngine::restore(WithConfig::from(Owner::with_config_value(OWNER_ACCOUNT)))
414/// ```
415///
416/// The extra parameters can be supplied to provide additional context to the operation.
417/// They're required for creation operation as they provide the [id assignment
418/// approach](IdAssignment), but they're optional for the restoring operation.
419///
420/// For instance, one can use `WithConfig` to create an asset with a predefined id this way:
421///
422/// ```rust,ignore
423/// NftEngine::create(WithConfig::new(
424/// Owner::with_config_value(OWNER_ACCOUNT),
425/// PredefinedId::from(ASSET_ID),
426/// ))
427/// ```
428///
429/// Note: you can use several config values by providing a tuple of them:
430///
431/// ```rust,ignore
432/// NftEngine::create(WithConfig::new(
433/// (
434/// Owner::with_config_value(OWNER_ACCOUNT),
435/// Admin::with_config_value(ADMIN_ACCOUNT),
436/// ),
437/// PredefinedId::from(ASSET_ID),
438/// ))
439/// ```
440#[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)]
441pub struct WithConfig<ConfigValue: ConfigValueMarker, Extra = ()> {
442 pub config: ConfigValue,
443 pub extra: Extra,
444}
445
446impl<ConfigValue: ConfigValueMarker> WithConfig<ConfigValue> {
447 pub fn from(config: ConfigValue) -> Self {
448 Self { config, extra: () }
449 }
450}
451impl<ConfigValue: ConfigValueMarker, Extra> WithConfig<ConfigValue, Extra> {
452 pub fn new(config: ConfigValue, extra: Extra) -> Self {
453 Self { config, extra }
454 }
455}
456impl<ConfigValue: ConfigValueMarker, Assignment: IdAssignment> CreateStrategy
457 for WithConfig<ConfigValue, Assignment>
458{
459 type Success = Assignment::ReportedId;
460}
461impl<ConfigValue: ConfigValueMarker, Extra> RestoreStrategy for WithConfig<ConfigValue, Extra> {
462 type Success = ();
463}
464
465/// This adapter allows one to derive a [CreateStrategy] value from the ID derivation parameters
466/// from the [DeriveAndReportId].
467///
468/// The instance will be created using the derived strategy.
469pub struct DeriveStrategyThenCreate<Strategy, DeriveCfg, CreateOp>(
470 PhantomData<(Strategy, DeriveCfg, CreateOp)>,
471);
472impl<Params, Strategy, DeriveCfg, CreateOp> Create<DeriveAndReportId<Params, Strategy::Success>>
473 for DeriveStrategyThenCreate<Strategy, DeriveCfg, CreateOp>
474where
475 Strategy: CreateStrategy,
476 DeriveCfg: Convert<Params, Result<Strategy, DispatchError>>,
477 CreateOp: Create<Strategy>,
478{
479 fn create(
480 id_assignment: DeriveAndReportId<Params, Strategy::Success>,
481 ) -> Result<Strategy::Success, DispatchError> {
482 let strategy = DeriveCfg::convert(id_assignment.params)?;
483
484 CreateOp::create(strategy)
485 }
486}