diff --git a/zenlink-protocol/src/fee/tests.rs b/zenlink-protocol/src/fee/tests.rs index 8d6fa33..edccbc7 100644 --- a/zenlink-protocol/src/fee/tests.rs +++ b/zenlink-protocol/src/fee/tests.rs @@ -2,7 +2,7 @@ // Licensed under Apache 2.0. use super::mock::*; -use crate::{AssetId, Error, MultiAssetsHandler}; +use crate::{AssetId, MultiAssetsHandler}; use frame_support::{assert_noop, assert_ok, error::BadOrigin}; use frame_system::RawOrigin; use sp_core::U256; @@ -44,11 +44,6 @@ fn fee_meta_setter_should_not_work() { ); assert_noop!(DexPallet::set_fee_point(RawOrigin::Signed(BOB).into(), 0), BadOrigin); - - assert_noop!( - DexPallet::set_fee_point(RawOrigin::Root.into(), 31u8), - Error::::InvalidFeePoint - ); }) } @@ -507,3 +502,68 @@ fn turn_on_protocol_fee_swap_have_fee_at_should_work() { assert_eq!(lp_total, lp_of_alice_0 + alice_lp_add + lp_of_bob); }); } + +#[test] +fn assert_higher_fee_point_decreases_protocol_fee() { + new_test_ext().execute_with(|| { + assert_ok!(DexPallet::set_fee_receiver(RawOrigin::Root.into(), Some(BOB))); + + assert_ok!(DexPallet::foreign_mint(DOT_ASSET_ID, &ALICE, 100_000_000 * DOT_UNIT)); + assert_ok!(DexPallet::foreign_mint(BTC_ASSET_ID, &ALICE, 100_000_000 * BTC_UNIT)); + assert_ok!(DexPallet::foreign_mint(DOT_ASSET_ID, &CHARLIE, 100_000_000 * DOT_UNIT)); + + assert_ok!(DexPallet::create_pair(RawOrigin::Root.into(), DOT_ASSET_ID, BTC_ASSET_ID,)); + + let total_supply_dot: u128 = 1_000_000 * DOT_UNIT; + let total_supply_btc: u128 = 1_000_000 * BTC_UNIT; + + assert_ok!(DexPallet::add_liquidity( + RawOrigin::Signed(ALICE).into(), + DOT_ASSET_ID, + BTC_ASSET_ID, + total_supply_dot, + total_supply_btc, + 0, + 0, + 100 + )); + + assert_ok!(DexPallet::inner_swap_exact_assets_for_assets( + &CHARLIE, + DOT_UNIT, + 1, + &vec![DOT_ASSET_ID, BTC_ASSET_ID], + &CHARLIE, + )); + + fn mint_fee_for_point(fee_point: u8) -> u128 { + let reserve_0 = + ::MultiAssetsHandler::balance_of(DOT_ASSET_ID, &PAIR_DOT_BTC); + let reserve_1 = + ::MultiAssetsHandler::balance_of(BTC_ASSET_ID, &PAIR_DOT_BTC); + let total_supply = ::MultiAssetsHandler::total_supply(LP_DOT_BTC); + assert_ok!(DexPallet::set_fee_point(RawOrigin::Root.into(), fee_point)); + DexPallet::mint_protocol_fee( + reserve_0, + reserve_1, + DOT_ASSET_ID, + BTC_ASSET_ID, + total_supply, + ) + .unwrap() + } + + // 1/(1/1)-1=0 + let total_fee = mint_fee_for_point(0); + // 1/(1/2)-1=1 + assert_eq!(mint_fee_for_point(1), total_fee / 2); + // 1/(1/4)-1=3 + assert_eq!(mint_fee_for_point(3), total_fee / 4); + // 1/(1/6)-1=5 + assert_eq!(mint_fee_for_point(5), total_fee / 6); + // 1/(1/10)-1=9 + assert_eq!(mint_fee_for_point(9), total_fee / 10); + // 1/(1/100)-1=99 + assert_eq!(mint_fee_for_point(99), total_fee / 100); + }); +} diff --git a/zenlink-protocol/src/lib.rs b/zenlink-protocol/src/lib.rs index 8a1937e..208d4a7 100644 --- a/zenlink-protocol/src/lib.rs +++ b/zenlink-protocol/src/lib.rs @@ -232,14 +232,10 @@ pub mod pallet { #[pallet::genesis_config] /// Refer: https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol#L88 pub struct GenesisConfig { - /// The admin of the protocol fee. - // pub fee_admin: T::AccountId, /// The receiver of the protocol fee. pub fee_receiver: Option, - /// The fee point which integer between [0,30] - /// 0 means no protocol fee. - /// 30 means 0.3% * 100% = 0.0030. - /// default is 5 and means 0.3% * 1 / 6 = 0.0005. + /// The higher the fee point, the smaller the + /// cut of the exchange fee taken from LPs. pub fee_point: u8, } @@ -391,8 +387,6 @@ pub mod pallet { RequireProtocolAdmin, /// Require the admin candidate who can become new admin after confirm. RequireProtocolAdminCandidate, - /// Invalid fee_point - InvalidFeePoint, /// Unsupported AssetId by this ZenlinkProtocol Version. UnsupportedAssetType, /// Account balance must be greater than or equal to the transfer amount. @@ -469,7 +463,7 @@ pub mod pallet { /// - `send_to`: /// (1) Some(receiver): it turn on the protocol fee and the new receiver account. /// (2) None: it turn off the protocol fee. - #[pallet::call_index(0)] + #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::set_fee_receiver())] pub fn set_fee_receiver( origin: OriginFor, @@ -495,15 +489,15 @@ pub mod pallet { /// # Arguments /// /// - `fee_point`: - /// The fee_point which integer between [0,30] - /// 0 means no protocol fee. - /// 30 means 0.3% * 100% = 0.0030. - /// default is 5 and means 0.3% * 1 / 6 = 0.0005. + /// An integer y which satisfies the equation `1/x-1=y` + /// where x is the percentage of the exchange fee, a + /// value of 0 gives everything to the fee receiver + /// e.g. 1/(1/6)-1=5, 1/(1/2)-1=1 + /// See section 2.4 of the Uniswap v2 whitepaper #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::set_fee_point())] pub fn set_fee_point(origin: OriginFor, fee_point: u8) -> DispatchResult { ensure_root(origin)?; - ensure!(fee_point <= 30, Error::::InvalidFeePoint); FeeMeta::::mutate(|fee_meta| fee_meta.1 = fee_point); diff --git a/zenlink-protocol/src/swap/mod.rs b/zenlink-protocol/src/swap/mod.rs index f07406a..06a40cb 100644 --- a/zenlink-protocol/src/swap/mod.rs +++ b/zenlink-protocol/src/swap/mod.rs @@ -94,7 +94,7 @@ impl Pallet { parameter.total_supply, )?; if let Some(fee_to) = Self::fee_meta().0 { - if mint_fee > 0 && Self::fee_meta().1 > 0 { + if mint_fee > 0 { T::MultiAssetsHandler::deposit(lp_asset_id, &fee_to, mint_fee) .map(|_| mint_fee)?; parameter.total_supply = parameter @@ -125,18 +125,16 @@ impl Pallet { T::MultiAssetsHandler::transfer(asset_1, who, ¶meter.pair_account, amount_1)?; if let Some(_fee_to) = Self::fee_meta().0 { - if Self::fee_meta().1 > 0 { - // update reserve_0 and reserve_1 - let reserve_0 = - T::MultiAssetsHandler::balance_of(asset_0, ¶meter.pair_account); - let reserve_1 = - T::MultiAssetsHandler::balance_of(asset_1, ¶meter.pair_account); - - let last_k_value = U256::from(reserve_0) - .checked_mul(U256::from(reserve_1)) - .ok_or(Error::::Overflow)?; - Self::mutate_k_last(asset_0, asset_1, last_k_value); - } + // update reserve_0 and reserve_1 + let reserve_0 = + T::MultiAssetsHandler::balance_of(asset_0, ¶meter.pair_account); + let reserve_1 = + T::MultiAssetsHandler::balance_of(asset_1, ¶meter.pair_account); + + let last_k_value = U256::from(reserve_0) + .checked_mul(U256::from(reserve_1)) + .ok_or(Error::::Overflow)?; + Self::mutate_k_last(asset_0, asset_1, last_k_value); } Self::deposit_event(Event::LiquidityAdded( @@ -198,7 +196,7 @@ impl Pallet { parameter.total_supply, )?; if let Some(fee_to) = Self::fee_meta().0 { - if mint_fee > 0 && Self::fee_meta().1 > 0 { + if mint_fee > 0 { //Self::mutate_liquidity(asset_0, asset_1, &fee_to, mint_fee, true)?; T::MultiAssetsHandler::deposit(lp_asset_id, &fee_to, mint_fee) .map(|_| mint_fee)?; @@ -232,18 +230,16 @@ impl Pallet { )?; if let Some(_fee_to) = Self::fee_meta().0 { - if Self::fee_meta().1 > 0 { - // update reserve_0 and reserve_1 - let reserve_0 = - T::MultiAssetsHandler::balance_of(asset_0, ¶meter.pair_account); - let reserve_1 = - T::MultiAssetsHandler::balance_of(asset_1, ¶meter.pair_account); - - let last_k_value = U256::from(reserve_0) - .checked_mul(U256::from(reserve_1)) - .ok_or(Error::::Overflow)?; - Self::mutate_k_last(asset_0, asset_1, last_k_value); - } + // update reserve_0 and reserve_1 + let reserve_0 = + T::MultiAssetsHandler::balance_of(asset_0, ¶meter.pair_account); + let reserve_1 = + T::MultiAssetsHandler::balance_of(asset_1, ¶meter.pair_account); + + let last_k_value = U256::from(reserve_0) + .checked_mul(U256::from(reserve_1)) + .ok_or(Error::::Overflow)?; + Self::mutate_k_last(asset_0, asset_1, last_k_value); } Self::deposit_event(Event::LiquidityRemoved( @@ -362,7 +358,7 @@ impl Pallet { let mut mint_fee: AssetBalance = 0; if let Some(_fee_to) = Self::fee_meta().0 { - if !new_k_last.is_zero() && Self::fee_meta().1 > 0 { + if !new_k_last.is_zero() { let root_k = U256::from(reserve_0) .checked_mul(U256::from(reserve_1)) .map(|n| n.integer_sqrt()) @@ -371,13 +367,12 @@ impl Pallet { let root_k_last = new_k_last.integer_sqrt(); if root_k > root_k_last { let fee_point = Self::fee_meta().1; - let fix_fee_point = (30 - fee_point) / fee_point; let numerator = U256::from(total_liquidity) .checked_mul(root_k.checked_sub(root_k_last).ok_or(Error::::Overflow)?) .ok_or(Error::::Overflow)?; let denominator = root_k - .checked_mul(U256::from(fix_fee_point)) + .checked_mul(U256::from(fee_point)) .and_then(|n| n.checked_add(root_k_last)) .ok_or(Error::::Overflow)?;