Uniswap v3 historical price estimation

Below is an example Python script that analyses the historical Uniswap v3 trade. It does some calculations around slippage and price impact analysis.

  • We know the block number when the trade decision was made (time and block number at the time when the price impact was estimated)

  • We know the block number when the trade was actually executed

  • We use a Polygon JSON-RPC archive node to check Uniswap WMATIC-USDC pool state at both blocks and compare the results

  • Slippage is assumed execution price vs. realised execution price

  • We also double check some other numbers like TVL and mid price of the underlying Uniswap v3 pool

  • See eth_defi.uniswap_v3.price module for more information

"""Check the Uniswap v3 price estimation at a historical block.

- Understand why slippage was what it was

- Check what was the estimated and executed sell WMATIC->USDC on Uniswap v3

- See the TX https://polygonscan.com/tx/0x5b76bf15bce4de5f5d6db8d929f13e28b11816f282ecd1522e4ec6eca3a1655e

"""
import os
from decimal import Decimal

from web3 import Web3, HTTPProvider

from eth_defi.token import fetch_erc20_details
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_sell_received_amount
from eth_defi.uniswap_v3.tvl import fetch_uniswap_v3_pool_tvl

# The test amount of WMATIC for which selling
# we calculate price impact and slippage numbers
wmatic_amount = Decimal("14.975601230579683413")

# WMATIC-USDC 5 BPS pool address
# https://tradingstrategy.ai/trading-view/polygon/uniswap-v3/matic-usdc-fee-5
pool_address = "0xa374094527e1673a86de625aa59517c5de346d32"
block_estimated = 45_583_631  # Assume this was when the trade was deciced
block_executed = 45_583_635  # Assume this was then the trade was executed
wmatic_address = "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"
usdc_address = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"
fee_tier = 0.0005  # BPS

# Give Polygon archive node JSON-RPC access
json_rpc_url = os.environ["JSON_RPC_POLYGON"]
web3 = Web3(HTTPProvider(json_rpc_url))

wmatic = fetch_erc20_details(web3, wmatic_address)
usdc = fetch_erc20_details(web3, usdc_address)

pool = fetch_pool_details(web3, pool_address)

wmatic_amount_raw = wmatic.convert_to_raw(wmatic_amount)

mid_price_estimated = get_onchain_price(web3, pool_address, block_identifier=block_estimated)
mid_price_executed = get_onchain_price(web3, pool_address, block_identifier=block_executed)

tvl_estimated = fetch_uniswap_v3_pool_tvl(
    pool,
    quote_token=usdc,
    block_identifier=block_estimated,
)

tvl_executed = fetch_uniswap_v3_pool_tvl(
    pool,
    quote_token=usdc,
    block_identifier=block_executed,
)

print(f"WMATIC sold {wmatic_amount}")
print(f"TVL during estimation: {tvl_estimated:,} USDC at block {block_estimated:,}")
print(f"TVL during execution: {tvl_executed:,} USDC")
print(f"Mid price when estimate at block {block_estimated:,} USDC/MATIC:", mid_price_estimated)
print(f"Mid price at the time of execution at block {block_executed:,} USDC/MATIC:", mid_price_executed)
print(f"Mid price movement {(mid_price_executed - mid_price_estimated) / mid_price_estimated * 100:.2f}%")

# Uniswap v3 deployment addresses are the same across the chains
# https://docs.uniswap.org/contracts/v3/reference/deployments
uniswap = fetch_deployment(
    web3,
    "0x1F98431c8aD98523631AE4a59f267346ea31F984",
    "0xE592427A0AEce92De3Edee1F18E0157C05861564",
    "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
    "0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6",
)

# Get the amount of price impact
estimated_sell_raw = estimate_sell_received_amount(
    uniswap,
    base_token_address=wmatic_address,
    quote_token_address=usdc_address,
    quantity=wmatic_amount_raw,
    target_pair_fee=int(fee_tier * 1_000_000),
    block_identifier=block_estimated,
    slippage=0,
)
estimated_sell = usdc.convert_to_decimals(estimated_sell_raw)

print(f"Estimated received quantity: {estimated_sell} USDC")

executed_sell_raw = estimate_sell_received_amount(
    uniswap,
    base_token_address=wmatic_address,
    quote_token_address=usdc_address,
    quantity=wmatic_amount_raw,
    target_pair_fee=int(fee_tier * 1_000_000),
    block_identifier=block_executed,
    slippage=0,
)
executed_sell = usdc.convert_to_decimals(executed_sell_raw)

executed_sell_price = executed_sell / wmatic_amount

print(f"Executed received quantity: {executed_sell} USDC")
print(f"Executed sell price: {executed_sell_price} USDC/MATIC")
print(f"Executed price impact (includes fees) {(executed_sell_price - mid_price_executed) / mid_price_executed * 100:.2f}%")
print(f"Slippage {(executed_sell - estimated_sell) / estimated_sell * 100:.2f}%")