For AI agents & builders
PoolPot's contracts are immutable and permissionless: an agent with a funded wallet on Base can buy tickets, collect the pool's prizes and claim its share directly on-chain — no API key, no account. The machine-readable version of this page is /llms.txt.
Contracts on Base (chainId 8453)
- Pool factory 0x039894BA3a66DAD20c58939EfD021663244B0A59
- Round pool implementation 0x9fde99dB9f48031aA9fC4Cd0D7301cD04e939404
- Auto-buy wallet implementation 0x7acbf03868C3d8F80426C18cDE2f22eE7A46e6a1
- Megapot Jackpot 0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2
- Megapot ticket NFT 0x48FfE35AbB9f4780a4f1775C2Ce1c46185b366e4
- USDC (6 decimals) 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Limits, verified in the contracts: 11 tickets per join transaction, 150 ids per collect (the site uses 100), 60 drawings per claimMany, 25 plans per fundMany, auto-buy tip at most $0.50/day, plans up to 365 days.
1 · Read the current drawing
import { createPublicClient, http, parseAbi } from "viem";
import { base } from "viem/chains";
const client = createPublicClient({ chain: base, transport: http() });
const JACKPOT = "0x3bAe643002069dBCbcd62B1A4eb4C4A397d042a2";
const FACTORY = "0x039894BA3a66DAD20c58939EfD021663244B0A59";
const jackpotAbi = parseAbi([
"function currentDrawingId() view returns (uint256)",
"struct DrawingState { uint256 prizePool; uint256 ticketPrice; uint256 edgePerTicket; uint256 referralWinShare; uint256 referralFee; uint256 globalTicketsBought; uint256 lpEarnings; uint256 drawingTime; uint256 winningTicket; uint8 ballMax; uint8 bonusballMax; address payoutCalculator; bool jackpotLock; }",
"function getDrawingState(uint256 _drawingId) view returns (DrawingState)",
]);
const factoryAbi = parseAbi(["function roundOf(uint256 d) view returns (address)"]);
const drawingId = await client.readContract({
address: JACKPOT, abi: jackpotAbi, functionName: "currentDrawingId",
});
const s = await client.readContract({
address: JACKPOT, abi: jackpotAbi, functionName: "getDrawingState", args: [drawingId],
});
// s.ticketPrice is USDC base units (6 decimals; 1_000_000 = $1)
// s.drawingTime is unix seconds (daily 17:00 UTC); s.jackpotLock true = closed
// s.ballMax / s.bonusballMax bound your picks
const round = await client.readContract({
address: FACTORY, abi: factoryAbi, functionName: "roundOf", args: [drawingId],
}); // address(0) until the first buyer of the day; join() opens it lazily2 · Buy tickets: approve, then join
Cost is exact: n × ticketPrice, where n = your picks + quick picks (1–11 per call). A Pick is five strictly ascending normals within ballMax plus a bonusball within bonusballMax. The contract accepts joins until the drawing locks (jackpotLock, around the 17:00 UTC draw); the site stops 60 seconds before drawingTime so a transaction cannot be mined too late — do the same.
import { parseAbi } from "viem";
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const erc20Abi = parseAbi(["function approve(address spender, uint256 value) returns (bool)"]);
const factoryAbi = parseAbi([
"struct Pick { uint8[5] normals; uint8 bonusball; }",
"function join(uint256 d, Pick[] picks, uint16 quickPicks, uint256 maxCost) returns (uint256[])",
]);
// 1 quick pick: n = 0 own picks + 1 quick pick, cost = 1 * ticketPrice
const cost = s.ticketPrice; // exact, in USDC base units
await walletClient.writeContract({
address: USDC, abi: erc20Abi, functionName: "approve",
args: [FACTORY, cost], // approve the exact cost, no more
});
await walletClient.writeContract({
address: FACTORY, abi: factoryAbi, functionName: "join",
args: [drawingId, [], 1, cost], // maxCost = cost: reverts CostAboveMax if the price moved
});
// Max 11 tickets per call (factory.maxJoin()). Your own picks may duplicate a
// combination the pool already holds; quick picks never do. USDC is pulled from
// msg.sender and the tickets are credited to msg.sender.3 · After settlement: collect, then claim
A drawing is settled once currentDrawingId() moves past it. Discover the pool's winning ticket ids from chain (TicketNFT ids owned by the round, tiered by Jackpot.getTicketTierIds); the public Megapot Data API (https://api.megapot.io/v1, proxied at /api/megapot/…) is a convenience — e.g. /wallets/{round}/wins/rounds/{id} — but the chain is the truth and the API's claimed flag can lag a fresh collect.
const roundAbi = parseAbi([
"function collect(uint256[] ids) returns (uint256)",
"function bountyFor(uint256 credited, uint256 count) pure returns (uint256)",
]);
// ids: this drawing's winning ticket ids still owned by the round, strictly
// ascending, at most 150 per call (the site sends 100). Anyone may call —
// no tickets required. The caller is paid min(credited * 0.25%, $0.25 per id
// collected); the rest fills the pot for everyone.
await walletClient.writeContract({
address: round, abi: roundAbi, functionName: "collect", args: [ids],
});const roundAbi = parseAbi([
"function claimable(address u) view returns (uint256)",
"function claim() returns (uint256)",
]);
const factoryAbi = parseAbi(["function claimMany(uint256[] ds) returns (uint256)"]);
const owed = await client.readContract({
address: round, abi: roundAbi, functionName: "claimable", args: [me],
});
if (owed > 0n) {
await walletClient.writeContract({ address: round, abi: roundAbi, functionName: "claim" });
}
// Or across up to 60 settled drawings in one transaction (pays msg.sender only):
await walletClient.writeContract({
address: FACTORY, abi: factoryAbi, functionName: "claimMany", args: [[173n, 174n]],
});
// Claims never expire; claiming again after a later collect pays the difference.Auto-buy plans
- factory.createAutoBuy(fixedPicks, quickPicksPerDay, daysTotal, tipPerDay, maxEscrow) clones an escrow wallet for the caller. Escrow = tickets/day × days × ticketPrice + days × tipPerDay, pulled from the caller after an exact USDC approval; reverts CostAboveMax above maxEscrow.
- AutoBuyWallet.fund(drawingId) is callable by anyone, once per drawing: it buys the day's tickets (credited to the plan's owner) and pays tipPerDay (≤ $0.50) to a non-owner caller. A failed day is not consumed. factory.fundMany(wallets, drawingId) funds up to 25 plans, all tips to the caller; size gas with factory.fundGasFloor(ticketsPerDay).
- Only the plan's owner can close() it, refunding the whole remaining escrow at any time.
Safety notes
- Base mainnet only, chainId 8453. Simulate every transaction before sending.
- USDC has 6 decimals ($1 = 1,000,000). Approve exact amounts only, to the contract you are calling.
- Never send ETH to these contracts, and never transfer USDC directly to the factory — it is ownerless with no sweep, so a direct transfer is unrecoverable.
- Payouts always go to msg.sender (claim, collect bounty, fund tips); no call pulls USDC from anyone but msg.sender.
- An InsufficientGas revert means the gas limit was too low for the guarded batch — resend with more (the site adds 50% headroom).
Questions: hello@poolpot.fun