FOMONICLaunch a coin

How FOMONIC works

FOMONIC is a launchpad on Arc, the Circle chain where gas is paid in USDC. A coin launched here goes straight into a Uniswap v4 pool paired with USDC. There is no bonding curve, no graduation and no team allocation: the pool is the market from the first block. Every swap through that pool pays a 15% fee, and every sell burns 2% of the coins sold. This page covers what happens on a launch, where the money goes, and how to do all of it from code.

Launching a coin

Fill in a name, a ticker, a logo and the links on the Launch page, optionally add a first buy, and press the button. One transaction does all of this:

  • deploys the coin — a fixed supply of 1,000,000,000, 18 decimals, no mint function and no owner that can pause or blacklist anything
  • creates the Uniswap v4 pool against USDC, attaches FomonicHook to it, and prices it at a $3,000 market cap
  • puts the entire supply into that pool single-sided, so every coin in existence is buyable by anyone from the first block
  • keeps the resulting liquidity position inside the FOMONIC contract
  • runs your first buy, if you asked for one, in the same transaction — before anyone else can react

A launch costs about 6.2 million gas. On Arc that is a few cents, paid in USDC, plus the launch fee shown on the page.

Because the whole supply is in the pool from the start, the price is decided by the market immediately. There is no snapshot, no whitelist and no moment where an insider can buy cheaper than you — except a first buy, which happens in public, in the launch transaction.

Why the liquidity cannot be pulled

The liquidity position is a Uniswap NFT, and it belongs to the FomonicLaunch contract. What matters is not who holds it but what the contract is able to do with it: the code has no function that decreases, transfers, burns or withdraws that position. Not for the creator, not for the platform, not for the owner key.

The only thing the contract ever does with the position is call collect on it to take the trading fees, and those fees can only be sent to the addresses fixed at launch. You do not have to take this on faith — read the verified source on the explorer and look for the missing functions.

What this does not mean: locked liquidity is not a promise about price. The price can still go to zero if everyone sells, and it usually does. It only means the pool itself cannot disappear from under you.

Fees

WhatWhere it goes
Pool fee — 1% of every trade70% to the creator, 30% to the platform, in USDC. The coin side of the fee is split the same way.
Buyback-burn — the creator choosesInstead of being paid out, the creator share buys the coin from the pool and burns it.
Launch feeA small USDC amount per launch, to the platform. Read it on-chain with launchFee().

Fees sit in the pool until somebody collects them. Anyone can press "Claim pool fees" on a coin page, or call claimFees(token) directly — the caller gets nothing for it, the money always goes to the fixed recipients.

Claim the fees of a coin
// anyone may call this; the money always goes to the fixed recipients
const pad = new ethers.Contract(LAUNCHPAD, [
  'function claimFees(address token)',
  'function getLaunch(address token) view returns (tuple(address creator,address feeRecipient,address pair,bool coinIs0,bool buyback,bool recipientChanged,bool exists,uint256 feesTok,uint256 feesQuote,uint40 launchedAt))',
], signer);

const info = await pad.getLaunch(coin);
console.log('collected so far:', ethers.utils.formatUnits(info.feesQuote, 6), 'USDC');

await (await pad.claimFees(coin, { gasLimit: 1200000 })).wait();

Trading

The Buy / Sell box goes through FomonicRouter. A Uniswap v4 swap has to happen inside the PoolManager’s lock, so something must hold that lock; the router does, keeps no funds and holds no position, and pays you in the same transaction. The estimate you see is not fetched from a quoter — it is computed in your browser from the pool’s √price and liquidity, which is exact here because each coin has exactly one full-range position. Any interface that supports Uniswap v4 on Arc can trade these coins too. Default slippage on the site is 3%.

You need USDC on Arc for both the trade and the gas.

Setup
import { ethers } from 'ethers';

const CHAIN = 5042;
const LAUNCHPAD = '0x50715401ad67c6cfdaf09f71569657c8fdcb3928';
const USDC     = '0x3600000000000000000000000000000000000000';         // 6 decimals, gas on Arc is paid in it
const ROUTER   = '0x6e72061a66419fa96e065c1849d85332a5f42f5a';   // FomonicRouter — buy / sell
const PM       = '0x8366a39cc670b4001a1121b8f6a443a643e40951';   // Uniswap v4 PoolManager
const HOOK     = '0x6336dfa1e077c860d5e13a6ae1c0b7fc5c830088';   // FomonicHook — sets the fee, burns on sells

const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = provider.getSigner();
const me = await signer.getAddress();
Buy with USDC
// FomonicRouter does the whole swap in one call: it takes USDC, unlocks the pool, and sends
// the coins straight to you. minOut is enforced inside the transaction, so a pool that moved
// reverts instead of filling at a price you never agreed to.
const router = new ethers.Contract(ROUTER, [
  'function buy(address coin, uint256 amountIn, uint256 minOut) payable returns (uint256)',
  'function sell(address coin, uint256 amountIn, uint256 minOut) returns (uint256)',
], signer);

