Launch a token
Call launchDrop with viem, with two pools and a dev buy.
This guide launches an ERC-20 on Base with two pools:
- The main pool has 80% of the supply against native ETH, at a start market cap of 10 ETH.
- The second pool has 20% against USDC, at a start market cap of about $33,000.
It also buys tokens from the main pool with 0.1 ETH, in the same transaction.
Set up the client
Copy abi.ts and addresses.ts into your project. Then create the clients:
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
export const account = privateKeyToAccount(
process.env.PRIVATE_KEY as `0x${string}`,
);
export const publicClient = createPublicClient({
chain: base,
transport: http(),
});
export const walletClient = createWalletClient({
account,
chain: base,
transport: http(),
});Calculate the start ticks
initialTick is the start price, as a tick of raw quote per raw token. This function converts a start market cap to a
tick. See Set the start price.
const TICK_SPACING = 200;
const TOTAL_SUPPLY = 1_000_000_000;
/**
* Start tick for a starting market cap, in whole units of the quote.
*/
export function startTick({
marketCap,
quoteDecimals,
}: {
marketCap: number; // In whole quote units
quoteDecimals: number;
}) {
// Whole quote per whole token, then raw quote per raw token
const price = marketCap / TOTAL_SUPPLY;
const rawPrice = price * 10 ** (quoteDecimals - 18);
const tick = Math.log(rawPrice) / Math.log(1.0001);
return Math.round(tick / TICK_SPACING) * TICK_SPACING;
}Build the launch parameters
import { keccak256, parseEther, toHex, zeroAddress } from "viem";
import { launchpadAbi } from "./abi";
import { ERC20_DEPLOYER, LAUNCHPAD, USDC } from "./addresses";
import { account, publicClient, walletClient } from "./client";
import { startTick } from "./start-tick";
// The partner of the launch. See "Register a partner".
const PARTNER_ID = Number(process.env.PARTNER_ID);
// 10 ETH and $33,000 start market caps. Give all pools the same start price.
const ethTick = startTick({ marketCap: 10, quoteDecimals: 18 }); // -184200
const usdcTick = startTick({ marketCap: 33_000, quoteDecimals: 6 }); // -379600
const params = {
name: "Example",
symbol: "EXMPL",
contractURI: "ipfs://bafkrei...",
salt: keccak256(toHex("example-1")),
partnerId: PARTNER_ID,
tokenDeployer: ERC20_DEPLOYER,
creatorFeeRecipient: account.address,
pools: [
// Main pool: 80% against native ETH
{ quote: zeroAddress, supplyBps: 80_00, initialTick: ethTick },
// 20% against USDC
{ quote: USDC, supplyBps: 20_00, initialTick: usdcTick },
],
// Buys from the main pool with 0.1 ETH.
devBuy: { amountIn: parseEther("0.1"), recipient: account.address },
} as const;saltis any 32 bytes. The launchpad combines it with your address, so nobody else can take your token address.partnerIdsets the LP fee and the partner's share of fees. Check the terms withpartners(partnerId)first. See Partners and Register a partner.- The pools must follow the pool rules. Put the main pool first.
- Use a
creatorFeeRecipientof0x…dEaDfor burn mode.
Send the launch
The main pool's quote is native ETH, so send the dev buy amount as value.
const { result: token, request } = await publicClient.simulateContract({
account,
address: LAUNCHPAD,
abi: launchpadAbi,
functionName: "launchDrop",
args: [params],
value: params.devBuy.amountIn,
});
const hash = await walletClient.writeContract(request);
await publicClient.waitForTransactionReceipt({ hash });
console.log(`Launched ${token}`);The simulation returns the token address and shows a revert before you send. See Errors for the revert reasons.
Use an ERC-20 for the dev buy
If the main pool's quote is an ERC-20, approve the launchpad for the dev buy amount before the launch. Then send no value.
import { erc20Abi } from "viem";
const amountIn = 1_000n * 10n ** 6n; // 1,000 USDC
await walletClient.writeContract({
address: USDC,
abi: erc20Abi,
functionName: "approve",
args: [LAUNCHPAD, amountIn],
});Launch a B-20 on Base
Set tokenDeployer to B20_DEPLOYER. Everything else is the same. A B-20 address does not depend on the name, so
use a new salt for each B-20 launch from the same account.
What the launch emits
The launchpad emits these events, in order:
DropCreatedwith the creator, token, partner, token deployer, and creator fee recipient.DropPoolCreatedfor each pool, with its pool ID, share, and start price.DropDevBuy, if the launch has a dev buy.
DropHook also emits DropSwap for the dev buy. See Events.