Skip to content

Commit

Permalink
Fixed minor add_liquidity bugs
Browse files Browse the repository at this point in the history
Fixed add_liquidity so that adds in which quantity <= 0 fail
Fixed add_liquidity so that adds when the agent already has a position in that asset fail
  • Loading branch information
poliwop committed Jun 27, 2023
1 parent aea9d6b commit c3bea45
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 24 deletions.
13 changes: 10 additions & 3 deletions hydradx/model/amm/omnipool_amm.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def __init__(self,
if last_oracle_values is None or 'price' not in last_oracle_values:
self.oracles['price'] = Oracle(sma_equivalent_length=19, first_block=Block(self))
else:
self.oracles['price'] = Oracle(sma_equivalent_length=19, first_block=Block(self), last_values=last_oracle_values['price'])
self.oracles['price'] = Oracle(sma_equivalent_length=19, first_block=Block(self),
last_values=last_oracle_values['price'])
if last_oracle_values is not None and oracles is not None:
self.oracles.update({
name: Oracle(sma_equivalent_length=period, last_values=last_oracle_values[name]
Expand Down Expand Up @@ -901,8 +902,14 @@ def execute_add_liquidity(
) -> tuple[OmnipoolState, Agent]:
"""Compute new state after liquidity addition"""

if quantity <= 0:
return state.fail_transaction('Quantity must be non-negative.', agent)

delta_Q = lrna_price(state, tkn_add) * quantity
if not (state.unique_id, tkn_add) in agent.holdings:
if (state.unique_id, tkn_add) in agent.holdings:
if agent.holdings[(state.unique_id, tkn_add)] != 0:
return state.fail_transaction(f'Agent already has liquidity in pool {tkn_add}.', agent)
else:
agent.holdings[(state.unique_id, tkn_add)] = 0

if agent.holdings[tkn_add] < quantity:
Expand Down Expand Up @@ -1326,4 +1333,4 @@ def cash_out_omnipool(omnipool: OmnipoolState, agent: Agent, prices) -> float:
if agent_holdings[tkn] > 0 and tkn not in prices:
raise ValueError(f'Agent has holdings in {tkn} but no price was given.')

return value_assets(prices, agent_holdings)
return value_assets(prices, agent_holdings)
78 changes: 57 additions & 21 deletions hydradx/tests/test_omnipool_amm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import copy
import math
import random

import pytest
from hypothesis import given, strategies as st, assume, settings
Expand All @@ -13,7 +12,6 @@
from hydradx.model.amm.trade_strategies import constant_swaps, omnipool_arbitrage
from hydradx.tests.strategies_omnipool import omnipool_reasonable_config, omnipool_config, assets_config


asset_price_strategy = st.floats(min_value=0.0001, max_value=100000)
asset_price_bounded_strategy = st.floats(min_value=0.1, max_value=10)
asset_number_strategy = st.integers(min_value=3, max_value=5)
Expand Down Expand Up @@ -127,6 +125,40 @@ def test_add_liquidity(initial_state: oamm.OmnipoolState):
raise AssertionError(f'legal transaction failed against weight limit in {i} ({new_state.fail})')


@settings(max_examples=1)
@given(omnipool_config(token_count=3, lrna_fee=0, asset_fee=0))
def test_add_liquidity_with_existing_position_fails(initial_state: oamm.OmnipoolState):
old_state = initial_state
tkn = old_state.asset_list[0]
old_agent = Agent(
holdings={tkn: old_state.liquidity[tkn] / 10, (old_state.unique_id, tkn): old_state.shares[tkn] / 10}
)

delta_R = old_agent.holdings[tkn]

new_state, new_agents = oamm.add_liquidity(old_state, old_agent, delta_R, tkn)

if not new_state.fail:
raise AssertionError(f'Adding liquidity to an existing position should fail.')


@settings(max_examples=1)
@given(omnipool_config(token_count=3, lrna_fee=0, asset_fee=0))
def test_add_liquidity_with_quantity_zero_should_fail(initial_state: oamm.OmnipoolState):
old_state = initial_state
tkn = old_state.asset_list[0]
old_agent = Agent(
holdings={tkn: old_state.liquidity[tkn] / 10, (old_state.unique_id, tkn): old_state.shares[tkn] / 10}
)

delta_R = 0

new_state, new_agents = oamm.add_liquidity(old_state, old_agent, delta_R, tkn)

if not new_state.fail:
raise AssertionError(f'Adding liquidity with quantity zero should fail.')


@given(omnipool_config(token_count=3, withdrawal_fee=False))
def test_remove_liquidity_no_fee(initial_state: oamm.OmnipoolState):
i = initial_state.asset_list[2]
Expand Down Expand Up @@ -1251,52 +1283,56 @@ def test_volatility_limit(omnipool: oamm.OmnipoolState):
def test_LP_limits(omnipool: oamm.OmnipoolState, max_withdrawal_per_block, max_lp_per_block):
omnipool.max_withdrawal_per_block = max_withdrawal_per_block
omnipool.max_lp_per_block = max_lp_per_block
agent = Agent(holdings={'HDX': 10000000000})
state = omnipool.copy()
initial_agent = Agent(holdings={'HDX': 10000000000})
agent = initial_agent.copy()
oamm.execute_add_liquidity(
state=omnipool,
state=state,
agent=agent,
tkn_add='HDX',
quantity=omnipool.liquidity['HDX'] * max_lp_per_block
quantity=state.liquidity['HDX'] * max_lp_per_block
)
if omnipool.fail:
if state.fail:
raise AssertionError('Valid LP operation failed.')
omnipool.update()
state = omnipool.copy()
agent = initial_agent.copy()
oamm.execute_add_liquidity(
state=omnipool,
state=state,
agent=agent,
tkn_add='HDX',
quantity=omnipool.liquidity['HDX'] * max_lp_per_block + 1
quantity=state.liquidity['HDX'] * max_lp_per_block + 1
)
if not omnipool.fail:
if not state.fail:
raise AssertionError('Invalid LP operation succeeded.')
omnipool.update()
state = omnipool.copy()
agent = initial_agent.copy()
# add liquidity again to test remove liquidity
oamm.execute_add_liquidity(
state=omnipool,
state=state,
agent=agent,
tkn_add='HDX',
quantity=omnipool.liquidity['HDX'] * max_lp_per_block
quantity=state.liquidity['HDX'] * max_lp_per_block
)
if omnipool.fail:
if state.fail:
raise AssertionError('Second LP operation failed.')
withdraw_quantity = agent.holdings[('omnipool', 'HDX')]
total_shares = omnipool.shares['HDX']
total_shares = state.shares['HDX']
oamm.execute_remove_liquidity(
state=omnipool,
state=state,
agent=agent,
tkn_remove='HDX',
quantity=withdraw_quantity # agent.holdings[('omnipool', 'HDX')]
)
if withdraw_quantity / total_shares > max_withdrawal_per_block and not omnipool.fail:
if withdraw_quantity / total_shares > max_withdrawal_per_block and not state.fail:
raise AssertionError('Agent was able to remove too much liquidity.')
omnipool.update()
state.update()
oamm.execute_remove_liquidity(
state=omnipool,
state=state,
agent=agent,
tkn_remove='HDX',
quantity=omnipool.shares['HDX'] * max_withdrawal_per_block
quantity=state.shares['HDX'] * max_withdrawal_per_block
)
if omnipool.fail:
if state.fail:
raise AssertionError('Agent was not able to remove liquidity.')


Expand Down

0 comments on commit c3bea45

Please sign in to comment.