@launchpad/abis and viem, with cast equivalents where they are short. Addresses are Robinhood Chain (4663); swap in another chain’s addresses for the testnets. Every amount convention used here is defined in Units.
Setup
packages/abis/deployments/<chainId>.json is written by the deploy script; the same data is served at https://windrose.market/api/deployment?chainId=4663. Gas: the registry’s gas field gives maxFeeGwei / priorityGwei to spread into writes (Arc needs at least 20 gwei; Robinhood Chain is happy with the defaults).
1. Read the currency
keeperFxOracleAbi from @launchpad/abis has peekRate and rate too; the inline ABI above works against any IFxOracle.
2. Create a launch
A launch needs a name, a symbol, a metadata URI, the currency code and, optionally, the creator’s first buy (initialIn). With payInUsdc = true on a synthetic currency the factory mints the synthetic from dollars inside the call; otherwise initialIn is in the quote asset.
To set minTokensOut for that first buy, reproduce the curve maths off-chain from the factory config and the oracle rate (the curve does not exist yet):
initialIn = 0 and minTokensOut = 0 (no approval needed). A EUR launch on Arc pays its first buy in EURC with payInUsdc = false.
3. Quote and trade on the curve
Read the curve state once and keep it fresh from events or polling:Buy with dollars (any synthetic launch, or a USD launch)
Buy with the quote asset (wINR held in the wallet, or EURC)
Sell
StalePrice on the dollar paths when the oracle rate is older than 5 days; the quote-asset paths never touch the oracle. Full semantics: Router.
4. Detect readiness and graduate
ready() flips inside the buy that reaches graduationQuote (or sells out the curve) and ReadyToGraduate is emitted. The router immediately tries graduate(); if that inner call failed (graduated() still false while ready() is true), anyone can call it:
Graduated(poolId, positionTokenId, lpQuote, lpTokens, burned) records the pool id and the locked position. From then on buy, sell and the quote functions are closed; the creator can still claimCreatorFees and anyone can sweepProtocolFees.
5. Read the pool after graduation
The pool key is{ currency0: min(token, quoteAsset), currency1: max(token, quoteAsset), fee: 3000, tickSpacing: 60, hooks: 0x0 }. poolId() on the curve is its id. StateView.getSlot0(poolId) returns sqrtPriceX96, which encodes sqrt(rawAmount1 / rawAmount0):
PoolManager unlock); Windrose’s router only serves the curve phase. The locked position and fee collection are described in LiquidityLocker.
6. Follow events
Checklist for a correct integration
- Never assume quote decimals: read
quoteDecimalsfrom the registry (6 for the dollar and EURC, 18 for synthetics). - Apply your own slippage to a fresh quote; the dollar paths have no oracle-side floor.
- Pass
[]andvalue: 0nforpriceUpdatewhile the registry oracle is the keeper oracle. - Use timestamp deadlines, and on Arc bid at least 20 gwei.
- Treat
ready && !graduatedas a state you may have to resolve yourself withgraduate(). - Fees accrue in the quote asset (wINR for an INR launch); claiming them does not convert to dollars.