Skip to main content

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

TransferAmountRecipient GetsAgent (70%)Protocol (30%)
1,000 tokens1,00099073
10,000 tokens10,0009,9007030
1,000,000 tokens1,000,000990,0007,0003,000

2% Fee Token (Maximum)

TransferAmountRecipient GetsAgent (70%)Protocol (30%)
1,000 tokens1,000980146
10,000 tokens10,0009,80014060

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

ErrorCauseFix
InvalidFeeRateFee not within boundsUse 50–200 bps (0.5%–2%)
ZeroAddressAgent wallet is zeroProvide valid wallet address
InvalidFeeRate (on lock)Lock period too shortUse ≥ 30 days
TokenAlreadyLaunchedLiquidity already addedCan only add once
LiquidityStillLockedLock period not expiredWait for lock to expire
UnauthorizedNot agent wallet or ownerOnly authorized addresses can withdraw