1

I'm currently trying to retrieve the price impact of a swap on a UniswapV3 pool. Uniswap itself seems to suggest the use of its quoter contract.

I see that the quoter contract has the "quoting" functions as write functions so I suppose a simulation of those is necessary to get the value over sending a transaction for every result needed. Unfortunately I seem unable to accomplish a simulation using Web3py.

My current code looks like this:

async def get_uniswap_v3_buy_impact(self, dex: Dex, amount_in_against: Decimal) -> Decimal:
    quoter = self.contracts.quoter_contract
    try:
        res = await quoter.functions.quoteExactInputSingle(
            dex.token.address,
            dex.token_against.address,
            dex.fee_tier,
            int(amount_in_against * 10 ** dex.token_against.decimals),
            0
        ).call()
        print(res)
    except Exception as e:
        print(e)
    return Decimal(0)

An error of execution reverted is always received with no data in the payload. I tried to integrate Web3py OffChainLookup by setting ccip_read_enabled=True in .call() but the result stays the same. This python library seems to do what I do as well. I'm sure I'm missing something here.

Updated code:

    async def get_uniswap_v3_buy_impact(self, dex: Dex, amount_in_against: Decimal):
    # get quoter contract
    quoter = self.contracts.quoter_contract

    # get and encode path
    path = [dex.token_against.address, dex.fee_tier, dex.token.address]
    path = "0x" + ''.join(item[2:] if isinstance(item, str) else f"{item:06X}" for item in path)

    try:
        res = await quoter.functions.quoteExactInput(
            self.w3.to_bytes(hexstr=HexStr(path)),
            int(amount_in_against * 10 ** dex.token_against.decimals)
        ).call()
        print(res)
    except Exception as ocl:
        print(ocl)
    return Decimal(0)
0

1 Answer 1

0

Welcome to Ethereum and Uniswap v3. I am one of the authors of eth_defi library.

I wrote a new tutorial for you - full code here.

"""Uniswap v3 price impact example.

- Price impact is the difference between mid price and quoted/filled price

To run:

.. code-block:: shell

    python scripts/uniswap-v3-price-impact.py

"""


import os
from decimal import Decimal

from eth_defi.provider.multi_provider import create_multi_provider_web3
from eth_defi.uniswap_v3.constants import UNISWAP_V3_DEPLOYMENTS
from eth_defi.uniswap_v3.deployment import fetch_deployment
from eth_defi.uniswap_v3.pool import fetch_pool_details
from eth_defi.uniswap_v3.price import get_onchain_price, estimate_buy_received_amount
from eth_defi.uniswap_v3.tvl import fetch_uniswap_v3_pool_tvl


