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