Skip to content

Commit

Permalink
Merge branch 'develop' into feature/reducingTestingTime
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexChetverov authored Sep 21, 2023
2 parents 0d2e9ef + 6530140 commit ecaad59
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 46 deletions.
26 changes: 25 additions & 1 deletion pallets/xyk/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_core::U256;
use sp_rpc::number::NumberOrHex;
use sp_runtime::traits::{Block as BlockT, MaybeDisplay, MaybeFromStr};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, MaybeDisplay, MaybeFromStr},
};
use sp_std::convert::{TryFrom, TryInto};
use std::sync::Arc;
pub use xyk_runtime_api::XykApi as XykRuntimeApi;
Expand Down Expand Up @@ -104,6 +107,9 @@ pub trait XykApi<
at: Option<BlockHash>,
) -> RpcResult<ResponseTypePrice>;

#[method(name = "xyk_get_liq_tokens_for_trading")]
fn get_liq_tokens_for_trading(&self, at: Option<BlockHash>) -> RpcResult<Vec<TokenId>>;

#[method(name = "xyk_is_buy_asset_lock_free")]
fn is_buy_asset_lock_free(
&self,
Expand Down Expand Up @@ -379,6 +385,22 @@ where
})
}

fn get_liq_tokens_for_trading(
&self,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<TokenId>> {
let api = self.client.runtime_api();
let at = self.client.info().best_hash;

api.get_liq_tokens_for_trading(at).map_err(|e| {
JsonRpseeError::Call(CallError::Custom(ErrorObject::owned(
1,
"Unable to serve the request",
Some(format!("{:?}", e)),
)))
})
}

fn is_buy_asset_lock_free(
&self,
path: sp_std::vec::Vec<TokenId>,
Expand All @@ -397,6 +419,7 @@ where
)))
})
}

fn is_sell_asset_lock_free(
&self,
path: sp_std::vec::Vec<TokenId>,
Expand All @@ -415,6 +438,7 @@ where
)))
})
}

fn get_tradeable_tokens(
&self,
at: Option<<Block as BlockT>::Hash>,
Expand Down
5 changes: 2 additions & 3 deletions pallets/xyk/runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,16 @@ sp_api::decl_runtime_apis! {
total_amount: Balance,
reserve_amount: Balance,
) -> XYKRpcResult<Balance>;

fn get_liq_tokens_for_trading(
) -> Vec<TokenId>;
fn is_buy_asset_lock_free(
path: sp_std::vec::Vec<TokenId>,
input_amount: Balance,
) -> Option<bool>;

fn is_sell_asset_lock_free(
path: sp_std::vec::Vec<TokenId>,
input_amount: Balance,
) -> Option<bool>;

fn get_tradeable_tokens() -> Vec<RpcAssetMetadata<TokenId>>;
}
}
3 changes: 2 additions & 1 deletion pallets/xyk/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,8 @@ benchmarks! {

}: burn_liquidity(RawOrigin::Signed(caller.clone().into()), non_native_asset_id1.into(), non_native_asset_id2.into(), total_liquidity_after_minting)
verify {
assert!(Xyk::<T>::liquidity_pool(liquidity_asset_id).is_none());
assert_eq!(Xyk::<T>::liquidity_pool(liquidity_asset_id), Some((non_native_asset_id1.into(), non_native_asset_id2.into())));
assert_eq!(<T as Config>::Currency::total_issuance(liquidity_asset_id.into()).into(), 0);
}

