Klik Finance<- Back to app
Developer Docs
Overview
What is KlikThe KLIK TokenAirdrop RoundsFee ScheduleHook Labs FeesFactory Contracts
Launch
Launch SequenceLaunch on a HookClaim FeesFull Examples
Integrations
Indexing Klik
APIs
Atomic EndpointUpload ImageUpload MetadataGenerate SaltRate Limits
Klik Finance

Developer Documentation

Last Updated: September 3, 2026

What is Klik?

Klik is a permissionless token launchpad on Ethereum mainnet, Base, Robinhood Chain and Arc. Anyone can deploy a fully on-chain ERC-20 token with bootstrapped Uniswap V4 liquidity in a single transaction - no seed rounds, no VCs, no presales.

Arc settles in USDC. USDC is Arc's native currency, so on that chain starting liquidity, creator buys, market caps and claimable creator fees are all denominated in USDC rather than ETH. Everything else - the contracts, the launch call, the fee split - works exactly the same.

Every token launched through Klik:

  • Gets a vanity address starting with 69 (Ethereum, Robinhood Chain) or 420 (Base), mined via CREATE2
  • Launches with real Uniswap V4 liquidity locked in the pool
  • Is immediately tradable with swap fees flowing to the creator and the platform

Note: In some cases, a token address might not start with 0x69 (or 0x420 on Base), and that is not an issue.

The KLIK Token

KLIK is the platform's own token, launched on Klik itself - same contracts, same Uniswap V4 pool, same fee schedule as every other token on Ethereum mainnet.

FieldValue
Token0x5886e4d8d6a41a5ea2f3f77f23d7a7e942d581f1
ChainEthereum mainnet 1
Symbol / decimalsKLIK / 18
Initial supply1,000,000,000
Treasury0xe6b2a647f636109e66d2942473d9bb5fb86278cc (Safe, 2-of-3)

Supply only ever goes down. Buybacks burn what they buy, sototalSupply() is the live number and the figure above is the starting one.

Distribution

80% of supply is held in the Safe multisig above and is not circulating. It splits two ways:

  • 70% - community reserve. Held in the Safe and not circulating. Nothing from it is sold on the market; how it is distributed is announced round by round (see Airdrop Rounds).
  • 10% - team, vesting. Released on a schedule rather than at once.

The remaining 20% went into the Uniswap V4 pool at launch and is what trades today. There was no presale, no private round and no allocation to anyone outside the two lines above.

What the revenue does

Klik earns a share of every swap on every token launched through it, across all chains (see Fee Schedule). That revenue funds three things and nothing else:

  • Buybacks and burns of KLIK, which permanently reduce supply
  • Airdrop rounds - a share of revenue paid out to the traders and creators who generated it (see Airdrop Rounds)
  • Operational costs - infrastructure, indexers, RPC and audits

Both sides scale with usage: every launch and every trade on any Klik chain feeds the same pool of revenue. Burns are visible on chain in the token's fallingtotalSupply().

Airdrop Rounds

Klik pays rewards in rounds, straight out of platform revenue.

  • Where the money comes from. Klik earns a share of every swap on every token launched through it, on every chain (see Fee Schedule). A slice of that revenue is set aside for each round.
  • Who gets it. Traders, by the volume they traded through Klik during the round, and creators, by the volume the tokens they launched brought. The more you traded or launched, the larger the share.
  • How it is paid. Directly to the wallet that earned it, on chain. Nothing to buy, stake, lock or claim.
  • When. Each round is announced ahead of time with its dates and the amount on the table. When it closes, shares are computed from on-chain volume and the payout follows.

Fee Schedule

This schedule applies to Normal launches. Tokens launched through Hook Labs do not use it at all: their fee is fixed by whoever designed the hook and splits five ways instead of two. If you launched with a community hook, read that section instead.

Klik charges a dynamic swap fee that decreases as the token's market cap grows. This rewards early believers and reduces friction for larger tokens. Fees are split between the platform and the token creator.

Market Cap (ETH)Total FeePlatformCreator
0 - 15 ETH1.00%0.60%0.40%
15 - 25 ETH0.95%0.55%0.40%
25 - 50 ETH0.90%0.45%0.45%
50 - 100 ETH0.85%0.42%0.43%
100 - 200 ETH0.80%0.38%0.42%
200 - 350 ETH0.75%0.35%0.40%
350 - 550 ETH0.70%0.32%0.38%
550 - 800 ETH0.65%0.29%0.36%
800 - 1200 ETH0.62%0.26%0.36%
1200 - 1600 ETH0.58%0.24%0.34%
1600 - 2000 ETH0.54%0.22%0.32%
2000 - 3000 ETH0.50%0.20%0.30%
3000 - 4500 ETH0.35%0.13%0.22%
4500 - 6000 ETH0.22%0.07%0.15%
>= 6000 ETH0.10%0.03%0.07%

