Skip to content

Whitepaper · v2.1 · September 2026

The Countdown Family protocol

A permissionless token launchpad on Robinhood Chain that deploys every token straight into a permanently locked Uniswap V3 position — no bonding curve, no migration, no liquidity an administrator can touch.

Robinhood Chain (4663)Read the docs →
Contents

1. Abstract

Countdown Family is a token launchpad on Robinhood Chain, an Arbitrum Orbit L2 that settles to Ethereum. A creator submits one transaction and receives, atomically, a fixed-supply ERC-20, a live Uniswap V3 pool paired with WETH, the full supply deposited as a one-sided liquidity position, and that position transferred into a locker contract with no withdrawal function. Trading begins in the same block, on Uniswap V3 itself, with no intermediary contract between the trader and the pool.

The protocol has no bonding curve and no graduation migration. The concentrated-liquidity position is the curve: because all of the supply sits above the opening price and none of the WETH sits below it, buying moves the price up along the V3 tick range in exactly the way a bonding curve would, except the liquidity is already in the place it would eventually have to migrate to. What other launchpads call “graduation” is here a milestone read from the position’s WETH principal, not a state transition that moves funds.

Fees are charged at two layers. Uniswap’s 1% pool fee accrues inside the locked position and is split on claim between the creator (70%) and the protocol (30%), a ratio snapshotted per token at launch. On top of that, trades sent through the app’s router pay a creator fee the deployer sets once (0–5%) and a 1% protocol fee, both in ETH, both capped by constants. For 300 L2 blocks after launch (about thirty seconds) per-wallet and cumulative caps bind every buyer except the creator’s atomic opening buy. Everything the app shows is indexed from chain reads with block and transaction provenance; the repository contains no mock data.

2. The problem with launchpads

The dominant launchpad pattern — a custom bonding-curve contract that later “graduates” to a DEX — works, but it carries four structural costs that this design was built to remove.

2.1 The migration is a moment of custody

A bonding-curve pool holds every buyer’s ETH until the curve completes. Graduation is a transaction the pool contract executes: withdraw the ETH, withdraw the tokens, call a router, receive LP tokens, burn them. Every step is code the launchpad wrote, and for the duration of the curve the launchpad — not a DEX with years of battle-testing — is the custodian. An earlier version of this project ran exactly that design (BondingCurvePool.sol, retired and kept only as history), and the migration path was where most of its risk lived.

2.2 Two price regimes mean two sets of bugs

Pre-graduation trades go through the curve; post-graduation trades go through the DEX. The UI, the indexer and the wallet integration all need two code paths, and the seam between them — the block in which the curve closes and the pool opens — is where price can be manipulated, where a stuck migration strands funds, and where an indexer that missed one event drifts permanently.

2.3 Snipers are a clock problem, not a wallet problem

Per-wallet caps are defeated by splitting across addresses for the cost of gas. The only constraint a sniper cannot buy around is time: what happens in the launch block and the blocks right after it. A protection that does not reason about block numbers is decoration.

2.4 Fake numbers

Launchpad frontends routinely display synthesised charts, invented volume and placeholder holders. A trader cannot tell a random walk from a market. A launchpad that earns from trades has an incentive to make markets look more alive than they are, and the only defence is a data layer where every number points at a block.

3. Design principles

One transaction, one venue

Deploy, pool, seed, lock, and buy in a single call. Trading happens on Uniswap V3 from block one. There is no second regime to migrate to.

The liquidity is not ours to move

The position NFT lives in a contract with no withdrawal, no arbitrary-call, and no upgrade function. The owner can change future policy; it cannot reach existing liquidity.

Time-bound protection

Anti-snipe rules are keyed to the chain’s own block height (ArbSys.arbBlockNumber()), apply only to buys from the pool or handed out by the router, and expire 300 L2 blocks — about thirty seconds — after launch. After that the token is a plain ERC-20.

Snapshot, don’t trust

Every economic parameter that affects an existing token — the fee split, the caps, the restriction window — is copied into the token or the locker at launch and read from there afterwards.

Every number has a block

The indexer writes only what it read from chain, keeps the block hash and transaction hash, and labels price points by source. A flat chart means nothing traded.

Simulate before you sign

Launches and fee claims are dry-run with the real calldata before the wallet opens, so the contract’s own revert reason — not a client-side guess — is what the user sees.

