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.

predict-erc20.ts
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.

predict-b20.ts
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.

On this page