Skip to content
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

UnionOf types for merged fungible and fungibles implementations #2033

Merged
merged 23 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
333 changes: 333 additions & 0 deletions substrate/frame/assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1775,3 +1775,336 @@ fn asset_destroy_refund_existence_deposit() {
assert_eq!(Balances::reserved_balance(&admin), 0);
});
}

mod sets {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of extremely long files, maybe we can move this to its own file under tests/sets.rs? What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved

use super::*;
use frame_support::{
parameter_types,
traits::{
fungible,
fungible::ItemOf,
fungibles,
tokens::{
fungibles::{
Balanced as FungiblesBalanced, Create as FungiblesCreate,
Inspect as FungiblesInspect, Mutate as FungiblesMutate,
},
Fortitude, Precision, Preservation,
},
},
};
use sp_runtime::{traits::ConvertToValue, Either};

const FIRST_ASSET: u32 = 0;
const UNKNOWN_ASSET: u32 = 10;

parameter_types! {
pub const LeftAsset: Either<(), u32> = Either::Left(());
pub const RightAsset: Either<u32, ()> = Either::Right(());
pub const RightUnitAsset: Either<(), ()> = Either::Right(());
}

/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
/// single asset class from [`T`] identified by [`FIRST_ASSET`].
type FirstFungible<T> = ItemOf<T, frame_support::traits::ConstU32<{ FIRST_ASSET }>, u64>;
/// Implementation of the `fungible` traits through the [`ItemOf`] type, specifically for a
/// single asset class from [`T`] identified by [`UNKNOWN_ASSET`].
type UnknownFungible<T> = ItemOf<T, frame_support::traits::ConstU32<{ UNKNOWN_ASSET }>, u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`FirstFungible`] from the left.
type LeftFungible<T> =
fungible::UnionOf<FirstFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`LeftFungible`] from the right.
type RightFungible<T> = fungible::UnionOf<
UnknownFungible<T>,
LeftFungible<T>,
ConvertToValue<RightUnitAsset>,
(),
u64,
>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`RightFungible`] from the left.
type LeftFungibles<T> =
fungibles::UnionOf<RightFungible<T>, T, ConvertToValue<LeftAsset>, (), u64>;
/// Implementation of `fungibles` traits using [`fungibles::UnionOf`] that exclusively utilizes
/// the [`LeftFungibles`] from the right.
///
/// By using this type, we can navigate through each branch of [`fungible::UnionOf`],
/// [`fungibles::UnionOf`], and [`ItemOf`] to access the underlying `fungibles::*`
/// implementation provided by the pallet.
type First<T> = fungibles::UnionOf<T, LeftFungibles<T>, ConvertToValue<RightAsset>, (), u64>;

#[test]
fn deposit_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1 = 0;
let account1 = 1;
let account2 = 2;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));

assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::deposit((), &account2, 50, Precision::Exact).unwrap();
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 100);

assert_eq!(imb.peek(), 50);

let (imb1, imb2) = imb.split(30);
assert_eq!(imb1.peek(), 30);
assert_eq!(imb2.peek(), 20);

drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 120);

assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 70);
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 120);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn issue_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));

assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::issue((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(imb.peek(), 100);

let (imb1, imb2) = imb.split(30);
assert_eq!(imb1.peek(), 30);
assert_eq!(imb2.peek(), 70);

drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 130);

assert!(First::<Assets>::resolve(&account1, imb1).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 130);
assert_eq!(First::<Assets>::total_issuance(()), 130);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn pair_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));

assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let (debt, credit) = First::<Assets>::pair((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(debt.peek(), 100);
assert_eq!(credit.peek(), 100);

let (debt1, debt2) = debt.split(30);
assert_eq!(debt1.peek(), 30);
assert_eq!(debt2.peek(), 70);

drop(debt2);
assert_eq!(First::<Assets>::total_issuance(()), 170);

assert!(First::<Assets>::settle(&account1, debt1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 70);
assert_eq!(First::<Assets>::total_issuance(()), 170);

let (credit1, credit2) = credit.split(40);
assert_eq!(credit1.peek(), 40);
assert_eq!(credit2.peek(), 60);

drop(credit2);
assert_eq!(First::<Assets>::total_issuance(()), 110);

assert!(First::<Assets>::resolve(&account1, credit1).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 110);
assert_eq!(First::<Assets>::total_issuance(()), 110);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn rescind_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));

assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::rescind((), 20);
assert_eq!(First::<Assets>::total_issuance(()), 80);

assert_eq!(imb.peek(), 20);

let (imb1, imb2) = imb.split(15);
assert_eq!(imb1.peek(), 15);
assert_eq!(imb2.peek(), 5);

drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 85);

