diff --git a/node/primitives/src/currency.rs b/node/primitives/src/currency.rs index 9baec6adce..ca37b92b23 100644 --- a/node/primitives/src/currency.rs +++ b/node/primitives/src/currency.rs @@ -75,9 +75,9 @@ macro_rules! create_currency_id { type Error = (); fn try_from(id: CurrencyId) -> Result { let _index = match id { - $(CurrencyId::Token(TokenSymbol::$symbol) => Ok((0_u32, TokenSymbol::$symbol as u32)),)* + $(CurrencyId::Native(TokenSymbol::$symbol) => Ok((0_u32, TokenSymbol::$symbol as u32)),)* $(CurrencyId::VToken(TokenSymbol::$symbol) => Ok((1_u32, TokenSymbol::$symbol as u32)),)* - $(CurrencyId::Native(TokenSymbol::$symbol) => Ok((2_u32, TokenSymbol::$symbol as u32)),)* + $(CurrencyId::Token(TokenSymbol::$symbol) => Ok((2_u32, TokenSymbol::$symbol as u32)),)* $(CurrencyId::Stable(TokenSymbol::$symbol) => Ok((3_u32, TokenSymbol::$symbol as u32)),)* $(CurrencyId::VSToken(TokenSymbol::$symbol) => Ok((4_u32, TokenSymbol::$symbol as u32)),)* _ => Err(()), @@ -110,9 +110,9 @@ macro_rules! create_currency_id { _ => Err(()), }; match c_discr { - 0 => Ok(CurrencyId::Token(token_symbol?)), + 0 => Ok(CurrencyId::Native(token_symbol?)), 1 => Ok(CurrencyId::VToken(token_symbol?)), - 2 => Ok(CurrencyId::Native(token_symbol?)), + 2 => Ok(CurrencyId::Token(token_symbol?)), 3 => Ok(CurrencyId::Stable(token_symbol?)), 4 => Ok(CurrencyId::VSToken(token_symbol?)), _ => Err(()), @@ -245,9 +245,9 @@ impl Default for TokenSymbol { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum CurrencyId { - Token(TokenSymbol), - VToken(TokenSymbol), Native(TokenSymbol), + VToken(TokenSymbol), + Token(TokenSymbol), Stable(TokenSymbol), VSToken(TokenSymbol), VSBond(TokenSymbol, ParaId, LeasePeriod, LeasePeriod), @@ -289,9 +289,9 @@ impl CurrencyId { pub fn discriminant(&self) -> u8 { match *self { - Self::Token(..) => 0, + Self::Native(..) => 0, Self::VToken(..) => 1, - Self::Native(..) => 2, + Self::Token(..) => 2, Self::Stable(..) => 3, Self::VSToken(..) => 4, Self::VSBond(..) => 5, @@ -361,9 +361,9 @@ impl TryFrom for CurrencyId { let token_symbol = TokenSymbol::try_from(t_discr)?; match c_discr { - 0 => Ok(Self::Token(token_symbol)), + 0 => Ok(Self::Native(token_symbol)), 1 => Ok(Self::VToken(token_symbol)), - 2 => Ok(Self::Native(token_symbol)), + 2 => Ok(Self::Token(token_symbol)), 3 => Ok(Self::Stable(token_symbol)), 4 => Ok(Self::VSToken(token_symbol)), 5 => Ok(Self::VSBond(token_symbol, pid, lp1, lp2)), diff --git a/node/primitives/src/tests.rs b/node/primitives/src/tests.rs index 650c70f380..4844f91449 100644 --- a/node/primitives/src/tests.rs +++ b/node/primitives/src/tests.rs @@ -30,12 +30,12 @@ fn currency_id_from_string_should_work() { #[test] fn currency_id_to_u64_should_work() { - let e00 = CurrencyId::Token(TokenSymbol::ASG); - let e01 = CurrencyId::Token(TokenSymbol::BNC); - let e02 = CurrencyId::Token(TokenSymbol::AUSD); - let e03 = CurrencyId::Token(TokenSymbol::DOT); - let e04 = CurrencyId::Token(TokenSymbol::KSM); - let e05 = CurrencyId::Token(TokenSymbol::ETH); + let e00 = CurrencyId::Native(TokenSymbol::ASG); + let e01 = CurrencyId::Native(TokenSymbol::BNC); + let e02 = CurrencyId::Native(TokenSymbol::AUSD); + let e03 = CurrencyId::Native(TokenSymbol::DOT); + let e04 = CurrencyId::Native(TokenSymbol::KSM); + let e05 = CurrencyId::Native(TokenSymbol::ETH); assert_eq!(0x0000_0000_0000_0000, e00.currency_id()); assert_eq!(0x0001_0000_0000_0000, e01.currency_id()); @@ -58,12 +58,12 @@ fn currency_id_to_u64_should_work() { assert_eq!(0x0104_0000_0000_0000, e14.currency_id()); assert_eq!(0x0105_0000_0000_0000, e15.currency_id()); - let e20 = CurrencyId::Native(TokenSymbol::ASG); - let e21 = CurrencyId::Native(TokenSymbol::BNC); - let e22 = CurrencyId::Native(TokenSymbol::AUSD); - let e23 = CurrencyId::Native(TokenSymbol::DOT); - let e24 = CurrencyId::Native(TokenSymbol::KSM); - let e25 = CurrencyId::Native(TokenSymbol::ETH); + let e20 = CurrencyId::Token(TokenSymbol::ASG); + let e21 = CurrencyId::Token(TokenSymbol::BNC); + let e22 = CurrencyId::Token(TokenSymbol::AUSD); + let e23 = CurrencyId::Token(TokenSymbol::DOT); + let e24 = CurrencyId::Token(TokenSymbol::KSM); + let e25 = CurrencyId::Token(TokenSymbol::ETH); assert_eq!(0x0200_0000_0000_0000, e20.currency_id()); assert_eq!(0x0201_0000_0000_0000, e21.currency_id()); @@ -117,12 +117,12 @@ fn currency_id_to_u64_should_work() { #[test] fn u64_to_currency_id_should_work() { - let e00 = CurrencyId::Token(TokenSymbol::ASG); - let e01 = CurrencyId::Token(TokenSymbol::BNC); - let e02 = CurrencyId::Token(TokenSymbol::AUSD); - let e03 = CurrencyId::Token(TokenSymbol::DOT); - let e04 = CurrencyId::Token(TokenSymbol::KSM); - let e05 = CurrencyId::Token(TokenSymbol::ETH); + let e00 = CurrencyId::Native(TokenSymbol::ASG); + let e01 = CurrencyId::Native(TokenSymbol::BNC); + let e02 = CurrencyId::Native(TokenSymbol::AUSD); + let e03 = CurrencyId::Native(TokenSymbol::DOT); + let e04 = CurrencyId::Native(TokenSymbol::KSM); + let e05 = CurrencyId::Native(TokenSymbol::ETH); assert_eq!(e00, CurrencyId::try_from(0x0000_0000_0000_0000).unwrap()); assert_eq!(e01, CurrencyId::try_from(0x0001_0000_0000_0000).unwrap()); @@ -145,12 +145,12 @@ fn u64_to_currency_id_should_work() { assert_eq!(e14, CurrencyId::try_from(0x0104_0000_0000_0000).unwrap()); assert_eq!(e15, CurrencyId::try_from(0x0105_0000_0000_0000).unwrap()); - let e20 = CurrencyId::Native(TokenSymbol::ASG); - let e21 = CurrencyId::Native(TokenSymbol::BNC); - let e22 = CurrencyId::Native(TokenSymbol::AUSD); - let e23 = CurrencyId::Native(TokenSymbol::DOT); - let e24 = CurrencyId::Native(TokenSymbol::KSM); - let e25 = CurrencyId::Native(TokenSymbol::ETH); + let e20 = CurrencyId::Token(TokenSymbol::ASG); + let e21 = CurrencyId::Token(TokenSymbol::BNC); + let e22 = CurrencyId::Token(TokenSymbol::AUSD); + let e23 = CurrencyId::Token(TokenSymbol::DOT); + let e24 = CurrencyId::Token(TokenSymbol::KSM); + let e25 = CurrencyId::Token(TokenSymbol::ETH); assert_eq!(e20, CurrencyId::try_from(0x0200_0000_0000_0000).unwrap()); assert_eq!(e21, CurrencyId::try_from(0x0201_0000_0000_0000).unwrap()); diff --git a/pallets/flexible-fee/src/fee_dealer.rs b/pallets/flexible-fee/src/fee_dealer.rs new file mode 100644 index 0000000000..8d09f9f71f --- /dev/null +++ b/pallets/flexible-fee/src/fee_dealer.rs @@ -0,0 +1,77 @@ +// This file is part of Bifrost. + +// Copyright (C) 2019-2021 Liebi Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// The swap pool algorithm implements Balancer protocol +// For more details, refer to https://balancer.finance/whitepaper/ + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_runtime::traits::UniqueSaturatedFrom; + +use super::*; +use crate::Config; + +pub trait FeeDealer { + fn ensure_can_charge_fee( + who: &AccountId, + fee: Balance, + reason: WithdrawReasons, + ) -> DispatchResult; +} + +pub struct FixedCurrencyFeeRate(PhantomData); + +impl FeeDealer> for FixedCurrencyFeeRate { + /// Make sure there are enough BNC to be deducted if the user has assets in other form of tokens + /// rather than BNC. + fn ensure_can_charge_fee( + who: &T::AccountId, + fee: PalletBalanceOf, + _reason: WithdrawReasons, + ) -> DispatchResult { + let fee_currency_id: CurrencyId = T::AlternativeFeeCurrencyId::get(); + let (fee_currency_base, native_currency_base): (u32, u32) = + T::AltFeeCurrencyExchangeRate::get(); + + let fee_currency_balance = T::MultiCurrency::free_balance(fee_currency_id, who); + + let consume_fee_currency_amount = + fee.saturating_mul(fee_currency_base.into()) / native_currency_base.into(); + ensure!( + consume_fee_currency_amount <= + PalletBalanceOf::::unique_saturated_from(fee_currency_balance.into()), + Error::::NotEnoughBalance + ); + + // deduct fee currency and increase native currency amount + // This withdraw operation allows death. So it will succeed given the remaining amount less + // than the existential deposit. + T::MultiCurrency::withdraw( + fee_currency_id, + who, + T::Balance::from(consume_fee_currency_amount), + )?; + T::MultiCurrency::deposit(T::NativeCurrencyId::get(), who, T::Balance::from(fee))?; + + crate::Pallet::::deposit_event(Event::FixedRateFeeExchanged( + fee_currency_id, + consume_fee_currency_amount, + )); + Ok(()) + } +} diff --git a/pallets/flexible-fee/src/lib.rs b/pallets/flexible-fee/src/lib.rs index a62891a8dc..842a0b199f 100644 --- a/pallets/flexible-fee/src/lib.rs +++ b/pallets/flexible-fee/src/lib.rs @@ -24,7 +24,6 @@ use core::convert::{Into, TryFrom}; use frame_support::{ - dispatch::DispatchResultWithPostInfo, pallet_prelude::*, traits::{ Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, ReservableCurrency, @@ -45,7 +44,10 @@ use sp_runtime::{ use sp_std::{vec, vec::Vec}; use zenlink_protocol::{AssetBalance, AssetId, ExportZenlink}; +use crate::fee_dealer::FeeDealer; + mod default_weight; +pub mod fee_dealer; mod mock; mod tests; @@ -87,9 +89,17 @@ pub mod pallet { /// Handler for the unbalanced decrease type OnUnbalanced: OnUnbalanced>; type DexOperator: ExportZenlink; + type FeeDealer: FeeDealer>; #[pallet::constant] type NativeCurrencyId: Get; + + #[pallet::constant] + type AlternativeFeeCurrencyId: Get; + + /// Alternative Fee currency exchange rate: ?x Fee currency: ?y Native currency + #[pallet::constant] + type AltFeeCurrencyExchangeRate: Get<(u32, u32)>; } pub type PalletBalanceOf = @@ -108,6 +118,7 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { FlexibleFeeExchanged(CurrencyId, u128), // token and amount + FixedRateFeeExchanged(CurrencyId, PalletBalanceOf), } #[pallet::type_value] @@ -117,8 +128,8 @@ pub mod pallet { CurrencyId::Stable(TokenSymbol::AUSD), CurrencyId::Token(TokenSymbol::DOT), CurrencyId::VToken(TokenSymbol::DOT), - CurrencyId::Token(TokenSymbol::ETH), - CurrencyId::VToken(TokenSymbol::ETH), + CurrencyId::Token(TokenSymbol::KSM), + CurrencyId::VToken(TokenSymbol::KSM), ] .to_vec() } @@ -139,7 +150,7 @@ pub mod pallet { #[pallet::error] pub enum Error { - ConversionError, + NotEnoughBalance, } #[pallet::call] @@ -149,7 +160,7 @@ pub mod pallet { pub fn set_user_fee_charge_order( origin: OriginFor, asset_order_list_vec: Option>, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { let who = ensure_signed(origin)?; if let Some(mut asset_order_list) = asset_order_list_vec { @@ -175,88 +186,6 @@ impl Pallet { charge_order_list } - /// Make sure there are enough BNC to be deducted if the user has assets in other form of tokens - /// rather than BNC. - fn ensure_can_charge_fee( - who: &T::AccountId, - fee: PalletBalanceOf, - reason: WithdrawReasons, - ) -> Result<(), Error> { - // get the user defined fee charge order list. - let user_fee_charge_order_list = Self::inner_get_user_fee_charge_order_list(who); - let existential_deposit = <::Currency as Currency< - ::AccountId, - >>::minimum_balance(); - - // charge the fee by the order of the above order list. - // first to check whether the user has the asset. If no, pass it. If yes, try to make - // transaction in the DEX in exchange for BNC - for currency_id in user_fee_charge_order_list { - let native_asset_id: AssetId = AssetId::try_from(T::NativeCurrencyId::get()) - .map_err(|_| Error::ConversionError)?; - // If it is mainnet currency - if currency_id == T::NativeCurrencyId::get() { - // check native balance if is enough - let native_is_enough = <::Currency as Currency< - ::AccountId, - >>::free_balance(who) - .checked_sub(&(fee + existential_deposit.into())) - .map_or(false, |new_free_balance| { - <::Currency as Currency< - ::AccountId, - >>::ensure_can_withdraw( - who, fee, reason, new_free_balance - ) - .is_ok() - }); - - if native_is_enough { - // native balance is enough, break iteration - break; - } - } else { - // If it is other assets - let native_balance = - T::MultiCurrency::free_balance(T::NativeCurrencyId::get(), who); - - // If native token balance is below existential deposit requirement, - // go exchange fee + existential deposit. Else to exchange fee amount. - let amount_out: AssetBalance; - if native_balance > T::Balance::from(existential_deposit) { - amount_out = fee.saturated_into(); - } else { - amount_out = (fee + existential_deposit).saturated_into(); - } - - let asset_balance = T::MultiCurrency::free_balance(currency_id, who); - let asset_id: AssetId = - AssetId::try_from(currency_id).map_err(|_| Error::::ConversionError)?; - - let path = vec![asset_id, native_asset_id]; - let amount_in_max: AssetBalance = asset_balance.saturated_into(); - - // query for amount in - let amounts = - T::DexOperator::get_amount_in_by_path(amount_out, &path).map_or(vec![0], |v| v); - - if T::DexOperator::inner_swap_assets_for_exact_assets( - &who, - amount_out, - amount_in_max, - &path, - &who, - ) - .is_ok() - { - Self::deposit_event(Event::FlexibleFeeExchanged(currency_id, amounts[0])); - // successfully swap, break iteration - break; - } - } - } - Ok(()) - } - /// This function is for runtime-api to call pub fn cal_fee_token_and_amount( who: &T::AccountId, @@ -339,12 +268,13 @@ where } else { WithdrawReasons::TRANSACTION_PAYMENT | WithdrawReasons::TIP }; + // Make sure there are enough BNC to be deducted if the user has assets in other form of // tokens rather than BNC. - Self::ensure_can_charge_fee(who, fee, withdraw_reason) + T::FeeDealer::ensure_can_charge_fee(who, fee, withdraw_reason) .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Payment))?; - match T::Currency::withdraw(who, fee, withdraw_reason, ExistenceRequirement::KeepAlive) { + match T::Currency::withdraw(who, fee, withdraw_reason, ExistenceRequirement::AllowDeath) { Ok(imbalance) => Ok(Some(imbalance)), Err(_msg) => Err(InvalidTransaction::Payment.into()), } @@ -366,6 +296,10 @@ where if let Some(paid) = already_withdrawn { // Calculate how much refund we should return let refund_amount = paid.peek().saturating_sub(corrected_fee); + + #[cfg(feature = "std")] + println!("refund_amount: {:?}", refund_amount); + // refund to the the account that paid the fees. If this fails, the // account might have dropped below the existential balance. In // that case we don't refund anything. @@ -385,3 +319,87 @@ where Ok(()) } } + +impl FeeDealer> for Pallet { + /// Make sure there are enough BNC to be deducted if the user has assets in other form of tokens + /// rather than BNC. + fn ensure_can_charge_fee( + who: &T::AccountId, + fee: PalletBalanceOf, + reason: WithdrawReasons, + ) -> DispatchResult { + // get the user defined fee charge order list. + let user_fee_charge_order_list = Self::inner_get_user_fee_charge_order_list(who); + let existential_deposit = <::Currency as Currency< + ::AccountId, + >>::minimum_balance(); + + // charge the fee by the order of the above order list. + // first to check whether the user has the asset. If no, pass it. If yes, try to make + // transaction in the DEX in exchange for BNC + for currency_id in user_fee_charge_order_list { + let native_asset_id: AssetId = AssetId::try_from(T::NativeCurrencyId::get()) + .map_err(|_| DispatchError::Other("Conversion Error."))?; + // If it is mainnet currency + if currency_id == T::NativeCurrencyId::get() { + // check native balance if is enough + let native_is_enough = <::Currency as Currency< + ::AccountId, + >>::free_balance(who) + .checked_sub(&(fee + existential_deposit.into())) + .map_or(false, |new_free_balance| { + <::Currency as Currency< + ::AccountId, + >>::ensure_can_withdraw( + who, fee, reason, new_free_balance + ) + .is_ok() + }); + + if native_is_enough { + // native balance is enough, break iteration + break; + } + } else { + // If it is other assets + let native_balance = + T::MultiCurrency::free_balance(T::NativeCurrencyId::get(), who); + + // If native token balance is below existential deposit requirement, + // go exchange fee + existential deposit. Else to exchange fee amount. + let amount_out: AssetBalance; + if native_balance > T::Balance::from(existential_deposit) { + amount_out = fee.saturated_into(); + } else { + amount_out = (fee + existential_deposit).saturated_into(); + } + + let asset_balance = T::MultiCurrency::free_balance(currency_id, who); + let asset_id: AssetId = AssetId::try_from(currency_id) + .map_err(|_| DispatchError::Other("Conversion Error."))?; + + let path = vec![asset_id, native_asset_id]; + let amount_in_max: AssetBalance = asset_balance.saturated_into(); + + // query for amount in + let amounts = + T::DexOperator::get_amount_in_by_path(amount_out, &path).map_or(vec![0], |v| v); + + if T::DexOperator::inner_swap_assets_for_exact_assets( + &who, + amount_out, + amount_in_max, + &path, + &who, + ) + .is_ok() + { + Self::deposit_event(Event::FlexibleFeeExchanged(currency_id, amounts[0])); + // successfully swap, break iteration + break; + } + } + } + Ok(()) + } +} diff --git a/pallets/flexible-fee/src/mock.rs b/pallets/flexible-fee/src/mock.rs index 266ea17866..b5a3b6075c 100644 --- a/pallets/flexible-fee/src/mock.rs +++ b/pallets/flexible-fee/src/mock.rs @@ -22,7 +22,6 @@ use std::convert::TryInto; // pub use polkadot_parachain::primitives::Id; pub use cumulus_primitives_core::ParaId; -// use node_primitives::Balance; use frame_support::{ parameter_types, weights::{IdentityFee, WeightToFeeCoefficients, WeightToFeePolynomial}, @@ -35,13 +34,15 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup, UniqueSaturatedInto}, - Perbill, + AccountId32, Perbill, }; use sp_std::cell::RefCell; use zenlink_protocol::{LocalAssetHandler, ZenlinkMultiAssets}; use super::*; use crate as flexible_fee; +// use node_primitives::Balance; +use crate::fee_dealer::FixedCurrencyFeeRate; pub type BlockNumber = u64; pub type Amount = i128; @@ -73,7 +74,7 @@ parameter_types! { impl system::Config for Test { type AccountData = balances::AccountData; - type AccountId = u128; + type AccountId = AccountId32; type BaseCallFilter = (); type BlockHashCount = BlockHashCount; type BlockLength = (); @@ -166,15 +167,21 @@ impl orml_tokens::Config for Test { parameter_types! { pub const NativeCurrencyId: CurrencyId = CurrencyId::Native(TokenSymbol::ASG); + pub const AlternativeFeeCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); + pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); } impl crate::Config for Test { type Balance = u64; type Currency = Balances; type DexOperator = ZenlinkProtocol; + type FeeDealer = FixedCurrencyFeeRate; + // type FeeDealer = FlexibleFee; type Event = Event; type MultiCurrency = Currencies; type NativeCurrencyId = NativeCurrencyId; + type AlternativeFeeCurrencyId = AlternativeFeeCurrencyId; + type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = (); type WeightInfo = (); } @@ -197,7 +204,7 @@ impl orml_currencies::Config for Test { parameter_types! { pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); pub const GetExchangeFee: (u32, u32) = (3, 1000); // 0.3% - // pub const SelfParaId: ParaId = ParaId{0: 2001}; + pub const SelfParaId: u32 = 2001; } impl zenlink_protocol::Config for Test { @@ -206,8 +213,7 @@ impl zenlink_protocol::Config for Test { type GetExchangeFee = GetExchangeFee; type MultiAssetsHandler = MultiAssets; type PalletId = ZenlinkPalletId; - // type SelfParaId = SelfParaId; - type SelfParaId = (); + type SelfParaId = SelfParaId; type TargetChains = (); type XcmExecutor = (); } diff --git a/pallets/flexible-fee/src/tests.rs b/pallets/flexible-fee/src/tests.rs index 678dc7a66e..4377a48e1e 100644 --- a/pallets/flexible-fee/src/tests.rs +++ b/pallets/flexible-fee/src/tests.rs @@ -20,27 +20,27 @@ #![cfg(test)] -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; // use balances::Call as BalancesCall; use frame_support::{ - assert_ok, + assert_noop, assert_ok, traits::WithdrawReasons, weights::{GetDispatchInfo, Pays, PostDispatchInfo}, }; use node_primitives::{CurrencyId, TokenSymbol}; use orml_traits::MultiCurrency; use pallet_transaction_payment::OnChargeTransaction; -use sp_runtime::testing::TestXt; +use sp_runtime::{testing::TestXt, AccountId32}; use zenlink_protocol::AssetId; -use crate::{mock::*, BlockNumberFor}; +use crate::{mock::*, BlockNumberFor, FeeDealer}; // some common variables -pub const ALICE: u128 = 1; -pub const BOB: u128 = 2; -pub const CHARLIE: u128 = 3; -pub const DICK: u128 = 4; +pub const CHARLIE: AccountId32 = AccountId32::new([0u8; 32]); +pub const BOB: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE: AccountId32 = AccountId32::new([2u8; 32]); +pub const DICK: AccountId32 = AccountId32::new([3u8; 32]); pub const CURRENCY_ID_0: CurrencyId = CurrencyId::Native(TokenSymbol::ASG); pub const CURRENCY_ID_1: CurrencyId = CurrencyId::Stable(TokenSymbol::AUSD); pub const CURRENCY_ID_2: CurrencyId = CurrencyId::Token(TokenSymbol::DOT); @@ -82,9 +82,6 @@ fn basic_setup() { let asset_1_currency_id: AssetId = AssetId::try_from(CURRENCY_ID_1).unwrap(); let asset_2_currency_id: AssetId = AssetId::try_from(CURRENCY_ID_2).unwrap(); - println!("asset_0_currency_id: {:?}", asset_0_currency_id); - println!("asset_1_currency_id: {:?}", asset_1_currency_id); - assert_ok!(ZenlinkProtocol::create_pair( Origin::signed(ALICE), asset_0_currency_id, @@ -162,8 +159,8 @@ fn inner_get_user_fee_charge_order_list_should_work() { default_order_list.push(CurrencyId::Stable(TokenSymbol::AUSD)); default_order_list.push(CurrencyId::Token(TokenSymbol::DOT)); default_order_list.push(CurrencyId::VToken(TokenSymbol::DOT)); - default_order_list.push(CurrencyId::Token(TokenSymbol::ETH)); - default_order_list.push(CurrencyId::VToken(TokenSymbol::ETH)); + default_order_list.push(CurrencyId::Token(TokenSymbol::KSM)); + default_order_list.push(CurrencyId::VToken(TokenSymbol::KSM)); assert_eq!(FlexibleFee::inner_get_user_fee_charge_order_list(&ALICE), default_order_list); @@ -195,10 +192,10 @@ fn ensure_can_charge_fee_should_work() { default_order_list.push(CurrencyId::Stable(TokenSymbol::AUSD)); default_order_list.push(CurrencyId::Token(TokenSymbol::DOT)); default_order_list.push(CurrencyId::Token(TokenSymbol::KSM)); - default_order_list.push(CurrencyId::Token(TokenSymbol::ETH)); + default_order_list.push(CurrencyId::Token(TokenSymbol::KSM)); default_order_list.push(CurrencyId::VToken(TokenSymbol::DOT)); default_order_list.push(CurrencyId::VToken(TokenSymbol::KSM)); - default_order_list.push(CurrencyId::VToken(TokenSymbol::ETH)); + default_order_list.push(CurrencyId::VToken(TokenSymbol::KSM)); // Set bob order as [4,3,2,1]. Alice and Charlie will use the default order of [0..11]] let _ = FlexibleFee::set_user_fee_charge_order( @@ -227,7 +224,7 @@ fn ensure_can_charge_fee_should_work() { Currencies::total_balance(CURRENCY_ID_1, &pool_0_1_account) ); - assert_ok!(FlexibleFee::ensure_can_charge_fee( + assert_ok!(::FeeDealer::ensure_can_charge_fee( &ALICE, 100, WithdrawReasons::TRANSACTION_PAYMENT, @@ -241,7 +238,7 @@ fn ensure_can_charge_fee_should_work() { assert_eq!(::Currency::free_balance(&ALICE), 150); // Bob - assert_ok!(FlexibleFee::ensure_can_charge_fee( + assert_ok!(::FeeDealer::ensure_can_charge_fee( &BOB, 100, WithdrawReasons::TRANSACTION_PAYMENT, @@ -265,7 +262,7 @@ fn withdraw_fee_should_work() { // prepare info variable let extra = (); - let xt = TestXt::new(call.clone(), Some((CHARLIE.try_into().unwrap(), extra))); + let xt = TestXt::new(call.clone(), Some((0u64, extra))); let info = xt.get_dispatch_info(); // 99 inclusion fee and a tip of 8 @@ -287,7 +284,7 @@ fn correct_and_deposit_fee_should_work() { Call::FlexibleFee(crate::Call::set_user_fee_charge_order(Some(asset_order_list_vec))); // prepare info variable let extra = (); - let xt = TestXt::new(call.clone(), Some((CHARLIE.try_into().unwrap(), extra))); + let xt = TestXt::new(call.clone(), Some((0u64, extra))); let info = xt.get_dispatch_info(); // prepare post info @@ -312,3 +309,127 @@ fn correct_and_deposit_fee_should_work() { assert_eq!(::Currency::free_balance(&CHARLIE), 120); }); } + +#[test] +// #[ignore = "This should be used with mock config type FeeDealer = FixedCurrencyFeeRate."] +fn ensure_can_charge_fee_v2_should_work() { + new_test_ext().execute_with(|| { + // Deposit 500 DOT and none of native token to Alice's account + assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::DOT), &ALICE, 500)); + + assert_noop!( + ::FeeDealer::ensure_can_charge_fee( + &ALICE, + 100, + WithdrawReasons::TRANSACTION_PAYMENT, + ), + crate::Error::::NotEnoughBalance + ); + + assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::KSM), &ALICE, 1)); + + assert_ok!(::FeeDealer::ensure_can_charge_fee( + &ALICE, + 100, + WithdrawReasons::TRANSACTION_PAYMENT, + )); + + assert_eq!( + ::MultiCurrency::free_balance( + CurrencyId::Token(TokenSymbol::KSM), + &ALICE + ), + 0 + ); + assert_eq!( + ::MultiCurrency::free_balance( + CurrencyId::Native(TokenSymbol::ASG), + &ALICE + ), + 100 + ); + }); +} + +#[test] +// #[ignore = "This should be used with mock config type FeeDealer = FixedCurrencyFeeRate."] +fn withdraw_fee_should_work_v2() { + new_test_ext().execute_with(|| { + assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::KSM), &CHARLIE, 2)); + + // prepare call variable + let asset_order_list_vec: Vec = + vec![CURRENCY_ID_0, CURRENCY_ID_1, CURRENCY_ID_2, CURRENCY_ID_3, CURRENCY_ID_4]; + let call = + Call::FlexibleFee(crate::Call::set_user_fee_charge_order(Some(asset_order_list_vec))); + + // prepare info variable + let extra = (); + let xt = TestXt::new(call.clone(), Some((0u64, extra))); + let info = xt.get_dispatch_info(); + + // println!("info: {:?}", info); + + assert_eq!(::Currency::free_balance(&CHARLIE), 0); + + // 99 inclusion fee and a tip of 8 + assert_ok!(FlexibleFee::withdraw_fee(&CHARLIE, &call, &info, 107, 8)); + + assert_eq!(::Currency::free_balance(&CHARLIE), 0); + }); +} + +#[test] +// #[ignore = "This should be used with mock config type FeeDealer = FixedCurrencyFeeRate."] +fn correct_and_deposit_fee_should_work_v2() { + new_test_ext().execute_with(|| { + assert_ok!(Currencies::deposit(CurrencyId::Token(TokenSymbol::KSM), &CHARLIE, 2)); + assert_ok!(Currencies::deposit(CurrencyId::Native(TokenSymbol::ASG), &CHARLIE, 30)); + // prepare call variable + let asset_order_list_vec: Vec = + vec![CURRENCY_ID_0, CURRENCY_ID_1, CURRENCY_ID_2, CURRENCY_ID_3, CURRENCY_ID_4]; + let call = + Call::FlexibleFee(crate::Call::set_user_fee_charge_order(Some(asset_order_list_vec))); + // prepare info variable + let extra = (); + let xt = TestXt::new(call.clone(), Some((0u64, extra))); + let info = xt.get_dispatch_info(); + + // prepare post info + let post_info = PostDispatchInfo { actual_weight: Some(20), pays_fee: Pays::Yes }; + + let corrected_fee = 80; + let tip = 8; + + let already_withdrawn = FlexibleFee::withdraw_fee(&CHARLIE, &call, &info, 107, 8).unwrap(); + + assert_eq!(::Currency::free_balance(&CHARLIE), 30); + + // Since the fee withdrawl mode if allowdeath, if the account is destroyed + // due to balance less than the existential deposit, no refund will be returned. + assert_ok!(FlexibleFee::correct_and_deposit_fee( + &CHARLIE, + &info, + &post_info, + corrected_fee, + tip, + already_withdrawn + )); + + assert_eq!( + ::MultiCurrency::free_balance( + CurrencyId::Native(TokenSymbol::ASG), + &CHARLIE + ), + 57 + ); + + assert_eq!( + ::MultiCurrency::free_balance( + CurrencyId::Token(TokenSymbol::KSM), + &CHARLIE + ), + 1 + ); + }); +} diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index feee4c8732..3a0a48f0d8 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -69,6 +69,7 @@ use sp_version::RuntimeVersion; /// Constant values used within the runtime. pub mod constants; +use bifrost_flexible_fee::fee_dealer::FixedCurrencyFeeRate; use bifrost_runtime_common::xcm_impl::{ BifrostAssetMatcher, BifrostCurrencyIdConvert, BifrostFilteredAssets, BifrostXcmTransactFilter, }; @@ -903,13 +904,22 @@ impl orml_tokens::Config for Runtime { type WeightInfo = (); } +parameter_types! { + pub const AlternativeFeeCurrencyId: CurrencyId = CurrencyId::Token(TokenSymbol::KSM); + pub const AltFeeCurrencyExchangeRate: (u32, u32) = (1, 100); +} + impl bifrost_flexible_fee::Config for Runtime { type Balance = Balance; type Currency = Balances; type DexOperator = ZenlinkProtocol; + // type FeeDealer = FlexibleFee; + type FeeDealer = FixedCurrencyFeeRate; type Event = Event; type MultiCurrency = Currencies; type NativeCurrencyId = NativeCurrencyId; + type AlternativeFeeCurrencyId = AlternativeFeeCurrencyId; + type AltFeeCurrencyExchangeRate = AltFeeCurrencyExchangeRate; type OnUnbalanced = (); type WeightInfo = (); }