From aa89327a23bd43a3b6828cc849aa3b3e2434f74d Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Sat, 25 Sep 2021 16:07:10 +1200 Subject: [PATCH 1/3] fungible helpers --- tokens/src/impls.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++ tokens/src/lib.rs | 2 + 2 files changed, 125 insertions(+) create mode 100644 tokens/src/impls.rs diff --git a/tokens/src/impls.rs b/tokens/src/impls.rs new file mode 100644 index 000000000..ad107b15b --- /dev/null +++ b/tokens/src/impls.rs @@ -0,0 +1,123 @@ +use codec::FullCodec; +use frame_support::traits::{ + fungible, fungibles, + tokens::{DepositConsequence, WithdrawConsequence}, + Contains, +}; +use sp_runtime::traits::AtLeast32BitUnsigned; +use sp_std::fmt::Debug; + +pub struct Combiner(sp_std::marker::PhantomData<(AccountId, TestKey, A, B)>); + +impl fungibles::Inspect for Combiner +where + TestKey: Contains<>::AssetId>, + A: fungible::Inspect>::Balance>, + B: fungibles::Inspect, +{ + type AssetId = >::AssetId; + type Balance = >::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + if TestKey::contains(&asset) { + A::total_issuance() + } else { + B::total_issuance(asset) + } + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + if TestKey::contains(&asset) { + A::minimum_balance() + } else { + B::minimum_balance(asset) + } + } + + fn balance(asset: Self::AssetId, who: &AccountId) -> Self::Balance { + if TestKey::contains(&asset) { + A::balance(who) + } else { + B::balance(asset, who) + } + } + + fn reducible_balance(asset: Self::AssetId, who: &AccountId, keep_alive: bool) -> Self::Balance { + if TestKey::contains(&asset) { + A::reducible_balance(who, keep_alive) + } else { + B::reducible_balance(asset, who, keep_alive) + } + } + + fn can_deposit(asset: Self::AssetId, who: &AccountId, amount: Self::Balance) -> DepositConsequence { + if TestKey::contains(&asset) { + A::can_deposit(who, amount) + } else { + B::can_deposit(asset, who, amount) + } + } + + fn can_withdraw( + asset: Self::AssetId, + who: &AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + if TestKey::contains(&asset) { + A::can_withdraw(who, amount) + } else { + B::can_withdraw(asset, who, amount) + } + } +} + +pub trait ConvertBalance { + fn convert_balance(amount: A) -> B; + fn convert_balance_back(amount: B) -> A; +} + +pub struct Mapper(sp_std::marker::PhantomData<(AccountId, T, C, B)>); +impl fungible::Inspect for Mapper +where + T: fungible::Inspect, + C: ConvertBalance<>::Balance, B>, + // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available + B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, +{ + type Balance = B; + + fn total_issuance() -> Self::Balance { + C::convert_balance(T::total_issuance()) + } + + fn minimum_balance() -> Self::Balance { + C::convert_balance(T::minimum_balance()) + } + + fn balance(who: &AccountId) -> Self::Balance { + C::convert_balance(T::balance(who)) + } + + fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance { + C::convert_balance(T::reducible_balance(who, keep_alive)) + } + + fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence { + T::can_deposit(who, C::convert_balance_back(amount)) + } + + fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { + use WithdrawConsequence::*; + let res = T::can_withdraw(who, C::convert_balance_back(amount)); + match res { + WithdrawConsequence::ReducedToZero(b) => WithdrawConsequence::ReducedToZero(C::convert_balance(b)), + NoFunds => NoFunds, + WouldDie => WouldDie, + UnknownAsset => UnknownAsset, + Underflow => Underflow, + Overflow => Overflow, + Frozen => Frozen, + Success => Success, + } + } +} diff --git a/tokens/src/lib.rs b/tokens/src/lib.rs index 6f3fa5f74..dffa5f125 100644 --- a/tokens/src/lib.rs +++ b/tokens/src/lib.rs @@ -75,10 +75,12 @@ use orml_traits::{ }; mod imbalances; +mod impls; mod mock; mod tests; mod weights; +pub use impls::*; pub use weights::WeightInfo; pub struct TransferDust(marker::PhantomData<(T, GetAccountId)>); From 0e83096c6716dc413eda4a6f3325bf59e563159e Mon Sep 17 00:00:00 2001 From: Frank Yin Date: Mon, 27 Sep 2021 17:08:49 +0800 Subject: [PATCH 2/3] implement mutate and transfers (#620) --- tokens/src/impls.rs | 108 ++++++++++++++++++++++++++---- tokens/src/tests.rs | 156 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+), 11 deletions(-) diff --git a/tokens/src/impls.rs b/tokens/src/impls.rs index ad107b15b..40130ef36 100644 --- a/tokens/src/impls.rs +++ b/tokens/src/impls.rs @@ -1,8 +1,9 @@ use codec::FullCodec; +use frame_support::dispatch::{DispatchError, DispatchResult}; use frame_support::traits::{ fungible, fungibles, tokens::{DepositConsequence, WithdrawConsequence}, - Contains, + Contains, Get, }; use sp_runtime::traits::AtLeast32BitUnsigned; use sp_std::fmt::Debug; @@ -71,44 +72,93 @@ where } } +impl fungibles::Transfer for Combiner +where + TestKey: Contains<>::AssetId>, + A: fungible::Transfer>::Balance>, + B: fungibles::Transfer, +{ + fn transfer( + asset: Self::AssetId, + source: &AccountId, + dest: &AccountId, + amount: Self::Balance, + keep_alive: bool, + ) -> Result { + if TestKey::contains(&asset) { + A::transfer(source, dest, amount, keep_alive) + } else { + B::transfer(asset, source, dest, amount, keep_alive) + } + } +} + +impl fungibles::Mutate for Combiner +where + TestKey: Contains<>::AssetId>, + A: fungible::Mutate>::Balance>, + B: fungibles::Mutate, +{ + fn mint_into(asset: Self::AssetId, dest: &AccountId, amount: Self::Balance) -> DispatchResult { + if TestKey::contains(&asset) { + A::mint_into(dest, amount) + } else { + B::mint_into(asset, dest, amount) + } + } + + fn burn_from( + asset: Self::AssetId, + dest: &AccountId, + amount: Self::Balance, + ) -> Result { + if TestKey::contains(&asset) { + A::burn_from(dest, amount) + } else { + B::burn_from(asset, dest, amount) + } + } +} + pub trait ConvertBalance { fn convert_balance(amount: A) -> B; fn convert_balance_back(amount: B) -> A; } -pub struct Mapper(sp_std::marker::PhantomData<(AccountId, T, C, B)>); -impl fungible::Inspect for Mapper +pub struct Mapper(sp_std::marker::PhantomData<(AccountId, T, C, B, GetCurrencyId)>); +impl fungible::Inspect for Mapper where - T: fungible::Inspect, - C: ConvertBalance<>::Balance, B>, + T: fungibles::Inspect, + C: ConvertBalance<>::Balance, B>, // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, + GetCurrencyId: Get<>::AssetId>, { type Balance = B; fn total_issuance() -> Self::Balance { - C::convert_balance(T::total_issuance()) + C::convert_balance(T::total_issuance(GetCurrencyId::get())) } fn minimum_balance() -> Self::Balance { - C::convert_balance(T::minimum_balance()) + C::convert_balance(T::minimum_balance(GetCurrencyId::get())) } fn balance(who: &AccountId) -> Self::Balance { - C::convert_balance(T::balance(who)) + C::convert_balance(T::balance(GetCurrencyId::get(), who)) } fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance { - C::convert_balance(T::reducible_balance(who, keep_alive)) + C::convert_balance(T::reducible_balance(GetCurrencyId::get(), who, keep_alive)) } fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence { - T::can_deposit(who, C::convert_balance_back(amount)) + T::can_deposit(GetCurrencyId::get(), who, C::convert_balance_back(amount)) } fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { use WithdrawConsequence::*; - let res = T::can_withdraw(who, C::convert_balance_back(amount)); + let res = T::can_withdraw(GetCurrencyId::get(), who, C::convert_balance_back(amount)); match res { WithdrawConsequence::ReducedToZero(b) => WithdrawConsequence::ReducedToZero(C::convert_balance(b)), NoFunds => NoFunds, @@ -121,3 +171,39 @@ where } } } + +impl fungible::Transfer for Mapper +where + T: fungibles::Transfer, + C: ConvertBalance<>::Balance, B>, + // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available + B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, + GetCurrencyId: Get<>::AssetId>, +{ + fn transfer(source: &AccountId, dest: &AccountId, amount: B, keep_alive: bool) -> Result { + T::transfer( + GetCurrencyId::get(), + source, + dest, + C::convert_balance_back(amount), + keep_alive, + ) + } +} + +impl fungible::Mutate for Mapper +where + T: fungibles::Mutate, + C: ConvertBalance<>::Balance, B>, + // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available + B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, + GetCurrencyId: Get<>::AssetId>, +{ + fn mint_into(dest: &AccountId, amount: Self::Balance) -> DispatchResult { + T::mint_into(GetCurrencyId::get(), dest, C::convert_balance_back(amount)) + } + + fn burn_from(dest: &AccountId, amount: Self::Balance) -> Result { + T::burn_from(GetCurrencyId::get(), dest, C::convert_balance_back(amount)) + } +} diff --git a/tokens/src/tests.rs b/tokens/src/tests.rs index 3aacd5916..693ff4c57 100644 --- a/tokens/src/tests.rs +++ b/tokens/src/tests.rs @@ -2079,3 +2079,159 @@ fn fungibles_mutate_hold_trait_should_work() { ); }); } + +#[test] +fn fungibles_inspect_convert_should_work() { + pub struct ConvertBalanceTest; + impl ConvertBalance for ConvertBalanceTest { + fn convert_balance(balance: Balance) -> Balance { + balance * 100 + } + + fn convert_balance_back(balance: Balance) -> Balance { + balance / 100 + } + } + + pub struct IsLiquidToken; + impl Contains for IsLiquidToken { + fn contains(currency_id: &CurrencyId) -> bool { + matches!(currency_id, &DOT) + } + } + + pub struct GetCurrencyId; + impl Get for GetCurrencyId { + fn get() -> CurrencyId { + DOT + } + } + + type RebaseTokens = Combiner< + AccountId, + IsLiquidToken, + Mapper, + Tokens, + >; + + ExtBuilder::default() + .balances(vec![(ALICE, DOT, 100), (BOB, DOT, 100)]) + .build() + .execute_with(|| { + assert_eq!( + >::balance(DOT, &ALICE), + 10000 + ); + assert_eq!( + >::total_issuance(DOT), + 20000 + ); + }); +} + +#[test] +fn fungibles_transfers_convert_should_work() { + pub struct ConvertBalanceTest; + impl ConvertBalance for ConvertBalanceTest { + fn convert_balance(balance: Balance) -> Balance { + balance * 100 + } + + fn convert_balance_back(balance: Balance) -> Balance { + balance / 100 + } + } + + pub struct IsLiquidToken; + impl Contains for IsLiquidToken { + fn contains(currency_id: &CurrencyId) -> bool { + matches!(currency_id, &DOT) + } + } + + pub struct GetCurrencyId; + impl Get for GetCurrencyId { + fn get() -> CurrencyId { + DOT + } + } + + type RebaseTokens = Combiner< + AccountId, + IsLiquidToken, + Mapper, + Tokens, + >; + + ExtBuilder::default() + .balances(vec![(ALICE, DOT, 300), (BOB, DOT, 200)]) + .build() + .execute_with(|| { + assert_ok!(>::transfer( + DOT, &ALICE, &BOB, 10000, true + )); + assert_eq!( + >::balance(DOT, &ALICE), + 20000 + ); + assert_eq!( + >::balance(DOT, &BOB), + 30000 + ); + }); +} + +#[test] +fn fungibles_mutate_convert_should_work() { + pub struct ConvertBalanceTest; + impl ConvertBalance for ConvertBalanceTest { + fn convert_balance(balance: Balance) -> Balance { + balance * 100 + } + + fn convert_balance_back(balance: Balance) -> Balance { + balance / 100 + } + } + + pub struct IsLiquidToken; + impl Contains for IsLiquidToken { + fn contains(currency_id: &CurrencyId) -> bool { + matches!(currency_id, &DOT) + } + } + + pub struct GetCurrencyId; + impl Get for GetCurrencyId { + fn get() -> CurrencyId { + DOT + } + } + + type RebaseTokens = Combiner< + AccountId, + IsLiquidToken, + Mapper, + Tokens, + >; + + ExtBuilder::default() + .balances(vec![(ALICE, DOT, 300), (BOB, DOT, 200)]) + .build() + .execute_with(|| { + assert_ok!(>::mint_into( + DOT, &ALICE, 10000 + )); + assert_ok!(>::burn_from( + DOT, &BOB, 10000 + )); + assert_eq!( + >::balance(DOT, &ALICE), + 40000 + ); + assert_eq!( + >::balance(DOT, &BOB), + 10000 + ); + }); +} From 734c2edaf62a3565096f4b639a90e3488a3294e2 Mon Sep 17 00:00:00 2001 From: Frank Yin Date: Tue, 5 Oct 2021 14:28:39 +0800 Subject: [PATCH 3/3] use asset id as an argument --- tokens/src/impls.rs | 64 ++++++++++++++++++++++++++++++++++----------- tokens/src/tests.rs | 15 ++++++----- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/tokens/src/impls.rs b/tokens/src/impls.rs index 40130ef36..272722b5a 100644 --- a/tokens/src/impls.rs +++ b/tokens/src/impls.rs @@ -121,15 +121,20 @@ where } pub trait ConvertBalance { - fn convert_balance(amount: A) -> B; - fn convert_balance_back(amount: B) -> A; + type AssetId; + fn convert_balance(amount: A, asset_id: Self::AssetId) -> B; + fn convert_balance_back(amount: B, asset_id: Self::AssetId) -> A; } pub struct Mapper(sp_std::marker::PhantomData<(AccountId, T, C, B, GetCurrencyId)>); impl fungible::Inspect for Mapper where T: fungibles::Inspect, - C: ConvertBalance<>::Balance, B>, + C: ConvertBalance< + >::Balance, + B, + AssetId = >::AssetId, + >, // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, GetCurrencyId: Get<>::AssetId>, @@ -137,30 +142,43 @@ where type Balance = B; fn total_issuance() -> Self::Balance { - C::convert_balance(T::total_issuance(GetCurrencyId::get())) + C::convert_balance(T::total_issuance(GetCurrencyId::get()), GetCurrencyId::get()) } fn minimum_balance() -> Self::Balance { - C::convert_balance(T::minimum_balance(GetCurrencyId::get())) + C::convert_balance(T::minimum_balance(GetCurrencyId::get()), GetCurrencyId::get()) } fn balance(who: &AccountId) -> Self::Balance { - C::convert_balance(T::balance(GetCurrencyId::get(), who)) + C::convert_balance(T::balance(GetCurrencyId::get(), who), GetCurrencyId::get()) } fn reducible_balance(who: &AccountId, keep_alive: bool) -> Self::Balance { - C::convert_balance(T::reducible_balance(GetCurrencyId::get(), who, keep_alive)) + C::convert_balance( + T::reducible_balance(GetCurrencyId::get(), who, keep_alive), + GetCurrencyId::get(), + ) } fn can_deposit(who: &AccountId, amount: Self::Balance) -> DepositConsequence { - T::can_deposit(GetCurrencyId::get(), who, C::convert_balance_back(amount)) + T::can_deposit( + GetCurrencyId::get(), + who, + C::convert_balance_back(amount, GetCurrencyId::get()), + ) } fn can_withdraw(who: &AccountId, amount: Self::Balance) -> WithdrawConsequence { use WithdrawConsequence::*; - let res = T::can_withdraw(GetCurrencyId::get(), who, C::convert_balance_back(amount)); + let res = T::can_withdraw( + GetCurrencyId::get(), + who, + C::convert_balance_back(amount, GetCurrencyId::get()), + ); match res { - WithdrawConsequence::ReducedToZero(b) => WithdrawConsequence::ReducedToZero(C::convert_balance(b)), + WithdrawConsequence::ReducedToZero(b) => { + WithdrawConsequence::ReducedToZero(C::convert_balance(b, GetCurrencyId::get())) + } NoFunds => NoFunds, WouldDie => WouldDie, UnknownAsset => UnknownAsset, @@ -175,7 +193,11 @@ where impl fungible::Transfer for Mapper where T: fungibles::Transfer, - C: ConvertBalance<>::Balance, B>, + C: ConvertBalance< + >::Balance, + B, + AssetId = >::AssetId, + >, // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, GetCurrencyId: Get<>::AssetId>, @@ -185,7 +207,7 @@ where GetCurrencyId::get(), source, dest, - C::convert_balance_back(amount), + C::convert_balance_back(amount, GetCurrencyId::get()), keep_alive, ) } @@ -194,16 +216,28 @@ where impl fungible::Mutate for Mapper where T: fungibles::Mutate, - C: ConvertBalance<>::Balance, B>, + C: ConvertBalance< + >::Balance, + B, + AssetId = >::AssetId, + >, // TOOD: use trait Balance after https://github.com/paritytech/substrate/pull/9863 is available B: AtLeast32BitUnsigned + FullCodec + Copy + Default + Debug, GetCurrencyId: Get<>::AssetId>, { fn mint_into(dest: &AccountId, amount: Self::Balance) -> DispatchResult { - T::mint_into(GetCurrencyId::get(), dest, C::convert_balance_back(amount)) + T::mint_into( + GetCurrencyId::get(), + dest, + C::convert_balance_back(amount, GetCurrencyId::get()), + ) } fn burn_from(dest: &AccountId, amount: Self::Balance) -> Result { - T::burn_from(GetCurrencyId::get(), dest, C::convert_balance_back(amount)) + T::burn_from( + GetCurrencyId::get(), + dest, + C::convert_balance_back(amount, GetCurrencyId::get()), + ) } } diff --git a/tokens/src/tests.rs b/tokens/src/tests.rs index 693ff4c57..1e45ba13c 100644 --- a/tokens/src/tests.rs +++ b/tokens/src/tests.rs @@ -2084,11 +2084,12 @@ fn fungibles_mutate_hold_trait_should_work() { fn fungibles_inspect_convert_should_work() { pub struct ConvertBalanceTest; impl ConvertBalance for ConvertBalanceTest { - fn convert_balance(balance: Balance) -> Balance { + type AssetId = CurrencyId; + fn convert_balance(balance: Balance, _asset_id: CurrencyId) -> Balance { balance * 100 } - fn convert_balance_back(balance: Balance) -> Balance { + fn convert_balance_back(balance: Balance, _asset_id: CurrencyId) -> Balance { balance / 100 } } @@ -2133,11 +2134,12 @@ fn fungibles_inspect_convert_should_work() { fn fungibles_transfers_convert_should_work() { pub struct ConvertBalanceTest; impl ConvertBalance for ConvertBalanceTest { - fn convert_balance(balance: Balance) -> Balance { + type AssetId = CurrencyId; + fn convert_balance(balance: Balance, _asset_id: CurrencyId) -> Balance { balance * 100 } - fn convert_balance_back(balance: Balance) -> Balance { + fn convert_balance_back(balance: Balance, _asset_id: CurrencyId) -> Balance { balance / 100 } } @@ -2185,11 +2187,12 @@ fn fungibles_transfers_convert_should_work() { fn fungibles_mutate_convert_should_work() { pub struct ConvertBalanceTest; impl ConvertBalance for ConvertBalanceTest { - fn convert_balance(balance: Balance) -> Balance { + type AssetId = CurrencyId; + fn convert_balance(balance: Balance, _asset_id: CurrencyId) -> Balance { balance * 100 } - fn convert_balance_back(balance: Balance) -> Balance { + fn convert_balance_back(balance: Balance, _asset_id: CurrencyId) -> Balance { balance / 100 } }