Skip to main content

topsoil_core/traits/tokens/
misc.rs

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