referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/currency/
reservable.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//! The reservable currency trait.
19
20use scale_info::TypeInfo;
21use sp_core::Get;
22
23use super::{super::misc::BalanceStatus, Currency};
24use crate::{
25	dispatch::DispatchResult,
26	traits::{ExistenceRequirement, SignedImbalance, WithdrawReasons},
27};
28use sp_runtime::DispatchError;
29
30/// A currency where funds can be reserved from the user.
31pub trait ReservableCurrency<AccountId>: Currency<AccountId> {
32	/// Same result as `reserve(who, value)` (but without the side-effects) assuming there
33	/// are no balance changes in the meantime.
34	fn can_reserve(who: &AccountId, value: Self::Balance) -> bool;
35
36	/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
37	///
38	/// As much funds up to `value` will be deducted as possible. If the reserve balance of `who`
39	/// is less than `value`, then the second item will be equal to the value not able to be
40	/// slashed.
41	fn slash_reserved(
42		who: &AccountId,
43		value: Self::Balance,
44	) -> (Self::NegativeImbalance, Self::Balance);
45
46	/// The amount of the balance of a given account that is externally reserved; this can still get
47	/// slashed, but gets slashed last of all.
48	///
49	/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
50	/// that are still 'owned' by the account holder, but which are suspendable.
51	///
52	/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
53	/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
54	fn reserved_balance(who: &AccountId) -> Self::Balance;
55
56	/// Moves `value` from balance to reserved balance.
57	///
58	/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
59	/// be returned to notify of this. This is different behavior than `unreserve`.
60	fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult;
61
62	/// Moves up to `value` from reserved balance to free balance. This function cannot fail.
63	///
64	/// As much funds up to `value` will be moved as possible. If the reserve balance of `who`
65	/// is less than `value`, then the remaining amount will be returned. This is different
66	/// behavior than `reserve`.
67	fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance;
68
69	/// Moves up to `value` from reserved balance of account `slashed` to balance of account
70	/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
71	/// returned. Funds will be placed in either the `free` balance or the `reserved` balance,
72	/// depending on the `status`.
73	///
74	/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
75	/// then `Ok(non_zero)` will be returned.
76	fn repatriate_reserved(
77		slashed: &AccountId,
78		beneficiary: &AccountId,
79		value: Self::Balance,
80		status: BalanceStatus,
81	) -> Result<Self::Balance, DispatchError>;
82}
83
84#[cfg(feature = "std")]
85impl<AccountId> ReservableCurrency<AccountId> for () {
86	fn can_reserve(_: &AccountId, _: Self::Balance) -> bool {
87		true
88	}
89	fn slash_reserved(_: &AccountId, _: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
90		((), 0)
91	}
92	fn reserved_balance(_: &AccountId) -> Self::Balance {
93		0
94	}
95	fn reserve(_: &AccountId, _: Self::Balance) -> DispatchResult {
96		Ok(())
97	}
98	fn unreserve(_: &AccountId, _: Self::Balance) -> Self::Balance {
99		0
100	}
101	fn repatriate_reserved(
102		_: &AccountId,
103		_: &AccountId,
104		_: Self::Balance,
105		_: BalanceStatus,
106	) -> Result<Self::Balance, DispatchError> {
107		Ok(0)
108	}
109}
110
111pub trait NamedReservableCurrency<AccountId>: ReservableCurrency<AccountId> {
112	/// An identifier for a reserve. Used for disambiguating different reserves so that
113	/// they can be individually replaced or removed.
114	type ReserveIdentifier: codec::Encode + TypeInfo + 'static;
115
116	/// Deducts up to `value` from reserved balance of `who`. This function cannot fail.
117	///
118	/// As much funds up to `value` will be deducted as possible. If the reserve balance of `who`
119	/// is less than `value`, then a non-zero second item will be returned.
120	fn slash_reserved_named(
121		id: &Self::ReserveIdentifier,
122		who: &AccountId,
123		value: Self::Balance,
124	) -> (Self::NegativeImbalance, Self::Balance);
125
126	/// The amount of the balance of a given account that is externally reserved; this can still get
127	/// slashed, but gets slashed last of all.
128	///
129	/// This balance is a 'reserve' balance that other subsystems use in order to set aside tokens
130	/// that are still 'owned' by the account holder, but which are suspendable.
131	///
132	/// When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'
133	/// is deleted: specifically, `ReservedBalance`.
134	///
135	/// `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets
136	/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
137	fn reserved_balance_named(id: &Self::ReserveIdentifier, who: &AccountId) -> Self::Balance;
138
139	/// Moves `value` from balance to reserved balance.
140	///
141	/// If the free balance is lower than `value`, then no funds will be moved and an `Err` will
142	/// be returned to notify of this. This is different behavior than `unreserve`.
143	fn reserve_named(
144		id: &Self::ReserveIdentifier,
145		who: &AccountId,
146		value: Self::Balance,
147	) -> DispatchResult;
148
149	/// Moves up to `value` from reserved balance to free balance. This function cannot fail.
150	///
151	/// As much funds up to `value` will be moved as possible. If the reserve balance of `who`
152	/// is less than `value`, then the remaining amount will be returned.
153	///
154	/// # NOTES
155	///
156	/// - This is different from `reserve`.
157	/// - If the remaining reserved balance is less than `ExistentialDeposit`, it will
158	/// invoke `on_reserved_too_low` and could reap the account.
159	fn unreserve_named(
160		id: &Self::ReserveIdentifier,
161		who: &AccountId,
162		value: Self::Balance,
163	) -> Self::Balance;
164
165	/// Moves up to `value` from reserved balance of account `slashed` to balance of account
166	/// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be
167	/// returned. Funds will be placed in either the `free` balance or the `reserved` balance,
168	/// depending on the `status`.
169	///
170	/// As much funds up to `value` will be deducted as possible. If this is less than `value`,
171	/// then `Ok(non_zero)` will be returned.
172	fn repatriate_reserved_named(
173		id: &Self::ReserveIdentifier,
174		slashed: &AccountId,
175		beneficiary: &AccountId,
176		value: Self::Balance,
177		status: BalanceStatus,
178	) -> Result<Self::Balance, DispatchError>;
179
180	/// Ensure the reserved balance is equal to `value`.
181	///
182	/// This will reserve extra amount of current reserved balance is less than `value`.
183	/// And unreserve if current reserved balance is greater than `value`.
184	fn ensure_reserved_named(
185		id: &Self::ReserveIdentifier,
186		who: &AccountId,
187		value: Self::Balance,
188	) -> DispatchResult {
189		let current = Self::reserved_balance_named(id, who);
190		if current > value {
191			// we always have enough balance to unreserve here
192			Self::unreserve_named(id, who, current - value);
193			Ok(())
194		} else if value > current {
195			// we checked value > current
196			Self::reserve_named(id, who, value - current)
197		} else {
198			// current == value
199			Ok(())
200		}
201	}
202
203	/// Unreserve all the named reserved balances, returning unreserved amount.
204	///
205	/// Is a no-op if the value to be unreserved is zero.
206	fn unreserve_all_named(id: &Self::ReserveIdentifier, who: &AccountId) -> Self::Balance {
207		let value = Self::reserved_balance_named(id, who);
208		Self::unreserve_named(id, who, value);
209		value
210	}
211
212	/// Slash all the reserved balance, returning the negative imbalance created.
213	///
214	/// Is a no-op if the value to be slashed is zero.
215	fn slash_all_reserved_named(
216		id: &Self::ReserveIdentifier,
217		who: &AccountId,
218	) -> Self::NegativeImbalance {
219		let value = Self::reserved_balance_named(id, who);
220		Self::slash_reserved_named(id, who, value).0
221	}
222
223	/// Move all the named reserved balance of one account into the balance of another, according to
224	/// `status`. If `status` is `Reserved`, the balance will be reserved with given `id`.
225	///
226	/// Is a no-op if:
227	/// - the value to be moved is zero; or
228	/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
229	fn repatriate_all_reserved_named(
230		id: &Self::ReserveIdentifier,
231		slashed: &AccountId,
232		beneficiary: &AccountId,
233		status: BalanceStatus,
234	) -> DispatchResult {
235		let value = Self::reserved_balance_named(id, slashed);
236		Self::repatriate_reserved_named(id, slashed, beneficiary, value, status).map(|_| ())
237	}
238}
239
240/// Adapter to allow a `NamedReservableCurrency` to be passed as regular `ReservableCurrency`
241/// together with an `Id`.
242///
243/// All "anonymous" operations are then implemented as their named counterparts with the given `Id`.
244pub struct WithName<NamedReservable, Id, AccountId>(
245	core::marker::PhantomData<(NamedReservable, Id, AccountId)>,
246);
247impl<
248		NamedReservable: NamedReservableCurrency<AccountId>,
249		Id: Get<NamedReservable::ReserveIdentifier>,
250		AccountId,
251	> Currency<AccountId> for WithName<NamedReservable, Id, AccountId>
252{
253	type Balance = <NamedReservable as Currency<AccountId>>::Balance;
254	type PositiveImbalance = <NamedReservable as Currency<AccountId>>::PositiveImbalance;
255	type NegativeImbalance = <NamedReservable as Currency<AccountId>>::NegativeImbalance;
256
257	fn total_balance(who: &AccountId) -> Self::Balance {
258		NamedReservable::total_balance(who)
259	}
260	fn can_slash(who: &AccountId, value: Self::Balance) -> bool {
261		NamedReservable::can_slash(who, value)
262	}
263	fn total_issuance() -> Self::Balance {
264		NamedReservable::total_issuance()
265	}
266	fn minimum_balance() -> Self::Balance {
267		NamedReservable::minimum_balance()
268	}
269	fn burn(amount: Self::Balance) -> Self::PositiveImbalance {
270		NamedReservable::burn(amount)
271	}
272	fn issue(amount: Self::Balance) -> Self::NegativeImbalance {
273		NamedReservable::issue(amount)
274	}
275	fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) {
276		NamedReservable::pair(amount)
277	}
278	fn free_balance(who: &AccountId) -> Self::Balance {
279		NamedReservable::free_balance(who)
280	}
281	fn ensure_can_withdraw(
282		who: &AccountId,
283		amount: Self::Balance,
284		reasons: WithdrawReasons,
285		new_balance: Self::Balance,
286	) -> DispatchResult {
287		NamedReservable::ensure_can_withdraw(who, amount, reasons, new_balance)
288	}
289
290	fn transfer(
291		source: &AccountId,
292		dest: &AccountId,
293		value: Self::Balance,
294		existence_requirement: ExistenceRequirement,
295	) -> DispatchResult {
296		NamedReservable::transfer(source, dest, value, existence_requirement)
297	}
298	fn slash(who: &AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
299		NamedReservable::slash(who, value)
300	}
301	fn deposit_into_existing(
302		who: &AccountId,
303		value: Self::Balance,
304	) -> Result<Self::PositiveImbalance, DispatchError> {
305		NamedReservable::deposit_into_existing(who, value)
306	}
307	fn resolve_into_existing(
308		who: &AccountId,
309		value: Self::NegativeImbalance,
310	) -> Result<(), Self::NegativeImbalance> {
311		NamedReservable::resolve_into_existing(who, value)
312	}
313	fn deposit_creating(who: &AccountId, value: Self::Balance) -> Self::PositiveImbalance {
314		NamedReservable::deposit_creating(who, value)
315	}
316	fn resolve_creating(who: &AccountId, value: Self::NegativeImbalance) {
317		NamedReservable::resolve_creating(who, value)
318	}
319	fn withdraw(
320		who: &AccountId,
321		value: Self::Balance,
322		reasons: WithdrawReasons,
323		liveness: ExistenceRequirement,
324	) -> Result<Self::NegativeImbalance, DispatchError> {
325		NamedReservable::withdraw(who, value, reasons, liveness)
326	}
327	fn settle(
328		who: &AccountId,
329		value: Self::PositiveImbalance,
330		reasons: WithdrawReasons,
331		liveness: ExistenceRequirement,
332	) -> Result<(), Self::PositiveImbalance> {
333		NamedReservable::settle(who, value, reasons, liveness)
334	}
335	fn make_free_balance_be(
336		who: &AccountId,
337		balance: Self::Balance,
338	) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
339		NamedReservable::make_free_balance_be(who, balance)
340	}
341}
342impl<
343		NamedReservable: NamedReservableCurrency<AccountId>,
344		Id: Get<NamedReservable::ReserveIdentifier>,
345		AccountId,
346	> ReservableCurrency<AccountId> for WithName<NamedReservable, Id, AccountId>
347{
348	fn can_reserve(who: &AccountId, value: Self::Balance) -> bool {
349		NamedReservable::can_reserve(who, value)
350	}
351
352	fn slash_reserved(
353		who: &AccountId,
354		value: Self::Balance,
355	) -> (Self::NegativeImbalance, Self::Balance) {
356		NamedReservable::slash_reserved_named(&Id::get(), who, value)
357	}
358
359	fn reserved_balance(who: &AccountId) -> Self::Balance {
360		NamedReservable::reserved_balance_named(&Id::get(), who)
361	}
362
363	fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult {
364		NamedReservable::reserve_named(&Id::get(), who, value)
365	}
366
367	fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance {
368		NamedReservable::unreserve_named(&Id::get(), who, value)
369	}
370
371	fn repatriate_reserved(
372		slashed: &AccountId,
373		beneficiary: &AccountId,
374		value: Self::Balance,
375		status: BalanceStatus,
376	) -> Result<Self::Balance, DispatchError> {
377		NamedReservable::repatriate_reserved_named(&Id::get(), slashed, beneficiary, value, status)
378	}
379}