Skip to content

Commit

Permalink
feat: single-side liquidity provision
Browse files Browse the repository at this point in the history
  • Loading branch information
kerber0x committed Apr 26, 2024
1 parent 75794fe commit 5d6ba8d
Show file tree
Hide file tree
Showing 4 changed files with 538 additions and 46 deletions.
3 changes: 3 additions & 0 deletions contracts/liquidity_hub/pool-manager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub enum ContractError {
#[error("{asset} is invalid")]
InvalidAsset { asset: String },

#[error("Trying to provide liquidity without any assets")]
EmptyAssets,

#[error("Pair does not exist")]
UnExistingPair {},

Expand Down
109 changes: 71 additions & 38 deletions contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use cosmwasm_std::{
coins, wasm_execute, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response,
coins, ensure, wasm_execute, BankMsg, Coin, CosmosMsg, DepsMut, Env, MessageInfo, Response,
StdError,
};
use white_whale_std::pool_network::asset::PairType;

Expand All @@ -14,14 +15,13 @@ use crate::{
// After writing create_pair I see this can get quite verbose so attempting to
// break it down into smaller modules which house some things like swap, liquidity etc
use cosmwasm_std::{Decimal, OverflowError, Uint128};
use white_whale_std::coin::aggregate_coins;
use white_whale_std::pool_network::{
asset::{get_total_share, MINIMUM_LIQUIDITY_AMOUNT},
U256,
};
pub const MAX_ASSETS_PER_POOL: usize = 4;

// todo allow providing liquidity with a single asset

#[allow(clippy::too_many_arguments)]
pub fn provide_liquidity(
deps: DepsMut,
Expand All @@ -45,18 +45,40 @@ pub fn provide_liquidity(
let mut pair = get_pair_by_identifier(&deps.as_ref(), &pair_identifier)?;

let mut pool_assets = pair.assets.clone();
let mut assets = info.funds.clone();
let deposits = aggregate_coins(info.funds.clone())?;
let mut messages: Vec<CosmosMsg> = vec![];

//TODO verify that the assets sent in info match the ones from the pool!!!

for (i, pool) in assets.iter_mut().enumerate() {
// Increment the pool asset amount by the amount sent
pool_assets[i].amount = pool_assets[i].amount.checked_add(pool.amount)?;
ensure!(!deposits.is_empty(), ContractError::EmptyAssets);

// verify that the assets sent match the ones from the pool
ensure!(
deposits.iter().all(|asset| pool_assets
.iter()
.any(|pool_asset| pool_asset.denom == asset.denom)),
ContractError::AssetMismatch {}
);

// check if the user is providing liquidity with a single asset
let is_single_asset_provision = deposits.len() == 1;

for asset in deposits.iter() {
let asset_denom = &asset.denom;
if let Some(pool_asset_index) = pool_assets
.iter()
.position(|pool_asset| pool_asset.denom == *asset_denom)
{
// Increment the pool asset amount by the amount sent
pool_assets[pool_asset_index].amount = pool_assets[pool_asset_index]
.amount
.checked_add(asset.amount)?;
} else {
return Err(ContractError::AssetMismatch {});

Check warning on line 75 in contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs#L75

Added line #L75 was not covered by tests
}
}

// After totting up the pool assets we need to check if any of them are zero
if pool_assets.iter().any(|deposit| deposit.amount.is_zero()) {
// After totting up the pool assets we need to check if any of them are zero.
// The very first deposit cannot be done with a single asset
if pool_assets.iter().any(|deposit| deposit.amount.is_zero()) && is_single_asset_provision {
return Err(ContractError::InvalidZeroAmount {});
}

Expand All @@ -70,7 +92,6 @@ pub fn provide_liquidity(
if total_share == Uint128::zero() {
// Make sure at least MINIMUM_LIQUIDITY_AMOUNT is deposited to mitigate the risk of the first
// depositor preventing small liquidity providers from joining the pool

let share = Uint128::new(
(U256::from(pool_assets[0].amount.u128())
.checked_mul(U256::from(pool_assets[1].amount.u128()))
Expand All @@ -82,6 +103,7 @@ pub fn provide_liquidity(
.map_err(|_| {
ContractError::InvalidInitialLiquidityAmount(MINIMUM_LIQUIDITY_AMOUNT)
})?;

// share should be above zero after subtracting the MINIMUM_LIQUIDITY_AMOUNT
if share.is_zero() {
return Err(ContractError::InvalidInitialLiquidityAmount(
Expand All @@ -98,19 +120,19 @@ pub fn provide_liquidity(

share
} else {
let share = {
let numerator = U256::from(pool_assets[0].amount.u128())
.checked_mul(U256::from(total_share.u128()))
.ok_or::<ContractError>(ContractError::LiquidityShareComputation {})?;

let denominator = U256::from(pool_assets[0].amount.u128());

let result = numerator
.checked_div(denominator)
.ok_or::<ContractError>(ContractError::LiquidityShareComputation {})?;

Uint128::from(result.as_u128())
};
// let share = {
// let numerator = U256::from(pool_assets[0].amount.u128())
// .checked_mul(U256::from(total_share.u128()))
// .ok_or::<ContractError>(ContractError::LiquidityShareComputation {})?;
//
// let denominator = U256::from(pool_assets[0].amount.u128());
//
// let result = numerator
// .checked_div(denominator)
// .ok_or::<ContractError>(ContractError::LiquidityShareComputation {})?;
//
// Uint128::from(result.as_u128())
// };

let amount = std::cmp::min(
pool_assets[0]
Expand All @@ -121,20 +143,31 @@ pub fn provide_liquidity(
.multiply_ratio(total_share, pool_assets[1].amount),
);

let deps_as = [pool_assets[0].amount, pool_assets[1].amount];
let pools_as = [pool_assets[0].clone(), pool_assets[1].clone()];

// assert slippage tolerance
helpers::assert_slippage_tolerance(
&slippage_tolerance,
&deps_as,
&pools_as,
pair.pair_type.clone(),
amount,
total_share,
)?;
//todo i think we need to skip slippage_tolerance assertion in this case
if !is_single_asset_provision {
let deposits_as: [Uint128; 2] = deposits
.iter()
.map(|coin| coin.amount)
.collect::<Vec<_>>()
.try_into()
.map_err(|_| StdError::generic_err("Error converting vector to array"))?;

Check warning on line 153 in contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs#L153

Added line #L153 was not covered by tests
let pools_as: [Coin; 2] = pool_assets
.to_vec()
.try_into()
.map_err(|_| StdError::generic_err("Error converting vector to array"))?;

Check warning on line 157 in contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs

View check run for this annotation

Codecov / codecov/patch

contracts/liquidity_hub/pool-manager/src/liquidity/commands.rs#L157

Added line #L157 was not covered by tests

// assert slippage tolerance
helpers::assert_slippage_tolerance(
&slippage_tolerance,
&deposits_as,
&pools_as,
pair.pair_type.clone(),
amount,
total_share,
)?;
}

share
amount
}
}
PairType::StableSwap { amp: _ } => {
Expand Down
Loading

0 comments on commit 5d6ba8d

Please sign in to comment.