# Quickstart

Launch a token with an opening buy, in one transaction, with viem. Every address below is Arc
testnet; see [Addresses](/documentation/addresses).

## 1 · Check you are allowed to launch

Launching is gated and **disabled by default on a fresh deployment**. Always check first:

```ts
const allowed = await client.readContract({
  address: FACTORY, abi: factoryAbi, functionName: "canLaunch", args: [account],
});
if (!allowed) throw new Error("launching is not open for this address");
```

## 2 · Pin the economics

`expectedEconomics` freezes the terms your user was shown. If the owner re-pegs the curve or
changes the launch fee between your quote and your signature, the launch reverts instead of
landing on terms nobody agreed to.

```ts
const expectedEconomics = await client.readContract({
  address: FACTORY, abi: factoryAbi,
  functionName: "previewLaunchEconomics", args: [0n, USDC],
});
```

Fetch it in the same flow as the submit. Do not cache it. Passing `bytes32(0)` waives the
check, which means accepting whatever terms are live when the transaction lands.

## 3 · Approve the router

The atomic path pulls the fee **and** the opening buy, so one approval covers both — and the
spender is the router, not the factory.

```ts
const fee = await client.readContract({ address: FACTORY, abi: factoryAbi, functionName: "launchFee" });
const openingBuy = 1_000_000n;                       // 1 USDC — 6 decimals

await walletClient.writeContract({
  address: USDC, abi: erc20Abi, functionName: "approve",
  args: [LAUNCH_AND_BUY, fee + openingBuy],
});
```

## 4 · Launch

```ts
const { result } = await client.simulateContract({
  address: LAUNCH_AND_BUY, abi: launchAndBuyAbi, functionName: "launchAndBuy",
  args: [
    {
      name: "My Token", symbol: "MINE", logo: "", description: "",
      socials: { twitter: "", telegram: "", discord: "", website: "", farcaster: "" },
      creatorFeeRecipient: account,                  // must NOT be zero on this path
      creatorTaxBps: 0,
      expectedEconomics,
      salt: crypto.getRandomValues(new Uint8Array(32)),
    },
    0n,            // launchConfigId
    USDC,          // pairToken
    openingBuy,    // quoteIn — must be non-zero here
    0n,            // minTokensOut
    account,       // recipient of the bought tokens
  ],
  account,
});
const [token, curve, tokensOut] = result;
```

Then send it with an **explicit gas limit** — see the warning below.

> [!WARNING]
> If the opening buy is large enough to cross the graduation threshold, the curve tries to
> seed the Uniswap pool inside this same transaction. `eth_estimateGas` cannot size that:
> the seed is a best-effort `try/catch`, so a simulation in which it fails still *succeeds*
> overall and returns a limit too small for it to work — every time, deterministically. Send
> something generous (8,000,000 is ample). EIP-1559 charges for gas used, not the limit, so
> over-providing costs nothing.

## 5 · Launch without an opening buy

`launchAndBuy` reverts on a zero `quoteIn` — it exists to buy and refuses to act as a plain
deployer. Use the factory directly, and note the approval target moves with it:

```ts
await walletClient.writeContract({
  address: USDC, abi: erc20Abi, functionName: "approve",
  args: [FACTORY, fee],                              // the FACTORY, not the router
});

const { result } = await client.simulateContract({
  address: FACTORY, abi: factoryAbi, functionName: "launchToken",
  args: [params, 0n, USDC], account,
});
```

A token launched this way starts with the creator holding **none** of its supply.

## 6 · Buy on the curve

The curve has no fixed address — read it from the launch record.

```ts
const launch = await client.readContract({
  address: FACTORY, abi: factoryAbi, functionName: "getLaunchedToken", args: [token],
});

await walletClient.writeContract({
  address: USDC, abi: erc20Abi, functionName: "approve",
  args: [launch.curve, amountIn],                    // approve the CURVE
});

await walletClient.writeContract({
  address: launch.curve, abi: curveAbi, functionName: "buy",
  args: [amountIn, minTokensOut, account],
});
```