Arc (USDC)

On Arc the market cap that drives the fee tier is measured in USDC, since USDC is the chain's native currency. The fee percentages and the platform/creator split are identical to the ladder above - only the thresholds are USD-denominated.

Market Cap (USDC)Total FeePlatformCreator
0 - 45K USDC1.00%0.60%0.40%
45K - 75K USDC0.95%0.55%0.40%
75K - 150K USDC0.90%0.45%0.45%
150K - 300K USDC0.85%0.42%0.43%
300K - 600K USDC0.80%0.38%0.42%
600K - 1.05M USDC0.75%0.35%0.40%
1.05M - 1.65M USDC0.70%0.32%0.38%
1.65M - 2.4M USDC0.65%0.29%0.36%
2.4M - 3.6M USDC0.62%0.26%0.36%
3.6M - 4.8M USDC0.58%0.24%0.34%
4.8M - 6M USDC0.54%0.22%0.32%
6M - 9M USDC0.50%0.20%0.30%
9M - 13.5M USDC0.35%0.13%0.22%
13.5M - 18M USDC0.22%0.07%0.15%
>= 18M USDC0.10%0.03%0.07%

Hook Labs Fees

A token launched with a community hook does not use the schedule above. Nothing about it carries over: not the rate, not how it changes, and not who gets paid. Everything below is fixed in the hook's contract when the hook is created, and cannot be changed afterwards by anyone, including klik.

What a trade pays

  • Base tax: a flat rate per side, up to 5% on buys and up to 5% on sells, chosen by the hook's designer. It does not fall as the market cap grows.
  • Launch protection (optional): for up to the first 10 minutes, both buys and sells can start as high as 25% and slide down to the base rate.
  • Dynamic tax (optional): up to +3% on top while the pool is busy, scaling with volume traded in a rolling window and fading as it cools.
  • Whatever these add up to, a single trade is never charged more than 25% in total. That ceiling is a constant in the hook contract.

Where the fee goes

Every fee splits five ways. The platform's share is a solidity constant; the other four are chosen by the hook's designer and must total exactly 90%.

ShareRangeWho sets it
Platform10%, fixedNobody. No owner or admin can change it.
Hook designer0 - 5%The hook's designer
Token creator0 - 90%The hook's designer
Buyback & burn0 - 90%The hook's designer
Holder rewards (paid in ETH)0 - 90%The hook's designer

Read that third row carefully before launching on someone else's hook. Your share as the token creator is whatever the hook's designer decided, and a hook is allowed to set it to 0%. The launch form shows the exact figures for the hook you picked, under “What this hook does”, before you sign anything.

Claiming works the same as a Normal launch: the token creator's share accumulates inside the token contract and is withdrawn with collectFees. The hook designer's share accumulates on the hook and is withdrawn with claimHookCreatorEarnings. Buyback and holder-reward shares are spent automatically and are never claimable by anyone.

Factory Contracts

Pass the correct factory address for your target chain when calling deployCoin or generating a salt.

ChainChain IDFactory Address
Ethereum10x254Bf550657040f78608476cE9AaD820aB2266ad
Base84530xC7A3b937E24087cFC8aD64A23326a989A3D03011
Robinhood Chain46630x16cF6788B762EE8969744586eD16fc5705140dd7
Arc (USDC)50420x7E5AEACFF30daBeDC729A0456f99f3f80Aa2217C

Hook Labs contracts (Ethereum)

Hook Labs launches run on a second factory, deployed alongside the one above. Normal launches are unaffected and still go through the factory in the table.

ContractAddressWhat it is
KlikFactory0xDfb7fe5E4Ea0504AfeE387C42Bb86e5860D29e8AdeployCoinWithHook lives here
KlikHookFactory0xDF78EDCcc9a038EA34a0423d9225579dD9722cD0the hook profile registry
Shared klik hook0xB8D9477eB6eF5F0df5219Cf0c8130DD7e520e0CCwhere profiles without their own contract live
ConfigurableKlikHook0xA5788fC80A709c1931299c7559a5D31ee1a7a3Dcimplementation every unique hook clones

