Create
Documentation
Guides

Launching a token

Two entrypoints, and the right one depends on whether there is an opening buy.

factory.launchTokenlaunchAndBuy.launchAndBuy
opening buynonerequired, non-zero
approvethe factory, launchFee()the router, launchFee() + quoteIn
creatorFeeRecipient zerodefaults to the callerrejected
creator's starting balancenothingthe opening buy

launchAndBuy reverts with ZeroAmount on a zero quoteIn — it exists to make deploy-and-buy atomic and refuses to be used as a plain deployer.

TokenParams#

solidity
struct TokenParams {
  string  name;                 // required, <= 64 bytes
  string  symbol;               // required, <= 16 bytes
  string  logo;                 // <= 512 bytes — a URI, never the image
  string  description;          // <= 2048 bytes
  Socials socials;              // five strings, each <= 256 bytes
  address creatorFeeRecipient;  // earns the creator split and the whole creator tax
  uint16  creatorTaxBps;        // extra tax on top of the curve fee, capped by maxCreatorTaxBps
  bytes32 expectedEconomics;    // terms pin; bytes32(0) waives it
  bytes32 salt;                 // CREATE2 salt, namespaced per account
}

Three of these deserve attention.

logo is a reference, not an image#

It is stored on-chain, so it must be short. Putting a base64 data URI here does not merely cost gas — the node rejects the transaction outright and the launch never reaches the contract. Upload the image first and store the resulting URL.

expectedEconomics#

A digest of the exact terms this launch will lock in. Get it from previewLaunchEconomics(launchConfigId, pairToken) in the same flow as the submit.

It covers the phantom reserve, graduation threshold, supply, curve fee, pool fee, tick spacing, the protocol fee shares — and the launch fee. So an owner calling setLaunchFee between your quote and your signature invalidates it, and the launch reverts with LaunchEconomicsMismatch(expected, actual) rather than landing on terms your user never saw.

Do not cache it. bytes32(0) waives the check entirely, which means accepting whatever is live when the transaction lands.

salt#

A raw CREATE2 salt, namespaced by the factory as keccak256(deployer, salt) — so it only needs to be unique among your own launches. Two creators may use the same value.

Reusing one on otherwise identical terms reverts with FailedDeployment, which says nothing about salts. Call launchDeployer.predictLaunchAddresses(...) first to check for existing code, and to mine a vanity address if you want one.

Preconditions worth checking before you show a form#

ts
const canLaunch = await read("canLaunch", [account]);      // false by default on a new deployment
const fee       = await read("launchFee");                  // may be zero -> no approval needed
const economics = await read("pairTokenEconomics", [USDC]); // phantom + threshold, in 6 decimals

launchEnabled is left false by deployment, so a fresh environment rejects every launch until the owner opens it or whitelists an address. Surfacing that as a disabled button beats a revert.

Common reverts#

ErrorCause
NotWhitelisted / NotApprovedLauncherlaunching is closed for this address
LaunchEconomicsMismatchterms moved between quote and submit
FailedDeploymentthis account already used that salt
CreatorTaxTooHighcreatorTaxBps above maxCreatorTaxBps()
PairTokenNotApprovedthe quote asset is not approved by the owner
ZeroAmountzero quoteIn on the router path — use launchToken
ERC-20 allowance revertapproved the wrong contract; see Approvals