owned this note
owned this note
Published
Linked with GitHub
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
# Power-Invariant Bonding Curve Derivations and Definitions
## Motivation
Analysis of Augmented Bonding curve subsystem for "Commons Stack"
![](https://i.imgur.com/3Ov4H8N.jpg)
## Export from Python notebook by mZargham
Notebook header cell:
```python
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
%matplotlib inline
```
### System timeline
- hatch sale: raise funds to initialize curve
- funded reference implementation launch
- post lauch system evolution modeled elsewhere
![](https://i.imgur.com/2eTfRey.jpg)
![](https://i.imgur.com/qA344p0.jpg)
![](https://i.imgur.com/oczA0Gb.jpg)
### Hatch Sale Equations
- Initial Raise $d_0$ Dai
- Initial split between the Bonding Curve Reserve and the Funding pool is $\theta$
- Initial Reserve is $R_0 = (1-\theta) d_0$
- Hatch sale Price $p_0$ (dai per token) determines the initial supply: $S_0 = d_0/p_0$
- Power Function Invariant shape: $V(R, S) = \frac{S^\kappa}{R}$
- Price function (DAI per Token): $P(R) = \frac{\kappa R^{(\kappa-1)/ \kappa}}{V_0^{1 / \kappa}}$
- Supply function (tokens): $S(R) = \sqrt[\kappa]{V_0 R}$
- Reserve function (xDAI): $R(S) = \frac{S^{\kappa}}{V_0}$
- The invariant coef: $V_0 = V(R_0, S_0) = \frac{S_0^\kappa}{R_0} = \left(\frac{1}{p_0(1-\theta)}\right)^\kappa R_0^{\kappa-1}$
- The post hatch price: $p_1=P(R_0) = \frac{\kappa R_0^{(\kappa-1)/ \kappa}}{V_0^{1 / \kappa}} = \kappa R_0^{(\kappa-1)/ \kappa} \cdot(1-\theta)p_0\cdot R_0^{-(\kappa-1)/\kappa} = \kappa(1-\theta) p_0$
- The Return factor: $\frac{p_1}{p_0} = {\kappa}(1-\theta)$
### Invariant Preserving Bond-to-Mint
- Deposit $\Delta R$ xdai
- Conservation equation: $V(R+ \Delta R, S+\Delta S) = \frac{(S+\Delta S)^\kappa}{R+\Delta R} =V_0$
- Derived Mint equation: $\Delta S = mint\big(\Delta R ; (R,S)\big)= \sqrt[\kappa]{V_0(R+\Delta R)}-S$
- Realized Price is: $\bar{P}(\Delta R) =\frac{\Delta R}{\Delta S} = \frac{\Delta R}{\sqrt[\kappa]{V_0(R+\Delta R)}-\sqrt[\kappa]{V_0(R)}} \rightarrow \big(\frac{\partial S(R)}{\partial R} \big)^{-1}$ as $\Delta R \rightarrow 0$
- The limiting price is the spot price: $\lim_{\Delta R \rightarrow 0} \bar{P}(\Delta R)=\big(\frac{\partial S(R)}{\partial R}\big)^{-1}= \big(\frac{V_0^{1/\kappa} \cdot R^{1/\kappa-1}}{\kappa}\big)^{-1}= \frac{\kappa R^{1-1/\kappa}}{V_0^{1/\kappa}} = \frac{\kappa R^{(\kappa-1)/\kappa}}{V_0^{1/\kappa}} =P(R)$
### Invariant Preserving Burn-to-Withdraw
- Burn $\Delta S$ tokens
- Conservation equation: $V(R- \Delta R, S-\Delta S) = \frac{(S-\Delta S)^\kappa}{R-\Delta R} =V_0$
- Derived Withdraw equation: $\Delta R = withdraw\big(\Delta S ; (R,S)\big)= R-\frac{(S-\Delta S)^\kappa}{V_0}$
- Realized Price is: $\bar{P}(\Delta S) =\frac{\Delta R}{\Delta S} = \frac{\frac{S^{\kappa}}{V_0}-\frac{(S-\Delta S)^\kappa}{V_0}}{\Delta S} \rightarrow \frac{\partial R(S)}{\partial S}$ as $\Delta S \rightarrow 0$
- The limiting price is the spot price: $\lim_{\Delta S \rightarrow 0} \bar{P}(\Delta S)=\frac{\partial R(S)}{\partial S}=\frac{\kappa S^{\kappa-1}}{V_0} = \frac{\kappa \cdot (\sqrt[\kappa]{V_0 R})^{\kappa-1}}{V_0}= \frac{\kappa R^{(\kappa-1)/\kappa}}{V_0^{1/\kappa}}=P(R)$
- Given friction coef $\phi$
- sent to burning agent address: $\Delta R_{agent} = (1-\phi) \Delta R$
- sent to the funding pool address: $\Delta R_{pool} = \phi \Delta R$
- due to the friction the true realized price for the agent is $(1-\phi)\cdot \bar{P}(\Delta S)$
- due to the friction the true return factor post withdraw is: ${\kappa}(1-\theta)(1-\phi)$
- note that immediate withdraw will not be available for hatch sale participants; tokens will vest slowly as a precaution against large batch dumping. Details of vesting schedules and related dynamics modeled elsewhere.
```python
#integer_units = 10**12 #account for decimal places to a token
#scale_units = 10**6 #millions of tokens, million of DAI
#mu = integer_units*scale_units
#value function for a given state (R,S)
def invariant(R,S,kappa):
return (S**kappa)/R
#given a value function (parameterized by kappa)
#and an invariant coeficient V0
#return Supply S as a function of reserve R
def supply(R, kappa, V0):
return (V0*R)**(1/kappa)
#given a value function (parameterized by kappa)
#and an invariant coeficient V0
#return a spot price P as a function of reserve R
def spot_price(R, kappa, V0):
return kappa*R**((kappa-1)/kappa)/V0**(1/kappa)
#for a given state (R,S)
#given a value function (parameterized by kappa)
#and an invariant coeficient V0
#deposit deltaR to Mint deltaS
#with realized price deltaR/deltaS
def mint(deltaR, R,S, kappa, V0):
deltaS = (V0*(R+deltaR))**(1/kappa)-S
realized_price = deltaR/deltaS
return deltaS, realized_price
#for a given state (R,S)
#given a value function (parameterized by kappa)
#and an invariant coeficient V0
#burn deltaS to Withdraw deltaR
#with realized price deltaR/deltaS
def withdraw(deltaS, R,S, kappa, V0):
deltaR = R-((S-deltaS)**kappa)/V0
realized_price = deltaR/deltaS
return deltaR, realized_price
```
```python
d0 = 5 #million DAI
p0 = .01 #DAI per tokens
theta = .4
R0 = d0*(1-theta) #million DAI
S0 = d0/p0
kappa = 6
V0 = invariant(R0,S0,kappa)
reserve = np.arange(0,100,.01)
supp = np.array([supply(r,kappa, V0) for r in reserve])
price = np.array([spot_price(r,kappa, V0) for r in reserve])
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel('Reserve (Millions of xDAI)')
ax1.set_ylabel('Supply (Millions of Tokens)', color=color)
ax1.plot(reserve, supp,'--', color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('Price in xDAI per Token', color=color) # we already handled the x-label with ax1
ax2.plot(reserve, price,'-.', color=color)
ax2.tick_params(axis='y', labelcolor=color)
ax1.vlines(R0,0,supp[-1], alpha=.5)
ax1.text(R0+.02*reserve[-1], supp[-1], "Initial Value R="+str(int(100*R0)/100)+" mil xDAI")
ax1.text(R0+.02*reserve[-1], .95*supp[-1], "Initial Value S="+str(S0)+" mil Tokens")
#ax1.hlines(S0,0,R0)
ax2.text(R0+.02*reserve[-1], price[3], "Initial Value p1="+str(int(100*spot_price(R0,kappa,V0))/100))
plt.title('Augmented Bonding Curve with Invariant S^'+str(kappa)+'/R')
fig.tight_layout() # otherwise the right y-label is slightly clipped
plt.show()
```
![](https://i.imgur.com/558HG6S.png)
```python
fig, ax1 = plt.subplots()
cp = 100
color = 'tab:red'
ax1.set_xlabel('Supply (Millions of Tokens)')
ax1.set_ylabel('Reserve (Millions of xDAI)', color=color)
ax1.plot(supp[cp:], reserve[cp:],'--', color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('Price in xDAI per Token', color=color) # we already handled the x-label with ax1
ax2.plot(supp[cp:], price[cp:],'-.', color=color)
ax2.tick_params(axis='y', labelcolor=color)
ax1.vlines(S0,0,reserve[-1], alpha=.5)
ax1.text(S0*1.02, reserve[-1], "Initial Value S="+str(int(100*S0)/100)+" mil tokens")
ax1.text(S0*1.02, .95*reserve[-1], "Initial Value R="+str(R0)+" mil xDAI")
#ax1.hlines(S0,0,R0)
ax2.text(S0*1.02, price[3], "Initial Value p1="+str(int(100*spot_price(R0,kappa,V0))/100))
plt.title('Augmented Bonding Curve with Invariant S^'+str(kappa)+'/R')
fig.tight_layout() # otherwise the right y-label is slightly clipped
plt.show()
```
![](https://i.imgur.com/eggmFU6.png)
```python
#given V0 and kappa
#sweep the reserve
reserve = None
reserve = np.arange(.01,100,.01)
price = np.array([spot_price(r,kappa, V0) for r in reserve])
#realized price for withdrawing burning .1% of tokens
withdraw_price=[withdraw(supply(r,kappa,V0)/1000, r,supply(r,kappa,V0), kappa, V0)[1] for r in reserve]
#realized price for depositing .1% more Xdai into the reserve
mint_price=[mint(r/1000, r, supply(r,kappa,V0), kappa, V0)[1] for r in reserve]
```
```python
from IPython.display import Image
Image(filename='slippage.jpeg')
```
![](https://i.imgur.com/2jZGIcm.jpg)
```python
pdf = pd.DataFrame({'reserve':reserve, 'spot_price':price, '.1% mint_price':mint_price,'.1% withdraw_price':withdraw_price })
```
```python
pdf.plot(x='reserve')
```
<matplotlib.axes._subplots.AxesSubplot at 0x1a21eee160>
![](https://i.imgur.com/3wjvui6.png)
```python
pdf['mint_slippage'] = (pdf['.1% mint_price']-pdf['spot_price'])/pdf['spot_price']
pdf['withdraw_slippage'] = (pdf['spot_price']-pdf['.1% withdraw_price'])/pdf['spot_price']
```
```python
pdf.plot(x='reserve', y = ['mint_slippage', 'withdraw_slippage'])#, logy=True)
```
<matplotlib.axes._subplots.AxesSubplot at 0x1a22526358>
![](https://i.imgur.com/3gzbGT3.png)
```python
#given V0 and kappa
R = 20
S = supply(R,kappa,V0)
p = spot_price(R,kappa,V0)
#sweep the transaction fraction
TXF = np.logspace(-6, 0, num=1000)
#realized price for withdrawing burning .1% of tokens
withdraw_price2=[withdraw(S*txf, R,S, kappa, V0)[1] for txf in TXF]
#realized price for depositing .1% more Xdai into the reserve
mint_price2=[mint(R*txf, R,S, kappa, V0)[1] for txf in TXF]
```
```python
print(S)
```
685.9431568581422
```python
pdf2 = pd.DataFrame({'tx_fraction':TXF, 'spot_price':p*np.ones(len(TXF)), 'mint_price':mint_price2,'withdraw_price':withdraw_price2 })
```
```python
pdf2.plot(x='tx_fraction',y=['mint_price','withdraw_price','spot_price'], logx=True)
```
<matplotlib.axes._subplots.AxesSubplot at 0x1a2255b780>
![](https://i.imgur.com/0ZWEfeQ.png)
```python
pdf2['mint_slippage'] = (pdf2['mint_price']-pdf2['spot_price'])/pdf2['spot_price']
pdf2['withdraw_slippage'] = (pdf2['spot_price']-pdf2['withdraw_price'])/pdf2['spot_price']
```
```python
pdf2.plot(x='tx_fraction', y = ['mint_slippage', 'withdraw_slippage'], logx=True, logy=True)
```
<matplotlib.axes._subplots.AxesSubplot at 0x1a22b0ae48>
![](https://i.imgur.com/UMYDazc.png)
```python
Kappa_List = [2,4,6,8]
for kappa in Kappa_List:
V0 = invariant(R0,S0,kappa)
reserve = np.arange(0,100,.01)
supp = np.array([supply(r,kappa, V0) for r in reserve])
price = np.array([spot_price(r,kappa, V0) for r in reserve])
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel('Reserve (Millions of xDAI)')
ax1.set_ylabel('Supply (Millions of Tokens)', color=color)
ax1.plot(reserve, supp,'--', color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
color = 'tab:blue'
ax2.set_ylabel('Price in xDAI per Token', color=color) # we already handled the x-label with ax1
ax2.plot(reserve, price,'-.', color=color)
ax2.tick_params(axis='y', labelcolor=color)
ax1.vlines(R0,0,supp[-1], alpha=.5)
ax1.text(R0+.02*reserve[-1], supp[-1], "Initial Value R="+str(int(100*R0)/100)+" mil xDAI")
ax1.text(R0+.02*reserve[-1], .95*supp[-1], "Initial Value S="+str(int(100*S0)/100)+" mil Tokens")
#ax1.hlines(S0,0,R0)
ax2.text(R0+.02*reserve[-1], price[3], "Initial Value p1="+str(int(1000*spot_price(R0,kappa,V0))/1000))
plt.title('Augmented Bonding Curve with Invariant S^'+str(kappa)+'/R')
fig.tight_layout() # otherwise the right y-label is slightly clipped
plt.show()
```
![](https://i.imgur.com/iCTVp6J.png)
![](https://i.imgur.com/N0nCf2F.png)
![](https://i.imgur.com/YRRBm0E.png)
![](https://i.imgur.com/vDtjQGX.png)
```python
#Power function independent variables for analysis
vec_d0 = np.arange(2.5,5.1,.1) #millon dai
vec_theta = np.arange(.1,.55,.05) #unitless
mat_R0 = np.outer(vec_d0.T, (1-vec_theta)) #million dai
vec_p0 = np.arange(.01,.11,.01) #dai per token
mat_S0 = np.outer(vec_d0.T, vec_p0) #milion tokens
vec_kappa = np.arange(2,9,1) #integer
mat_return_ratio = np.outer(vec_kappa.T, (1-vec_theta))
```
```python
p0_lab = [str(int(100*p)/100) for p in vec_p0]
th_lab = [str(int(100*th)/100) for th in vec_theta]
k_lab = [str(k) for k in vec_kappa]
sns.heatmap(mat_return_ratio.T,yticklabels=th_lab, xticklabels=k_lab, annot=True)
plt.yticks(rotation=0)
plt.xlabel('Invariant Power: Kappa')
plt.ylabel('Funding Pool Fraction: Theta')
plt.title('Hatch Return Rate p1/p0')
```
Text(0.5, 1.0, 'Hatch Return Rate p1/p0')
![](https://i.imgur.com/7thgXTc.png)
```python
d_lab = [str(int(100*d)/100) for d in vec_d0]
sns.heatmap(vec_d0-mat_R0.T,yticklabels=th_lab, xticklabels=d_lab)#, annot=True)
plt.yticks(rotation=0)
plt.xlabel('Initial Fundraise: d0 (Millions of xDAI)')
plt.ylabel('Funding Pool Fraction: Theta')
plt.title('Funding Pool Funds at Launch (Millions of xDAI)')
```
Text(0.5, 1.0, 'Funding Pool Funds at Launch (Millions of xDAI)')
![](https://i.imgur.com/5m1y14p.png)
```python
d_lab = [str(int(100*d)/100) for d in vec_d0]
sns.heatmap(mat_R0.T,yticklabels=th_lab, xticklabels=d_lab)#, annot=True)
plt.yticks(rotation=0)
plt.title('Initial Reserve: R0 (Millions of xDAI)')
plt.ylabel('Funding Pool Fraction: Theta')
plt.xlabel('Intial Raise d0 (Millions of xDAI)')
```
Text(0.5, 15.0, 'Intial Raise d0 (Millions of xDAI)')
![](https://i.imgur.com/2kfKCET.png)
```python
sns.heatmap(mat_S0.T,yticklabels=p0_lab, xticklabels=d_lab)#, annot=True)
plt.yticks(rotation=0)
plt.title('Initial Supply: S0 (Millions of Tokens)')
plt.ylabel('Hatch Sale Price: p0 (xDAI per Token)')
plt.xlabel('Intial Raise d0 (Millions of xDAI)')
```
Text(0.5, 15.0, 'Intial Raise d0 (Millions of xDAI)')
![](https://i.imgur.com/qD5XlYZ.png)
```python
```