const coin = '0xYourCoinAddress';
const amountIn = ethers.utils.parseUnits('20', 6);        // 20 USDC

// there is no quoter contract on Arc and none is needed: every coin here has one full-range
// position, so L never changes mid-swap and the closed form below is exact, not an estimate.
const t = await fetch(`https://fomonic.fun/api/d/api/token/${coin}`).then((r) => r.json());
const Q96 = ethers.BigNumber.from(2).pow(96);
const P = ethers.BigNumber.from(t.sqrtPriceX96), Lq = ethers.BigNumber.from(t.poolLiq);
const afterFee = amountIn.sub(amountIn.mul(t.feePips).div(1000000));   // 15% pool fee
const P2 = t.isToken0
  ? Lq.mul(Q96).mul(P).div(Lq.mul(Q96).add(afterFee.mul(P)))           // paying currency0
  : P.add(afterFee.mul(Q96).div(Lq));                                  // paying currency1
const out = t.isToken0 ? Lq.mul(P.sub(P2)).div(Q96) : Lq.mul(Q96).mul(P2.sub(P)).div(P2).div(P);
const minOut = out.mul(9700).div(10000);                  // 3% slippage

const usdc = new ethers.Contract(USDC, ['function approve(address,uint256) returns (bool)'], signer);
await (await usdc.approve(ROUTER, amountIn)).wait();

await (await router.buy(coin, amountIn, minOut, { gasLimit: 700000 })).wait();
Sell back into USDC
// A sell is taxed twice before it reaches the pool: the hook burns 2% of the coins going in
// (until 500,000,000 are dead), and then the 15% pool fee applies to what is left. Price the
// trade the same way as a buy, with those two cuts taken off the input first.
const coinC = new ethers.Contract(coin, [
  'function approve(address,uint256) returns (bool)',
  'function balanceOf(address) view returns (uint256)',
], signer);

const amountIn = (await coinC.balanceOf(me)).div(2);      // sell half
await (await coinC.approve(ROUTER, amountIn)).wait();

const afterBurn = amountIn.sub(amountIn.mul(t.sellBurnBps).div(10000));
const netIn = afterBurn.sub(afterBurn.mul(t.feePips).div(1000000));
const P2 = t.isToken0
  ? Lq.mul(Q96).mul(P).div(Lq.mul(Q96).add(netIn.mul(P)))
  : P.add(netIn.mul(Q96).div(Lq));
const usdcOut = t.isToken0 ? Lq.mul(P.sub(P2)).div(Q96) : Lq.mul(Q96).mul(P2.sub(P)).div(P2).div(P);

await (await router.sell(coin, amountIn, usdcOut.mul(9700).div(10000), { gasLimit: 700000 })).wait();

Launching from code

The website is only one client. Approve USDC for the launch fee plus your first buy, then call launch() on the launchpad. Estimate the gas rather than hard-coding it — the call deploys a contract, opens a pool and mints a position, which is far more than a normal transfer.

launch()
const LAUNCH_ABI = [
  'function launchFee() view returns (uint256)',
  'function launch(string name, string symbol, (string uri,string logo,string description,string website,string twitter,string telegram) meta, bool buyback, address pair, uint256 pairBuyAmount) returns (address)',
];
const usdc = new ethers.Contract(USDC, [
  'function approve(address,uint256) returns (bool)',
  'function allowance(address,address) view returns (uint256)',
], signer);
const pad = new ethers.Contract(LAUNCHPAD, LAUNCH_ABI, signer);

const firstBuy = ethers.utils.parseUnits('25', 6);        // optional, 0 to skip
const fee = await pad.launchFee();
const need = fee.add(firstBuy);
if ((await usdc.allowance(me, LAUNCHPAD)).lt(need)) {
  await (await usdc.approve(LAUNCHPAD, ethers.constants.MaxUint256)).wait();
}

const meta = {
  uri: '', logo: 'ipfs://…', description: 'what this coin is about',
  website: 'https://example.com', twitter: 'https://x.com/handle', telegram: '',
};

// one transaction: deploy the coin, open the pool, seed the whole supply, keep the position
const gas = await pad.estimateGas.launch('My Coin', 'MINE', meta, false, USDC, firstBuy);
const tx = await pad.launch('My Coin', 'MINE', meta, false, USDC, firstBuy, {
  gasLimit: gas.mul(125).div(100),                        // a launch costs ≈6.2M gas
});
const receipt = await tx.wait();
console.log('launched in', receipt.transactionHash);

