| name | clanker |
| version | 1.0.0 |
| description | Deploy ERC20 tokens on Base, Ethereum, Arbitrum, and other EVM chains using the Clanker SDK. Use when the user wants to deploy a new token, create a memecoin, set up token vesting, configure airdrops, manage token rewards, claim LP fees, or update token metadata. Supports V4 deployment with vaults, airdrops, dev buys, custom market caps, vanity addresses, and multi-chain deployment. |
| author | BankrBot |
| license | MIT |
| tags | ["crypto","defi","token-deployment","base"] |
| env_needed | [{"name":"PRIVATE_KEY","description":"Private key for the EVM wallet deploying the tokens (must start with 0x)","required":true}] |
| metadata | {"zeptoclaw":{"emoji":"🚀","requires":{"anyBins":["npm","yarn","pnpm"]}}} |
Clanker SDK
Deploy production-ready ERC20 tokens with built-in liquidity pools using the official Clanker TypeScript SDK.
🚨 Financial Safety Guardrails
This skill enables high-risk financial operations (token deployment requires gas, dev buys require funds, and liquidity parameters are immutable). You MUST adhere to the following safety rules:
- Always confirm with the user before executing the deployment script.
- Display all parameters clearly before execution: Token Name, Symbol, Total Supply, Vault allocations, Dev buy amounts, and target chain.
- Double-check dev buy amounts: Do NOT execute large dev buys without explicit double-confirmation from the user.
- Warn users about irreversibility: Smart contract deployments are permanent and cannot be deleted once broadcast to the blockchain.
Overview
Clanker is a token deployment protocol that creates ERC20 tokens with Uniswap V4 liquidity pools in a single transaction. The SDK provides a TypeScript interface for deploying tokens with advanced features like vesting, airdrops, and customizable reward distribution.
Quick Start
Installation
npm install clanker-sdk viem
yarn add clanker-sdk viem
pnpm add clanker-sdk viem
Environment Setup
Create a .env file with your private key:
PRIVATE_KEY=0x...your_private_key_here
Basic Token Deployment
import { Clanker } from 'clanker-sdk';
import { createPublicClient, createWalletClient, http, type PublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({
chain: base,
transport: http(),
}) as PublicClient;
const wallet = createWalletClient({
account,
chain: base,
transport: http(),
});
const clanker = new Clanker({ wallet, publicClient });
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
tokenAdmin: account.address,
metadata: {
description: 'My awesome token',
},
context: {
interface: 'Clanker SDK',
},
vanity: true,
});
if (error) throw error;
const { address: tokenAddress } = await waitForTransaction();
console.log('Token deployed at:', tokenAddress);
Core Capabilities
1. Token Deployment
Deploy tokens with full customization including metadata, social links, and pool configuration.
Basic deployment:
- Token name, symbol, and image (IPFS)
- Description and social media links
- Vanity address generation
- Custom pool configurations
Reference: the Advanced Reference Guide below (deployment)
2. Vault (Token Vesting)
Lock a percentage of tokens with lockup and vesting periods:
vault: {
percentage: 10,
lockupDuration: 2592000,
vestingDuration: 2592000,
recipient: account.address,
}
Reference: the Advanced Reference Guide below (vesting)
3. Airdrops
Distribute tokens to multiple addresses using Merkle tree proofs:
import { createAirdrop, registerAirdrop } from 'clanker-sdk/v4/extensions';
const { tree, airdrop } = createAirdrop([
{ account: '0x...', amount: 200_000_000 },
{ account: '0x...', amount: 50_000_000 },
]);
airdrop: {
...airdrop,
lockupDuration: 86_400,
vestingDuration: 86_400,
}
Reference: the Advanced Reference Guide below (airdrops)
4. Rewards Configuration
Configure trading fee distribution:
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 5000,
token: 'Both',
},
{
recipient: '0x...',
admin: '0x...',
bps: 5000,
token: 'Both',
},
],
}
Token Type Options
Choose which tokens each recipient receives from trading fees:
| Token Type | Description |
|---|
'Clanker' | Receive only the deployed token |
'Paired' | Receive only the paired token (e.g., WETH) |
'Both' | Receive both tokens |
Default Bankr Interface Fee
When deploying via Bankr, use this default rewards configuration with 20% interface fee:
const BANKR_INTERFACE_ADDRESS = '0xF60633D02690e2A15A54AB919925F3d038Df163e';
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 8000,
token: 'Paired',
},
{
recipient: BANKR_INTERFACE_ADDRESS,
admin: BANKR_INTERFACE_ADDRESS,
bps: 2000,
token: 'Paired',
},
],
}
Reference: the Advanced Reference Guide below (rewards)
5. Dev Buy
Include an initial token purchase in the deployment:
devBuy: {
ethAmount: 0.1,
recipient: account.address,
}
6. Custom Market Cap
Set initial token price/market cap:
import { getTickFromMarketCap } from 'clanker-sdk';
const customPool = getTickFromMarketCap(5);
pool: {
...customPool,
positions: [
{
tickLower: customPool.tickIfToken0IsClanker,
tickUpper: -120000,
positionBps: 10_000,
},
],
}
Reference: the Advanced Reference Guide below (pool-config)
7. Anti-Sniper Protection
Configure fee decay to protect against snipers:
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
}
Contract Limits & Constants
| Parameter | Value | Notes |
|---|
| Token Supply | 100 billion | Fixed at 100,000,000,000 with 18 decimals |
| Max Extension BPS | 9000 (90%) | Max tokens to extensions, min 10% to LP |
| Max Extensions | 10 | Maximum number of extensions per deployment |
| Vault Min Lockup | 7 days | Minimum lockup duration for vesting |
| Airdrop Min Lockup | 1 day | Minimum lockup duration for airdrops |
| Max LP Fee | 10% | Normal trading fee cap |
| Max Sniper Fee | 80% | Maximum MEV/sniper protection fee |
| Sniper Fee Decay | 2 minutes max | Maximum time for sniper fee decay |
| Max Reward Recipients | 7 | Maximum fee distribution recipients |
| Max LP Positions | 7 | Maximum liquidity positions |
Supported Chains
| Chain | Chain ID | Native Token | Status |
|---|
| Base | 8453 | ETH | ✅ Full support |
| Ethereum | 1 | ETH | ✅ Full support |
| Arbitrum | 42161 | ETH | ✅ Full support |
| Unichain | - | ETH | ✅ Full support |
| Monad | - | MON | ✅ Static fees only |
Post-Deployment Operations
Claim Vaulted Tokens
const claimable = await clanker.getVaultClaimableAmount({ token: TOKEN_ADDRESS });
if (claimable > 0n) {
const { txHash } = await clanker.claimVaultedTokens({ token: TOKEN_ADDRESS });
}
Collect Trading Rewards
const availableFees = await clanker.availableRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
const { txHash } = await clanker.claimRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
Update Token Metadata
const metadata = JSON.stringify({
description: 'Updated description',
socialMediaUrls: [
{ platform: 'twitter', url: 'https://twitter.com/mytoken' },
{ platform: 'telegram', url: 'https://t.me/mytoken' },
],
});
const { txHash } = await clanker.updateMetadata({
token: TOKEN_ADDRESS,
metadata,
});
Update Token Image
const { txHash } = await clanker.updateImage({
token: TOKEN_ADDRESS,
image: 'ipfs://new_image_hash',
});
Common Workflows
Simple Memecoin Launch
- Prepare token image (upload to IPFS)
- Deploy with basic config (name, symbol, image)
- Enable vanity address for memorable contract
- Share contract address
Community Token with Airdrop
- Compile airdrop recipient list
- Create Merkle tree with
createAirdrop()
- Deploy token with airdrop extension
- Register airdrop with Clanker service
- Share claim instructions
Creator Token with Vesting
- Deploy with vault configuration
- Set lockup period (cliff)
- Set vesting duration
- Claim tokens as they vest
Full Deployment Config
const BANKR_INTERFACE_ADDRESS = '0xF60633D02690e2A15A54AB919925F3d038Df163e';
const tokenConfig = {
chainId: 8453,
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://...',
tokenAdmin: account.address,
metadata: {
description: 'Token description',
socialMediaUrls: [
{ platform: 'twitter', url: '...' },
{ platform: 'telegram', url: '...' },
],
},
context: {
interface: 'Bankr',
platform: 'farcaster',
messageId: '',
id: '',
},
vault: {
percentage: 10,
lockupDuration: 2592000,
vestingDuration: 2592000,
recipient: account.address,
},
devBuy: {
ethAmount: 0,
recipient: account.address,
},
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 8000,
token: 'Paired',
},
{
recipient: BANKR_INTERFACE_ADDRESS,
admin: BANKR_INTERFACE_ADDRESS,
bps: 2000,
token: 'Paired',
},
],
},
pool: {
pairedToken: '0x4200000000000000000000000000000000000006',
positions: 'Standard',
},
fees: 'StaticBasic',
vanity: true,
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
},
};
Best Practices
Security
- Never expose private keys - Use environment variables
- Test on testnet first - Verify configs before mainnet
- Simulate transactions - Use
*Simulate methods before execution
- Verify addresses - Double-check all recipient addresses
Token Design
- Choose meaningful names - Clear, memorable token identity
- Use quality images - High-res, appropriate IPFS images
- Configure vesting wisely - Align with project timeline
Gas Optimization
- Use Base or Arbitrum - Lower gas fees
- Batch operations - Combine when possible
- Monitor gas prices - Deploy during low-traffic periods
Troubleshooting
Common Issues
- "Missing PRIVATE_KEY" - Set environment variable
- "Insufficient balance" - Fund wallet with native token
- "Transaction reverted" - Check parameters, simulate first
- "Invalid image" - Ensure IPFS hash is accessible
Debug Steps
- Check wallet balance
- Verify chain configuration
- Use simulation methods
- Check transaction on block explorer
- Review error message details
Resources
💡 Pro Tip: Always use the vanity: true option for memorable contract addresses.
⚠️ Security: Never commit private keys. Use .env files and add them to .gitignore.
🚀 Quick Win: Start with the simple deployment example, then add features like vesting and rewards as needed.
📚 Advanced Reference Guide
The following sections were inlined from the former references/ directory.
📖 Reference: airdrops.md
Airdrops
Distribute tokens to multiple addresses using Merkle tree proofs with the Clanker airdrop extension.
Overview
Airdrops allow you to allocate tokens to specific addresses during deployment. Key features:
- Merkle tree verification - Efficient on-chain proof verification
- Lockup/vesting - Control when tokens become claimable
- Clanker service - Optional proof storage and generation
Create an Airdrop
import { createAirdrop, registerAirdrop } from 'clanker-sdk/v4/extensions';
const { tree, airdrop } = createAirdrop([
{
account: '0x308112D06027Cd838627b94dDFC16ea6B4D90004',
amount: 200_000_000,
},
{
account: '0x1eaf444ebDf6495C57aD52A04C61521bBf564ace',
amount: 50_000_000,
},
{
account: '0x04F6ef12a8B6c2346C8505eE4Cff71C43D2dd825',
amount: 10_000_000,
},
]);
Deploy with Airdrop
Include the airdrop in your token deployment:
import { Clanker } from 'clanker-sdk';
import { createAirdrop } from 'clanker-sdk/v4/extensions';
const { tree, airdrop } = createAirdrop([
{ account: '0x...', amount: 200_000_000 },
{ account: '0x...', amount: 50_000_000 },
]);
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'Airdrop Token',
symbol: 'DROP',
image: 'ipfs://...',
tokenAdmin: account.address,
metadata: {
description: 'Token with an airdrop',
},
context: {
interface: 'Clanker SDK',
},
airdrop: {
...airdrop,
lockupDuration: 86_400,
vestingDuration: 86_400,
},
vanity: true,
});
if (error) throw error;
const { address } = await waitForTransaction();
console.log('Token deployed at:', address);
Register Airdrop with Clanker Service
Store the Merkle tree with Clanker's service for easy proof generation:
import { registerAirdrop, fetchAirdropProofs } from 'clanker-sdk/v4/extensions';
import { sleep } from 'bun';
await sleep(10_000);
await registerAirdrop(tokenAddress, tree);
console.log('Airdrop registered!');
Fetch Airdrop Proofs
Get proofs for a specific address:
import { fetchAirdropProofs } from 'clanker-sdk/v4/extensions';
const { proofs } = await fetchAirdropProofs(
tokenAddress,
'0x308112D06027Cd838627b94dDFC16ea6B4D90004'
);
console.log('Proofs:', proofs);
Claim Airdrop Tokens
Build and execute a claim transaction:
import { getClaimAirdropTransaction } from 'clanker-sdk/v4/extensions';
const { proof, entry } = proofs[0];
const tx = getClaimAirdropTransaction({
chainId: base.id,
token: tokenAddress,
recipient: entry.account,
amount: entry.amount,
proof,
});
const hash = await wallet.sendTransaction(tx);
Self-Managed Tree Storage
If you don't want to use the Clanker service, store and manage the tree yourself:
import { StandardMerkleTree } from '@openzeppelin/merkle-tree';
import fs from 'fs';
const { tree, airdrop } = createAirdrop([...]);
fs.writeFileSync('merkle-tree.json', JSON.stringify(tree.dump()));
const loadedTree = StandardMerkleTree.load(
JSON.parse(fs.readFileSync('merkle-tree.json', 'utf8'))
);
Contract Limits
From the Solidity contracts:
- Minimum Lockup Duration: 1 day (86,400 seconds) - enforced on-chain
- Maximum Extension BPS: 9000 (90% of supply can go to extensions total)
airdrop: {
...airdrop,
lockupDuration: 86_400,
vestingDuration: 0,
}
Note: Unlike vault (7 days min), airdrop only requires 1 day minimum lockup.
Airdrop with Extended Vesting
For gradual distribution:
const THIRTY_DAYS = 2592000;
airdrop: {
...airdrop,
lockupDuration: THIRTY_DAYS,
vestingDuration: THIRTY_DAYS * 3,
}
Complete Airdrop Workflow
import { sleep } from 'bun';
import { createPublicClient, createWalletClient, http, type PublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import {
createAirdrop,
fetchAirdropProofs,
getClaimAirdropTransaction,
registerAirdrop,
} from 'clanker-sdk/v4/extensions';
import { Clanker } from 'clanker-sdk/v4';
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({ chain: base, transport: http() }) as PublicClient;
const wallet = createWalletClient({ account, chain: base, transport: http() });
const clanker = new Clanker({ publicClient, wallet });
const { tree, airdrop } = createAirdrop([
{ account: '0x308112D06027Cd838627b94dDFC16ea6B4D90004', amount: 200_000_000 },
{ account: '0x1eaf444ebDf6495C57aD52A04C61521bBf564ace', amount: 50_000_000 },
{ account: '0x04F6ef12a8B6c2346C8505eE4Cff71C43D2dd825', amount: 10_000_000 },
]);
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'Airdrop Token',
symbol: 'DROP',
image: 'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
tokenAdmin: account.address,
metadata: { description: 'Token with an airdrop' },
context: { interface: 'Clanker SDK' },
airdrop: {
...airdrop,
lockupDuration: 86_400,
vestingDuration: 86_400,
},
vanity: true,
});
if (error) throw error;
const { address, error: txError } = await waitForTransaction();
if (txError) throw txError;
console.log(`Token deployed at: ${address}`);
console.log('Waiting for indexing...');
await sleep(10_000);
await registerAirdrop(address, tree);
console.log('Airdrop registered!');
const { proofs } = await fetchAirdropProofs(
address,
'0x308112D06027Cd838627b94dDFC16ea6B4D90004'
);
console.log('Proofs ready for claiming:', proofs);
Best Practices
- Verify recipient addresses - Double-check all addresses before deployment
- Test with small amounts - Verify the flow on testnet first
- Secure tree storage - Back up the Merkle tree if self-managing
- Reasonable lockup - Balance between anti-dump and user experience
- Communicate claim process - Provide clear instructions to recipients
📖 Reference: deployment.md
Token Deployment
Complete guide to deploying tokens with the Clanker SDK.
Simple Deployment
The minimal configuration for deploying a token:
import { Clanker } from 'clanker-sdk';
import { createPublicClient, createWalletClient, http, type PublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({
chain: base,
transport: http(),
}) as PublicClient;
const wallet = createWalletClient({
account,
chain: base,
transport: http(),
});
const clanker = new Clanker({ wallet, publicClient });
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
tokenAdmin: account.address,
chainId: base.id,
metadata: {
description: 'My really cool token',
},
context: {
interface: 'Clanker SDK',
},
vanity: true,
});
if (error) throw error;
console.log(`Deploying... ${base.blockExplorers.default.url}/tx/${txHash}`);
const { address: tokenAddress } = await waitForTransaction();
console.log(`Done! ${base.blockExplorers.default.url}/address/${tokenAddress}`);
Multi-Chain Deployment
Deploy to different chains by changing the chain configuration:
import { base, mainnet, arbitrum, unichain } from 'viem/chains';
const RPC_URLS: Record<number, string | undefined> = {
[mainnet.id]: process.env.RPC_URL_MAINNET,
[base.id]: process.env.RPC_URL_BASE,
[arbitrum.id]: process.env.RPC_URL_ARBITRUM,
[unichain.id]: process.env.RPC_URL_UNICHAIN,
};
const CHAIN = base;
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(RPC_URLS[CHAIN.id]),
}) as PublicClient;
const wallet = createWalletClient({
account,
chain: CHAIN,
transport: http(RPC_URLS[CHAIN.id]),
});
const clanker = new Clanker({ wallet, publicClient });
const { txHash, waitForTransaction, error } = await clanker.deploy({
chainId: CHAIN.id,
name: 'My Token',
symbol: 'TKN',
});
Token Metadata
Configure rich metadata for your token:
metadata: {
description: 'Token with custom configuration including vesting and rewards',
socialMediaUrls: [
{ platform: 'twitter', url: 'https://twitter.com/mytoken' },
{ platform: 'telegram', url: 'https://t.me/mytoken' },
{ platform: 'discord', url: 'https://discord.gg/mytoken' },
],
auditUrls: ['https://example.com/audit'],
}
Context Configuration
Track deployment source and social platform info:
context: {
interface: 'Clanker SDK',
platform: 'farcaster',
messageId: '',
id: '',
}
Vanity Addresses
Enable vanity address generation for memorable contract addresses:
const { txHash, waitForTransaction, error } = await clanker.deploy({
vanity: true,
});
Full Configuration Example
const BANKR_INTERFACE_ADDRESS = '0xF60633D02690e2A15A54AB919925F3d038Df163e';
const { txHash, waitForTransaction, error } = await clanker.deploy({
chainId: base.id,
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi',
tokenAdmin: account.address,
metadata: {
description: 'Token with custom configuration including vesting and rewards',
socialMediaUrls: [
{ platform: 'twitter', url: 'https://twitter.com/mytoken' },
{ platform: 'telegram', url: 'https://t.me/mytoken' },
],
},
context: {
interface: 'Bankr',
platform: 'farcaster',
messageId: '',
id: '',
},
vault: {
percentage: 10,
lockupDuration: 2592000,
vestingDuration: 2592000,
recipient: account.address,
},
devBuy: {
ethAmount: 0,
recipient: account.address,
},
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 8000,
token: 'Paired',
},
{
recipient: BANKR_INTERFACE_ADDRESS,
admin: BANKR_INTERFACE_ADDRESS,
bps: 2000,
token: 'Paired',
},
],
},
pool: {
pairedToken: '0x4200000000000000000000000000000000000006',
positions: 'Standard',
},
fees: 'StaticBasic',
vanity: true,
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
},
});
Deploy with Custom Salt
Use a specific salt for deterministic address generation:
import { zeroHash } from 'viem';
const { txHash, waitForTransaction, error } = await clanker.deploy({
salt: zeroHash,
});
Deploy with USDC Pair
Create a token paired with USDC instead of WETH:
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const { txHash, waitForTransaction, error } = await clanker.deploy({
pool: {
pairedToken: USDC_BASE,
positions: 'Standard',
},
});
Deploy with 20 ETH Initial Liquidity
Configure larger initial market cap:
import { getTickFromMarketCap } from 'clanker-sdk';
const customPool = getTickFromMarketCap(20);
const { txHash, waitForTransaction, error } = await clanker.deploy({
pool: {
...customPool,
positions: [
{
tickLower: customPool.tickIfToken0IsClanker,
tickUpper: -120000,
positionBps: 10_000,
},
],
},
});
Error Handling
Always handle deployment errors:
const { txHash, waitForTransaction, error } = await clanker.deploy(config);
if (error) {
console.error('Deployment failed:', error.message);
process.exit(1);
}
console.log(`Transaction: ${txHash}`);
const { address, error: txError } = await waitForTransaction();
if (txError) {
console.error('Transaction failed:', txError.message);
process.exit(1);
}
console.log('Token deployed at:', address);
Simulation Before Deployment
Test deployment without executing:
try {
await clanker.deploySimulate(config);
console.log('Simulation successful');
} catch (error) {
console.error('Simulation failed:', error);
}
📖 Reference: pool-config.md
Pool Configuration
Configure Uniswap V4 liquidity pool settings for your Clanker token.
Overview
Clanker tokens are deployed with Uniswap V4 liquidity pools. You can customize:
- Paired token - WETH, USDC, or other tokens
- Initial market cap - Starting price/liquidity
- Pool positions - Liquidity distribution
- Fee structure - Static or dynamic fees
- Sniper protection - Fee decay for early trades
Default Pool Configuration
By default, tokens are paired with WETH using standard positions:
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://...',
tokenAdmin: account.address,
});
Paired Token Options
WETH (Default)
import { WETH_ADDRESSES } from 'clanker-sdk/constants';
pool: {
pairedToken: WETH_ADDRESSES[base.id],
positions: 'Standard',
}
USDC
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
pool: {
pairedToken: USDC_BASE,
positions: 'Standard',
}
Pool Positions
Choose from preset position configurations:
import { POOL_POSITIONS } from 'clanker-sdk/constants';
pool: {
pairedToken: WETH_ADDRESSES[base.id],
positions: POOL_POSITIONS.Standard,
positions: POOL_POSITIONS.Project,
}
Custom Market Cap
Set a specific initial market cap:
import { getTickFromMarketCap } from 'clanker-sdk';
const customPool = getTickFromMarketCap(5);
pool: {
...customPool,
positions: [
{
tickLower: customPool.tickIfToken0IsClanker,
tickUpper: -120000,
positionBps: 10_000,
},
],
}
Market Cap Examples
const pool5ETH = getTickFromMarketCap(5);
const pool10ETH = getTickFromMarketCap(10);
const pool20ETH = getTickFromMarketCap(20);
Fee Configurations
Choose between static and dynamic fee structures:
import { FEE_CONFIGS } from 'clanker-sdk/constants';
fees: FEE_CONFIGS.StaticBasic,
fees: FEE_CONFIGS.Dynamic3,
Note: Monad chain only supports static fees (FEE_CONFIGS.StaticBasic).
Sniper Protection
Configure fee decay to protect against snipers:
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
}
Contract Limits
From the Solidity contracts:
| Parameter | Limit | Notes |
|---|
| MAX_MEV_LP_FEE | 800,000 (80%) | Maximum starting fee |
| MAX_LP_FEE | 100,000 (10%) | Normal LP fee cap |
| MAX_MEV_MODULE_DELAY | 120 seconds | Maximum decay time |
| Fee Denominator | 1,000,000 | 100% = 1,000,000 |
Important:
startingFee cannot exceed 800,000 (80%)
secondsToDecay cannot exceed 120 seconds (2 minutes)
startingFee must be greater than endingFee
How Sniper Fees Work
- Trade immediately after deployment → Pay up to 80% fee
- Fee decays parabolically over the decay period
- After decay period → Normal LP fee applies
Aggressive Sniper Protection
sniperFees: {
startingFee: 800_000,
endingFee: 30_000,
secondsToDecay: 120,
}
Moderate Sniper Protection
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
}
Minimal Sniper Protection
sniperFees: {
startingFee: 100_000,
endingFee: 30_000,
secondsToDecay: 5,
}
Full Pool Configuration Example
import { getTickFromMarketCap } from 'clanker-sdk';
import { FEE_CONFIGS, WETH_ADDRESSES } from 'clanker-sdk/constants';
import { base } from 'viem/chains';
const customPool = getTickFromMarketCap(10);
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'Custom Pool Token',
symbol: 'CPT',
image: 'ipfs://...',
tokenAdmin: account.address,
pool: {
pairedToken: WETH_ADDRESSES[base.id],
...customPool,
positions: [
{
tickLower: customPool.tickIfToken0IsClanker,
tickUpper: -120000,
positionBps: 10_000,
},
],
},
fees: FEE_CONFIGS.StaticBasic,
sniperFees: {
startingFee: 666_777,
endingFee: 41_673,
secondsToDecay: 15,
},
});
Chain-Specific WETH Addresses
import { WETH_ADDRESSES } from 'clanker-sdk/constants';
const wethBase = WETH_ADDRESSES[8453];
const wethMainnet = WETH_ADDRESSES[1];
const wethArbitrum = WETH_ADDRESSES[42161];
LP Position Limits
From the Solidity contracts:
- Maximum LP Positions: 7
- Position BPS Must Total: 10,000 (100%)
- Tick Bounds: Must be within MIN_TICK and MAX_TICK
- Tick Spacing: Ticks must be multiples of the pool's tick spacing
- Lower Tick Constraint: tickLower must be >= tickIfToken0IsClanker
pool: {
positions: [
{ tickLower: -60000, tickUpper: -30000, positionBps: 5000 },
{ tickLower: -30000, tickUpper: 0, positionBps: 3000 },
{ tickLower: 0, tickUpper: 30000, positionBps: 2000 },
],
}
Best Practices
- Start with defaults - Use standard positions unless you have specific needs
- Reasonable market cap - Don't set unrealistically high initial valuations
- Enable sniper protection - Protect early liquidity from bots
- Consider pair token - WETH for most tokens, USDC for stables
- Test configurations - Verify on testnet before mainnet
- Respect contract limits - Max 7 LP positions, max 80% sniper fee, max 2 min decay
📖 Reference: rewards.md
Rewards Configuration
Configure trading fee distribution for your Clanker token.
Overview
Clanker tokens generate trading fees from the Uniswap V4 pool. You can configure how these fees are distributed among different recipients.
Basic Rewards Configuration
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 5000,
token: 'Both',
},
{
recipient: '0x...other...',
admin: '0x...other...',
bps: 5000,
token: 'Both',
},
],
}
Basis Points (bps)
Fees are specified in basis points where:
- 100 bps = 1%
- 1000 bps = 10%
- 5000 bps = 50%
- 10000 bps = 100%
Total rewards must equal 10000 bps (100%).
Contract Limits
From the Solidity contracts:
- Maximum Reward Recipients: 7
- Total BPS Must Equal: 10,000 (100%)
- No Zero Addresses: All admin and recipient addresses must be non-zero
- No Zero Amounts: Each recipient must have bps > 0
Token Types
Choose which tokens each recipient receives from trading fees:
| Token Type | Description |
|---|
'Clanker' | Receive only the deployed token |
'Paired' | Receive only the paired token (e.g., WETH) |
'Both' | Receive both tokens |
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 10000,
token: 'Paired',
},
]
Recommendation: Use 'Paired' for simpler fee management since WETH is more liquid and easier to convert.
Single Recipient
Receive all fees to one address:
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 10000,
token: 'Both',
},
],
}
Multiple Recipients
Split fees between creator and interface:
rewards: {
recipients: [
{
recipient: '0x...creator...',
admin: '0x...creator...',
bps: 7500,
token: 'Both',
},
{
recipient: '0x...interface...',
admin: '0x...interface...',
bps: 2500,
token: 'Both',
},
],
}
Default Bankr Interface Fee
When deploying via Bankr, use this standard configuration with 20% interface fee:
const BANKR_INTERFACE_ADDRESS = '0xF60633D02690e2A15A54AB919925F3d038Df163e';
rewards: {
recipients: [
{
recipient: account.address,
admin: account.address,
bps: 8000,
token: 'Paired',
},
{
recipient: BANKR_INTERFACE_ADDRESS,
admin: BANKR_INTERFACE_ADDRESS,
bps: 2000,
token: 'Paired',
},
],
}
This is the default and recommended configuration for all token deployments via Bankr. Both recipients receive fees in the paired token (e.g., WETH) to simplify fee management.
Check Available Rewards
Query unclaimed rewards for a recipient:
const TOKEN_ADDRESS = '0x...';
const FEE_OWNER_ADDRESS = '0x...';
const availableFees = await clanker.availableRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
console.log('Available fees:', availableFees);
Claim Rewards
Claim accumulated trading fees:
const TOKEN_ADDRESS = '0x...';
const FEE_OWNER_ADDRESS = '0x...';
const { txHash, error } = await clanker.claimRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
if (error) {
console.error('Claim failed:', error.message);
} else {
console.log('Rewards claimed:', txHash);
}
Update Reward Recipient
Change where rewards are sent (must be called by admin):
const { txHash, error } = await clanker.updateRewardRecipient({
token: TOKEN_ADDRESS,
oldRecipient: '0x...old...',
newRecipient: '0x...new...',
});
Update Reward Admin
Transfer admin rights to a new address:
const { txHash, error } = await clanker.updateRewardAdmin({
token: TOKEN_ADDRESS,
recipient: '0x...recipient...',
newAdmin: '0x...newAdmin...',
});
Read-Only Rewards Check
Check rewards without needing a wallet:
import { createPublicClient, http, type PublicClient } from 'viem';
import { base } from 'viem/chains';
import { Clanker } from 'clanker-sdk/v4';
const publicClient = createPublicClient({
chain: base,
transport: http(),
}) as PublicClient;
const clanker = new Clanker({ publicClient });
const availableFees = await clanker.availableRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
Complete Rewards Example
import { createPublicClient, createWalletClient, http, type PublicClient } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import { Clanker } from 'clanker-sdk/v4';
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({ chain: base, transport: http() }) as PublicClient;
const wallet = createWalletClient({ account, chain: base, transport: http() });
const clanker = new Clanker({ wallet, publicClient });
const TOKEN_ADDRESS = '0x1A84F1eD13C733e689AACffFb12e0999907357F0';
const FEE_OWNER_ADDRESS = '0x46e2c233a4C5CcBD6f48073F8808E0e4b3296477';
const availableFees = await clanker.availableRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
console.log('Available fees:', availableFees);
if (availableFees.token0 > 0n || availableFees.token1 > 0n) {
const { txHash, error } = await clanker.claimRewards({
token: TOKEN_ADDRESS,
rewardRecipient: FEE_OWNER_ADDRESS,
});
if (error) {
console.error('Claim failed:', error.message);
} else {
console.log('Transaction hash:', txHash);
}
}
Best Practices
- Secure admin addresses - Use multisig for high-value tokens
- Document fee split - Be transparent with community about distribution
- Regular claims - Don't let rewards accumulate excessively
- Test updates - Verify recipient/admin changes on testnet first
- Fair distribution - Consider community expectations for fee splits
📖 Reference: troubleshooting.md
Troubleshooting
Common issues and solutions when using the Clanker SDK.
Setup Issues
"Missing PRIVATE_KEY env var"
Cause: Environment variable not set or not in correct format.
Solution:
export PRIVATE_KEY=0x...your_64_character_hex_key...
PRIVATE_KEY=0x...your_64_character_hex_key...
"Invalid private key format"
Cause: Private key missing 0x prefix or incorrect length.
Solution:
const PRIVATE_KEY = process.env.PRIVATE_KEY;
if (!PRIVATE_KEY || !isHex(PRIVATE_KEY)) {
throw new Error('PRIVATE_KEY must be a hex string starting with 0x');
}
TypeScript Import Errors
Cause: Incorrect import paths or missing viem peer dependency.
Solution:
npm install clanker-sdk viem
import { Clanker } from 'clanker-sdk';
import { Clanker } from 'clanker-sdk/v4';
import { createAirdrop } from 'clanker-sdk/v4/extensions';
Deployment Issues
"Insufficient balance"
Cause: Wallet doesn't have enough native token for gas.
Solution:
const balance = await publicClient.getBalance({ address: account.address });
console.log('Balance:', formatEther(balance), 'ETH');
"Transaction reverted"
Cause: Invalid configuration or contract state issue.
Solution:
- Simulate first:
try {
await clanker.deploySimulate(config);
console.log('Simulation passed');
} catch (error) {
console.error('Would revert:', error);
}
- Check configuration values:
if (!config.name || !config.symbol) {
throw new Error('Name and symbol required');
}
if (config.vault?.percentage > 30) {
throw new Error('Vault percentage max is 30%');
}
const totalBps = config.rewards?.recipients?.reduce(
(sum, r) => sum + r.bps, 0
) || 0;
if (totalBps !== 10000) {
throw new Error('Rewards must total 10000 bps');
}
"Invalid image"
Cause: IPFS hash not accessible or invalid format.
Solution:
const image = 'ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi';
"Transaction pending too long"
Cause: Gas price too low or network congestion.
Solution:
const { address, error } = await Promise.race([
waitForTransaction(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 120000)
),
]);
const receipt = await publicClient.getTransactionReceipt({ hash: txHash });
Chain-Specific Issues
Monad: "Dynamic fees not supported"
Cause: Monad only supports static fee configurations.
Solution:
import { FEE_CONFIGS } from 'clanker-sdk/constants';
fees: FEE_CONFIGS.StaticBasic,
Wrong Chain
Cause: Wallet and publicClient configured for different chains.
Solution:
const CHAIN = base;
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(),
});
const wallet = createWalletClient({
account,
chain: CHAIN,
transport: http(),
});
const { txHash } = await clanker.deploy({
chainId: CHAIN.id,
});
Post-Deployment Issues
"No vault tokens to claim"
Cause: Lockup period hasn't passed yet.
Solution:
const claimable = await clanker.getVaultClaimableAmount({ token: TOKEN_ADDRESS });
console.log('Claimable:', claimable.toString());
"Cannot claim rewards"
Cause: Not the reward recipient or no rewards accumulated.
Solution:
const available = await clanker.availableRewards({
token: TOKEN_ADDRESS,
rewardRecipient: YOUR_ADDRESS,
});
console.log('Available:', available);
"Cannot update metadata"
Cause: Not the token admin.
Solution:
console.log('Your address:', account.address);
Airdrop Issues
"Airdrop not registered"
Cause: Didn't wait for indexing before registering.
Solution:
await sleep(10_000);
await registerAirdrop(tokenAddress, tree);
"Invalid proof"
Cause: Merkle tree mismatch or wrong address.
Solution:
const { tree, airdrop } = createAirdrop(originalRecipients);
const proof = getAllowlistMerkleProof(tree, entries, address.toLowerCase(), amount);
RPC Issues
"Rate limited"
Cause: Public RPC rate limits exceeded.
Solution:
const publicClient = createPublicClient({
chain: base,
transport: http(process.env.RPC_URL_BASE),
});
"Request failed"
Cause: Network issues or RPC unavailable.
Solution:
async function deployWithRetry(config, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await clanker.deploy(config);
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(1000 * (i + 1));
}
}
}
Contract Error Reference
Common revert reasons from the Solidity contracts:
| Error | Cause | Solution |
|---|
VaultLockupDurationTooShort | Lockup < 7 days | Set lockupDuration ≥ 604800 |
AirdropLockupDurationTooShort | Lockup < 1 day | Set lockupDuration ≥ 86400 |
MaxExtensionBpsExceeded | Extensions > 90% | Reduce total extension percentage |
MaxExtensionsExceeded | More than 10 extensions | Use fewer extensions |
TooManyRewardParticipants | More than 7 recipients | Reduce reward recipients |
TooManyPositions | More than 7 LP positions | Use fewer positions |
InvalidRewardBps | Rewards ≠ 10000 bps | Ensure rewards total exactly 10000 |
StartingFeeGreaterThanMaxLpFee | Sniper fee > 80% | Set startingFee ≤ 800000 |
TimeDecayLongerThanMaxMevDelay | Decay > 2 min | Set secondsToDecay ≤ 120 |
StartingFeeMustBeGreaterThanEndingFee | startingFee ≤ endingFee | Increase starting or decrease ending fee |
Debug Checklist
- ✅ Private key set and valid (starts with 0x)
- ✅ Wallet funded with native token
- ✅ Chain configuration consistent
- ✅ IPFS image accessible
- ✅ Required fields provided (name, symbol, image, tokenAdmin)
- ✅ Total extension BPS ≤ 9000 (90%)
- ✅ Vault lockup ≥ 7 days (604800 seconds)
- ✅ Airdrop lockup ≥ 1 day (86400 seconds)
- ✅ Rewards total = 10000 bps exactly
- ✅ Reward recipients ≤ 7
- ✅ LP positions ≤ 7
- ✅ Sniper startingFee ≤ 800000 (80%)
- ✅ Sniper secondsToDecay ≤ 120 (2 minutes)
- ✅ Static fees for Monad chain
- ✅ Waited for indexing before airdrop registration
- ✅ Using correct recipient address for claims
Getting Help
- Check SDK examples: github.com/clanker-devco/clanker-sdk/tree/main/examples
- Review error messages: Usually contain specific guidance
- Verify on block explorer: Check transaction status and logs
- Test on testnet: Validate configuration before mainnet
📖 Reference: vesting.md
Token Vesting (Vault)
Configure token vesting with lockup periods and linear vesting using the Clanker vault system.
Overview
The vault system allows you to lock a percentage of the token supply with:
- Lockup Duration: Cliff period before any tokens can be claimed
- Vesting Duration: Linear vesting period after lockup
- Recipient: Address that receives vested tokens
Basic Vault Configuration
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://...',
tokenAdmin: account.address,
vault: {
percentage: 10,
lockupDuration: 2592000,
vestingDuration: 2592000,
recipient: account.address,
},
});
Duration Values
Common duration values in seconds:
| Duration | Seconds |
|---|
| 1 hour | 3600 |
| 1 day | 86400 |
| 7 days | 604800 |
| 14 days | 1209600 |
| 30 days | 2592000 |
| 60 days | 5184000 |
| 90 days | 7776000 |
| 180 days | 15552000 |
| 1 year | 31536000 |
Contract Limits
From the Solidity contracts:
- Minimum Lockup Duration: 7 days (enforced on-chain)
- Maximum Extension BPS: 9000 (90% of supply can go to extensions total)
- Minimum to LP: 10% of supply must go to liquidity pool
vault: {
percentage: 10,
lockupDuration: 604800,
vestingDuration: 2592000,
}
Note: The vault is one of several possible extensions. The total of all extension percentages (vault + airdrop + etc.) cannot exceed 90%.
Check Claimable Amount
After deployment, check how many tokens are available to claim:
const TOKEN_ADDRESS = '0x...';
const claimable = await clanker.getVaultClaimableAmount({
token: TOKEN_ADDRESS,
});
console.log('Claimable amount:', claimable.toString());
Claim Vaulted Tokens
Claim tokens once the lockup period has passed:
const TOKEN_ADDRESS = '0x...';
const claimable = await clanker.getVaultClaimableAmount({
token: TOKEN_ADDRESS,
});
if (claimable > 0n) {
const { txHash, error } = await clanker.claimVaultedTokens({
token: TOKEN_ADDRESS,
});
if (error) {
console.error('Claim failed:', error.message);
} else {
console.log('Claim transaction:', txHash);
}
} else {
console.log('No tokens available to claim yet');
}
Get Vault Claim Transaction
Get the transaction object for claiming (useful for batching or custom signing):
const txObject = await clanker.getVaultClaimTransaction({
token: TOKEN_ADDRESS,
});
console.log('Transaction object:', txObject);
Vesting Timeline Example
For a 10% vault with 30-day lockup and 30-day vesting:
Day 0: Token deployed, 10% locked in vault
↓
Day 1-30: Lockup period (nothing claimable)
↓
Day 31: Vesting begins
~3.33% claimable (1/30 of vaulted amount)
↓
Day 45: ~50% of vaulted tokens claimable
↓
Day 60: 100% claimable
Custom Recipient
Set a different address to receive vested tokens:
vault: {
percentage: 10,
lockupDuration: 2592000,
vestingDuration: 2592000,
recipient: '0x...treasury_address...',
}
If not specified, recipient defaults to tokenAdmin.
Team Vesting Example
Configure vesting for a team allocation:
const THIRTY_DAYS = 2592000;
const SIX_MONTHS = 15552000;
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'Team Token',
symbol: 'TEAM',
image: 'ipfs://...',
tokenAdmin: account.address,
vault: {
percentage: 20,
lockupDuration: SIX_MONTHS,
vestingDuration: SIX_MONTHS * 2,
recipient: '0x...team_multisig...',
},
metadata: {
description: 'Token with team vesting schedule',
},
context: {
interface: 'Clanker SDK',
},
});
No Vesting
To deploy without any vesting, simply omit the vault configuration:
const { txHash, waitForTransaction, error } = await clanker.deploy({
name: 'My Token',
symbol: 'TKN',
image: 'ipfs://...',
tokenAdmin: account.address,
});
Best Practices
- Reasonable lockup periods - Align with project milestones
- Gradual vesting - Avoid cliff-only (0 vesting duration)
- Transparent communication - Share vesting schedule with community
- Secure recipient - Use multisig for team vesting
- Test first - Verify vesting math before mainnet