provide_liquidity_with_conversion {
Expand Down
122 changes: 86 additions & 36 deletions pallets/xyk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
//! - account_id
//! - settle_treasury_buy_and_burn
//! - calculate_balanced_sell_amount
//! - get_liq_tokens_for_trading
//!
//! # fn create_pool
//! -Sets the initial ratio/price of both assets to each other depending on amounts of each assets when creating pool.
Expand Down Expand Up @@ -283,6 +284,8 @@
//! # calculate_balanced_sell_amount
//! - Supporting public function accessible through rpc call which calculates how much amount x we need to swap from total_amount, so that after `y = swap(x)`, the resulting balance equals `(total_amount - x) / y = pool_x / pool_y`
//! - the resulting amounts can then be used to `mint_liquidity` with minimal leftover after operation
//! # get_liq_tokens_for_trading
//! - Supporting public function accessible through rpc call which lists all of the liquidity pool token ids that are available for trading
#![cfg_attr(not(feature = "std"), no_std)]

Expand Down Expand Up @@ -502,6 +505,7 @@ pub mod pallet {
MultiSwapCantHaveSameTokenConsequetively,
/// Trading blocked by maintenance mode
TradingBlockedByMaintenanceMode,
PoolIsEmpty,
}

#[pallet::event]
Expand Down Expand Up @@ -1306,6 +1310,15 @@ impl<T: Config> Pallet<T> {
Ok(result)
}

pub fn get_liq_tokens_for_trading() -> Result<Vec<TokenId>, DispatchError> {
let result = LiquidityAssets::<T>::iter_values()
.filter_map(|v| v)
.filter(|v| !<T as Config>::Currency::total_issuance((*v).into()).is_zero())
.collect();

Ok(result)
}

// MAX: 2R
pub fn get_liquidity_asset(
first_asset_id: TokenId,
Expand Down Expand Up @@ -1565,6 +1578,34 @@ impl<T: Config> Pallet<T> {
fn native_token_id() -> TokenId {
<T as Config>::NativeCurrencyId::get()
}

fn calculate_initial_liquidity(
first_asset_amount: Balance,
second_asset_amount: Balance,
) -> Result<u128, DispatchError> {
let mut initial_liquidity = first_asset_amount
.checked_div(2)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?
.checked_add(
second_asset_amount
.checked_div(2)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?,
)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?;

return Ok(if initial_liquidity == 0 { 1 } else { initial_liquidity })
}

fn is_pool_empty(
first_asset_id: TokenId,
second_asset_id: TokenId,
) -> Result<bool, DispatchError> {
let liquidity_asset_id = Pallet::<T>::get_liquidity_asset(first_asset_id, second_asset_id)?;
let total_liquidity_assets: Balance =
<T as Config>::Currency::total_issuance(liquidity_asset_id.into()).into();

return Ok(total_liquidity_assets.is_zero())
}
}

impl<T: Config> PreValidateSwaps for Pallet<T> {
Expand Down Expand Up @@ -1598,6 +1639,8 @@ impl<T: Config> PreValidateSwaps for Pallet<T> {
Error::<T>::FunctionNotAvailableForThisToken
);

ensure!(!(Self::is_pool_empty(sold_asset_id, bought_asset_id)?), Error::<T>::PoolIsEmpty);

let buy_and_burn_amount = multiply_by_rational_with_rounding(
sold_asset_amount,
T::BuyAndBurnFeePercentage::get(),
Expand Down Expand Up @@ -1702,6 +1745,8 @@ impl<T: Config> PreValidateSwaps for Pallet<T> {
.collect();

for (x, y) in atomic_pairs.iter() {
ensure!(!(Self::is_pool_empty(*x, *y)?), Error::<T>::PoolIsEmpty);

if x == y {
return Err(Error::<T>::MultiSwapCantHaveSameTokenConsequetively.into())
}
Expand Down Expand Up @@ -1799,6 +1844,8 @@ impl<T: Config> PreValidateSwaps for Pallet<T> {
Error::<T>::FunctionNotAvailableForThisToken
);

ensure!(!(Self::is_pool_empty(sold_asset_id, bought_asset_id)?), Error::<T>::PoolIsEmpty);

// Get token reserves
let (input_reserve, output_reserve) =
Pallet::<T>::get_reserves(sold_asset_id, bought_asset_id)?;
Expand Down Expand Up @@ -1923,6 +1970,8 @@ impl<T: Config> PreValidateSwaps for Pallet<T> {
let mut atomic_pairs_hashset = BTreeSet::new();

for (x, y) in atomic_pairs.iter() {
ensure!(!(Self::is_pool_empty(*x, *y)?), Error::<T>::PoolIsEmpty);

if x == y {
return Err(Error::<T>::MultiSwapCantHaveSameTokenConsequetively.into())
} else if x > y {
Expand Down Expand Up @@ -2060,19 +2109,8 @@ impl<T: Config> XykFunctionsTrait<T::AccountId> for Pallet<T> {
ensure!(first_asset_id != second_asset_id, Error::<T>::SameAsset,);

// Liquidity token amount calculation
let mut initial_liquidity = first_asset_amount
.checked_div(2)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?
.checked_add(
second_asset_amount
.checked_div(2)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?,
)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?;

if initial_liquidity == 0 {
initial_liquidity = 1
}
let initial_liquidity =
Pallet::<T>::calculate_initial_liquidity(first_asset_amount, second_asset_amount)?;

Pools::<T>::insert(
(first_asset_id, second_asset_id),
Expand Down Expand Up @@ -2820,24 +2858,26 @@ impl<T: Config> XykFunctionsTrait<T::AccountId> for Pallet<T> {
let total_liquidity_assets: Self::Balance =
<T as Config>::Currency::total_issuance(liquidity_asset_id.into()).into();

// Calculation of required second asset amount and received liquidity token amount
ensure!(!first_asset_reserve.is_zero(), Error::<T>::DivisionByZero);
let second_asset_amount = multiply_by_rational_with_rounding(
first_asset_amount,
second_asset_reserve,
first_asset_reserve,
Rounding::Down,
)
.ok_or(Error::<T>::UnexpectedFailure)?
.checked_add(1)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?;
let liquidity_assets_minted = multiply_by_rational_with_rounding(
first_asset_amount,
total_liquidity_assets,
first_asset_reserve,
Rounding::Down,
)
.ok_or(Error::<T>::UnexpectedFailure)?;
// The pool is empty and we are basically creating a new pool and reusing the existing one
let second_asset_amount = if !(first_asset_reserve.is_zero() &&
second_asset_reserve.is_zero()) &&
!total_liquidity_assets.is_zero()
{
// Calculation of required second asset amount and received liquidity token amount
ensure!(!first_asset_reserve.is_zero(), Error::<T>::DivisionByZero);

multiply_by_rational_with_rounding(
first_asset_amount,
second_asset_reserve,
first_asset_reserve,
Rounding::Down,
)
.ok_or(Error::<T>::UnexpectedFailure)?
.checked_add(1)
.ok_or_else(|| DispatchError::from(Error::<T>::MathOverflow))?
} else {
expected_second_asset_amount
};

ensure!(
second_asset_amount <= expected_second_asset_amount,
Expand All @@ -2850,6 +2890,19 @@ impl<T: Config> XykFunctionsTrait<T::AccountId> for Pallet<T> {
Error::<T>::ZeroAmount,
);

// We calculate the required liquidity token amount and also validate asset amounts
let liquidity_assets_minted = if total_liquidity_assets.is_zero() {
Pallet::<T>::calculate_initial_liquidity(first_asset_amount, second_asset_amount)?
} else {
multiply_by_rational_with_rounding(
first_asset_amount,
total_liquidity_assets,
first_asset_reserve,
Rounding::Down,
)
.ok_or(Error::<T>::UnexpectedFailure)?
};

// Ensure user has enough withdrawable tokens to create pool in amounts required

<T as Config>::Currency::ensure_can_withdraw(
Expand Down Expand Up @@ -3191,6 +3244,7 @@ impl<T: Config> XykFunctionsTrait<T::AccountId> for Pallet<T> {
second_asset_amount
);

// Is liquidity asset amount empty?
if liquidity_asset_amount == total_liquidity_assets {
log!(
info,
Expand All @@ -3200,11 +3254,7 @@ impl<T: Config> XykFunctionsTrait<T::AccountId> for Pallet<T> {
second_asset_id,
first_asset_id,
);
Pools::<T>::remove((first_asset_id, second_asset_id));
Pools::<T>::remove((second_asset_id, first_asset_id));
LiquidityAssets::<T>::remove((first_asset_id, second_asset_id));
LiquidityAssets::<T>::remove((second_asset_id, first_asset_id));
LiquidityPools::<T>::remove(liquidity_asset_id);
Pallet::<T>::set_reserves(first_asset_id, 0, second_asset_id, 0)?;
} else {
// Apply changes in token pools, removing withdrawn amounts
// Cannot underflow due to earlier ensure
Expand Down
Loading

0 comments on commit ecaad59

Please sign in to comment.