def main():
    # You can pass your own endpoint in an environment variable
    json_rpc_url = os.environ.get("JSON_RPC_POLYGON", "https://polygon-rpc.com")

    # Search pair contract addresses using Trading Strategy search: https://tradingstrategy.ai/search
    # This one is:
    # https://tradingstrategy.ai/trading-view/polygon/uniswap-v3/eth-usdc-fee-5
    pool_address = os.environ.get("PAIR_ADDRESS", "0x45dda9cb7c25131df268515131f647d726f50608")

    # Create web3 connection instance
    web3 = create_multi_provider_web3(json_rpc_url)

    contract_details = UNISWAP_V3_DEPLOYMENTS["polygon"]
    uniswap = fetch_deployment(
        web3,
        factory_address=contract_details["factory"],
        router_address=contract_details["router"],
        position_manager_address=contract_details["position_manager"],
        quoter_address=contract_details["quoter"],
    )

    # Get Pool contract ABI file, prepackaged in eth_defi Python package
    # and convert it to a wrapped Python object
    pool = fetch_pool_details(web3, pool_address)

    inverse = True

    # Manually resolve token order from random Uniswap v3 order
    if inverse:
        base_token = pool.token1
        quote_token = pool.token0
    else:
        base_token = pool.token0
        quote_token = pool.token1

    # Print out pool details
    # token0 and token1 will be always in a random order
    # and may inverse the price
    print("-" * 80)
    print("Uniswap pool details")
    print("Chain", web3.eth.chain_id)
    print("Pool", pool_address)
    print("Token0", pool.token0.symbol)
    print("Token1", pool.token1.symbol)
    print("Base token", base_token.symbol)
    print("Quote token", quote_token.symbol)
    print("Fee (BPS)", pool.get_fee_bps())
    print("-" * 80)

    inverse = True  # Is price inverted for output

    # Record the block number close to our timestamp
    block_num = web3.eth.get_block_number()

    # Use get_onchain_price() to get a human readable price
    # in Python Decimal
    mid_price = get_onchain_price(
        web3,
        pool.address,
    )

    if inverse:
        mid_price = 1 / mid_price

    target_pair_fee_bps = 5

    # Attempt to buy ETH wit $1,000,000.50
    swap_amount = Decimal("1_000_000.50")
    swap_amount_raw = quote_token.convert_to_raw(swap_amount)

    received_amount_raw = estimate_buy_received_amount(
        uniswap=uniswap,
        base_token_address=base_token.address,
        quote_token_address=quote_token.address,
        quantity=swap_amount_raw,
        target_pair_fee=target_pair_fee_bps * 100,  # Uniswap v3 units
        block_identifier=block_num,
    )

    received_amount = base_token.convert_to_decimals(received_amount_raw)

    quoted_price = received_amount / swap_amount

    if inverse:
        quoted_price = 1 / quoted_price

    price_impact = (quoted_price - mid_price) / mid_price

    tvl_quote = fetch_uniswap_v3_pool_tvl(
        pool,
        quote_token,
        block_identifier=block_num,
    )

    tvl_base = fetch_uniswap_v3_pool_tvl(
        pool,
        base_token,
        block_identifier=block_num,
    )

    print(f"Block: {block_num:,}")
    print(f"Swap size: {swap_amount:,.2f} {quote_token.symbol}")
    print(f"Pool base token TVL: {tvl_base:,.2f} {base_token.symbol}")
    print(f"Pool quote token TVL: {tvl_quote:,.2f} {quote_token.symbol}")
    print(f"Mid price {base_token.symbol} / {quote_token.symbol}: {mid_price:,.2f}")
    print(f"Quoted amount to received: {received_amount:,.2f} {base_token.symbol}")
    print(f"Quoted price (no execution slippage): {quoted_price:,.2f} {quote_token.symbol}")
    print(f"Price impact: {price_impact * 100:.2f}%")

if __name__ == "__main__":
    main()

Output:

    --------------------------------------------------------------------------------
    Uniswap pool details
    Chain 137
    Pool 0x45dda9cb7c25131df268515131f647d726f50608
    Token0 USDC
    Token1 WETH
    Base token WETH
    Quote token USDC
    Fee (BPS) 5
    --------------------------------------------------------------------------------
    Block: 62,632,744
    Swap size: 1,000,000.50 USDC
    Pool base token TVL: 739.37 WETH
    Pool quote token TVL: 558,088.84 USDC
    Mid price WETH / USDC: 2,423.61
    Quoted amount to received: 354.87 WETH
    Quoted price (no execution slippage): 2,817.91 USDC
    Price impact: 16.27%
10
  • 1
    Thank you for your kind response. It's very valuable but I'm really curious in knowing how you retrieve the actual response from .quoteExactInput(...) without actually sending a transaction on chain.
    – null
    Commented Oct 4 at 20:56
  • All source code is open. You can click "View definition" in your IDE or just browser the code on Github: github.com/tradingstrategy-ai/web3-ethereum-defi Commented Oct 5 at 8:21
  • 1
    I did check out the code at first but I was using a different function which in theory should work any way. Now I updated the code above to use your same function "quoteExactInput" and the same overall logic. I checked the contract object you are using and it's the standard Web3 one. Still get the revert without any data out. Do you have any clue for what I'm doing wrong here?
    – null
    Commented Oct 5 at 9:10
  • 1
    you mean copy pasting from eth-defi? or from my implementation? My implementation is just getting the contract instance of the quoter contract and calling that function after encoding the arguments. The call is what returns the revert which to me makes sense because of it being a write function of the quoter. Is there anything in particular that you do to simulate this call or is your implementation equivalent to mine?
    – null
    Commented Oct 5 at 9:29
  • 1
    Thank you for the directions, trying a minimum reproducible example led to me the solution. The fee wasn't properly added inside the object "Dex".
    – null
    Commented Oct 5 at 11:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.