frame_support/traits/tokens/misc.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//! Miscellaneous types.
19
20use crate::{traits::Contains, TypeInfo};
21use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, HasCompact, MaxEncodedLen};
22use core::fmt::Debug;
23use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero};
24use sp_core::RuntimeDebug;
25use sp_runtime::{
26 traits::{Convert, MaybeSerializeDeserialize},
27 ArithmeticError, DispatchError, TokenError,
28};
29
30/// The origin of funds to be used for a deposit operation.
31#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
32pub enum Provenance {
33 /// The funds will be minted into the system, increasing total issuance (and potentially
34 /// causing an overflow there).
35 Minted,
36 /// The funds already exist in the system, therefore will not affect total issuance.
37 Extant,
38}
39
40/// The mode under which usage of funds may be restricted.
41#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
42pub enum Restriction {
43 /// Funds are under the normal conditions.
44 Free,
45 /// Funds are on hold.
46 OnHold,
47}
48
49/// The mode by which we describe whether an operation should keep an account alive.
50#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
51pub enum Preservation {
52 /// We don't care if the account gets killed by this operation.
53 Expendable,
54 /// The account may not be killed, but we don't care if the balance gets dusted.
55 Protect,
56 /// The account may not be killed and our provider reference must remain (in the context of
57 /// tokens, this means that the account may not be dusted).
58 Preserve,
59}
60
61/// The privilege with which a withdraw operation is conducted.
62#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
63pub enum Fortitude {
64 /// The operation should execute with regular privilege.
65 Polite,
66 /// The operation should be forced to succeed if possible. This is usually employed for system-
67 /// level security-critical events such as slashing.
68 Force,
69}
70
71/// The precision required of an operation generally involving some aspect of quantitative fund
72/// withdrawal or transfer.
73#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
74pub enum Precision {
75 /// The operation should must either proceed either exactly according to the amounts involved
76 /// or not at all.
77 Exact,
78 /// The operation may be considered successful even if less than the specified amounts are
79 /// available to be used. In this case a best effort will be made.
80 BestEffort,
81}
82
83/// One of a number of consequences of withdrawing a fungible from an account.
84#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
85pub enum WithdrawConsequence<Balance> {
86 /// Withdraw could not happen since the amount to be withdrawn is less than the total funds in
87 /// the account.
88 BalanceLow,
89 /// The withdraw would mean the account dying when it needs to exist (usually because it is a
90 /// provider and there are consumer references on it).
91 WouldDie,
92 /// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist
93 /// on the system.
94 UnknownAsset,
95 /// There has been an underflow in the system. This is indicative of a corrupt state and
96 /// likely unrecoverable.
97 Underflow,
98 /// There has been an overflow in the system. This is indicative of a corrupt state and
99 /// likely unrecoverable.
100 Overflow,
101 /// Not enough of the funds in the account are available for withdrawal.
102 Frozen,
103 /// Account balance would reduce to zero, potentially destroying it. The parameter is the
104 /// amount of balance which is destroyed.
105 ReducedToZero(Balance),
106 /// Account continued in existence.
107 Success,
108}
109
110impl<Balance: Zero> WithdrawConsequence<Balance> {
111 /// Convert the type into a `Result` with `DispatchError` as the error or the additional
112 /// `Balance` by which the account will be reduced.
113 pub fn into_result(self, keep_nonzero: bool) -> Result<Balance, DispatchError> {
114 use WithdrawConsequence::*;
115 match self {
116 BalanceLow => Err(TokenError::FundsUnavailable.into()),
117 WouldDie => Err(TokenError::OnlyProvider.into()),
118 UnknownAsset => Err(TokenError::UnknownAsset.into()),
119 Underflow => Err(ArithmeticError::Underflow.into()),
120 Overflow => Err(ArithmeticError::Overflow.into()),
121 Frozen => Err(TokenError::Frozen.into()),
122 ReducedToZero(_) if keep_nonzero => Err(TokenError::NotExpendable.into()),
123 ReducedToZero(result) => Ok(result),
124 Success => Ok(Zero::zero()),
125 }
126 }
127}
128
129/// One of a number of consequences of withdrawing a fungible from an account.
130#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
131pub enum DepositConsequence {
132 /// Deposit couldn't happen due to the amount being too low. This is usually because the
133 /// account doesn't yet exist and the deposit wouldn't bring it to at least the minimum needed
134 /// for existence.
135 BelowMinimum,
136 /// Deposit cannot happen since the account cannot be created (usually because it's a consumer
137 /// and there exists no provider reference).
138 CannotCreate,
139 /// The asset is unknown. Usually because an `AssetId` has been presented which doesn't exist
140 /// on the system.
141 UnknownAsset,
142 /// An overflow would occur. This is practically unexpected, but could happen in test systems
143 /// with extremely small balance types or balances that approach the max value of the balance
144 /// type.
145 Overflow,
146 /// Account continued in existence.
147 Success,
148 /// Account cannot receive the assets.
149 Blocked,
150}
151
152impl DepositConsequence {
153 /// Convert the type into a `Result` with `TokenError` as the error.
154 pub fn into_result(self) -> Result<(), DispatchError> {
155 use DepositConsequence::*;
156 Err(match self {
157 BelowMinimum => TokenError::BelowMinimum.into(),
158 CannotCreate => TokenError::CannotCreate.into(),
159 UnknownAsset => TokenError::UnknownAsset.into(),
160 Overflow => ArithmeticError::Overflow.into(),
161 Blocked => TokenError::Blocked.into(),
162 Success => return Ok(()),
163 })
164 }
165}
166
167/// Simple boolean for whether an account needs to be kept in existence.
168#[derive(Copy, Clone, RuntimeDebug, Eq, PartialEq)]
169pub enum ExistenceRequirement {
170 /// Operation must not result in the account going out of existence.
171 ///
172 /// Note this implies that if the account never existed in the first place, then the operation
173 /// may legitimately leave the account unchanged and still non-existent.
174 KeepAlive,
175 /// Operation may result in account going out of existence.
176 AllowDeath,
177}
178
179/// Status of funds.
180#[derive(
181 PartialEq,
182 Eq,
183 Clone,
184 Copy,
185 Encode,
186 Decode,
187 DecodeWithMemTracking,
188 RuntimeDebug,
189 scale_info::TypeInfo,
190 MaxEncodedLen,
191)]
192pub enum BalanceStatus {
193 /// Funds are free, as corresponding to `free` item in Balances.
194 Free,
195 /// Funds are reserved, as corresponding to `reserved` item in Balances.
196 Reserved,
197}
198
199bitflags::bitflags! {
200 /// Reasons for moving funds out of an account.
201 #[derive(Encode, Decode, MaxEncodedLen)]
202 pub struct WithdrawReasons: u8 {
203 /// In order to pay for (system) transaction costs.
204 const TRANSACTION_PAYMENT = 0b00000001;
205 /// In order to transfer ownership.
206 const TRANSFER = 0b00000010;
207 /// In order to reserve some funds for a later return or repatriation.
208 const RESERVE = 0b00000100;
209 /// In order to pay some other (higher-level) fees.
210 const FEE = 0b00001000;
211 /// In order to tip a validator for transaction inclusion.
212 const TIP = 0b00010000;
213 }
214}
215
216impl WithdrawReasons {
217 /// Choose all variants except for `one`.
218 ///
219 /// ```rust
220 /// # use frame_support::traits::WithdrawReasons;
221 /// # fn main() {
222 /// assert_eq!(
223 /// WithdrawReasons::FEE | WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE | WithdrawReasons::TIP,
224 /// WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT),
225 /// );
226 /// # }
227 /// ```
228 pub fn except(one: WithdrawReasons) -> WithdrawReasons {
229 let mut flags = Self::all();
230 flags.toggle(one);
231 flags
232 }
233}
234
235/// Simple amalgamation trait to collect together properties for an AssetId under one roof.
236pub trait AssetId:
237 FullCodec
238 + DecodeWithMemTracking
239 + Clone
240 + Eq
241 + PartialEq
242 + Debug
243 + scale_info::TypeInfo
244 + MaxEncodedLen
245{
246}
247impl<
248 T: FullCodec
249 + DecodeWithMemTracking
250 + Clone
251 + Eq
252 + PartialEq
253 + Debug
254 + scale_info::TypeInfo
255 + MaxEncodedLen,
256 > AssetId for T
257{
258}
259
260/// Simple amalgamation trait to collect together properties for a Balance under one roof.
261pub trait Balance:
262 AtLeast32BitUnsigned
263 + FullCodec
264 + DecodeWithMemTracking
265 + HasCompact<Type: DecodeWithMemTracking>
266 + Copy
267 + Default
268 + Debug
269 + scale_info::TypeInfo
270 + MaxEncodedLen
271 + Send
272 + Sync
273 + MaybeSerializeDeserialize
274 + 'static
275{
276}
277impl<
278 T: AtLeast32BitUnsigned
279 + FullCodec
280 + DecodeWithMemTracking
281 + HasCompact<Type: DecodeWithMemTracking>
282 + Copy
283 + Default
284 + Debug
285 + scale_info::TypeInfo
286 + MaxEncodedLen
287 + Send
288 + Sync
289 + MaybeSerializeDeserialize
290 + 'static,
291 > Balance for T
292{
293}
294
295/// Converts a balance value into an asset balance.
296pub trait ConversionToAssetBalance<InBalance, AssetId, AssetBalance> {
297 type Error;
298 fn to_asset_balance(balance: InBalance, asset_id: AssetId)
299 -> Result<AssetBalance, Self::Error>;
300}
301
302/// Converts an asset balance value into balance.
303pub trait ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance> {
304 type Error;
305 fn from_asset_balance(
306 balance: AssetBalance,
307 asset_id: AssetId,
308 ) -> Result<OutBalance, Self::Error>;
309 /// Ensures that a conversion for the `asset_id` will be successful if done immediately after
310 /// this call.
311 #[cfg(feature = "runtime-benchmarks")]
312 fn ensure_successful(asset_id: AssetId);
313}
314
315/// Implements [`ConversionFromAssetBalance`], enabling a 1:1 conversion of the asset balance
316/// value to the balance.
317pub struct UnityAssetBalanceConversion;
318impl<AssetBalance, AssetId, OutBalance>
319 ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance> for UnityAssetBalanceConversion
320where
321 AssetBalance: Into<OutBalance>,
322{
323 type Error = ();
324 fn from_asset_balance(balance: AssetBalance, _: AssetId) -> Result<OutBalance, Self::Error> {
325 Ok(balance.into())
326 }
327 #[cfg(feature = "runtime-benchmarks")]
328 fn ensure_successful(_: AssetId) {}
329}
330
331/// Implements [`ConversionFromAssetBalance`], allowing for a 1:1 balance conversion of the asset
332/// when it meets the conditions specified by `C`. If the conditions are not met, the conversion is
333/// delegated to `O`.
334pub struct UnityOrOuterConversion<C, O>(core::marker::PhantomData<(C, O)>);
335impl<AssetBalance, AssetId, OutBalance, C, O>
336 ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance> for UnityOrOuterConversion<C, O>
337where
338 C: Contains<AssetId>,
339 O: ConversionFromAssetBalance<AssetBalance, AssetId, OutBalance>,
340 AssetBalance: Into<OutBalance>,
341{
342 type Error = O::Error;
343 fn from_asset_balance(
344 balance: AssetBalance,
345 asset_id: AssetId,
346 ) -> Result<OutBalance, Self::Error> {
347 if C::contains(&asset_id) {
348 return Ok(balance.into());
349 }
350 O::from_asset_balance(balance, asset_id)
351 }
352 #[cfg(feature = "runtime-benchmarks")]
353 fn ensure_successful(asset_id: AssetId) {
354 O::ensure_successful(asset_id)
355 }
356}
357
358/// Trait to handle NFT locking mechanism to ensure interactions with the asset can be implemented
359/// downstream to extend logic of Uniques/Nfts current functionality.
360pub trait Locker<CollectionId, ItemId> {
361 /// Check if the asset should be locked and prevent interactions with the asset from executing.
362 fn is_locked(collection: CollectionId, item: ItemId) -> bool;
363}
364
365impl<CollectionId, ItemId> Locker<CollectionId, ItemId> for () {
366 // Default will be false if not implemented downstream.
367 // Note: The logic check in this function must be constant time and consistent for benchmarks
368 // to work.
369 fn is_locked(_collection: CollectionId, _item: ItemId) -> bool {
370 false
371 }
372}
373
374/// Retrieve the salary for a member of a particular rank.
375pub trait GetSalary<Rank, AccountId, Balance> {
376 /// Retrieve the salary for a given rank. The account ID is also supplied in case this changes
377 /// things.
378 fn get_salary(rank: Rank, who: &AccountId) -> Balance;
379}
380
381/// Adapter for a rank-to-salary `Convert` implementation into a `GetSalary` implementation.
382pub struct ConvertRank<C>(core::marker::PhantomData<C>);
383impl<A, R, B, C: Convert<R, B>> GetSalary<R, A, B> for ConvertRank<C> {
384 fn get_salary(rank: R, _: &A) -> B {
385 C::convert(rank)
386 }
387}
388
389/// An identifier and balance.
390#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
391pub struct IdAmount<Id, Balance> {
392 /// An identifier for this item.
393 pub id: Id,
394 /// Some amount for this item.
395 pub amount: Balance,
396}