---
name: livo-token-launch
description: How to create a token on Livo programmatically — authentication, contract calls, metadata API, and end-to-end flow.
user_invocable: true
---

# Creating a Token on Livo

This document describes the complete flow for creating a token on the Livo launchpad programmatically (outside the frontend UI).

## Overview

Token creation on Livo is a two-step process:

1. **On-chain**: Call a Livo factory contract to deploy the token (returns a transaction hash).
2. **Off-chain**: Submit token metadata (name, image, socials, etc.) to the Livo API keyed by that transaction hash.

The metadata API must be called **after** the transaction is broadcast (you need the txHash), but it does not need to wait for confirmation.

---

## Step 1: Authenticate

All API calls require a JWT Bearer token. Obtain one by signing a message with your wallet.

### Request

```
POST /api/auth/wallet
Content-Type: application/json

{
  "address": "0xYourWalletAddress",
  "signature": "<signature>",
  "message": "Sign in to Livo\nTimestamp: <unix_ms>"
}
```

The message must contain `Timestamp: <unix_ms>` where the timestamp is within the last 5 minutes.

### Response

```json
{ "token": "eyJhbGci..." }
```

Use this token as `Authorization: Bearer <token>` in all subsequent API calls. Tokens expire after 7 days.

---

## Step 2: Call the Factory Contract (on-chain)

Token creation goes through one of three factory contracts depending on the token configuration:

### Factory Selection Logic

```
if (buyTax > 0 || sellTax > 0 || taxPeriodDuration > 0):
    use factoryTaxV4   (LivoFactoryTaxToken ABI)
else if (creatorLPFeeShare is enabled):
    use factoryV4      (LivoFactoryBase ABI)
else:
    use factoryV2      (LivoFactoryBase ABI)
```

### Contract Addresses

Contract addresses may be updated over time. Always refer to the canonical deployment files for the latest addresses:

- **Mainnet**: https://github.com/LivoLaunchpad/livo-contracts/blob/main/deployments.mainnet.md
- **Sepolia**: https://github.com/LivoLaunchpad/livo-contracts/blob/main/deployments.sepolia.md

Key contracts to look for: `factoryV2`, `factoryV4`, `factoryTaxV4`, `livoToken` (implementation), `livoTaxableTokenUniV4` (implementation), `livoLaunchpad`.

### Token Implementation Selection

```
if using factoryTaxV4:
    tokenImplementation = livoTaxableTokenUniV4
else:
    tokenImplementation = livoToken
```

### Vanity Salt

Before calling the factory, you must compute a vanity salt. The factory uses CREATE2, and Livo requires the deployed token address to end in `1110`.

The salt is found by brute-forcing CREATE2 addresses until one ends in `1110`. Here's a reference implementation using viem:

```javascript
import { keccak256, concat, toBytes } from "viem";

const PROXY_PREFIX = "0x3d602d80600a3d3981f3363d3d373d3d3d363d73";
const PROXY_SUFFIX = "0x5af43d82803e903d91602b57fd5bf3";

/**
 * @param {`0x${string}`} factoryAddress - The factory contract address (deployer).
 * @param {`0x${string}`} tokenImplementation - The token implementation address.
 * @returns {{ salt: `0x${string}`, tokenAddress: string }}
 */
function findVanitySalt(factoryAddress, tokenImplementation) {
  const initcodeHash = keccak256(concat([PROXY_PREFIX, tokenImplementation, PROXY_SUFFIX]));

  // Build the 85-byte buffer: 0xff (1) ++ factory (20) ++ salt (32) ++ initcodeHash (32)
  const buffer = new Uint8Array(85);
  buffer[0] = 0xff;
  buffer.set(toBytes(factoryAddress), 1);
  buffer.set(toBytes(initcodeHash), 53);

  const salt = crypto.getRandomValues(new Uint8Array(32));

  for (;;) {
    buffer.set(salt, 21);
    const hash = keccak256(buffer);

    // Check if the last 4 hex chars of the address are "1110"
    if (hash.endsWith("1110")) {
      const saltHex = "0x" + Array.from(salt, (b) => b.toString(16).padStart(2, "0")).join("");
      const tokenAddress = "0x" + hash.slice(26);
      return { salt: saltHex, tokenAddress };
    }

    // Increment salt as big-endian counter
    for (let i = 31; i >= 0; i--) {
      if (salt[i] < 255) {
        salt[i]++;
        break;
      }
      salt[i] = 0;
    }
  }
}
```

