5 Commits

Author SHA1 Message Date
akildemir
08d258a16f Merge branch 'develop' of https://github.com/serai-dex/serai into set-correct-swap-fee 2024-10-18 09:51:14 +03:00
akildemir
be46ac3ee1 bug fixes 2024-10-18 09:50:49 +03:00
akildemir
a116bdfea2 Merge branch 'develop' of https://github.com/akildemir/serai into set-correct-swap-fee 2024-10-08 12:43:17 +03:00
akildemir
eea7cc06aa bug fixes 2024-09-03 13:42:30 +03:00
akildemir
457638f776 set correct swap fee, burn half 2024-09-02 16:40:32 +03:00
4 changed files with 67 additions and 21 deletions

View File

@@ -116,7 +116,7 @@ serai_test!(
send_to: pair.public().into(), send_to: pair.public().into(),
path, path,
amount_in: amount_in.0, amount_in: amount_in.0,
amount_out: 16633299966633 amount_out: 16611018363939
}] }]
); );
@@ -144,7 +144,7 @@ serai_test!(
send_to: pair.public().into(), send_to: pair.public().into(),
path, path,
amount_in: amount_in.0, amount_in: amount_in.0,
amount_out: 17254428681101 amount_out: 17207430166736
}] }]
); );
}) })
@@ -210,7 +210,7 @@ serai_test!(
send_to: pair.public().into(), send_to: pair.public().into(),
path, path,
amount_in: amount_in.0, amount_in: amount_in.0,
amount_out: 12453103964435, amount_out: 12421816676180,
}] }]
); );
}) })
@@ -264,8 +264,8 @@ serai_test!(
mint_to: pair.public().into(), mint_to: pair.public().into(),
pool_id: coin, pool_id: coin,
coin_amount: 10_000_000_000_000, // half of sent amount coin_amount: 10_000_000_000_000, // half of sent amount
sri_amount: 111_333_778_668, sri_amount: 111631562261,
lp_token_minted: 1_054_092_553_383 lp_token_minted: 1055499886564
}] }]
); );
}) })
@@ -349,7 +349,7 @@ serai_test!(
send_to: IN_INSTRUCTION_EXECUTOR, send_to: IN_INSTRUCTION_EXECUTOR,
path, path,
amount_in: 200_000_000_000_000, amount_in: 200_000_000_000_000,
amount_out: 19_044_944_233 amount_out: 18970355346
}] }]
); );
} }
@@ -388,7 +388,7 @@ serai_test!(
send_to: out_address.as_native().unwrap(), send_to: out_address.as_native().unwrap(),
path, path,
amount_in: 200_000_000_000, amount_in: 200_000_000_000,
amount_out: 1487294253782353 amount_out: 1482888317565764
}] }]
); );
} }
@@ -426,7 +426,7 @@ serai_test!(
send_to: out_address.as_native().unwrap(), send_to: out_address.as_native().unwrap(),
path, path,
amount_in: 100_000_000_000_000, amount_in: 100_000_000_000_000,
amount_out: 1_762_662_819 amount_out: 1755477054
}] }]
); );
} }

View File

