feat: initial work on v3 by ErikBjare · Pull Request #106 · uniswap-python/uniswap-python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 67 additions & 10 deletions .github/workflows/test.yml
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ typecheck:
lint:
poetry run flake8

format:
black uniswap

format-abis:
npx prettier --write --parser=json uniswap/assets/*/*.abi

precommit:
make typecheck
make lint
Expand Down
226 changes: 111 additions & 115 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ web3 = "^5.12.0"
click = "^7.1.2"

[tool.poetry.dev-dependencies]
pytest = "^5.4.3"
mypy = "*"
black = "^19.10b0"
pytest = "^5.4.3"
pytest-cov = "^2.10.0"
pytest-dotenv = "^0.5.2"
python-dotenv = "^0.14.0"
flake8 = "^3.8.3"
mypy = "^0.782"
Sphinx = "^3.5.4"
sphinx-book-theme = "^0.1.0"
sphinx-click = "^2.7.1"
Expand Down
23 changes: 17 additions & 6 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
import json
import sys

from click.testing import CliRunner

from uniswap.cli import main


def print_result(result):
print(result)
print(result.stdout.strip())
print(result.stderr.strip(), file=sys.stderr)


def test_get_price():
runner = CliRunner()
runner = CliRunner(mix_stderr=False)
result = runner.invoke(main, ["price", "weth", "dai"])
print_result(result)
assert result.exit_code == 0

# Will break when ETH breaks 10k
assert 1000 < float(result.output) < 10_000
assert 1000 < float(result.stdout) < 10_000

result = runner.invoke(main, ["price", "wbtc", "dai"])
assert result.exit_code == 0

# Will break when BTC breaks 100k
assert 10_000 < float(result.output) < 100_000
assert 10_000 < float(result.stdout) < 100_000


def test_get_token():
runner = CliRunner()
runner = CliRunner(mix_stderr=False)
result = runner.invoke(main, ["token", "weth"])
print_result(result)
assert result.exit_code == 0
out = json.loads(result.output.replace("'", '"'))

out = json.loads(result.stdout.replace("'", '"'))
assert out["symbol"] == "WETH"
assert out["decimals"] == 18


def test_get_tokendb():
runner = CliRunner()
runner = CliRunner(mix_stderr=False)
result = runner.invoke(main, ["tokendb", "--metadata"])
print_result(result)
assert result.exit_code == 0
92 changes: 63 additions & 29 deletions tests/test_uniswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import subprocess
import shutil
import logging
from typing import Generator
from contextlib import contextmanager
from dataclasses import dataclass
Expand All @@ -12,20 +13,44 @@
from uniswap import Uniswap, InvalidToken, InsufficientBalance


logger = logging.getLogger(__name__)

ENV_UNISWAP_VERSION = os.getenv("UNISWAP_VERSION", None)
if ENV_UNISWAP_VERSION:
UNISWAP_VERSIONS = [int(ENV_UNISWAP_VERSION)]
else:
UNISWAP_VERSIONS = [1, 2, 3]


@dataclass
class GanacheInstance:
provider: str
eth_address: str
eth_privkey: str


@pytest.fixture(scope="module", params=[1, 2])
@pytest.fixture(scope="module", params=UNISWAP_VERSIONS)
def client(request, web3: Web3, ganache: GanacheInstance):
uniswap = Uniswap(
return Uniswap(
ganache.eth_address, ganache.eth_privkey, web3=web3, version=request.param
)
uniswap._buy_test_assets()
return uniswap


@pytest.fixture(scope="module")
def test_assets(client: Uniswap):
"""
Buy some DAI and USDC to test with.
"""
tokens = client._get_token_addresses()

for token_name, amount in [("DAI", 100 * 10 ** 18), ("USDC", 100 * 10 ** 6)]:
token_addr = tokens[token_name]
price = client.get_eth_token_output_price(token_addr, amount)
logger.info(f"Cost of {amount} {token_name}: {price}")
logger.info("Buying...")

tx = client.make_trade_output(tokens["ETH"], token_addr, amount)
client.w3.eth.waitForTransactionReceipt(tx)


@pytest.fixture(scope="module")
Expand Down Expand Up @@ -78,6 +103,7 @@ class TestUniswap(object):
weth = Web3.toChecksumAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")
bat = Web3.toChecksumAddress("0x0D8775F648430679A709E98d2b0Cb6250d2887EF")
dai = Web3.toChecksumAddress("0x6b175474e89094c44da98b954eedeac495271d0f")
usdc = Web3.toChecksumAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")

# For Rinkeby
# eth = "0x0000000000000000000000000000000000000000"
Expand All @@ -86,10 +112,14 @@ class TestUniswap(object):

# ------ Exchange ------------------------------------------------------------------
def test_get_fee_maker(self, client: Uniswap):
if client.version not in [1, 2]:
pytest.skip("Tested method not supported in this Uniswap version")
r = client.get_fee_maker()
assert r == 0

def test_get_fee_taker(self, client: Uniswap):
if client.version not in [1, 2]:
pytest.skip("Tested method not supported in this Uniswap version")
r = client.get_fee_taker()
assert r == 0.003

Expand Down Expand Up @@ -121,19 +151,21 @@ def test_get_token_eth_input_price(self, client, token, qty):
assert r

@pytest.mark.parametrize(
"token0, token1, qty",
"token0, token1, qty, kwargs",
[
(bat, dai, ONE_ETH),
(dai, bat, ONE_ETH),
(bat, dai, 2 * ONE_ETH),
(weth, dai, ONE_ETH),
(dai, weth, ONE_ETH),
# BAT/DAI has no liquidity in V3
# (bat, dai, ONE_ETH),
# (dai, bat, ONE_ETH),
# (bat, dai, 2 * ONE_ETH),
(dai, usdc, ONE_ETH, {"fee": 500}),
(weth, dai, ONE_ETH, {}),
(dai, weth, ONE_ETH, {}),
],
)
def test_get_token_token_input_price(self, client, token0, token1, qty):
if not client.version == 2:
pytest.skip("Tested method only supported on Uniswap v2")
r = client.get_token_token_input_price(token0, token1, qty)
def test_get_token_token_input_price(self, client, token0, token1, qty, kwargs):
if client.version not in [2, 3]:
pytest.skip("Tested method not supported in this Uniswap version")
r = client.get_token_token_input_price(token0, token1, qty, **kwargs)
assert r

@pytest.mark.parametrize(
Expand Down Expand Up @@ -163,19 +195,19 @@ def test_get_token_eth_output_price(self, client, token, qty):
assert r

@pytest.mark.parametrize(
"token0, token1, qty",
"token0, token1, qty, kwargs",
[
(bat, dai, ONE_ETH),
(dai, bat, ONE_ETH),
(bat, dai, 2 * ONE_ETH),
(weth, dai, ONE_ETH),
(dai, weth, ONE_ETH),
# (bat, dai, ONE_ETH),
(dai, usdc, ONE_ETH, {"fee": 500}),
# (bat, dai, 2 * ONE_ETH),
(weth, dai, ONE_ETH, {}),
(dai, weth, ONE_ETH, {}),
],
)
def test_get_token_token_output_price(self, client, token0, token1, qty):
if not client.version == 2:
pytest.skip("Tested method only supported on Uniswap v2")
r = client.get_token_token_output_price(token0, token1, qty)
def test_get_token_token_output_price(self, client, token0, token1, qty, kwargs):
if client.version not in [2, 3]:
pytest.skip("Tested method not supported in this Uniswap version")
r = client.get_token_token_output_price(token0, token1, qty, **kwargs)
assert r

# ------ ERC20 Pool ----------------------------------------------------------------
Expand Down Expand Up @@ -241,11 +273,11 @@ def test_remove_liquidity(
"input_token, output_token, qty, recipient, expectation",
[
# ETH -> Token
(eth, bat, 1_000_000_000 * ONE_WEI, None, does_not_raise),
(eth, dai, 1_000_000_000 * ONE_WEI, None, does_not_raise),
# Token -> Token
(bat, dai, 1_000_000_000 * ONE_WEI, None, does_not_raise),
(dai, usdc, 1_000_000_000 * ONE_WEI, None, does_not_raise),
# Token -> ETH
(bat, eth, 1_000_000 * ONE_WEI, None, does_not_raise),
(usdc, eth, 1_000_000 * ONE_WEI, None, does_not_raise),
# (eth, bat, 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
# (bat, eth, 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
# (dai, bat, 0.00001 * ONE_ETH, ZERO_ADDRESS, does_not_raise),
Expand All @@ -256,6 +288,7 @@ def test_make_trade(
self,
client: Uniswap,
web3: Web3,
test_assets,
input_token,
output_token,
qty: int,
Expand All @@ -278,9 +311,9 @@ def test_make_trade(
"input_token, output_token, qty, recipient, expectation",
[
# ETH -> Token
(eth, bat, 1_000_000_000 * ONE_WEI, None, does_not_raise),
(eth, dai, 1_000_000 * ONE_WEI, None, does_not_raise),
# Token -> Token
(bat, dai, 1_000_000_000 * ONE_WEI, None, does_not_raise),
(dai, usdc, 1_000_000 * ONE_WEI, None, does_not_raise),
# Token -> ETH
(dai, eth, 1_000_000 * ONE_WEI, None, does_not_raise),
# FIXME: These should probably be uncommented eventually
Expand All @@ -301,6 +334,7 @@ def test_make_trade_output(
self,
client: Uniswap,
web3: Web3,
test_assets,
input_token,
output_token,
qty: int,
Expand Down
2 changes: 1 addition & 1 deletion uniswap/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .uniswap import Uniswap, InvalidToken, InsufficientBalance
from .uniswap import Uniswap, InvalidToken, InsufficientBalance, _str_to_addr
from .cli import main
2 changes: 1 addition & 1 deletion uniswap/assets/uniswap-v1/exchange.abi
Loading