Private beta · test money onlyRequest an invite

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.16e21

In whole units, with n in claims and results in dollars:

price(n)   = n² / 720_000_000
reserve(n) = n³ / 2_160_000_000

The 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 DOWN

Rounding 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) − N

Newton'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).totalCost

quoteBuy 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 claim

That 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 price1,292.66 × 5.00 = 6,463 USD
Returned by the curve6,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 with SlippageExceeded above it.
  • deadline: a signed order does not execute an hour later at a different price.
  • Approve the factory for totalCost in 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.