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:
69 (Ethereum, Robinhood Chain) 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.
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 Fee | Platform | Creator |
|---|---|---|---|
| 0 - 15 ETH | 1.00% | 0.60% | 0.40% |
| 15 - 25 ETH | 0.95% | 0.55% | 0.40% |
| 25 - 50 ETH | 0.90% | 0.45% | 0.45% |
| 50 - 100 ETH | 0.85% | 0.42% | 0.43% |
| 100 - 200 ETH | 0.80% | 0.38% | 0.42% |
| 200 - 350 ETH | 0.75% | 0.35% | 0.40% |
| 350 - 550 ETH | 0.70% | 0.32% | 0.38% |
| 550 - 800 ETH | 0.65% | 0.29% | 0.36% |
| 800 - 1200 ETH | 0.62% | 0.26% | 0.36% |
| 1200 - 1600 ETH | 0.58% | 0.24% | 0.34% |
| 1600 - 2000 ETH | 0.54% | 0.22% | 0.32% |
| 2000 - 3000 ETH | 0.50% | 0.20% | 0.30% |
| 3000 - 4500 ETH | 0.35% | 0.13% | 0.22% |
| 4500 - 6000 ETH | 0.22% | 0.07% | 0.15% |
| >= 6000 ETH | 0.10% | 0.03% | 0.07% |
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 Fee | Platform | Creator |
|---|---|---|---|
| 0 - 45K USDC | 1.00% | 0.60% | 0.40% |
| 45K - 75K USDC | 0.95% | 0.55% | 0.40% |
| 75K - 150K USDC | 0.90% | 0.45% | 0.45% |
| 150K - 300K USDC | 0.85% | 0.42% | 0.43% |
| 300K - 600K USDC | 0.80% | 0.38% | 0.42% |
| 600K - 1.05M USDC | 0.75% | 0.35% | 0.40% |
| 1.05M - 1.65M USDC | 0.70% | 0.32% | 0.38% |
| 1.65M - 2.4M USDC | 0.65% | 0.29% | 0.36% |
| 2.4M - 3.6M USDC | 0.62% | 0.26% | 0.36% |
| 3.6M - 4.8M USDC | 0.58% | 0.24% | 0.34% |
| 4.8M - 6M USDC | 0.54% | 0.22% | 0.32% |
| 6M - 9M USDC | 0.50% | 0.20% | 0.30% |
| 9M - 13.5M USDC | 0.35% | 0.13% | 0.22% |
| 13.5M - 18M USDC | 0.22% | 0.07% | 0.15% |
| >= 18M USDC | 0.10% | 0.03% | 0.07% |
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.
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%.
| Share | Range | Who sets it |
|---|---|---|
| Platform | 10%, fixed | Nobody. No owner or admin can change it. |
| Hook designer | 0 - 5% | The hook's designer |
| Token creator | 0 - 90% | The hook's designer |
| Buyback & burn | 0 - 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.
Pass the correct factory address for your target chain when calling deployCoin or generating a salt.
| Chain | Chain ID | Factory Address |
|---|---|---|
| Ethereum | 1 | 0x254Bf550657040f78608476cE9AaD820aB2266ad |
| Base | 8453 | 0xC7A3b937E24087cFC8aD64A23326a989A3D03011 |
| Robinhood Chain | 4663 | 0x16cF6788B762EE8969744586eD16fc5705140dd7 |
| Arc (USDC) | 5042 | 0x7E5AEACFF30daBeDC729A0456f99f3f80Aa2217C |
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.
| Contract | Address | What it is |
|---|---|---|
| KlikFactory | 0xDfb7fe5E4Ea0504AfeE387C42Bb86e5860D29e8A | deployCoinWithHook lives here |
| KlikHookFactory | 0xDF78EDCcc9a038EA34a0423d9225579dD9722cD0 | the hook profile registry |
| Shared klik hook | 0xB8D9477eB6eF5F0df5219Cf0c8130DD7e520e0CC | where profiles without their own contract live |
| ConfigurableKlikHook | 0xA5788fC80A709c1931299c7559a5D31ee1a7a3Dc | implementation 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.
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.
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.
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.
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.
deployCoinfunction 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 fineconfigId - Ethereum uses 0 to 4; Base uses 0; Arc uses 0 to 2msg.value - optional creator buy amount (native USDC on Arc, ETH elsewhere)Ethereum 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.
Arc liquidity tiers (USDC):
| configId | Starting Liquidity |
|---|---|
0 | 2,000 USDC |
1 | 4,000 USDC |
2 | 8,000 USDC |
Arc tiers carry no creator-buy penalty (penaltyMultiplier = 0), and the creator buy you send as msg.value is native USDC.
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.
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.
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, pagedA 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.
The 5% tax ceiling can only ever be lowered by the platform, never raised —setLimits rejects any value above the current one.
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.
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 0x8daf503382665d950e449b86172be5222275c90f4ddf69c29fdaa8237a562a6dWatch both factories for ERC20TokenCreated and you see every Klik launch on Ethereum:
0x254Bf550657040f78608476cE9AaD820aB2266ad // Normal launches
0xDfb7fe5E4Ea0504AfeE387C42Bb86e5860D29e8A // Hook Labs launchesThese 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) -> addressTwo 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 tradeTokens are still plain ERC-20s, the pools are ordinary Uniswap v4 pools with a hook attached, and nothing about routing or quoting changes.
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);creator on the token contract) or the platform admin. Anyone else gets "Not authorized".This applies to the new V4 factory only. V3-legacy tokens use a different flow (Uniswap NFT positions); not covered here.
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);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)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.
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": "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.
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 = '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);
});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)