Deploy a coin
How createToken works, beneficiary rules, and a full viem example.
What happens in one transaction
createToken does all of this atomically:
- Checks creates are not paused and
msg.value >= creationFee - Validates metadata lengths
- CREATE2-deploys
MintFastToken(1B supply minted to the launchpad) - Snapshots the current default Fletcher / LP fee splits onto the token
- Registers beneficiaries (or defaults 100% to the creator wallet)
- Accrues the creation fee into
protocolFeesAccrued - Initializes the Uniswap v4 pool and locks the full supply
- If you sent ETH above the creation fee, runs an atomic first buy to
msg.sender
Function signature
function createToken(
string calldata name,
string calldata symbol,
string calldata metadataURI,
BeneficiaryInput[] calldata beneficiaries,
uint256 initialBuyMinTokensOut,
bytes32 salt
) external payable returns (address token);Parameters
| Param | Rules |
|---|---|
name | 1 to 32 bytes |
symbol | 1 to 10 bytes |
metadataURI | up to 256 bytes (usually an ipfs:// or https://ipfs.io/ipfs/... URL) |
beneficiaries | 0 to 8 rows, bps sum to 10_000 if non-empty |
initialBuyMinTokensOut | slippage floor for the optional first buy (use 0 if no buy) |
salt | your entropy mixed into CREATE2, so retries and grief resistance stay practical |
msg.value | at least creationFee, plus any ETH you want to spend on the first buy |
BeneficiaryInput
struct BeneficiaryInput {
uint8 kind; // 1 = X, 2 = GitHub, 3 = Wallet
string handle; // normalized handle for X/GitHub, empty for Wallet
address wallet; // payout address for Wallet, address(0) for social
uint16 bps; // share of the Fletcher cut, all rows sum to 10000
}Handles must already be lowercase / canonical. See HandleLib.
Empty vs named beneficiaries
- Empty array: creator gets 100% via a wallet key, list stays unlocked, creator may call
setBeneficiariesonce later. - Named list: locked in the same create tx. Typos need a CTO / fee-manager path.
Example: mint with an X Fletcher and a first buy
import {
createWalletClient,
createPublicClient,
http,
parseEther,
encodeFunctionData,
type Hex,
} from "viem"
import { privateKeyToAccount } from "viem/accounts"
const LAUNCHPAD = "0xYourLaunchpad" as const
const creationFee = parseEther("0.0005")
const firstBuy = parseEther("0.05")
const createTokenAbi = [
{
type: "function",
name: "createToken",
stateMutability: "payable",
inputs: [
{ name: "name", type: "string" },
{ name: "symbol", type: "string" },
{ name: "metadataURI", type: "string" },
{
name: "beneficiaries",
type: "tuple[]",
components: [
{ name: "kind", type: "uint8" },
{ name: "handle", type: "string" },
{ name: "wallet", type: "address" },
{ name: "bps", type: "uint16" },
],
},
{ name: "initialBuyMinTokensOut", type: "uint256" },
{ name: "salt", type: "bytes32" },
],
outputs: [{ name: "token", type: "address" }],
},
] as const
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex)
const client = createWalletClient({
account,
transport: http(process.env.RPC_URL),
})
const publicClient = createPublicClient({ transport: http(process.env.RPC_URL) })
const salt = ("0x" +
Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex")) as Hex
const hash = await client.writeContract({
address: LAUNCHPAD,
abi: createTokenAbi,
functionName: "createToken",
args: [
"Cool Coin",
"COOL",
"ipfs://bafybeigexamplemetadata",
[
{
kind: 1, // X
handle: "alice",
wallet: "0x0000000000000000000000000000000000000000",
bps: 10000,
},
],
0n, // tighten with a quoter-style estimate in production
salt,
],
value: creationFee + firstBuy,
})
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log("mined", receipt.transactionHash)Rough first-buy size estimate
Before the pool exists you cannot call quoteBuy. Near launch geometry, this constant-product style estimate tracks well (2% fee netted first, ~1.5 ETH virtual reserve, 1B supply):
const TOTAL_SUPPLY = 1_000_000_000n * 10n ** 18n
const LAUNCH_MCAP_WEI = 1_500_000_000_000_000_000n // 1.5 ETH
const FEE_BPS = 200n
function estimateInitialBuyTokens(ethIn: bigint): bigint {
if (ethIn <= 0n) return 0n
const ethNet = ethIn - (ethIn * FEE_BPS) / 10_000n
if (ethNet <= 0n) return 0n
return (TOTAL_SUPPLY * ethNet) / (LAUNCH_MCAP_WEI + ethNet)
}Use that (minus a slippage buffer) as initialBuyMinTokensOut when you send a first buy.
Common reverts
| Error | Usual cause |
|---|---|
CreationPaused | owner paused new creates |
InsufficientCreationFee | msg.value below creationFee |
BadMetadata | name / symbol / URI length |
InvalidHandle | handle not canonical for the platform |
BpsSumMismatch | beneficiary bps do not sum to 10_000 |
TooManyBeneficiaries | more than 8 |
Slippage | first buy got fewer tokens than initialBuyMinTokensOut |
After create
- Read
state(token)for creator, splits, pool id, and spot price wad - Trade with buy / sell
- Watch migration progress toward 18.67 ETH mcap