use crate::traits::{
fungible::{Balanced, Inspect},
tokens::{imbalance::Imbalance as ImbalanceT, Fortitude, Precision, Preservation},
};
use core::fmt::Debug;
use frame_support::traits::tokens::fungible::imbalance::{Credit, Debt};
use sp_arithmetic::{traits::AtLeast8BitUnsigned, ArithmeticError};
use sp_runtime::{traits::Bounded, TokenError};
pub fn issue_and_resolve_credit<T, AccountId>()
where
T: Balanced<AccountId>,
<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
AccountId: AtLeast8BitUnsigned,
{
let account = AccountId::from(0);
assert_eq!(T::total_issuance(), 0.into());
assert_eq!(T::balance(&account), 0.into());
let credit: Credit<AccountId, T> = T::issue(T::minimum_balance() - 1.into());
assert_eq!(T::total_issuance(), credit.peek());
match T::resolve(&account, credit) {
Ok(_) => panic!("Balanced::resolve should have failed"),
Err(c) => assert_eq!(c.peek(), T::minimum_balance() - 1.into()),
};
assert_eq!(T::total_issuance(), 0.into());
assert_eq!(T::balance(&account), 0.into());
let credit: Credit<AccountId, T> = T::issue(T::minimum_balance());
match T::resolve(&account, credit) {
Ok(()) => {},
Err(_) => panic!("resolve failed"),
};
assert_eq!(T::total_issuance(), T::minimum_balance());
assert_eq!(T::balance(&account), T::minimum_balance());
let total_issuance_before = T::total_issuance();
let balance_before = T::balance(&account);
let amount = T::minimum_balance() - 1.into();
let credit: Credit<AccountId, T> = T::issue(amount);
match T::resolve(&account, credit) {
Ok(()) => {},
Err(_) => panic!("resolve failed"),
};
assert_eq!(T::total_issuance(), total_issuance_before + amount);
assert_eq!(T::balance(&account), balance_before + amount);
let total_issuance_before = T::total_issuance();
let balance_before = T::balance(&account);
let _ = T::issue(5.into());
assert_eq!(T::total_issuance(), total_issuance_before);
assert_eq!(T::balance(&account), balance_before);
}
pub fn rescind_and_settle_debt<T, AccountId>()
where
T: Balanced<AccountId>,
<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
AccountId: AtLeast8BitUnsigned,
{
let account = AccountId::from(0);
let initial_bal = T::minimum_balance() + 10.into();
let credit = T::issue(initial_bal);
match T::resolve(&account, credit) {
Ok(()) => {},
Err(_) => panic!("resolve failed"),
};
assert_eq!(T::total_issuance(), initial_bal);
assert_eq!(T::balance(&account), initial_bal);
let rescind_amount = 2.into();
let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
assert_eq!(debt.peek(), rescind_amount);
match T::settle(&account, debt, Preservation::Expendable) {
Ok(c) => {
assert_eq!(c.peek(), 0.into());
},
Err(_) => panic!("settle failed"),
};
assert_eq!(T::total_issuance(), initial_bal - rescind_amount);
assert_eq!(T::balance(&account), initial_bal - rescind_amount);
let _ = T::rescind(T::minimum_balance());
assert_eq!(T::total_issuance(), initial_bal - rescind_amount);
assert_eq!(T::balance(&account), initial_bal - rescind_amount);
let balance_before = T::balance(&account);
let total_issuance_before = T::total_issuance();
let rescind_amount = balance_before - T::minimum_balance() + 1.into();
let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
assert_eq!(debt.peek(), rescind_amount);
assert_eq!(T::total_issuance(), total_issuance_before - debt.peek().into());
match T::settle(&account, debt, Preservation::Preserve) {
Ok(_) => panic!("Balanced::settle should have failed"),
Err(d) => assert_eq!(d.peek(), rescind_amount),
};
assert_eq!(T::total_issuance(), total_issuance_before);
assert_eq!(T::balance(&account), balance_before);
let debt: Debt<AccountId, T> = T::rescind(rescind_amount);
match T::settle(&account, debt, Preservation::Expendable) {
Ok(c) => {
assert_eq!(c.peek(), 0.into());
},
Err(_) => panic!("settle failed"),
};
assert_eq!(T::total_issuance(), 0.into());
assert_eq!(T::balance(&account), 0.into());
}
pub fn deposit<T, AccountId>()
where
T: Balanced<AccountId>,
<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
AccountId: AtLeast8BitUnsigned,
{
let account = AccountId::from(0);
let amount = T::minimum_balance() - 1.into();
match T::deposit(&account, amount, Precision::Exact) {
Ok(_) => panic!("Balanced::deposit should have failed"),
Err(e) => assert_eq!(e, TokenError::BelowMinimum.into()),
};
assert_eq!(T::total_issuance(), 0.into());
assert_eq!(T::balance(&account), 0.into());
let amount = T::minimum_balance();
match T::deposit(&account, amount, Precision::Exact) {
Ok(d) => assert_eq!(d.peek(), amount),
Err(_) => panic!("Balanced::deposit failed"),
};
assert_eq!(T::total_issuance(), amount);
assert_eq!(T::balance(&account), amount);
let amount = T::Balance::max_value();
let balance_before = T::balance(&account);
let total_issuance_before = T::total_issuance();
match T::deposit(&account, amount, Precision::Exact) {
Ok(_) => panic!("Balanced::deposit should have failed"),
Err(e) => assert_eq!(e, ArithmeticError::Overflow.into()),
};
assert_eq!(T::total_issuance(), total_issuance_before);
assert_eq!(T::balance(&account), balance_before);
match T::deposit(&account, amount, Precision::BestEffort) {
Ok(d) => assert_eq!(d.peek(), T::Balance::max_value() - balance_before),
Err(_) => panic!("Balanced::deposit failed"),
};
assert_eq!(T::total_issuance(), T::Balance::max_value());
assert_eq!(T::balance(&account), T::Balance::max_value());
}
pub fn withdraw<T, AccountId>()
where
T: Balanced<AccountId>,
<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
AccountId: AtLeast8BitUnsigned,
{
let account = AccountId::from(0);
let initial_balance = T::minimum_balance() + 10.into();
match T::deposit(&account, initial_balance, Precision::Exact) {
Ok(_) => {},
Err(_) => panic!("Balanced::deposit failed"),
};
assert_eq!(T::total_issuance(), initial_balance);
assert_eq!(T::balance(&account), initial_balance);
let amount = 1.into();
match T::withdraw(
&account,
amount,
Precision::Exact,
Preservation::Expendable,
Fortitude::Polite,
) {
Ok(c) => assert_eq!(c.peek(), amount),
Err(_) => panic!("withdraw failed"),
};
assert_eq!(T::total_issuance(), initial_balance - amount);
assert_eq!(T::balance(&account), initial_balance - amount);
let balance_before = T::balance(&account);
let amount = balance_before + 1.into();
match T::withdraw(
&account,
amount,
Precision::Exact,
Preservation::Expendable,
Fortitude::Polite,
) {
Ok(_) => panic!("should have failed"),
Err(e) => assert_eq!(e, TokenError::FundsUnavailable.into()),
};
assert_eq!(T::total_issuance(), balance_before);
assert_eq!(T::balance(&account), balance_before);
let balance_before = T::balance(&account);
let amount = balance_before + 1.into();
match T::withdraw(
&account,
amount,
Precision::BestEffort,
Preservation::Expendable,
Fortitude::Polite,
) {
Ok(c) => assert_eq!(c.peek(), balance_before),
Err(_) => panic!("withdraw failed"),
};
assert_eq!(T::total_issuance(), 0.into());
assert_eq!(T::balance(&account), 0.into());
}
pub fn pair<T, AccountId>()
where
T: Balanced<AccountId>,
<T as Inspect<AccountId>>::Balance: AtLeast8BitUnsigned + Debug,
AccountId: AtLeast8BitUnsigned,
{
T::set_total_issuance(50.into());
let (credit, debt) = T::pair(0.into()).unwrap();
assert_eq!(debt.peek(), 0.into());
assert_eq!(credit.peek(), 0.into());
let balance = 10.into();
let (credit, debt) = T::pair(balance).unwrap();
assert_eq!(credit.peek(), balance);
assert_eq!(debt.peek(), balance);
let max_value = T::Balance::max_value();
let distance_from_max_value = 5.into();
T::set_total_issuance(max_value - distance_from_max_value);
T::pair(distance_from_max_value + 5.into()).unwrap_err();
}