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

Commit06e95ac

Browse files
authored
Merge pull request#201 from uniswap-python/dev/ganache-v7
2 parentsfa9836e +2a5c791 commit06e95ac

File tree

5 files changed

+89
-41
lines changed

5 files changed

+89
-41
lines changed

‎.github/workflows/test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ jobs:
4848
-name:Install dependencies
4949
run:|
5050
poetry install
51-
npm install -g ganache-cli
51+
# TODO: Update to stable ganache when released!
52+
npm install -g ganache@beta
5253
-name:Test
5354
env:
5455
PROVIDER:${{ secrets.MAINNET_PROVIDER }}

‎Makefile

Lines changed: 1 addition & 1 deletion
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 --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

‎pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ Sphinx = "*"
3939
sphinx-book-theme ="*"
4040
sphinx-click ="*"
4141

42+
[tool.pytest.ini_options]
43+
log_cli =false# to print logs during tests, set to true
44+
#log_level = "NOTSET"
45+
4246
[build-system]
4347
requires = ["poetry>=0.12"]
4448
build-backend ="poetry.masonry.api"

‎tests/test_uniswap.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818

1919

2020
logger=logging.getLogger(__name__)
21+
logging.basicConfig(level=logging.INFO)
2122

2223
ENV_UNISWAP_VERSION=os.getenv("UNISWAP_VERSION",None)
2324
ifENV_UNISWAP_VERSION:
2425
UNISWAP_VERSIONS= [int(ENV_UNISWAP_VERSION)]
2526
else:
2627
UNISWAP_VERSIONS= [1,2,3]
2728

29+
RECEIPT_TIMEOUT=5
30+
2831

2932
@dataclass
3033
classGanacheInstance:
@@ -36,7 +39,11 @@ class GanacheInstance:
3639
@pytest.fixture(scope="module",params=UNISWAP_VERSIONS)
3740
defclient(request,web3:Web3,ganache:GanacheInstance):
3841
returnUniswap(
39-
ganache.eth_address,ganache.eth_privkey,web3=web3,version=request.param
42+
ganache.eth_address,
43+
ganache.eth_privkey,
44+
web3=web3,
45+
version=request.param,
46+
use_estimate_gas=False,# see note in _build_and_send_tx
4047
)
4148

4249

@@ -54,35 +61,46 @@ def test_assets(client: Uniswap):
5461
logger.info("Buying...")
5562

5663
tx=client.make_trade_output(tokens["ETH"],token_addr,amount)
57-
client.w3.eth.wait_for_transaction_receipt(tx)
64+
client.w3.eth.wait_for_transaction_receipt(tx,timeout=RECEIPT_TIMEOUT)
5865

5966

6067
@pytest.fixture(scope="module")
6168
defweb3(ganache:GanacheInstance):
62-
w3=Web3(Web3.HTTPProvider(ganache.provider,request_kwargs={"timeout":60}))
69+
w3=Web3(Web3.HTTPProvider(ganache.provider,request_kwargs={"timeout":30}))
6370
if1!=int(w3.net.version):
6471
raiseException("PROVIDER was not a mainnet provider, which the tests require")
6572
returnw3
6673

6774

6875
@pytest.fixture(scope="module")
6976
defganache()->Generator[GanacheInstance,None,None]:
70-
"""Fixture that runs ganache-cli which has forked off mainnet"""
71-
ifnotshutil.which("ganache-cli"):
77+
"""Fixture that runs ganache which has forked off mainnet"""
78+
ifnotshutil.which("ganache"):
7279
raiseException(
73-
"ganache-cli was not found in PATH, you can install it with `npm install -g ganache-cli`"
80+
"ganache was not found in PATH, you can install it with `npm install -g ganache`"
7481
)
7582
if"PROVIDER"notinos.environ:
7683
raiseException(
7784
"PROVIDER was not set, you need to set it to a mainnet provider (such as Infura) so that we can fork off our testnet"
7885
)
7986

8087
port=10999
88+
defaultGasPrice=1000_000_000_000# 1000 gwei
8189
p=subprocess.Popen(
82-
f"ganache-cli --port{port} -s test --networkId 1 --fork{os.environ['PROVIDER']}",
90+
f"""ganache
91+
--port{port}
92+
--wallet.seed test
93+
--chain.networkId 1
94+
--chain.chainId 1
95+
--fork.url{os.environ['PROVIDER']}
96+
--miner.defaultGasPrice{defaultGasPrice}
97+
--miner.legacyInstamine true
98+
""".replace(
99+
"\n"," "
100+
),
83101
shell=True,
84102
)
85-
# Address #1 when ganache is run with `-s test`, it starts with100 ETH
103+
# Address #1 when ganache is run with `--wallet.seed test`, it starts with1000 ETH
86104
eth_address="0x94e3361495bD110114ac0b6e35Ed75E77E6a6cFA"
87105
eth_privkey="0x6f1313062db38875fb01ee52682cbf6a8420e92bfbc578c5d4fdc0a32c50266f"
88106
sleep(3)
@@ -105,7 +123,7 @@ class TestUniswap(object):
105123
ZERO_ADDRESS="0x0000000000000000000000000000000000000000"
106124

