Skip to content

Commit

Permalink
Merge pull request #13 from Unstoppable-DeFi/fix-140
Browse files Browse the repository at this point in the history
fix-140: ensure user cannot intentionally create bad debt
  • Loading branch information
Unstoppable-DeFi authored Jul 27, 2023
2 parents fe07324 + 0602f81 commit 9582844
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 34 deletions.
8 changes: 6 additions & 2 deletions contracts/margin-dex/Vault.vy
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,7 @@ def open_position(
assert self.is_whitelisted_dex[msg.sender], "unauthorized"
assert self.is_enabled_market[_debt_token][_position_token], "market not enabled"
assert self.margin[_account][_debt_token] >= _margin_amount, "not enough margin"
assert ((_debt_amount + _margin_amount) * PRECISION / _margin_amount) <= self.max_leverage[_debt_token][_position_token] * PRECISION, "too much leverage"
assert (self._available_liquidity(_debt_token) >= _debt_amount), "insufficient liquidity"
assert self._available_liquidity(_debt_token) >= _debt_amount, "insufficient liquidity"

self.margin[_account][_debt_token] -= _margin_amount
debt_shares: uint256 = self._borrow(_debt_token, _debt_amount)
Expand Down Expand Up @@ -205,6 +204,8 @@ def open_position(
self.margin[_account][_debt_token] -= fee
self._distribute_trading_fee(_debt_token, fee)

assert not self._is_liquidatable(position_uid), "cannot open liquidatable position"

log PositionOpened(_account, position)

return position_uid, amount_bought
Expand All @@ -227,6 +228,7 @@ event BadDebt:
@external
def close_position(_position_uid: bytes32, _min_amount_out: uint256) -> uint256:
assert self.is_whitelisted_dex[msg.sender], "unauthorized"
assert _min_amount_out >= self._debt(_position_uid), "invalid min_amount_out"
return self._close_position(_position_uid, _min_amount_out)


Expand Down Expand Up @@ -336,6 +338,8 @@ def reduce_position(

self.positions[_position_uid] = position

assert not self._is_liquidatable(_position_uid), "cannot reduce into liquidation"

log PositionReduced(position.account, _position_uid, position, amount_out_received)

return amount_out_received
Expand Down
14 changes: 7 additions & 7 deletions tests/vault/test_bad_debt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def setup(vault, mock_router, owner, weth, usdc):
def open_position(vault, owner, weth, usdc):
margin_amount = 15 * 10**6
usdc_in = 150 * 10**6
min_weth_out = 123
min_weth_out = 1 * 10**18

uid, amount_bought = vault.open_position(
owner, # account
Expand All @@ -28,22 +28,22 @@ def open_position(vault, owner, weth, usdc):


def test_repay_bad_debt_001(vault, usdc, weth, owner):
"""when bad-debt is repayd it should be reduced in the vault,
"""when bad-debt is repaid it should be reduced in the vault,
the available-liquidity should go up correspondentingly"""
bad_debt_before = vault.bad_debt(usdc)
position_uid, _ = open_position(vault, owner, weth, usdc)
bad_debt_before = vault.bad_debt(usdc)
assert bad_debt_before == 0

vault.positions(position_uid)
vault.close_position(position_uid, 0) # 0 out
vault.internal._close_position(position_uid, 1) # 1 wei out
bad_debt_after = vault.bad_debt(usdc)

assert bad_debt_after == 150 * 10**6
assert vault.available_liquidity(usdc) == 999700000000
assert bad_debt_after == 150 * 10**6 - 1
assert vault.available_liquidity(usdc) == 999700000002

vault.repay_bad_debt(usdc, 150 * 10**6)
vault.repay_bad_debt(usdc, bad_debt_after)
bad_debt_after_repaying = vault.bad_debt(usdc)
assert bad_debt_after_repaying == 0

assert vault.available_liquidity(usdc) == 999700000000 + 150 * 10**6
assert vault.available_liquidity(usdc) == 999700000002 + bad_debt_after
31 changes: 16 additions & 15 deletions tests/vault/test_close_position.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ def setup(vault, mock_router, owner, weth, usdc):


def open_position(vault, owner, weth, usdc):
margin_amount = 15 * 10**6
usdc_in = 150 * 10**6
min_weth_out = 123
margin_amount = 150 * 10**6
usdc_in = 1500 * 10**6
min_weth_out = 3 * 10**18

uid, amount_bought = vault.open_position(
owner, # account
Expand Down Expand Up @@ -42,7 +42,7 @@ def test_close_position_reduces_position_to_zero(vault, owner, weth, usdc):

assert position_before[6] > 0 # [6] = postion_amount

vault.close_position(position_uid, 150 * 10**6)
vault.close_position(position_uid, 1500 * 10**6)
position_after = vault.positions(position_uid)

assert position_after[6] == 0 # [6] = position_amount
Expand All @@ -53,8 +53,8 @@ def test_close_position_swaps_liquidity_for_underlying(vault, weth, usdc, owner)
liquidity_before = usdc.balanceOf(vault)
underlying_before = weth.balanceOf(vault)

weth_in = 123
min_usdc_out = 150 * 10**6
weth_in = 3 * 10**18
min_usdc_out = 1500 * 10**6
vault.close_position(position_uid, min_usdc_out)

liquidity_after = usdc.balanceOf(vault)
Expand All @@ -71,16 +71,16 @@ def test_close_position_in_profit_increases_margin(vault, weth, usdc, owner):

position = vault.positions(position_uid)
position_margin = position[3]
assert position_margin == 15 * 10**6
assert position_margin == 150 * 10**6

min_usdc_out = (
250 * 10**6
1750 * 10**6
) # simulated profit of 100 in addition to initial (margin + borrowed)
vault.close_position(position_uid, min_usdc_out)

margin_after = vault.margin(owner, usdc)

expected_pnl = 85 * 10**6
expected_pnl = 100 * 10**6
assert (
margin_after == account_margin_after_trade_open + position_margin + expected_pnl
)
Expand All @@ -93,9 +93,9 @@ def test_close_position_in_loss_reduces_margin(vault, usdc, weth, owner):
position = vault.positions(position_uid)
position_margin = position[3]

# position-supply = debt + margin = 150 + 15
# position-supply = debt + margin = 1500 + 150
expected_loss = 10 * 10**6
min_usdc_out = 165 * 10**6 - expected_loss # simulated loss of 10
min_usdc_out = 1650 * 10**6 - expected_loss # simulated loss of 10
vault.close_position(position_uid, min_usdc_out)

margin_after = vault.margin(owner, usdc)
Expand Down Expand Up @@ -144,24 +144,25 @@ def test_close_position_in_bad_debt_records_bad_debt(vault, usdc, weth, owner):
assert bad_debt_before == 0

vault.positions(position_uid)
vault.close_position(position_uid, 0) # 0 out
vault.internal._close_position(position_uid, 1) # 1 wei out
bad_debt_after = vault.bad_debt(usdc)

# TODO ensure position-debt is here the correct value
# we are minting new bad-debt shares and it is the first
# position, thus all share-positions should now be bad-debt
# that means all the 150 that where borrowed are now bad debt
assert bad_debt_after == 150 * 10**6
# that means all the 1500 that where borrowed are now bad debt
assert bad_debt_after == 1500 * 10**6 - 1


def test_close_position_in_bad_debt_deactivates_accepting_new_orders(
vault, usdc, weth, owner
):
position_uid, _ = open_position(vault, owner, weth, usdc)
assert vault.is_accepting_new_orders()
assert vault.acceptable_amount_of_bad_debt(usdc) == 0
vault.positions(position_uid)

vault.close_position(position_uid, 0) # 0 out
vault.internal._close_position(position_uid, 1) # 0 out

bad_debt_after = vault.bad_debt(usdc)

Expand Down
20 changes: 10 additions & 10 deletions tests/vault/test_open_position.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ def test_open_trade_swaps_liquidity_for_underlying(vault, owner, weth, usdc):
liquidity_before = usdc.balanceOf(vault)
underlying_before = weth.balanceOf(vault)

margin_amount = 15 * 10**6
min_borrow_amount = 150 * 10**6
min_position_amount = 123
margin_amount = 150 * 10**6
min_borrow_amount = 1500 * 10**6
min_position_amount = 3 * 10**18
vault.open_position(
owner, weth, min_position_amount, usdc, min_borrow_amount, margin_amount
)
Expand All @@ -156,9 +156,9 @@ def test_open_trade_swaps_liquidity_for_underlying(vault, owner, weth, usdc):
def test_open_trade_reduces_free_margin_correctly(vault, owner, usdc, weth, alice):
margin_before = vault.margin(owner, usdc)

margin_amount = 15 * 10**6
min_borrow_amount = 150 * 10**6
min_position_amount = 123
margin_amount = 150 * 10**6
min_borrow_amount = 1500 * 10**6
min_position_amount = 3 * 10**18
vault.open_position(
owner, weth, min_position_amount, usdc, min_borrow_amount, margin_amount
)
Expand All @@ -172,9 +172,9 @@ def test_open_trade_reduces_free_margin_correctly(vault, owner, usdc, weth, alic
def test_open_position_records_amount_lent_out_correctly(vault, owner, usdc, weth):
debt_before = vault.total_debt_amount(usdc)

margin_amount = 15 * 10**6
min_borrow_amount = 150 * 10**6
min_position_amount = 123
margin_amount = 150 * 10**6
min_borrow_amount = 1500 * 10**6
min_position_amount = 3 * 10**18
vault.open_position(
owner, weth, min_position_amount, usdc, min_borrow_amount, margin_amount
)
Expand Down Expand Up @@ -204,7 +204,7 @@ def test_cannot_open_trade_with_higher_than_max_leverage(vault, owner, weth, usd
min_borrow_amount = margin_amount * max_leverage + 1
min_position_amount = 123

with boa.reverts("too much leverage"):
with boa.reverts("cannot open liquidatable position"):
vault.open_position(
owner, weth, min_position_amount, usdc, min_borrow_amount, margin_amount
)
Expand Down

0 comments on commit 9582844

Please sign in to comment.