the bonding curve

closed-form forward and inverse curves. eth in mints sato; sato in burns and pays eth out. fixed-point arithmetic, no oracle.

sato is minted along an exponential bonding curve parameterized by two constants: the asymptote K = 21,000,000 sato and the scale S = 500 eth. cumulative eth spent into the curve is the single state variable; everything else derives from it.

forward curve

the cumulative supply minted after eth total eth has been spent is:

q(eth) = K * (1 - e^(-eth / S))

at eth = 0, supply is 0. at eth = S, supply is K · (1 − 1/e) &approx; 63.2% of K. as eth → ∞, supply approaches K but never reaches it analytically. in solidity it saturates: when eth / S ≥ 50, e^(−eth/S) < 2 × 10⁻²², which is below the prb-mathUD60x18 precision floor of 1e-18, so the contract returns exactly K_SUPPLY.

inverse curve

given a target supply q, the cumulative eth required to have minted it is:

eth(q) = -S * ln(1 - q / K)
        = S * ln( K / (K - q) )

this is what the contract evaluates whenever it needs to know “how much eth has the curve absorbed if its position is q?” — for instance, when computing the eth owed to a seller.

sell formula

a seller burns satoIn tokens and receives eth. the curve position before the sell is q; after, it is q − satoIn. the eth paid out is the difference between the two inverse-curve evaluations, which simplifies to a clean log:

ethOut(q, satoIn) =
    S * ln( (K - q + satoIn) / (K - q) )

the same formula handles both small and large sells. nothing forks on size. the only failure modes are domain errors: selling more than currently circulates (SellExceedsSupply) or trying to sell when the denominator K − q has rounded to zero (InverseDomainError) — both impossible in practice within the saturation bounds.

marginal price

the price of the next infinitesimal sato at curve position eth is the derivative of the inverse curve:

marginalPrice(eth) = deth / dq
                  = S * e^(eth / S) / K
                  = ETH per SATO at curve position eth

at eth = 0, the marginal price is S/K &approx; 2.38 × 10⁻⁵ eth/sato— the cheapest sato ever exist. as the curve moves forward, the price grows exponentially with each additional eth absorbed. by eth = S, price is e &approx; 2.72× the opening; by eth = 5S, 148×; by eth = 10S, 22,000×.

key properties

monotonic
supply is strictly non-decreasing in eth. every buy moves the curve strictly forward; sells move it strictly backward.
path-independent
two buyers who together spend the same total eth get the same total sato, regardless of the order or sizing of their buys. splitting a buy across blocks costs the same as buying in one shot (outside the entropy window).
reversible
burning sato along the inverse curve always returns exactly the eth that would put the curve back at its prior position. the system never accidentally accumulates eth from rounding; fees are tracked separately.
saturating
arithmetic precision (1e-18) sets a hard floor long before K is mathematically reached. in practice the curve treats itself as exhausted around eth/S = 50, well past any plausible inflow.

implementation notes

all math runs through prb-math's UD60x18 fixed-point type for exp and ln. four functions in Curve.sol cover everything the hook needs:

  • totalMinted(eth)— the forward curve. used to settle buys.
  • mintFor(ethBefore, eth)— the delta of the forward curve across one buy. clamps to zero on the sub-wei rounding edge.
  • burnFor(currentTotal, satoIn)— the inverse log formula above. used to settle sells.
  • marginalPrice(eth)— never called by the swap path, exposed for off-chain queries and tooling.

this page describes deployed code. nothing here is a promise — it is a description of what the contract does and what it doesn't.