From d95a0b2bb3fba853fbf2fd99d7098c7b0de88004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Thu, 11 Nov 2021 15:22:58 +0100 Subject: [PATCH 1/2] docs: added warning about trading illiquid pools/routes --- docs/getting-started.rst | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/getting-started.rst b/docs/getting-started.rst index fdc850c..7842c18 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -76,14 +76,14 @@ Quoting prices .. note:: - These methods assume a certain route for the swap to take, which may not be the optimal route. See :issue:`69` for details. + These methods assume a certain route for the swap to take, which may not be the optimal route. See :issue:`93` for details. There are two functions to retrieve the price for a given pair, one for specifying how much you get given a certain amount of the input token, and another for specifying how much you need to pay to receive a certain amount of the output token. :func:`~uniswap.Uniswap.get_price_input` ```````````````````````````````````````` -Returns the cost of the given number of input tokens, priced in the output token. +Returns the amount of output tokens you get for a given amount of input tokens. .. code:: python @@ -120,26 +120,34 @@ Making trades The same route assumptions and need for handling decimals apply here as those mentioned in the previous section. +.. warning:: + + Always check the expected price before executing a trade. It's important that you're using a pool with adequate liquidity, or else you may suffer significant losses! (see :issue:`198`) + + Use the Uniswap version with the most liquidity for your route, and if using v3, make sure you set the ``fee`` parameter to use the best pool. + :func:`~uniswap.Uniswap.make_trade` ``````````````````````````````````` .. code:: python - # Make a trade where the input qty being known parameters - uniswap.make_trade(eth, bat, 1*10**18) # sell 1 ETH for however many BAT - uniswap.make_trade(bat, eth, 1*10**18) # sell 1 BAT for however many ETH - uniswap.make_trade(bat, dai, 1*10**18) # sell 1 BAT for however many DAI - uniswap.make_trade(eth, bat, 1*10**18, "0x123...") # sell 1 ETH for however many BAT, and send the BAT to the provided address + # Make a trade by specifying the quantity of the input token you wish to sell + uniswap.make_trade(eth, bat, 1*10**18) # sell 1 ETH for BAT + uniswap.make_trade(bat, eth, 1*10**18) # sell 1 BAT for ETH + uniswap.make_trade(bat, dai, 1*10**18) # sell 1 BAT for DAI + uniswap.make_trade(eth, bat, 1*10**18, "0x123...") # sell 1 ETH for BAT, and send the BAT to the provided address + uniswap.make_trade(dai, usdc, 1*10**18, fee=500) # sell 1 DAI for USDC using the 0.05% fee pool (v3 only) :func:`~uniswap.Uniswap.make_trade_output` `````````````````````````````````````````` .. code:: python - # Make a trade where the output qty is known, based on the input parameters - uniswap.make_trade_output(eth, bat, 1*10**18) # buy however many ETH for 1 BAT - uniswap.make_trade_output(bat, eth, 1*10**18) # buy however many BAT for 1 ETH - uniswap.make_trade_output(bat, dai, 1*10**18, "0x123...") # buy however many BAT for 1 DAI, and send the BAT to the provided address + # Make a trade by specifying the quantity of the output token you wish to buy + uniswap.make_trade_output(eth, bat, 1*10**18) # buy ETH for 1 BAT + uniswap.make_trade_output(bat, eth, 1*10**18) # buy BAT for 1 ETH + uniswap.make_trade_output(bat, dai, 1*10**18, "0x123...") # buy BAT for 1 DAI, and send the BAT to the provided address + uniswap.make_trade_output(dai, usdc, 1*10**8, fee=500) # buy USDC for 1 DAI using the 0.05% fee pool (v3 only) Pool Methods (v1 only) From 350296fd0409e2f874364bd0e8ab08e60d5075d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Bj=C3=A4reholt?= Date: Thu, 11 Nov 2021 15:24:05 +0100 Subject: [PATCH 2/2] feat: added estimate_price_impact helper function and example --- examples/price_impact.py | 63 ++++++++++++++++++++++++++++++++++++++++ uniswap/uniswap.py | 30 ++++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 examples/price_impact.py diff --git a/examples/price_impact.py b/examples/price_impact.py new file mode 100644 index 0000000..15c3a0a --- /dev/null +++ b/examples/price_impact.py @@ -0,0 +1,63 @@ +from typing import List + +from web3 import Web3 + +from uniswap import Uniswap +from uniswap.types import AddressLike + +eth = Web3.toChecksumAddress("0x0000000000000000000000000000000000000000") +weth = Web3.toChecksumAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") +usdt = Web3.toChecksumAddress("0xdac17f958d2ee523a2206206994597c13d831ec7") +vxv = Web3.toChecksumAddress("0x7d29a64504629172a429e64183d6673b9dacbfce") + + +def _perc(f: float) -> str: + return f"{round(f * 100, 3)}%" + + +def usdt_to_vxv_v2(): + """ + Checks impact for a pool with very little liquidity. + + This particular route caused a $14k loss for one user: https://github.com/uniswap-python/uniswap-python/discussions/198 + """ + uniswap = Uniswap(address=None, private_key=None, version=2) + + route: List[AddressLike] = [usdt, weth, vxv] + + # Compare the results with the output of: + # https://app.uniswap.org/#/swap?use=v2&inputCurrency=0xdac17f958d2ee523a2206206994597c13d831ec7&outputCurrency=0x7d29a64504629172a429e64183d6673b9dacbfce + qty = 10 * 10 ** 8 + + # price = uniswap.get_price_input(usdt, vxv, qty, route=route) / 10 ** 18 + # print(price) + + impact = uniswap.estimate_price_impact(usdt, vxv, qty, route=route) + # NOTE: Not sure why this differs from the quote in the UI? + # Getting -27% in the UI for 10 USDT, but this returns >95% + # The slippage for v3 (in example below) returns correct results. + print(f"Impact for buying VXV on v2 with {qty / 10**8} USDT: {_perc(impact)}") + + qty = 13900 * 10 ** 8 + impact = uniswap.estimate_price_impact(usdt, vxv, qty, route=route) + print(f"Impact for buying VXV on v2 with {qty / 10**8} USDT: {_perc(impact)}") + + +def eth_to_vxv_v3(): + """Checks price impact for a pool with liquidity.""" + uniswap = Uniswap(address=None, private_key=None, version=3) + + # Compare the results with the output of: + # https://app.uniswap.org/#/swap?use=v3&inputCurrency=ETH&outputCurrency=0x7d29a64504629172a429e64183d6673b9dacbfce + qty = 1 * 10 ** 18 + impact = uniswap.estimate_price_impact(eth, vxv, qty, fee=10000) + print(f"Impact for buying VXV on v3 with {qty / 10**18} ETH: {_perc(impact)}") + + qty = 100 * 10 ** 18 + impact = uniswap.estimate_price_impact(eth, vxv, qty, fee=10000) + print(f"Impact for buying VXV on v3 with {qty / 10**18} ETH: {_perc(impact)}") + + +if __name__ == "__main__": + usdt_to_vxv_v2() + eth_to_vxv_v3() diff --git a/uniswap/uniswap.py b/uniswap/uniswap.py index 418c5ce..6c54b70 100644 --- a/uniswap/uniswap.py +++ b/uniswap/uniswap.py @@ -1172,7 +1172,7 @@ def _calculate_max_output_token( # ------ Helpers ------------------------------------------------------------ - def get_token(self, address: AddressLike, abi_name:str="erc20") -> ERC20Token: + def get_token(self, address: AddressLike, abi_name: str = "erc20") -> ERC20Token: """ Retrieves metadata from the ERC20 contract of a given token, like its name, symbol, and decimals. """ @@ -1209,6 +1209,34 @@ def get_weth_address(self) -> ChecksumAddress: address = self.router.functions.WETH9().call() return address + def estimate_price_impact( + self, + token_in: AddressLike, + token_out: AddressLike, + amount_in: int, + fee: int = None, + route: Optional[List[AddressLike]] = None, + ) -> float: + """ + Returns the estimated price impact as a positive float (0.01 = 1%). + + NOTE: Work-in-progress. + + See ``examples/price_impact.py`` for an example which uses this. + """ + amount_small = 10 ** 2 + cost_small = self.get_price_input( + token_in, token_out, amount_small, fee=fee, route=route + ) + cost_amount = self.get_price_input( + token_in, token_out, amount_in, fee=fee, route=route + ) + + price_small = cost_small / amount_small + price_amount = cost_amount / amount_in + + return (price_small - price_amount) / price_small + # ------ Exchange ------------------------------------------------------------------ @supports([1, 2]) def get_fee_maker(self) -> float: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy