Integration docs

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

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. Once the curve has raised its graduation threshold, the launch graduates: the raised quote and the tokens held back for it open one full-range position in a Uniswap v4 pool behind our hook, at the curve's final price, locked for good. Held-back tokens the position doesn't take are locked too and never trade.

Trading runs on Pons V2, byte-identical to the live Pons deployment on Robinhood Chain: if you already integrate Pons trades there, point the same code at our factory addresses. Launching goes through ReafundLauncher, which binds each launch to its project and gives it its own fee splitter.

Addresses

Robinhoodchain id 4663RPC https://rpc.mainnet.chain.robinhood.comexplorerdeployed at block 90,281,269

Treasury and ownerevery trade fee and launch fee; the owner Safe0x6fba0d0079302732515A945161fcF7d36818B6a5
LauncherReafundLauncher, the factory's only whitelisted launcher0x012b5455677d9D8A263E3Ca403004a1bAB499988
Developer vaultReafundDevVault, each project's share until its owner claims it0x0Bce2dAFe303F3D96f4236470E18354eb9283242
FactoryPonsV2LaunchFactory0x182a7992cB8c18AbE1f255388A665DE5dB39d26d
HookPonsV2MemeHook, on every graduated pool0xe6dA98Faca06d7db50a42f93cA79Fd6D1fFBa044
Launch deployer0xcA2Cb108A7cCC5F989aDCD6fc015E2281B3AFE2d
Lockerholds every pool position, for good0xfC766B6EC1F99c9708E8629426220de2F5354A92
Fee escrow0x11a21c0639f38375f70B663d73fA2085e30d20c2
Buyback vault0x47cE0adC28FA718E819f5260dD3DAF51bB52c163
Graduation executor0x1dEf19c3875fdB6C3c0E30083d4F483B9C69117f
Graduation guard0x2b4ffBaebAa1cB850d05Afc0437f08d405095b34
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.

Discovering launches

Every launch is one event on the factory: index the factory from its deploy block. The event names the token, its bonding curve and its quote asset (a pairToken of the zero address is native ETH, on Robinhood Chain only). Every launch goes through the launcher, so the factory's deployer is always the launcher. The launcher's own event, in the same transaction, names the launching wallet, the open-source project the launch is bound to (keccak256 of github:<repository id>), its fee splitter, the creator fee and the project's share of it. The splitter pays the trade fee's part of what it receives to the protocol fee recipient and splits the rest, the creator fee, between the launcher and the project.

"event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)"
"event Launched(address indexed token, address indexed creator, bytes32 indexed projectId, address curve, address splitter, address quote, uint16 devShareBps, uint16 creatorTaxBps, uint256 quoteSpent, uint256 tokensOut)"

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 or a Stock Token): approve the curve for quoteIn, then call buy with no value.
  2. Native ETH quote: 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 trade fee and any creator fee taken from the input first, a buy gives tokensOut = ⌊tokenReserve × in′ / (quoteReserve + in′)⌋ where in′ = quoteIn − ⌊quoteIn × feeBps / 10000⌋ − ⌊quoteIn × taxBps / 10000⌋, less any snipe tax. A sell is the same curve in reverse, with the trade fee and creator fee 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 in 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 one 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 at the curve's final price, mints the full-range position and hands it to the locker (PoolGraduated, phase 2). The position can never be withdrawn. Swept tokens the position doesn't take at that price stay in the locker for good (GraduationTokensPermanentlyLocked).

"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 fee 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%
Snipe tax on buys, in the launch window99% at launch, falling to nothing over 3 seconds; the launching wallet pays none
Creator fee1% to 10% per trade for the token's life, set at launch; the least is signed into the binding, and only $REAFUND's own launch has none
Where the trade fee goesAll of it to the protocol fee recipient: 30% of each fee paid by Pons directly (currentFeePolicy().protocolFeeShareBps), the rest forwarded by the launch's ReafundFeeSplitter
Project's share of the creator feeAt least 50%, signed into the binding and fixed at launch (Launched.devShareBps); the rest is the launcher's, credited in the fee escrow
Protocol fee recipient (treasury)0x6fba0d0079302732515A945161fcF7d36818B6a5
Developer vault, a payout addressTakes effect 3 days after it is proposed (NOTICE), executable by anyone for 7 days after that (EXECUTION_WINDOW)
A launch's creator fee recipientThe owner can change it after 3 days (CREATOR_FEE_RECIPIENT_TIMELOCK)
Graduation target, ETH4.2 ETH raised (about $10.2k today)
Graduation target, USDG8,090 USDG raised, over a phantom reserve of 3,236
Graduation target, cbBTC0.09947 cbBTC raised, over a phantom reserve of 0.039788
Graduation target, TAO29.37 TAO raised, over a phantom reserve of 11.748
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)

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's issuer can pause it, block addresses, and burn or split it; a launch paired with one inherits that.

What the owner and issuers can do

The contracts have an owner, and some pairs have an issuer. This is everything either can do to a launch.

  • OwnerName a project's payout addressTo settle a dispute or recover a lost account, after 3 days of public notice. It can name any address, its own included: the notice is the guard. A project already paid can withdraw what is held before the change takes effect.
  • OwnerRedirect a launch's creator feeChange where a launch's creator fee goes, the project's part with it, after 3 days of public notice, which Pons enforces.
  • OwnerRescue a stalled graduationIf a graduated launch's pool still isn't created 7 days later, send its reserves to an address it chooses.
  • OwnerChange the terms of future launchesThe trade fee, the launch fee, the creator fee cap, the graduation targets, the launch window and the approved pairs; a launch already made keeps its own. And hide a launch from this site, though it keeps trading on chain.
  • IssuerPause, block or burn a Stock TokenRobinhood can do all three to its Stock Tokens, from any address, the fee escrow and the developer vault included. A launch paired with one inherits that.

What nobody can do

  • Take a trading curve's reserves.
  • Take a graduated pool's liquidity: it is locked for good.
  • Change a launch's name, ticker, project or pair.
  • Hold your funds or your keys: every transaction is yours.

ABI

Full JSON ABIs for the launchpad contracts, to share with exchanges and indexers.

Below, selected functions and events in the human-readable form ethers and viem's parseAbi take. Both come from the ABIs this site itself uses.

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)"

How it works · Terms