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

Commitcd5d95e

Browse files
authored
fix: make fee a required argument for Uniswap V3 (#358)
* Add FeeTier* Add FeeTier tests* Update project version 0.7.0 -> 0.7.1* Revert several unnecessary changes* Fix tests
1 parentad8c8a1 commitcd5d95e

File tree

9 files changed

+209
-82
lines changed

9 files changed

+209
-82
lines changed

‎README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ Contributors also earn this beautiful [GitPOAP](https://gitpoap.notion.site/What
8080

8181
##Changelog
8282

83+
_0.7.1_
84+
85+
* Updated: Default fee is not applied when using Uniswap V3. Default fee for Uniswap V1 and V2 is still 0.3%.
86+
* 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)
87+
88+
_0.7.0_
89+
90+
* incomplete changelog
91+
8392
_0.5.4_
8493

8594
* added use of gas estimation instead of a fixed gas limit (to support Arbitrum)

‎pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name ="uniswap-python"
3-
version ="0.7.0"
3+
version ="0.7.1"
44
description ="An unofficial Python wrapper for the decentralized exchange Uniswap"
55
repository ="https://github.com/shanefontaine/uniswap-python"
66
readme ="README.md"

‎tests/test_uniswap.py

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
fromtimeimportsleep
1010

1111
fromweb3importWeb3
12+
fromweb3.typesimportWei
1213

1314
fromuniswapimportUniswap
1415
fromuniswap.constantsimportETH_ADDRESS
15-
fromuniswap.exceptionsimportInsufficientBalance
16+
fromuniswap.feeimportFeeTier
17+
fromuniswap.exceptionsimportInsufficientBalance,InvalidFeeTier
1618
fromuniswap.tokensimportget_tokens
1719
fromuniswap.utilimport (
1820
_str_to_addr,
@@ -75,11 +77,11 @@ def test_assets(client: Uniswap):
7577
("USDC",10_000*ONE_USDC),
7678
]:
7779
token_addr=tokens[token_name]
78-
price=client.get_price_output(_str_to_addr(ETH_ADDRESS),token_addr,amount)
80+
price=client.get_price_output(_str_to_addr(ETH_ADDRESS),token_addr,amount,fee=FeeTier.TIER_3000)
7981
logger.info(f"Cost of{amount}{token_name}:{price}")
8082
logger.info("Buying...")
8183

82-
txid=client.make_trade_output(tokens["ETH"],token_addr,amount)
84+
txid=client.make_trade_output(tokens["ETH"],token_addr,amount,fee=FeeTier.TIER_3000)
8385
tx=client.w3.eth.wait_for_transaction_receipt(txid,timeout=RECEIPT_TIMEOUT)
8486
asserttx["status"]==1,f"Transaction failed:{tx}"
8587

@@ -159,47 +161,47 @@ def test_get_fee_taker(self, client: Uniswap):
159161

160162
# ------ Market --------------------------------------------------------------------
161163
@pytest.mark.parametrize(
162-
"token0, token1, qty, kwargs",
164+
"token0, token1, qty",
163165
[
164-
("ETH","UNI",ONE_ETH, {}),
165-
("UNI","ETH",ONE_ETH, {}),
166-
("ETH","DAI",ONE_ETH, {}),
167-
("DAI","ETH",ONE_ETH, {}),
168-
("ETH","UNI",2*ONE_ETH, {}),
169-
("UNI","ETH",2*ONE_ETH, {}),
170-
("WETH","DAI",ONE_ETH, {}),
171-
("DAI","WETH",ONE_ETH, {}),
172-
("DAI","USDC",ONE_ETH, {"fee":500}),
166+
("ETH","UNI",ONE_ETH),
167+
("UNI","ETH",ONE_ETH),
168+
("ETH","DAI",ONE_ETH),
169+
("DAI","ETH",ONE_ETH),
170+
("ETH","UNI",2*ONE_ETH),
171+
("UNI","ETH",2*ONE_ETH),
172+
("WETH","DAI",ONE_ETH),
173+
("DAI","WETH",ONE_ETH),
174+
("DAI","USDC",ONE_ETH),
173175
],
174176
)
175-
deftest_get_price_input(self,client,tokens,token0,token1,qty,kwargs):
177+
deftest_get_price_input(self,client:Uniswap,tokens,token0,token1,qty):
176178
token0,token1=tokens[token0],tokens[token1]
177179
ifclient.version==1andETH_ADDRESSnotin [token0,token1]:
178180
pytest.skip("Not supported in this version of Uniswap")
179-
r=client.get_price_input(token0,token1,qty,**kwargs)
181+
r=client.get_price_input(token0,token1,qty,fee=FeeTier.TIER_3000)
180182
assertr
181183

182184
@pytest.mark.parametrize(
183-
"token0, token1, qty, kwargs",
185+
"token0, token1, qty",
184186
[
185-
("ETH","UNI",ONE_ETH, {}),
186-
("UNI","ETH",ONE_ETH//100, {}),
187-
("ETH","DAI",ONE_ETH, {}),
188-
("DAI","ETH",ONE_ETH, {}),
189-
("ETH","UNI",2*ONE_ETH, {}),
190-
("WETH","DAI",ONE_ETH, {}),
191-
("DAI","WETH",ONE_ETH, {}),
192-
("DAI","USDC",ONE_USDC, {"fee":500}),
187+
("ETH","UNI",ONE_ETH),
188+
("UNI","ETH",ONE_ETH//100),
189+
("ETH","DAI",ONE_ETH),
190+
("DAI","ETH",ONE_ETH),
191+
("ETH","UNI",2*ONE_ETH),
192+
("WETH","DAI",ONE_ETH),
193+
("DAI","WETH",ONE_ETH),
194+
("DAI","USDC",ONE_USDC),
193195
],
194196
)
195-
deftest_get_price_output(self,client,tokens,token0,token1,qty,kwargs):
197+
deftest_get_price_output(self,client:Uniswap,tokens,token0,token1,qty):
196198
token0,token1=tokens[token0],tokens[token1]
197199
ifclient.version==1andETH_ADDRESSnotin [token0,token1]:
198200
pytest.skip("Not supported in this version of Uniswap")
199-
r=client.get_price_output(token0,token1,qty,**kwargs)
201+
r=client.get_price_output(token0,token1,qty,fee=FeeTier.TIER_3000)
200202
assertr
201203

202-
@pytest.mark.parametrize("token0, token1, fee", [("DAI","USDC",500)])
204+
@pytest.mark.parametrize("token0, token1, fee", [("DAI","USDC",FeeTier.TIER_3000)])
203205
deftest_get_raw_price(self,client:Uniswap,tokens,token0,token1,fee):
204206
token0,token1=tokens[token0],tokens[token1]
205207
ifclient.version==1:
@@ -210,7 +212,7 @@ def test_get_raw_price(self, client: Uniswap, tokens, token0, token1, fee):
210212
@pytest.mark.parametrize(
211213
"token0, token1, kwargs",
212214
[
213-
("WETH","DAI", {"fee":500}),
215+
("WETH","DAI", {"fee":FeeTier.TIER_3000}),
214216
],
215217
)
216218
deftest_get_pool_instance(self,client,tokens,token0,token1,kwargs):
@@ -223,7 +225,7 @@ def test_get_pool_instance(self, client, tokens, token0, token1, kwargs):
223225
@pytest.mark.parametrize(
224226
"token0, token1, kwargs",
225227
[
226-
("WETH","DAI", {"fee":500}),
228+
("WETH","DAI", {"fee":FeeTier.TIER_3000}),
227229
],
228230
)
229231
deftest_get_pool_immutables(self,client,tokens,token0,token1,kwargs):
@@ -238,7 +240,7 @@ def test_get_pool_immutables(self, client, tokens, token0, token1, kwargs):
238240
@pytest.mark.parametrize(
239241
"token0, token1, kwargs",
240242
[
241-
("WETH","DAI", {"fee":500}),
243+
("WETH","DAI", {"fee":FeeTier.TIER_3000}),
242244
],
243245
)
244246
deftest_get_pool_state(self,client,tokens,token0,token1,kwargs):
@@ -253,7 +255,7 @@ def test_get_pool_state(self, client, tokens, token0, token1, kwargs):
253255
@pytest.mark.parametrize(
254256
"amount0, amount1, token0, token1, kwargs",
255257
[
256-
(1,10,"WETH","DAI", {"fee":500}),
258+
(1,10,"WETH","DAI", {"fee":FeeTier.TIER_3000}),
257259
],
258260
)
259261
deftest_mint_position(
@@ -308,7 +310,7 @@ def test_get_exchange_rate(
308310
@pytest.mark.parametrize(
309311
"token0, token1, amount0, amount1, qty, fee",
310312
[
311-
("DAI","USDC",ONE_ETH,ONE_USDC,ONE_ETH,3000),
313+
("DAI","USDC",ONE_ETH,ONE_USDC,ONE_ETH,FeeTier.TIER_3000),
312314
],
313315
)
314316
deftest_v3_deploy_pool_with_liquidity(
@@ -325,14 +327,14 @@ def test_v3_deploy_pool_with_liquidity(
325327
print(pool.address)
326328
# Ensuring client has sufficient balance of both tokens
327329
eth_to_dai=client.make_trade(
328-
tokens["ETH"],tokens[token0],qty,client.address
330+
tokens["ETH"],tokens[token0],qty,client.address,fee=fee,
329331
)
330332
eth_to_dai_tx=client.w3.eth.wait_for_transaction_receipt(
331333
eth_to_dai,timeout=RECEIPT_TIMEOUT
332334
)
333335
asserteth_to_dai_tx["status"]
334336
dai_to_usdc=client.make_trade(
335-
tokens[token0],tokens[token1],qty*10,client.address
337+
tokens[token0],tokens[token1],qty*10,client.address,fee=fee,
336338
)
337339
dai_to_usdc_tx=client.w3.eth.wait_for_transaction_receipt(
338340
dai_to_usdc,timeout=RECEIPT_TIMEOUT
@@ -381,7 +383,7 @@ def test_get_tvl_in_pool_on_chain(self, client: Uniswap, tokens, token0, token1)
381383
ifclient.version!=3:
382384
pytest.skip("Not supported in this version of Uniswap")
383385

384-
pool=client.get_pool_instance(tokens[token0],tokens[token1])
386+
pool=client.get_pool_instance(tokens[token0],tokens[token1],fee=FeeTier.TIER_3000)
385387
tvl_0,tvl_1=client.get_tvl_in_pool(pool)
386388
asserttvl_0>0
387389
asserttvl_1>0
@@ -452,7 +454,7 @@ def test_make_trade(
452454
withexpectation():
453455
bal_in_before=client.get_token_balance(input_token)
454456

455-
txid=client.make_trade(input_token,output_token,qty,recipient)
457+
txid=client.make_trade(input_token,output_token,qty,recipient,fee=FeeTier.TIER_3000)
456458
tx=web3.eth.wait_for_transaction_receipt(txid,timeout=RECEIPT_TIMEOUT)
457459
asserttx["status"],f"Transaction failed with status{tx['status']}:{tx}"
458460

@@ -474,13 +476,6 @@ def test_make_trade(
474476
# ("ETH", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
475477
# ("UNI", "ETH", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
476478
# ("DAI", "UNI", int(0.000001 * ONE_ETH), ZERO_ADDRESS),
477-
(
478-
"DAI",
479-
"ETH",
480-
10*ONE_ETH,
481-
None,
482-
lambda:pytest.raises(InsufficientBalance),
483-
),
484479
("DAI","DAI",ONE_USDC,None,lambda:pytest.raises(ValueError)),
485480
],
486481
)
@@ -504,11 +499,45 @@ def test_make_trade_output(
504499
withexpectation():
505500
balance_before=client.get_token_balance(output_token)
506501

507-
r=client.make_trade_output(input_token,output_token,qty,recipient)
502+
r=client.make_trade_output(input_token,output_token,qty,recipient,fee=FeeTier.TIER_3000)
508503
tx=web3.eth.wait_for_transaction_receipt(r,timeout=RECEIPT_TIMEOUT)
509504
asserttx["status"]
510505

511-
# TODO: Checks for ETH, taking gas into account
506+
##TODO: Checks for ETH, taking gas into account
512507
balance_after=client.get_token_balance(output_token)
513508
ifoutput_token!=tokens["ETH"]:
514509
assertbalance_before+qty==balance_after
510+
511+
deftest_fee_required_for_uniswap_v3(
512+
self,
513+
client:Uniswap,
514+
tokens,
515+
)->None:
516+
ifclient.version!=3:
517+
pytest.skip("Not supported in this version of Uniswap")
518+
withpytest.raises(InvalidFeeTier):
519+
client.get_price_input(tokens["ETH"],tokens["UNI"],ONE_ETH,fee=None)
520+
withpytest.raises(InvalidFeeTier):
521+
client.get_price_output(tokens["ETH"],tokens["UNI"],ONE_ETH,fee=None)
522+
withpytest.raises(InvalidFeeTier):
523+
client._get_eth_token_output_price(tokens["UNI"],ONE_ETH,fee=None)
524+
withpytest.raises(InvalidFeeTier):
525+
client._get_token_eth_output_price(tokens["UNI"],Wei(ONE_ETH),fee=None)
526+
withpytest.raises(InvalidFeeTier):
527+
client._get_token_token_output_price(
528+
tokens["UNI"],tokens["ETH"],ONE_ETH,fee=None
529+
)
530+
withpytest.raises(InvalidFeeTier):
531+
client.make_trade(tokens["ETH"],tokens["UNI"],ONE_ETH,fee=None)
532+
withpytest.raises(InvalidFeeTier):
533+
client.make_trade_output(tokens["ETH"],tokens["UNI"],ONE_ETH,fee=None)
534+
# NOTE: (rudiemeant@gmail.com): Since in 0.7.1 we're breaking the
535+
# backwards-compatibility with 0.7.0, we should check
536+
# that clients now get an error when trying to call methods
537+
# without explicitly specifying a fee tier.
538+
withpytest.raises(InvalidFeeTier):
539+
client.get_pool_instance(tokens["ETH"],tokens["UNI"],fee=None)# type: ignore[arg-type]
540+
withpytest.raises(InvalidFeeTier):
541+
client.create_pool_instance(tokens["ETH"],tokens["UNI"],fee=None)# type: ignore[arg-type]
542+
withpytest.raises(InvalidFeeTier):
543+
client.get_raw_price(tokens["ETH"],tokens["UNI"],fee=None)

‎tests/units/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+


‎tests/units/test_fee_tier.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
fromtypingimportAny
2+
3+
importpytest
4+
5+
fromuniswap.feeimportFeeTier,validate_fee_tier
6+
fromuniswap.exceptionsimportInvalidFeeTier
7+
8+
9+
10+
@pytest.mark.parametrize("version", [1,2])
11+
deftest_fee_tier_default(version:int)->None:
12+
fee_tier=validate_fee_tier(fee=None,version=version)
13+
assertfee_tier==FeeTier.TIER_3000
14+
15+
16+
deftest_fee_tier_default_v3()->None:
17+
withpytest.raises(InvalidFeeTier)asexc:
18+
validate_fee_tier(fee=None,version=3)
19+
assert"Explicit fee tier is required for Uniswap V3"instr(exc.value)
20+
21+
22+
@pytest.mark.parametrize(
23+
("fee","version"),
24+
[
25+
(FeeTier.TIER_100,1),
26+
(FeeTier.TIER_500,1),
27+
(FeeTier.TIER_10000,1),
28+
(FeeTier.TIER_100,2),
29+
(FeeTier.TIER_500,2),
30+
(FeeTier.TIER_10000,2),
31+
],
32+
)
33+
deftest_unsupported_fee_tiers(fee:int,version:int)->None:
34+
withpytest.raises(InvalidFeeTier)asexc:
35+
validate_fee_tier(fee=fee,version=version)
36+
assert"Unsupported fee tier"instr(exc.value)
37+
38+
39+
@pytest.mark.parametrize(
40+
"invalid_fee",
41+
[
42+
"undefined",
43+
0,
44+
1_000_000,
45+
1.1,
46+
(1,3),
47+
type,
48+
],
49+
)
50+
deftest_invalid_fee_tiers(invalid_fee:Any)->None:
51+
withpytest.raises(InvalidFeeTier)asexc:
52+
validate_fee_tier(fee=invalid_fee,version=3)
53+
assert"Invalid fee tier"instr(exc.value)

‎uniswap/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
fromweb3importWeb3
88

99
from .constantsimportETH_ADDRESS
10+
from .feeimportFeeTier
1011
from .tokenimportBaseToken
1112
from .tokensimportget_tokens
1213
from .uniswapimportAddressLike,Uniswap,_str_to_addr
@@ -80,7 +81,7 @@ def price(
8081
else:
8182
decimals=uni.get_token(token_in).decimals
8283
quantity=10**decimals
83-
price=uni.get_price_input(token_in,token_out,qty=quantity)
84+
price=uni.get_price_input(token_in,token_out,qty=quantity,fee=FeeTier.TIER_3000)
8485
ifraw:
8586
click.echo(price)
8687
else:

‎uniswap/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ class InsufficientBalance(Exception):
1313

1414
def__init__(self,had:int,needed:int)->None:
1515
Exception.__init__(self,f"Insufficient balance. Had{had}, needed{needed}")
16+
17+
18+
classInvalidFeeTier(Exception):
19+
"""
20+
Raised when an invalid or unsupported fee tier is used.
21+
"""

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp