Using the Token Launcher
This guide walks through deploying a custom ERC-20 token with built-in trading fees through the ClawTokenLauncher. Launched tokens automatically generate revenue for agent wallets from trading activity.
Overview
When you launch a token, you get:
- A standard ERC-20 token with configurable trading fees (0.5%–2%)
- 70% of fees automatically sent to your agent wallet
- 30% of fees sent to the protocol treasury
- Locked liquidity (minimum 30 days) for anti-rug protection
Step 1: Prepare Launch Parameters
prepare-launch.ts
import { ethers } from 'ethers';
const LAUNCHER = '0x89B974b6977157f1E7D743D70e28fCA1B5Ca84b5';
const provider = new ethers.JsonRpcProvider('https://api.mainnet.abs.xyz');
const wallet = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
const launcher = new ethers.Contract(LAUNCHER, [
'function minFeeRate() external view returns (uint256)',
'function maxFeeRate() external view returns (uint256)',
'function minLiquidityLockDays() external view returns (uint256)',
'function defaultAgentFeeSplit() external view returns (uint256)',
], provider);
// Check current constraints
const minFee = await launcher.minFeeRate();
const maxFee = await launcher.maxFeeRate();
const minLock = await launcher.minLiquidityLockDays();
const feeSplit = await launcher.defaultAgentFeeSplit();
console.log('Fee range:', Number(minFee)/100, '% -', Number(maxFee)/100, '%');
console.log('Min lock:', minLock.toString(), 'days');
console.log('Agent fee split:', Number(feeSplit)/100, '%');
Step 2: Launch the Token
launch-token.ts
const launcher = new ethers.Contract(LAUNCHER, [
'function launchToken(string name, string symbol, uint256 totalSupply, address agentWallet, uint256 feeRateBps, uint256 liquidityLockDays) external returns (address)',
'event TokenLaunched(address indexed token, address indexed agentWallet, string name, string symbol, uint256 totalSupply, uint256 feeRateBps)',
], wallet);
// Launch parameters
const name = 'Agent Alpha Token';
const symbol = 'AAT';
const totalSupply = ethers.parseEther('1000000000'); // 1 billion
const agentWallet = '0xYOUR_AGENT_WALLET_ADDRESS';
const feeRate = 100; // 1% (100 basis points)
const lockDays = 90; // 90-day liquidity lock
console.log('Launching token...');
const tx = await launcher.launchToken(
name,
symbol,
totalSupply,
agentWallet,
feeRate,
lockDays,
);
const receipt = await tx.wait();
// Extract token address from event
const iface = launcher.interface;
let tokenAddress: string | undefined;
for (const log of receipt.logs) {
try {
const parsed = iface.parseLog({ topics: log.topics, data: log.data });
if (parsed?.name === 'TokenLaunched') {
tokenAddress = parsed.args.token;
console.log('✅ Token launched!');
console.log(' Address:', tokenAddress);
console.log(' Name:', parsed.args.name);
console.log(' Symbol:', parsed.args.symbol);
console.log(' Supply:', ethers.formatEther(parsed.args.totalSupply));
console.log(' Fee:', Number(parsed.args.feeRateBps) / 100, '%');
}
} catch {}
}
Step 3: Add Liquidity
After launching, add initial liquidity (ETH + tokens):
add-liquidity.ts
const tokenAddress = '0xYOUR_NEW_TOKEN_ADDRESS';
const tokenAmount = ethers.parseEther('500000000'); // 500M tokens for liquidity
const ethAmount = ethers.parseEther('5.0'); // 5 ETH
// Approve token transfer to launcher
const token = new ethers.Contract(tokenAddress, [
'function approve(address spender, uint256 amount) external returns (bool)',
'function balanceOf(address account) external view returns (uint256)',
], wallet);
// The tokens are initially held by the launcher, so you need to get them first
// if you're not the launcher. Check the launcher balance:
const balance = await token.balanceOf(LAUNCHER);
console.log('Launcher token balance:', ethers.formatEther(balance));
// As the launcher caller, approve the tokens
await token.approve(LAUNCHER, tokenAmount);
// Add liquidity
const launcher = new ethers.Contract(LAUNCHER, [
'function addLiquidity(address token, uint256 tokenAmount) external payable',
], wallet);
const tx = await launcher.addLiquidity(tokenAddress, tokenAmount, {
value: ethAmount,
});
await tx.wait();
console.log('✅ Liquidity added:', ethers.formatEther(ethAmount), 'ETH +', ethers.formatEther(tokenAmount), 'tokens');
Step 4: Verify the Launch
verify-launch.ts
const launcher = new ethers.Contract(LAUNCHER, [
'function getLaunchConfig(address token) external view returns (tuple(address token, address agentWallet, uint256 totalSupply, uint256 feeRateBps, uint256 agentFeeSplitBps, uint256 liquidityLockEnd, uint256 launchedAt, bool liquidityAdded))',
'function isLiquidityLocked(address token) external view returns (bool)',
], provider);
const config = await launcher.getLaunchConfig(tokenAddress);
console.log('=== Launch Verification ===');
console.log('Token:', config.token);
console.log('Agent wallet:', config.agentWallet);
console.log('Total supply:', ethers.formatEther(config.totalSupply));
console.log('Fee rate:', Number(config.feeRateBps) / 100, '%');
console.log('Agent fee split:', Number(config.agentFeeSplitBps) / 100, '%');
console.log('Liquidity lock end:', new Date(Number(config.liquidityLockEnd) * 1000).toISOString());
console.log('Launched at:', new Date(Number(config.launchedAt) * 1000).toISOString());
console.log('Liquidity added:', config.liquidityAdded);
const locked = await launcher.isLiquidityLocked(tokenAddress);
console.log('Liquidity locked:', locked);
Step 5: Monitor Trading Fees
As tokens are traded, fees accumulate in the agent wallet and protocol treasury:
monitor-fees.ts
const token = new ethers.Contract(tokenAddress, [
'function balanceOf(address) external view returns (uint256)',
'function feeRateBps() external view returns (uint256)',
'function agentFeeSplitBps() external view returns (uint256)',
'function agentWallet() external view returns (address)',
'function protocolTreasury() external view returns (address)',
], provider);
const agentAddr = await token.agentWallet();
const treasuryAddr = await token.protocolTreasury();
const agentBalance = await token.balanceOf(agentAddr);
const treasuryBalance = await token.balanceOf(treasuryAddr);
console.log('Agent fee balance:', ethers.formatEther(agentBalance), symbol);
console.log('Protocol fee balance:', ethers.formatEther(treasuryBalance), symbol);
Step 6: Withdraw Liquidity (After Lock)
withdraw-liquidity.ts
const launcher = new ethers.Contract(LAUNCHER, [
'function withdrawLiquidity(address token) external',
'function isLiquidityLocked(address token) external view returns (bool)',
], wallet);
const locked = await launcher.isLiquidityLocked(tokenAddress);
if (locked) {
const config = await launcher.getLaunchConfig(tokenAddress);
const remaining = Number(config.liquidityLockEnd) - Math.floor(Date.now() / 1000);
console.log(`⏳ Liquidity still locked for ${Math.ceil(remaining / 86400)} days`);
} else {
await launcher.withdrawLiquidity(tokenAddress);
console.log('✅ Liquidity withdrawn');
}
Fee Examples
1% Fee Token
| Transfer | Amount | Recipient Gets | Agent (70%) | Protocol (30%) |
|---|---|---|---|---|
| 1,000 tokens | 1,000 | 990 | 7 | 3 |
| 10,000 tokens | 10,000 | 9,900 | 70 | 30 |
| 1,000,000 tokens | 1,000,000 | 990,000 | 7,000 | 3,000 |
2% Fee Token (Maximum)
| Transfer | Amount | Recipient Gets | Agent (70%) | Protocol (30%) |
|---|---|---|---|---|
| 1,000 tokens | 1,000 | 980 | 14 | 6 |
| 10,000 tokens | 10,000 | 9,800 | 140 | 60 |
Querying All Launches
const launcher = new ethers.Contract(LAUNCHER, [
'function getTokenCount() external view returns (uint256)',
'function allTokens(uint256 index) external view returns (address)',
'function getAgentTokens(address agentWallet) external view returns (address[])',
], provider);
// Total tokens launched
const total = await launcher.getTokenCount();
console.log('Total tokens launched:', total.toString());
// Get all tokens by an agent
const agentTokens = await launcher.getAgentTokens(agentWalletAddress);
console.log('Agent tokens:', agentTokens);
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
InvalidFeeRate | Fee not within bounds | Use 50–200 bps (0.5%–2%) |
ZeroAddress | Agent wallet is zero | Provide valid wallet address |
InvalidFeeRate (on lock) | Lock period too short | Use ≥ 30 days |
TokenAlreadyLaunched | Liquidity already added | Can only add once |
LiquidityStillLocked | Lock period not expired | Wait for lock to expire |
Unauthorized | Not agent wallet or owner | Only authorized addresses can withdraw |