Skip to content
Closed
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
133 changes: 66 additions & 67 deletions contracts/pool/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
use phoenix::utils::LiquidityPoolInitInfo;
use soroban_sdk::{
contract, contractimpl, contractmeta, log, panic_with_error, Address, BytesN, Env, IntoVal,
};

use num_integer::Roots;

use crate::contracterror::ContractError;
use crate::storage::utils::{is_initialized, set_initialized};
use crate::storage::{ComputeSwap, LiquidityPoolInfo};
use crate::{
error::ContractError,
stake_contract,
storage::{
get_config, save_config, utils, validate_fee_bps, Asset, Config, PairType, PoolResponse,
get_config, save_config, utils,
utils::{is_initialized, set_initialized},
validate_fee_bps, Asset, ComputeSwap, Config, LiquidityPoolInfo, PairType, PoolResponse,
SimulateReverseSwapResponse, SimulateSwapResponse,
},
token_contract,
};
use decimal::Decimal;
use phoenix::{utils::is_approx_ratio, validate_bps, validate_int_parameters};
use phoenix::{
utils::{is_approx_ratio, LiquidityPoolInitInfo},
validate_bps, validate_int_parameters,
};

// Metadata that is added on to the WASM custom section
contractmeta!(
Expand Down Expand Up @@ -243,7 +245,7 @@ impl LiquidityPoolTrait for LiquidityPool {
// Check if custom_slippage_bps is more than max_allowed_slippage
if let Some(custom_slippage) = custom_slippage_bps {
if custom_slippage > config.max_allowed_slippage_bps {
panic!("Pool: ProvideLiquidity: Custom slippage tolerance is more than max allowed slippage tolerance");
panic_with_error!(env, ContractError::ProvideLiquiditySlippageToleranceTooHigh);
}
}

Expand All @@ -265,6 +267,7 @@ impl LiquidityPoolTrait for LiquidityPool {
}
// Only token A is provided
(Some(a), None) if a > 0 => {
// @audit
let (a_for_swap, b_from_swap) = split_deposit_based_on_pool_ratio(
&env,
&config,
Expand All @@ -273,21 +276,24 @@ impl LiquidityPoolTrait for LiquidityPool {
a,
&config.token_a,
);
do_swap(
let actual_b_from_swap = do_swap(
env.clone(),
sender.clone(),
// FIXM: Disable Referral struct
// FIXME: Disable Referral struct
// None,
config.clone().token_a,
a_for_swap,
None,
None,
);
// return: rest of Token A amount, simulated result of swap of portion A
(a - a_for_swap, b_from_swap)
if (actual_b_from_swap - b_from_swap).abs() > 1 {
panic!("Off by more than rounding error! a: {}, b: {}", actual_b_from_swap, b_from_swap);
}
(a - a_for_swap, actual_b_from_swap)
}
// Only token B is provided
(None, Some(b)) if b > 0 => {
// @audit
let (b_for_swap, a_from_swap) = split_deposit_based_on_pool_ratio(
&env,
&config,
Expand All @@ -296,26 +302,31 @@ impl LiquidityPoolTrait for LiquidityPool {
b,
&config.token_b,
);
do_swap(
let actual_a_from_swap = do_swap(
env.clone(),
sender.clone(),
// FIXM: Disable Referral struct
// FIXME: Disable Referral struct
// None,
config.clone().token_b,
b_for_swap,
None,
None,
);
// return: simulated result of swap of portion B, rest of Token B amount
(a_from_swap, b - b_for_swap)
if (actual_a_from_swap - a_from_swap).abs() > 1 {
panic!("Off by more than rounding error!");
}
(actual_a_from_swap, b - b_for_swap)
}
// None or invalid amounts are provided
_ => {
log!(
&env,
"At least one token must be provided and must be bigger then 0!"
);
panic!("Pool: ProvideLiquidity: At least one token must be provided and must be bigger then 0!");
panic_with_error!(
env,
ContractError::ProvideLiquidityAtLeastOneTokenMustBeBiggerThenZero
);
}
};

Expand Down Expand Up @@ -426,9 +437,10 @@ impl LiquidityPoolTrait for LiquidityPool {
return_amount_a,
return_amount_b
);
panic!(
"Pool: WithdrawLiquidity: Minimum amount of token_a or token_b is not satisfied!"
)
panic_with_error!(
env,
ContractError::WithdrawLiquidityMinimumAmountOfAOrBIsNotSatisfied
);
}

// burn shares
Expand Down Expand Up @@ -779,62 +791,49 @@ fn split_deposit_based_on_pool_ratio(
// Validate the inputs
if a_pool <= 0 || b_pool <= 0 || deposit <= 0 {
log!(env, "Both pools and deposit must be a positive!");
panic!(
"Pool: split_deposit_based_on_pool_ratio: Both pools and deposit must be a positive!"
panic_with_error!(
env,
ContractError::SplitDepositBothPoolsAndDepositMustBePositive
);
}

// Calculate the current ratio in the pool
let target_ratio = Decimal::from_ratio(b_pool, a_pool);
// Define boundaries for binary search algorithm
let mut low = 0;
let mut high = deposit;
if offer_asset != &config.token_a && offer_asset != &config.token_b {
panic!("Pool: split_deposit_based_on_pool_ratio: asset must be asset a or asset b!");
}

// Tolerance is the smallest difference in deposit that we care about
let tolerance = 500;
let fee = config.protocol_fee_rate();
let (offer_pool, ask_pool) = if offer_asset == &config.token_a {
(a_pool, b_pool)
} else {
(b_pool, a_pool)
};

let mut final_offer_amount = deposit; // amount of deposit tokens to be swapped
let mut final_ask_amount = 0; // amount of other tokens to be received
let final_offer_amount = {
let numerator = deposit * fee - 2 * offer_pool
+ (deposit * deposit * fee * fee
+ 4 * deposit * offer_pool
+ 4 * offer_pool * offer_pool)
.sqrt();
let denominator = 2 * (fee + Decimal::one());
numerator / denominator
};

while high - low > tolerance {
let mid = (low + high) / 2; // Calculate middle point
let final_ask_amount = {
let numerator = ask_pool * final_offer_amount;
let denominator = offer_pool + final_offer_amount;
numerator / denominator
};

// Simulate swap to get amount of other tokens to be received for `mid` amount of deposit tokens
let SimulateSwapResponse {
ask_amount,
spread_amount: _,
commission_amount: _,
total_return: _,
} = LiquidityPool::simulate_swap(env.clone(), offer_asset.clone(), mid);

// Update final amounts
final_offer_amount = mid;
final_ask_amount = ask_amount;

// Calculate the ratio that would result from swapping `mid` deposit tokens
let ratio = if offer_asset == &config.token_a {
Decimal::from_ratio(ask_amount, deposit - mid)
} else {
Decimal::from_ratio(deposit - mid, ask_amount)
};
log!(
&env,
"log",
a_pool,
b_pool,
deposit,
final_offer_amount,
final_ask_amount
);

// If the resulting ratio is approximately equal (1%) to the target ratio, break the loop
if is_approx_ratio(ratio, target_ratio, Decimal::percent(1)) {
break;
}
// Update boundaries for the next iteration of the binary search
if ratio > target_ratio {
if offer_asset == &config.token_a {
high = mid;
} else {
low = mid;
}
} else if offer_asset == &config.token_a {
low = mid;
} else {
high = mid;
};
}
(final_offer_amount, final_ask_amount)
}

Expand Down
8 changes: 0 additions & 8 deletions contracts/pool/src/contracterror.rs

This file was deleted.

22 changes: 22 additions & 0 deletions contracts/pool/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use soroban_sdk::contracterror;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ContractError {
SpreadExceedsLimit = 1,

ProvideLiquiditySlippageToleranceTooHigh = 2,
ProvideLiquidityAtLeastOneTokenMustBeBiggerThenZero = 3,

WithdrawLiquidityMinimumAmountOfAOrBIsNotSatisfied = 4,
SplitDepositBothPoolsAndDepositMustBePositive = 5,
ValidateFeeBpsTotalFeesCantBeGreaterThen100 = 6,

GetDepositAmountsMinABiggerThenDesiredA = 7,
GetDepositAmountsMinBBiggerThenDesiredB = 8,
GetDepositAmountsAmountABiggerThenDesiredA = 9,
GetDepositAmountsAmountALessThenMinA = 10,
GetDepositAmountsAmountBBiggerThenDesiredB = 11,
GetDepositAmountsAmountBLessThenMinB = 12,
}
2 changes: 1 addition & 1 deletion contracts/pool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![no_std]
mod contract;
mod contracterror;
mod error;
mod storage;

pub mod token_contract {
Expand Down
29 changes: 19 additions & 10 deletions contracts/pool/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use soroban_sdk::{
contracttype, log, symbol_short, xdr::ToXdr, Address, Bytes, BytesN, ConversionError, Env,
Symbol, TryFromVal, Val,
contracttype, log, panic_with_error, symbol_short, xdr::ToXdr, Address, Bytes, BytesN,
ConversionError, Env, Symbol, TryFromVal, Val,
};

use crate::token_contract;
use crate::{error::ContractError, token_contract};
use decimal::Decimal;

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -58,7 +58,10 @@ const MAX_TOTAL_FEE_BPS: i64 = 10_000;
pub fn validate_fee_bps(env: &Env, total_fee_bps: i64) -> i64 {
if total_fee_bps > MAX_TOTAL_FEE_BPS {
log!(env, "Total fees cannot be greater than 100%");
panic!("Pool: Validate fee bps: total fees cannot be greater than 100%")
panic_with_error!(
env,
ContractError::ValidateFeeBpsTotalFeesCantBeGreaterThen100
);
}
total_fee_bps
}
Expand Down Expand Up @@ -247,12 +250,12 @@ pub mod utils {

if let Some(min_a) = min_a {
if min_a > desired_a {
panic!("Pool: Get deposit amounts: min_a > desired_a");
panic_with_error!(env, ContractError::GetDepositAmountsMinABiggerThenDesiredA);
}
}
if let Some(min_b) = min_b {
if min_b > desired_b {
panic!("Pool: Get deposit amounts: min_b > desired_b");
panic_with_error!(env, ContractError::GetDepositAmountsMinABiggerThenDesiredA);
}
}

Expand All @@ -269,7 +272,10 @@ pub mod utils {
amount_a,
desired_a,
);
panic!("Pool: Get deposit amounts: amount_a > desired_a");
panic_with_error!(
env,
ContractError::GetDepositAmountsAmountABiggerThenDesiredA
);
}
};
if let Some(min_a) = min_a {
Expand All @@ -280,7 +286,7 @@ pub mod utils {
amount_a,
min_a
);
panic!("Pool: Get deposit amounts: amount_a < min_a");
panic_with_error!(env, ContractError::GetDepositAmountsAmountALessThenMinA);
}
}
amount_a
Expand All @@ -299,7 +305,10 @@ pub mod utils {
amount_b,
desired_b,
);
panic!("Pool: Get deposit amounts: amount_b > desired_b");
panic_with_error!(
env,
ContractError::GetDepositAmountsAmountBBiggerThenDesiredB
);
}
};
if let Some(min_b) = min_b {
Expand All @@ -310,7 +319,7 @@ pub mod utils {
amount_b,
min_b
);
panic!("Pool: Get deposit amounts: amount_b < min_b");
panic_with_error!(env, ContractError::GetDepositAmountsAmountBLessThenMinB);
}
}
amount_b
Expand Down
2 changes: 1 addition & 1 deletion contracts/pool/src/tests/liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ fn provide_liqudity_single_asset_equal_with_fees() {
&Some(token_a_amount),
&Some(50_000),
&None,
&Some(49_000),
&None,
&None,
);
// before swap : A(10_000_000), B(10_000_000)
Expand Down
Loading