The formula
Let:vt= virtual TON reserve = 300 TONvT= virtual Jetton reserve = 1,010,000,000 (1.010B)Δton= TON in (after 1% fee)Δtok= Jettons out
Δtok:
Δton before the math; sells deduct from Δton_out after.
Why virtual reserves
A pure constant-product curve withreal_ton = 0 and real_token = 800M would have a price of 0 at the first buy — anyone could acquire the entire supply for 1 nanoTON. Virtual reserves solve this by setting a synthetic starting state.
With vt = 300 and vT = 1.010B:
- Initial price ≈
300 / 1.010B = 297 nanoTON per Jetton unit. (Each Jetton has 9 decimals, so this is 297 nanoTON per nano-Jetton, or 0.297 µTON per Jetton.) - Buying 1 TON moves price by ~0.33% — small enough that the first few buyers aren’t massively rewarded for being early.
- Buying 10 TON moves price by ~3.3%.
- Buying 100 TON moves price by ~33% — getting steep, as it should.
Curve trajectory
If every buy is exactlyΔton = 1 TON net, the simulator computes:
| Cumulative TON in | Cumulative Jettons sold | Price per Jetton (nanoTON) |
|---|---|---|
| 0 | 0 | 297 |
| 100 | ~252M | 533 |
| 250 | ~459M | 905 |
| 500 | ~631M | 1,801 |
| 750 | ~706M | 2,943 |
| 1,000 (graduation) | ~777M | 5,577 |
real_ton = 1000 TON net; at that point tokens_sold ≈ 777M of the 800M for-sale supply, leaving ~23M tokens that get burned at graduation.)
Price grows roughly 19× from launch to graduation. For comparison, Pump.fun’s price grows ~270× over its curve — tonch’s curve is gentler, intentionally.
Why the curve burns the leftover
At graduation, the curve has sold ~777M of its 800M for-sale supply. The remaining ~23M Jettons are leftover from rounding and the asymptotic shape of the curve (the last few Jettons would cost an enormous amount of TON to extract). The graduation handler burns these viaop::jetton_burn, returning the supply to a clean state where the only outstanding Jettons are:
- Jettons held by curve buyers.
- The 200M AMM reserve, about to be deposited into DeDust.
Why a per-launch curve and not a shared pool
Each launch gets its own curve contract for these reasons:- Isolation. A bug in one curve doesn’t affect any other. Each curve has its own state, its own balance, its own LP locker.
- Permissionless graduation. Anyone can call
op::graduateon a filled curve. There’s no shared queue or admin to gate the migration. - DeDust vault wiring. Each Jetton needs its own DeDust per-Jetton vault. The curve resolves and stores its specific DeDust addresses at deploy time.
tonlaunch/contracts/bonding-curve-sale.fc. For the canonical curve simulator see tonlaunch/simulator/curve-sim.ts.