From b75f7918617907adc5a76cbb38b6c3ad3044b428 Mon Sep 17 00:00:00 2001 From: Kerber0x Date: Mon, 29 Apr 2024 17:23:09 +0100 Subject: [PATCH] fix: pool-manager max spread assertion --- .../liquidity_hub/pool-manager/src/helpers.rs | 76 ++++++------------- .../pool-manager/src/swap/perform_swap.rs | 2 - .../src/tests/integration_tests.rs | 42 +++++++--- 3 files changed, 52 insertions(+), 68 deletions(-) diff --git a/contracts/liquidity_hub/pool-manager/src/helpers.rs b/contracts/liquidity_hub/pool-manager/src/helpers.rs index 472194b5..25498b6d 100644 --- a/contracts/liquidity_hub/pool-manager/src/helpers.rs +++ b/contracts/liquidity_hub/pool-manager/src/helpers.rs @@ -1,15 +1,16 @@ -use std::cmp::Ordering; use std::ops::Mul; +use std::str::FromStr; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - coin, Addr, Coin, Decimal, Decimal256, Deps, Env, StdError, StdResult, Storage, Uint128, - Uint256, + coin, Addr, Coin, Decimal, Decimal256, Deps, Env, Fraction, StdError, StdResult, Storage, + Uint128, Uint256, }; use white_whale_std::fee::PoolFee; use white_whale_std::pool_manager::{SimulateSwapOperationsResponse, SwapOperation}; use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType}; +use white_whale_std::pool_network::swap::{DEFAULT_SLIPPAGE, MAX_ALLOWED_SLIPPAGE}; use crate::error::ContractError; use crate::math::Decimal256Helper; @@ -432,61 +433,28 @@ pub fn assert_max_spread( offer_asset: Coin, return_asset: Coin, spread_amount: Uint128, - offer_decimal: u8, - return_decimal: u8, ) -> Result<(), ContractError> { - let (offer_amount, return_amount, spread_amount): (Uint256, Uint256, Uint256) = - match offer_decimal.cmp(&return_decimal) { - Ordering::Greater => { - let diff_decimal = 10u64.pow((offer_decimal - return_decimal).into()); - - ( - offer_asset.amount.into(), - return_asset - .amount - .checked_mul(Uint128::from(diff_decimal))? - .into(), - spread_amount - .checked_mul(Uint128::from(diff_decimal))? - .into(), - ) - } - Ordering::Less => { - let diff_decimal = 10u64.pow((return_decimal - offer_decimal).into()); - - ( - offer_asset - .amount - .checked_mul(Uint128::from(diff_decimal))? - .into(), - return_asset.amount.into(), - spread_amount.into(), - ) - } - Ordering::Equal => ( - offer_asset.amount.into(), - return_asset.amount.into(), - spread_amount.into(), - ), - }; - - if let (Some(max_spread), Some(belief_price)) = (max_spread, belief_price) { - let belief_price: Decimal256 = belief_price.into(); - let max_spread: Decimal256 = max_spread.into(); - - let expected_return = offer_amount * (Decimal256::one() / belief_price); - let spread_amount = expected_return.saturating_sub(return_amount); - - if return_amount < expected_return + let max_spread: Decimal256 = max_spread + .unwrap_or(Decimal::from_str(DEFAULT_SLIPPAGE)?) + .min(Decimal::from_str(MAX_ALLOWED_SLIPPAGE)?) + .into(); + + if let Some(belief_price) = belief_price { + let expected_return = offer_asset.amount + * belief_price + .inv() + .ok_or_else(|| StdError::generic_err("Belief price can't be zero"))?; + let spread_amount = expected_return.saturating_sub(return_asset.amount); + + if return_asset.amount < expected_return && Decimal256::from_ratio(spread_amount, expected_return) > max_spread { - return Err(ContractError::MaxSpreadAssertion {}); - } - } else if let Some(max_spread) = max_spread { - let max_spread: Decimal256 = max_spread.into(); - if Decimal256::from_ratio(spread_amount, return_amount + spread_amount) > max_spread { - return Err(ContractError::MaxSpreadAssertion {}); + return Err(StdError::generic_err("Spread limit exceeded").into()); } + } else if Decimal256::from_ratio(spread_amount, return_asset.amount + spread_amount) + > max_spread + { + return Err(StdError::generic_err("Spread limit exceeded").into()); } Ok(()) diff --git a/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs b/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs index c928aa7c..33d57971 100644 --- a/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs +++ b/contracts/liquidity_hub/pool-manager/src/swap/perform_swap.rs @@ -91,8 +91,6 @@ pub fn perform_swap( offer_asset.clone(), return_asset.clone(), swap_computation.spread_amount, - offer_decimal, - ask_decimal, )?; // State changes to the pairs balances diff --git a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs index fc432a8d..f68906a3 100644 --- a/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs +++ b/contracts/liquidity_hub/pool-manager/src/tests/integration_tests.rs @@ -320,7 +320,8 @@ mod pair_creation_failures { } mod router { - use cosmwasm_std::Event; + use cosmwasm_std::{Event, StdError}; + use std::error::Error; use white_whale_std::pool_manager::{SwapRoute, SwapRouteCreatorResponse}; use super::*; @@ -1716,17 +1717,34 @@ mod router { // execute the swap operations to unbalance the pools // sold 10_000 whale for some uusd, so the price of whale should go down - suite.execute_swap_operations( - creator.clone(), - swap_operations.clone(), - None, - None, - None, - vec![coin(10_000u128, "uwhale".to_string())], - |result| { - result.unwrap(); - }, - ); + suite + .execute_swap_operations( + creator.clone(), + swap_operations.clone(), + None, + None, + None, + vec![coin(10_000u128, "uwhale".to_string())], + |result| { + let err = result.unwrap_err().downcast::().unwrap(); + + assert_eq!( + err, + ContractError::Std(StdError::generic_err("Spread limit exceeded")) + ); + }, + ) + .execute_swap_operations( + creator.clone(), + swap_operations.clone(), + None, + None, + Some(Decimal::percent(5)), + vec![coin(10_000u128, "uwhale".to_string())], + |result| { + result.unwrap(); + }, + ); // now to get 1_000 uusd we should swap more whale than before suite.query_reverse_simulate_swap_operations(