4. Protocol architecture

The protocol is four contracts plus shared Uniswap V3 infrastructure that already exists on the chain.

ContractRoleOwnership
CountdownLaunchFactoryEntry point. launchToken checks the configuration, pays the launch fee, has the token deployed via CREATE2, creates and initialises the pool, mints the position, hands it to the locker, records the launch, passes the mint’s rounding dust to the creator, and executes the opening buy. Holds the DEX and launch configurations. Exposes graduationStatus.Ownable2Step
CountdownTokenDeployerHolds the token’s creation code and performs the CREATE2 on the factory’s behalf, with the salt namespaced by caller so no one else can occupy an address the factory predicts. It exists because the token bytecode, embedded in the factory, put the factory over the 24,576-byte limit (§14.3).None (no owner, no state)
CountdownLaunchLockerPermanent custodian of every position NFT. Accepts NFTs only from the factory, verifies custody in lockPosition, splits collected fees, tracks per-token protocol share and fee redirects. No withdrawal path exists.Ownable2Step
CountdownTradeRouterThe app’s swap entry point. Wraps SwapRouter02 and charges the creator’s per-token fee plus the protocol fee in ETH. Independent of the factory; trades on unregistered tokens pay only the protocol leg.Ownable2Step
CountdownLauncherTokenFixed-supply ERC-20 with immutable launch parameters, self-describing metadata (logo, description, socials), and a transfer hook that enforces the launch-window caps on pool-to-wallet and router-to-wallet transfers only, measured in L2 blocks (§7).None (immutable)

Off-chain, a single long-running indexer reads factory launch events, token metadata, pool state and swap logs into a libSQL database; a Next.js API serves that database to the browser. The browser touches the chain for exactly one purpose: signing a transaction the user asked for.

Data flow
Robinhood Chain (factories, tokens, V3 pools)
        │  eth_getLogs + view calls (paced, retried)
        ▼
scripts/pons-indexer.ts            long-running worker (PM2)
        │  writes markets / price_points / data_versions
        ▼
libSQL (local file in dev, Turso in production)
        │
        ▼
GET /api/data?kind=updates|markets|market      ETag + 304
GET /api/config
        │
        ▼
lib/use-markets.ts  →  React UI

Wallet writes (launch, swap, claim) go straight from the browser to the chain.
Nothing is settled server-side; the pool's own events are the record.

5. The launch transaction

launchToken(TokenParams, launchConfigId, dexId, salt) is payable and nonReentrant. msg.value must be at least the launch fee; whatever exceeds it becomes the creator’s opening buy. The full sequence, in order:

  1. Gate checks. launchEnabled (true from construction on this factory) or the caller is allowlisted; fee paid; DEX and launch config exist and are enabled; name and symbol are non-empty.
  2. Address prediction. The token’s creation code is built from the params and the config, and its CREATE2 address is computed. If a V3 pool for (predicted token, WETH, 1%) already exists, the call reverts PoolAlreadyExists. This is what makes a collision free to detect by simulation.
  3. Launch fee is forwarded to the locker’s protocolFeeRecipient. Default 0.0005 ETH, owner-adjustable.
  4. Token deploy via CREATE2 through the token deployer, with the caller’s salt namespaced by the factory. The constructor mints the entire supply (1,000,000,000 × 10¹⁸) to the factory and stamps launchBlock, restrictionEndBlock = launchBlock + 300 (L2 blocks), and the caps as immutables.
  5. Pool creation. Token ordering is determined (isToken0 = token < WETH); the initial tick is sign-flipped if the token is token1; the pool is created and initialised at sqrtPriceX96 = getSqrtRatioAtTick(tick).
  6. One-sided mint. The factory approves the position manager for the supply and mints a position whose range is [initialTick, maxUsableTick] (token0 case) or [minUsableTick, −initialTick] (token1 case). Only token goes in; amountMin is zero because the pool is fresh and nothing can front-run a pool that did not exist a moment earlier.
  7. Record. A LaunchedToken struct is stored: deployer, paired token, position manager and id, config ids, restriction end block, supply, ordering, pool fee, and the opening buy amount. The graduation threshold is copied per token.
  8. Lock. The NFT is safeTransferFrom’d to the locker, which only accepts transfers where both operator and sender are the factory. lockPosition re-reads ownerOf and reverts unless the locker holds it, then snapshots the protocol fee share for that token.
  9. Fee redirect. If feeWallet was supplied, the locker is told to pay the creator share there instead of to the deployer.
  10. Event. TokenLaunched is emitted with a signature byte-identical to pons’s (§13).
  11. Opening buy. If anything remains above the fee, the token opens a one-call recipient exemption, the factory calls exactInputSingle on SwapRouter02 with the remaining ETH, and the exemption is closed. The buy lands with the creator (or their feeWallet) as recipient.
