Klik is a permissionless token launchpad on Ethereum mainnet and Base. 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.
Every token launched through Klik:
69 (Ethereum) or 420 (Base), mined via CREATE2Note: In some cases, a token address might not start with 0x69 (or 0x420 on Base), and that is not an issue.
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 Fee | Platform | Creator |
|---|---|---|---|
| 0 - 15 ETH | 1.25% | 0.80% | 0.45% |
| 15 - 55 ETH | 1.20% | 0.70% | 0.50% |
| 55 - 90 ETH | 1.15% | 0.60% | 0.55% |
| 90 - 125 ETH | 1.10% | 0.55% | 0.55% |
| 125 - 165 ETH | 1.05% | 0.52% | 0.53% |
| 165 - 365 ETH | 1.00% | 0.50% | 0.50% |
| 365 - 545 ETH | 0.95% | 0.47% | 0.48% |
| 545 - 725 ETH | 0.90% | 0.44% | 0.46% |
| 725 - 910 ETH | 0.85% | 0.41% | 0.44% |
| 910 - 1090 ETH | 0.80% | 0.38% | 0.42% |
| 1090 - 1275 ETH | 0.75% | 0.35% | 0.40% |
| 1275 - 1450 ETH | 0.70% | 0.33% | 0.37% |
| 1450 - 1635 ETH | 0.65% | 0.30% | 0.35% |
| 1635 - 1815 ETH | 0.60% | 0.28% | 0.32% |
| 1815 - 2000 ETH | 0.55% | 0.27% | 0.28% |
| 2000 - 2175 ETH | 0.525% | 0.25% | 0.275% |
| >= 2175 ETH | 0.35% | 0.15% | 0.20% |
Pass the correct factory address for your target chain when calling deployCoin or generating a salt.
| Chain | Chain ID | Factory Address |
|---|---|---|
| Ethereum | 1 | 0xDE60796060c24638c389eFBD36b6b919805CA655 |
| Base | 8453 | 0xB6CB1c049ee8942683Fd3172f7EBA63B6e8a6835 |
Before the wallet signs, the current frontend does three backend calls in this order: upload image, upload metadata JSON, then generate the vanity salt.
If the user attached an image, the frontend first calls /api/uploadImage with multipart form data and receives an image CID.
The frontend then calls /api/uploadMetadata. If an image exists, the metadata payload uses ipfs://<imageCid>. Otherwise it sends an empty string.
The frontend calls /api/generate-salt with the wallet address, chosen chain, and matching factory address.
deployCoinfunction deployCoin(
string calldata name,
string calldata symbol,
string calldata metadataUri,
bytes32 salt,
uint256 configId
) external payable;metadataUri - the current frontend passes the raw metadata CID returned by /api/uploadMetadatasalt - the 0x-prefixed bytes32 value from /api/generate-saltconfigId - Ethereum uses 0 to 4; Base uses 0msg.value - optional creator buy amountEthereum liquidity tiers:
| configId | Starting Liquidity |
|---|---|
0 | 0.69 ETH |
1 | 1 ETH |
2 | 2 ETH |
3 | 5 ETH |
4 | 10 ETH |
Base: only configId = 0 is available.
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 factorFinal 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.
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.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Token name |
symbol | string | Yes | Token ticker symbol |
description | string | No | Token description |
website | string | No | Project website URL |
twitter | string | No | Twitter/X handle or URL |
telegram | string | No | Telegram link |
image | file | No | Token image, multipart only, max 1 MB |
creator | string | No | Wallet address, required if you want salt generation |
factory | string | No | Factory address, defaults to Ethereum |
chainId | string | No | "1" or "8453" |
testnet | boolean | No | Use testnet RPC for salt generation |
{
"name": "My Token",
"symbol": "MYT",
"description": "Your token description",
"creator": "0xYourWalletAddress",
"chainId": "1",
"image": "<binary file>"
}{
"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
}Endpoint: POST /api/uploadImage
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | Image file, max 1 MB |
{
"file": "<binary file>"
}Response:
{ "cid": "QmAbc...", "url": "QmAbc..." }Endpoint: POST /api/uploadMetadata
Content-Type: application/json
The frontend sends this payload before launch.
{
"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..." }Endpoint: POST /api/generate-salt
The frontend sends the chain-specific factory address along with the wallet address.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Token name |
symbol | string | Yes | Token symbol |
creator | string | Yes | Creator wallet address |
factory | string | No | Factory address, defaults to Ethereum factory |
chainId | string | No | "1" or "8453" |
testnet | boolean | No | Use testnet RPC |
num_matches | number | No | Defaults to 1 |
max_attempts | number | No | Defaults to 25000 |
{
"name": "My Token",
"symbol": "MYT",
"creator": "0xYourWalletAddress",
"factory": "0xDE60796060c24638c389eFBD36b6b919805CA655",
"chainId": "1"
}Response:
{
"factory": "0xDE60796060c24638c389eFBD36b6b919805CA655",
"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.
All endpoints enforce per-IP rate limits.
| Endpoint | Per Second | Per Minute |
|---|---|---|
/api/uploadImage | 30 | 120 |
/api/uploadMetadata | 30 | 120 |
/api/uploadTokenMetadata | 10 | 60 |
/api/generate-salt | - | 60 |
/api/search | 6 | 100 |
/api/sort-mcap | 6 | 40 |
/api/sort-trending | 4 | 30 |
/api/deployed-tokens | 4 | 20 |
/api/ecosystem-stats | 4 | 20 |
{
"error": "Rate limit exceeded",
"message": "Too many requests per second. Please slow down.",
"resetIn": 1,
"type": "second"
}These examples match the current app flow more closely than the previous version: metadata upload, salt generation, then deployCoin.
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 = '0xDE60796060c24638c389eFBD36b6b919805CA655';
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);
});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 = "0xDE60796060c24638c389eFBD36b6b919805CA655"
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)