assert!(First::<Assets>::settle(&account1, imb1, Preservation::Preserve).is_ok());
assert_eq!(First::<Assets>::balance((), &account1), 85);
assert_eq!(First::<Assets>::total_issuance(()), 85);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn resolve_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
let account2: u64 = 2;
let ed = 11;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
assert_ok!(Assets::mint_into(asset1, &account1, 100));

assert_eq!(First::<Assets>::balance((), &account1), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::issue((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(imb.peek(), 100);

let (imb1, imb2) = imb.split(10);
assert_eq!(imb1.peek(), 10);
assert_eq!(imb2.peek(), 90);
assert_eq!(First::<Assets>::total_issuance(()), 200);

// ed requirements not met.
let imb1 = First::<Assets>::resolve(&account2, imb1).unwrap_err();
assert_eq!(imb1.peek(), 10);
drop(imb1);
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 0);

// resolve to new account `2`.
assert_ok!(First::<Assets>::resolve(&account2, imb2));
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 90);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn settle_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1: u32 = 0;
let account1: u64 = 1;
let account2: u64 = 2;
let ed = 11;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, ed));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_ok!(Assets::mint_into(asset1, &account2, 100));

assert_eq!(First::<Assets>::balance((), &account2), 100);
assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::rescind((), 100);
assert_eq!(First::<Assets>::total_issuance(()), 100);
assert_eq!(imb.peek(), 100);

let (imb1, imb2) = imb.split(10);
assert_eq!(imb1.peek(), 10);
assert_eq!(imb2.peek(), 90);
assert_eq!(First::<Assets>::total_issuance(()), 100);

// ed requirements not met.
let imb2 =
First::<Assets>::settle(&account2, imb2, Preservation::Preserve).unwrap_err();
assert_eq!(imb2.peek(), 90);
drop(imb2);
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 100);

// settle to account `1`.
assert_ok!(First::<Assets>::settle(&account2, imb1, Preservation::Preserve));
assert_eq!(First::<Assets>::total_issuance(()), 190);
assert_eq!(First::<Assets>::balance((), &account2), 90);

let imb = First::<Assets>::rescind((), 85);
assert_eq!(First::<Assets>::total_issuance(()), 105);
assert_eq!(imb.peek(), 85);

// settle to account `1` and expect some dust.
let imb = First::<Assets>::settle(&account2, imb, Preservation::Expendable).unwrap();
assert_eq!(imb.peek(), 5);
assert_eq!(First::<Assets>::total_issuance(()), 105);
assert_eq!(First::<Assets>::balance((), &account2), 0);

drop(imb);
assert_eq!(First::<Assets>::total_issuance(()), 100);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}

#[test]
fn withdraw_from_set_types_works() {
new_test_ext().execute_with(|| {
let asset1 = 0;
let account1 = 1;
let account2 = 2;

assert_ok!(<Assets as FungiblesCreate<u64>>::create(asset1, account1, true, 1));
assert_ok!(Assets::mint_into(asset1, &account1, 100));
assert_ok!(Assets::mint_into(asset1, &account2, 100));

assert_eq!(First::<Assets>::total_issuance(()), 200);
assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));

let imb = First::<Assets>::withdraw(
(),
&account2,
50,
Precision::Exact,
Preservation::Preserve,
Fortitude::Polite,
)
.unwrap();
assert_eq!(First::<Assets>::balance((), &account2), 50);
assert_eq!(First::<Assets>::total_issuance(()), 200);

assert_eq!(imb.peek(), 50);
drop(imb);
assert_eq!(First::<Assets>::total_issuance(()), 150);
assert_eq!(First::<Assets>::balance((), &account2), 50);

assert_eq!(First::<Assets>::total_issuance(()), Assets::total_issuance(asset1));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalance
pub(crate) fn new(amount: B) -> Self {
Self { amount, _phantom: PhantomData }
}

/// Forget the imbalance without invoking the on-drop handler.
pub(crate) fn forget(imbalance: Self) {
sp_std::mem::forget(imbalance);
}
}

impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>>
Expand Down
Loading
Loading