@@ -81,7 +81,7 @@ mod mock;
use frame_support::{ensure, pallet_prelude::*, BoundedBTreeSet}; use frame_support::{ensure, pallet_prelude::*, BoundedBTreeSet};
use frame_system::{ use frame_system::{
pallet_prelude::{BlockNumberFor, OriginFor}, pallet_prelude::{BlockNumberFor, OriginFor},
ensure_signed, ensure_signed, RawOrigin,
}; };
pub use pallet::*; pub use pallet::*;
@@ -109,7 +109,7 @@ pub mod pallet {
use sp_core::sr25519::Public; 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. /// Pool ID.
/// ///
@@ -828,7 +828,7 @@ pub mod pallet {
to: &T::AccountId, to: &T::AccountId,
balance: Balance, balance: Balance,
) -> Result<Amount, DispatchError> { ) -> Result<Amount, DispatchError> {
CoinsPallet::<T>::transfer_internal(*from, *to, balance)?; Coins::<T>::transfer_internal(*from, *to, balance)?;
Ok(balance.amount) Ok(balance.amount)
} }
@@ -927,7 +927,7 @@ pub mod pallet {
/// Get the `owner`'s balance of `coin`, which could be the chain's native coin or another /// 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`. /// fungible. Returns a value in the form of an `Amount`.
fn get_balance(owner: &T::AccountId, coin: Coin) -> SubstrateAmount { 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. /// Returns a pool id constructed from 2 coins.
@@ -992,6 +992,10 @@ pub mod pallet {
let (reserve_in, reserve_out) = Self::get_reserves(coin1, coin2)?; let (reserve_in, reserve_out) = Self::get_reserves(coin1, coin2)?;
let prev_amount = amounts.last().expect("Always has at least one element"); 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)?; 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); amounts.push(amount_out);
} }
} }
@@ -999,6 +1003,24 @@ pub mod pallet {
Ok(amounts) 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.try_into().map_err(|_| Error::<T>::Overflow)?) },
)?;
Ok(())
}
/// Used by the RPC service to provide current prices. /// Used by the RPC service to provide current prices.
pub fn quote_price_exact_tokens_for_tokens( pub fn quote_price_exact_tokens_for_tokens(
coin1: Coin, coin1: Coin,

View File

@@ -29,6 +29,7 @@ pub use coins_pallet as coins;
use coins::Pallet as CoinsPallet; use coins::Pallet as CoinsPallet;
use serai_primitives::{Balance, COINS, PublicKey, system_address, Amount}; use serai_primitives::{Balance, COINS, PublicKey, system_address, Amount};
use sp_core::Get;
type LiquidityTokens<T> = coins_pallet::Pallet<T, coins::Instance1>; type LiquidityTokens<T> = coins_pallet::Pallet<T, coins::Instance1>;
type LiquidityTokensError<T> = coins_pallet::Error<T, coins::Instance1>; type LiquidityTokensError<T> = coins_pallet::Error<T, coins::Instance1>;
@@ -63,6 +64,20 @@ fn pool_balance(owner: PublicKey, token_id: Coin) -> u64 {
LiquidityTokens::<Test>::balance(owner, token_id).0 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 { macro_rules! bvec {
($( $x:tt )*) => { ($( $x:tt )*) => {
vec![$( $x )*].try_into().unwrap() vec![$( $x )*].try_into().unwrap()
@@ -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: coin2, amount: Amount(base2) }));
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin3, amount: Amount(base2) })); assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin3, amount: Amount(base2) }));
let liquidity1 = 10000; let liquidity1_pool1 = 10000;
let liquidity2 = 200; let mut liquidity1_pool2 = liquidity1_pool1;
let mut liquidity2 = 200;
let liquidity3 = 2000; let liquidity3 = 2000;
assert_ok!(Dex::add_liquidity( assert_ok!(Dex::add_liquidity(
RuntimeOrigin::signed(user), RuntimeOrigin::signed(user),
coin2.try_into().unwrap(), coin2.try_into().unwrap(),
liquidity2, liquidity2,
liquidity1, liquidity1_pool1,
1, 1,
1, 1,
user, user,
@@ -1100,15 +1116,15 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
RuntimeOrigin::signed(user), RuntimeOrigin::signed(user),
coin3.try_into().unwrap(), coin3.try_into().unwrap(),
liquidity3, liquidity3,
liquidity1, liquidity1_pool2,
1, 1,
1, 1,
user, user,
)); ));
let input_amount = 500; let input_amount = 500;
let expect_out2 = Dex::get_amount_out(input_amount, liquidity2, liquidity1).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, liquidity3).ok().unwrap(); let expect_out3 = Dex::get_amount_out(expect_out2, liquidity1_pool2, liquidity3).ok().unwrap();
assert_noop!( assert_noop!(
Dex::swap_exact_tokens_for_tokens( Dex::swap_exact_tokens_for_tokens(
@@ -1140,6 +1156,13 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
user, 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_id1 = Dex::get_pool_id(coin1, coin2).unwrap();
let pool_id2 = Dex::get_pool_id(coin1, coin3).unwrap(); let pool_id2 = Dex::get_pool_id(coin1, coin3).unwrap();
let pallet_account1 = Dex::get_pool_account(pool_id1); let pallet_account1 = Dex::get_pool_account(pool_id1);
@@ -1147,8 +1170,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
assert_eq!(balance(user, coin2), base2 - liquidity2 - input_amount); assert_eq!(balance(user, coin2), base2 - liquidity2 - input_amount);
assert_eq!(balance(pallet_account1, coin2), 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_account1, coin1), liquidity1_pool1 - expect_out2);
assert_eq!(balance(pallet_account2, coin1), liquidity1 + expect_out2); assert_eq!(balance(pallet_account2, coin1), liquidity1_pool2 + expect_out2);
assert_eq!(balance(pallet_account2, coin3), liquidity3 - expect_out3); assert_eq!(balance(pallet_account2, coin3), liquidity3 - expect_out3);
assert_eq!(balance(user, coin3), 10000 - liquidity3 + expect_out3); assert_eq!(balance(user, coin3), 10000 - liquidity3 + expect_out3);
}); });

View File

@@ -215,7 +215,8 @@ impl coins::Config<coins::Instance1> for Runtime {
impl dex::Config for Runtime { impl dex::Config for Runtime {
type RuntimeEvent = RuntimeEvent; 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 MintMinLiquidity = ConstU64<10000>;
type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2 type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2