Swap-Deposit
[15]:
import os
import math
import numpy as np
from termcolor import colored
[16]:
from uniswappy import *
[17]:
user_nm = 'user0'
eth_amount = 1000
usdc_amount = 1000000
Problem Defined
A
SwapDeposit
is where a certain amount of a specific token is deposited into the LP under one operationIncludes two steps:
(step 1) perform approx. 50% swap for opposing token
(step 2) using amt from step 1, perform 1:1 deposit
A portion of the incoming funds are swapped to achieve equal portions of both assets
These portions are then deposited into the LP
To ensure all the funds are deposited, we must determine the portion (\(\alpha\)) of \(s_{in}\) that must first get swapped
\begin{equation} \Delta x_{sd} = \Delta x_{swap} + \Delta x_{deposit} \end{equation}
\begin{equation} \Delta x_{sd} = \alpha \Delta x_{sd} + \frac{\Delta y_{s}(x+\Delta x_{sd})}{y - \Delta y_{s}} \end{equation}
Follows this system of equations:
\begin{equation} \Delta y_{s} = \frac{997 y \alpha \Delta x }{1000x + 997\alpha\Delta x } \tag{Eq. 1} \end{equation}
\begin{equation} \Delta x_{sd} = \alpha\Delta x_{sd} + \frac{\Delta y_{s}(x + \alpha\Delta x_{sd} )}{y - \Delta y_{s}} \tag{Eq. 2} \end{equation}
where
\(\Delta x_{sd}\) -> amt token in
\(\Delta y_{s}\) -> amt opposing token out after swap
\(\alpha\) -> portion of \(\Delta x\) swapped in
x -> reserve0
y -> reserve1
Let’s highlight why the above considerations are important …
[18]:
eth = ERC20("ETH", "0x09")
usdc = ERC20("USDC", "0x111")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = usdc, symbol="LP", address="0x011")
factory = UniswapFactory("ETH pool factory", "0x2")
lp = factory.deploy(exchg_data)
lp.add_liquidity(user_nm, eth_amount, usdc_amount, eth_amount, usdc_amount)
print('***\nInitial LP\n***')
lp.summary()
s_in = 100
alpha = 0.5
s_out = Swap().apply(lp, eth, user_nm, alpha*s_in)
print('***\nLP post step 1\n***')
lp.summary()
balance0 = alpha*s_in
balance1 = lp.quote(balance0, lp.reserve0, lp.reserve1)
lp.add_liquidity(user_nm, balance0, balance1, balance0, balance1)
print('***\nLP post step 2\n***')
lp.summary()
print('Given {} initial ETH:'.format(s_in))
print(' (step 1) {} ETH must first get swapped for {} '.format(alpha*s_in, s_out))
print(' (step 2) The received TKN gets deposited along with the remaining {} ETH'.format(balance0))
print('\nTotal deposited is {:.6f} + {:.6f} = {:.6f} ETH:'.format(alpha*s_in, balance0, alpha*s_in + balance0))
print('However, we have {} unaccounted USDC which need to be considered when using a 50/50 split'.format(colored(str(usdc_amount-lp.get_reserve(usdc)), 'red', attrs=['bold'])))
***
Initial LP
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1000.0, USDC = 1000000.0
Liquidity: 31622.776601683792
***
LP post step 1
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1050.0, USDC = 952517.026241844
Liquidity: 31622.776601683792
***
LP post step 2
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1100.0, USDC = 997874.9798724081
Liquidity: 33128.623106525876
Given 100 initial ETH:
(step 1) 50.0 ETH must first get swapped for 47482.973758155924
(step 2) The received TKN gets deposited along with the remaining 50.0 ETH
Total deposited is 50.000000 + 50.000000 = 100.000000 ETH:
However, we have 2125.02012759191 unaccounted USDC which need to be considered when using a 50/50 split
Solve Problem
Let’s now address this problem …
[19]:
usdc = ERC20("TKN", "0x111")
eth = ERC20("ETH", "0x09")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = usdc, symbol="LP", address="0x011")
factory = UniswapFactory("ETH pool factory", "0x2")
lp = factory.deploy(exchg_data)
lp.add_liquidity(user_nm, eth_amount, usdc_amount, eth_amount, usdc_amount)
s_in = 100
alpha = 0.5
y = lp.get_reserve(usdc)
x = lp.get_reserve(eth)
Plug above into equation (1), and see how many TKN we get when 50% of ETH is swapped in for step (1)
[20]:
s_out = (997*alpha*s_in*y)/(1000*x + 997*alpha*s_in)
print('For {} ETH, we get {:.2f} TKN with a 50% portion'.format(s_in, s_out))
For 100 ETH, we get 47482.97 TKN with a 50% portion
Now, lets check how many ETH gets SwapDeposited in when 50% of ETH is swapped in for step (1)
[21]:
a1_out = alpha*s_in + s_out*(x + alpha*s_in)/(y - s_out)
print('Instead of {} ETH, we get {:.2f} ETH under a 50% portion'.format(s_in, a1_out))
Instead of 100 ETH, we get 102.34 ETH under a 50% portion
We can see that there is an imbalance in the system under a 50% distribution for step (1); * we need to solve the system above for \(\alpha\) to get the proper distribution * plug (1) into (2) and we get:
\begin{equation} \Delta x = \alpha\Delta x + \left(\frac{997 y\alpha\Delta x}{1000x + 997\alpha\Delta x} \right) \left(\frac{ x + \alpha\Delta x}{y - \frac{997 y \alpha \Delta x}{1000x + 997\alpha\Delta x}} \right) \end{equation}
reduces to:
\begin{equation} \alpha^2 \frac{997 \Delta x^2}{1000x} + \alpha\frac{1997\Delta x}{1000} - \Delta x = 0 \end{equation}
Now, solve for, and we can calculate the correct distribution using calc_deposit_dist
[22]:
def calc_deposit_portion(lp, token_in, dx):
tokens = lp.factory.token_from_exchange[lp.name]
if(token_in.token_name == lp.token0):
tkn_supply = lp.get_reserve(tokens[lp.token0])
else:
tkn_supply = lp.get_reserve(tokens[lp.token1])
a = 997*(dx**2)/(1000*tkn_supply)
b = dx*(1997/1000)
c = -dx
alpha = -(b - np.sqrt(b*b - 4*a*c)) / (2*a)
return alpha
[23]:
alpha = calc_deposit_portion(lp, eth, s_in)
print('The correct swap distrbution (for step 1) is {}'.format(alpha))
The correct swap distrbution (for step 1) is 0.4888217399419355
Now, check against our reduced quadratic, and we should expect to get 0
[24]:
997*(alpha**2)*(s_in**2)/(1000*x) + alpha*s_in*(1997/1000) - s_in
[24]:
-5.684341886080802e-14
Final Steps
Finally, lets run through the steps to a SwapDeposit
and compare above
[25]:
usdc = ERC20("USDC", "0x111")
eth = ERC20("ETH", "0x09")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = usdc, symbol="LP", address="0x011")
factory = UniswapFactory("ETH pool factory", "0x2")
lp = factory.deploy(exchg_data)
lp.add_liquidity(user_nm, eth_amount, usdc_amount, eth_amount, usdc_amount)
print('***\nInitial LP\n***')
lp.summary()
s_in = 100
alpha = calc_deposit_portion(lp, eth, s_in)
s_out = Swap().apply(lp, eth, user_nm, alpha*s_in)
print('***\nLP post step 1\n***')
lp.summary()
balance1 = s_out
balance0 = s_in-alpha*s_in
lp.add_liquidity(user_nm, balance0, balance1, balance0, balance1)
print('***\nLP post step 2\n***')
lp.summary()
print('Given {} initial ETH:'.format(s_in))
print(' (step 1) {} ETH must first get swapped for {} USDC'.format(alpha*s_in, s_out))
print(' (step 2) The received USDC gets deposited along with the remaining {} ETH'.format(balance0))
print('\nTotal deposited is {:.6f} + {:.6f} = {:.6f} ETH:'.format(alpha*s_in, balance0, alpha*s_in + balance0))
***
Initial LP
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1000.0, USDC = 1000000.0
Liquidity: 31622.776601683792
***
LP post step 1
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1048.8821739941936, USDC = 953529.2490856305
Liquidity: 31622.776601683792
***
LP post step 2
***
Exchange ETH-USDC (LP)
Reserves: ETH = 1100.0, USDC = 1000000.0
Liquidity: 33163.92929950274
Given 100 initial ETH:
(step 1) 48.88217399419355 ETH must first get swapped for 46470.75091436945 USDC
(step 2) The received USDC gets deposited along with the remaining 51.11782600580645 ETH
Total deposited is 48.882174 + 51.117826 = 100.000000 ETH:
Final Check
Finally, let’s check when our solution is integrated into SwapDeposit
[26]:
usdc = ERC20("USDC", "0x111")
eth = ERC20("ETH", "0x09")
exchg_data = UniswapExchangeData(tkn0 = eth, tkn1 = usdc, symbol="LP", address="0x011")
factory = UniswapFactory("ETH pool factory", "0x2")
lp = factory.deploy(exchg_data)
lp.add_liquidity(user_nm, eth_amount, usdc_amount, eth_amount, usdc_amount)
lp.summary()
s_in = 100
dep = SwapDeposit().apply(lp, eth, user_nm, s_in)
lp.summary()
Exchange ETH-USDC (LP)
Reserves: ETH = 1000.0, USDC = 1000000.0
Liquidity: 31622.776601683792
Exchange ETH-USDC (LP)
Reserves: ETH = 1100.0, USDC = 1000000.0
Liquidity: 33163.92929950274