Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set correct swap fee, burn half #602

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions substrate/client/tests/dex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ serai_test!(
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 16633299966633
amount_out: 16611018363939
}]
);

Expand Down Expand Up @@ -144,7 +144,7 @@ serai_test!(
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 17254428681101
amount_out: 17207430166736
}]
);
})
Expand Down Expand Up @@ -210,7 +210,7 @@ serai_test!(
send_to: pair.public().into(),
path,
amount_in: amount_in.0,
amount_out: 12453103964435,
amount_out: 12421816676180,
}]
);
})
Expand Down Expand Up @@ -264,8 +264,8 @@ serai_test!(
mint_to: pair.public().into(),
pool_id: coin,
coin_amount: 10_000_000_000_000, // half of sent amount
sri_amount: 111_333_778_668,
lp_token_minted: 1_054_092_553_383
sri_amount: 111631562261,
lp_token_minted: 1055499886564
}]
);
})
Expand Down Expand Up @@ -349,7 +349,7 @@ serai_test!(
send_to: IN_INSTRUCTION_EXECUTOR,
path,
amount_in: 200_000_000_000_000,
amount_out: 19_044_944_233
amount_out: 18970355346
}]
);
}
Expand Down Expand Up @@ -388,7 +388,7 @@ serai_test!(
send_to: out_address.as_native().unwrap(),
path,
amount_in: 200_000_000_000,
amount_out: 1487294253782353
amount_out: 1482888317565764
}]
);
}
Expand Down Expand Up @@ -426,7 +426,7 @@ serai_test!(
send_to: out_address.as_native().unwrap(),
path,
amount_in: 100_000_000_000_000,
amount_out: 1_762_662_819
amount_out: 1755477054
}]
);
}
Expand Down
30 changes: 26 additions & 4 deletions substrate/dex/pallet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mod mock;
use frame_support::{ensure, pallet_prelude::*, BoundedBTreeSet};
use frame_system::{
pallet_prelude::{BlockNumberFor, OriginFor},
ensure_signed,
ensure_signed, RawOrigin,
};

pub use pallet::*;
Expand Down Expand Up @@ -109,7 +109,7 @@ pub mod pallet {

use sp_core::sr25519::Public;

use coins_pallet::{Pallet as CoinsPallet, Config as CoinsConfig};
use coins_pallet::{Pallet as Coins, Config as CoinsConfig};

/// Pool ID.
///
Expand Down Expand Up @@ -828,7 +828,7 @@ pub mod pallet {
to: &T::AccountId,
balance: Balance,
) -> Result<Amount, DispatchError> {
CoinsPallet::<T>::transfer_internal(*from, *to, balance)?;
Coins::<T>::transfer_internal(*from, *to, balance)?;
Ok(balance.amount)
}

Expand Down Expand Up @@ -927,7 +927,7 @@ pub mod pallet {
/// Get the `owner`'s balance of `coin`, which could be the chain's native coin or another
/// fungible. Returns a value in the form of an `Amount`.
fn get_balance(owner: &T::AccountId, coin: Coin) -> SubstrateAmount {
CoinsPallet::<T>::balance(*owner, coin).0
Coins::<T>::balance(*owner, coin).0
}

/// Returns a pool id constructed from 2 coins.
Expand Down Expand Up @@ -992,13 +992,35 @@ pub mod pallet {
let (reserve_in, reserve_out) = Self::get_reserves(coin1, coin2)?;
let prev_amount = amounts.last().expect("Always has at least one element");
let amount_out = Self::get_amount_out(*prev_amount, reserve_in, reserve_out)?;

// now that we got swap fee from the user, burn half of it.
Self::burn_half_of_swap_fee(Self::get_pool_id(*coin1, *coin2)?, *coin1, *prev_amount)?;

amounts.push(amount_out);
}
}

Ok(amounts)
}

fn burn_half_of_swap_fee(
pool: PoolId,
coin: Coin,
amount: SubstrateAmount,
) -> Result<(), DispatchError> {
let pool_account = Self::get_pool_account(pool);

// half of the taken fee
let burn_percent = T::LPFee::get().checked_div(2).ok_or(Error::<T>::Overflow)?;
let burn_amount = Self::mul_div(amount, burn_percent.into(), 1000)?;

Coins::<T>::burn(
RawOrigin::Signed(pool_account).into(),
Balance { coin, amount: Amount(burn_amount) },
)?;
Ok(())
}

