Skip to content

Commit 032c578

Browse files
Split liquidity tests
1 parent 56510d8 commit 032c578

File tree

8 files changed

+433
-416
lines changed

8 files changed

+433
-416
lines changed

.github/workflows/ci.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ jobs:
3030
- gauge
3131
- pools/exchange
3232
- pools/general
33+
- pools/liquidity
3334
- pools/meta
3435
- pools/oracle
3536
- factory
@@ -59,4 +60,4 @@ jobs:
5960
WEB3_PROVIDER_URL: ${{ secrets.WEB3_PROVIDER_URL }}
6061
run: |
6162
source .venv/bin/activate
62-
pytest -n auto tests/${{ matrix.name }}/
63+
pytest --exitfirst --numprocesses=auto tests/${{ matrix.name }}/

tests/pools/general/test_liquidity.py

Lines changed: 0 additions & 415 deletions
This file was deleted.

tests/pools/liquidity/__init__.py

Whitespace-only changes.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import boa
2+
import pytest
3+
4+
from tests.fixtures.constants import INITIAL_AMOUNT
5+
from tests.utils.transactions import call_returning_result_and_logs
6+
7+
pytestmark = pytest.mark.usefixtures("initial_setup")
8+
9+
10+
def test_add_liquidity(
11+
bob,
12+
swap,
13+
pool_type,
14+
pool_tokens,
15+
underlying_tokens,
16+
deposit_amounts,
17+
initial_amounts,
18+
pool_token_types,
19+
metapool_token_type,
20+
):
21+
swap.add_liquidity(deposit_amounts, 0, sender=bob)
22+
is_ideal = True
23+
24+
if pool_type == 0:
25+
for i, (pool_token, amount) in enumerate(zip(pool_tokens, deposit_amounts)):
26+
if pool_token_types[i] == 2:
27+
is_ideal = False
28+
assert pool_token.balanceOf(bob) >= initial_amounts[i] - deposit_amounts[i]
29+
assert pool_token.balanceOf(swap.address) >= deposit_amounts[i] * 2
30+
else:
31+
assert pool_token.balanceOf(bob) == initial_amounts[i] - deposit_amounts[i]
32+
assert pool_token.balanceOf(swap.address) == deposit_amounts[i] * 2
33+
34+
if pool_token_types[i] == 1:
35+
is_ideal = False
36+
37+
ideal = len(pool_tokens) * INITIAL_AMOUNT // 2 * 10**18
38+
if is_ideal:
39+
assert abs(swap.balanceOf(bob) - ideal) <= 1
40+
assert abs(swap.totalSupply() - ideal * 2) <= 2
41+
else:
42+
if metapool_token_type == 2:
43+
assert underlying_tokens[0].balanceOf(bob) >= initial_amounts[0] - deposit_amounts[0]
44+
assert underlying_tokens[0].balanceOf(swap.address) >= deposit_amounts[0] * 2
45+
else:
46+
assert underlying_tokens[0].balanceOf(bob) == initial_amounts[0] - deposit_amounts[0]
47+
assert underlying_tokens[0].balanceOf(swap.address) == deposit_amounts[0] * 2
48+
49+
if metapool_token_type == 0:
50+
ideal = INITIAL_AMOUNT * 10**18 # // 2 * 2
51+
assert abs(swap.balanceOf(bob) - ideal) <= 1
52+
assert abs(swap.totalSupply() - ideal * 2) <= 2
53+
54+
assert underlying_tokens[1].balanceOf(bob) == initial_amounts[1] - deposit_amounts[1]
55+
assert underlying_tokens[1].balanceOf(swap) == deposit_amounts[1] * 2
56+
57+
58+
@pytest.mark.parametrize("idx", (0, 1))
59+
def test_add_one_coin(
60+
bob,
61+
swap,
62+
pool_type,
63+
pool_tokens,
64+
underlying_tokens,
65+
deposit_amounts,
66+
initial_amounts,
67+
pool_token_types,
68+
metapool_token_type,
69+
idx,
70+
):
71+
amounts = [0] * len(pool_tokens)
72+
amounts[idx] = deposit_amounts[idx]
73+
74+
swap.add_liquidity(amounts, 0, sender=bob)
75+
is_ideal = True
76+
77+
if pool_type == 0:
78+
for i, pool_token in enumerate(pool_tokens):
79+
if pool_token_types[i] == 2:
80+
is_ideal = False
81+
assert pool_token.balanceOf(bob) >= initial_amounts[i] - amounts[i] - 1
82+
assert pool_token.balanceOf(swap.address) >= deposit_amounts[i] + amounts[i] - 1
83+
else:
84+
assert pool_token.balanceOf(bob) == initial_amounts[i] - amounts[i]
85+
assert pool_token.balanceOf(swap.address) == deposit_amounts[i] + amounts[i]
86+
else:
87+
if metapool_token_type == 2:
88+
is_ideal = False
89+
assert underlying_tokens[0].balanceOf(bob) >= initial_amounts[0] - amounts[0] - 1
90+
assert underlying_tokens[0].balanceOf(swap.address) >= deposit_amounts[0] + amounts[0] - 1
91+
else:
92+
assert underlying_tokens[0].balanceOf(bob) == initial_amounts[0] - amounts[0]
93+
assert underlying_tokens[0].balanceOf(swap) == deposit_amounts[0] + amounts[0]
94+
95+
assert underlying_tokens[1].balanceOf(bob) == initial_amounts[1] - amounts[1]
96+
assert underlying_tokens[1].balanceOf(swap) == deposit_amounts[1] + amounts[1]
97+
98+
difference = abs(swap.balanceOf(bob) - deposit_amounts[idx])
99+
if is_ideal:
100+
assert difference / (deposit_amounts[idx]) < 0.01
101+
else:
102+
assert difference / (deposit_amounts[idx]) < 0.02
103+
104+
105+
def test_insufficient_balance(charlie, swap, pool_type, decimals, meta_decimals):
106+
if pool_type == 0:
107+
amounts = [(10**i) for i in decimals]
108+
else:
109+
amounts = [(10**i) for i in [meta_decimals, 18]]
110+
111+
with boa.reverts(): # invalid approval or balance
112+
swap.add_liquidity(amounts, 0, sender=charlie)
113+
114+
115+
def test_min_amount_too_high(bob, swap, pool_type, deposit_amounts, pool_tokens):
116+
size = 2
117+
if pool_type == 0:
118+
size = len(pool_tokens)
119+
120+
with boa.reverts():
121+
swap.add_liquidity(deposit_amounts, size * INITIAL_AMOUNT // 2 * 10**18 * 101 // 100, sender=bob)
122+
123+
124+
def test_event(bob, swap, pool_type, deposit_amounts, pool_tokens, pool_token_types, metapool_token_type):
125+
size = 2
126+
check_invariant = True
127+
if pool_type == 0:
128+
size = len(pool_tokens)
129+
130+
for t in pool_token_types:
131+
if t != 0:
132+
check_invariant = False
133+
134+
if pool_type == 1:
135+
if metapool_token_type != 0:
136+
check_invariant = False
137+
138+
_, events = call_returning_result_and_logs(swap, "add_liquidity", deposit_amounts, 0, sender=bob)
139+
140+
assert len(events) == 4 # Transfer token1, Transfer token2, Transfer LP, Add liquidity
141+
if check_invariant:
142+
assert (
143+
repr(events[3]) == f"AddLiquidity(provider={bob}, token_amounts={deposit_amounts}, fees=[0, 0], "
144+
f"invariant={size * INITIAL_AMOUNT * 10 ** 18}, token_supply={swap.totalSupply()})"
145+
)
146+
147+
148+
def test_send_eth(bob, swap, deposit_amounts):
149+
with boa.reverts():
150+
swap.add_liquidity(deposit_amounts, 0, sender=bob, value=1)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import boa
2+
import pytest
3+
4+
from tests.fixtures.accounts import add_base_pool_liquidity, mint_account
5+
from tests.utils.tokens import mint_for_testing
6+
7+
8+
@pytest.fixture(scope="module")
9+
def initial_setup_alice(
10+
alice,
11+
deposit_amounts,
12+
swap,
13+
pool_type,
14+
base_pool,
15+
base_pool_tokens,
16+
base_pool_decimals,
17+
base_pool_lp_token,
18+
initial_balance,
19+
initial_amounts,
20+
pool_tokens,
21+
underlying_tokens,
22+
):
23+
with boa.env.anchor():
24+
mint_for_testing(alice, 1 * 10**18, None, True)
25+
26+
if pool_type == 0:
27+
mint_account(alice, pool_tokens, initial_balance, initial_amounts)
28+
with boa.env.prank(alice):
29+
for token in pool_tokens:
30+
token.approve(swap.address, 2**256 - 1)
31+
32+
else:
33+
add_base_pool_liquidity(alice, base_pool, base_pool_tokens, base_pool_decimals)
34+
mint_for_testing(alice, initial_amounts[0], underlying_tokens[0], False)
35+
36+
with boa.env.prank(alice):
37+
for token in underlying_tokens:
38+
token.approve(swap.address, 2**256 - 1)
39+
40+
yield
41+
42+
43+
@pytest.mark.parametrize("min_amount", [0, 10**18])
44+
def test_initial(
45+
alice,
46+
initial_setup_alice,
47+
swap,
48+
pool_type,
49+
pool_tokens,
50+
underlying_tokens,
51+
pool_token_types,
52+
metapool_token_type,
53+
min_amount,
54+
decimals,
55+
meta_decimals,
56+
deposit_amounts,
57+
initial_amounts,
58+
):
59+
swap.add_liquidity(deposit_amounts, len(pool_tokens) * min_amount, sender=alice)
60+
61+
token_types = pool_token_types if pool_type == 0 else [metapool_token_type, 18]
62+
63+
for coin, und_coin, amount, initial, pool_token_type in zip(
64+
pool_tokens, underlying_tokens, deposit_amounts, initial_amounts, token_types
65+
):
66+
if pool_type == 0:
67+
assert coin.balanceOf(alice) == pytest.approx(initial - amount, rel=1.5e-2)
68+
assert coin.balanceOf(swap) == pytest.approx(amount, rel=1.5e-2)
69+
else:
70+
assert und_coin.balanceOf(alice) == pytest.approx(initial - amount, rel=1.5e-2)
71+
assert und_coin.balanceOf(swap) == pytest.approx(amount, rel=1.5e-2)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import boa
2+
import pytest
3+
4+
from tests.utils.transactions import call_returning_result_and_logs
5+
6+
pytestmark = pytest.mark.usefixtures("initial_setup")
7+
8+
9+
@pytest.mark.parametrize("min_amount", (0, 1))
10+
def test_remove_liquidity(alice, swap, pool_type, pool_tokens, underlying_tokens, min_amount, deposit_amounts):
11+
swap.remove_liquidity(swap.balanceOf(alice), [i * min_amount for i in deposit_amounts], sender=alice)
12+
13+
coins = pool_tokens if pool_type == 0 else underlying_tokens[:2]
14+
15+
for coin, amount in zip(coins, deposit_amounts):
16+
assert coin.balanceOf(alice) == pytest.approx(amount * 2, rel=1.5e-2)
17+
assert coin.balanceOf(swap) == 0
18+
19+
assert swap.balanceOf(alice) == 0
20+
assert swap.totalSupply() == 0
21+
22+
23+
def test_remove_partial(alice, swap, pool_type, pool_tokens, underlying_tokens, pool_size):
24+
initial_amount = swap.balanceOf(alice)
25+
withdraw_amount = initial_amount // 2
26+
coins = pool_tokens if pool_type == 0 else underlying_tokens[:2]
27+
swap.remove_liquidity(withdraw_amount, [0] * pool_size, sender=alice)
28+
29+
for coin in coins:
30+
assert coin.balanceOf(swap) + coin.balanceOf(alice) == pytest.approx(initial_amount, rel=1.5e-2)
31+
32+
assert swap.balanceOf(alice) == initial_amount - withdraw_amount
33+
assert swap.totalSupply() == initial_amount - withdraw_amount
34+
35+
36+
@pytest.mark.parametrize("idx", range(2))
37+
def test_below_min_amount(alice, swap, initial_amounts, idx):
38+
min_amount = initial_amounts.copy()
39+
min_amount[idx] += 1
40+
41+
with boa.reverts():
42+
swap.remove_liquidity(swap.balanceOf(alice), min_amount, sender=alice)
43+
44+
45+
def test_amount_exceeds_balance(alice, swap, pool_size):
46+
with boa.reverts():
47+
swap.remove_liquidity(swap.balanceOf(alice) + 1, [0] * pool_size, sender=alice)
48+
49+
50+
def test_event(alice, bob, swap, pool_size):
51+
swap.transfer(bob, 10**18, sender=alice)
52+
_, events = call_returning_result_and_logs(swap, "remove_liquidity", 10**18, [0] * pool_size, sender=alice)
53+
54+
assert f"RemoveLiquidity(provider={alice}" in repr(events[3])
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import boa
2+
import pytest
3+
4+
from tests.utils.transactions import call_returning_result_and_logs
5+
6+
pytestmark = pytest.mark.usefixtures("initial_setup")
7+
8+
9+
@pytest.mark.parametrize("divisor", [2, 5, 10])
10+
def test_remove_balanced(
11+
alice, swap, pool_type, pool_tokens, underlying_tokens, divisor, deposit_amounts, initial_amounts
12+
):
13+
initial_balance = swap.balanceOf(alice)
14+
amounts = [i // divisor for i in deposit_amounts]
15+
swap.remove_liquidity_imbalance(amounts, initial_balance, sender=alice)
16+
17+
coins = pool_tokens if pool_type == 0 else underlying_tokens[:2]
18+
19+
for i, coin in enumerate(coins):
20+
assert coin.balanceOf(alice) == pytest.approx(amounts[i] + initial_amounts[i] - deposit_amounts[i], rel=1.5e-2)
21+
assert coin.balanceOf(swap) == pytest.approx(deposit_amounts[i] - amounts[i], rel=1.5e-2)
22+
23+
assert swap.balanceOf(alice) / initial_balance == pytest.approx(1 - 1 / divisor, rel=1.5e-2)
24+
25+
26+
@pytest.mark.parametrize("idx", range(2))
27+
def test_remove_one(
28+
alice, swap, pool_type, pool_tokens, underlying_tokens, pool_size, idx, deposit_amounts, initial_amounts
29+
):
30+
amounts = [0] * pool_size
31+
amounts[idx] = deposit_amounts[idx] // 2
32+
33+
lp_balance = pool_size * deposit_amounts[idx]
34+
swap.remove_liquidity_imbalance(amounts, lp_balance, sender=alice)
35+
36+
coins = pool_tokens if pool_type == 0 else underlying_tokens[:2]
37+
38+
for i, coin in enumerate(coins):
39+
assert coin.balanceOf(alice) == pytest.approx(amounts[i] + initial_amounts[i] - deposit_amounts[i], rel=1.5e-2)
40+
assert coin.balanceOf(swap) == pytest.approx(deposit_amounts[i] - amounts[i], rel=1.5e-2)
41+
42+
actual_balance = swap.balanceOf(alice)
43+
actual_total_supply = swap.totalSupply()
44+
ideal_balance = (2 * pool_size - 1) * lp_balance / (2 * pool_size)
45+
46+
assert actual_balance == actual_total_supply
47+
assert ideal_balance * 0.9994 < actual_balance
48+
assert actual_balance < ideal_balance * 1.07
49+
50+
51+
@pytest.mark.parametrize("divisor", [1, 2, 10])
52+
def test_exceed_max_burn(alice, swap, pool_size, divisor, deposit_amounts):
53+
amounts = [i // divisor for i in deposit_amounts]
54+
max_burn = pool_size * 1_000_000 * 10**18 // divisor
55+
56+
with boa.reverts():
57+
swap.remove_liquidity_imbalance(amounts, max_burn - 1, sender=alice)
58+
59+
60+
def test_cannot_remove_zero(alice, swap, pool_size):
61+
with boa.reverts():
62+
swap.remove_liquidity_imbalance([0] * pool_size, 0, sender=alice)
63+
64+
65+
def test_no_total_supply(alice, swap, pool_size):
66+
swap.remove_liquidity(swap.totalSupply(), [0] * pool_size, sender=alice)
67+
with boa.reverts():
68+
swap.remove_liquidity_imbalance([0] * pool_size, 0, sender=alice)
69+
70+
71+
def test_event(alice, bob, swap, pool_size, deposit_amounts):
72+
swap.transfer(bob, swap.balanceOf(alice), sender=alice)
73+
amounts = [i // 5 for i in deposit_amounts]
74+
max_burn = pool_size * 1_000_000 * 10**18
75+
76+
_, events = call_returning_result_and_logs(swap, "remove_liquidity_imbalance", amounts, max_burn, sender=bob)
77+
78+
assert f"RemoveLiquidityImbalance(provider={bob}" in repr(events[3])

0 commit comments

Comments
 (0)