All four are verified on Etherscan. The shared hook is an EIP-1167 clone, so Etherscan shows it as a proxy onto the implementation.

Pair-to-anything launchers

A separate launcher opens a coin against any ERC-20 rather than against the chain's own coin. Because the pair is not on a list, the contract cannot know what a unit of it is worth: the dollar price arrives with the launch as a quote signed by the site's API, and the launcher derives the opening price from it, so every coin opens at the same $5,000 market cap whatever the pair is. Fee is 1% in and 1% out, forever.

ChainContractAddress
Ethereum 1AnyLauncher0x95E0dA371711502EeD78Bcc3F17856b4415120CA
AnyHook0x6D01769aB90674D0d56b155f57003284E1B460Cc
AnyTokenDeployer0x97904aF137638F2068CaF2f15FE97bA29232172F
AnyPairZap0x30A62AAcC51c2730FE3c2CeC8bcc9eB980d76cDc
Base 8453AnyB20Launcher0x55fD4A4c5F852c1FAfDA40333FeF65d8C948035D
AnyHook0x5B0a864213d49F23Ed2A6635cc42b3b4FE7720CC
AnyHookDeployer0x7FFAe822eb74Fc8694C2B63326f395882235E199
AnyPairZap0x03AC48637d2A4A942BD5e133bA4F723858373844

The coin differs between the two chains, and so do two calls. On Ethereum it is an ERC-20 whose CREATE2 address depends on the salt and the name and symbol, so predictToken takes four arguments and isValidSalt five. On Base it is a native B20 minted through the chain's factory precompile — admin-less, with its supply cap pinned to the amount minted, both enforced by the chain rather than by a contract — and its address comes from the salt alone, so the same two calls take two and three arguments. There is no token-deployer on Base for the same reason: there is no creation code to carry.

Base cannot pair against every token. A pool needs the coin to sort above the pair, and every native B20 is created at or above 0xB200000000000000000000000000000000000000. A pair at or above that address can therefore never be sorted below one — not by trying more salts, but at all. Roughly three addresses in ten fall there, cbBTC among them, as does every tokenized stock on Base. Ask isSortablePair(pair) before you offer a pair to anyone; the launcher refuses to whitelist one and refuses the launch as well. Those pairs still work on Ethereum.

Which pairs are allowed is a list on the launcher, with a switch: canLaunchAgainst(pair) folds both. A blacklist always applies; the whitelist applies while whitelistEnforced() is true, which it is from deployment. allowedPairs() returns the whole list.

AnyPairZap is a convenience, not a requirement: it turns ETH into the pair and the pair into the coin in one transaction, which is what a buyer who does not already hold the pair needs. It holds nothing between transactions and is owned by nobody.

Stock launchers

The same one-transaction launch, with a tokenized stock on the other side of the pool instead of the chain's own coin. Note the decimals: a stock carries 8 on Base and 18 on Robinhood Chain, and the launcher reads the figure off the asset rather than assuming either.

ChainChain IDLauncherHook
Ethereum10xB6Da931Df2ed2C80db47923D4eb46Fd913Ee01c30x1D06Faa7fF674ccfd8A5736C0c0973cB0a8Fa0cc
Base84530xc6c34E9e2db62EAE58632fB7a5757ecf6B85e4eb0x64746DCa345c5652BC251597bd99562216a7a0Cc
Robinhood Chain46630xb2585cF66680776caaAB892f2AD7141C5A927d540x41b82571EAaed65F18aD3428F066CD23b9b3A0cc

Klik Reserve (NFT-backed coins)

A vault holds a collection's NFTs and issues fungible units against them; a coin is then launched against those units. Live on Ethereum and Robinhood Chain.

ChainContractAddress
Ethereum 1KlikFactory0xc3bd1b155C5b4e73369706Ae0c2aD6FC57Eef131
Router0xd0F658CCE3BA32eaab565618401ffbFeef18A885
Hook0x2DA89b56294a2A63bC5cc8431Eb86dc86761A0cc
Robinhood Chain 4663KlikFactory0xf4cB0cD49aF39753759deA5D1e7DEFc8f5203a63
Router0xadb7Cb08a44B1Ef578DF75Bd9103f6f31577b746
Hook0xc6571752C0F7f80ED5a38589ad345d48Fe92a0Cc

Every address on this page was read back off its chain on 2026-09-09 and had code at it. Hooks sit at addresses ending in the permission bits Uniswap v4 requires, so they are mined rather than chosen — a hook address is never arbitrary.

