End-to-end rehearsal of one filter.fun season on Base Sepolia (chain 84532). Covers deploy → $FILTER seed → public launch → soft filter → finals cut → settlement → rollover & bonus claim → POL deployment → events stream verification. If this runbook passes, the contract suite + indexer + web are wired correctly for the mainnet cutover. Anything that fails here represents a real production bug — don’t paper over it; fix it and re-run from the failed step.Documentation Index
Fetch the complete documentation index at: https://docs.filter.fun/llms.txt
Use this file to discover all available pages before exploring further.
Spec refs: §4 (lifecycle), §5 (genesis token), §27.1 (deploy), §27.7 (smoke-test).
0. Prereqs
- Foundry ≥ 1.0 (
foundryupif needed). The deploy uses cheatcodes that landed in late 2025. -
jqon PATH (the wrapper script reads the manifest with it). - Node ≥ 20 + npm. Workspaces are configured at the monorepo root.
- Funded deployer key on Base Sepolia. ~0.5 ETH recommended (0.05 ETH per launch slot * a handful of launches + gas headroom for the deploy itself, which is ~12 contracts).
- Basescan API key. Tier doesn’t matter; verification is rate-limit friendly.
- Canonical Base Sepolia addresses on hand (V4 PoolManager, WETH9). The example env file (
.env.sepolia.example) ships with current values; double-check before each deploy in case Uniswap rotates a deployment.
1. Deploy
- Mines the FilterHook CREATE2 salt against the canonical Deterministic Deployer Proxy (deterministic — same salt every time given the same hook bytecode).
- Deploys the suite in dependency order: TreasuryTimelock → BonusDistributor → POLVault → FilterLauncher (which inline-deploys CreatorRegistry / CreatorFeeDistributor / TournamentRegistry / TournamentVault) → POLManager → FilterHook → FilterFactory.
- Wires
polManager↔launcher↔polVault(one-shot setters),factory↔hook↔launcher. - Applies Sepolia config:
setMaxLaunchesPerWallet(1),setRefundableStakeEnabled(true). - Transfers POLVault ownership to
POL_VAULT_OWNER(Ownable2Step — owner accepts in step 2 below). - Writes
deployments/base-sepolia.jsonwith every address + the cached hook salt + the deploy commit hash + block height. - Verifies each contract on Basescan via
forge verify-contract.
-
deployments/base-sepolia.jsonexists with non-zero addresses everywhere. - Basescan shows green “Contract Source Verified” badges on each address.
-
cast call $LAUNCHER 'maxLaunchesPerWallet()(uint256)' --rpc-url $BASE_SEPOLIA_RPC_URLreturns1. -
cast call $LAUNCHER 'refundableStakeEnabled()(bool)' --rpc-url ...returnstrue.
forge verify-contract rate-limits, re-running it is safe (the script uses --skip-is-verified-check to avoid re-uploading already-verified contracts).
2. Accept POLVault ownership
POLVault ownership transfer is two-step. The deploy initiated it; the owner must accept.-
cast call $POL_VAULT 'owner()(address)' --rpc-url ...returnsPOL_VAULT_OWNER.
3. Open Season 1
-
cast call $LAUNCHER 'currentSeasonId()(uint256)' --rpc-url ...returns1. -
cast call $LAUNCHER 'phaseOf(uint256)(uint8)' 1 --rpc-url ...returns0(= Launch).
4. Seed $FILTER
- Manifest
filterToken.addressandfilterToken.lockerare now populated. - FILTER ‘totalSupply()(uint256)’ …`).
- Spec §5.3 sanity: FILTER to lose.**
5. Public launches
Open a fresh wallet (not the deployer) and exercise the per-wallet cap:- First call succeeds, emits
TokenLaunched(seasonId=1, ..., creator=A). - Second call reverts with
LaunchCapReached. - At least 2 distinct wallets each launched 1 token; we want ≥3 tokens in the season so the soft-filter has something to filter.
6. Sync indexer + web
- Indexer boots without errors. Boot log shows the launcher/factory addresses.
-
curl localhost:3000/season(default Ponder/HTTP port — adjust to your local) returns the season object with the launched tokens. -
curl localhost:3000/tokensreturns the array of tokens. - Open the web app: tokens display, leaderboard renders.
7. Soft filter
Advance the launcher to the Filter phase, set the finalists, and trigger a filter event.- Indexer events stream emits
FILTER_FIREDfor non-finalist tokens. - Non-finalist creators’ stakes were forfeited to
forfeitRecipient(launchInfoOfshowsrefunded=false, filteredEarly=true).
8. Finals + cut
Advance to Finals, then to Settlement. Submit the winner.$ROLLOVER_ROOT and $BONUS_ROOT come from the oracle package. For the smoke test, generate a minimal merkle tree off-chain (one leaf per loser holder, encoded as (holder, share) per the rollover-share-merkle convention).
- Winner is recorded in TournamentRegistry as that week’s WEEKLY_WINNER.
- POLManager deploys the season’s POL WETH into a permanent V4 LP on the winner pool. Verify via
cast call $POL_VAULT 'positionOf(address)(...)' $WINNER_TOKEN.
9. Rollover + bonus claim
A loser holder claims their rollover share + bonus.- Claimant receives winner-token shares (rollover) and WETH (bonus).
- Indexer emits
RolloverClaimedandBonusClaimedevents. -
/profile/$CLAIMANTreflects the new claim aggregates.
10. Events stream regression check
With everything else done, hit the SSE endpoint to confirm the priority pipeline works.- Connection holds, heartbeats arrive.
- HIGH-priority events (FILTER_FIRED, RANK_CHANGED on the winner) stream in real-time during steps 7–8.
- LOW-priority noise (minor HP wobbles) is suppressed when HIGH events queue.
Recovering from a botched deploy
If the deploy half-completes (e.g. ran out of gas mid-way) you’ll have a partial manifest. The script’s idempotency guard refuses to redeploy by default:- Discard everything:
rm deployments/base-sepolia.jsonand runnpm run deploy:sepoliaagain. The deployer EOA will produce different addresses (different nonce). The previously-deployed contracts remain on-chain but are unreachable through the manifest. Cheap on Sepolia. - Manual surgery: edit the manifest by hand to fill in missing addresses, then run any further setup (
launcher.setFactory, etc) viacast send. Only worth it on mainnet.
What this runbook deliberately doesn’t cover
- Mainnet deploy — separate runbook (TBD); requires multisig setup + treasury custody design that’s out of scope for Sepolia rehearsal.
- Custom V4 swap UI — the smoke test uses
cast sendfor swaps; the spectator UI doesn’t ship a swap form in genesis (Phase 2). - Arena UI smoke-testing — Arena page (Epic 1.4 / PR #34) is independent and smoke-tested separately once it merges.
- Indexer scaling — single-instance Ponder is fine for testnet rehearsal; the production indexer scaling story is a separate concern.