Skip to content

Commit 9edfbaa

Browse files
KeremPErikBjare
authored andcommitted
feat: implemented V3 features, like create pool, mint/close liquidity position, TVL calculation (#264)
implementing V3 features. added methods to fetch on-chain pool data feature: adding position miniting (WIP) WIP testing V3 pool position minting wip v3 features feat: create v3 pool and mint/add liquidity position fix: add 0x0 address assert in get_pool_instance feat: close v3 liquidity position minor cleanups add get_tvl_in_pool to return total value locked in pool implementing V3 features. added methods to fetch on-chain pool data feature: adding position miniting (WIP) WIP testing V3 pool position minting wip v3 features feat: create v3 pool and mint/add liquidity position feat: close v3 liquidity position minor cleanups fixing minor errors afer rebase cleanup asset amounts for tests fix: ensure asset amounts are correct for v3 liquidity position tests add tests for TVL calculations. include method for fetching TVL from V3 subgraph as on-chain method takes long to run add arbitrum subgraph endpoint fix typo skip tvl tests for now feat: get TVL on chain minor cleanups fix aribitrum multicall2 address. clean up TVL test update get_liquidity_positions to take arbitrary address param
1 parent 208843c commit 9edfbaa

File tree

7 files changed

+2288
-13
lines changed

7 files changed

+2288
-13
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.PHONY: test typecheck lint precommit docs
22

33
test:
4-
poetry run pytest -v --maxfail=10 --cov=uniswap --cov-report html --cov-report term --cov-report xml
4+
poetry run pytest -v --tb=line --maxfail=4 --cov=uniswap --cov-report html --cov-report term --cov-report xml
55

66
typecheck:
77
poetry run mypy --pretty
@@ -21,4 +21,4 @@ precommit:
2121
make test
2222

2323
docs:
24-
cd docs/ && make html
24+
cd docs/ && make html

tests/test_uniswap.py

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from async_timeout import timeout
12
import pytest
23
import os
34
import subprocess
@@ -11,11 +12,11 @@
1112
from web3 import Web3
1213
from web3.exceptions import NameNotFound
1314

14-
from uniswap import Uniswap
15-
from uniswap.constants import ETH_ADDRESS
15+
from uniswap import Uniswap, token
16+
from uniswap.constants import ETH_ADDRESS, WETH9_ADDRESS
1617
from uniswap.exceptions import InsufficientBalance
17-
from uniswap.util import _str_to_addr
1818
from uniswap.tokens import get_tokens
19+
from uniswap.util import _str_to_addr, default_tick_range, _addr_to_str, _load_contract_erc20
1920

2021

2122
logger = logging.getLogger(__name__)
@@ -193,6 +194,60 @@ def test_get_raw_price(self, client: Uniswap, tokens, token0, token1, fee):
193194
r = client.get_raw_price(token0, token1, fee=fee)
194195
assert r
195196

197+
@pytest.mark.parametrize(
198+
"token0, token1, kwargs",
199+
[
200+
(weth, dai, {"fee": 500}),
201+
]
202+
)
203+
def test_get_pool_instance(self, client, token0, token1, kwargs):
204+
if client.version != 3:
205+
pytest.skip("Not supported in this version of Uniswap")
206+
r = client.get_pool_instance(token0, token1, **kwargs)
207+
assert r
208+
209+
@pytest.mark.parametrize(
210+
"token0, token1, kwargs",
211+
[
212+
(weth, dai, {"fee": 500}),
213+
]
214+
)
215+
def test_get_pool_immutables(self, client, token0, token1, kwargs):
216+
if client.version != 3:
217+
pytest.skip("Not supported in this version of Uniswap")
218+
pool = client.get_pool_instance(token0, token1, **kwargs)
219+
r = client.get_pool_immutables(pool)
220+
print(r)
221+
assert r
222+
223+
@pytest.mark.parametrize(
224+
"token0, token1, kwargs",
225+
[
226+
(weth, dai, {"fee": 500}),
227+
]
228+
)
229+
def test_get_pool_state(self, client, token0, token1, kwargs):
230+
if client.version != 3:
231+
pytest.skip("Not supported in this version of Uniswap")
232+
pool = client.get_pool_instance(token0, token1, **kwargs)
233+
r = client.get_pool_state(pool)
234+
print(r)
235+
assert r
236+
237+
@pytest.mark.parametrize(
238+
"amount0, amount1, token0, token1, kwargs",
239+
[
240+
(1, 10, weth, dai, {"fee":500}),
241+
]
242+
)
243+
def test_mint_position(self, client, amount0, amount1, token0, token1, kwargs):
244+
if client.version != 3:
245+
pytest.skip("Not supported in this version of Uniswap")
246+
pool = client.get_pool_instance(token0, token1, **kwargs)
247+
r = client.mint_position(pool, amount0, amount1)
248+
print(r)
249+
assert r
250+
196251
# ------ ERC20 Pool ----------------------------------------------------------------
197252
@pytest.mark.parametrize("token", [("UNI"), ("DAI")])
198253
def test_get_ex_eth_balance(
@@ -231,6 +286,80 @@ def test_get_exchange_rate(
231286
assert r
232287

233288
# ------ Liquidity -----------------------------------------------------------------
289+
@pytest.mark.parametrize(
290+
"token0, token1, amount0, amount1, qty, fee",
291+
[
292+
('DAI', 'USDC', ONE_ETH, ONE_USDC, ONE_ETH, 3000),
293+
]
294+
)
295+
def test_v3_deploy_pool_with_liquidity(self, client: Uniswap, tokens, token0, token1, amount0, amount1, qty, fee):
296+
if client.version != 3:
297+
pytest.skip("Not supported in this version of Uniswap")
298+
299+
try:
300+
pool = client.create_pool_instance(tokens[token0], tokens[token1], fee)
301+
except Exception:
302+
pool = client.get_pool_instance(tokens[token0], tokens[token1], fee)
303+
304+
print(pool.address)
305+
# Ensuring client has sufficient balance of both tokens
306+
eth_to_dai = client.make_trade(tokens['ETH'], tokens[token0], qty, client.address)
307+
eth_to_dai_tx = client.w3.eth.wait_for_transaction_receipt(eth_to_dai, timeout=RECEIPT_TIMEOUT)
308+
assert eth_to_dai_tx["status"]
309+
dai_to_usdc = client.make_trade(tokens[token0], tokens[token1], qty*10, client.address)
310+
dai_to_usdc_tx = client.w3.eth.wait_for_transaction_receipt(dai_to_usdc, timeout=RECEIPT_TIMEOUT)
311+
assert dai_to_usdc_tx["status"]
312+
313+
balance_0 = client.get_token_balance(tokens[token0])
314+
balance_1 = client.get_token_balance(tokens[token1])
315+
316+
assert balance_0 > amount0, f'Have: {balance_0} need {amount0}'
317+
assert balance_1 > amount1, f'Have: {balance_1} need {amount1}'
318+
319+
320+
min_tick, max_tick = default_tick_range(fee)
321+
r = client.mint_liquidity(
322+
pool,
323+
amount0,
324+
amount1,
325+
tick_lower=min_tick,
326+
tick_upper=max_tick,
327+
deadline=2**64
328+
)
329+
assert r["status"]
330+
331+
position_balance = client.nonFungiblePositionManager.functions.balanceOf(_addr_to_str(client.address)).call()
332+
assert position_balance > 0
333+
334+
position_array = client.get_liquidity_positions()
335+
assert len(position_array) > 0
336+
337+
338+
@pytest.mark.parametrize(
339+
"deadline",
340+
[(2**64)],
341+
)
342+
def test_close_position(self, client: Uniswap, deadline):
343+
if client.version != 3:
344+
pytest.skip("Not supported in this version of Uniswap")
345+
position_array = client.get_liquidity_positions()
346+
tokenId = position_array[0]
347+
r = client.close_position(tokenId, deadline=deadline)
348+
assert r["status"]
349+
350+
@pytest.mark.parametrize(
351+
"token0, token1",
352+
[("DAI", "USDC")]
353+
)
354+
def test_get_tvl_in_pool_on_chain(self, client: Uniswap, tokens, token0, token1):
355+
if client.version != 3:
356+
pytest.skip("Not supported in this version of Uniswap")
357+
358+
pool = client.get_pool_instance(tokens[token0], tokens[token1])
359+
tvl_0, tvl_1 = client.get_tvl_in_pool(pool)
360+
assert tvl_0 > 0
361+
assert tvl_1 > 0
362+
234363
@pytest.mark.skip
235364
@pytest.mark.parametrize(
236365
"token, max_eth",
@@ -271,7 +400,7 @@ def test_remove_liquidity(
271400
# Token -> Token
272401
("DAI", "USDC", ONE_ETH, None, does_not_raise),
273402
# Token -> ETH
274-
("USDC", "ETH", 100 * ONE_USDC, None, does_not_raise),
403+
("USDC", "ETH", ONE_USDC, None, does_not_raise),
275404
# ("ETH", "UNI", 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
276405
# ("UNI", "ETH", 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
277406
# ("DAI", "UNI", 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
@@ -310,19 +439,19 @@ def test_make_trade(
310439
"input_token, output_token, qty, recipient, expectation",
311440
[
312441
# ETH -> Token
313-
("ETH", "DAI", 10 ** 18, None, does_not_raise),
442+
("ETH", "DAI", ONE_ETH, None, does_not_raise),
314443
# Token -> Token
315444
("DAI", "USDC", ONE_USDC, None, does_not_raise),
316445
# Token -> ETH
317-
("DAI", "ETH", 10 ** 16, None, does_not_raise),
446+
("DAI", "ETH", 100 * ONE_USDC, None, does_not_raise),
318447
# FIXME: These should probably be uncommented eventually
319448
# ("ETH", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
320449
# ("UNI", "ETH", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
321450
# ("DAI", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
322451
(
323452
"DAI",
324453
"ETH",
325-
10 * 10 ** 18,
454+
10 * ONE_ETH,
326455
None,
327456
lambda: pytest.raises(InsufficientBalance),
328457
),

0 commit comments

Comments
 (0)