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}