mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 18:07:58 +00:00
UnionOf types for merged fungible and fungibles implementations (#2033)
Introduces `UnionOf` types, crafted to merge `fungible` and `fungibles` implementations or two `fungibles` implementations into a single type implementing `fungibles`. This also addresses an issue where `ItemOf` initiates a double drop for an imbalance type, leading to inaccurate total issuance accounting. Find the application of these types in this PR - [link](https://github.com/paritytech/polkadot-sdk/pull/2031), places in code - [1](https://github.com/paritytech/polkadot-sdk/blob/4ec7496fa2632385b08fae860fcf28a523a7b5de/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs#L327), [2](https://github.com/paritytech/polkadot-sdk/blob/4ec7496fa2632385b08fae860fcf28a523a7b5de/cumulus/parachains/runtimes/assets/asset-hub-kusama/src/lib.rs#L343). --------- Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: joepetrowski <joe@parity.io>
This commit is contained in:
@@ -1648,8 +1648,20 @@ pub mod pallet {
|
||||
T::AssetAccountDeposit::get()
|
||||
}
|
||||
|
||||
fn touch(asset: T::AssetId, who: T::AccountId, depositor: T::AccountId) -> DispatchResult {
|
||||
Self::do_touch(asset, who, depositor, false)
|
||||
fn should_touch(asset: T::AssetId, who: &T::AccountId) -> bool {
|
||||
match Asset::<T, I>::get(&asset) {
|
||||
Some(info) if info.is_sufficient => false,
|
||||
Some(_) => !Account::<T, I>::contains_key(asset, who),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn touch(
|
||||
asset: T::AssetId,
|
||||
who: &T::AccountId,
|
||||
depositor: &T::AccountId,
|
||||
) -> DispatchResult {
|
||||
Self::do_touch(asset, who.clone(), depositor.clone(), false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ use pallet_balances::Error as BalancesError;
|
||||
use sp_io::storage;
|
||||
use sp_runtime::{traits::ConvertInto, TokenError};
|
||||
|
||||
mod sets;
|
||||
|
||||
fn asset_ids() -> Vec<u32> {
|
||||
let mut s: Vec<_> = Assets::asset_ids().collect();
|
||||
s.sort();
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Tests for [`ItemOf`], [`fungible::UnionOf`] and [`fungibles::UnionOf`] set types.
|
||||
|
||||
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));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user