Launch config 0 (deploy script)ValueMeaning
pairTokenWETHEvery pool is token/WETH.
supply1,000,000,000Fixed. No mint function exists after construction.
initialTick−204,200Opening price ≈ 1.3557 × 10⁻⁹ WETH per token (§6).
graduationThreshold4.2 WETHPrincipal in the position at which graduated = true (§10).
maxWalletBps500 (5%)Max balance a buyer may hold via pool buys during the window.
maxTxBps550 (5.5%)Derived as 110% of maxWallet; cumulative pool-buy cap per wallet.
restrictionBlocks2Window = launch block plus the next two.
Pool fee tier10,000 (1%)Uniswap fee on every swap; the only LP fee in the pool.
tickSpacing200Required by the 1% tier.
launchFee0.0005 ETHPaid to the protocol fee recipient. Owner-adjustable.
The opening buy is not capped
The token’s transfer hook exempts the recipient of the factory’s atomic buy from both caps, in the launch block only. The creator can therefore take as much of the supply as their ETH buys at the pool price. The caps in §7 bind everyone else. This is disclosed rather than hidden: a buyer should read initialBuyAmount from getLaunchedToken, which is also shown on the token page, before deciding.

6. Price discovery on concentrated liquidity

Uniswap V3 quotes price in ticks. The price of token1 in units of token0 at tick i is:

P(i) = 1.0001i

With the token as token0 and WETH as token1, the launch config’s tick of −204,200 gives an opening price of 1.0001⁻²⁰⁴²⁰⁰ ≈ 1.3557 × 10⁻⁹ WETH per token. If the token sorts as token1 the sign flips and the pool price is the reciprocal, but the economic price is the same. The indexer computes it from slot0().sqrtPriceX96:

price = (sqrtPriceX96 / 296)²  ·  then inverted if the token is token1

At launch, all 10⁹ tokens are deposited into the range [P₀, Pmax] with no WETH. For a position entirely above the current price, V3 defines the token0 amount as x = L · (1/√P − 1/√Pmax). Since Pmax is the top of the tick range and effectively infinite, L ≈ S · √P₀, where S is the supply. As buyers add WETH the price rises and the WETH principal inside the position becomes:

y(P) = L · (√P − √P₀)  =  S · P₀ · (√(P / P₀) − 1)

S · P₀ is the fully diluted value at launch: ≈ 1.3557 ETH. That single number characterises the whole curve. Some consequences, ignoring the 1% swap fee:

WETH principal in positionPrice multiple vs launchFDVSupply sold
01.00×1.36 ETH0%
1.36 ETH4.00×5.42 ETH50.0%
2.71 ETH9.00×12.2 ETH66.7%
4.20 ETH (graduation)16.8×22.8 ETH75.6%
8.00 ETH47.6×64.5 ETH85.5%
20.0 ETH248×337 ETH93.7%

This is the same shape as a constant-product bonding curve with virtual reserves — it is one, expressed in V3’s square-root coordinates — with the difference that there is nothing to migrate. The liquidity that traders are buying against is already on the DEX, the position already exists, and the NFT is already locked.

6.1 Why the quoter, not the chart

The displayed price is the pool’s spot price and ignores the impact of the trade’s own size. On a curve this steep a modest buy moves the price materially, so a slippage floor derived from the displayed price would sit above what the trade can actually fill and revert every large order. The app therefore quotes every trade through Quoter V2 — which simulates the swap against current pool state — and applies the user’s slippage tolerance to that quote, capped at 50%.