/// Used by the RPC service to provide current prices.
pub fn quote_price_exact_tokens_for_tokens(
coin1: Coin,
Expand Down
39 changes: 31 additions & 8 deletions substrate/dex/pallet/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub use coins_pallet as coins;
use coins::Pallet as CoinsPallet;

use serai_primitives::{Balance, COINS, PublicKey, system_address, Amount};
use sp_core::Get;

type LiquidityTokens<T> = coins_pallet::Pallet<T, coins::Instance1>;
type LiquidityTokensError<T> = coins_pallet::Error<T, coins::Instance1>;
Expand Down Expand Up @@ -63,6 +64,20 @@ fn pool_balance(owner: PublicKey, token_id: Coin) -> u64 {
LiquidityTokens::<Test>::balance(owner, token_id).0
}

fn get_burn_amount(liquidity: u64) -> u64 {
// burn half of the swap fee left in the pool
let burn_percent = HigherPrecisionBalance::from(<<Test as Config>::LPFee as Get<u32>>::get())
.checked_div(2)
.unwrap();

let burn_amount = HigherPrecisionBalance::from(liquidity)
.checked_mul(burn_percent)
.unwrap()
.checked_div(1000)
.unwrap();
u64::try_from(burn_amount).unwrap()
}

macro_rules! bvec {
($( $x:tt )*) => {
vec![$( $x )*].try_into().unwrap()
Expand Down Expand Up @@ -1083,15 +1098,16 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(base2) }));
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin3, amount: Amount(base2) }));

let liquidity1 = 10000;
let liquidity2 = 200;
let liquidity1_pool1 = 10000;
let mut liquidity1_pool2 = liquidity1_pool1;
let mut liquidity2 = 200;
let liquidity3 = 2000;

assert_ok!(Dex::add_liquidity(
RuntimeOrigin::signed(user),
coin2.try_into().unwrap(),
liquidity2,
liquidity1,
liquidity1_pool1,
1,
1,
user,
Expand All @@ -1100,15 +1116,15 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
RuntimeOrigin::signed(user),
coin3.try_into().unwrap(),
liquidity3,
liquidity1,
liquidity1_pool2,
1,
1,
user,
));

let input_amount = 500;
let expect_out2 = Dex::get_amount_out(input_amount, liquidity2, liquidity1).ok().unwrap();
let expect_out3 = Dex::get_amount_out(expect_out2, liquidity1, liquidity3).ok().unwrap();
let expect_out2 = Dex::get_amount_out(input_amount, liquidity2, liquidity1_pool1).ok().unwrap();
let expect_out3 = Dex::get_amount_out(expect_out2, liquidity1_pool2, liquidity3).ok().unwrap();

assert_noop!(
Dex::swap_exact_tokens_for_tokens(
Expand Down Expand Up @@ -1140,15 +1156,22 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
user,
));

// burn half of the taken fees
let burn_amount = get_burn_amount(input_amount);
liquidity2 -= burn_amount;

let burn_amount = get_burn_amount(expect_out2);
liquidity1_pool2 -= burn_amount;

let pool_id1 = Dex::get_pool_id(coin1, coin2).unwrap();
let pool_id2 = Dex::get_pool_id(coin1, coin3).unwrap();
let pallet_account1 = Dex::get_pool_account(pool_id1);
let pallet_account2 = Dex::get_pool_account(pool_id2);

assert_eq!(balance(user, coin2), base2 - liquidity2 - input_amount);
assert_eq!(balance(pallet_account1, coin2), liquidity2 + input_amount);
assert_eq!(balance(pallet_account1, coin1), liquidity1 - expect_out2);
assert_eq!(balance(pallet_account2, coin1), liquidity1 + expect_out2);
assert_eq!(balance(pallet_account1, coin1), liquidity1_pool1 - expect_out2);
assert_eq!(balance(pallet_account2, coin1), liquidity1_pool2 + expect_out2);
assert_eq!(balance(pallet_account2, coin3), liquidity3 - expect_out3);
assert_eq!(balance(user, coin3), 10000 - liquidity3 + expect_out3);
});
Expand Down
3 changes: 2 additions & 1 deletion substrate/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ impl coins::Config<coins::Instance1> for Runtime {
impl dex::Config for Runtime {
type RuntimeEvent = RuntimeEvent;

type LPFee = ConstU32<3>; // 0.3%
// 0.5% in total but only half will go to LPs(0.25%) other half to be burned.
type LPFee = ConstU32<5>;
type MintMinLiquidity = ConstU64<10000>;

type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2
Expand Down