CooCoo

Integration docs

What to read and what to call to list CooCoo launches and trade them: on their bonding curves first, then in the locked Uniswap v4 pools they graduate into. Chains: Robinhood Chain and Arc.

What it is

A token launches with a fixed supply of 1,000,000,000 (18 decimals) on a bonding curve paired with a quote asset. When the curve has raised its graduation threshold, the launch graduates: the raised quote and the tokens held back for it are placed as one full-range position in a Uniswap v4 pool behind our hook, and that position is locked for good. Trading happens on the curve until graduation and in the pool after it.

The contracts are Pons V2 without modification, byte-identical to the live Pons deployment on Robinhood Chain. If you already integrate Pons there, point the same code at our factory addresses. Two peripheral contracts on Arc carry a small patch for Arc's native USDC; they change no interface.

Addresses

Robinhoodchain id 4663RPC https://rpc.mainnet.chain.robinhood.comexplorerdeployed at block 63,606,612

FactoryPonsV2LaunchFactory0x7F02F7d1022E6124b6DDBfAaE7Df6da04dE80083
HookPonsV2MemeHook, on every graduated pool0x37966005085dD9F7Fb7F9d51E65daf5FeA552044
Launch-and-buy forwarder0xED0F9a0e7472Edb5dCEb2e72Eacc616659aE177d
Launch deployer0xB838D702d80bFA9b3ed7f741dcD04dF253385e4f
Lockerholds every pool position, for good0xbaEEcA4975cB1c4722E82753b1fa9cbF3C595313
Fee escrow0xB1703f631084fec914cE2834F29EBEb8e07d3fb4
Buyback vault0x3681239c40b0d13AeaE9c771d7a0261D1C96B452
Graduation executor0xE372AF122Ef5460f7f4C28d29D7EbdF0A67d5b03
Graduation guard0xe4c664ed351C51cB4d37ce367297f500eC8FC6F3
Uniswap v4 PoolManager0x8366a39CC670B4001A1121B8F6A443A643e40951
Uniswap v4 PositionManager0x58daec3116aae6D93017bAAea7749052E8a04fA7
Universal Router0x8876789976decbfcbbbe364623c63652db8c0904
V4 Quoter0x8dc178efb8111bb0973dd9d722ebeff267c98f94
StateView0xf3334192d15450cdd385c8b70e03f9a6bd9e673b
Permit20x000000000022D473030F116dDEE9F6B43aC78BA3
Quote: ETHnative, 18 decimals0x0000000000000000000000000000000000000000
Quote: USDG6 decimals0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
Quote: cbBTC8 decimals0xCEC185eB182c47d1bA1EFc84e6959e18cd620Be4
Quote: TAO18 decimals0xf3081494B87e8D5fb7960f066E931D1D0e6E3d67

Plus 192 Robinhood Stock Tokens; each approved one answers approvedPairTokens true.

Arcchain id 5042RPC https://rpc.mainnet.arc.iono public explorer yetdeployed at block 21,148,973

FactoryPonsV2LaunchFactory0xc954f286027067dfce8b50dCE46dbe8D683E9002
HookPonsV2MemeHook, on every graduated pool0x915bF3CB732Bb692D63EcA08f962D62221CFE044
Launch-and-buy forwarder0xee6430930F1a47bc19b0Ccd988e6a060Ce763910
Launch deployer0x6004695B5DDB67E715E5e04d38987D9FaeA5F9d8
Lockerholds every pool position, for good0x29B591B2fe7e7e30fBbC273FDc054143edfCBd12
Fee escrow0x21524873EFaB3Af236A35eA287099eD1fBe4c4cD
Buyback vault0x0659cBd25D738011c03F139222ee531dDB854e61
Graduation executor0x5f4265F231b7f26a1B3a158c8Fbcf139FbCe5b05
Graduation guard0xF839f84428f0ac687d89B557B206E122c3eAE12A
Uniswap v4 PoolManager0x8366a39cc670b4001a1121b8f6a443a643e40951
Uniswap v4 PositionManager0x6049c9a0e26405c0985f9e3685c87d0ae917f82b
Universal Router0x4fca4a51ab4f23a7447b3284fbd7d73289a89fb1
V4 Quoter0x8dc178efb8111bb0973dd9d722ebeff267c98f94
StateView0xf3334192d15450cdd385c8b70e03f9a6bd9e673b
Permit20x000000000022D473030F116dDEE9F6B43aC78BA3
Quote: USDC6 decimals, the same balance as native gas0x3600000000000000000000000000000000000000

