ProgressiveCurve

Git Source

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

NameTypeDescription
_namestringThe name of the curve (i.e. "Progressive Curve #465")
slope18uint256The 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

NameTypeDescription
assetsuint256Quantity of assets to deposit
<none>uint256
totalSharesuint256Total quantity of shares already awarded by the curve

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
sharesuint256Quantity of shares to burn
totalSharesuint256Total quantity of shares already awarded by the curve
<none>uint256

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
sharesuint256Quantity of shares to mint
totalSharesuint256Total quantity of shares already awarded by the curve
<none>uint256

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
assetsuint256Quantity of assets to withdraw
<none>uint256
totalSharesuint256Total quantity of shares already awarded by the curve

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
totalSharesuint256Total quantity of shares already awarded by the curve

Returns

NameTypeDescription
sharePriceuint256The 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

NameTypeDescription
assetsuint256Quantity of assets to convert to shares
<none>uint256
totalSharesuint256Total quantity of shares already awarded by the curve

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
sharesuint256Quantity of shares to convert to assets
totalSharesuint256Total quantity of shares already awarded by the curve
<none>uint256

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
juniorSupplyUD60x18The smaller supply in the operation (the initial supply during mint, or the final supply during a redeem operation).
seniorSupplyUD60x18The larger supply in the operation (the final supply during mint, or the initial supply during a redeem operation).

Returns

NameTypeDescription
assetsUD60x18The 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);