Predict the token address
Know the token address before the launch, or search for a vanity address.
A token address depends on the caller of launchDrop and the salt. The launchpad hashes the salt with its caller, the
creator. The token deployer then hashes the result with its own caller, the launchpad:
launchpadSalt = keccak256(abi.encode(creator, salt))
deployerSalt = keccak256(abi.encode(launchpad, launchpadSalt))So nobody else can launch at your address, even with the same salt.
Simulate the launch
The simplest way is to simulate launchDrop. The result is the token address.
const { result: token } = await publicClient.simulateContract({
account,
address: LAUNCHPAD,
abi: launchpadAbi,
functionName: "launchDrop",
args: [params],
value: params.devBuy.amountIn,
});The simulation needs valid parameters. If it has a dev buy, the account also needs the funds.
Calculate an ERC-20 address
DropERC20Deployer deploys DropToken with CREATE2. The init code includes the name, symbol, and contract URI. Its
supply recipient is the launchpad.
Download DropToken.json. It has the ABI and the creation bytecode of DropToken.
import {
encodeAbiParameters,
encodeDeployData,
getContractAddress,
keccak256,
parseAbiParameters,
} from "viem";
import { ERC20_DEPLOYER, LAUNCHPAD } from "./addresses";
import DropToken from "./DropToken.json";
const TOTAL_SUPPLY = 1_000_000_000n * 10n ** 18n;
/**
* The salt that the token deployer receives from the launchpad.
*/
export function deployerSalt({
creator,
salt,
}: {
creator: `0x${string}`;
salt: `0x${string}`;
}) {
const encode = (caller: `0x${string}`, value: `0x${string}`) =>
keccak256(
encodeAbiParameters(parseAbiParameters("address, bytes32"), [
caller,
value,
]),
);
return encode(LAUNCHPAD, encode(creator, salt));
}
export function predictErc20Address(params: {
creator: `0x${string}`;
salt: `0x${string}`;
name: string;
symbol: string;
contractURI: string;
}) {
const initCode = encodeDeployData({
abi: DropToken.abi,
bytecode: DropToken.bytecode as `0x${string}`,
args: [
params.name,
params.symbol,
params.contractURI,
LAUNCHPAD,
TOTAL_SUPPLY,
],
});
return getContractAddress({
opcode: "CREATE2",
from: ERC20_DEPLOYER,
salt: deployerSalt(params),
bytecodeHash: keccak256(initCode),
});
}Calculate a B-20 address
On Base, the B-20 factory precompile calculates B-20 addresses. The address does not depend on the name, symbol, or contract URI.
import { parseAbi } from "viem";
import { B20_DEPLOYER } from "./addresses";
import { publicClient } from "./client";
import { deployerSalt } from "./predict-erc20";
const B20_FACTORY = "0xB20f000000000000000000000000000000000000";
const b20FactoryAbi = parseAbi([
"function getB20Address(uint8 variant, address sender, bytes32 salt) view returns (address)",
]);
export function predictB20Address(params: {
creator: `0x${string}`;
salt: `0x${string}`;
}) {
return publicClient.readContract({
address: B20_FACTORY,
abi: b20FactoryAbi,
functionName: "getB20Address",
args: [0, B20_DEPLOYER, deployerSalt(params)], // 0 is the ASSET variant
});
}Vanity addresses
To get an address with a pattern, try many salts with predictErc20Address until one matches. Then launch with that
salt, from the same account, with the same name, symbol, and contract URI.
Reused salts
A launch reverts if its address is already in use:
- For an ERC-20, the same creator, salt, name, symbol, and contract URI give the same address.
- For a B-20, the same creator and salt give the same address.