diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index f1b116a0f4a0d4ec612aed393290f5f18adb8ddd..10e50136ef65d7a51dda5f680ddaccf6eae7d4e2 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1775,3 +1775,336 @@ fn asset_destroy_refund_existence_deposit() { assert_eq!(Balances::reserved_balance(&admin), 0); }); } + +mod sets { + 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)); + }); + } +}