6.2 Trade mechanics

  • Buy: exactInputSingle on SwapRouter02 with native ETH as msg.value; the router wraps it to WETH itself. Recipient is the buyer.
  • Sell: ERC-20 approve (once, unlimited) then a router multicall of exactInputSingle with recipient address(2) — the router’s “keep it here” sentinel — followed by unwrapWETH9(minOut, seller). The slippage floor is enforced on the unwrap, so the seller receives ETH, not WETH, or the whole multicall reverts.
  • Nothing is written to the database by the client. The pool’s Swap event is the record and the indexer reads it back. Writing client-side would double-count.

7. Launch-window protections

Protections live in the token’s _update override and are evaluated only while the current block is at or before restrictionEndBlock. They apply when from is a pool for this token and WETH registered by the Uniswap factory — at any fee tier, so a pool created at a different tier to dodge the hook is still recognised — and when from is the swap router, which can hold a buy in flight. Wallet-to-wallet transfers, sells into the pool, and everything after the window are untouched.

7.1 Which block

Robinhood Chain is an Arbitrum Orbit L2, and on such chains the EVM’s block.number is the parent chain’s height. This was measured rather than assumed: on 20 September 2026 a contract read of block.number returned 26,015,315 while eth_blockNumber returned 67,532,809, and the former moves once every ~12 seconds. A window written in that unit would be neither what the documentation promised nor comparable with anything the indexer stores. The token therefore reads ArbSys(0x64).arbBlockNumber() — the height the explorer, the RPC and the indexer all count — through a gas-capped static call, and falls back to block.number only where the precompile does not answer (a plain test network). launchBlock, restrictionEndBlock and the restrictionsEndBlock field of TokenLaunched are L2 heights.

L2 blocks arrive about every 0.1 s (303 blocks in 31 s, same measurement). The shipped launch configuration sets restrictionBlocks = 300: roughly thirty seconds of caps after the launch block, which is the same order of protection the earlier block.number version gave by accident, now in the unit it claims.

7.2 The rules

BlockRule for pool → wallet and router → wallet transfers
launchBlockAll pool buys revert (LaunchBlockBuyBlocked) except the single recipient the factory registered for its atomic opening buy, and only while that registration is open. A sniper who lands in the launch block gets a revert, not a fill.
launchBlock + 1 … + 300Resulting balance must not exceed maxWalletLimit() = 5% of supply (MaxWalletExceeded), and cumulative buys per recipient must not exceed maxTxLimit() = 5.5% of supply (MaxTxExceeded). The cumulative counter is per recipient, so splitting one large buy into several within the window does not help. A transfer to the router is not counted; the transfer from the router to a wallet is, keyed by that wallet, so parking a buy in the router and sweeping it out is capped exactly like a direct buy.
launchBlock + 301 onwardNo rules. The first transfer to observe this records it, and the token is a plain OpenZeppelin ERC-20 from then on, without reading the block again.

The relationship maxTxBps = ⌊1.1 × maxWalletBps⌋ is enforced by the factory when a launch config is added; a config with any other pairing reverts InvalidMaxTxBasisPoints. The 10% headroom exists so a wallet already near the cap can still complete a buy whose rounding would otherwise fail.

8. The liquidity lock

The locker is the contract the whole design rests on, and it is deliberately small. It is 5,426 bytes compiled — identical in size to pons’s deployed locker, which is a reasonable signal that the build reproduces the verified source.

  • onERC721Received returns the magic value only when both operator and from are the factory. Any other NFT transfer reverts.
  • lockPosition(token) is onlyFactory, reads the launch record back from the factory, and reverts PositionNotHeld unless ownerOf(positionId) is the locker itself. It cannot be called twice for one token.
  • initialize(factory) binds the locker to one factory, once. AlreadyInitialized otherwise.
  • There is no function that calls decreaseLiquidity, burn, transferFrom or safeTransferFrom on the position manager, no generic execute(address, bytes), no selfdestruct, and no proxy. The only call the locker makes on the position manager after locking is collect, which withdraws accrued fees and cannot touch principal.

The consequence is that the liquidity cannot be rugged by the creator, and cannot be rugged by the protocol either. The owner of the locker can change who receives the protocol’s share of future fees, and what that share is for tokens launched after the change. It cannot remove a position, cannot redirect an existing token’s split, and cannot pause trading — trading happens on Uniswap, which the locker does not control.

9. Fees and revenue split

