Skip to content

[feat] extend NamedMultiReservable #732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tokens/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,8 +1476,8 @@ impl<T: Config> NamedMultiReservableCurrency<T::AccountId> for Pallet<T> {
})
}

/// Slash from reserved balance, returning the negative imbalance created,
/// and any amount that was unable to be slashed.
/// Slash from reserved balance, returning the amount that was unable to be
/// slashed.
///
/// Is a no-op if the value to be slashed is zero.
fn slash_reserved_named(
Expand Down
88 changes: 88 additions & 0 deletions tokens/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,94 @@ fn slashed_reserved_named_works() {
});
}

#[test]
fn named_multi_reservable_ensure_named_reserved_works() {
ExtBuilder::default()
.balances(vec![(ALICE, DOT, 100)])
.build()
.execute_with(|| {
assert_ok!(Tokens::reserve_named(&RID_1, DOT, &ALICE, 50));
assert_eq!(Tokens::free_balance(DOT, &ALICE), 50);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 50);
assert_eq!(Tokens::total_issuance(DOT), 100);

assert_ok!(Tokens::ensure_reserved_named(&RID_1, DOT, &ALICE, 20));
assert_ok!(Tokens::ensure_reserved_named(&RID_1, DOT, &ALICE, 70));

assert_eq!(Tokens::free_balance(DOT, &ALICE), 30);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 70);
});
}

#[test]
fn named_multi_reservable_unreserve_all_named() {
ExtBuilder::default()
.balances(vec![(ALICE, DOT, 100)])
.build()
.execute_with(|| {
assert_ok!(Tokens::reserve_named(&RID_1, DOT, &ALICE, 50));
assert_ok!(Tokens::reserve_named(&RID_1, DOT, &ALICE, 20));
assert_eq!(Tokens::free_balance(DOT, &ALICE), 30);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 70);

let value = Tokens::unreserve_all_named(&RID_1, DOT, &ALICE);
assert!(value == 70);

assert_eq!(Tokens::free_balance(DOT, &ALICE), 100);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 0);
});
}

#[test]
fn named_multi_reservable_slash_all_reserved_named() {
ExtBuilder::default()
.balances(vec![(ALICE, DOT, 100)])
.build()
.execute_with(|| {
assert_ok!(Tokens::reserve_named(&RID_1, DOT, &ALICE, 50));
assert_eq!(Tokens::free_balance(DOT, &ALICE), 50);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 50);

let value = Tokens::slash_all_reserved_named(&RID_1, DOT, &ALICE);
assert!(value == 0);

assert_eq!(Tokens::free_balance(DOT, &ALICE), 50);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 0);
});
}

#[test]
fn named_multi_reservable_repatriate_all_reserved_named_works() {
ExtBuilder::default()
.balances(vec![(ALICE, DOT, 100), (BOB, DOT, 100)])
.build()
.execute_with(|| {
assert_eq!(Tokens::free_balance(DOT, &ALICE), 100);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &ALICE), 0);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &BOB), 0);
assert_ok!(Tokens::reserve_named(&RID_1, DOT, &ALICE, 50));

assert_ok!(Tokens::repatriate_all_reserved_named(
&RID_1,
DOT,
&ALICE,
&BOB,
BalanceStatus::Reserved
));

assert_eq!(Tokens::free_balance(DOT, &BOB), 100);
assert_eq!(Tokens::reserved_balance_named(&RID_1, DOT, &BOB), 50);

System::assert_last_event(Event::Tokens(crate::Event::ReserveRepatriated {
currency_id: DOT,
from: ALICE,
to: BOB,
amount: 50,
status: BalanceStatus::Reserved,
}));
});
}

// *************************************************
// tests for CurrencyAdapter
// *************************************************
Expand Down
73 changes: 72 additions & 1 deletion traits/src/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use sp_runtime::{
DispatchError, DispatchResult,
};
use sp_std::{
cmp::{Eq, PartialEq},
cmp::{Eq, Ordering, PartialEq},
fmt::Debug,
result,
};
Expand Down Expand Up @@ -288,6 +288,77 @@ pub trait NamedMultiReservableCurrency<AccountId>: MultiReservableCurrency<Accou
value: Self::Balance,
status: BalanceStatus,
) -> result::Result<Self::Balance, DispatchError>;

/// Ensure the reserved balance is equal to `value`.
///
/// This will reserve extra amount of current reserved balance is less than
/// `value`. And unreserve if current reserved balance is greater than
/// `value`.
fn ensure_reserved_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &AccountId,
value: Self::Balance,
) -> DispatchResult {
let current = Self::reserved_balance_named(id, currency_id, who);
match current.cmp(&value) {
Ordering::Less => {
// we checked value > current
Self::reserve_named(id, currency_id, who, value - current)
}
Ordering::Equal => Ok(()),
Ordering::Greater => {
// we always have enough balance to unreserve here
Self::unreserve_named(id, currency_id, who, current - value);
Ok(())
}
}
}

/// Unreserve all the named reserved balances, returning unreserved amount.
///
/// Is a no-op if the value to be unreserved is zero.
fn unreserve_all_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &AccountId,
) -> Self::Balance {
let value = Self::reserved_balance_named(id, currency_id, who);
Self::unreserve_named(id, currency_id, who, value);
value
}

/// Slash all the reserved balance, returning the amount that was unable to
/// be slashed.
///
/// Is a no-op if the value to be slashed is zero.
fn slash_all_reserved_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
who: &AccountId,
) -> Self::Balance {
let value = Self::reserved_balance_named(id, currency_id, who);
Self::slash_reserved_named(id, currency_id, who, value)
}

/// Move all the named reserved balance of one account into the balance of
/// another, according to `status`. If `status` is `Reserved`, the balance
/// will be reserved with given `id`.
///
/// Is a no-op if:
/// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is
/// `Reserved`.
fn repatriate_all_reserved_named(
id: &Self::ReserveIdentifier,
currency_id: Self::CurrencyId,
slashed: &AccountId,
beneficiary: &AccountId,
status: BalanceStatus,
) -> DispatchResult {
let value = Self::reserved_balance_named(id, currency_id, slashed);
Self::repatriate_reserved_named(id, currency_id, slashed, beneficiary, value, status).map(|_| ())
}
}

/// Abstraction over a fungible (single) currency system.
Expand Down