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 authored May 2, 2024
1 parent 9cb4af9 commit d196218
Show file tree
Hide file tree
Showing 15 changed files with 1,185 additions and 222 deletions.
10 changes: 10 additions & 0 deletions contracts/liquidity_hub/pool-manager/schema/pool-manager.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@
"null"
]
},
"max_spread": {
"anyOf": [
{
"$ref": "#/definitions/Decimal"
},
{
"type": "null"
}
]
},
"pair_identifier": {
"type": "string"
},
Expand Down
10 changes: 10 additions & 0 deletions contracts/liquidity_hub/pool-manager/schema/raw/execute.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@
"null"
]
},
"max_spread": {
"anyOf": [
{
"$ref": "#/definitions/Decimal"
},
{
"type": "null"
}
]
},
"pair_identifier": {
"type": "string"
},
Expand Down
48 changes: 46 additions & 2 deletions contracts/liquidity_hub/pool-manager/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
use crate::error::ContractError;
use crate::helpers::{reverse_simulate_swap_operations, simulate_swap_operations};
use crate::helpers::{
reverse_simulate_swap_operations, simulate_swap_operations, validate_asset_balance,
};
use crate::queries::{get_pair, get_swap_route, get_swap_route_creator, get_swap_routes};
use crate::router::commands::{add_swap_routes, remove_swap_routes};
use crate::state::{Config, MANAGER_CONFIG, PAIR_COUNTER};
use crate::state::{
Config, SingleSideLiquidityProvisionBuffer, MANAGER_CONFIG, PAIR_COUNTER,
TMP_SINGLE_SIDE_LIQUIDITY_PROVISION,
};
use crate::{liquidity, manager, queries, router, swap};
#[cfg(not(feature = "library"))]
use cosmwasm_std::{
entry_point, to_json_binary, Addr, Api, Binary, Deps, DepsMut, Env, MessageInfo, Response,
};
use cosmwasm_std::{wasm_execute, Reply, StdError};
use cw2::set_contract_version;
use semver::Version;
use white_whale_std::pool_manager::{
Expand All @@ -17,6 +23,7 @@ use white_whale_std::pool_manager::{
// version info for migration info
const CONTRACT_NAME: &str = "crates.io:ww-pool-manager";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const SINGLE_SIDE_LIQUIDITY_PROVISION_REPLY_ID: u64 = 1;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
Expand Down Expand Up @@ -45,6 +52,41 @@ pub fn instantiate(
Ok(Response::default())
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
SINGLE_SIDE_LIQUIDITY_PROVISION_REPLY_ID => {
let SingleSideLiquidityProvisionBuffer {
receiver,
expected_offer_asset_balance_in_contract,
expected_ask_asset_balance_in_contract,
offer_asset_half,
expected_ask_asset,
liquidity_provision_data,
} = TMP_SINGLE_SIDE_LIQUIDITY_PROVISION.load(deps.storage)?;

validate_asset_balance(&deps, &env, &expected_offer_asset_balance_in_contract)?;
validate_asset_balance(&deps, &env, &expected_ask_asset_balance_in_contract)?;

TMP_SINGLE_SIDE_LIQUIDITY_PROVISION.remove(deps.storage);

Ok(Response::default().add_message(wasm_execute(
env.contract.address.into_string(),
&ExecuteMsg::ProvideLiquidity {
slippage_tolerance: liquidity_provision_data.slippage_tolerance,
max_spread: liquidity_provision_data.max_spread,
receiver: Some(receiver),
pair_identifier: liquidity_provision_data.pair_identifier,
unlocking_duration: liquidity_provision_data.unlocking_duration,
lock_position_identifier: liquidity_provision_data.lock_position_identifier,
},
vec![offer_asset_half, expected_ask_asset],
)?))
}
_ => Err(StdError::generic_err("reply id not found").into()),
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
Expand All @@ -70,6 +112,7 @@ pub fn execute(
pair_identifier,
),
ExecuteMsg::ProvideLiquidity {
max_spread,
slippage_tolerance,
receiver,
pair_identifier,
Expand All @@ -80,6 +123,7 @@ pub fn execute(
env,
info,
slippage_tolerance,
max_spread,
receiver,
pair_identifier,
unlocking_duration,
Expand Down
20 changes: 18 additions & 2 deletions contracts/liquidity_hub/pool-manager/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::liquidity::commands::MAX_ASSETS_PER_POOL;
use cosmwasm_std::{
CheckedFromRatioError, CheckedMultiplyRatioError, ConversionOverflowError, DivideByZeroError,
Instantiate2AddressError, OverflowError, StdError, Uint128,
CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError,
ConversionOverflowError, DivideByZeroError, Instantiate2AddressError, OverflowError, StdError,
Uint128,
};
use cw_ownable::OwnershipError;
use cw_utils::PaymentError;
Expand Down Expand Up @@ -65,6 +66,15 @@ pub enum ContractError {
#[error("{asset} is invalid")]
InvalidAsset { asset: String },

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

#[error("Invalid single side liquidity provision swap, expected {expected} got {actual}")]
InvalidSingleSideLiquidityProvisionSwap { expected: Uint128, actual: Uint128 },

#[error("Cannot provide single-side liquidity when the pool is empty")]
EmptyPoolForSingleSideLiquidityProvision,

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

Expand All @@ -80,6 +90,9 @@ pub enum ContractError {
#[error("Failed to compute the LP share with the given deposit")]
LiquidityShareComputation {},

#[error("The amount of LP shares to withdraw is invalid")]
InvalidLpShare,

#[error("Spread limit exceeded")]
MaxSpreadAssertion {},

Expand Down Expand Up @@ -116,6 +129,9 @@ pub enum ContractError {
#[error(transparent)]
CheckedMultiplyRatioError(#[from] CheckedMultiplyRatioError),

#[error(transparent)]
CheckedMultiplyFractionError(#[from] CheckedMultiplyFractionError),

#[error(transparent)]
CheckedFromRatioError(#[from] CheckedFromRatioError),

Expand Down
57 changes: 54 additions & 3 deletions contracts/liquidity_hub/pool-manager/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use std::ops::Mul;

use cosmwasm_schema::cw_serde;
use cosmwasm_std::{
coin, Addr, Coin, Decimal, Decimal256, Deps, Env, StdError, StdResult, Storage, Uint128,
Uint256,
coin, ensure, Addr, Coin, Decimal, Decimal256, Deps, DepsMut, Env, StdError, StdResult,
Storage, Uint128, Uint256,
};

use white_whale_std::fee::PoolFee;
use white_whale_std::pool_manager::{SimulateSwapOperationsResponse, SwapOperation};
use white_whale_std::pool_manager::{
SimulateSwapOperationsResponse, SimulationResponse, SwapOperation,
};
use white_whale_std::pool_network::asset::{Asset, AssetInfo, PairType};

use crate::error::ContractError;
Expand Down Expand Up @@ -171,6 +173,9 @@ pub fn compute_swap(
let protocol_fee_amount: Uint256 = pool_fees.protocol_fee.compute(return_amount);
let burn_fee_amount: Uint256 = pool_fees.burn_fee.compute(return_amount);

//todo compute the extra fees
//let extra_fees_amount: Uint256 = pool_fees.extra_fees.compute(return_amount);

// swap and protocol fee will be absorbed by the pool. Burn fee amount will be burned on a subsequent msg.
#[cfg(not(feature = "osmosis"))]
{
Expand Down Expand Up @@ -590,3 +595,49 @@ pub fn reverse_simulate_swap_operations(

Ok(SimulateSwapOperationsResponse { amount })
}

/// Validates the amounts after a single side liquidity provision swap are correct.
pub fn validate_asset_balance(
deps: &DepsMut,
env: &Env,
expected_balance: &Coin,
) -> Result<(), ContractError> {
let new_asset_balance = deps
.querier
.query_balance(&env.contract.address, expected_balance.denom.to_owned())?;

ensure!(
expected_balance == &new_asset_balance,
ContractError::InvalidSingleSideLiquidityProvisionSwap {
expected: expected_balance.amount,
actual: new_asset_balance.amount
}
);

Ok(())
}

/// Aggregates the fees from a simulation response that go out of the contract, i.e. protocol fee, burn fee
/// and osmosis fee, if applicable. Doesn't know about the denom, just the amount.
pub fn aggregate_outgoing_fees(
simulation_response: &SimulationResponse,
) -> Result<Uint128, ContractError> {
let fees = {
#[cfg(not(feature = "osmosis"))]
{
simulation_response
.protocol_fee_amount
.checked_add(simulation_response.burn_fee_amount)?
}

#[cfg(feature = "osmosis")]
{
simulation_response
.protocol_fee_amount
.checked_add(simulation_response.burn_fee_amount)?
.checked_add(simulation_response.osmosis_fee_amount)?
}
};

Ok(fees)
}
Loading

0 comments on commit d196218

Please sign in to comment.