referrerpolicy=no-referrer-when-downgrade

frame_support/traits/tokens/fungible/conformance_tests/regular/
unbalanced.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
18use crate::traits::{
19	fungible::{Inspect, Unbalanced},
20	tokens::{Fortitude, Precision, Preservation},
21};
22use core::fmt::Debug;
23use sp_arithmetic::{traits::AtLeast8BitUnsigned, ArithmeticError};
24use sp_runtime::{traits::Bounded, TokenError};
25
26/// Tests [`Unbalanced::write_balance`].
27///
28/// We don't need to test the Error case for this function, because the trait makes no
29/// assumptions about the ways it can fail. That is completely an implementation detail.
30pub fn write_balance<T, AccountId>()
31where
32	T: Unbalanced<AccountId>,
33	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
34	AccountId: AtLeast8BitUnsigned,
35{
36	// Setup some accounts to test varying initial balances
37	let account_0_ed = AccountId::from(0);
38	let account_1_gt_ed = AccountId::from(1);
39	let account_2_empty = AccountId::from(2);
40	T::increase_balance(&account_0_ed, T::minimum_balance(), Precision::Exact).unwrap();
41	T::increase_balance(&account_1_gt_ed, T::minimum_balance() + 5.into(), Precision::Exact)
42		.unwrap();
43
44	// Test setting the balances of each account by gt the minimum balance succeeds with no
45	// dust.
46	let amount = T::minimum_balance() + 10.into();
47	assert_eq!(T::write_balance(&account_0_ed, amount), Ok(None));
48	assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(None));
49	assert_eq!(T::write_balance(&account_2_empty, amount), Ok(None));
50	assert_eq!(T::balance(&account_0_ed), amount);
51	assert_eq!(T::balance(&account_1_gt_ed), amount);
52	assert_eq!(T::balance(&account_2_empty), amount);
53
54	// Test setting the balances of each account to below the minimum balance succeeds with
55	// the expected dust.
56	// If the minimum balance is 1, then the dust is 0, represented as None.
57	// If the minimum balance is >1, then the dust is the remaining balance that will be wiped
58	// as the account is reaped.
59	let amount = T::minimum_balance() - 1.into();
60	if T::minimum_balance() == 1.into() {
61		assert_eq!(T::write_balance(&account_0_ed, amount), Ok(None));
62		assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(None));
63		assert_eq!(T::write_balance(&account_2_empty, amount), Ok(None));
64	} else if T::minimum_balance() > 1.into() {
65		assert_eq!(T::write_balance(&account_0_ed, amount), Ok(Some(amount)));
66		assert_eq!(T::write_balance(&account_1_gt_ed, amount), Ok(Some(amount)));
67		assert_eq!(T::write_balance(&account_2_empty, amount), Ok(Some(amount)));
68	}
69}
70
71/// Tests [`Unbalanced::decrease_balance`] called with [`Preservation::Expendable`].
72pub fn decrease_balance_expendable<T, AccountId>()
73where
74	T: Unbalanced<AccountId>,
75	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
76	AccountId: AtLeast8BitUnsigned,
77{
78	// Setup account with some balance
79	let account_0 = AccountId::from(0);
80	let account_0_initial_balance = T::minimum_balance() + 10.into();
81	T::increase_balance(&account_0, account_0_initial_balance, Precision::Exact).unwrap();
82
83	// Decreasing the balance still above the minimum balance should not reap the account.
84	let amount = 1.into();
85	assert_eq!(
86		T::decrease_balance(
87			&account_0,
88			amount,
89			Precision::Exact,
90			Preservation::Expendable,
91			Fortitude::Polite,
92		),
93		Ok(amount),
94	);
95	assert_eq!(T::balance(&account_0), account_0_initial_balance - amount);
96
97	// Decreasing the balance below funds available should fail when Precision::Exact
98	let balance_before = T::balance(&account_0);
99	assert_eq!(
100		T::decrease_balance(
101			&account_0,
102			account_0_initial_balance,
103			Precision::Exact,
104			Preservation::Expendable,
105			Fortitude::Polite,
106		),
107		Err(TokenError::FundsUnavailable.into())
108	);
109	// Balance unchanged
110	assert_eq!(T::balance(&account_0), balance_before);
111
112	// And reap the account when Precision::BestEffort
113	assert_eq!(
114		T::decrease_balance(
115			&account_0,
116			account_0_initial_balance,
117			Precision::BestEffort,
118			Preservation::Expendable,
119			Fortitude::Polite,
120		),
121		Ok(balance_before),
122	);
123	// Account reaped
124	assert_eq!(T::balance(&account_0), 0.into());
125}
126
127/// Tests [`Unbalanced::decrease_balance`] called with [`Preservation::Preserve`].
128pub fn decrease_balance_preserve<T, AccountId>()
129where
130	T: Unbalanced<AccountId>,
131	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
132	AccountId: AtLeast8BitUnsigned,
133{
134	// Setup account with some balance
135	let account_0 = AccountId::from(0);
136	let account_0_initial_balance = T::minimum_balance() + 10.into();
137	T::increase_balance(&account_0, account_0_initial_balance, Precision::Exact).unwrap();
138
139	// Decreasing the balance below the minimum when Precision::Exact should fail.
140	let amount = 11.into();
141	assert_eq!(
142		T::decrease_balance(
143			&account_0,
144			amount,
145			Precision::Exact,
146			Preservation::Preserve,
147			Fortitude::Polite,
148		),
149		Err(TokenError::FundsUnavailable.into()),
150	);
151	// Balance should not have changed.
152	assert_eq!(T::balance(&account_0), account_0_initial_balance);
153
154	// Decreasing the balance below the minimum when Precision::BestEffort should reduce to
155	// minimum balance.
156	let amount = 11.into();
157	assert_eq!(
158		T::decrease_balance(
159			&account_0,
160			amount,
161			Precision::BestEffort,
162			Preservation::Preserve,
163			Fortitude::Polite,
164		),
165		Ok(account_0_initial_balance - T::minimum_balance()),
166	);
167	assert_eq!(T::balance(&account_0), T::minimum_balance());
168}
169
170/// Tests [`Unbalanced::increase_balance`].
171pub fn increase_balance<T, AccountId>()
172where
173	T: Unbalanced<AccountId>,
174	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
175	AccountId: AtLeast8BitUnsigned,
176{
177	let account_0 = AccountId::from(0);
178	assert_eq!(T::balance(&account_0), 0.into());
179
180	// Increasing the bal below the ED errors when precision is Exact
181	if T::minimum_balance() > 0.into() {
182		assert_eq!(
183			T::increase_balance(&account_0, T::minimum_balance() - 1.into(), Precision::Exact),
184			Err(TokenError::BelowMinimum.into()),
185		);
186	}
187	assert_eq!(T::balance(&account_0), 0.into());
188
189	// Increasing the bal below the ED leaves the balance at zero when precision is BestEffort
190	if T::minimum_balance() > 0.into() {
191		assert_eq!(
192			T::increase_balance(&account_0, T::minimum_balance() - 1.into(), Precision::BestEffort),
193			Ok(0.into()),
194		);
195	}
196	assert_eq!(T::balance(&account_0), 0.into());
197
198	// Can increase if new bal is >= ED
199	assert_eq!(
200		T::increase_balance(&account_0, T::minimum_balance(), Precision::Exact),
201		Ok(T::minimum_balance()),
202	);
203	assert_eq!(T::balance(&account_0), T::minimum_balance());
204	assert_eq!(T::increase_balance(&account_0, 5.into(), Precision::Exact), Ok(5.into()),);
205	assert_eq!(T::balance(&account_0), T::minimum_balance() + 5.into());
206
207	// Increasing by amount that would overflow fails when precision is Exact
208	assert_eq!(
209		T::increase_balance(&account_0, T::Balance::max_value(), Precision::Exact),
210		Err(ArithmeticError::Overflow.into()),
211	);
212
213	// Increasing by amount that would overflow saturates when precision is BestEffort
214	let balance_before = T::balance(&account_0);
215	assert_eq!(
216		T::increase_balance(&account_0, T::Balance::max_value(), Precision::BestEffort),
217		Ok(T::Balance::max_value() - balance_before),
218	);
219	assert_eq!(T::balance(&account_0), T::Balance::max_value());
220}
221
222/// Tests [`Unbalanced::set_total_issuance`].
223pub fn set_total_issuance<T, AccountId>()
224where
225	T: Unbalanced<AccountId>,
226	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
227	AccountId: AtLeast8BitUnsigned,
228{
229	T::set_total_issuance(1.into());
230	assert_eq!(T::total_issuance(), 1.into());
231
232	T::set_total_issuance(0.into());
233	assert_eq!(T::total_issuance(), 0.into());
234
235	T::set_total_issuance(T::minimum_balance());
236	assert_eq!(T::total_issuance(), T::minimum_balance());
237
238	T::set_total_issuance(T::minimum_balance() + 5.into());
239	assert_eq!(T::total_issuance(), T::minimum_balance() + 5.into());
240
241	if T::minimum_balance() > 0.into() {
242		T::set_total_issuance(T::minimum_balance() - 1.into());
243		assert_eq!(T::total_issuance(), T::minimum_balance() - 1.into());
244	}
245}
246
247/// Tests [`Unbalanced::deactivate`] and [`Unbalanced::reactivate`].
248pub fn deactivate_and_reactivate<T, AccountId>()
249where
250	T: Unbalanced<AccountId>,
251	<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
252	AccountId: AtLeast8BitUnsigned,
253{
254	T::set_total_issuance(10.into());
255	assert_eq!(T::total_issuance(), 10.into());
256	assert_eq!(T::active_issuance(), 10.into());
257
258	T::deactivate(2.into());
259	assert_eq!(T::total_issuance(), 10.into());
260	assert_eq!(T::active_issuance(), 8.into());
261
262	// Saturates at total_issuance
263	T::reactivate(4.into());
264	assert_eq!(T::total_issuance(), 10.into());
265	assert_eq!(T::active_issuance(), 10.into());
266
267	// Decrements correctly after saturating at total_issuance
268	T::deactivate(1.into());
269	assert_eq!(T::total_issuance(), 10.into());
270	assert_eq!(T::active_issuance(), 9.into());
271
272	// Saturates at zero
273	T::deactivate(15.into());
274	assert_eq!(T::total_issuance(), 10.into());
275	assert_eq!(T::active_issuance(), 0.into());
276
277	// Increments correctly after saturating at zero
278	T::reactivate(1.into());
279	assert_eq!(T::total_issuance(), 10.into());
280	assert_eq!(T::active_issuance(), 1.into());
281}