Set buyback to true if you want your fee share to buy the coin back and burn it instead of being paid to you; you can change it later with setBuyback(token, on).

Data API

Every number on this site comes from a public, read-only JSON API. No key, no sign-up, CORS is open, and responses are cached for a few seconds at the edge — polling it every few seconds is fine.

EndpointReturns
GET /api/tokens?sort=new|trending|mc|vol&q=&limit=every coin, with market cap, volume, holders
GET /api/token/:addressone coin + last 50 trades + top 20 holders
GET /api/candles/:address?tf=5m&limit=300OHLC candles, price in USDC
GET /api/trades?limit=50the newest trades across all coins
GET /api/wallet/:addressholdings, coins created, trades of one wallet
GET /api/statstotals: coins, 24h volume, USDC in pools
GET /api/configchain id and the addresses above

Base URL: https://fomonic.fun/api/d

One coin
// price, market cap and the last trades of one coin — no wallet, no key
const r = await fetch('https://fomonic.fun/api/d/api/token/0xYourCoinAddress');
const t = await r.json();

console.log(t.symbol, t.price, t.mc, t.liquidityUsd, t.holders);
t.tradesList.slice(0, 5).forEach((x) =>
  console.log(x.side, Number(x.amountUsdc) / 1e6, 'USDC', x.trader));
The coin list
// every coin on the launchpad, newest first
const { rows, total } = await fetch('https://fomonic.fun/api/d/api/tokens?sort=new&limit=50').then((r) => r.json());

for (const t of rows) {
  console.log(`$${t.symbol}  ${t.name}  mc $${Math.round(t.mc)}  ${t.trades} trades`);
}
console.log(total, 'coins');
Candles for a chart
// OHLC candles for a chart: tf = 1m | 5m | 15m | 1h | 4h | 1d
const { rows } = await fetch('https://fomonic.fun/api/d/api/candles/0xYourCoinAddress?tf=5m&limit=300')
  .then((r) => r.json());

// [{ t: unix seconds, o, h, l, c, v }]  — price is USDC per coin
const marketCaps = rows.map((k) => ({ time: k.t, value: k.c * 1e9 }));
Watch for new launches
// poll the coin list and react to a new launch (the CDN caches for a few seconds)
let seen = new Set();
setInterval(async () => {
  const { rows } = await fetch('https://fomonic.fun/api/d/api/tokens?sort=new&limit=20').then((r) => r.json());
  for (const t of rows.reverse()) {
    if (seen.has(t.address)) continue;
    seen.add(t.address);
    console.log('new coin', t.symbol, t.address, 'mc', t.mc);
  }
}, 5000);

Contract reference

FunctionWhat it does
launch(name, symbol, meta, buyback, pair, pairBuyAmount)deploy + open the pool + seed the supply, in one call
claimFees(token)collect the pool fees and pay them out (anyone may call)
setBuyback(token, on)creator: pay my share out, or buy back and burn
setFeeRecipient(token, to)creator: move my fee payouts, once
getLaunch(token)creator, fee recipient, pool, position id, fees collected
launchFee()the USDC fee charged per launch
FEE() · SPACING()the pool tier the launchpad was deployed with

Notice what is not in the list: there is no withdrawLiquidity, no removeLiquidity, no transfer of the position, no pause and no blacklist.

Arc network

Arc is Circle’s chain. Gas is paid in USDC, so you never need a second token to move. Chain id 5042. The site adds the network to your wallet when you connect; to do it yourself:

Add Arc to a wallet
await window.ethereum.request({
  method: 'wallet_addEthereumChain',
  params: [{
    chainId: '0x13b2',
    chainName: 'Arc',
    rpcUrls: ['https://fomonic.fun/api/rpc'],
    nativeCurrency: { name: 'USDC', symbol: 'USDC', decimals: 18 },
    blockExplorerUrls: ['https://arc.etherscan.io'],
  }],
});
ContractAddress
FomonicLaunch0x50715401ad67c6cfdaf09f71569657c8fdcb3928
USDC0x3600000000000000000000000000000000000000
FomonicRouter0x6e72061a66419fa96e065c1849d85332a5f42f5a
FomonicHook0x6336dfa1e077c860d5e13a6ae1c0b7fc5c830088
Uniswap v4 PoolManager0x8366a39cc670b4001a1121b8f6a443a643e40951

Risk

Anyone can launch anything here, and most of it will be worthless. A locked pool means nobody can remove the liquidity — it is not a promise that the price holds, that the creator is honest, or that the coin does anything at all.

Things worth checking before you buy: how much USDC is actually in the pool, how concentrated the holders are, whether the creator bought a large share at launch, and how old the coin is. All of it is on the coin page and in the API above. Nothing here is investment advice — do your own research and only spend what you can lose.