Launch Sequence

deployCoin is the only on-chain step. Everything before it - hosting metadata, picking a salt - is up to you. The Klik APIs exist for convenience but are not required: any URL-hosted JSON and any random bytes32 work. Each step below explains the API path the in-app launch flow uses, plus the self-hosted alternative.

Step 1 - Image (use any host you like)

Your token needs an image URL inside its metadata JSON. You do not need to call our API for this - host the image on your own CDN, S3, R2, IPFS pin, anywhere reachable over HTTP(S), and reference that URL in the metadata JSON. The Klik in-app flow uses /api/uploadImage only as a convenience for users who don't have their own hosting; it's not part of the deploy contract.

Step 2 - Metadata JSON (use any host you like)

deployCoin takes a string metadataUri. The contract stores it verbatim and never fetches it. You do not need to use our metadata API - any URL pointing at JSON in the structure below will work: an HTTPS URL on your own server (e.g. https://example.com/metadata.json), an ipfs://... URI, an Arweave link, anything. Klik and any third-party indexer that follows our schema will read it.

Required JSON structure:

{
  "name": "My Token",
  "symbol": "MYT",
  "description": "Your token description",
  "image": "https://example.com/logo.png",
  "website": "https://mytoken.com",
  "twitter": "https://x.com/mytoken",
  "telegram": "https://t.me/mytoken"
}

The Klik in-app flow uses /api/uploadMetadata to pin the JSON to IPFS for users who don't want to run their own host. It's a convenience layer - the contract treats your self-hosted URL identically.

Step 3 - Salt (use any random bytes32)

deployCoin takes a bytes32 salt that determines the CREATE2 address of your token. You do not need to call our API for this - any random 32-byte value works, generated client-side in your frontend or backend:

// Random bytes32 (browser)
const salt = '0x' + Array.from(crypto.getRandomValues(new Uint8Array(32)))
  .map(b => b.toString(16).padStart(2, '0'))
  .join('');

// Random bytes32 (Node / ethers)
const salt = ethers.hexlify(ethers.randomBytes(32));

Use /api/generate-salt only if you want a vanity address starting with 0x69 (Ethereum) or 0x420 (Base). The API mines salts on a private RPC pool until it finds one that produces the desired prefix. Skipping the call costs nothing - your token still deploys, just at a non-vanity address.

Step 4 - Call deployCoin

function deployCoin(
  string calldata name,
  string calldata symbol,
  string calldata metadataUri,
  bytes32 salt,
  uint256 configId
) external payable;
  • metadataUri - any URL pointing at JSON in the structure above (the in-app flow passes the IPFS CID returned by /api/uploadMetadata, but https://example.com/metadata.json works just as well)
  • salt - any 0x-prefixed bytes32. Use /api/generate-salt only if you want a vanity prefix; otherwise random bytes are fine
  • configId - Ethereum uses 0 to 4; Base uses 0; Arc uses 0 to 2
  • msg.value - optional creator buy amount (native USDC on Arc, ETH elsewhere)

Ethereum liquidity tiers:

configIdStarting Liquidity
00.69 ETH
11 ETH
22 ETH
35 ETH
410 ETH

Base: only configId = 0 is available.

Arc liquidity tiers (USDC):

configIdStarting Liquidity
02,000 USDC
14,000 USDC
28,000 USDC

Arc tiers carry no creator-buy penalty (penaltyMultiplier = 0), and the creator buy you send as msg.value is native USDC.

Creator Buy Penalty

⚠️ Removed — the creator buy penalty no longer exists. Creator buys at deploy time are no longer penalized.

If you pass a non-zero msg.value to deployCoin, the contract applies a penalty to discourage disproportionate supply accumulation by the creator at launch. The penalty is not a flat percentage — each liquidity config carries its ownpenaltyMultiplier, so the same creator buy amount results in a different penalty depending on which configId you picked.

The effective penalty is derived on-chain from two values:

  • getPenalty(ethAmount) — the base penalty for your buy size (in bps)
  • getLiquidityConfig(configId).penaltyMultiplier — a per-config scaling factor

Final penalty ≈ getPenalty(msg.value) × penaltyMultiplier. Higher-liquidity tiers tend to carry a lower multiplier, so a creator buy on a deeper pool is penalized less than the same buy on a shallower pool. Always simulate deployCoin with your intended msg.value before sending the transaction — the simulation returns the exact token amount you would receive, inclusive of penalty, so you can confirm the outcome instead of deploying blind.

Launch on a Hook

A Hook Labs launch is the same sequence — image, metadata, salt — with one different call at the end, on a different factory. Instead of the standard fee schedule the token inherits the economics of a hook profile: taxes, launch decay, the activity surcharge and the five-way fee split, all chosen by whoever created that profile.

function deployCoinWithHook(
    string  _name,
    string  _symbol,
    string  _metadata,
    bytes32 salt,
    uint256 configId,      // liquidity tier, same ids as deployCoin
    uint256 hookProfileId  // which hook profile the token launches on
) payable returns (uint256 tokensReceived)

Only the last argument is new. configId keeps the same meaning and the same five liquidity tiers, and msg.value is still the creator's initial buy with no deploy penalty.

Finding a profile id

Profiles live in KlikHookFactory. They are numbered from 0 in creation order, so profileCount() minus one is the newest.

profileCount()                  -> uint256
getProfile(uint256 id)         -> (hook, creator, unique, enabled, createdAt, launchCount, name, config)
getProfilesByCreatorPage(...)  -> profiles for one creator, paged

A profile is either shared — a config row on the canonical klik hook, no contract of its own — or unique, its own EIP-1167 clone at a mined …0CC address. Both run identical code and everything in it is keyed per pool, so the two are indistinguishable in behaviour. Only the address and the deploy cost differ.

What the token inherits

  • Buy and sell tax — up to 5% each. A profile may set 0/0.
  • Launch decay — starts high (up to 25%) and slides to the base rate over up to 10 minutes, so snipers pay for being early.
  • Activity surcharge — up to +3% extra while the pool is busy, decaying back as volume cools.
  • Fee split — platform 10% (a constant, unchangeable), then the profile creator's cut, the token creator's cut, buyback & burn, and holder rewards.

The 5% tax ceiling can only ever be lowered by the platform, never raised —setLimits rejects any value above the current one.

Buyback and holder rewards run themselves

Both are automatic and need no keeper. When a pool's buyback budget passes 0.1 ETH, the next swap spends it on the open market and burns what it buys, reducing totalSupply. Holder rewards are pushed out in small batches on every swap.

One thing integrators and holders both need to know: the automatic reward push skips any account that has code. It runs inside a Uniswap v4 unlock, where handing execution to an arbitrary contract could break the swap for everyone else on the pool. Contracts — including EIP-7702 delegated EOAs, i.e. most smart wallets — call claimReward() instead and spend their own gas. Nothing is lost either way; it waits.

Indexing Klik

If you already index Klik launches, adding Hook Labs is one more address on the same filter. The new factory emits the same events with the same signatures, and exposes the same read methods as the one you are already using.

ERC20TokenCreated(address tokenAddress)
  topic0 0x60122e78030aba0a2e4a67adb3e52b411343cc51778f919095d3fe394090c1b2

TokenPurchased(address buyer, address tokenOut, uint256 ethSpent, uint256 tokensReceived)
  topic0 0x8daf503382665d950e449b86172be5222275c90f4ddf69c29fdaa8237a562a6d

Watch both factories for ERC20TokenCreated and you see every Klik launch on Ethereum:

0x254Bf550657040f78608476cE9AaD820aB2266ad  // Normal launches
0xDfb7fe5E4Ea0504AfeE387C42Bb86e5860D29e8A  // Hook Labs launches

These reads behave identically on both:

tokenCount()                      -> uint256
deployedTokens(uint256)           -> (address,string,string,address,uint256,string,uint256,uint256)
getDeploysByPage(page, order)     -> TokenInfo[]
getAllTokensByCreator(address)    -> address[]
getMarketCap(address)             -> uint256   // in wei
tokenHook(address)                -> address

Two differences worth handling. getDeploysByPage reverts with "No tokens deployed" on an empty factory rather than returning an empty array, so wrap the call and treat a revert as zero results. And a Hook Labs token's tax is not a constant — it moves with the launch decay and the activity surcharge, so read it live rather than caching it:

// on the hook returned by factory.tokenHook(token)
tokenToPoolId(address token)              -> bytes32 poolId
currentTaxBps(bytes32 poolId, bool isBuy) -> uint256   // what a swap pays right now
configOfToken(address token)              -> the full profile config
tokenStats(address token)                 -> lifetime fees, volume, swaps, buyback
                                             budget, ETH spent on buybacks, tokens
                                             burned, rewards forwarded, last trade

Tokens are still plain ERC-20s, the pools are ordinary Uniswap v4 pools with a hook attached, and nothing about routing or quoting changes.

Claim Fees

Every swap on a Klik V4 pool routes a slice of the native currency through the hook - ETH on Ethereum, Base and Robinhood Chain, USDC on Arc. The hook sends the platform share straight to the platform treasury and parks the creator share inside the token contract itself. To withdraw that creator share to your wallet, the creator (or the platform admin) makes a single on-chain call:

function collectFees(address tokenAddress)
  external
  returns (uint256 ethCollected);
  • Call it on the factory, not the token. Factory addresses are listed under Factory Contracts.
  • The caller must be either the token's creator (the address recorded as creator on the token contract) or the platform admin. Anyone else gets "Not authorized".
  • The factory withdraws the token contract's entire native-currency balance - ETH, or USDC on Arc - and forwards it to the creator address in the same transaction.
  • The platform share was already taken atomically at swap time, so 100% of the balance held by the token contract belongs to the creator - no extra split happens here.
  • If there's nothing to claim the call simply returns 0 - no revert. Fee balance is per-token, so you call once per token you want to claim.

This applies to the new V4 factory only. V3-legacy tokens use a different flow (Uniswap NFT positions); not covered here.

JavaScript (ethers)

import { ethers } from 'ethers';

const FACTORY_ADDRESS = '0x254Bf550657040f78608476cE9AaD820aB2266ad'; // Ethereum
const TOKEN_ADDRESS   = '0xYourKlikTokenAddress';

const FACTORY_ABI = [
  'function collectFees(address tokenAddress) returns (uint256 ethCollected)'
];

const provider = new ethers.JsonRpcProvider('https://rpc.mevblocker.io/fast');
const wallet   = new ethers.Wallet('0xyour_creator_private_key', provider);

const factory = new ethers.Contract(FACTORY_ADDRESS, FACTORY_ABI, wallet);

const tx = await factory.collectFees(TOKEN_ADDRESS);
console.log('Broadcasted:', tx.hash);
const receipt = await tx.wait();
console.log('Confirmed in block:', receipt.blockNumber);

Python (web3.py)

from web3 import Web3

FACTORY_ADDRESS = '0x254Bf550657040f78608476cE9AaD820aB2266ad'
TOKEN_ADDRESS   = '0xYourKlikTokenAddress'

FACTORY_ABI = [
    {
        'type': 'function',
        'name': 'collectFees',
        'stateMutability': 'nonpayable',
        'inputs': [{'name': 'tokenAddress', 'type': 'address'}],
        'outputs': [{'name': 'ethCollected', 'type': 'uint256'}],
    }
]

w3 = Web3(Web3.HTTPProvider('https://rpc.mevblocker.io/fast'))
account = w3.eth.account.from_key('0xyour_creator_private_key')

factory = w3.eth.contract(
    address=Web3.to_checksum_address(FACTORY_ADDRESS),
    abi=FACTORY_ABI,
)

tx = factory.functions.collectFees(
    Web3.to_checksum_address(TOKEN_ADDRESS)
).build_transaction({
    'from': account.address,
    'nonce': w3.eth.get_transaction_count(account.address),
})

signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print('Broadcasted:', tx_hash.hex())

receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print('Confirmed in block:', receipt.blockNumber)

How much can I claim right now?

The token contract holds the unclaimed creator share as a plain native-currency balance. Read it without sending a tx:

const claimable = await provider.getBalance(TOKEN_ADDRESS);
// ETH on Ethereum / Base / Robinhood Chain - USDC on Arc (also 18 decimals)
console.log('Claimable creator fees:', ethers.formatEther(claimable));

That value is exactly what collectFees would forward to the creator if called right now. On Arc the same call returns USDC, so no ETH-price conversion is needed to read it as a dollar amount.

Atomic Endpoint

Base URL: https://klik.finance

/api/uploadTokenMetadata is the one-call developer flow. It is not the path used by the in-app launch modal, but it is useful for external integrations.

Content-Type: multipart/form-data or application/json

Use multipart when sending an image. JSON mode is text-only.

FieldTypeRequiredDescription
namestringYesToken name
symbolstringYesToken ticker symbol
descriptionstringNoToken description
websitestringNoProject website URL
twitterstringNoTwitter/X handle or URL
telegramstringNoTelegram link
imagefileNoToken image, multipart only, max 1 MB
creatorstringNoWallet address, required if you want salt generation
factorystringNoFactory address, defaults to Ethereum
chainIdstringNo"1" or "8453"
testnetbooleanNoUse testnet RPC for salt generation
Example payload (multipart with image)
{
  "name": "My Token",
  "symbol": "MYT",
  "description": "Your token description",
  "creator": "0xYourWalletAddress",
  "chainId": "1",
  "image": "<binary file>"
}
Example payload (JSON without image)
{
  "name": "My Token",
  "symbol": "MYT",
  "description": "Your token description",
  "creator": "0xYourWalletAddress",
  "chainId": "1"
}

Response:

{
  "cid": "QmXyz...",
  "url": "QmXyz...",
  "imageCid": "QmAbc...",
  "salt": "0xdeadbeef...",
  "saltAddress": "0x69ab12...",
  "targetPrefix": "69",
  "hasTargetPrefix": true
}

Upload Image

Endpoint: POST /api/uploadImage

Content-Type: multipart/form-data

FieldTypeRequiredDescription
filefileYesImage file, max 1 MB
Frontend payload
{
  "file": "<binary file>"
}

Response:

{ "cid": "QmAbc...", "url": "QmAbc..." }

Upload Metadata

Endpoint: POST /api/uploadMetadata

Content-Type: application/json

The frontend sends this payload before launch.

Frontend payload
{
  "name": "My Token",
  "symbol": "MYT",
  "description": "Your token description",
  "image": "ipfs://QmImageCid...",
  "website": "https://mytoken.com",
  "twitter": "https://x.com/mytoken",
  "telegram": "https://t.me/mytoken"
}

Example payload:

{
  "name": "My Token",
  "symbol": "MYT",
  "description": "Your token description",
  "image": "ipfs://QmAbc...",
  "website": "https://mytoken.com",
  "twitter": "https://x.com/mytoken",
  "telegram": "https://t.me/mytoken"
}

Response:

{ "cid": "QmXyz...", "url": "QmXyz..." }

Generate Salt

Endpoint: POST /api/generate-salt

The frontend sends the chain-specific factory address along with the wallet address.

FieldTypeRequiredDescription
namestringYesToken name
symbolstringYesToken symbol
creatorstringYesCreator wallet address
factorystringNoFactory address, defaults to Ethereum factory
chainIdstringNo"1" or "8453"
testnetbooleanNoUse testnet RPC
num_matchesnumberNoDefaults to 1
max_attemptsnumberNoDefaults to 25000
Frontend payload
{
  "name": "My Token",
  "symbol": "MYT",
  "creator": "0xYourWalletAddress",
  "factory": "0x254Bf550657040f78608476cE9AaD820aB2266ad",
  "chainId": "1"
}

Response:

{
  "factory": "0x254Bf550657040f78608476cE9AaD820aB2266ad",
  "target_prefix": "69",
  "total_attempts": 4821,
  "has_target_prefix": true,
  "results": [
    { "salt": "0xdeadbeef...", "address": "0x69ab12..." }
  ]
}

If has_target_prefix is false, the endpoint still returns a valid fallback salt.

Rate Limits

All endpoints enforce per-IP rate limits.

EndpointPer SecondPer Minute
/api/uploadImage30120
/api/uploadMetadata30120
/api/uploadTokenMetadata1060
/api/generate-salt-60
/api/search6100
/api/sort-mcap640
/api/sort-trending430
/api/deployed-tokens420
/api/ecosystem-stats420
{
  "error": "Rate limit exceeded",
  "message": "Too many requests per second. Please slow down.",
  "resetIn": 1,
  "type": "second"
}

Full Examples

These examples match the current app flow more closely than the previous version: metadata upload, salt generation, then deployCoin.

JavaScript (ethers)

Install: npm install ethers

import { ethers } from 'ethers';

const FACTORY_ABI = [
  'function deployCoin(string name, string symbol, string metadataUri, bytes32 salt, uint256 configId) payable'
];

const RPC_URL = 'https://rpc.mevblocker.io/fast';
const PRIVATE_KEY = '0xyour_private_key_here';
const CHAIN_ID = '1';
const FACTORY_ADDRESS = '0x254Bf550657040f78608476cE9AaD820aB2266ad';

const TOKEN_NAME = 'My Token';
const TOKEN_SYMBOL = 'MYT';
const TOKEN_DESCRIPTION = 'Your token description';
const CONFIG_ID = 1n;
const CREATOR_BUY_ETH = '0';

async function main() {
  const provider = new ethers.JsonRpcProvider(RPC_URL);
  const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
  const creator = await wallet.getAddress();

  const metadataRes = await fetch('https://klik.finance/api/uploadMetadata', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: TOKEN_NAME,
      symbol: TOKEN_SYMBOL,
      description: TOKEN_DESCRIPTION,
      image: '',
      website: '',
      twitter: '',
      telegram: '',
    }),
  });
  if (!metadataRes.ok) throw new Error(await metadataRes.text());
  const { cid } = await metadataRes.json();

  const saltRes = await fetch('https://klik.finance/api/generate-salt', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: TOKEN_NAME,
      symbol: TOKEN_SYMBOL,
      creator,
      factory: FACTORY_ADDRESS,
      chainId: CHAIN_ID,
    }),
  });
  if (!saltRes.ok) throw new Error(await saltRes.text());
  const saltPayload = await saltRes.json();
  const salt = saltPayload.results[0].salt;

  const factory = new ethers.Contract(FACTORY_ADDRESS, FACTORY_ABI, wallet);
  const tx = await factory.deployCoin(
    TOKEN_NAME,
    TOKEN_SYMBOL,
    cid,
    salt,
    CONFIG_ID,
    { value: ethers.parseEther(CREATOR_BUY_ETH) }
  );

  console.log('Broadcasted tx:', tx.hash);
  const receipt = await tx.wait();
  console.log('Confirmed in block:', receipt.blockNumber);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Python (web3.py)

Install: pip install web3 requests

import requests
from web3 import Web3

FACTORY_ABI = [
    {
        "type": "function",
        "name": "deployCoin",
        "stateMutability": "payable",
        "inputs": [
            {"name": "name", "type": "string"},
            {"name": "symbol", "type": "string"},
            {"name": "metadataUri", "type": "string"},
            {"name": "salt", "type": "bytes32"},
            {"name": "configId", "type": "uint256"}
        ],
        "outputs": []
    }
]

RPC_URL = "https://rpc.mevblocker.io/fast"
PRIVATE_KEY = "0xyour_private_key_here"
CHAIN_ID = "1"
FACTORY_ADDRESS = "0x254Bf550657040f78608476cE9AaD820aB2266ad"

TOKEN_NAME = "My Token"
TOKEN_SYMBOL = "MYT"
TOKEN_DESCRIPTION = "Your token description"
CONFIG_ID = 1
CREATOR_BUY_ETH = "0"

w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = w3.eth.account.from_key(PRIVATE_KEY)
creator = account.address

metadata_res = requests.post(
    "https://klik.finance/api/uploadMetadata",
    json={
        "name": TOKEN_NAME,
        "symbol": TOKEN_SYMBOL,
        "description": TOKEN_DESCRIPTION,
        "image": "",
        "website": "",
        "twitter": "",
        "telegram": "",
    },
    timeout=60,
)
metadata_res.raise_for_status()
cid = metadata_res.json()["cid"]