Discovering launches

Every launch is one event on the factory, from its deploy block onward. It names the token, its bonding curve and the quote asset it pairs with; a pairToken of the zero address is native ETH, on Robinhood Chain only.

"event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)"

Read a launch's state at any time from the factory:

"function getLaunchedToken(address token) view returns ((address token, address curve, address deployer, address creatorFeeRecipient, address pairToken, uint256 graduationThreshold, uint24 poolFee, int24 tickSpacing, uint16 creatorTaxBps, bool buybackEnabled, uint8 phase, uint256 sweptQuote, uint256 sweptTokens, uint256 sweptAt, bool exists))"
phaseMeaningWhere it trades
0NotGraduatedThe bonding curve.
1Swept: graduated, pool not yet createdNowhere, for a block or two, until createGraduatedPool runs; anyone may call it, and our keeper does.
2PoolCreatedThe Uniswap v4 pool.
3Rescued: an owner rescue after a stuck graduationTreat as delisted.

The token is a plain ERC-20 with name(), symbol(), 18 decimals, and views for the logo, description and socials its creator set at launch.

Trading on the curve

Each launch has its own curve contract. Two functions do all the trading, and both return the amount received, so an eth_call of the same call is an exact quote.

"function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)"
"function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)"
  1. ERC-20 quote (USDG, cbBTC, TAO, a Stock Token, Arc's USDC): approve the curve for quoteIn, then call buy with no value.
  2. Native ETH quote (Robinhood Chain): call buy with value equal to quoteIn.
  3. Selling: approve the curve for tokensIn, then sell. The quote comes back to recipient, native or ERC-20 as the pair is.

Pricing is constant-product over virtual reserves: quoteReserve() is the real quote plus a fixed phantom amount, tokenReserve() the tokens still on the curve. With the fee and any tax taken from the input first, a buy gives tokensOut = tokenReserve × in′ / (quoteReserve + in′) where in′ = quoteIn × (10000 − feeBps − taxBps) / 10000. A sell is the same curve in reverse, with the fee and tax taken from the proceeds. A buy larger than sellableTokens() is clamped and the unspent quote refunded in the same transaction.

Views for quoting, and the events for indexing trades:

"function getReserves() view returns (uint256 quoteReserve_, uint256 tokenReserve_)"
"function quoteReserve() view returns (uint256 quoteReserve_)"
"function tokenReserve() view returns (uint256 tokenReserve_)"
"function phantomQuote() view returns (uint256)"
"function feeBps() view returns (uint256)"
"function creatorTaxBps() view returns (uint256)"
"function currentSnipeTaxBps(address recipient) view returns (uint256)"
"function sellableTokens() view returns (uint256)"
"function graduationThreshold() view returns (uint256)"
"function readyToGraduate() view returns (bool)"
"function graduated() view returns (bool)"
"event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)"
"event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)"
"event CurveBuyRefunded(address indexed buyer, uint256 refund)"
"event CurveCompleted(address recipient, uint256 quoteOut, uint256 tokenOut)"

A buy that crosses the graduation threshold graduates the launch inside the same transaction. Send it with generous gas: a bare eth_estimateGas often lands on the path where that inner graduation runs out of gas and is caught, leaving the curve readyToGraduate(). We send the estimate plus a quarter plus 450,000. If it slips through, anyone can call graduate(token) on the factory.

Graduation and the pool

When the real quote reserve reaches graduationThreshold, the curve sweeps its reserves to the factory (LaunchSwept, phase 1). Then createGraduatedPool(token), callable by anyone, initialises the Uniswap v4 pool, mints the full-range position and hands it to the locker (PoolGraduated, phase 2). The position can never be withdrawn.

"event LaunchSwept(address indexed token, uint256 quoteOut, uint256 tokenOut)"
"event PoolGraduated(address indexed token, uint256 positionId, uint256 tokenAmount, uint256 pairTokenAmount)"

PoolKey {
  currency0, currency1 = the token and the quote, sorted by address (native ETH is address(0))
  fee         = getLaunchedToken(token).poolFee
  tickSpacing = getLaunchedToken(token).tickSpacing
  hooks       = the Hook address for that chain
}

Trade the pool as any v4 pool: the Universal Router's V4_SWAP with SWAP_EXACT_IN_SINGLE, SETTLE_ALL and TAKE_ALL, Permit2 for ERC-20 input, and the V4 Quoter for quotes. The hook takes its fee and the creator's tax inside the swap and emits HookFeeCollected; a swap log's amounts are gross of that, so net the fee off the unspecified side when you index pool trades.

Fees and terms

Robinhood

TermValue
Launch fee0.0005 ETH
Curve fee, per trade1%
Pool fee, per swap, taken by the hook1%
Launch-window tax on buys99% at launch, falling to nothing over 3 seconds; addresses the creator exempted pay none
Creator tax0 to 10% per trade for the token's life, set at launch
Graduation target, ETH4.2 ETH raised (about $10.1k today)
Graduation target, USDG8,090 USDG raised, over a phantom reserve of 3,236
Graduation target, cbBTC0.1049 cbBTC raised, over a phantom reserve of 0.04196
Graduation target, TAO36.05 TAO raised, over a phantom reserve of 14.42
Graduation target, Stock TokensPegged per token when it was listed; read pairTokenEconomics for the exact figure
Supply1,000,000,000 tokens; the share held back for the pool is supply × phantom / (phantom + threshold)

Arc

TermValue
Launch fee2 USDC, sent as native value
Curve fee, per trade1%
Pool fee, per swap, taken by the hook1%
Launch-window tax on buys99% at launch, falling to nothing over 3 seconds; addresses the creator exempted pay none
Creator tax0 to 10% per trade for the token's life, set at launch
Graduation target, USDC8,090 USDC raised, over a phantom reserve of 3,236
Supply1,000,000,000 tokens; the share held back for the pool is supply × phantom / (phantom + threshold)

Per-pair economics live on the factory: approvedPairTokens(address) and pairTokenEconomics(address). Native ETH's live in launch config 0, getLaunchConfig(0). A Stock Token can be paused, can block addresses and can be burned or split by its issuer; a launch paired with one inherits that. Circle can pause or blocklist USDC on Arc.

ABI

Human-readable, ready for ethers or viem's parseAbi, printed from the ABIs this site itself sends to. The full JSON ABIs are Pons V2's; ask and we'll send them.

Factory

"event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)"
"event LaunchSwept(address indexed token, uint256 quoteOut, uint256 tokenOut)"
"event PoolGraduated(address indexed token, uint256 positionId, uint256 tokenAmount, uint256 pairTokenAmount)"
"function getLaunchedToken(address token) view returns ((address token, address curve, address deployer, address creatorFeeRecipient, address pairToken, uint256 graduationThreshold, uint24 poolFee, int24 tickSpacing, uint16 creatorTaxBps, bool buybackEnabled, uint8 phase, uint256 sweptQuote, uint256 sweptTokens, uint256 sweptAt, bool exists))"
"function approvedPairTokens(address pairToken) view returns (bool approved)"
"function pairTokenEconomics(address pairToken) view returns (uint256 phantomQuote, uint256 graduationThreshold, uint8 decimals)"
"function launchEnabled() view returns (bool)"
"function launchFee() view returns (uint256)"
"function graduate(address token)"
"function createGraduatedPool(address token) returns (uint256 positionId)"

Bonding curve, one per launch

"function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)"
"function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)"
"function getReserves() view returns (uint256 quoteReserve_, uint256 tokenReserve_)"
"function quoteReserve() view returns (uint256 quoteReserve_)"
"function tokenReserve() view returns (uint256 tokenReserve_)"
"function phantomQuote() view returns (uint256)"
"function feeBps() view returns (uint256)"
"function creatorTaxBps() view returns (uint256)"
"function currentSnipeTaxBps(address recipient) view returns (uint256)"
"function sellableTokens() view returns (uint256)"
"function graduationThreshold() view returns (uint256)"
"function readyToGraduate() view returns (bool)"
"function graduated() view returns (bool)"
"function pairToken() view returns (address)"
"function token() view returns (address)"
"event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)"
"event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)"
"event CurveBuyRefunded(address indexed buyer, uint256 refund)"
"event CurveCompleted(address recipient, uint256 quoteOut, uint256 tokenOut)"

CooCoo is run by OpenLegion LLC. Questions: @CooCooParty on X. About · Terms