Trading fees are charged at two layers. The pool layer is Uniswap V3’s 1% tier, taken by Uniswap on every swap in both directions and accrued inside the locked position, because that position is the only liquidity in the pool. The router layer is CountdownTradeRouter, the app’s swap entry point, which charges a per-token creator fee and a flat protocol fee in ETH on every trade sent through it.

FeeAmountGoes to
Launch fee0.0005 ETH (owner-adjustable)Protocol fee recipient, at launch.
Creator trading fee (router)0–5%, set once by the deployer; default 2%Creator wallet, in ETH, on every routed trade.
Protocol trading fee (router)1% (hard-capped at 2%)Protocol fee recipient, in ETH, on every routed trade.
Pool fee (Uniswap)1% of every buy and sellAccrues inside the locked V3 position.
— creator share on claim70% (100 − protocolFeeShare)Deployer, or the feeWallet redirect.
— protocol share on claim30% (default; hard-capped at 50%)Protocol fee recipient.

9.0 The router layer

The pool’s fee tier is fixed by Uniswap and cannot be raised, so a creator-chosen fee has to be collected outside the pool. The router wraps SwapRouter02: on a buy it takes creatorBps + protocolBps off msg.value and swaps the remainder; on a sell it swaps to WETH, unwraps, takes the same share off the output and pays the seller the rest. The slippage floor is on the net figure. The UI shows the two router legs as one number — a 2% creator fee displays as 3% — with the pool fee listed separately.

  • setTokenFee is callable only by the token’s deployer(), only once, and only up to MAX_CREATOR_FEE_BPS = 500. The fee a trader sees is the fee for the life of the token; only the recipient wallet can move. The 5% ceiling is a constant, not an owner setting, chosen because a round trip at 5% costs 10% and a round trip at anything higher starts to resemble a honeypot.
  • The protocol leg is owner-set between 0 and MAX_PROTOCOL_FEE_BPS = 200.
  • Fees are pushed at trade time under a 30k gas stipend; a recipient that cannot accept ETH has the amount booked to pending and claims it with claimPending(). A fee recipient can never revert a trade.
  • A fee-on-transfer hook in the token was rejected: it breaks sells on Uniswap V3, gets the token flagged as a tax token, and pays the creator in tokens they must then sell. The cost of the router design is that a trade sent directly to Uniswap pays only the pool’s 1%. That trade-off is deliberate and disclosed.

9.1 Claiming

collectFees(token) on the locker calls the position manager’s collect for the maximum of both assets, reverts NoFeesToCollect if both are zero, splits each asset by the token’s snapshotted share, and transfers all four legs. Authorised callers are the locker owner, the token’s deployer, the current fee redirect recipient, and any address in feeCollectors. Fees arrive in the assets they were paid in — a mix of WETH and the token — not converted.

9.2 Why fees are read by simulation

Uniswap V3 exposes no view that returns a position’s uncollected fees after accounting for fee growth; the honest number is whatever collect would return right now. The app therefore simulates collectFees with eth_call to display the pending amount and sends the real transaction only when the creator clicks claim. A NoFeesToCollect revert in simulation is rendered as zero. Zero means “nothing accrued yet”, never “already paid” — these contracts never push fees on their own.

9.3 Snapshotting

tokenProtocolFeeShares[token] is written once, in lockPosition, from the locker’s current protocolFeeShare. setProtocolFeeShare changes only what future launches will copy. A fork test launches a token at 30%, raises the share to 50%, and asserts the first token still splits at 30%.

9.4 Fee redirects

The deployer (or the factory, during launch) can point a token’s creator share at another wallet with setFeeRedirect. The locker maintains a reverse index (feeRecipientTokens) so a profile page can list every token a wallet earns from, whether it deployed them or was assigned them.

10. Graduation

Graduation on this protocol is a label, not a migration. Nothing moves. The factory’s graduationStatus(token) view computes the WETH principal currently inside the locked position — from slot0, the position’s tick bounds and its liquidity, via the same amount formulas V3 uses — and compares it to the threshold copied at launch:

graduated = threshold ≠ 0 ∧ pairedPrincipal ≥ threshold

The indexer stores progress = min(pairedPrincipal / threshold, 1). With the deployed config (4.2 WETH) and the curve in §6, graduation corresponds to roughly a 16.8× price move from launch, an FDV near 22.8 ETH, and about 75.6% of supply sold, before fees.

