# Launching a token

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

| | `factory.launchToken` | `launchAndBuy.launchAndBuy` |
|---|---|---|
| opening buy | none | required, non-zero |
| approve | **the factory**, `launchFee()` | **the router**, `launchFee() + quoteIn` |
| `creatorFeeRecipient` zero | defaults to the caller | **rejected** |
| creator's starting balance | nothing | the 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

| Error | Cause |
|---|---|
| `NotWhitelisted` / `NotApprovedLauncher` | launching is closed for this address |
| `LaunchEconomicsMismatch` | terms moved between quote and submit |
| `FailedDeployment` | this account already used that salt |
| `CreatorTaxTooHigh` | `creatorTaxBps` above `maxCreatorTaxBps()` |
| `PairTokenNotApproved` | the quote asset is not approved by the owner |
| `ZeroAmount` | zero `quoteIn` on the router path — use `launchToken` |
| ERC-20 allowance revert | approved the wrong contract; see [Approvals](/documentation/approvals) |