107125
# TODO: Detect mainnet vs rinkeby and set accordingly, like _get_token_addresses in the Uniswap class
108-
# For Mainnet testing (with `ganache-cli --fork` as per the ganache fixture)
126+
# For Mainnet testing (with `ganache --fork` as per the ganache fixture)
109127
eth="0x0000000000000000000000000000000000000000"
110128
weth=Web3.toChecksumAddress("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")
111129
bat=Web3.toChecksumAddress("0x0D8775F648430679A709E98d2b0Cb6250d2887EF")
@@ -219,7 +237,7 @@ def get_exchange_rate(
219237
)
220238
deftest_add_liquidity(self,client:Uniswap,web3:Web3,token,max_eth):
221239
r=client.add_liquidity(token,max_eth)
222-
tx=web3.eth.wait_for_transaction_receipt(r,timeout=6000)
240+
tx=web3.eth.wait_for_transaction_receipt(r,timeout=RECEIPT_TIMEOUT)
223241
asserttx["status"]
224242

225243
@pytest.mark.skip
@@ -274,7 +292,7 @@ def test_make_trade(
274292
bal_in_before=client.get_token_balance(input_token)
275293

276294
txid=client.make_trade(input_token,output_token,qty,recipient)
277-
tx=web3.eth.wait_for_transaction_receipt(txid)
295+
tx=web3.eth.wait_for_transaction_receipt(txid,timeout=RECEIPT_TIMEOUT)
278296
asserttx["status"]
279297

280298
# TODO: Checks for ETH, taking gas into account
@@ -324,7 +342,7 @@ def test_make_trade_output(
324342
balance_before=client.get_token_balance(output_token)
325343

326344
r=client.make_trade_output(input_token,output_token,qty,recipient)
327-
tx=web3.eth.wait_for_transaction_receipt(r,timeout=30)
345+
tx=web3.eth.wait_for_transaction_receipt(r,timeout=RECEIPT_TIMEOUT)
328346
asserttx["status"]
329347

330348
# TODO: Checks for ETH, taking gas into account

‎uniswap/uniswap.py

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ class Uniswap:
4646
Wrapper around Uniswap contracts.
4747
"""
4848

49+
address:AddressLike
50+
version:int
51+
52+
w3:Web3
53+
netid:int
54+
netname:str
55+
56+
default_slippage:float
57+
use_estimate_gas:bool
58+
4959
def__init__(
5060
self,
5161
address:Union[AddressLike,str,None],
@@ -54,6 +64,8 @@ def __init__(
5464
web3:Web3=None,
5565
version:int=1,
5666
default_slippage:float=0.01,
67+
use_estimate_gas:bool=True,
68+
# use_eip1559: bool = True,
5769
factory_contract_addr:str=None,
5870
router_contract_addr:str=None,
5971
)->None:
@@ -67,7 +79,7 @@ def __init__(
6779
:param factory_contract_addr: Can be optionally set to override the address of the factory contract.
6880
:param router_contract_addr: Can be optionally set to override the address of the router contract (v2 only).
6981
"""
70-
self.address:AddressLike=_str_to_addr(
82+
self.address=_str_to_addr(
7183
addressor"0x0000000000000000000000000000000000000000"
7284
)
7385
self.private_key= (
@@ -79,22 +91,23 @@ def __init__(
7991

8092
# TODO: Write tests for slippage
8193
self.default_slippage=default_slippage
94+
self.use_estimate_gas=use_estimate_gas
8295

8396
ifweb3:
8497
self.w3=web3
8598
else:
8699
# Initialize web3. Extra provider for testing.
87-
self.provider=provideroros.environ["PROVIDER"]
88-
self.w3=Web3(
89-
Web3.HTTPProvider(self.provider,request_kwargs={"timeout":60})
90-
)
91-
92-
netid=int(self.w3.net.version)
93-
ifnetidin_netid_to_name:
94-
self.network=_netid_to_name[netid]
100+
ifnotprovider:
101+
provider=os.environ["PROVIDER"]
102+
self.w3=Web3(Web3.HTTPProvider(provider,request_kwargs={"timeout":60}))
103+
104+
# Cache netid to avoid extra RPC calls
105+
self.netid=int(self.w3.net.version)
106+
ifself.netidin_netid_to_name:
107+
self.netname=_netid_to_name[self.netid]
95108
else:
96-
raiseException(f"Unknown netid:{netid}")
97-
logger.info(f"Using{self.w3} ('{self.network}')")
109+
raiseException(f"Unknown netid:{self.netid}")
110+
logger.info(f"Using{self.w3} ('{self.netname}', netid:{self.netid})")
98111

99112
self.last_nonce:Nonce=self.w3.eth.get_transaction_count(self.address)
100113

@@ -103,14 +116,14 @@ def __init__(
103116
# max_approval_check checks that current approval is above a reasonable number
104117
# The program cannot check for max_approval each time because it decreases
105118
# with each trade.
106-
self.max_approval_hex=f"0x{64*'f'}"
107-
self.max_approval_int=int(self.max_approval_hex,16)
108-
self.max_approval_check_hex=f"0x{15*'0'}{49*'f'}"
109-
self.max_approval_check_int=int(self.max_approval_check_hex,16)
119+
max_approval_hex=f"0x{64*'f'}"
120+
self.max_approval_int=int(max_approval_hex,16)
121+
max_approval_check_hex=f"0x{15*'0'}{49*'f'}"
122+
self.max_approval_check_int=int(max_approval_check_hex,16)
110123

111124
ifself.version==1:
112125
iffactory_contract_addrisNone:
113-
factory_contract_addr=_factory_contract_addresses_v1[self.network]
126+
factory_contract_addr=_factory_contract_addresses_v1[self.netname]
114127

115128
self.factory_contract=_load_contract(
116129
self.w3,
@@ -119,11 +132,11 @@ def __init__(
119132
)
120133
elifself.version==2:
121134
ifrouter_contract_addrisNone:
122-
router_contract_addr=_router_contract_addresses_v2[self.network]
135+
router_contract_addr=_router_contract_addresses_v2[self.netname]
123136
self.router_address:AddressLike=_str_to_addr(router_contract_addr)
124137

125138
iffactory_contract_addrisNone:
126-
factory_contract_addr=_factory_contract_addresses_v2[self.network]
139+
factory_contract_addr=_factory_contract_addresses_v2[self.netname]
127140
self.factory_contract=_load_contract(
128141
self.w3,
129142
abi_name="uniswap-v2/factory",
@@ -1092,8 +1105,19 @@ def _build_and_send_tx(
10921105
ifnottx_params:
10931106
tx_params=self._get_tx_params()
10941107
transaction=function.buildTransaction(tx_params)
1095-
# Uniswap3 uses 20% margin for transactions
1096-
transaction["gas"]=Wei(int(self.w3.eth.estimate_gas(transaction)*1.2))
1108+
1109+
if"gas"notintx_params:
1110+
# `use_estimate_gas` needs to be True for networks like Arbitrum (can't assume 250000 gas),
1111+
# but it breaks tests for unknown reasons because estimateGas takes forever on some tx's.
1112+
# Maybe an issue with ganache? (got GC warnings once...)
1113+
ifself.use_estimate_gas:
1114+
# The Uniswap V3 UI uses 20% margin for transactions
1115+
transaction["gas"]=Wei(
1116+
int(self.w3.eth.estimate_gas(transaction)*1.2)
1117+
)
1118+
else:
1119+
transaction["gas"]=Wei(250000)
1120+
10971121
signed_txn=self.w3.eth.account.sign_transaction(
10981122
transaction,private_key=self.private_key
10991123
)
@@ -1105,15 +1129,18 @@ def _build_and_send_tx(
11051129
logger.debug(f"nonce:{tx_params['nonce']}")
11061130
self.last_nonce=Nonce(tx_params["nonce"]+1)
11071131

1108-
def_get_tx_params(self,value:Wei=Wei(0))->TxParams:
1132+
def_get_tx_params(self,value:Wei=Wei(0),gas:Wei=None)->TxParams:
11091133
"""Get generic transaction parameters."""
1110-
return {
1134+
params:TxParams= {
11111135
"from":_addr_to_str(self.address),
11121136
"value":value,
11131137
"nonce":max(
11141138
self.last_nonce,self.w3.eth.get_transaction_count(self.address)
11151139
),
11161140
}
1141+
ifgas:
1142+
params["gas"]=gas
1143+
returnparams
11171144

11181145
# ------ Price Calculation Utils ---------------------------------------------------
11191146
def_calculate_max_input_token(
@@ -1344,14 +1371,12 @@ def _get_token_addresses(self) -> Dict[str, ChecksumAddress]:
13441371
Returns a dict with addresses for tokens for the current net.
13451372
Used in testing.
13461373
"""
1347-
netid=int(self.w3.net.version)
1348-
netname=_netid_to_name[netid]
1349-
ifnetname=="mainnet":
1374+
ifself.netname=="mainnet":
13501375
returntokens
1351-
elifnetname=="rinkeby":
1376+
elifself.netname=="rinkeby":
13521377
returntokens_rinkeby
13531378
else:
1354-
raiseException(f"Unknown net '{netname}'")
1379+
raiseException(f"Unknown net '{self.netname}'")
13551380

13561381
# ---- Old v1 utils ----
13571382

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp