Developer documentation

Quik docs

Launch an ERC-20 token with bootstrapped Uniswap V3 liquidity in a single transaction. Vanity addresses end in 6969 by default.

Overview

Quik is a permissionless launchpad on Robinhood Chain. A single transaction deploys an ERC-20 token, seeds a Uniswap V3 pool with one of several preset liquidity configs, and locks the LP NFT inside the factory. Trading fees accrue to the creator and can be claimed at any time.

  • Total supply: 1,000,000,000 tokens, 18 decimals, fixed.
  • Pool fee: 1% Uniswap V3 (fee tier 10000).
  • Vanity suffix: all token addresses end in 0x…6969 via CREATE2. Configurable backend-side throughVANITY_SUFFIX.
  • Anti-snipe tax: a per-config penalty curve applied for the first 5 blocks after launch; 2% max-wallet during the same window.
  • Network: Robinhood Chain (chain id 4663).

Factory contracts

The factory is immutable. Configs are stored on-chain and read live via GET /liquidity-configs.

NetworkChain IDAddress
Robinhood Chain46630xa8ffcbbae5b14a3ad3f0e83e7813301ecc49d46a
The previous mainnet factory 0xca1f6f8f01e8e6d078c4258f41063d6cfa7d4c9b is inert and marked inactive in the indexer; do not use it.

Fees

The Uniswap V3 pool fee is fixed at 1%. The ETH side of swap fees splits between the creator and the protocol; the token side accumulated in the LP position is automatically burned.

BucketShareBehaviour
Creator (receiver)50%Withdrawn manually via collectFees
Platform50%Goes to the protocol treasury
Token side of LP fees100% burnRemoved from circulating supply on each claim

During the first 5 blocks after launch, an anti-snipe penalty is applied on top of the swap fee. The exact curve depends on the chosen liquidity config (each config carries a penalty_multiplier in [0, 100]). Preview the curve with GET /penalty-curve.

Launch sequence

Launching a token is four steps:

  1. 1

    Upload a token logo (optional)

    POST /api/v1/uploads/token-logo?owner=0xCREATOR accepts multipart/form-data with a single file field. PNG, JPEG, WebP and GIF are supported up to 5 MB. The response contains a CDN url you can pass in the next step.

  2. 2

    Create a metadata draft

    POST /api/v1/launch/draft stores the off-chain metadata and returns an opaque id of the form qmd_<hex>. This id is what you pass on-chain as the _metadata argument — the indexer attaches the full metadata to the token after the launch event fires.

    {
      "name": "My Token",
      "symbol": "MYT",
      "image_url": "https://cdn.quik.meme/...",
      "description": "Optional, max 1000 chars",
      "socials": {
        "twitter": "https://x.com/...",
        "telegram": "https://t.me/...",
        "discord": "https://discord.gg/...",
        "website": "https://...",
        "farcaster": "..."
      },
      "chain_id": 1
    }
    Drafts are scoped to the creator wallet that signed in via SIWE. Unclaimed drafts expire after 48 hours.
  3. 3

    Mine a vanity salt

    POST /api/v1/launch/vanity-salt runs a Rust CREATE2 search and returns a bytes32 salt that produces a token address ending in 6969.

    POST /api/v1/launch/vanity-salt
    Authorization: Bearer <jwt>
    
    {
      "name": "My Token",
      "symbol": "MYT",
      "creator": "0xCREATOR",
      "chain_id": 1
    }
    
    → {
      "salt": "0x...",
      "address": "0x…6969",
      "attempts": 4821,
      "suffix": "6969"
    }
  4. 4

    Call launchToken

    The factory exposes a single payable function. The optional msg.value is a creator buy executed atomically with deployment.

    function launchToken(
      string  _name,
      string  _symbol,
      string  _metadata,   // qmd_<hex> draft id, or "" for none
      bytes32 salt,        // from /launch/vanity-salt
      address receiver,    // fee beneficiary, usually = msg.sender
      uint256 split,       // creator/receiver split, usually 100
      uint256 configId     // 0..N, see /liquidity-configs
    ) external payable returns (uint256 tokensReceived);
    receiver and split are stored on-chain but the v1 contract does not yet route fees through them — the fee receiver is set at the factory level. Pass (msg.sender, 100) for forward-compatibility.

Claim fees

Fees do not auto-distribute — the creator (or whoever is set as the fee receiver on the factory) must call collectFees themselves. Call it as often as you like; there is no minimum.

function collectFees(uint256 tokenId)
  external
  returns (uint256 amount0, uint256 amount1);

tokenId is the Uniswap V3 LP NFT id minted at launch. Look it up via GET /tokens/:address (field liquidity.token_id) or read GET /tokens/:address/pending-fees first to see how much is currently claimable.

Authentication (SIWE)

Endpoints that mutate state — drafts, vanity-salt mining, profile edits, uploads — require a JWT obtained via Sign-In-With-Ethereum (EIP-4361).

# 1. Get a nonce + message
POST /api/v1/auth/nonce
{ "address": "0xYOU", "chain_id": 1 }
→ { "message": "...", "nonce": "..." }

# 2. Sign the message with the wallet, then verify
POST /api/v1/auth/verify
{ "message": "...", "signature": "0x..." }
→ { "token": "<jwt>", "expires_at": "..." }

# 3. Send the JWT on subsequent calls
Authorization: Bearer <jwt>
The SIWE message is bound to AUTH_DOMAIN and AUTH_URI on the backend; signatures from a different origin will be rejected.

API reference

Base URL https://api.quik.meme/api/v1. All endpoints return JSON. Read endpoints are open; write endpoints require a JWT (see above).

Launch

MethodPathDescriptionAuth
POST/auth/nonceGet the SIWE message to sign
POST/auth/verifyExchange a signature for a JWT
POST/launch/draftCreate a metadata draftJWT
POST/launch/vanity-saltMine a CREATE2 salt with the configured suffixJWT
GET/liquidity-configs?chain_id=1List on-chain liquidity configs
GET/factory-state?chain_id=1Active factory + admin state
GET/penalty-quote?eth=0.05&config_id=0Quote anti-snipe penalty for one buy size
GET/penalty-curve?max_eth=0.5&samples=100&config_id=0Sample the full penalty curve

Tokens & discovery

MethodPathDescriptionAuth
GET/tokens?sort=…&limit=20&offset=0Leaderboard. sort ∈ {volume_24h, volume_7d, market_cap, fees, new, trending}
GET/tokens/:address?chain_id=1Full token detail
GET/tokens/:address/swaps?limit=50&before=<unix_ms>Trade history
GET/tokens/:address/holdersTop holders
GET/tokens/:address/pending-feesUncollected LP fees in WETH
GET/tokens/:address/chart/ohlcvOHLCV candles
GET/tokens/:address/chart/tradesRaw trade points
GET/tokens/:address/messagesCoin chat messages

Profiles & search

MethodPathDescriptionAuth
GET/meAuthenticated user profileJWT
PATCH/me/profileUpdate username, bio, socialsJWT
GET/profile/:address/launchedTokens deployed by a wallet
GET/profile/:address/earningsTokens where wallet is receiver
GET/profile/:address/activityWallet swap history
GET/profile/:address/balancesToken holdings
GET/search?q=…&limit=10Combined token + profile search
GET/resolve/:addressENS / Basename reverse lookup
POST/resolve/batchBatch reverse lookup
GET/username/:name/availableUsername availability check

Uploads

MethodPathDescriptionAuth
POST/uploads/avatarUser avatar (max 2 MB)JWT
POST/uploads/bannerUser banner (max 3 MB)JWT
POST/uploads/token-logo?owner=0x…Token logo (max 5 MB). PNG, JPEG, WebP, GIF

Image responses include url, key, content_type, width, height, size and animated.

Stats & activity

MethodPathDescriptionAuth
GET/stats/protocolTotal fees, 24h volume, token count
GET/stats/top-earners?limit=10Creator earnings leaderboard
GET/activity/recentRecent swaps across all tokens
GET/activity/big-buysNotable buy orders
GET/activity/claimsRecent fee claims

Full example (TypeScript + viem)

End-to-end: sign in with SIWE, mine a vanity salt, create a metadata draft, deploy. Replace the placeholders before running.

import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { mainnet } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";

const API = "https://api.quik.meme/api/v1";
const FACTORY = "0x35497ec9e3b142d8b1783049a9ad6f58fc50c9dd" as const;
const RPC = "https://rpc.mevblocker.io/fast";

const account = privateKeyToAccount("0xPRIVATE_KEY");
const wallet = createWalletClient({ account, chain: mainnet, transport: http(RPC) });
const publicClient = createPublicClient({ chain: mainnet, transport: http(RPC) });

// 1. SIWE — get a JWT
const { message } = await fetch(`${API}/auth/nonce`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ address: account.address, chain_id: 1 }),
}).then((r) => r.json());

const signature = await wallet.signMessage({ message });

const { token } = await fetch(`${API}/auth/verify`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ message, signature }),
}).then((r) => r.json());

const auth = { authorization: `Bearer ${token}`, "content-type": "application/json" };

// 2. Create a metadata draft
const { id: metadataId } = await fetch(`${API}/launch/draft`, {
  method: "POST",
  headers: auth,
  body: JSON.stringify({
    name: "My Token",
    symbol: "MYT",
    description: "An onchain memecoin.",
    socials: { twitter: "https://x.com/example" },
    chain_id: 1,
  }),
}).then((r) => r.json());

// 3. Mine a vanity salt
const { salt } = await fetch(`${API}/launch/vanity-salt`, {
  method: "POST",
  headers: auth,
  body: JSON.stringify({
    name: "My Token",
    symbol: "MYT",
    creator: account.address,
    chain_id: 1,
  }),
}).then((r) => r.json());

// 4. Deploy
const abi = [{
  type: "function",
  name: "launchToken",
  stateMutability: "payable",
  inputs: [
    { name: "_name", type: "string" },
    { name: "_symbol", type: "string" },
    { name: "_metadata", type: "string" },
    { name: "salt", type: "bytes32" },
    { name: "receiver", type: "address" },
    { name: "split", type: "uint256" },
    { name: "configId", type: "uint256" },
  ],
  outputs: [{ name: "tokensReceived", type: "uint256" }],
}] as const;

const hash = await wallet.writeContract({
  address: FACTORY,
  abi,
  functionName: "launchToken",
  args: ["My Token", "MYT", metadataId, salt, account.address, 100n, 0n],
  value: parseEther("0.1"), // creator buy, optional
});

const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("Launched in block", receipt.blockNumber);

For the full open-source frontend implementation, see the app/create/page.tsx route in the Quik repo — it wires the same flow to RainbowKit and a UI.

Found a bug or missing endpoint? Open an issue or DM us. Docs are versioned with the contract; if a field on this page disagrees with on-chain reality, on-chain wins.