### Function Signatures

#### `createToken` (LivoFactoryBase — no tax)

```solidity
function createToken(
    string name,
    string symbol,
    address feeReceiver,  // address to receive LP fees (usually msg.sender)
    bytes32 salt          // vanity salt
) returns (address token)
```

#### `createToken` (LivoFactoryTaxToken — with tax)

```solidity
function createToken(
    string name,
    string symbol,
    address feeReceiver,
    bytes32 salt,
    uint16 buyTaxBps,           // buy tax in basis points (e.g., 250 = 2.5%), max 500
    uint16 sellTaxBps,          // sell tax in basis points, max 500
    uint32 taxDurationSeconds   // how long the tax lasts (max 14 days = 1209600s)
) returns (address token)
```

#### `createTokenWithFeeSplit` (LivoFactoryBase — no tax, multiple fee recipients)

```solidity
function createTokenWithFeeSplit(
    string name,
    string symbol,
    address[] recipients,    // fee recipient addresses
    uint256[] sharesBps,     // share per recipient in basis points (must sum to 10000)
    bytes32 salt
) returns (address token, address feeSplitter)
```

#### `createTokenWithFeeSplit` (LivoFactoryTaxToken — with tax, multiple fee recipients)

```solidity
function createTokenWithFeeSplit(
    string name,
    string symbol,
    address[] recipients,
    uint256[] sharesBps,
    bytes32 salt,
    uint16 buyTaxBps,
    uint16 sellTaxBps,
    uint32 taxDurationSeconds
) returns (address token, address feeSplitter)
```

### Validation Rules (enforced by frontend and contracts, should be followed by API callers)

- **name**: 1-32 characters, non-empty
- **symbol**: 1-15 characters, uppercase alphanumeric only (`[A-Z0-9]+`)
- **buyTaxBps**: 0-500 (0%-5%)
- **sellTaxBps**: 0-500 (0%-5%)
- **taxDurationSeconds**: 86400-1209600 (1-14 days)
- If using tax: at least one of buyTax or sellTax must be > 0
- Fee split shares must sum to 10000 (100%)
- No duplicate addresses in fee splits

---

## Step 3: Submit Metadata to API (off-chain)

After broadcasting the transaction and obtaining the `txHash`, submit the token metadata.

### Request

```
POST /api/tokens/create
Authorization: Bearer <jwt_token>
Content-Type: multipart/form-data

Fields:
  txHash     (required) — 0x-prefixed, 66-character hex string
  name       (required) — token name, max 32 characters
  symbol     (required) — token symbol, max 15 chars, uppercase alphanumeric
  chainId    (required) — 1, 11155111, or 8453
  description (optional) — max 250 characters
  telegram   (optional) — must be a valid t.me / telegram.me URL
  twitter    (optional) — must be a valid x.com / twitter.com URL
  website    (optional) — must be a valid http/https URL
  image      (optional) — JPEG, PNG, GIF, or WebP, max 5MB
```

### Response (success)

```json
{
  "success": true,
  "txHash": "0x...",
  "imageUrl": "https://..."
}
```

### Error Responses

| Status | Meaning                                                 |
| ------ | ------------------------------------------------------- |
| 400    | Validation error (missing fields, invalid format, etc.) |
| 401    | Unauthorized (missing or invalid JWT)                   |
| 409    | Metadata already exists for this txHash                 |
| 500    | Server error                                            |

