# Create a token

One transaction creates the token and its bonding curve. After graduation the token gets a Uniswap v4 pool with locked liquidity and its own lending pool, which opens leverage on it. It is permanent: the token cannot be deleted, and its name, symbol, image, banner, description, links, supply and pair asset can never change.

## Preconditions

- A wallet that can sign on Robinhood Chain (chain id 4663), holding the launch fee plus gas in ETH (and the first-buy amount, if any).
- `AtomicLaunchFactory` and `AtomicLaunchAndBuy` from [deployments.json](https://atomic.green/agents/deployments.json).
- `canLaunch(yourAddress)` on the factory returns true (false means launches are limited to a whitelist).

## Cost

- **Launch fee:** `launchFee()` on the factory, in ETH, sent as `value`. Read it, do not hardcode it.
- **Gas:** a plain launch is a few million gas. Launch-and-buy that fills the curve and opens the pool in the same transaction needs up to about 29 million: use a gas limit of at least 32,000,000 for `launchAndBuyThrough`.
- **First buy (optional):** any amount of the pair asset; it buys at the opening price, without the snipe tax.

## Fields (`TokenParams`)

| Field | Type | Rule |
|---|---|---|
| `name` | string | Required, not empty |
| `symbol` | string | Required, not empty; the site uses at most 10 characters |
| `logo` | string | Image URI, `ipfs://…` or `https://…`, at most 512 bytes; may be empty |
| `description` | string | Free text |
| `banner` | string | Banner image URI, at most 512 bytes; may be empty |
| `socials` | tuple | `(twitter, telegram, discord, website, farcaster)`, full `https://` links or empty strings |
| `creatorFeeRecipient` | address | Receives the creator's 70% share of trade fees. Can be changed later by the current recipient |
| `creatorTaxBps` | uint16 | Extra creator fee on every trade, 0 to `maxCreatorTaxBps()` (1000 = 10%). The trade fee is 1% + this |
| `buybackEnabled` | bool | Route half of the creator's fee share to buying back the token (vests to the creator over 5 years). Can be switched later |
| `expectedEconomics` | bytes32 | `previewLaunchEconomics(launchConfigId, pairToken)`. The launch reverts with `LaunchEconomicsMismatch` if the terms change before it lands. `0x00…00` skips the check (not recommended) |
| `salt` | bytes32 | 32 fresh random bytes. Token and curve addresses are derived from it |

Other arguments:

- `launchConfigId`: index into `getLaunchConfig(id)` for `id < launchConfigCount()`. Each config sets the total supply. Use only configs with `enabled` true.
- `pairToken`: `0x0000000000000000000000000000000000000000` for ETH, or an approved ERC-20 (`approvedPairTokens(address)` is true; USDG is under `tokens` in deployments.json).
- `snipeTaxExemptions` (optional overload): up to 32 extra wallets that skip the snipe tax in the first seconds. The creator and the fee recipient are always exempt. Leverage on the token opens only after this window.

Image: pass an `ipfs://` or `https://` URI of an image you host or pinned yourself (PNG, JPEG, WebP or GIF). Atomic does not host images for agents.

## Functions

```solidity
// AtomicLaunchFactory
function launchFee() view returns (uint256)
function canLaunch(address launcher) view returns (bool)
function maxCreatorTaxBps() view returns (uint256)
function launchConfigCount() view returns (uint256)
function getLaunchConfig(uint256 id) view returns ((uint256 supply, uint256 curveFeeBps, uint256 phantomQuote, uint256 graduationThreshold, uint24 poolFee, int24 tickSpacing, bool enabled))
function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)
function launchToken(TokenParams params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)
function launchToken(TokenParams params, uint256 launchConfigId, address pairToken, address[] snipeTaxExemptions) payable returns (address token, address curve)

// AtomicLaunchAndBuy: launch and buy in one transaction
function launchAndBuyThrough(TokenParams params, uint256 launchConfigId, address pairToken, uint256 quoteIn, uint256 minTokensOut, uint256 minPoolTokensOut, address recipient, address[] snipeTaxExemptions) payable returns (address token, address curve, uint256 tokensOut, uint256 poolTokensOut)

// TokenParams = (string name, string symbol, string logo, string description, string banner,
//   (string twitter, string telegram, string discord, string website, string farcaster) socials,
//   address creatorFeeRecipient, uint16 creatorTaxBps, bool buybackEnabled, bytes32 expectedEconomics, bytes32 salt)
```

The graduation threshold and phantom a launch freezes are the config's values times `AtomicGraduationSchedule.factorWad()` at launch time, rounded up. The factor follows the ramp, except during a graduation discount (a promotion the platform owner sets): from `discountStart()` until `discountEnd()` it is the lower of the ramp's and `discountFactorWad()`, and after `discountEnd()` it returns to the ramp on its own. `rampFactorWad()` is the ramp alone. Quote the threshold right before launching: a window that opens or closes changes `previewLaunchEconomics`, and a stale `expectedEconomics` makes the launch revert.

## Steps

1. Read `launchFee()`, `canLaunch(you)`, `maxCreatorTaxBps()`, the config you want, and `previewLaunchEconomics(configId, pairToken)`.
2. Build `TokenParams` with a fresh random `salt`.
3. Without a first buy: simulate `launchToken(params, configId, pairToken)` with `value = launchFee` from your address. The result is the future `(token, curve)`.
4. With a first buy:
   - ETH pair: `value = launchFee + quoteIn`.
   - ERC-20 pair: `approve(AtomicLaunchAndBuy, quoteIn)` on the pair token first; `value = launchFee`.
   - Simulate `launchAndBuyThrough(params, configId, pairToken, quoteIn, 0, 0, recipient, [])`; set `minTokensOut = tokensOut × (10000 − slippageBps) / 10000`. A buy larger than the curve fills it, opens the pool and buys the rest there (`poolTokensOut`); nobody can trade that pool before you in the same transaction, so `minPoolTokensOut` may stay 0.
5. Show the user (or your policy check) the name, symbol, pair, supply, fee and total ETH spent, and get approval if your operator requires it.
6. Send the same call with the same `params` (same salt). Keep `expectedEconomics`; if it reverts with `LaunchEconomicsMismatch`, read the preview again and rebuild.
7. Read the result from the `TokenLaunched` event or from the simulation: `token` and `curve`.

## Example (TypeScript, viem)

```ts
import { createPublicClient, http, parseAbi, toHex, zeroAddress } from "viem";
import { robinhood as chain } from "viem/chains";

const factoryAbi = parseAbi([
  "struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }",
  "struct TokenParams { string name; string symbol; string logo; string description; string banner; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }",
  "function launchFee() view returns (uint256)",
  "function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)",
  "function launchToken(TokenParams params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)",
]);

const client = createPublicClient({ chain, transport: http("https://rpc.mainnet.chain.robinhood.com") });
const net = (await (await fetch("https://atomic.green/agents/deployments.json")).json()).networks.find((n: any) => n.chainId === 4663);
const FACTORY = net.contracts.AtomicLaunchFactory;
const me = "0x…your address…";
const configId = 0n;          // getLaunchConfig(0).supply is its total supply
const pairToken = zeroAddress; // ETH

const [launchFee, expectedEconomics] = await Promise.all([
  client.readContract({ address: FACTORY, abi: factoryAbi, functionName: "launchFee" }),
  client.readContract({ address: FACTORY, abi: factoryAbi, functionName: "previewLaunchEconomics", args: [configId, pairToken] }),
]);
const params = {
  name: "Agent Test", symbol: "AGENT", logo: "", description: "Created from the Atomic agent docs", banner: "",
  socials: { twitter: "", telegram: "", discord: "", website: "", farcaster: "" },
  creatorFeeRecipient: me, creatorTaxBps: 0, buybackEnabled: false,
  expectedEconomics, salt: toHex(crypto.getRandomValues(new Uint8Array(32))),
};
const { request, result: [token, curve] } = await client.simulateContract({
  account: me, address: FACTORY, abi: factoryAbi, functionName: "launchToken",
  args: [params, configId, pairToken], value: launchFee,
});
// Send with your wallet client: await walletClient.writeContract(request);
```

## Where to see the result

- Page: `https://atomic.green/token/<token address>`.
- API: the token appears in `GET https://atomic.green/indexer/v1/launches` within a few blocks, with `phase` 0.
- Creator fees: `GET https://atomic.green/indexer/v1/profile/<creatorFeeRecipient>` → `creatorFees`; claim them as described in [fees-referrals.md](https://atomic.green/agents/fees-referrals.md).
