@@ -182,7 +182,7 @@ def get_price_input(
182182 token1 : AddressLike , # output token
183183 qty : int ,
184184 fee : int = None ,
185- route : Optional [ List [AddressLike ] ] = None ,
185+ route : List [AddressLike ] = None ,
186186 ) -> int :
187187 """Given `qty` amount of the input `token0`, returns the maximum output amount of output `token1`."""
188188 if fee is None :
@@ -203,7 +203,7 @@ def get_price_output(
203203 token1 : AddressLike ,
204204 qty : int ,
205205 fee : int = None ,
206- route : Optional [ List [AddressLike ] ] = None ,
206+ route : List [AddressLike ] = None ,
207207 ) -> int :
208208 """Returns the minimum amount of `token0` required to buy `qty` amount of `token1`."""
209209 if fee is None :
@@ -264,7 +264,7 @@ def _get_token_token_input_price(
264264 token1 : AddressLike , # output token
265265 qty : int ,
266266 fee : int ,
267- route : Optional [ List [AddressLike ] ] = None ,
267+ route : List [AddressLike ] = None ,
268268 ) -> int :
269269 """
270270 Public price (i.e. amount of output token received) for token to token trades with an exact input.
@@ -581,68 +581,93 @@ def _token_to_eth_swap_input(
581581 function = token_funcs .tokenToEthTransferInput (* func_params )
582582 return self ._build_and_send_tx (function )
583583 elif self .version == 2 :
584- if recipient is None :
585- recipient = self .address
586- amount_out_min = int (
587- (1 - slippage ) * self ._get_token_eth_input_price (input_token , qty , fee )
588- )
589- if fee_on_transfer :
590- func = (
591- self .router .functions .swapExactTokensForETHSupportingFeeOnTransferTokens
592- )
593- else :
594- func = self .router .functions .swapExactTokensForETH
595- return self ._build_and_send_tx (
596- func (
597- qty ,
598- amount_out_min ,
599- [input_token , self .get_weth_address ()],
600- recipient ,
601- self ._deadline (),
602- ),
584+ return self ._token_to_eth_swap_input_v2 (
585+ input_token , qty , recipient , fee , slippage , fee_on_transfer
603586 )
604587 elif self .version == 3 :
605- if recipient is None :
606- recipient = self .address
607-
608588 if fee_on_transfer :
609589 raise Exception ("fee on transfer not supported by Uniswap v3" )
610590
611- output_token = self .get_weth_address ()
612- min_tokens_bought = int (
613- (1 - slippage )
614- * self ._get_token_eth_input_price (input_token , qty , fee = fee )
591+ return self ._token_to_eth_swap_input_v3 (
592+ input_token , qty , recipient , fee , slippage
615593 )
616- sqrtPriceLimitX96 = 0
594+ else :
595+ raise ValueError
617596
618- swap_data = self .router .encodeABI (
619- fn_name = "exactInputSingle" ,
620- args = [
621- (
622- input_token ,
623- output_token ,
624- fee ,
625- ETH_ADDRESS ,
626- self ._deadline (),
627- qty ,
628- min_tokens_bought ,
629- sqrtPriceLimitX96 ,
630- )
631- ],
597+ def _token_to_eth_swap_input_v2 (
598+ self ,
599+ input_token : AddressLike ,
600+ qty : int ,
601+ recipient : Optional [AddressLike ],
602+ fee : int ,
603+ slippage : float ,
604+ fee_on_transfer : bool ,
605+ ) -> HexBytes :
606+ if recipient is None :
607+ recipient = self .address
608+ amount_out_min = int (
609+ (1 - slippage ) * self ._get_token_eth_input_price (input_token , qty , fee )
610+ )
611+ if fee_on_transfer :
612+ func = (
613+ self .router .functions .swapExactTokensForETHSupportingFeeOnTransferTokens
632614 )
615+ else :
616+ func = self .router .functions .swapExactTokensForETH
617+ return self ._build_and_send_tx (
618+ func (
619+ qty ,
620+ amount_out_min ,
621+ [input_token , self .get_weth_address ()],
622+ recipient ,
623+ self ._deadline (),
624+ ),
625+ )
633626
634- unwrap_data = self .router .encodeABI (
635- fn_name = "unwrapWETH9" , args = [min_tokens_bought , recipient ]
636- )
627+ def _token_to_eth_swap_input_v3 (
628+ self ,
629+ input_token : AddressLike ,
630+ qty : int ,
631+ recipient : Optional [AddressLike ],
632+ fee : int ,
633+ slippage : float ,
634+ ) -> HexBytes :
635+ """NOTE: Should always be called via the dispatcher `_token_to_eth_swap_input`"""
636+ if recipient is None :
637+ recipient = self .address
637638
638- # Multicall
639- return self ._build_and_send_tx (
640- self .router .functions .multicall ([swap_data , unwrap_data ]),
641- self ._get_tx_params (),
642- )
639+ output_token = self .get_weth_address ()
640+ min_tokens_bought = int (
641+ (1 - slippage ) * self ._get_token_eth_input_price (input_token , qty , fee = fee )
642+ )
643+ sqrtPriceLimitX96 = 0
644+
645+ swap_data = self .router .encodeABI (
646+ fn_name = "exactInputSingle" ,
647+ args = [
648+ (
649+ input_token ,
650+ output_token ,
651+ fee ,
652+ ETH_ADDRESS ,
653+ self ._deadline (),
654+ qty ,
655+ min_tokens_bought ,
656+ sqrtPriceLimitX96 ,
657+ )
658+ ],
659+ )
643660
644- else :
645- raise ValueError
661+ # NOTE: This will probably lead to dust WETH accumulation
662+ unwrap_data = self .router .encodeABI (
663+ fn_name = "unwrapWETH9" , args = [min_tokens_bought , recipient ]
664+ )
665+
666+ # Multicall
667+ return self ._build_and_send_tx (
668+ self .router .functions .multicall ([swap_data , unwrap_data ]),
669+ self ._get_tx_params (),
670+ )
646671
647672 def _token_to_token_swap_input (
648673 self ,
@@ -1111,13 +1136,17 @@ def _build_and_send_tx(
11111136 # `use_estimate_gas` needs to be True for networks like Arbitrum (can't assume 250000 gas),
11121137 # but it breaks tests for unknown reasons because estimateGas takes forever on some tx's.
11131138 # Maybe an issue with ganache? (got GC warnings once...)
1139+
1140+ # In case gas estimation is disabled.
1141+ # Without this set before gas estimation, it can lead to ganache stack overflow.
1142+ # See: https://github.com/trufflesuite/ganache/issues/985#issuecomment-998937085
1143+ transaction ["gas" ] = Wei (250000 )
1144+
11141145 if self .use_estimate_gas :
11151146 # The Uniswap V3 UI uses 20% margin for transactions
11161147 transaction ["gas" ] = Wei (
11171148 int (self .w3 .eth .estimate_gas (transaction ) * 1.2 )
11181149 )
1119- else :
1120- transaction ["gas" ] = Wei (250000 )
11211150
11221151 signed_txn = self .w3 .eth .account .sign_transaction (
11231152 transaction , private_key = self .private_key
@@ -1225,11 +1254,11 @@ def get_token(self, address: AddressLike, abi_name: str = "erc20") -> ERC20Token
12251254 raise InvalidToken (address )
12261255 try :
12271256 name = _name .decode ()
1228- except :
1257+ except Exception : # FIXME: Be more precise about exception to catch
12291258 name = _name
12301259 try :
12311260 symbol = _symbol .decode ()
1232- except :
1261+ except Exception : # FIXME: Be more precise about exception to catch
12331262 symbol = _symbol
12341263 return ERC20Token (symbol , address , name , decimals )
12351264
@@ -1256,11 +1285,11 @@ def get_raw_price(
12561285 if token_out == ETH_ADDRESS :
12571286 token_out = self .get_weth_address ()
12581287
1288+ params : Tuple [ChecksumAddress , ChecksumAddress ] = (
1289+ self .w3 .toChecksumAddress (token_in ),
1290+ self .w3 .toChecksumAddress (token_out ),
1291+ )
12591292 if self .version == 2 :
1260- params : Iterable [Union [ChecksumAddress ,Optional [int ]]] = [
1261- self .w3 .toChecksumAddress (token_in ),
1262- self .w3 .toChecksumAddress (token_out ),
1263- ]
12641293 pair_token = self .factory_contract .functions .getPair (* params ).call ()
12651294 token_in_erc20 = _load_contract_erc20 (
12661295 self .w3 , self .w3 .toChecksumAddress (token_in )
@@ -1286,12 +1315,7 @@ def get_raw_price(
12861315
12871316 raw_price = token_out_balance / token_in_balance
12881317 else :
1289- params = [
1290- self .w3 .toChecksumAddress (token_in ),
1291- self .w3 .toChecksumAddress (token_out ),
1292- fee ,
1293- ]
1294- pool_address = self .factory_contract .functions .getPool (* params ).call ()
1318+ pool_address = self .factory_contract .functions .getPool (* params , fee ).call ()
12951319 pool_contract = _load_contract (
12961320 self .w3 , abi_name = "uniswap-v3/pool" , address = pool_address
12971321 )
@@ -1317,7 +1341,7 @@ def estimate_price_impact(
13171341 token_out : AddressLike ,
13181342 amount_in : int ,
13191343 fee : int = None ,
1320- route : Optional [ List [AddressLike ] ] = None ,
1344+ route : List [AddressLike ] = None ,
13211345 ) -> float :
13221346 """
13231347 Returns the estimated price impact as a positive float (0.01 = 1%).
0 commit comments