I'm trying to calculate the price of a token in Uniswap using web3.py and a Uniswap pair contract, but I must be doing something wrong with the math. Example: I would like to calculate the price of one ETH denominated in USDC. I first connect to the pair contract USDC/ETH (0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc) to get access to the smart contracts functions via web3.py.
As explained here (https://uniswap.org/docs/v2/smart-contract-integration/building-an-oracle/) I'm storing the result of price1CumulativeLast()
and the associated timestamp at two distinct points in time (one minute between each function call). I then use the formula
(price0CumulativeLATEST — price0CumulativeFIRST) / (timestampOfLATEST — timestampOfFIRST)
(see: https://medium.com/@epheph/using-uniswap-v2-oracle-with-storage-proofs-3530e699e1d3) to compute the price of token1 (ETH) denominated in token0 (USDC).
The Uniswap docs say that price1CumulativeLast() returns a Q112Q112 fixed point number which is why I think I cannot make sense of the numbers.
I've tried searching for Python functions to easily convert a fixed point Q112Q112 to float but haven't found a working solution so I guess I must getting something fundamental wrong with the math or units used in Ethereum smart contracts or Uniswap specifically.
def calculate_price():
'''
Call price1CumulativeLast() three times with one minute interval between each call.
'''
results = []
for x in range(3):
try:
temp_dict = {}
start_ts = w3.eth.getBlock(w3.eth.block_number).timestamp
token1_price_start = contract.functions.price1CumulativeLast().call()
time.sleep(60*1)
end_ts = w3.eth.getBlock(w3.eth.block_number).timestamp
token1_price_end = contract.functions.price1CumulativeLast().call()
temp_dict['start_ts'] = start_ts
temp_dict['token1_price_start'] = token1_price_start
temp_dict['end_ts'] = end_ts
temp_dict['token1_price_end'] = token1_price_end
results.append(temp_dict)
except:
continue
return results
This gives me:
results = [{'start_ts': 1623002172,
'token1_price_start': 183015811459414492033193017518027,
'end_ts': 1623002242,
'token1_price_end': 183016664977333417354464783721666},
{'start_ts': 1623002242,
'token1_price_start': 183016664977333417354464783721666,
'end_ts': 1623002250,
'token1_price_end': 183016664977333417354464783721666},
{'start_ts': 1623002250,
'token1_price_start': 183016664977333417354464783721666,
'end_ts': 1623002355,
'token1_price_end': 183018525945544514538790080485913}]
I now insert two timestamps and two prices into the formula to recalculate the price of token1 denominated in token0 over an interval of one minute:
price = (results[0]['token1_price_end'] - results[0]['token1_price_start']) / (results[0]['end_ts'] - results[0]['start_ts'])
format(price, '.10f')
This returns the following string:
'12193113127504589866663936.0000000000'
At the time of writing this should be around 2800 (1 ETH = 2800 USDC) but I don't know how to get to that number from here. What am I doing wrong?