IntegrationEnglish
Pricing and quotes
The closed-form curve, quoting, rounding, worked examples
The curve
N is total supply in micro-claims, results in micro-dollars. Both carry 6 decimals.
price(N) = N² / 720_000_000_000_000 // 7.2e14
reserve(N) = N³ / 2_160_000_000_000_000_000_000 // 2.16e21In whole units, with n in claims and results in dollars:
price(n) = n² / 720_000_000
reserve(n) = n³ / 2_160_000_000The constants resolve from one anchor: 5 USD per claim at a 100,000 USD reserve, which lands on the exactly rational c = 1/720,000,000. No fixed-point library, no exponential, no square root.
There is no supply cap. The single bound is a uint256 overflow guard at ~4.87e25 micro-claims, a reserve near 5.4e49 USD. It makes the contract revert cleanly instead of wrapping around.
Cost and proceeds
A buy of k claims on supply N:
cost(N, k) = reserve(N+k) − reserve(N)The contract never materialises a cube. It uses b³ − a³ = (b − a)(b² + ab + a²), so the subtraction never loses a unit across two floor divisions:
uint256 next = supply + claims;
uint256 sum = next*next + next*supply + supply*supply;
cost = mulDiv(claims, sum, 2.16e21, Ceil); // buy: round UP
proceeds = mulDiv(claims, sum, 2.16e21, Floor); // sell: round DOWNRounding always favours the reserve. A buyer pays the ceiling, a seller receives the floor. Measured dust: 0.0008 USD across 2,000 simulated trades.
Claims for a deposit
The inverse takes an integer cube root:
claimsForDeposit(N, d) = ∛(N³ + d · 2.16e21) − NNewton's method on integers, seeded by bit length, converging in about five iterations, landing exactly on the floor. Rounded down, so a quote is never optimistic. The trade itself is priced by costToMint, so a quote's rounding never lets anyone underpay.
Quoting
quoteBuy(claims) returns (reserveCost, fee, totalCost); // totalCost = reserveCost + fee
quoteSell(claims) returns (grossProceeds, fee, netProceeds); // netProceeds = grossProceeds − fee
quoteClaimsForDeposit(deposit) returns (claims);
exitValue(account) returns (netProceeds for the whole position);quoteClaimsForDeposit takes a reserve deposit. The 1 % fee sits on top. For a user spending a total of S:
deposit = S × 10_000 / (10_000 + feeBps)
claims = quoteClaimsForDeposit(deposit)
total = quoteBuy(claims).totalCostquoteBuy reverts rather than clamping: InvalidAmount on zero claims, TradeTooLarge above maxBuyCost.
Worked example: the first dollar
Empty market, totalSupply = 0, one dollar of reserve:
deposit = 1_000_000 // 1.00 USD
claims = ∛(1_000_000 × 2.16e21)
= 1_292_660_000 // 1,292.66 claims
reserveCost = 1_000_000 // 1.00 USD, ceiling
fee = 10_000 // 1% → 0.01 USD
totalCost = 1_010_000 // 1.01 USD
marginalPrice = 1_292_660_000² / 7.2e14 ≈ 2_320 // 0.00232 USD per claimThat market reaching a 100,000 USD reserve (60,000 claims, 5.00 USD each) puts the same 1,292.66 claims at:
| Shown at the marginal price | 1,292.66 × 5.00 = 6,463 USD |
| Returned by the curve | ≈ 6,325 USD, before the 1 % sell fee |
Both are correct. The second one is receivable. Show both.
Executing
const deadline = BigInt(Math.floor(Date.now() / 1000) + 300); // 5 minutes
await client.simulateContract({
address: factory,
abi: factoryAbi,
functionName: "buyOnMarket",
args: [market, claims, maxTotalCost, deadline],
account,
});maxTotalCost: your slippage bound. The contract re-quotes at execution and reverts withSlippageExceededabove it.deadline: a signed order does not execute an hour later at a different price.- Approve the factory for
totalCostin USDC. One approval covers every market it created.
Selling mirrors it: sell(claims, minNetProceeds, deadline), no approval, because the market already holds the reserve it pays from.
Reference implementation
The curve exists twice and both agree:
- Solidity:
PerpetualCurve.sol, the authority. - TypeScript:
@conviction/financial-core, what the interface recomputes between chain reads.
Test a reimplementation against reserveFor(supply) on a live market.