11. Admin surface and what it cannot do

Both the factory and the locker use OpenZeppelin Ownable2Step: an ownership transfer must be accepted by the new owner, which prevents a typo from bricking administration. The complete list of owner-only functions:

ContractFunctionEffectAffects existing tokens?
FactoryaddDexConfig / setDexStatusRegister or disable a V3 deployment.No
FactoryaddLaunchConfig / updateLaunchConfigAdd or replace a launch parameter set.No — params are immutables in each token
FactorysetLaunchFeeChange the ETH fee for future launches.No
FactorysetLaunchEnabledOpen or close public launching.No — trading is on Uniswap
FactorysetWhitelistedLauncherAllowlist an address while public launching is closed.No
LockerinitializeBind to the factory, once.
LockersetProtocolFeeRecipientWhere the protocol share is paid.Yes, for future claims
LockersetProtocolFeeShareShare snapshotted by future launches (≤ 50).No — snapshotted
LockersetFeeCollectorAllow an address to trigger collectFees for any token.Only who may call; not the split
RoutersetProtocolFeeBps (≤ 200)Countdown Family’s leg on routed trades.Yes, from that block on
RoutersetProtocolFeeRecipientWhere Countdown Family’s leg goes.Yes, for future trades

Not possible for any owner: withdrawing or transferring a locked position; minting tokens; changing a token’s supply, caps, or restriction window; changing an existing token’s fee split; raising a creator’s trading fee or the 5% / 2% caps; pausing or blocking swaps; upgrading any contract. None of these functions exist.

12. Data integrity

The frontend serves nothing it computed itself and nothing it could not point at a block. The rules the indexer will not bend:

  • Every row in markets keeps block, blockHash and hash from its launch transaction. Every swap price point keeps its txHash.
  • A price point is either a real Swap log (source: "swap") or a real slot0 read (source: "sample"). They are labelled and never merged. No interpolation, no jitter, no synthesised history.
  • Reserves and prices are re-read from the pool on every pass rather than adjusted by event deltas, so a missed log costs one stale poll instead of permanent drift.
  • live.volume is the count of trades in the last 24 hours, not a notional USD figure. It is named honestly rather than inflated into a dollar amount that was not computed.
  • live.stale is set when a snapshot is older than PONS_STALE_MS (default 5 minutes), so the UI can say the data is old instead of presenting it as current.
  • Liquidity USD values both sides of the pool separately. A V3 pool is not balanced, so “double the WETH side” would overstate a pool that has drifted.
  • /api/config reports simulated: false and dataSource: "indexed-onchain". If a simulated mode is ever added, that flag is how the UI must gate a banner.
  • The repository contains no seed data for markets. An empty list means nothing has launched.

13. Interoperability with pons

pons is a third-party launchpad on Robinhood Chain whose contracts are verified on Blockscout and published under MIT. This protocol’s contracts are adapted from that source with two changes: type names carry a Countdown Family prefix, and launchEnabled is true from construction. pons ships it false and admits launchers by allowlist (checked 7 September 2026 on both of their factories), which is the entire reason for running a separate deployment.

Event signatures are byte-identical. TokenLaunched’s topic0 on this factory equals pons’s, so one indexer reads both factories with no second code path, and the app lists tokens from either. A fork test asserts the hash so it cannot drift silently. Trading is factory-agnostic anyway — a swap needs only the pool fee tier and WETH — and only launch metadata and fee claims walk the factory list.

pons factoryAddressCreator share
Legacy (PONS itself launched here)0x0c37a24F5D23A486FA692d1500881d698B1F77a490%
Active0xA5aAb3F0c6EeadF30Ef1D3Eb997108E976351feB70%
Countdown Family indexes public on-chain data from pons and calls its public contracts. This is not a partnership; pons is not affiliated with this project and does not endorse it.

14. Security considerations

