size.fun Open app

TypeScript

Launch a token

One call creates the token and its curve. A second form does that and takes the first position in the same transaction — the one you want if you're buying your own launch.

A launch is one call to the factory: the coin's metadata, which launch config to use, and the fee attached. It deploys the token and its curve, mints the whole supply onto it and opens trading, all in that transaction.

This page is the curve launcher

To launch a coin that opens as a Uniswap v3 pool instead — indexed everywhere from block one, with your own buy in the same transaction — see seeded launches. Two ways to launch covers which one to pick.

The parameters#

Everything the launch needs in one object. Most of it is metadata; three fields are decisions.

ts launch-params.ts
import { toHex } from 'viem'
import { account } from './size.js'

/** CREATE2 salt. It only has to be unused by this account — any random 32 bytes. */
const salt = toHex(crypto.getRandomValues(new Uint8Array(32)))

export const params = {
  name: 'Example Coin',
  symbol: 'EXAMPLE',
  // A URL, not the image itself. Host the picture and put its link here —
  // uploading through size.fun gives you one, pinned to IPFS.
  logo: 'https://size.fun/api/img/0000000000000000000000000000000000000000000000000000000000000000',
  description: 'A coin that exists.',
  socials: {
    twitter: 'https://x.com/example',
    telegram: '',
    discord: '',
    website: '',
    farcaster: '',
  },
  // Where this launch's fees are paid. Changeable later, by this address only.
  creatorFeeRecipient: account.address,
  // 0 to 1000, charged on every trade on top of the 1% base fee and paid to you
  // in full. Frozen at launch: there is no setter for it afterwards.
  creatorTaxBps: 0,
  // Puts half of your share of the base fee into buying the coin back, on a
  // five-year vest. Can be turned on or off later.
  buybackEnabled: false,
  // 32 zero bytes waives the check on the launch terms. See "Pinning the terms".
  expectedEconomics: `0x${'00'.repeat(32)}` as `0x${string}`,
  salt,
} as const
FieldWhat it does
name, symbol Standard ERC-20 metadata. Permanent.
logo A URL. The contract stores the link, not the image, so host it somewhere that will outlive the launch.
description, socials Shown on the coin's page. All five social fields exist; pass empty strings for the ones you're not using.
creatorFeeRecipient Where this launch's fees go. Transferable later, by that address.
creatorTaxBps Permanent. 0–1000 (0–10%), charged on every trade on top of the base fee and paid to you in full.
buybackEnabled Puts half your share of the base fee into buying the coin back on a five-year vest. Changeable later.
expectedEconomics Pins the launch terms. 32 zero bytes waives it — see below.
salt Feeds CREATE2, so it has to be one this account hasn't used. Any random 32 bytes. Mining it is how you get a vanity address.

Launching#

ts launch.ts
import { parseEventLogs } from 'viem'
import {
  publicClient, walletClient, account,
  size, factoryAbi, OKB, LAUNCH_CONFIG_ID,
} from './size.js'
import { params } from './launch-params.js'

const launchFee = await publicClient.readContract({
  address: size.factory,
  abi: factoryAbi,
  functionName: 'launchFee',
})

// Simulating first does two things: it reverts here rather than on chain if
// anything about the parameters is wrong, and it hands back the token and curve
// addresses before the transaction has been sent.
const { request, result } = await publicClient.simulateContract({
  address: size.factory,
  abi: factoryAbi,
  functionName: 'launchToken',
  args: [params, LAUNCH_CONFIG_ID, OKB],
  value: launchFee,
  account,
})

const [token, curve] = result
console.log({ token, curve })

const hash = await walletClient.writeContract(request)
const receipt = await publicClient.waitForTransactionReceipt({ hash })

// The same two addresses, read back off the receipt.
const [launched] = parseEventLogs({
  abi: factoryAbi,
  eventName: 'TokenLaunched',
  logs: receipt.logs,
})
console.log(launched.args)

Simulating before writing is worth the round trip on this call. The factory validates the whole parameter set, so a simulation tells you which constraint you missed with the real error instead of costing you a reverted transaction to find out.

It also hands back the token and curve addresses before anything is sent, since both are CREATE2-derived and knowable in advance.

The third argument is the quote asset. OKB here is the zero address, the factory's built-in case; passing an approved wrapper instead launches a coin priced in a tokenised stock rather than in the gas token — see quote assets for the approved list and what changes.

