From 457638f77602ec0aeebb031516ed1e53fd24432d Mon Sep 17 00:00:00 2001 From: akildemir Date: Mon, 2 Sep 2024 16:40:32 +0300 Subject: [PATCH 1/4] set correct swap fee, burn half --- substrate/client/tests/dex.rs | 16 ++++++++-------- substrate/dex/pallet/src/lib.rs | 32 ++++++++++++++++++++++++++++---- substrate/runtime/src/lib.rs | 3 ++- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/substrate/client/tests/dex.rs b/substrate/client/tests/dex.rs index d02d52602..21256e290 100644 --- a/substrate/client/tests/dex.rs +++ b/substrate/client/tests/dex.rs @@ -111,7 +111,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 16633299966633 + amount_out: 16599866399465 }] ); @@ -131,7 +131,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 17254428681101 + amount_out: 17260886638951 }] ); }) @@ -191,7 +191,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 12453103964435, + amount_out: 12406166091918, }] ); }) @@ -246,8 +246,8 @@ serai_test!( mint_to: pair.public().into(), pool_id: Coin::Bitcoin, 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: 110557340473, + lp_token_minted: 1054092553386 }] ); }) @@ -333,7 +333,7 @@ serai_test!( send_to: IN_INSTRUCTION_EXECUTOR, path, amount_in: 200_000_000_000_000, - amount_out: 19_044_944_233 + amount_out: 18933113030 }] ); } @@ -372,7 +372,7 @@ serai_test!( send_to: out_address.as_native().unwrap(), path, amount_in: 200_000_000_000, - amount_out: 1487294253782353 + amount_out: 1487256912435088 }] ); } @@ -410,7 +410,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: 1760904169 }] ); } diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 60a38926b..1974c8f7d 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -81,7 +81,7 @@ mod mock; use frame_support::ensure; use frame_system::{ pallet_prelude::{BlockNumberFor, OriginFor}, - ensure_signed, + ensure_signed, RawOrigin, }; pub use pallet::*; @@ -108,7 +108,7 @@ pub mod pallet { use sp_core::sr25519::Public; use sp_runtime::traits::IntegerSquareRoot; - use coins_pallet::{Pallet as CoinsPallet, Config as CoinsConfig}; + use coins_pallet::{Pallet as Coins, Config as CoinsConfig}; use serai_primitives::{Coin, Amount, Balance, SubstrateAmount, reverse_lexicographic_order}; @@ -812,7 +812,7 @@ pub mod pallet { to: &T::AccountId, balance: Balance, ) -> Result { - CoinsPallet::::transfer_internal(*from, *to, balance)?; + Coins::::transfer_internal(*from, *to, balance)?; Ok(balance.amount) } @@ -911,7 +911,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::::balance(*owner, coin).0 + Coins::::balance(*owner, coin).0 } /// Returns a pool id constructed from 2 coins. @@ -979,12 +979,36 @@ pub mod pallet { 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)?; amounts.push(amount_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)?, *coin2)?; } } Ok(amounts) } + fn burn_half_of_swap_fee(pool: PoolId, coin: Coin) -> Result<(), DispatchError> { + let pool_account = Self::get_pool_account(pool); + let origin = RawOrigin::Signed(pool_account.into()); + + let balance = Coins::::balance(pool_account, coin).0; + let burn_percent = + HigherPrecisionBalance::from(T::LPFee::get()).checked_div(2).ok_or(Error::::Overflow)?; + + let burn_amount = HigherPrecisionBalance::from(balance) + .checked_mul(burn_percent) + .ok_or(Error::::Overflow)? + .checked_div(1000) + .ok_or(Error::::Overflow)?; + + Coins::::burn( + origin.into(), + Balance { coin, amount: Amount(burn_amount.try_into().map_err(|_| Error::::Overflow)?) }, + )?; + Ok(()) + } + /// Used by the RPC service to provide current prices. pub fn quote_price_exact_tokens_for_tokens( coin1: Coin, diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index e55270cb9..21898ebab 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -214,7 +214,8 @@ impl coins::Config for Runtime { impl dex::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type LPFee = ConstU32<3>; // 0.3% + // 0.6% in total but only half will go to LPs(0.3%) other half to be burned. + type LPFee = ConstU32<6>; type MintMinLiquidity = ConstU64<10000>; type MaxSwapPathLength = ConstU32<3>; // coin1 -> SRI -> coin2 From eea7cc06aafbc641d0028a36739757d7d289c740 Mon Sep 17 00:00:00 2001 From: akildemir Date: Tue, 3 Sep 2024 13:42:30 +0300 Subject: [PATCH 2/4] bug fixes --- substrate/client/tests/dex.rs | 10 ++++---- substrate/dex/pallet/src/lib.rs | 4 ++-- substrate/dex/pallet/src/tests.rs | 39 ++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/substrate/client/tests/dex.rs b/substrate/client/tests/dex.rs index 21256e290..78e42783d 100644 --- a/substrate/client/tests/dex.rs +++ b/substrate/client/tests/dex.rs @@ -131,7 +131,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 17260886638951 + amount_out: 17166744497317 }] ); }) @@ -246,8 +246,8 @@ serai_test!( mint_to: pair.public().into(), pool_id: Coin::Bitcoin, coin_amount: 10_000_000_000_000, // half of sent amount - sri_amount: 110557340473, - lp_token_minted: 1054092553386 + sri_amount: 111669009482, + lp_token_minted: 1055147701082 }] ); }) @@ -372,7 +372,7 @@ serai_test!( send_to: out_address.as_native().unwrap(), path, amount_in: 200_000_000_000, - amount_out: 1487256912435088 + amount_out: 1473437558561637 }] ); } @@ -410,7 +410,7 @@ serai_test!( send_to: out_address.as_native().unwrap(), path, amount_in: 100_000_000_000_000, - amount_out: 1760904169 + amount_out: 1751430396 }] ); } diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 1974c8f7d..f69a0c674 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -981,7 +981,7 @@ pub mod pallet { amounts.push(amount_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)?, *coin2)?; + Self::burn_half_of_swap_fee(Self::get_pool_id(*coin1, *coin2)?, *coin1)?; } } @@ -990,7 +990,7 @@ pub mod pallet { fn burn_half_of_swap_fee(pool: PoolId, coin: Coin) -> Result<(), DispatchError> { let pool_account = Self::get_pool_account(pool); - let origin = RawOrigin::Signed(pool_account.into()); + let origin = RawOrigin::Signed(pool_account); let balance = Coins::::balance(pool_account, coin).0; let burn_percent = diff --git a/substrate/dex/pallet/src/tests.rs b/substrate/dex/pallet/src/tests.rs index ee714471f..e7c367c30 100644 --- a/substrate/dex/pallet/src/tests.rs +++ b/substrate/dex/pallet/src/tests.rs @@ -26,6 +26,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 = coins_pallet::Pallet; type LiquidityTokensError = coins_pallet::Error; @@ -60,6 +61,20 @@ fn pool_balance(owner: PublicKey, token_id: Coin) -> u64 { LiquidityTokens::::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(<::LPFee as Get>::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() @@ -1013,15 +1028,16 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { assert_ok!(CoinsPallet::::mint(user, Balance { coin: coin2, amount: Amount(base2) })); assert_ok!(CoinsPallet::::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, liquidity2, - liquidity1, + liquidity1_pool1, 1, 1, user, @@ -1030,15 +1046,15 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { RuntimeOrigin::signed(user), coin3, 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( @@ -1070,6 +1086,13 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { user, )); + // burn half of the taken fees + let burn_amount = get_burn_amount(liquidity2); + liquidity2 -= burn_amount; + + let burn_amount = get_burn_amount(liquidity1_pool2); + 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); @@ -1077,8 +1100,8 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { 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); }); From be46ac3ee155e90f09fd428284aa267634accf21 Mon Sep 17 00:00:00 2001 From: akildemir Date: Fri, 18 Oct 2024 09:50:49 +0300 Subject: [PATCH 3/4] bug fixes --- substrate/client/tests/dex.rs | 16 ++++++++-------- substrate/dex/pallet/src/lib.rs | 26 ++++++++++++-------------- substrate/dex/pallet/src/tests.rs | 4 ++-- substrate/runtime/src/lib.rs | 4 ++-- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/substrate/client/tests/dex.rs b/substrate/client/tests/dex.rs index f9e6f2898..2503cc971 100644 --- a/substrate/client/tests/dex.rs +++ b/substrate/client/tests/dex.rs @@ -116,7 +116,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 16599866399465 + amount_out: 16611018363939 }] ); @@ -144,7 +144,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 17166744497317 + amount_out: 17207430166736 }] ); }) @@ -210,7 +210,7 @@ serai_test!( send_to: pair.public().into(), path, amount_in: amount_in.0, - amount_out: 12406166091918, + amount_out: 12421816676180, }] ); }) @@ -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: 111669009482, - lp_token_minted: 1055147701082 + sri_amount: 111631562261, + lp_token_minted: 1055499886564 }] ); }) @@ -349,7 +349,7 @@ serai_test!( send_to: IN_INSTRUCTION_EXECUTOR, path, amount_in: 200_000_000_000_000, - amount_out: 18933113030 + amount_out: 18970355346 }] ); } @@ -388,7 +388,7 @@ serai_test!( send_to: out_address.as_native().unwrap(), path, amount_in: 200_000_000_000, - amount_out: 1473437558561637 + amount_out: 1482888317565764 }] ); } @@ -426,7 +426,7 @@ serai_test!( send_to: out_address.as_native().unwrap(), path, amount_in: 100_000_000_000_000, - amount_out: 1751430396 + amount_out: 1755477054 }] ); } diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 0b0d3d1fe..30031a16e 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -992,32 +992,30 @@ 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)?; - amounts.push(amount_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)?; + 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) -> Result<(), DispatchError> { + fn burn_half_of_swap_fee( + pool: PoolId, + coin: Coin, + amount: SubstrateAmount, + ) -> Result<(), DispatchError> { let pool_account = Self::get_pool_account(pool); - let origin = RawOrigin::Signed(pool_account); - - let balance = Coins::::balance(pool_account, coin).0; - let burn_percent = - HigherPrecisionBalance::from(T::LPFee::get()).checked_div(2).ok_or(Error::::Overflow)?; - let burn_amount = HigherPrecisionBalance::from(balance) - .checked_mul(burn_percent) - .ok_or(Error::::Overflow)? - .checked_div(1000) - .ok_or(Error::::Overflow)?; + // half of the taken fee + let burn_percent = T::LPFee::get().checked_div(2).ok_or(Error::::Overflow)?; + let burn_amount = Self::mul_div(amount, burn_percent.into(), 1000)?; Coins::::burn( - origin.into(), + RawOrigin::Signed(pool_account).into(), Balance { coin, amount: Amount(burn_amount.try_into().map_err(|_| Error::::Overflow)?) }, )?; Ok(()) diff --git a/substrate/dex/pallet/src/tests.rs b/substrate/dex/pallet/src/tests.rs index 4c3030b90..82fa0a1b0 100644 --- a/substrate/dex/pallet/src/tests.rs +++ b/substrate/dex/pallet/src/tests.rs @@ -1157,10 +1157,10 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() { )); // burn half of the taken fees - let burn_amount = get_burn_amount(liquidity2); + let burn_amount = get_burn_amount(input_amount); liquidity2 -= burn_amount; - let burn_amount = get_burn_amount(liquidity1_pool2); + let burn_amount = get_burn_amount(expect_out2); liquidity1_pool2 -= burn_amount; let pool_id1 = Dex::get_pool_id(coin1, coin2).unwrap(); diff --git a/substrate/runtime/src/lib.rs b/substrate/runtime/src/lib.rs index 9b76a08f1..048c7b9ec 100644 --- a/substrate/runtime/src/lib.rs +++ b/substrate/runtime/src/lib.rs @@ -215,8 +215,8 @@ impl coins::Config for Runtime { impl dex::Config for Runtime { type RuntimeEvent = RuntimeEvent; - // 0.6% in total but only half will go to LPs(0.3%) other half to be burned. - type LPFee = ConstU32<6>; + // 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 From 63345f2c6808fb579d09f1c1f05342528ddb2239 Mon Sep 17 00:00:00 2001 From: akildemir Date: Fri, 18 Oct 2024 12:17:35 +0300 Subject: [PATCH 4/4] fix clippy --- substrate/dex/pallet/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/dex/pallet/src/lib.rs b/substrate/dex/pallet/src/lib.rs index 30031a16e..613568da3 100644 --- a/substrate/dex/pallet/src/lib.rs +++ b/substrate/dex/pallet/src/lib.rs @@ -1016,7 +1016,7 @@ pub mod pallet { Coins::::burn( RawOrigin::Signed(pool_account).into(), - Balance { coin, amount: Amount(burn_amount.try_into().map_err(|_| Error::::Overflow)?) }, + Balance { coin, amount: Amount(burn_amount) }, )?; Ok(()) }