14.1 What has been verified

  • Nineteen tests run against a fork of Robinhood Chain mainnet and the real Uniswap V3 deployment (FORK=1 npm run contracts:test): unallowlisted launch succeeds; the whole supply reaches a live pool and the rounding dust reaches the creator; everything above the fee becomes the opening buy; the creator’s buy is exempt from the caps; a later buyer is held to the caps inside the window both directly and through a parked-and-swept router buy; the caps lift after the window; the launch fee reaches the recipient; the position is locked and graduation reports progress; the fee split is snapshotted and redirects work; the launch block admits nobody but the creator; the TokenLaunched hash matches pons’s. Last full run: 60 of 60 on 20 September 2026.
  • Forty-one offline tests: seven for the token’s transfer hook against a mock pool and router (every branch of §7), twenty-two for the factory and locker (construction, the size guard, event hashes, configuration validation including tick spacing and the router requirement, the launch gate, address prediction and the deployer’s salt namespace, two-step ownership), twelve for the trade router (fee arithmetic on both legs of both directions, once-only capped creator fees, owner-only capped protocol fee, after-fee slippage floor, deferred fees, refusal of stray ETH, token rescue, zero-amount rejection).
  • The full deployment sequence — four contracts, initialisation, both configs, read-back checks — was rehearsed on a fork of mainnet with the real Uniswap addresses on 20 September 2026.
  • ReentrancyGuard on launchToken, collectFees, buy and sell; SafeERC20 for all token transfers; forceApprove reset to zero after the mint.
  • The deploy script refuses any chain other than 4663 and verifies each dependency address holds code.

14.2 What has not

14.3 Known limitations and risks

  • Mainnet only. Uniswap V3 is not deployed on Robinhood Chain testnet; there is no way to rehearse a launch or a swap with test ETH. The first of each is real money.
  • Uncapped creator buy. A creator can buy a large fraction of supply at launch. It is public in the event and the token page, but it is possible.
  • Curve steepness. With FDV ≈ 1.36 ETH at launch, small buys move price a lot. This is the nature of a launchpad, not a bug, but slippage settings matter.
  • Rate limiter is database-backed. Server-side write limits live in a rate_limits table, so every instance shares one window; a limit is only as fresh as the database round-trip, and the client IP behind a proxy is only as trustworthy as the proxy.
  • Public RPC. Robinhood’s public endpoint rate limits hard. The indexer paces and backs off, but production needs a dedicated endpoint or snapshots will go stale.
  • Build settings are load-bearing. viaIR is required or the factory does not compile; evmVersion: shanghai is what the artefacts were verified with. Cancun is avoided because TSTORE/MCOPY support varies across Orbit chains. Bytecode size is no longer marginal: the factory is 15,699 bytes of the 24,576 allowed since the token bytecode moved into the deployer.

14.4 What the September 2026 review changed

An internal review before mainnet found four things in the adapted contracts. Each was fixed in Solidity, tested, and recorded with its measurements in docs/CONTRACT-NOTES.md.

  • The window unit. The token measured its window in block.number, which on this chain is Ethereum’s height (§7.1). It now measures it in L2 blocks.
  • The router sweep. SwapRouter02 could leave a buy inside itself and hand it out with sweepToken; that second leg was not a pool transfer and was uncapped. It is capped now.
  • Tick alignment. A launch config whose initialTick was not a multiple of the pool’s spacing was accepted and would have reverted every launch deep inside Uniswap after the fee was paid. The factory now checks it first (InvalidInitialTick).
  • Smaller items. A DEX config without a router is rejected; the mint’s rounding dust goes to the creator instead of sitting in the factory; the router refuses ETH from anything but WETH and can return tokens sent to it by mistake; licence headers say what each file actually is.

15. Robinhood Chain

Robinhood Chain is an Arbitrum Orbit L2 that settles to Ethereum and uses ETH for gas. It is a different network from Unichain. The app pins chain 4663 for every write regardless of other configuration.

MainnetTestnet
Chain ID466346630
RPCrpc.mainnet.chain.robinhood.comrpc.testnet.chain.robinhood.com
Explorerrobinhoodchain.blockscout.comexplorer.testnet.chain.robinhood.com
Uniswap V3DeployedNot deployed
This protocolTargetCannot run (no V3)
Measured on 20 September 2026Value
eth_blockNumber (L2 height)67,532,809
block.number inside the EVM (Ethereum height)26,015,315
ArbSys(0x64).arbBlockNumber()67,532,835
L2 block time≈ 0.10 s (303 blocks in 31 s)
Gas for ArbSys.arbBlockNumber()≈ 1.4k

Roughly 860,000 L2 blocks a day. Two things follow. Anything on this chain that reasons in block.number is reasoning in Ethereum’s clock, twelve seconds per tick; and a launch window of 300 L2 blocks is about thirty seconds of wall-clock time. The protection is about ordering, not duration.

