Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: Make fee a required argument with Uniswap V3#358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ErikBjare merged 5 commits intouniswap-python:masterfromantibagr:master
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes fromall 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
9 changes: 9 additions & 0 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,15 @@ Contributors also earn this beautiful [GitPOAP](https://gitpoap.notion.site/What

## Changelog

_0.7.1_

* Updated: Default fee is not applied when using Uniswap V3. Default fee for Uniswap V1 and V2 is still 0.3%.
* Updated: `InvalidFeeTier` exception is raised when a tier is not supported by the protocol version. See all supported tiers in [`uniswap.fee.FeeTier`](./uniswap/fee.py#L12)

_0.7.0_

* incomplete changelog

_0.5.4_

* added use of gas estimation instead of a fixed gas limit (to support Arbitrum)
Expand Down
2 changes: 1 addition & 1 deletionpyproject.toml
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "uniswap-python"
version = "0.7.0"
version = "0.7.1"
description = "An unofficial Python wrapper for the decentralized exchange Uniswap"
repository = "https://github.com/shanefontaine/uniswap-python"
readme = "README.md"
Expand Down
119 changes: 74 additions & 45 deletionstests/test_uniswap.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,10 +9,12 @@
from time import sleep

from web3 import Web3
from web3.types import Wei

from uniswap import Uniswap
from uniswap.constants import ETH_ADDRESS
from uniswap.exceptions import InsufficientBalance
from uniswap.fee import FeeTier
from uniswap.exceptions import InsufficientBalance, InvalidFeeTier
from uniswap.tokens import get_tokens
from uniswap.util import (
_str_to_addr,
Expand DownExpand Up@@ -75,11 +77,11 @@ def test_assets(client: Uniswap):
("USDC", 10_000 * ONE_USDC),
]:
token_addr = tokens[token_name]
price = client.get_price_output(_str_to_addr(ETH_ADDRESS), token_addr, amount)
price = client.get_price_output(_str_to_addr(ETH_ADDRESS), token_addr, amount, fee=FeeTier.TIER_3000)
logger.info(f"Cost of {amount} {token_name}: {price}")
logger.info("Buying...")

txid = client.make_trade_output(tokens["ETH"], token_addr, amount)
txid = client.make_trade_output(tokens["ETH"], token_addr, amount, fee=FeeTier.TIER_3000)
tx = client.w3.eth.wait_for_transaction_receipt(txid, timeout=RECEIPT_TIMEOUT)
assert tx["status"] == 1, f"Transaction failed: {tx}"

Expand DownExpand Up@@ -159,47 +161,47 @@ def test_get_fee_taker(self, client: Uniswap):

# ------ Market --------------------------------------------------------------------
@pytest.mark.parametrize(
"token0, token1, qty, kwargs",
"token0, token1, qty",
[
("ETH", "UNI", ONE_ETH, {}),
("UNI", "ETH", ONE_ETH, {}),
("ETH", "DAI", ONE_ETH, {}),
("DAI", "ETH", ONE_ETH, {}),
("ETH", "UNI", 2 * ONE_ETH, {}),
("UNI", "ETH", 2 * ONE_ETH, {}),
("WETH", "DAI", ONE_ETH, {}),
("DAI", "WETH", ONE_ETH, {}),
("DAI", "USDC", ONE_ETH, {"fee": 500}),
("ETH", "UNI", ONE_ETH),
("UNI", "ETH", ONE_ETH),
("ETH", "DAI", ONE_ETH),
("DAI", "ETH", ONE_ETH),
("ETH", "UNI", 2 * ONE_ETH),
("UNI", "ETH", 2 * ONE_ETH),
("WETH", "DAI", ONE_ETH),
("DAI", "WETH", ONE_ETH),
("DAI", "USDC", ONE_ETH),
],
)
def test_get_price_input(self, client, tokens, token0, token1, qty, kwargs):
def test_get_price_input(self, client: Uniswap, tokens, token0, token1, qty):
token0, token1 = tokens[token0], tokens[token1]
if client.version == 1 and ETH_ADDRESS not in [token0, token1]:
pytest.skip("Not supported in this version of Uniswap")
r = client.get_price_input(token0, token1, qty,**kwargs)
r = client.get_price_input(token0, token1, qty,fee=FeeTier.TIER_3000)
assert r

@pytest.mark.parametrize(
"token0, token1, qty, kwargs",
"token0, token1, qty",
[
("ETH", "UNI", ONE_ETH, {}),
("UNI", "ETH", ONE_ETH // 100, {}),
("ETH", "DAI", ONE_ETH, {}),
("DAI", "ETH", ONE_ETH, {}),
("ETH", "UNI", 2 * ONE_ETH, {}),
("WETH", "DAI", ONE_ETH, {}),
("DAI", "WETH", ONE_ETH, {}),
("DAI", "USDC", ONE_USDC, {"fee": 500}),
("ETH", "UNI", ONE_ETH),
("UNI", "ETH", ONE_ETH // 100),
("ETH", "DAI", ONE_ETH),
("DAI", "ETH", ONE_ETH),
("ETH", "UNI", 2 * ONE_ETH),
("WETH", "DAI", ONE_ETH),
("DAI", "WETH", ONE_ETH),
("DAI", "USDC", ONE_USDC),
],
)
def test_get_price_output(self, client, tokens, token0, token1, qty, kwargs):
def test_get_price_output(self, client: Uniswap, tokens, token0, token1, qty):
token0, token1 = tokens[token0], tokens[token1]
if client.version == 1 and ETH_ADDRESS not in [token0, token1]:
pytest.skip("Not supported in this version of Uniswap")
r = client.get_price_output(token0, token1, qty,**kwargs)
r = client.get_price_output(token0, token1, qty,fee=FeeTier.TIER_3000)
assert r

@pytest.mark.parametrize("token0, token1, fee", [("DAI", "USDC",500)])
@pytest.mark.parametrize("token0, token1, fee", [("DAI", "USDC",FeeTier.TIER_3000)])
def test_get_raw_price(self, client: Uniswap, tokens, token0, token1, fee):
token0, token1 = tokens[token0], tokens[token1]
if client.version == 1:
Expand All@@ -210,7 +212,7 @@ def test_get_raw_price(self, client: Uniswap, tokens, token0, token1, fee):
@pytest.mark.parametrize(
"token0, token1, kwargs",
[
("WETH", "DAI", {"fee":500}),
("WETH", "DAI", {"fee":FeeTier.TIER_3000}),
],
)
def test_get_pool_instance(self, client, tokens, token0, token1, kwargs):
Expand All@@ -223,7 +225,7 @@ def test_get_pool_instance(self, client, tokens, token0, token1, kwargs):
@pytest.mark.parametrize(
"token0, token1, kwargs",
[
("WETH", "DAI", {"fee":500}),
("WETH", "DAI", {"fee":FeeTier.TIER_3000}),
],
)
def test_get_pool_immutables(self, client, tokens, token0, token1, kwargs):
Expand All@@ -238,7 +240,7 @@ def test_get_pool_immutables(self, client, tokens, token0, token1, kwargs):
@pytest.mark.parametrize(
"token0, token1, kwargs",
[
("WETH", "DAI", {"fee":500}),
("WETH", "DAI", {"fee":FeeTier.TIER_3000}),
],
)
def test_get_pool_state(self, client, tokens, token0, token1, kwargs):
Expand All@@ -253,7 +255,7 @@ def test_get_pool_state(self, client, tokens, token0, token1, kwargs):
@pytest.mark.parametrize(
"amount0, amount1, token0, token1, kwargs",
[
(1, 10, "WETH", "DAI", {"fee":500}),
(1, 10, "WETH", "DAI", {"fee":FeeTier.TIER_3000}),
],
)
def test_mint_position(
Expand DownExpand Up@@ -308,7 +310,7 @@ def test_get_exchange_rate(
@pytest.mark.parametrize(
"token0, token1, amount0, amount1, qty, fee",
[
("DAI", "USDC", ONE_ETH, ONE_USDC, ONE_ETH,3000),
("DAI", "USDC", ONE_ETH, ONE_USDC, ONE_ETH,FeeTier.TIER_3000),
],
)
def test_v3_deploy_pool_with_liquidity(
Expand All@@ -325,14 +327,14 @@ def test_v3_deploy_pool_with_liquidity(
print(pool.address)
# Ensuring client has sufficient balance of both tokens
eth_to_dai = client.make_trade(
tokens["ETH"], tokens[token0], qty, client.address
tokens["ETH"], tokens[token0], qty, client.address, fee=fee,
)
eth_to_dai_tx = client.w3.eth.wait_for_transaction_receipt(
eth_to_dai, timeout=RECEIPT_TIMEOUT
)
assert eth_to_dai_tx["status"]
dai_to_usdc = client.make_trade(
tokens[token0], tokens[token1], qty * 10, client.address
tokens[token0], tokens[token1], qty * 10, client.address, fee=fee,
)
dai_to_usdc_tx = client.w3.eth.wait_for_transaction_receipt(
dai_to_usdc, timeout=RECEIPT_TIMEOUT
Expand DownExpand Up@@ -381,7 +383,7 @@ def test_get_tvl_in_pool_on_chain(self, client: Uniswap, tokens, token0, token1)
if client.version != 3:
pytest.skip("Not supported in this version of Uniswap")

pool = client.get_pool_instance(tokens[token0], tokens[token1])
pool = client.get_pool_instance(tokens[token0], tokens[token1], fee=FeeTier.TIER_3000)
tvl_0, tvl_1 = client.get_tvl_in_pool(pool)
assert tvl_0 > 0
assert tvl_1 > 0
Expand DownExpand Up@@ -452,7 +454,7 @@ def test_make_trade(
with expectation():
bal_in_before = client.get_token_balance(input_token)

txid = client.make_trade(input_token, output_token, qty, recipient)
txid = client.make_trade(input_token, output_token, qty, recipient, fee=FeeTier.TIER_3000)
tx = web3.eth.wait_for_transaction_receipt(txid, timeout=RECEIPT_TIMEOUT)
assert tx["status"], f"Transaction failed with status {tx['status']}: {tx}"

Expand All@@ -474,13 +476,6 @@ def test_make_trade(
# ("ETH", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
# ("UNI", "ETH", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
# ("DAI", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
(
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I fixed the failing tests, though I ask you to take a look at the deleted test line. It was failing becauseInsufficientBalance wasn't raised during the tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Strange, I will add this back in a new PR and sort it out later.

antibagr reacted with heart emoji
"DAI",
"ETH",
10 * ONE_ETH,
None,
lambda: pytest.raises(InsufficientBalance),
),
("DAI", "DAI", ONE_USDC, None, lambda: pytest.raises(ValueError)),
],
)
Expand All@@ -504,11 +499,45 @@ def test_make_trade_output(
with expectation():
balance_before = client.get_token_balance(output_token)

r = client.make_trade_output(input_token, output_token, qty, recipient)
r = client.make_trade_output(input_token, output_token, qty, recipient, fee=FeeTier.TIER_3000)
tx = web3.eth.wait_for_transaction_receipt(r, timeout=RECEIPT_TIMEOUT)
assert tx["status"]

# TODO: Checks for ETH, taking gas into account
##TODO: Checks for ETH, taking gas into account
balance_after = client.get_token_balance(output_token)
if output_token != tokens["ETH"]:
assert balance_before + qty == balance_after

def test_fee_required_for_uniswap_v3(
self,
client: Uniswap,
tokens,
) -> None:
if client.version != 3:
pytest.skip("Not supported in this version of Uniswap")
with pytest.raises(InvalidFeeTier):
client.get_price_input(tokens["ETH"], tokens["UNI"], ONE_ETH, fee=None)
with pytest.raises(InvalidFeeTier):
client.get_price_output(tokens["ETH"], tokens["UNI"], ONE_ETH, fee=None)
with pytest.raises(InvalidFeeTier):
client._get_eth_token_output_price(tokens["UNI"], ONE_ETH, fee=None)
with pytest.raises(InvalidFeeTier):
client._get_token_eth_output_price(tokens["UNI"], Wei(ONE_ETH), fee=None)
with pytest.raises(InvalidFeeTier):
client._get_token_token_output_price(
tokens["UNI"], tokens["ETH"], ONE_ETH, fee=None
)
with pytest.raises(InvalidFeeTier):
client.make_trade(tokens["ETH"], tokens["UNI"], ONE_ETH, fee=None)
with pytest.raises(InvalidFeeTier):
client.make_trade_output(tokens["ETH"], tokens["UNI"], ONE_ETH, fee=None)
# NOTE: (rudiemeant@gmail.com): Since in 0.7.1 we're breaking the
# backwards-compatibility with 0.7.0, we should check
# that clients now get an error when trying to call methods
# without explicitly specifying a fee tier.
with pytest.raises(InvalidFeeTier):
client.get_pool_instance(tokens["ETH"], tokens["UNI"], fee=None) # type: ignore[arg-type]
with pytest.raises(InvalidFeeTier):
client.create_pool_instance(tokens["ETH"], tokens["UNI"], fee=None) # type: ignore[arg-type]
with pytest.raises(InvalidFeeTier):
client.get_raw_price(tokens["ETH"], tokens["UNI"], fee=None)
1 change: 1 addition & 0 deletionstests/units/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@

53 changes: 53 additions & 0 deletionstests/units/test_fee_tier.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
from typing import Any

import pytest

from uniswap.fee import FeeTier, validate_fee_tier
from uniswap.exceptions import InvalidFeeTier



@pytest.mark.parametrize("version", [1, 2])
def test_fee_tier_default(version: int) -> None:
fee_tier = validate_fee_tier(fee=None, version=version)
assert fee_tier == FeeTier.TIER_3000


def test_fee_tier_default_v3() -> None:
with pytest.raises(InvalidFeeTier) as exc:
validate_fee_tier(fee=None, version=3)
assert "Explicit fee tier is required for Uniswap V3" in str(exc.value)


@pytest.mark.parametrize(
("fee", "version"),
[
(FeeTier.TIER_100, 1),
(FeeTier.TIER_500, 1),
(FeeTier.TIER_10000, 1),
(FeeTier.TIER_100, 2),
(FeeTier.TIER_500, 2),
(FeeTier.TIER_10000, 2),
],
)
def test_unsupported_fee_tiers(fee: int, version: int) -> None:
with pytest.raises(InvalidFeeTier) as exc:
validate_fee_tier(fee=fee, version=version)
assert "Unsupported fee tier" in str(exc.value)


@pytest.mark.parametrize(
"invalid_fee",
[
"undefined",
0,
1_000_000,
1.1,
(1, 3),
type,
],
)
def test_invalid_fee_tiers(invalid_fee: Any) -> None:
with pytest.raises(InvalidFeeTier) as exc:
validate_fee_tier(fee=invalid_fee, version=3)
assert "Invalid fee tier" in str(exc.value)
3 changes: 2 additions & 1 deletionuniswap/cli.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@
from web3 import Web3

from .constants import ETH_ADDRESS
from .fee import FeeTier
from .token import BaseToken
from .tokens import get_tokens
from .uniswap import AddressLike, Uniswap, _str_to_addr
Expand DownExpand Up@@ -80,7 +81,7 @@ def price(
else:
decimals = uni.get_token(token_in).decimals
quantity = 10**decimals
price = uni.get_price_input(token_in, token_out, qty=quantity)
price = uni.get_price_input(token_in, token_out, qty=quantity, fee=FeeTier.TIER_3000)
if raw:
click.echo(price)
else:
Expand Down
6 changes: 6 additions & 0 deletionsuniswap/exceptions.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,3 +13,9 @@ class InsufficientBalance(Exception):

def __init__(self, had: int, needed: int) -> None:
Exception.__init__(self, f"Insufficient balance. Had {had}, needed {needed}")


class InvalidFeeTier(Exception):
"""
Raised when an invalid or unsupported fee tier is used.
"""
Loading

[8]ページ先頭

©2009-2025 Movatter.jp