salt_res = requests.post(
    "https://klik.finance/api/generate-salt",
    json={
        "name": TOKEN_NAME,
        "symbol": TOKEN_SYMBOL,
        "creator": creator,
        "factory": FACTORY_ADDRESS,
        "chainId": CHAIN_ID,
    },
    timeout=60,
)
salt_res.raise_for_status()
salt = salt_res.json()["results"][0]["salt"]

factory = w3.eth.contract(
    address=Web3.to_checksum_address(FACTORY_ADDRESS),
    abi=FACTORY_ABI,
)

deploy_call = factory.functions.deployCoin(
    TOKEN_NAME,
    TOKEN_SYMBOL,
    cid,
    bytes.fromhex(salt[2:]),
    CONFIG_ID,
)

value_wei = w3.to_wei(CREATOR_BUY_ETH, "ether")
tx = deploy_call.build_transaction(
    {
        "from": creator,
        "chainId": int(CHAIN_ID),
        "nonce": w3.eth.get_transaction_count(creator),
        "value": value_wei,
    }
)
tx["gas"] = deploy_call.estimate_gas({"from": creator, "value": value_wei})

latest_block = w3.eth.get_block("latest")
if latest_block.get("baseFeePerGas") is not None:
    tx["maxPriorityFeePerGas"] = w3.to_wei(1, "gwei")
    tx["maxFeePerGas"] = latest_block["baseFeePerGas"] * 2 + tx["maxPriorityFeePerGas"]
else:
    tx["gasPrice"] = w3.eth.gas_price

signed_tx = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
print("Broadcasted tx:", tx_hash.hex())

receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print("Confirmed in block:", receipt.blockNumber)