The public RPC hostname is DNS-filtered on Indonesian ISPs. The app relays browser RPC through its own origin (/api/rpc), and the repository ships a local forwarder (npm run rpc:proxy) for development and deployment from such networks.

16. Licensing

CountdownLauncherToken.sol, CountdownLaunchLocker.sol, CountdownTradeRouter.sol and CountdownTokenDeployer.sol are MIT. TickMath.sol and LiquidityMath.sol are Uniswap’s, GPL-2.0-or-later, and copyleft carries into any contract that includes them, so CountdownLaunchFactory.sol is GPL-2.0-or-later. The contracts are published and verified either way; the practical consequence is that the factory cannot be relicensed as proprietary.

17. Status and roadmap

ItemStatus
Contracts written, compiled, fork-testedDone
Pre-mainnet review: window unit, router sweep, tick alignment, bytecode sizeDone — 20 September 2026, see §14.4
Deployment rehearsed end to end on a mainnet forkDone — 20 September 2026
Indexer running under PM2 against pons factoriesDone
Client hooks for launch / swap / quote / fee claimDone
Deploy the four contracts to mainnetPending — needs a funded deployer key and a treasury address (docs/DEPLOY-MAINNET.md); until then launching falls back to pons’s gated factory and is blocked with a clear message; trading works
First mainnet swap and launch, reconciled against the indexerPending
Hosted libSQL (Turso) for productionPending — code supports it
Shared-store rate limiterDone — a rate_limits table in the same database, so every instance sees one window
Automated tests for client write modulesPending
Independent auditNot scheduled
Remove the retired database-settled trading path (curve maths, its API routes and indexer)Done

18. Questions a careful reader asks

If nothing migrates, what is “graduation” for?

It is a label, and only a label. When the locked position holds 4.2 WETH of principal the market is marked graduated. The number is exposed by graduationStatus so a screener can sort by it; no transaction happens and nothing moves (§10).

Who can withdraw the liquidity?

Nobody. The locker has no function that transfers, burns or decreases a position; the only position-manager call it makes after locking is collect (§8). This is the easiest claim in the document to check: read the verified source and look for the absence.

Why is the creator’s opening buy uncapped?

Because it is atomic with the launch and there is nothing to race. Capping it would only move the same purchase to the next block, where it would compete with everyone else. It is public in the launch event and on the token page (§5, §14.3).

Why are there two fees on a trade through the app and one on a trade elsewhere?

The creator fee and the protocol fee are collected by the trade router in ETH, not by the token. A trade sent straight to Uniswap pays only the pool’s 1%. The alternative — a fee-on-transfer token — breaks sells on V3 and gets the token flagged as a tax token, so the router is a front door, not a wall (§9).

What does the owner of the protocol control?

Future fee policy within hard caps, which configurations are enabled, and whether public launching is open. Not any locked position, not any token’s supply, not any trade (§11).

Why measure the launch window in L2 blocks rather than Ethereum blocks?

Because on this chain block.number is Ethereum’s, which was measured and not assumed (§7.1). A window in that unit would be ~12 s per block and incomparable with the chain’s own height.

Where do the numbers on the site come from?

From an indexer that reads the chain: launch events, Swap logs, slot0 and position reads, each stored with its block and transaction. There is no seed data and no demo mode; an empty list that is true is preferred to a full one that is not (§12).

What happens if a contract turns out to be wrong after deployment?

Nothing is upgradeable. The response is a new deployment, appended to the list of factories the indexer follows so existing tokens keep being indexed and traded. Launching on an old factory can be closed; trading on its pools cannot be stopped by anyone (§11, §17).

Has anyone independent reviewed this?

No (§14.2). The tests, the fork rehearsal and the internal review are described; an audit is not.

19. Disclaimer

This document describes software. It is not investment advice, an offer to sell, or a solicitation to buy any token. Tokens launched through the protocol are created by their deployers, not by Countdown Family, and the protocol makes no representation about any of them. Concentrated-liquidity markets with small initial depth are volatile; a buyer can lose the entire amount spent. The contracts are unaudited and provided as-is under their respective licences. Nothing here should be read as a promise that any feature listed as pending will ship.