Integrate
Everything the site does is a contract call anyone can make. Rules are on rules, risks on risk.
addresses
One set per chain. Coins and their curves are found through the factory, or the read API below. The same list is in apps/web/src/lib/deployments.json; the ABI files are in apps/web/src/abi.
on Base Sepolia
- LaunchpadFactory
- 0xf5c867b4cb9bf7210f9ebb3b18c6ab045ee47cf0 (opens the explorer)
- VRF coordinator (Chainlink)
- 0x5C210eF41CD1a72de73bF76eC39637bB0d3d7BEE (opens the explorer)
- PotHouse (library)
- 0xe7fa3864f2800f6e627c3d18d6de9b1f6089d32f (opens the explorer)
- PotShares (library)
- 0xd48b17def70a95c4b0c39268a6cc91896e3ee5e8 (opens the explorer)
- PotRules (library)
- 0x5a8675fea2123cbbd8cddee815eef54659fd74cc (opens the explorer)
- PotPay (library)
- 0x9712e6ad81591bc637f7680aec87d95503360f9b (opens the explorer)
calls
- quoteBuy(ethIn) → (tokensOut, fee, spent)
- What a buy of ethIn pays and returns now. A quote is the price at this block, not a promise: send a minimum.
- buy(minTokensOut, recipient) payable
- Buys with the ETH sent. The coins go to recipient and the transaction reverts if fewer than minTokensOut would. recipient is also the spinner: on a coin that spins, the spin belongs to that wallet, never to msg.sender, so a router names the user.
- buyCapped(minTokensOut, maxEthIn, recipient) payable
- The same buy, spending at most maxEthIn and refunding the rest: the form for a buy that may cross the curve's end.
- quoteSell(tokensIn) → (ethOut, fee)
- What selling tokensIn returns now, fee taken.
- sell(tokensIn, minEthOut, recipient)
- Sells coins the caller approved to the curve and sends the ETH to recipient. A sell never spins and never opens a ticket.
- launches(token) → (token, curve, creator, createdAt)
- On the factory: the curve of a coin. Everything a coin does is on its curve.
A spin resolves a few seconds after the buy lands, when Chainlink VRF answers; read the result from the buy's events or the read API. A coin whose pot is not Every buy does not spin: its draws pay the holders at its draw hour, and a bag under the ticket floor holds no ticket.
read API
The index serves its data at https://luckypad.fun/api/v1: /view/board, /view/coin, /view/pot, /wallet/{address}/bags,/head and the block it has read; the routes are listed in apps/indexer/API.md. The index is a cache of the chain, not a source of truth: a figure that moves money is read from the contract. Pass your own RPC to the example; the API serves reads only.
TypeScript
import { createPublicClient, createWalletClient, erc20Abi, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import { BondingCurveAbi, LaunchpadFactoryAbi } from "./abi"; // the app's own ABI files, apps/web/src/abi
// the two addresses to fill in: the Factory from the list above, and the coin's token address
const FACTORY = "0x…" as const;
const TOKEN = "0x…" as const;
const account = privateKeyToAccount(process.env.KEY as `0x${string}`);
const pub = createPublicClient({ chain: base, transport: http(process.env.RPC) });
const wallet = createWalletClient({ account, chain: base, transport: http(process.env.RPC) });
// 1. the coin's curve: the factory knows it by the token's address
const { curve } = await pub.readContract({ address: FACTORY, abi: LaunchpadFactoryAbi, functionName: "launches", args: [TOKEN] });
// 2. a buy: quote it, keep a minimum, name who gets the coins (and the spin)
const ethIn = parseEther("0.05");
const [tokensOut] = await pub.readContract({ address: curve, abi: BondingCurveAbi, functionName: "quoteBuy", args: [ethIn] });
await wallet.writeContract({
address: curve, abi: BondingCurveAbi, functionName: "buy",
args: [(tokensOut * 99n) / 100n, account.address], // minTokensOut (1 % slippage), recipient
value: ethIn,
});
// 3. a sell: quote it, keep a minimum, approve exactly what you sell, then sell; a sell never spins
const amount = parseEther("1000"); // coins to sell, in wei of the token
const [ethOut] = await pub.readContract({ address: curve, abi: BondingCurveAbi, functionName: "quoteSell", args: [amount] });
await wallet.writeContract({ address: TOKEN, abi: erc20Abi, functionName: "approve", args: [curve, amount] });
await wallet.writeContract({
address: curve, abi: BondingCurveAbi, functionName: "sell",
args: [amount, (ethOut * 99n) / 100n, account.address], // tokensIn, minEthOut (1 % slippage), recipient
});Approve exactly what you sell, never an unlimited amount. Every trade can be reached by anyone, a bot included; none can be reversed.