Uniswap V3
-
In this tutorial, we review the following:
- Swap
- Double-sided withdraw
- Double-sided deposit
- Single-sided withdraw
-
To download notebook to this tutorial, see here
from uniswappy import *import numpy as npimport matplotlib.pyplot as pltimport mathuser_nm = 'user_intro'eth_amount = 1000dai_amount = 1000000fee = UniV3Utils.FeeAmount.MEDIUMtick_spacing = UniV3Utils.TICK_SPACINGS[fee]lwr_tick = UniV3Utils.getMinTick(tick_spacing)upr_tick = UniV3Utils.getMaxTick(tick_spacing)(1a) Swap: dy -> dx
Section titled “(1a) Swap: dy -> dx”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()Eqs (6.13) and (6.15) of Uniswap V3 whitepaper respectively give us:
where
We calculate received to be
where the next price, , derived from input, with fee included () gives us:
Perform dy -> dx swap using dervation
Section titled “Perform dy -> dx swap using dervation”dy = 1000
Q96 = 2**96sqrtp_cur = lp.slot0.sqrtPriceX96/Q96 # convert from Q96 to human
gamma = 997/1000x = lp.get_reserve(eth)y = lp.get_reserve(dai)L = lp.get_liquidity()
sqrtp_next = sqrtp_cur + (gamma*dy) / (L)dx = L * (1/sqrtp_cur - 1/sqrtp_next)
print(f'We receive {dx:.5f} ETH for {dy} DAI')Perform dy -> dx swap using uniswappy
Section titled “Perform dy -> dx swap using uniswappy”out = Swap().apply(lp, dai, user_nm, dy)lp.summary()
print(f'We receive {out:.5f} ETH for {dy} DAI')print(f'Confirm price: (1/sqrtp_next^2)={1/sqrtp_next**2:.8f} vs (actual price)={lp.get_price(dai):.8f}')(1b) Swap: dx -> dy
Section titled “(1b) Swap: dx -> dy”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()Eqs (6.13) and (6.15) of Uniswap V3 whitepaper respectively give us:
where
We calculate received to be:
where the next price, , derived from input, with fee included () give us:
Perform dx -> dy swap using dervation
Section titled “Perform dx -> dy swap using dervation”dx = 1
Q96 = 2**96sqrtp_cur = lp.slot0.sqrtPriceX96/Q96 # convert from Q96 to human
gamma = 997/1000x = lp.get_reserve(eth)y = lp.get_reserve(dai)L = lp.get_liquidity()
sqrtp_next = 1/(1/sqrtp_cur + (gamma*dx)/(L))dy = L * (sqrtp_cur - sqrtp_next)
print(f'We receive {dy:.5f} DAI for {dx} ETH')out = Swap().apply(lp, eth, user_nm, dx)lp.summary()
print(f'We receive {out:.5f} DAI for {dx} ETH')print(f'Confirm price: (sqrtp_next^2)={sqrtp_next**2:.6f} vs (actual price)={lp.get_price(eth):.6f}')(2) Double-sided withdrawal
Section titled “(2) Double-sided withdrawal”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()Double-sided deltas for both x and y are provided in eqs (6.29) and (6.30) in Uniswap V3 whitepaper, which are as such:
Once determined, we update our price curve using the CPT withdraw formula, which is given as:
For derivation of above formula, see here
Calculate withdrawal using derivation
Section titled “Calculate withdrawal using derivation”dx = 1
Q96 = 2**96sqrtp_pa = TickMath.getSqrtRatioAtTick(lwr_tick)/Q96sqrtp_pb = TickMath.getSqrtRatioAtTick(upr_tick)/Q96sqrtp_cur = lp.slot0.sqrtPriceX96/Q96
dPx = (1/sqrtp_cur - 1/sqrtp_pb)dPy = (sqrtp_cur - sqrtp_pa)dLx = dx/(1/sqrtp_cur - 1/sqrtp_pb)
dx = dLx*dPxdy = dLx*dPy
new_x = (x-dx)new_y = (y-dy)new_L = L-dLx
print(f'The updated reserves are {new_x:8f} ETH and {new_y:8f} DAI, and the updated liquidity is {new_L:8f}')Perform withdrawal using uniswappy
Section titled “Perform withdrawal using uniswappy”RemoveLiquidity().apply(lp, eth, user_nm, dx, lwr_tick, upr_tick)lp.summary()(3) Double-sided deposit
Section titled “(3) Double-sided deposit”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()Double-sided deltas for both x and y are provided in eqs (6.29) and (6.30) in Uniswap V3 whitepaper, which are as such:
Once determined, we update our price curve using the CPT deposit formula, which is given as:
For derivation of above formula, see here
Calculate deposit using derivation
Section titled “Calculate deposit using derivation”dx = 1
Q96 = 2**96sqrtp_pa = TickMath.getSqrtRatioAtTick(lwr_tick)/Q96sqrtp_pb = TickMath.getSqrtRatioAtTick(upr_tick)/Q96sqrtp_cur = lp.slot0.sqrtPriceX96/Q96
dPx = (1/sqrtp_cur - 1/sqrtp_pb)dPy = (sqrtp_cur - sqrtp_pa)dLx = dx/(1/sqrtp_cur - 1/sqrtp_pb)
dx = dLx*dPxdy = dLx*dPy
new_x = x+dxnew_y = y+dynew_L = L+dLx
print(f'The updated reserves are {new_x:8f} ETH and {new_y:8f} DAI, and the updated liquidity is {new_L:8f}')Perform deposit using uniswappy
Section titled “Perform deposit using uniswappy”AddLiquidity().apply(lp, eth, user_nm, dx, lwr_tick, upr_tick)lp.summary()(4a) Single-sided withdrawal: dx
Section titled “(4a) Single-sided withdrawal: dx”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()A single-sided withdraw constitutes of the sum total of a withdraw and a swap, otherwise known as a WithdrawSwap, and is given by
,
To solve for we plug and into , after some algebra, we get the following quadratic:
Given the fact we know our desired withdraw amount, , we solve the above quadratic to determine our single-sided settlement amount in terms of
Perform single-sided withdrawal using uniswappy
Section titled “Perform single-sided withdrawal using uniswappy”WithdrawSwap().apply(lp, eth, user_nm, 1, lwr_tick, upr_tick)lp.summary()(4b) Single-sided withdrawal: dy
Section titled “(4b) Single-sided withdrawal: dy”eth = ERC20("ETH", "0x09")dai = ERC20("TKN", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = dai, symbol="LP", address="0x011", version = 'V3', tick_spacing = tick_spacing, fee = fee)
factory = UniswapFactory("ETH pool factory", "0x2")lp = factory.deploy(exchg_data)
Join().apply(lp, user_nm, eth_amount, dai_amount, lwr_tick, upr_tick)lp.summary()A single-sided withdraw constitutes of the sum total of a withdraw and a swap, otherwise known as a WithdrawSwap, and is given by
,
To solve for we plug and into , after some algebra, we get the following quadratic:
Given the fact we know our desired withdraw amount, , we solve the above quadratic to determine our single-sided settlement amount in terms of
Perform single-sided withdrawal using uniswappy
Section titled “Perform single-sided withdrawal using uniswappy”WithdrawSwap().apply(lp, dai, user_nm, 1000, lwr_tick, upr_tick)lp.summary()