---

## Complete End-to-End Example (using viem + fetch)

```javascript
import { createWalletClient, http, custom } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
import { keccak256, concat, toBytes } from "viem";

// --- Config ---
const FACTORY_V4 = "0x571CD864b15275Ddd13AC100c3c07B7cb072cEFd";
const TOKEN_IMPL = "0x758Af7bCde2875a6Aa06337125EA81335a860AC5";
const LIVO_API = "https://livo.trade";
const CHAIN_ID = 1;

// --- 1. Authenticate ---
const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const timestamp = Date.now();
const message = `Sign in to Livo\nTimestamp: ${timestamp}`;
const signature = await account.signMessage({ message });

const authRes = await fetch(`${LIVO_API}/api/auth/wallet`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ address: account.address, signature, message }),
});
const { token: jwt } = await authRes.json();

// --- 2. Compute vanity salt ---
// (use findVanitySalt from src/lib/findVanitySalt.js or reimplement)
const { salt } = findVanitySalt(FACTORY_V4, TOKEN_IMPL);

// --- 3. Call factory contract ---
const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

const txHash = await walletClient.writeContract({
  address: FACTORY_V4,
  abi: LivoFactoryBaseABI, // fetch from Etherscan using the factory address
  functionName: "createToken",
  args: [
    "My Token", // name
    "MYTKN", // symbol
    account.address, // feeReceiver
    salt, // vanity salt
  ],
});

// --- 4. Submit metadata ---
const formData = new FormData();
formData.append("txHash", txHash);
formData.append("name", "My Token");
formData.append("symbol", "MYTKN");
formData.append("chainId", String(CHAIN_ID));
formData.append("description", "A cool token");
formData.append("website", "https://mytoken.com");
formData.append("telegram", "https://t.me/mytoken");
formData.append("twitter", "https://x.com/mytoken");
// formData.append("image", imageFile); // optional File object

const metaRes = await fetch(`${LIVO_API}/api/tokens/create`, {
  method: "POST",
  headers: { Authorization: `Bearer ${jwt}` },
  body: formData,
});

const result = await metaRes.json();
console.log("Token metadata stored:", result);
```

---

## ABIs

All Livo contracts are verified on Etherscan. To get the ABI for any contract:

1. Get the contract address from the deployment files (see [Contract Addresses](#contract-addresses) above)
2. Look it up on Etherscan (mainnet: `etherscan.io`, sepolia: `sepolia.etherscan.io`)
3. Go to the "Contract" tab → "Read/Write Contract" or use the Etherscan API:
   `https://api.etherscan.io/api?module=contract&action=getabi&address=<CONTRACT_ADDRESS>`

Key contracts and their roles:

- **factoryV2 / factoryV4** — `createToken`, `createTokenWithFeeSplit` (LivoFactoryBase)
- **factoryTaxV4** — `createToken`, `createTokenWithFeeSplit` with tax params (LivoFactoryTaxToken)
- **livoLaunchpad** — `buyTokensWithExactEth`, `sellExactTokens`, quote functions (post-creation trading)

## Key Events

After token creation, the factory emits:

- **`TokenCreated(address indexed token, string name, string symbol, address tokenOwner, address launchpad, address graduator, address feeHandler, address feeReceiver)`**

The launchpad emits:

- **`TokenLaunched(address indexed token, uint256 graduationThreshold, uint256 maxExcessOverThreshold)`**

Parse the `TokenLaunched` event from the transaction receipt to get the created token address.

## Notes

- Token creation is free (no ETH cost beyond gas).
- Tokens start on a bonding curve managed by the LivoLaunchpad contract. Once enough ETH is collected (graduation threshold ~3.5 ETH), the token graduates to Uniswap.
- The `image` field should be a raw file upload (not a URL). The API uploads it to IPFS via Pinata.
