Crypto Training
Bonding Curves: The Math, the Protocols, and the Money
A comprehensive deep dive into bonding curves — from the integral calculus behind pricing to 17 real protocols (Uniswap, Curve, Balancer, Bancor, Friend.tech, Intuition, pump.fun, VRGDA, and more). How each curve shapes incentives, who benefits, and what breaks.
Every time you swap on Uniswap, buy a friend's key on Friend.tech, or mint a meme token on pump.fun, a bonding curve decides what you pay.
A bonding curve is a mathematical function hardcoded into a smart contract that determines the price of a token based on its supply or reserves. No order book. No market maker. Just math.
This article covers:
- The mathematical foundations of every major curve type
- 17 real protocols and the specific curves they use
- How DEX AMM curves differ from social/attestation curves
- Security considerations for builders and auditors
Part I: The Math#
What makes it a "bonding" curve?#
The core idea: price is a continuous function of supply. When someone buys, tokens are minted and the price moves up. When someone sells, tokens are burned and the price moves down. The contract holds reserves — it's always the counterparty.
The cost of buying tokens from supply S to S + n is the area under the price curve:
Cost = ∫ from S to S+n of P(s) ds
This integral is what connects the price function to actual money. It is the reason bonding curves feel smooth — there's no discrete jump between the price of one token and the next.
Analogy: Think of filling a swimming pool. The deeper the pool gets (more supply), the more water pressure you fight (higher price). The total effort to fill from 2m to 3m depth is the area under the pressure curve between those depths.
The shaded area under any segment of this line is what you pay. Let's look at each curve type.
1. Linear Bonding Curve#
P(S) = m · S + b
- m = slope (how fast price grows per token)
- b = base price (starting price when supply is 0)
Cost to buy n tokens starting at supply S:
Cost = m/2 · [(S+n)² − S²] + b · n
Derivation (simplified):
The area under a straight line from S to S+n is a trapezoid. The integral of m·s + b is m·s²/2 + b·s. Evaluating between limits gives the formula above.
Example: If m=0.001 ETH and b=0.01 ETH, buying the first 100 tokens costs:
Cost = 0.001/2 · (100² − 0) + 0.01 · 100 = 5 + 1 = 6 ETH
The 100th token costs 0.11 ETH. The 1st costs 0.01 ETH. That's an 11× difference — gentle enough that early and late buyers aren't too far apart.
Who uses it: Intuition (LinearCurve mode), sudoswap (linear mode), some social token launchers.
Character: Predictable, democratic. No one gets a massive early-buyer advantage.
2. Polynomial / Power-Law Curve (Quadratic, Cubic, ...)#
P(S) = a · Sⁿ
- n = the exponent. n=2 is quadratic, n=3 is cubic.
- a = scaling factor
Cost to buy from S to S + Δ:
Cost = a/(n+1) · [(S+Δ)^(n+1) − S^(n+1)]
The quadratic case (n=2) is by far the most common. Here's why: it gives early buyers a huge advantage while still being computationally tractable (you can compute sqrt on-chain).
Example (Friend.tech): P(S) = S²/16000
| Keys (S) | Price (ETH) |
|---|---|
| 1 | 0.0000625 |
| 10 | 0.00625 |
| 50 | 0.15625 |
| 100 | 0.625 |
| 200 | 2.5 |
The 200th key costs 40,000× more than the 1st. That's the quadratic effect.
Derivation of the integral:
Cost = ∫ S²/16000 ds = s³/(3·16000) evaluated from S to S+n
= [ (S+n)³ − S³ ] / 48000
Character: Aggressively rewards the earliest participants. Creates FOMO. Great for speculative assets. Bad for broad participation.
Who uses it: Friend.tech, Intuition (ProgressiveCurve), pump.fun (identical √(C·x + S²) − S formula).
3. Exponential Curve#
P(S) = a · e^(b·S)
Cost integral:
Cost = a/b · [e^(b·(S+n)) − e^(b·S)]
Even more aggressive than quadratic. Price doubles every ln(2)/b tokens. Early buyers become incredibly wealthy if adoption continues.
Character: Ponzi-shaped. Unsustainable unless there's genuine exponential growth in demand. Often used by protocols that intend to "graduate" tokens to a DEX before the curve gets too steep.
Who uses it: sudoswap (exponential mode), EulerBeats (generative art prints), some meme token launchers.
3b. Negative Exponential Curve#
P(S) = a · e^(−b·S)
The mirror image of exponential: price decreases as supply grows.
When would you want price to go down with more supply? Buyback/burn mechanisms. If a protocol wants to incentivize reducing supply, it makes burning cheaper when supply is high (lots of tokens in circulation) and more expensive when supply is low (tokens are scarce). The result: holders are incentivized to burn when supply is bloated, creating deflationary pressure.
Character: Deflationary. Anti-speculative. Rare in practice but appears in buyback mechanisms and some tokenomic designs for deflationary assets.
4. Logarithmic / Sub-Linear Curve#
P(S) = a · ln(b·S + 1)
The opposite of exponential: price rises quickly at first, then decelerates.
Character: Egalitarian. Early and late buyers pay roughly similar prices. Good for utility tokens where you want broad distribution, not speculation.
Who uses it: Data token markets, some DAO membership tokens.
5. Sigmoid / S-Curve#
P(S) = L / (1 + e^(−k·(S − S₀)))
- L = price ceiling (maximum asymptote)
- k = steepness of the transition
- S₀ = inflection point (where rapid growth happens)
Three phases:
- Flat bottom (S << S₀): price is near zero. Early adoption is cheap.
- Rapid growth (S ≈ S₀): price surges through the inflection point.
- Plateau (S >> S₀): price approaches the ceiling. Late buyers pay about the same.
Analogy: A startup's valuation. Cheap in stealth mode, explodes during growth, stabilizes as a mature company.
Character: Arguably the most "fair" shape. Rewards early believers, has an exciting growth phase, but doesn't punish latecomers forever.
Who uses it: Sound.xyz (hybrid sigmoid for music NFTs).
6. Constant Product (Hyperbolic) — The AMM Curve#
x · y = k
This is not supply-based. It's reserve-based. x and y are the quantities of two tokens in a pool.
Marginal price: P = y/x (price of token X in terms of token Y)
When you trade Δx of token X, you receive:
Δy = y − k/(x + Δx)
Analogy: Imagine a seesaw. When one side goes up (more of token X), the other must go down (less of token Y). The product stays constant.
The curve is a hyperbola. Near equal reserves, trades have low slippage. As reserves become imbalanced, slippage increases dramatically — this is the curve's built-in safety mechanism against draining.
Who uses it: Uniswap V2 (canonical implementation), SushiSwap, PancakeSwap, sudoswap (XYK mode).
7. Weighted Constant Mean#
∏(xᵢ^wᵢ) = k where ∑wᵢ = 1
A generalization of constant product to N tokens with arbitrary weights.
Spot price between token i and token o:
SP = (Bᵢ/wᵢ) / (Bₒ/wₒ)
Example: An 80/20 pool of ETH/USDC means the pool is 80% ETH by value. When ETH price moves, the pool rebalances less aggressively — LPs experience less impermanent loss on their ETH.
Uniswap V2 is the special case: 2 tokens, both weighted 50/50.
Who uses it: Balancer (supports up to 8 tokens with arbitrary weights).
8. StableSwap (Curve Finance's Hybrid)#
The most mathematically complex curve in DeFi:
A·n^n·∑xᵢ + D = A·D·n^n + D^(n+1) / (n^n · ∏xᵢ)
- D = the invariant (total value when perfectly balanced)
- A = amplification coefficient
- n = number of tokens in the pool
The key insight: When reserves are balanced, the curve behaves like a constant sum (x + y = k → zero slippage, 1:1 swap). When reserves become imbalanced, it transitions to a constant product (high slippage, protects against draining).
A = ∞ → constant sum (straight line, zero slippage)
A = 0 → constant product (hyperbola, Uniswap-like)
Real pools use A between 10 and 5000. A=100 for most stablecoin pairs.
The StableSwap curve is much flatter near 50/50 — that's the "sweet spot" where stablecoins trade at near-parity with minimal slippage.
Analogy: Constant product is a rubber band — it stretches evenly. StableSwap is like a stiff ruler with rubber ends. In the middle, barely any give (almost 1:1). At the extremes, it bends like rubber.
The Newton's method trick: The StableSwap invariant can't be solved algebraically. Curve Finance iterates Newton's method on-chain to converge on the solution. Typically converges in 4-8 iterations.
Who uses it: Curve Finance (all stablecoin pools), Saddle Finance, Ellipsis.
Part II: 17 Protocols#
A taxonomy of bonding curve protocols#
Before diving into each protocol, it helps to see the landscape:
1. Uniswap V2 — The Canonical Constant Product AMM#
Curve: x · y = k
How it works in practice:
Suppose a pool has 10 ETH and 30,000 USDC. So k = 300,000.
You want to buy 1 ETH. After the trade, the pool needs:
(10 − 1) · y_new = 300,000
y_new = 33,333.33 USDC
You pay 33,333.33 − 30,000 = 3,333.33 USDC for 1 ETH. The "market price" was 30,000/10 = 3,000 USDC/ETH, but you paid 3,333 — that's 11% slippage.
The bigger the trade relative to the pool, the worse the slippage. This is the constant product curve's natural defense against draining.
What the actual code does — the invariant check in swap():
// UniswapV2Pair.sol:swap() — the core invariant enforcement
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(
balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2),
'UniswapV2: K'
);
The 0.3% fee is baked into the invariant check: the adjusted balances must satisfy k after deducting fees. This means k actually increases after every trade (fees accumulate in reserves).
First LP protection — the code mints MINIMUM_LIQUIDITY to address(0):
// UniswapV2Pair.sol:mint()
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock first 1000 LP tokens
}
This prevents the inflation attack that plagues ERC-4626 vaults. The first 1000 LP tokens are burned permanently — you can't manipulate the initial share price.
Uniswap V3/V4 — Concentrated Liquidity:
V3 changed the game. Instead of providing liquidity across the entire 0→∞ price range, LPs concentrate capital in specific ranges [pₐ, p_b].
Within a range, the effective curve is:
(x + L/√p_b) · (y + L·√pₐ) = L²
where L = liquidity. The aggregate of all LP positions creates a custom curve shape. More capital concentrated near the current price → lower slippage.
The price is stored as sqrtPriceX96 — the square root of the price ratio with 96-bit fixed-point precision. Ticks represent discrete price points: price = 1.0001^tick. This allows precise range boundaries.
V4 adds "hooks" (Uniswap/v4-core) — arbitrary Solidity code that executes on every swap, enabling dynamic fees, on-chain limit orders, TWAP oracles, etc.
2. Curve Finance — StableSwap for Pegged Assets#
Source: curvefi/curve-contract
Curve: A·n^n·∑xᵢ + D = A·D·n^n + D^(n+1)/(n^n·∏xᵢ)
Why it exists: Swapping 1 USDC for 1 USDT on Uniswap V2 costs you significant slippage, because the constant product curve doesn't know these tokens should be worth the same. Curve's StableSwap invariant does.
Real numbers: In a Curve 3pool (USDC/USDT/DAI) with A=2000 and $500M TVL, swapping $1M USDC→USDT costs roughly 0.01% slippage. On Uniswap V2 with the same TVL, it'd be ~0.4%.
Curve V2 (Tricrypto): For volatile pairs (ETH/USDT/WBTC), Curve V2 uses an internal oracle to continuously re-center the StableSwap curve around the current price. It's StableSwap with an auto-adjusting midpoint.
Key parameter — the amplification coefficient A:
| A value | Behavior |
|---|---|
| A = 1 | Nearly identical to Uniswap (constant product) |
| A = 100 | Good for loosely pegged assets |
| A = 2000 | Extremely flat near parity — ideal for stablecoins |
| A → ∞ | Pure constant sum (1:1, no slippage at all — but unsafe if depeg occurs) |
3. Balancer — Weighted Multi-Token Pools#
Source: balancer/balancer-v3-monorepo
Curve: ∏(Bᵢ^wᵢ) = k
Spot price: SP = (Bᵢ/wᵢ) / (Bₒ/wₒ)
The weight insight: In a 50/50 ETH/USDC pool (like Uniswap), the pool rebalances aggressively when ETH price moves — causing impermanent loss. In an 80/20 ETH/USDC pool, the pool holds onto its ETH longer, reducing IL on the majority asset.
Example — 80/20 vs 50/50 impermanent loss:
If ETH doubles in price:
- 50/50 pool: ~5.7% IL
- 80/20 pool: ~1.0% IL
That's why Balancer 80/20 pools are popular for governance tokens — projects want to provide trading liquidity without losing most of their token to IL.
Balancer also supports:
- Stable pools (using a StableSwap-like invariant for pegged assets)
- Linear pools (for yield-bearing tokens)
- Boosted pools (that route idle capital to lending protocols)
- Managed pools (where a controller can change weights over time — useful for LBPs)
Liquidity Bootstrapping Pools (LBPs): Start with 96/4 weight favoring the new token, then gradually shift to 50/50. This creates a decreasing price curve that discourages front-running and bots. The opposite of a bonding curve — designed to find the fair price through a Dutch auction.
4. Bancor — The OG Continuous Token Model#
Source: bancorprotocol/contracts-v3
Price formula:
Price = Reserve Balance / (Token Supply × CW)
Purchase return:
Tokens Received = Supply × [(1 + Deposit/Reserve)^CW − 1]
CW (Connector Weight / Reserve Ratio): This single parameter controls the entire curve shape:
| CW | Curve Shape | Behavior |
|---|---|---|
| CW = 100% | Constant price | Fully backed 1:1. Like a stablecoin. |
| CW = 50% | Linear | Price grows proportionally with supply. |
| CW = 33% | Convex (quadratic-ish) | Moderate early-buyer advantage. |
| CW = 10% | Strongly convex | Aggressive growth. Only 10% of value is in reserve. |
Analogy: CW is like the "backing ratio" of a fractional reserve bank. CW=100% means fully backed. CW=10% means only 10 cents on the dollar are in the vault — if everyone sells, only the first sellers get fair value.
Bancor's evolution:
- V1 (2017): Single connector tokens with CW-based bonding curves
- V2: Introduced impermanent loss protection by adjusting weights dynamically
- V3: Omnipool model — all tokens in a single pool with BNT as the intermediary. IL protection funded by BNT inflation. (Later paused due to unsustainability of the IL protection.)
5. Friend.tech — Quadratic Social Keys#
Source: FriendtechSharesV1.sol (deployed on Base)
Formula:
Price(S) = S² / 16000 (in ETH)
The actual getPrice() function uses the closed-form sum-of-squares ∑k² = n(n+1)(2n+1)/6:
// FriendtechSharesV1.sol
function getPrice(uint256 supply, uint256 amount) public pure returns (uint256) {
uint256 sum1 = supply == 0 ? 0 : (supply - 1) * (supply) * (2 * (supply - 1) + 1) / 6;
uint256 sum2 = supply == 0 && amount == 1
? 0
: (supply - 1 + amount) * (supply + amount) * (2 * (supply - 1 + amount) + 1) / 6;
uint256 summation = sum2 - sum1;
return summation * 1 ether / 16000;
}
Why sum of squares? The price of key i is i²/16000. To buy amount keys starting at supply S, you pay the sum ∑ i² from S to S+amount-1. The formula n(n+1)(2n+1)/6 computes this in O(1) — no loops needed.
Key detail: "Cannot sell the last share." The first key is bought by the subject themselves and can never be sold (require(supply > amount)). This ensures every subject always has at least 1 key holder.
The economics at scale:
| Keys | Buy Price | Total ETH in Pool |
|---|---|---|
| 10 | 0.00625 ETH | ~0.02 ETH |
| 50 | 0.15625 ETH | ~2.6 ETH |
| 100 | 0.625 ETH | ~20.8 ETH |
| 200 | 2.5 ETH | ~166.7 ETH |
Fees: 5% protocol + 5% to the key subject (creator). So a round-trip (buy + sell) costs 20% in fees alone. This is not a market — it's a game.
Why quadratic? It creates a strong wealth transfer from late buyers to early buyers. The first 10 keys cost a total of ~0.02 ETH. If supply reaches 100, those early keys are each worth ~0.6 ETH. That's a 300× return for ~0.002 ETH average cost.
The sell side problem: Selling follows Price(S-1) = (S-1)²/16000. When someone sells, the price drops for everyone else. In a thin market (say S=20), a single sell drops the price by ~10%. Two sellers trigger a cascade.
Security note: The contract sends ETH to both protocolFeeDestination and sharesSubject via .call{value:} — if either is a malicious contract that reverts, the entire transaction fails. This means a subject could grief sellers by deploying a reverting receive function.
6. Intuition — Attestation Bonding Curves#
Source: 0xIntuition/intuition-contracts-v2 — see
src/protocol/curves/
Intuition uses bonding curves to price "conviction" in knowledge claims. Each attestation (e.g., "Vitalik is a developer") has its own vault, and buying shares signals agreement.
Three curve types (from the actual smart contracts):
LinearCurve (pro-rata, ERC-4626 style):
shares = assets × totalShares / totalAssets (if supply > 0)
shares = assets (if supply = 0)
This is the standard vault share model. Price per share = totalAssets/totalShares. It only goes up if external value (like fees) flows into the vault.
ProgressiveCurve (quadratic):
P(S) = SLOPE × S
Deposit (assets → shares):
shares = √(S² + assets/HALF_SLOPE) − S
Redeem (shares → assets):
assets = HALF_SLOPE × (S² − (S − shares)²)
Where HALF_SLOPE = SLOPE / 2 and all math uses 18-decimal fixed-point (UD60x18).
The actual Solidity code — deposit (assets → shares):
// ProgressiveCurve.sol:_convertToShares()
UD60x18 s = wrap(totalShares);
UD60x18 inner = add(PCMath.square(s), div(wrap(assets), HALF_SLOPE));
UD60x18 sharesUD = sub(sqrt(inner), s); // sqrt(S² + A/halfSlope) - S
shares = unwrap(sharesUD);
And redeem (shares → assets):
// ProgressiveCurve.sol:_convertToAssets()
UD60x18 s = wrap(totalShares);
UD60x18 sNext = sub(s, wrap(shares));
UD60x18 area = sub(PCMath.square(s), PCMath.square(sNext)); // S² - (S-n)²
UD60x18 assetsUD = mul(area, HALF_SLOPE);
assets = unwrap(assetsUD);
Overflow protection — derived from constructor:
// ProgressiveCurve.sol:initialize()
UD60x18 maxSharesUD = sqrt(wrap(uMAX_UD60x18 / uUNIT)); // sqrt(uint256.max / 1e18)
UD60x18 maxAssetsUD = mul(PCMath.square(maxSharesUD), HALF_SLOPE);
MAX_SHARES = unwrap(maxSharesUD);
MAX_ASSETS = unwrap(maxAssetsUD);
Every entry point checks _checkCurveDomains(totalAssets, totalShares, MAX_ASSETS, MAX_SHARES) before doing math. This prevents the overflow that would otherwise occur when squaring large supply values.
Derivation of the deposit formula:
You deposit A assets. You need to find how many shares n you get. The cost of n shares starting from supply S is the integral:
A = ∫ from S to S+n of SLOPE·s ds
= SLOPE/2 · [(S+n)² − S²]
= HALF_SLOPE · [(S+n)² − S²]
Solving for n:
A/HALF_SLOPE = (S+n)² − S²
S² + A/HALF_SLOPE = (S+n)²
S + n = √(S² + A/HALF_SLOPE)
n = √(S² + A/HALF_SLOPE) − S
That's exactly what the smart contract computes.
OffsetProgressiveCurve (quadratic with price floor):
P(S) = SLOPE × (S + OFFSET)
The OFFSET shifts the curve right, so the starting price is SLOPE × OFFSET instead of zero. This prevents the first token from being nearly free — solving the "first depositor gets everything" problem.
Rounding conventions (critical for security):
previewDeposit→ rounds DOWN (fewer shares for depositor)previewRedeem→ rounds DOWN (fewer assets returned)previewMint→ rounds UP (costs more assets)previewWithdraw→ rounds UP (burns more shares)
This ensures the protocol never loses value through rounding.
7. pump.fun — Meme Token Launch Ramp#
Source: pumpfun bonding curve (Anchor/Rust) — Solana program
What's really happening: pump.fun uses a quadratic bonding curve — the same √(C·x + S²) − S formula as Intuition's ProgressiveCurve. The actual Rust code from the Anchor program:
// state.rs — buy function (simplified)
let bought_amount = (self.total_supply - self.reserve_token) / 1e6 / 1e9;
// PROPORTION = 1280, derived from: 800M tokens sold on 500 SOL
// (500 * 2 / 800) = 1.25 → 800 / 1.25 = 640 → 640 * 2 = 1280
let root_val = (PROPORTION as f64 * amount as f64 / 1e9 + bought_amount * bought_amount).sqrt();
let amount_out = (root_val - bought_amount) * 1e6 * 1e9;
This is tokens_out = √(PROPORTION · sol_in + S²) − S — exactly the quadratic integral formula. The PROPORTION = 1280 constant determines the curve's slope (how fast price rises with supply).
Initial pool state:
// state.rs — add_liquidity()
self.total_supply = 1_000_000_000_000_000_000; // 1 billion tokens (with decimals)
self.update_reserves(token_supply, INITIAL_LAMPORTS_FOR_POOL)?; // 0.01 SOL initial
800M tokens are placed on the curve. As people buy, the price rises along the quadratic.
The graduation mechanic: When the market cap hits ~$69K (about 85 SOL of liquidity), the token "graduates" — liquidity is pulled from the bonding curve and deposited into a Raydium AMM pool. The bonding curve closes permanently.
Why this works for meme tokens: The bonding curve phase creates price discovery with built-in liquidity. No need for seed liquidity from the creator. No LPs to attract. Just a contract that mints/burns. Once there's enough interest, the token transitions to a "real" market.
The risk: Between the bonding curve price and the Raydium listing price, there can be a gap. Sophisticated traders monitor graduation events and snipe the initial DEX liquidity.
8. Ocean Protocol — Data Token AMM#
Source: oceanprotocol/contracts — see
contracts/templates/
Mechanism: Each dataset gets its own ERC-20 "datatoken." These are paired with OCEAN in Balancer-style weighted pools: 90% datatoken / 10% OCEAN.
Why 90/10? The extreme weighting means even small OCEAN purchases move the datatoken price significantly. This creates a strong price signal for data curation — staking OCEAN in a pool is equivalent to saying "this dataset is valuable."
Spot price: Follows Balancer's weighted formula:
SP = (B_datatoken / 0.9) / (B_OCEAN / 0.1)
= (B_datatoken / B_OCEAN) × (0.1/0.9)
= (B_datatoken / B_OCEAN) / 9
The 90/10 split means the datatoken side is 9× less sensitive to imbalance — the price is overwhelmingly determined by how much OCEAN people put in.
9. Nexus Mutual — Insurance Pricing Curve#
Original formula (before RAMM):
Price = 0.01028 + (MCR_eth / 5,800,000) × MCR%⁴
MCR%= Capital Pool / Minimum Capital RequirementMCR_eth= minimum capital requirement in ETH
This is not supply-based. Price depends on the mutual's funding ratio. When the capital pool is healthy (MCR% > 100%), NXM is expensive. When it drops toward 100%, NXM gets cheap.
The degree-4 polynomial makes this extremely sensitive. If MCR% goes from 150% to 200%, the price increases by (200/150)⁴ ≈ 3.16×.
RAMM (2023 replacement) — the actual code uses two separate constant product pools:
// Ramm.sol:swapEthForNxm() — Pool A (buy side)
uint k = state.eth * state.nxmA; // constant product
uint newEth = state.eth + ethIn;
uint newNxmA = k / newEth; // standard x*y=k
nxmOut = state.nxmA - newNxmA;
// Also adjust Pool B proportionally:
uint newNxmB = state.nxmB * newEth / state.eth;
// Ramm.sol:swapNxmForEth() — Pool B (sell side)
uint k = state.eth * state.nxmB; // separate k for sells
uint newNxmB = state.nxmB + nxmIn;
uint newEth = k / newNxmB;
ethOut = state.eth - newEth;
// MCR floor check:
if (context.capital - ethOut < context.mcr) revert NoSwapsInBufferZone();
The ratchet mechanism: Over time, nxmA ratchets down (buy price decreases toward book value) and nxmB ratchets up (sell price increases toward book value). ETH is injected/extracted to maintain target liquidity. Circuit breakers limit daily ETH and NXM throughput.
Key constants from the contract:
| Parameter | Value | Purpose |
|---|---|---|
LIQ_SPEED_PERIOD | 1 day | Liquidity injection/extraction period |
RATCHET_PERIOD | 1 day | Price ratchet period |
TARGET_LIQUIDITY | 5,000 ETH | Target ETH in RAMM reserves |
FAST_LIQUIDITY_SPEED | 1,500 ETH/day | Injection speed when budget available |
NORMAL_RATCHET_SPEED | 400 bps/day | How fast prices converge to book value |
This replaced the legacy bonding curve because the old MCR%⁴ formula was too volatile and the 2.5% spread was insufficient to prevent speculation.
10. Fairmint / Continuous Organizations (C-ORG)#
Concept: A Decentralized Autonomous Trust (DAT) that issues FAIR Securities backed by real revenue.
The buy formula from the actual code — during RUN state:
// ContinuousOffering.sol:estimateBuyValue() — STATE_RUN branch
uint supply = totalSupply() + burnedSupply - initReserve;
// tokenValue = sqrt(2 * buySlopeDen * currencyValue / buySlopeNum + supply²) - supply
tokenValue = BigDiv.bigDiv2x1(_currencyValue, 2 * buySlopeDen, buySlopeNum);
tokenValue = tokenValue.add(supply * supply);
tokenValue = tokenValue.sqrt();
tokenValue = tokenValue.sub(supply);
This is the quadratic bonding curve integral solved for tokens: tokens = √(2·value/slope + S²) − S. The same formula Intuition uses, derived from P(S) = slope · S.
The sell formula includes burned supply for "sponsored burning":
// ContinuousOffering.sol:estimateSellValue() — the full expression
// (a·b²·r)/(t·(b+t)²) + (2·a·r)/(b+t) − (a²·r)/(b+t)²
// where a=quantityToSell, b=burnedSupply, r=reserve, t=totalSupply
Four states — the contract is a state machine:
What makes it unique: The bonding curve is fed by actual company revenue. A fixed percentage of revenue flows into the reserve, making the sell-side curve stronger over time. If the company does well, selling tokens gives you more — like a dividend embedded in the curve. burnedSupply tracks tokens burned outside of sells — this "sponsored burning" increases the sell price for remaining holders.
Evolved into CAFE (Continuous Agreement for Future Equity): Legal equity instruments on-chain. The curve provides price discovery, the reserve provides liquidity, and the revenue commitment creates fundamental value.
11. Sound.xyz — Music NFT Sigmoid#
Curve type: Hybrid — quadratic in the early phase, transitions to square root after an inflection point.
Early phase (S < inflection): P ∝ S²
Late phase (S > inflection): P ∝ √S
The actual sigmoid2Sum from BondingCurveLib.sol computes the total cost for a batch:
// BondingCurveLib.sol:sigmoid2Sum()
function sigmoid2Sum(
uint32 inflectionPoint, // g
uint128 inflectionPrice, // h
uint32 fromSupply,
uint32 quantity
) internal pure returns (uint256 sum) {
unchecked {
uint256 g = inflectionPoint;
uint256 h = inflectionPrice;
uint256 s = uint256(fromSupply) + 1;
uint256 end = s + uint256(quantity);
uint256 quadraticEnd = FixedPointMathLib.min(g, end);
// Quadratic region: uses closed-form n(n+1)(2n+1)/6
if (s < quadraticEnd) {
uint256 a = FixedPointMathLib.rawDiv(h, g * g); // h/g²
sum = ((n*(n+1)*((n<<1)+1) - k*(k+1)*((k<<1)+1)) / 6) * a;
}
// Sqrt region: iterates (no closed form for ∑√i)
if (s < end) {
uint256 c = (3 * g) >> 2; // ¾ of inflection point
uint256 h2 = h << 1; // 2h
do {
sum += FixedPointMathLib.rawDiv(h2 * FixedPointMathLib.sqrt((s - c) * g), g);
} while (++s != end);
}
}
}
Key design decisions:
- Quadratic region (
S < g): Price at tokeniish·i²/g². The library usesn(n+1)(2n+1)/6— same sum-of-squares trick as Friend.tech — to compute the batch cost in O(1). - Sqrt region (
S ≥ g): Price at tokeniis2h·√((i - ¾g)·g) / g. This must be computed iteratively since there's no closed form for∑√i. The¾goffset ensures C1-continuity at the inflection. - Linear mode also available:
linearSumuses the arithmetic seriesm·(n(n+1) - k(k+1))/2.
Why this shape? For music drops:
- Early phase (quadratic): First fans get a bargain. Price rises sharply — creating excitement.
- Late phase (sqrt): Growth slows dramatically. Latecomers still pay more, but not ruinously so.
The sigmoid shape prevents the "infinite price at infinite supply" problem of pure quadratics.
12. sudoswap — NFT AMM with Configurable Curves#
Source: sudoswap/lssvm — see
src/bonding-curves/
Three curve contracts, each implementing ICurve:
| Mode | Contract | Formula | Use Case |
|---|---|---|---|
| Linear | LinearCurve.sol | P_next = P + δ | Predictable price steps for floor NFTs |
| Exponential | ExponentialCurve.sol | P_next = P × δ (where δ > 1e18) | Percentage-based growth |
| XYK | XykCurve.sol | nftBalance × tokenBalance = k | Uniswap-style for liquid NFTs |
LinearCurve — the actual buy cost for n NFTs (from the code):
// LinearCurve.sol:getBuyInfo()
// Buy spot price is shifted up by delta to prevent immediate-arbitrage:
uint256 buySpotPrice = spotPrice + delta;
// Total cost = n*(buySpotPrice) + delta*(n*(n-1))/2
inputValue = numItems * buySpotPrice + (numItems * (numItems - 1) * delta) / 2;
This is the arithmetic series sum. The buySpotPrice = spotPrice + delta shift is subtle — without it, someone could buy at spotPrice and immediately sell at spotPrice + delta for free profit.
XykCurve — virtual reserves stored in unexpected places:
// XykCurve.sol — the virtual reserve trick
uint256 tokenBalance = spotPrice; // spotPrice IS the token reserve
uint256 nftBalance = delta; // delta IS the NFT reserve
// Buy: inputValue = (numItems * tokenBalance) / (nftBalance - numItems)
// Sell: outputValue = (numItems * tokenBalance) / (nftBalance + numItems)
The spotPrice and delta fields are repurposed as virtual reserves for gas efficiency — no extra storage slots needed.
Pool creators choose the curve, starting price, and delta. This makes sudoswap a bonding curve construction kit for NFTs.
13. Aavegotchi (GHST) — Bancor at 33%#
Source: aavegotchi/aavegotchi-contracts — uses Aragon fundraising controller
Formula: Bancor with CW = 33% (reserve ratio = 1/3), reserve in DAI.
Opening price: 0.2 DAI per GHST.
The 33% CW creates a moderately convex curve — rising faster than linear but slower than Friend.tech's quadratic. This was intentional: enough growth to reward early supporters of the game, but not so aggressive that it becomes purely speculative.
Note: The bonding curve was closed by DAO vote (AGIP-64) in 2023, permanently capping GHST supply. This is a governance risk unique to bonding curves — the community can vote to change or close the curve.
14. Mint Club — Step-Function / Piecewise-Linear Curves#
Mechanism: Creators define price breakpoints at specific supply milestones.
Supply 0 - 1000: Price = 0.001 ETH
Supply 1000 - 5000: Price = 0.01 ETH
Supply 5000 - 10000: Price = 0.1 ETH
The on-chain data structure:
// MCV2_Bond.sol
struct BondStep {
uint128 rangeTo; // supply threshold
uint128 price; // price (scaled by 1e18)
}
struct Bond {
address creator;
uint16 mintRoyalty; // basis points
uint16 burnRoyalty;
address reserveToken;
uint256 reserveBalance;
BondStep[] steps; // immutable after creation
}
Buy pricing — walks through steps, accumulating cost with ceiling division:
// MCV2_Bond.sol:getReserveForToken()
for (uint256 i = getCurrentStep(token, currentSupply); i < steps.length; ++i) {
supplyLeft = step.rangeTo - currentSupply;
if (supplyLeft < tokensLeft) {
reserveToBond += Math.ceilDiv(supplyLeft * step.price, multiFactor);
currentSupply += supplyLeft;
tokensLeft -= supplyLeft;
} else {
reserveToBond += Math.ceilDiv(tokensLeft * step.price, multiFactor);
break;
}
}
Sell pricing — walks steps in reverse. Note: buy uses ceilDiv (rounds up, costs more), sell uses regular division (rounds down, returns less). This asymmetry prevents round-trip extraction.
Supports both ERC-20 and ERC-1155 tokens via clone factories. Each token gets its own Bond with immutable steps. Creators can set separate mint and burn royalties.
15. EulerBeats — Exponential NFT Prints#
Source: EulerBeats.sol (deployed)
Curve type: Exponential bonding curve — but not a simple e^x. The actual formula from the smart contract is a hybrid exponential + linear function:
// EulerBeats.sol — Constants
uint256 constant K = 1 ether;
uint256 constant B = 50; // inflection point
uint256 constant C = 26; // linear component
uint256 constant D = 8; // offset
uint256 constant SIG_DIGITS = 3;
function getPrintPrice(uint256 printNumber) public pure returns (uint256 price) {
uint256 decimals = 10 ** SIG_DIGITS;
if (printNumber < B) {
price = (10 ** (B - printNumber)) * decimals / (11 ** (B - printNumber));
} else if (printNumber == B) {
price = decimals;
} else {
price = (11 ** (printNumber - B)) * decimals / (10 ** (printNumber - B));
}
price = price + C * printNumber; // add linear component
price = price - D; // subtract offset
price = price * 1 ether / decimals;
}
The core ratio (11/10)^n creates the exponential growth. Below the inflection point B=50, the function is (10/11)^(50-n) (exponentially approaching 1). Above it, (11/10)^(n-50) (exponentially growing). Plus a linear term 26n - 8.
Burn price = 90% of print price — this 10% spread funds the protocol and original NFT owner:
function getBurnPrice(uint256 supply) public pure returns (uint256 price) {
price = getPrintPrice(supply) * 90 / 100;
}
The key twist: holders can burn their print NFT to reclaim reserve ETH from the curve. This makes EulerBeats one of the few NFT projects where the bonding curve works in both directions.
Max supply: 120 prints per seed, 27 original seeds. The exponential curve makes prints beyond ~80 extremely expensive.
16. Fei Protocol — One-Way Stablecoin Curve#
Concept: FEI stablecoins were minted on a bonding curve denominated in ETH, approaching the $1 peg as supply increased toward a target ("Scale") of 250M FEI.
Phase 1 (below Scale): Price increases toward $1 as supply grows
Phase 2 (at Scale): Price = $1
The controversial design: Users could buy FEI on the curve, but could not sell back. The ETH raised was retained as Protocol Controlled Value (PCV) and used to provide Uniswap liquidity.
This was a one-way bonding curve — it incentivized early participation to bootstrap the stablecoin but trapped users who bought above peg during the initial rush. The launch was widely criticized: FEI traded below $1 for weeks after launch, and early buyers couldn't use the curve to exit.
Lesson: A bonding curve without sell-side liquidity is a fundraising mechanism disguised as a market. Two-way curves (where you can always sell back) are more honest about what they offer.
17. VRGDA — Variable Rate Gradual Dutch Auction#
Source: transmissions11/VRGDAs
Not a bonding curve in the traditional sense, but a dynamic pricing function that shares the same core principle: price is a deterministic function computed by a contract.
The actual Solidity code is remarkably concise:
// VRGDA.sol — the entire pricing function
function getVRGDAPrice(int256 timeSinceStart, uint256 sold)
public view virtual returns (uint256)
{
unchecked {
return uint256(wadMul(targetPrice, wadExp(unsafeWadMul(decayConstant,
timeSinceStart - getTargetSaleTime(toWadUnsafe(sold + 1))
))));
}
}
In math:
Price = targetPrice × e^(decayConstant × (timeSinceStart − targetSaleTime(sold + 1)))
Where decayConstant = ln(1 - priceDecayPercent) — always negative, so price decays when no sales happen.
Three flavors of issuance schedule:
LinearVRGDA — target perTimeUnit tokens per day:
// LinearVRGDA.sol
function getTargetSaleTime(int256 sold) public view override returns (int256) {
return unsafeWadDiv(sold, perTimeUnit);
}
LogisticVRGDA — target follows a sigmoid (total capped at logisticLimit):
// LogisticVRGDA.sol
function getTargetSaleTime(int256 sold) public view override returns (int256) {
unchecked {
return -unsafeWadDiv(
wadLn(unsafeDiv(logisticLimitDoubled, sold + logisticLimit) - 1e18),
timeScale
);
}
}
The logistic version is what Art Gobblers uses — a finite number of NFTs following an S-curve schedule.
The key insight (Paradigm blog post): The formula in cleaner notation is:
Price_n(t) = p₀ · (1 − k)^(t − f⁻¹(n))
where f⁻¹(n) is the target time for the nth sale. When sales run ahead of schedule, t < f⁻¹(n), the exponent is positive, and price increases. When behind schedule, t > f⁻¹(n), price decreases.
Concrete example: Target = 10 NFTs/day. On day 5, 70 have sold (vs target 50) — that's 2 days ahead. Price multiplier: (1-k)^(-2). If k = 0.5, that's 2² = 4× the target price. On day 15, 120 have sold (vs target 150) — 3 days behind. Multiplier: (1-k)^3 = 0.5³ = 0.125×.
Linear VRGDA is isomorphic to a standard GDA — this is the key mathematical insight from the Paradigm paper. Setting f(t) = r·t (linear schedule) gives f⁻¹(n) = n/r, and the pricing reduces to a standard Gradual Dutch Auction where each consecutive auction starts at the previous auction's starting price.
Who uses it: Art Gobblers (Paradigm), generative art drops. Can be combined with sigmoid issuance schedules for logistic VRGDAs where the target emission follows an S-curve.
Why it matters: VRGDAs solve the "Dutch auction cliff" problem where all demand concentrates at the end. By continuously adjusting to demand, VRGDAs produce smoother price discovery. They're the bonding curve equivalent of a thermostat — always adjusting toward equilibrium.
Part III: AMM Curves vs. Social/Attestation Curves#
They both use math to set prices. But they're fundamentally different beasts.
| Dimension | DEX AMM Curves | Social / Attestation Curves |
|---|---|---|
| What's priced | Exchange rate between two existing tokens | Price of a single token as a function of its own supply |
| Reserves | Two-sided: LPs deposit both tokens | One-sided: buyers deposit ETH/DAI, tokens are minted |
| Liquidity source | External LPs | The contract itself |
| Token lifecycle | Tokens pre-exist; AMM facilitates exchange | Tokens minted on buy, burned on sell |
| Impermanent loss | Yes — LPs lose vs holding | N/A — no LP positions |
| Primary purpose | Trading / market making | Primary issuance, curation, signaling |
| Curve shape goal | Minimize slippage, maximize capital efficiency | Reward early participants, control price discovery |
| Typical curve | Constant product, StableSwap | Quadratic, sigmoid, Bancor power |
| Fee model | Swap fees to LPs (0.01% - 1%) | Protocol + creator fees (Friend.tech: 10% total) |
| Price direction | Bidirectional (price can go up or down) | Monotonic with supply (up on buy, down on sell) |
The fundamental similarity: Both are deterministic, on-chain pricing functions that eliminate order books. Both use the integral under the curve to compute costs.
The fundamental difference: AMM curves define an exchange relationship between two reserves that must stay in balance. Social curves define a minting/burning relationship where the token supply itself changes.
Part IV: Other Curve Types Worth Knowing#
Constant Sum: x + y = k#
Zero slippage, but completely unsafe. Once one side is drained, the pool is empty. Only useful in theoretical contexts or as one extreme of the StableSwap spectrum.
Concentrated Liquidity as "Virtual Curves"#
Uniswap V3's insight: concentrated liquidity positions are mathematically equivalent to constant product curves with virtual reserves. Each position is its own mini-curve within a price range. The aggregate of all positions creates a custom, community-designed curve — often resembling StableSwap for popular pairs where LPs cluster around the current price.
RAMM (Ratcheting AMM)#
Nexus Mutual's replacement for their bonding curve. Two pools: one prices above book value, one below. Over time, they ratchet toward each other. This creates bounded price discovery without the single-point pricing of a traditional bonding curve.
Dynamic Curves / Hooks#
Uniswap V4 hooks and Balancer custom pools allow arbitrary logic per swap. This enables:
- Dynamic fees that adjust to volatility
- TWAP-based pricing
- Oracle-integrated curves
- Limit orders embedded in the curve
These aren't fixed curves anymore — they're programmable curves that adapt in real-time.
Part V: Security Considerations#
1. Sandwich Attacks#
The #1 threat to any bonding curve user. A bot:
- Sees your pending buy in the mempool
- Front-runs: buys before you (pushing the price up)
- Your transaction executes at the inflated price
- Back-runs: bot sells at the higher price, profiting from your slippage
Why bonding curves are especially vulnerable: Price is deterministic and public. The price impact of any pending transaction is calculable in advance.
Mitigations:
- Slippage tolerance (revert if price moves beyond threshold)
- Private mempools (Flashbots Protect, MEV Blocker)
- Commit-reveal schemes
- Batch auctions (aggregate orders at a single clearing price)
2. First Depositor / Inflation Attacks#
In pro-rata curves (shares = assets × totalShares / totalAssets), the first depositor can:
- Deposit 1 wei, getting 1 share
- Donate a large amount directly to the vault (without minting shares)
- Now
totalAssetsis huge buttotalSharesis 1 - Next depositor's
assets × 1 / (huge number)rounds to 0 shares
Mitigations:
- Virtual offset (mint "dead shares" to address(0) on first deposit)
- Minimum deposit requirement
- Use OffsetProgressiveCurve (Intuition's approach — OFFSET ensures the starting price is never zero)
3. Rounding Exploitation#
In progressive/quadratic curves, rounding direction is critical:
- Deposits should round DOWN (fewer shares for the buyer → favor the protocol)
- Redemptions should round DOWN (fewer assets returned → favor the protocol)
- Minting should round UP (costs more assets → favor the protocol)
- Withdrawals should round UP (burns more shares → favor the protocol)
If any direction is wrong, an attacker can profit by repeatedly depositing and redeeming, extracting tiny amounts each round-trip.
4. Curve Parameter Governance Attacks#
If curve parameters (slope, amplification coefficient, weights) can be changed via governance, an attacker with enough votes can:
- Lower the slope → reduce sell-side returns → extract value
- Change Curve's
Aparameter → enable arbitrage during the transition - Adjust Balancer weights → create temporary price dislocations
Mitigation: Timelocks, parameter bounds, gradual ramp (Curve ramps A over 3 days minimum).
5. Reserve Draining / Rug Pulls#
- If the reserve is admin-withdrawable, the curve becomes unbacked overnight
- Low reserve ratios (Bancor CW < 50%) mean there's not enough value to pay all sellers
- "Graduation" mechanics (pump.fun) create windows where liquidity transitions between venues — sophisticated traders exploit these gaps
6. Precision and Overflow#
Quadratic and exponential curves square or exponentiate large numbers. Without proper fixed-point math:
S²overflowsuint256whenS > 2^128- Division before multiplication loses precision
sqrt()implementations have edge cases near zero
Best practices:
- Use UD60x18 (PRBMath, Solady) for 18-decimal fixed-point arithmetic
- Calculate and enforce
MAX_SHARESandMAX_ASSETSbounds (Intuition derives these fromsqrt(type(uint256).max / 1e18)) - Use
fullMulDivfor intermediate products that exceed uint256
Audit Checklist for Bonding Curves#
- Rounding direction — Does every conversion favor the protocol?
- First depositor — Is there protection against share inflation?
- Reserve backing — Is 100% of deposited value held in reserve? Can admin withdraw?
- Slippage protection — Do buy/sell functions accept minimum output parameters?
- Parameter mutability — Can curve parameters be changed? By whom? With what constraints?
- Overflow bounds — Are MAX values properly calculated and enforced at every entry point?
- Fee-on-transfer tokens — Does the curve handle tokens where received ≠ sent?
- Reentrancy — If the reserve is native ETH, are state changes before external calls?
- Curve continuity — At transition points (graduation, inflection, parameter changes), is pricing continuous?
- Round-trip profitability — Can
deposit → redeemormint → withdrawextract value?
Cross-Protocol Insight: The Quadratic Family#
Reading the actual smart contracts reveals a surprising convergence. Four protocols — built independently, on different chains, for completely different use cases — all implement the same quadratic integral formula:
tokens = √(constant · payment + S²) − S
The math doesn't care whether you're pricing social keys, meme coins, attestation shares, or continuous securities. The quadratic bonding curve produces the same formula every time — the only difference is the constant C (slope) and what the tokens represent.
Similarly, Nexus Mutual's RAMM and sudoswap's XykCurve both use constant product (x·y=k) — one for insurance token pricing, the other for NFT trading. The same k / newX calculation appears in both codebases.
Summary#
| Protocol | Curve Type | Formula | Primary Use |
|---|---|---|---|
| Uniswap V2 | Constant Product | x·y = k | Token exchange |
| Uniswap V3/V4 | Concentrated Product | (x+L/√p_b)·(y+L·√pₐ) = L² | Capital-efficient exchange |
| Curve Finance | StableSwap | A·n^n·∑xᵢ+D = A·D·n^n+D^(n+1)/(n^n·∏xᵢ) | Pegged asset swap |
| Balancer | Weighted Mean | ∏(Bᵢ^wᵢ) = k | Multi-token pools |
| Bancor | Connector Weight | Return = Supply·((1+Dep/Reserve)^CW−1) | Continuous token model |
| Friend.tech | Quadratic | P = S²/16000 | Social keys |
| Intuition | Progressive/Offset/Linear | P = SLOPE·S, P = SLOPE·(S+OFFSET) | Attestation shares |
| pump.fun | Quadratic (same as Intuition) | tokens = √(1280·SOL + S²) − S | Meme token launch |
| Ocean Protocol | Weighted AMM (90/10) | Balancer with 90/10 weight | Data token curation |
| Nexus Mutual | RAMM (dual constant product) | Two pools: eth×nxmA=k, eth×nxmB=k | Insurance token |
| Fairmint/C-ORG | DAT + Sponsored Burn | sell = 2·reserve/(supply+burnt)² | Continuous securities |
| Sound.xyz | Hybrid Sigmoid | Quadratic → √S, C1-continuous | Music NFT minting |
| sudoswap | Configurable (3 types) | Linear, Exponential, or XYK | NFT trading |
| Aavegotchi | Bancor (CW=33%) | Bancor with reserve ratio 1/3 | Gaming token |
| Mint Club | Piecewise-Linear | Step-function with custom breakpoints | No-code token launch |
| EulerBeats | Hybrid Exponential+Linear | (11/10)^(n−50) + 26n − 8, burn=90% | Generative art prints |
| Fei Protocol | One-Way Approach | Price → $1 as supply → Scale | Stablecoin bootstrap |
| VRGDA | Schedule-Adjusted Dutch | p₀·(1−k)^(t−f⁻¹(n)) | NFT drops (Art Gobblers) |
The bonding curve is one of DeFi's most powerful primitives. The same core idea — "price is a function, the contract is the counterparty" — powers trillion-dollar DEXes and $10 meme tokens alike. The math determines who benefits, how much, and when.
Sources and further reading:
- Variable Rate GDAs (Paradigm)
- Uniswap V3 Development Book — CFMM
- Curve StableSwap Mathematical Guide (Xord)
- Balancer Weighted Math
- Converting Between Bancor and Bonding Curve Formulas (Billy Rennekamp)
- Friend.tech Deep Dive (Coinmonks)
- Intuition Bonding Curves
- The Math Behind pump.fun
- Ocean Protocol Pricing Schemas
- Nexus Mutual Token Model
- Continuous Organizations Whitepaper (C-ORG)
- Sound Swap Technical Deep Dive
- sudoswap Bonding Curves and Pricing
- Aavegotchi Bonding Curve Wiki
- Intro to Bonding Curves and Shapes (Linum Labs)