ProgressiveCurve
Curve Visualization
%%{init: {"xychart": {"showTitle": true}} }%%
xychart-beta title "Progressive Curve" x-axis "Assets (ETH)" [0.000000000000000000, 1.000000000000000000, 2.000000000000000000, 3.000000000000000000, 4.000000000000000000, 5.000000000000000000, 6.000000000000000000, 7.000000000000000000, 8.000000000000000000, 9.000000000000000000, 10.000000000000000000, 11.000000000000000000, 12.000000000000000000, 13.000000000000000000, 14.000000000000000000, 15.000000000000000000, 16.000000000000000000, 17.000000000000000000, 18.000000000000000000, 19.000000000000000000, 20.000000000000000000, 21.000000000000000000, 22.000000000000000000, 23.000000000000000000, 24.000000000000000000, 25.000000000000000000, 26.000000000000000000, 27.000000000000000000, 28.000000000000000000, 29.000000000000000000, 30.000000000000000000, 31.000000000000000000, 32.000000000000000000, 33.000000000000000000, 34.000000000000000000, 35.000000000000000000, 36.000000000000000000, 37.000000000000000000, 38.000000000000000000, 39.000000000000000000, 40.000000000000000000, 41.000000000000000000, 42.000000000000000000, 43.000000000000000000, 44.000000000000000000, 45.000000000000000000, 46.000000000000000000, 47.000000000000000000, 48.000000000000000000, 49.000000000000000000, 50.000000000000000000, 51.000000000000000000, 52.000000000000000000, 53.000000000000000000, 54.000000000000000000, 55.000000000000000000, 56.000000000000000000, 57.000000000000000000, 58.000000000000000000, 59.000000000000000000, 60.000000000000000000, 61.000000000000000000, 62.000000000000000000, 63.000000000000000000, 64.000000000000000000, 65.000000000000000000, 66.000000000000000000, 67.000000000000000000, 68.000000000000000000, 69.000000000000000000, 70.000000000000000000, 71.000000000000000000, 72.000000000000000000, 73.000000000000000000, 74.000000000000000000, 75.000000000000000000, 76.000000000000000000, 77.000000000000000000, 78.000000000000000000, 79.000000000000000000, 80.000000000000000000, 81.000000000000000000, 82.000000000000000000, 83.000000000000000000, 84.000000000000000000, 85.000000000000000000, 86.000000000000000000, 87.000000000000000000, 88.000000000000000000, 89.000000000000000000, 90.000000000000000000, 91.000000000000000000, 92.000000000000000000, 93.000000000000000000, 94.000000000000000000, 95.000000000000000000, 96.000000000000000000, 97.000000000000000000, 98.000000000000000000, 99.000000000000000000] y-axis "Shares" line [0.000000000000000000, 1.000000000000000000, 1.414213562373095145, 1.732050807568877415, 2.000000000000000000, 2.236067977499789805, 2.449489742783178325, 2.645751311064590272, 2.828427124746190291, 3.000000000000000000, 3.162277660168379523, 3.316624790355399810, 3.464101615137754830, 3.605551275463989125, 3.741657386773941329, 3.872983346207417021, 4.000000000000000000, 4.123105625617660586, 4.242640687119285658, 4.358898943540673976, 4.472135954999579610, 4.582575694955839829, 4.690415759823429731, 4.795831523312720002, 4.898979485566356651, 5.000000000000000000, 5.099019513592784492, 5.196152422706632024, 5.291502622129180544, 5.385164807134503739, 5.477225575051661188, 5.567764362830021518, 5.656854249492380582, 5.744562646538028616, 5.830951894845300743, 5.916079783099616130, 6.000000000000000000, 6.082762530298219339, 6.164414002968976014, 6.244997998398398309, 6.324555320336759046, 6.403124237432848531, 6.480740698407860378, 6.557438524302001248, 6.633249580710799620, 6.708203932499368527, 6.782329983125268136, 6.855654600401044796, 6.928203230275509661, 7.000000000000000000, 7.071067811865475505, 7.141428428542850426, 7.211102550927978250, 7.280109889280518054, 7.348469228349534532, 7.416198487095662983, 7.483314773547882659, 7.549834435270749822, 7.615773105863908654, 7.681145747868608709, 7.745966692414834043, 7.810249675906653977, 7.874007874011811126, 7.937253933193772149, 8.000000000000000000, 8.062257748298549132, 8.124038404635960831, 8.185352771872450361, 8.246211251235321171, 8.306623862918074863, 8.366600265340755627, 8.426149773176359048, 8.485281374238571317, 8.544003745317532150, 8.602325267042626677, 8.660254037844387298, 8.717797887081347952, 8.774964387392122589, 8.831760866327847737, 8.888194417315588680, 8.944271909999159220, 9.000000000000000000, 9.055385138137417300, 9.110433579144299188, 9.165151389911679658, 9.219544457292887074, 9.273618495495703939, 9.327379053088815652, 9.380831519646859462, 9.433981132056604935, 9.486832980505136348, 9.539392014169456147, 9.591663046625440003, 9.643650760992954929, 9.695359714832658682, 9.746794344808964894, 9.797958971132713302, 9.848857801796103928, 9.899494936611665352, 9.949874371066199430]
Inherits: BaseCurve
Author: 0xIntuition
A bonding curve implementation that uses a progressive pricing model where each new share costs more than the last. The price follows the formula: $$P(s) = m \cdot s$$ where:
- $m$ is the slope (in basis points)
- $s$ is the total supply of shares The cost to mint shares is calculated as the area under this curve: $$\text{Cost} = (s_2^2 - s_1^2) \cdot \frac{m}{2}$$ where $s_1$ is the starting share supply and $s_2$ is the final share supply. This curve creates stronger incentives for early stakers compared to the LinearCurve, while maintaining fee-based appreciation.
Uses the prb-math library for fixed point arithmetic with UD60x18
Fixed point precision used for all internal calculations, while return values are all represented as regular uint256s, and unwrapped. I.e. we might use 123.456 internally and return 123.
The core equation: $$P(s) = m \cdot s$$ and the cost equation: $$\text{Cost} = (s_2^2 - s_1^2) \cdot \frac{m}{2}$$ comes from calculus - it's the integral of a linear price function. The area under a linear curve from point $s_1$ to $s_2$ gives us the total cost/return of minting/redeeming shares.
Inspired by the Solaxy.sol contract: https://github.com/M3tering/Solaxy/blob/main/src/Solaxy.sol and https://m3tering.whynotswitch.com/token-economics/mint-and-distribution. * The key difference between the Solaxy contract and this one is that the economic state is handled by the EthMultiVault instead of directly in the curve implementation. * Otherwise the math is identical.
State Variables
SLOPE
The slope of the curve, in basis points. This is the rate at which the price of shares increases.
0.0025e18 -> 25 basis points, 0.0001e18 = 1 basis point, etc etc
If minDeposit is 0.003 ether, this value would need to be 0.00007054e18 to avoid returning 0 shares for minDeposit assets
UD60x18 public SLOPE;
HALF_SLOPE
The half of the slope, used for calculations.
UD60x18 public HALF_SLOPE;
MAX_SHARES
Since powu(2) will overflow first (see slope equation), maximum totalShares is sqrt(MAX_UD60x18)
uint256 public MAX_SHARES;
MAX_ASSETS
The maximum assets is totalShares * slope / 2, because multiplication (see slope equation) would overflow beyond that point.
uint256 public MAX_ASSETS;
Functions
constructor
Constructs a new ProgressiveCurve with the given name and slope
Computes maximum values given constructor arguments
Computes Slope / 2 as commonly used constant
constructor(string memory _name, uint256 slope18) BaseCurve(_name);
Parameters
Name | Type | Description |
---|---|---|
_name | string | The name of the curve (i.e. "Progressive Curve #465") |
slope18 | uint256 | The slope of the curve, in basis points (i.e. 0.0025e18) |
previewDeposit
Preview how many shares would be minted for a deposit of assets
Let $s$ = current total supply
Let $a$ = amount of assets to deposit
Let $\frac{m}{2}$ = half of the slope
shares: $$\text{shares} = \sqrt{s^2 + \frac{a}{m/2}} - s$$
or to say that another way: $$\text{shares} = \sqrt{s^2 + \frac{2a}{m}} - s$$
function previewDeposit(uint256 assets, uint256, uint256 totalShares) external view override returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | Quantity of assets to deposit |
<none> | uint256 | |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares that would be minted |
previewRedeem
Preview how many assets would be returned for burning a specific amount of shares
Let $s$ = initial total supply of shares
Let $r$ = shares to redeem
Let $\frac{m}{2}$ = half of the slope
assets: $$\text{assets} = (s^2 - (s-r)^2) \cdot \frac{m}{2}$$
this can be expanded to: $$\text{assets} = (s^2 - (s^2 - 2sr + r^2)) \cdot \frac{m}{2}$$
which simplifies to: $$\text{assets} = (2sr - r^2) \cdot \frac{m}{2}$$
function previewRedeem(uint256 shares, uint256 totalShares, uint256) public view override returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | Quantity of shares to burn |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
<none> | uint256 |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The number of assets that would be returned |
previewMint
Preview how many assets would be required to mint a specific amount of shares
Let $s$ = current total supply of shares
Let $n$ = new shares to mint
Let $\frac{m}{2}$ = half of the slope
assets: $$\text{assets} = ((s + n)^2 - s^2) \cdot \frac{m}{2}$$
which can be expanded to: $$\text{assets} = (s^2 + 2sn + n^2 - s^2) \cdot \frac{m}{2}$$
which simplifies to: $$\text{assets} = (2sn + n^2) \cdot \frac{m}{2}$$
function previewMint(uint256 shares, uint256 totalShares, uint256) external view override returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | Quantity of shares to mint |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
<none> | uint256 |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The number of assets that would be required to mint the shares |
previewWithdraw
Preview how many shares would be redeemed for a withdrawal of assets
Let $s$ = current total supply of shares
Let $a$ = assets to withdraw
Let $\frac{m}{2}$ = half of the slope
shares: $$\text{shares} = s - \sqrt{s^2 - \frac{a}{m/2}}$$
or to say that another way: $$\text{shares} = s - \sqrt{s^2 - \frac{2a}{m}}$$
function previewWithdraw(uint256 assets, uint256, uint256 totalShares)
external
view
override
returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | Quantity of assets to withdraw |
<none> | uint256 | |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares that would need to be redeemed |
currentPrice
Get the current price of a share
Let $s$ = current total supply of shares
Let $m$ = the slope of the curve
sharePrice: $$\text{sharePrice} = s \cdot m$$
This is the basic linear price function where the price increases linearly with the total supply
And the slope ($m$) determines how quickly the price increases
TLDR: Each new share costs more than the last
function currentPrice(uint256 totalShares) public view override returns (uint256 sharePrice);
Parameters
Name | Type | Description |
---|---|---|
totalShares | uint256 | Total quantity of shares already awarded by the curve |
Returns
Name | Type | Description |
---|---|---|
sharePrice | uint256 | The current price of a share, scaled by 1e18 |
convertToShares
Convert assets to shares at a specific point on the curve
Let $s$ = current total supply of shares
Let $a$ = assets to convert to shares
Let $\frac{m}{2}$ = half of the slope
shares: $$\text{shares} = \frac{a}{s \cdot m/2}$$
Or to say that another way: $$\text{shares} = \frac{2a}{s \cdot m}$$
function convertToShares(uint256 assets, uint256, uint256 totalShares)
external
view
override
returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | Quantity of assets to convert to shares |
<none> | uint256 | |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The number of shares equivalent to the given assets |
convertToAssets
Convert shares to assets at a specific point on the curve
Let $s$ = current total supply of shares
Let $n$ = quantity of shares to convert to assets
conversion price: $$\text{price} = s \cdot \frac{m}{2}$$
where $\frac{m}{2}$ is average price per share
assets: $$\text{assets} = n \cdot (s \cdot \frac{m}{2})$$
Or to say that another way: $$\text{assets} = n \cdot s \cdot \frac{m}{2}$$
function convertToAssets(uint256 shares, uint256 totalShares, uint256)
external
view
override
returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | Quantity of shares to convert to assets |
totalShares | uint256 | Total quantity of shares already awarded by the curve |
<none> | uint256 |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The number of assets equivalent to the given shares |
_convertToAssets
Computes assets as the area under a linear curve with a simplified form of the area of a trapezium: $$f(x) = mx + c$$ $$\text{Area} = \frac{1}{2} \cdot (a + b) \cdot h$$ where $a$ and $b$ can be both $f(\text{juniorSupply})$ or $f(\text{seniorSupply})$ depending if used in minting or redeeming. Calculates area as: $$(\text{seniorSupply}^2 - \text{juniorSupply}^2) \cdot \text{halfSlope}$$ where: $$\text{halfSlope} = \frac{\text{slope}}{2}$$
function _convertToAssets(UD60x18 juniorSupply, UD60x18 seniorSupply) internal view returns (UD60x18 assets);
Parameters
Name | Type | Description |
---|---|---|
juniorSupply | UD60x18 | The smaller supply in the operation (the initial supply during mint, or the final supply during a redeem operation). |
seniorSupply | UD60x18 | The larger supply in the operation (the final supply during mint, or the initial supply during a redeem operation). |
Returns
Name | Type | Description |
---|---|---|
assets | UD60x18 | The computed assets as an instance of UD60x18 (a fixed-point number). |
maxShares
The maximum number of shares that this curve can handle without overflowing.
Checked by the EthMultiVault before transacting
function maxShares() external view override returns (uint256);
maxAssets
The maximum number of assets that this curve can handle without overflowing.
Checked by the EthMultiVault before transacting
function maxAssets() external view override returns (uint256);