Launching and buying in one transaction#

The factory can't fold a dev buy into launchToken, so doing it in two transactions leaves a gap between the coin opening and your buy landing. That gap is blocks for a bot and seconds for a human holding a wallet prompt, and it's long enough to lose the whole allocation — one launch on this factory was bought out by twenty-two addresses in the two blocks after it opened.

LaunchAndBuy closes it. Both legs settle in one transaction, so there's no intermediate state to trade against, and a failed buy takes the launch down with it rather than leaving you holding a coin you didn't want.

ts launch-and-buy.ts
import { parseEther } from 'viem'
import {
  publicClient, walletClient, account,
  size, factoryAbi, launchAndBuyAbi, OKB, LAUNCH_CONFIG_ID,
} from './size.js'
import { params } from './launch-params.js'

/** What you want to spend on your own coin, in OKB. */
const quoteIn = parseEther('0.5')

const launchFee = await publicClient.readContract({
  address: size.factory,
  abi: factoryAbi,
  functionName: 'launchFee',
})

const { request, result } = await publicClient.simulateContract({
  address: size.launchAndBuy,
  abi: launchAndBuyAbi,
  functionName: 'launchAndBuy',
  args: [
    params,
    LAUNCH_CONFIG_ID,
    OKB,
    quoteIn,
    // No slippage bound is needed. The curve does not exist until this call
    // creates it, so there is no price for anyone else to move first.
    0n,
    // Who receives the tokens. Exempted from the snipe tax automatically.
    account.address,
    // Extra wallets to exempt, if a team is bundling its opening buys. Up to 31.
    [],
  ],
  // For a native launch the call carries both legs: the fee and the buy.
  value: launchFee + quoteIn,
  account,
})

const [token, curve, tokensOut] = result
console.log({ token, curve, tokensOut })

const hash = await walletClient.writeContract(request)
await publicClient.waitForTransactionReceipt({ hash })

Use this one

If you intend to hold any of your own coin, launch through launchAndBuy. Plain launchToken is for launches with no dev buy at all.

Bundling several wallets#

Buys in the first 15 seconds pay the snipe tax. The dev buy's recipient is exempted automatically; a team opening across several addresses passes them in snipeTaxExemptions, up to 31 more. They're declared inside the launch transaction, so they're fixed before anyone else can see the coin exists.

ts
args: [
  params,
  LAUNCH_CONFIG_ID,
  OKB,
  quoteIn,
  0n,
  account.address,
  ['0xabc…', '0xdef…'], // exempt as well; recipient is added for you
],

Pinning the terms#

The launch terms — supply, base fee, phantom reserve, threshold, the pool's fee tier and the fee split — are protocol settings. They move rarely, but if one moved between you reading it and your launch mining, your coin would quietly get terms you didn't agree to.

expectedEconomics makes that revert instead. Read the digest, pass it, and a launch under changed terms fails rather than repricing.

ts pin-economics.ts
import { publicClient, size, factoryAbi, OKB, LAUNCH_CONFIG_ID } from './size.js'

/**
 * The launch terms — supply, fee, phantom reserve, threshold, the pool's fee
 * tier, the fee split — are protocol settings. They change rarely, but a change
 * landing between the moment you read them and the moment your launch mines
 * would silently give you different terms than the ones you agreed to.
 *
 * Passing this digest as `expectedEconomics` makes that case revert instead.
 * Passing 32 zero bytes waives the check, which is what the site does.
 */
export const expectedEconomics = await publicClient.readContract({
  address: size.factory,
  abi: factoryAbi,
  functionName: 'previewLaunchEconomics',
  args: [LAUNCH_CONFIG_ID, OKB],
})

32 zero bytes waives the check, which is what the site does. For a scripted launch where nobody's watching the prompt, pin it.

After it lands#

Trading is already open. There's no second step to enable it. From here:

  • The coin's page is https://size.fun/#/token/<token>.
  • Fees start accruing on the curve from the first trade. Collecting them is two calls.
  • getLaunchedToken(token) returns the curve address and the terms the launch is running under, any time.

Errors worth recognising

CreatorTaxTooHigh — above the 10% cap. LaunchFeeNotPaid — the value sent doesn't match launchFee(). LaunchEconomicsMismatch — the terms moved under a pinned launch. A repeated salt reverts too, because the pair already exists at that address.