starling
Reference

The engine

Two files, no dependencies, the same code in Node and in the browser: engine/starling-engine.mjs (curve and replay) and engine/starling-abi.mjs (ABI and keccak). The site copies them at build time, so what you see on a page is what the tests run.

Curve and flock (BigInt, exact)

import * as E from './starling-engine.mjs';

E.flockBuy(total, taxBps)        // { qNet, quoteReserve, tokenReserve, tokensOut, avgPrice, premium, supplyShare, spotAfter }
E.sell(x, y, tokensIn, taxBps)   // { out, quoteReserve, tokenReserve }  one sale on the curve
E.roundTrip(total, taxBps)       // { out, ratio }  join, launch, everybody sells back
E.suggestFloor(total, taxBps)    // minTokensPerEth for Flock.open
E.allocate(deposits, size, tokensOut)  // pro rata fill: { used, fill, members: [{ dep, tokens, back }] }
E.shareOf(tokensOut, dep, total)

Constants: X0 = 1.68 ETH, Y0 = 1e27, K, CURVE_FEE_BPS = 100, MAX_TOTAL = 0.25 ETH, GRADUATION_QUOTE = 4.2 ETH.

Replay of a real launch (Number)

A launch is a list of [secondsSinceLaunch, raisedEth] points, where raised is the quote in the curve above its start. On a constant-product curve the price is x²/K, so everything follows from it:

E.spotPremium(points, sec)                        // price at sec vs the start, as a fraction
E.lateBuyerReturn(points, entrySec, exitSec, tax)  // a small buyer arriving late, exiting at exitSec
E.flockMemberReturn(points, totalEth, tax, exitSec) // a member of a flock that buys at the start and sells as a group
E.summarize(returns)                               // mean, median, loss shares, best, worst

ABI

import * as A from './starling-abi.mjs';

A.encodeOpen(params, terms)       // calldata of Flock.open
A.encodeLaunch(id, params)        // calldata of Flock.launch
A.encodeId('join' | 'leave' | 'claim' | 'refund' | 'cancel' | 'reclaim', id)
A.paramsHash(params)              // keccak256(abi.encode(params)), the hash the contract stores
A.decodeOpened(eventData)         // params + terms from an Opened log
A.decodeFlock(returnData)         // the 16 fields of flocks(id)

The encoder is checked against Foundry's cast calldata and cast abi-encode (engine/tests/abi-vectors.json), including non-ASCII strings and empty fields.

Tests

17 tests: 20 real flock launches reproduced to the wei (tokens and both reserves), sales within 2 wei of the real curve, the invariant, rounding, allocation, replay, calldata, hashes and event decoding. Run them with node --test engine/tests/engine.test.mjs engine/tests/abi.test.mjs.