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
| What | Where it goes |
|---|---|
| Pool fee — 1% of every trade | 70% to the creator, 30% to the platform, in USDC. The coin side of the fee is split the same way. |
| Buyback-burn — the creator chooses | Instead of being paid out, the creator share buys the coin from the pool and burns it. |
| Launch fee | A 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.
// 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.
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();// 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();// 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.
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.
| Endpoint | Returns |
|---|---|
| GET /api/tokens?sort=new|trending|mc|vol&q=&limit= | every coin, with market cap, volume, holders |
| GET /api/token/:address | one coin + last 50 trades + top 20 holders |
| GET /api/candles/:address?tf=5m&limit=300 | OHLC candles, price in USDC |
| GET /api/trades?limit=50 | the newest trades across all coins |
| GET /api/wallet/:address | holdings, coins created, trades of one wallet |
| GET /api/stats | totals: coins, 24h volume, USDC in pools |
| GET /api/config | chain id and the addresses above |
Base URL: https://fomonic.fun/api/d
// 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));// 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');// 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 }));// 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
| Function | What 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:
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'],
}],
});| Contract | Address |
|---|---|
| FomonicLaunch | 0x50715401ad67c6cfdaf09f71569657c8fdcb3928 |
| USDC | 0x3600000000000000000000000000000000000000 |
| FomonicRouter | 0x6e72061a66419fa96e065c1849d85332a5f42f5a |
| FomonicHook | 0x6336dfa1e077c860d5e13a6ae1c0b7fc5c830088 |
| Uniswap v4 PoolManager | 0x8366a39cc670b4001a1121b8f6a443a643e40951 |
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.