# The Hooks Store — product and build specification Version 1.58 · 2026-10-23 · Status: draft for review, then handed to the IMD swarm job by job. This document is a specification, not a wish list. Every section says exactly what to build, what data it uses, and how to tell when it is done. The clickable mockup in `mockup/index.html` is the visual reference; where the mockup and this text disagree, this text wins. Changes in 1.58 (2026-10-23): full live-site sweep after IMD's outage (decision 51): every page, the free check end to end, the Promotions balances against the chain, the hook pages with IMD's stage details, all sound. Found: IMD's free check refuses the Builder's default draft (the proven lane) and any own-code launch with blocker audit_required, because the request names a repository but its steps start with build-contract-project; IMD requires audit-imported-code first, then adapt-contract-project, then the review. The corrected steps pass with no blockers. 4.17 now states the steps. The free check is optional and Approve is enabled without it, so a user who skips the check could pay for a request IMD refuses; carry-over (z) leads job 15 with the composer fix and the recommended Approve lock. Changes in 1.57 (2026-10-23): job 14 delivered and accepted (decision 50): the public-launch rule with the "Not on the pond" line and My hooks (u), the Checking… pill (v), the launch-records snapshot fallback (r), log windows and backoff (s) and findings grouped by cause (t). Verified from IMD's repository on a local server in fresh browser contexts, because IMD's domain imd.fun was put on client hold by its registrar at 05:45 UTC and every imd.fun name has failed to resolve since (decision 50). The outage exercised (r) for real: with api.imd.fun, the relay's origin and ipfs.imd.fun all unreachable, the four live hooks still read LIVE and PROVEN with their launch numbers and addresses from the snapshot. Found while verifying, carried to job 15: the two ad-placement contracts launches read "Contracts launch , in-progress" without a name or link in My hooks when IMD is unreachable, because the snapshot keeps only launches whose job objective ends with the composer line (w); a stopped launch's page still offers the browser notification for when the launch goes live (x). The Checking… pill was caught on a cold load: the test token's card read Checking… at 1.2 seconds and LIVE at 1.4 seconds, never In progress (v). Changes in 1.56 (2026-10-23): stopped and failed launches leave the public pond (decision 49). The Store's grid listed the three test launches of 2026-10-21 that IMD's builders stopped with a question as REQUESTED cards for good, because the Intake never completes a stopped launch and the site had no rule for it. 3.1 now defines which launches are public (live, in progress, or requested for under 24 hours with no record saying it stopped); the rest stay in My hooks for their payer and at their direct link, which says so (3.4, 3.5, 7.3). A live hook awaiting its first code check reads "Checking…" rather than "In progress" (item v). Job 14 carries both at the top, then the resilience items (r), (s) and the findings grouping (t) of 1.55; the owner chose to pay for it. Changes in 1.55 (2026-10-22): job 13 delivered and accepted (decision 48): the balance-first Promotions panel, ad-blocker-safe markup with the EasyList check, RPC fallback lists and the proven-line wording. Found while verifying: when IMD's record plane answers "busy", a fresh browser shows every hook as "In progress" because Live is resolved through /jobs and /intake; and the fallback RPC endpoints refuse the site's log windows. Two resilience items for a later job 14 (r, s); nothing else is planned. Changes in 1.54 (2026-10-22): job 12 delivered and accepted (decision 47): the proven lane, the readable fonts on every page, the fluid Builder, the contracts-launch page and the two Promotions fixes, all verified on the live site. One edge case carried (item q): a stack that exactly matches a proven launch on the other chain reads "removes none and adds none" instead of saying it is redeployed on the other chain. Job 13 is drafted with items (n) to (q). Changes in 1.53 (2026-10-22): ad blockers hide the featured strip (decision 46). The placement cards carry the class adcard, which EasyList's generic rule ##.adcard hides on every site, so any visitor with an ad blocker sees an empty band, as the owner did. 3.1 now forbids class names, ids and attribute values that match a generic cosmetic filter anywhere on the site, with a build-time check against the current EasyList. Job 13 carries the rename (item p). Changes in 1.52 (2026-10-22): the first paid placement is live (decision 45) and the Ethereum RPC needs a fallback: rpc.mevblocker.io answers 429 under its rate limit, so the Store sometimes shows "Could not read promotions on Ethereum". 7.2 now requires a list of public RPCs per chain with fallback and one retry before any error is shown. Job 13 carries it (item o). Changes in 1.51 (2026-10-22): the Promotions panel is a balance first, then hooks (decision 44). The owner tried the delivered four-step flow and found it confusing: deposit is hidden inside step 2 of "Promote a hook". New rule in 3.5: the panel opens with the wallet's IMD and the deposited balance, a DEPOSIT box with a typed amount (approve, then deposit) and WITHDRAW; under it the wallet's live hooks, each with PROMOTE (free listing if none yet, then the 24-hour purchase paid from the deposit), BOOST and CANCEL. Job 13 carries it (jobs/next-job-carry-overs.md item n); job 12, already running, keeps its items (l) and (m). Changes in 1.50 (2026-10-22): promotions pay on any chain (decision 43). The owner tried the delivered Promotions panel: it shows only the deposit held inside the ad contract ("Available balance: 0 IMD") and never the wallet's IMD, and it ties the promotion chain to the hook's chain, so a Robinhood Chain hook can be promoted only with IMD on Robinhood Chain. 3.5 and 6.6 now say: the promotion chain is where the IMD is paid, any live hook of the wallet on either chain can be promoted there, the listing records the hook's own chain, the strip merges both chains, and the panel shows the wallet's IMD on the promotion chain beside the deposit. Job 12 carries it (items l and m). Changes in 1.49 (2026-10-22): job 10 delivered and accepted (decision 42): listings and #ad placements live against the configured contracts, all nineteen carry-overs met on the live site, the phantom scroll gap of decision 37 already fixed by the critic's own finding, so job 12 drops item (h). One miss carried: a request id that is not a hook (the ad contracts launches) still lands on "Hook not found" with only a Browse hooks link when the visitor's wallet is not connected; the plain "Contracts launch" page appears only once the site has matched the request to the wallet's own launches. Changes in 1.48 (2026-10-22): readable text (decision 41). Press Start 2P stays for the bar, titles, buttons, pills and labels; Pixelify Sans and VT323 go: reading text is Atkinson Hyperlegible, numbers and addresses are JetBrains Mono, sizes unchanged. Job 12 carries it (jobs/next-job-carry-overs.md item j). Changes in 1.47 (2026-10-22): where the store's money goes (decision 40): ad proceeds to the owner's wallet, set by the owner on both ad contracts after deployment (they shipped unconfigured: token and recipient are owner steps), and a factory charge, if factories ever come, to the same wallet. Changes in 1.46 (2026-10-22): phase 3 is the proven lane, not factories (decision 39). Every launch keeps paying the swarm and gets its own audit; speed comes from proven launches: the index now updates itself (4.17, scripts/proven_index.py on a schedule in this repository) and the next website job adds the nearest match, the proven marks in the catalog and the wait line (job 12, jobs/next-job-carry-overs.md). Factories are parked (6.8): the owner will revisit them once a tally of launched block sets justifies several at once, in their own section with a 0.25 IMD charge per use. Changes in 1.45 (2026-10-22): the Builder goes fluid (decision 38): it fills the window minus scaling gutters up to 1800 px, the card grows from 420 to 520 px with the window, paragraph text keeps its measure; the next job implements it (jobs/next-job-carry-overs.md item i). Changes in 1.44 (2026-10-22): the phantom scroll gap under the findings (decision 37): the sticky column scrolled up to about 350 px past its last box because the visually hidden labels inside the card's details area and the findings list are absolutely positioned and escape those unpositioned scroll containers; 3.2 now requires every scroll container to be positioned. The 1240 px content cap of 3.2 is up for the owner's decision (keep, 1600 px, or fluid). Changes in 1.43 (2026-10-22): the first launches are live (decision 36). IMD's deployer came back at 14:40 UTC after a twelve-hour stall and deployed the test token (launch 2448), ReedGuard (2449), LilyGuard (2450) and the ad contracts on both chains (2454 Robinhood Chain, 2456 Ethereum); all verified on chain and added to evidence/proven-launches.json (the three hooks) and 6.6 (the ad contracts). Found on the live site and carried into job 10: a type A launch never reaches Live because the site trusts only the result file, which carries no addresses; the type C page cannot read its owner and shows pool and distributor rows that do not apply; contracts launches open as "Hook not found". Changes in 1.42 (2026-10-22): job 11 accepted (decision 35). Two sizing rules join 3.2 after the live check of Builder v2: the card's details area is sized from where the card sits on screen, and on phones it is at least 40 percent of the screen; both ride at the top of job 10 with the ported browser tests. Changes in 1.41 (2026-10-22): Builder v2 (decision 34). Section 3.2 is rewritten from mockup/builder-v2.html: two zones instead of three columns, the hook as a card of tiles on the right, the palette as a category shelf, one line per rule, settings last, own code as a starting point in step 2 with its fields shown only when chosen, findings boxed and one line each, the exact text in a sheet, a fixed content width. Nothing sent to IMD changes. Job 11, after the ads. Changes in 1.34 (2026-10-22): proven launches are automatic (decision 26 amended): no switch; a matching stack is built from the proven repository in the background, the summary shows one line, and a failed import falls back silently to a fresh build. Only the creator's own repository in Advanced overrides it. Changes in 1.33 (2026-10-22): the wait. A hook takes one to three hours because every one is written, tested and audited; the owner decided two answers (decisions 26 and 27). Proven launches (4.17): the store indexes every verified launch and lets a user who stacks the same blocks start from its audited repository, so the swarm adapts and re-audits instead of writing from scratch; the wait is stated before payment and the hook page can be left and found again. Instant hooks (6.8, phase 3): for stacks with a proven launch the swarm builds an audited factory per chain, and the Builder offers "Create instantly, gas only" beside the swarm launch for hook-only and existing-token stacks. Job 9 ships 4.17 with the open carry-overs; the ads move to jobs 10a and 10; the factories are phase 3. Changes in 1.32 (2026-10-22): launch 1 is live and its hook PondsideGuard is deployed, the store's first. Found on the way: for a contracts-only launch the result file the Completed event points at is written before deployment and never names an address, so Live cannot come from the chain alone; 7.3 now says the hook page takes the deployed addresses from the launch record's artifacts through the relay, reads hook() when the artifact is a deployer, and treats the Completed event with status 0 as the chain confirmation. Job 9 carries it. Changes in 1.31 (2026-10-21): section 4.16 is rewritten in full after an independent review that read the audit findings of all four launches, IMD's own successful hook launch on Robinhood Chain and 18 more free checks (evidence/review-2026-10-21/). Corrections: the deployer mines its salt inside its constructor (an off-chain salt cannot be known), partial fills that specify the pair-currency amount revert, the no-fee sentence uses IMD's phrasing "Hook fee rate: 0%". New: type A hooks accept initialization only from the IMD launch factory ($factory), the Buyback jar cannot burn the standard token and keeps the tokens instead, the buyback happens only inside buyback(), fee blocks are tied together with one rate and one destination, liquidity rules are keyed per position, time is measured in seconds (Robinhood Chain's block number is the L1's), incompatible advanced blocks exclude each other, Batch auctions leaves version 1, the dynamic-fee flag, pull payments for the prize pot, the owner always in the body, and a no-outside-dependency sentence in every request. Job 8 ships all of it. Changes in 1.30 (2026-10-21): a sweep of every block through IMD's free check under every launch kind and pairing (122 checks, evidence/check-sweep-2026-10-21.json) found one reproducible blocker: a request with no fee block still carries the fee footer of 5.1, which the checker reads as an unstated fee (open_rate). 4.16 item 18: a request without a fee block says instead that the hook takes no fee of its own. Item 19 reworks the Donation rules sentence per option. Job 8 ships both. Changes in 1.29 (2026-10-21): the audit panels of launches 3 and 4 (read on the explorer, evidence/launch-audit-notes.md) found design flaws in the block sentences themselves and rated them high: a fee computed on the amount requested instead of the amount traded, the Anti-sandwich rule that lets a dust first swap pick the capped direction, the cap's withheld difference going to the fee recipient, a window that starts at pool initialization which anyone can trigger early, and per-wallet limits keyed by tx.origin. One auditor wrote that the fix is an owner decision because the brief specifies the rule. 4.16 gains items 11 to 16 with the decided wording; job 8 ships them. Changes in 1.28 (2026-10-21): the words mode and the Custom rule block carry a warning (4.16 item 8): written rules are riskier than blocks, and a builder question after payment spends the fee. Job 8 becomes a two-step chain whose second step is a read-only adversarial review of the composer, the consistency check and the deployer wording, inside the same 0.5 IMD (13, decision 22); no standalone audit job is planned, because every hook launch and the ad contracts launch already carry IMD's audit panel. Changes in 1.27 (2026-10-21): a deep review of the catalog, the composer and IMD's launch rules, done after three blocked launches and before any more spending, is recorded as section 4.16 (builder-proof catalog): the deployer sentence for hook-only and existing-token launches (accepted by the free check, evidence/check-B-deployer.*), the pair currency in every threshold, the Yield block offered only where its protocol exists and named by address, the undefined "burn" fee destination removed, the Volatility fee tied to a Swap fee, "the pool's token" defined for hook-only hooks, the hooks reference attached to every launch kind, and a what-to-name checklist for the words mode. Job 8 becomes "Catalog hardening" and carries all of it; the ads move to jobs 9a and 9. Changes in 1.26 (2026-10-21): the hook-only question of 6.5 is answered by launch 4: an `evm_contracts` launch deploys at a fixed, launch-salted address and does not mine the hook's permission bits, so the hook's constructor would revert; the manifest step stopped with that question (evidence/launch-4-job-blocked.json). From now on type B and C requests ask for a deployer contract that creates the hook with CREATE2 at an address carrying its permission bits, and the site reads the hook's address from the deployer (6.5, decision 20). Job 8 carries the composer change; launches 1 and 3 were sent before this and may block the same way. Changes in 1.25 (2026-10-21): launch safety (6.7, owner's decision 19). A request the swarm has to stop on after payment spends the fee and IMD refunds nothing, so the store must guarantee the request before anyone pays: a consistency check that locks Launch on any sentence a builder would have to guess, a comparison of the free check's fixed facts against the request, the builder's question shown on the hook page with a Fix in Builder button, a known-questions list that turns every past question into a rule, and plain copy about the spent fee. Job 7 becomes "Launch safety" and carries every open finding; the ads move to jobs 8a and 8. Changes in 1.24 (2026-10-21): launch 2 (the test token, paired with IMD) was blocked by the builder with needs_input: the Swap fee block's sentence still says "of the ETH side … settled in ETH" while the footer says fees come from the IMD side, and IMD's own rule sells an IMD fee for ETH in the same swap unless told otherwise, so the builder asked for an IMD/ETH conversion pool the brief never named. Correction (5.1, decision 18): every fee sentence names the pair currency for both the side and the settlement ("of the IMD side … settled in IMD"), says "do not convert the fee to ETH and do not use any other pool" for IMD pairs, and names the recipient address instead of "my wallet". The free check accepts that wording (evidence/check-test-imdfee.*). Job 7 carries it; launch 2's 0.5 IMD is spent. Changes in 1.23 (2026-10-21): the owner decided what "treasury" means (decision 17, revised): every fee choice that routes to a treasury routes to IMD's fee recipient 0x3f252e859a277a86A3967c11D4E054B2f07a1F22 on both chains, the wallet IMD's docs name for the network's pool-fee share. The option reads "IMD's treasury", the composed sentence names the address, and no address field is needed. Job 7 carries it; until then creators choose "my wallet". Changes in 1.22 (2026-10-21): the "treasury address" choice of the fee blocks never asked for an address, so a request read "send it to treasury address" and IMD's check assumed the paying wallet ("Who receives fees and payouts: unknown"). Now choosing it reveals a required address field in the block, the sentence carries the address, and the option is never IMD's own fee recipient (4, decision 17). Job 7 carries it. Changes in 1.21 (2026-10-21): job 6 is delivered and verified (jobs/log.md): the seven review findings are fixed, the relay address is set, the banner is gone and the site takes real launches. Its critic left two edge-case findings (a confirmed cancelled launch transaction still locks the flow; the pond ticker keeps an orphaned launch after a reorg) that ride at the top of job 7 with the hat removal. Phase 1 is complete; the three test launches of 15.4 come next. Changes in 1.20 (2026-10-21): no hat on Pepe (owner's decision 16): the pointed hat job 5 drew over the sprite makes his head look triangular; job 7 removes it and the sprite stays as the mockup draws it. The far boat and the birds stay (9). Changes in 1.19 (2026-10-21): job 5 is delivered and verified (jobs/log.md). Its adversarial review (three findings: timeline node states, the fee sentence for IMD pairs, relay error text) and its design critic (four reproducible bugs: a cancelled approval stuck pending, reorged Intake events kept, a full-storage import lost, a ticker timestamp that never ages) are recorded in evidence/job-5-reviews.md; none reopened the build, so all seven ride at the top of job 6, which also goes live: the relay URL set and the in-progress banner removed (13, 15, owner's decision 15). Phase 2 jobs are renumbered 7a and 7. 7.4 records the job states the API returns. Changes in 1.18 (2026-10-21): the owner scrapped the pairing and IMD-fee idea; the proposal and its switch are removed from the mockup and nothing of it is planned. The three mockup repairs and the fee-side wording of 5.1 from 1.17 stay. Changes in 1.17 (2026-10-21): the mockup carries a parked proposal behind the SHOW PROPOSAL switch in its top strip: a Pair with choice for existing-token launches (IMD, ETH or another token that already exists) and a store IMD fee of 0.1% of the paired side on every pool not paired with IMD, converted to IMD and burned or sent to a treasury address the creator picks. It is not part of any job until this document says so. One correction found while drawing it: the composer's fee-side sentence (5.1) names the pair currency, IMD when paired with IMD, instead of always ETH; the next job mirrors it. Changes in 1.16 (2026-10-21): job 4 is delivered and verified (jobs/log.md): the chain reads settle in about five seconds on a fresh browser, the top bar has no chain pill, and the launch flow is live behind the in-progress banner. The Builder's Pair with select lists IMD first and starts on IMD, ETH second (owner's decision 14 in section 14); job 5 carries it. IMD's launch policy pairs a new token's pool only with ETH, IMD or, on Ethereum, FWA (capabilities read 2026-10-21); the store offers ETH and IMD. Changes in 1.15 (2026-10-21): the top bar loses the chain pill (owner's decision 13 in section 14): the bar is the logo, the nav, the search box and Connect, and the chosen deploy chain shows only in the Builder's settings and summary and on the cards (3.0, 4 step 3). Job 4 carries the removal with its other corrections. Changes in 1.14 (2026-10-21): job 3 is delivered (jobs/log.md). Its chain reads need two corrections that job 4 carries: the Ethereum RPC is `https://rpc.mevblocker.io`, because `ethereum-rpc.publicnode.com` refuses `eth_getLogs` older than about 10,000 blocks as archive requests; and the reads start at the Intake's deployment block on each chain with a log window sized per chain (7.2), instead of walking the whole chain from block 0 in 10,000-block windows. The resolved-address card says "not launched through this store" (3.1). Job 5 becomes a two-step chain (build-website, then adversarial-review) so the money path gets an independent review inside the same 0.5 IMD job (13, 15). Changes in 1.13 (2026-10-21): no test network. Hooks deploy on Robinhood Chain or Ethereum from the first launch (owner's decision 12 in section 14): Sepolia leaves the chain lists, the filters, the Builder, the Learn page and the go-live path; the deploy chain is a required choice with no default; the tested marks record real launches instead of a verification round; IMD's free check accepts the sample launch bodies for both mainnets (5.3); job 3 carries the change. Changes in 1.12 (2026-10-21): the brand mark is a drawn pixel fishing hook (inline SVG in the mockup's top bar) instead of the ʃ character, because VT323 has no glyph for ʃ and the character fell back to a different system font on every machine (owner's decision 11 in section 14); job 3 carries the swap. Changes in 1.11 (2026-10-21): the catalog lists only hooks launched through this site (owner's decision 9 in section 14); IMD's other launches are no longer snapshotted or shown, the featured strip and the pond ticker follow the same rule, and the catalog gets an empty pond panel; 7.2 reads only this site's launches from chain; job 2 removes the snapshot catalog and keeps the top bar exactly as the mockup; job 3 follows. Changes in 1.10 (2026-10-21): job 1 is delivered and live at the-hooks-store.sites.imd.fun (jobs/log.md); the mockup's Learn cost answer says 24 hours for a featured slot (the delivery's critic caught the stale 7 days); the two cosmetic deviations found in the delivery are carried at the top of job 2's objective. Changes in 1.9: the landing scene is rebuilt as a live ASCII canvas in the manner of the ascii-sharks demo: IMD's Pepe (converted to tiles, three poses) fishing from a rowboat, drawn fish silhouettes and a flocking school, a hook on the line, and a full cast, splash, bite, reel and cheer sequence on every launch; the anime pond composition and the DOM tile layers are gone (section 9). Changes in 1.8: the three friends on the bank are Pepe frogs drawn in filled tiles, the one at the water's edge holding the rod; lily pads are flat notched discs with no dark centre; the water field uses dashes, dots and a few `><>` fish instead of tildes; reed clusters stand at the shores (section 9). Changes in 1.7: the brief is published at github.com/the-hook-store/hooks-store-spec and job 1 imports it as a two-step chain (content check, then build); jobs are paid on-chain through the Intake because the explorer's form cannot send an imported-repository build; every block must pass a real Sepolia launch before the mainnets open (15.4, 12), with the results in `evidence/blocks-verified.json` read by the Builder (4.14). Changes in 1.6: gameable blocks show a small warning sign with a hover/focus explanation instead of a red label; the live site name is `the-hooks-store.sites.imd.fun`, claimed from job 1; the brief is hosted on a GitHub account created for the project, not the owner's personal one. Changes in 1.5: the owner's decisions on the open questions are recorded (section 14); the pool share has no default and must be chosen; a Games and lotteries group marks blocks that can be gamed, with warnings on the card, the summary and the request; no reference to any other product remains in the brief; a new section 15 explains how the site goes from mockup to live. Changes in 1.4: the featured #ad row is a self-scrolling strip of compact one-line cards (name, symbol, creator, hours left), boosting and promoting live only in My hooks under Promotions, and Learn gains links to the IMD and Uniswap v4 docs plus a plain-words reference of the 14 hook permissions; the mockup was screenshot-checked at desktop and phone widths. Changes in 1.3: the landing scene is monochrome character-tile art again, redrawn with the composition of the owner's pond reference (no painting, no image job); every block states who uses it after launch; blocks added in Describe mode can be removed there and a stated rule says numbered rules win over the words; featured #ad placements last 24 hours, anyone can boost one to rank it higher, and the cards are compact; the ad contract becomes FeaturedPlacementsV2 deployed through an IMD contracts launch (job 6a). Changes in 1.2: the landing scene is a painted pond (an IMD image job, in the spirit of the owner's reference, not a copy) animated in layers, with no counter or button; the landing is full-bleed and centered; a featured #ad carousel sits between the landing and the catalog and the listing/#ad contracts move to phase 2; the Builder shows the launch type first; every block that gives the owner or the public something to do names its functions, and the hook page gets Manage and Anyone-can panels; all numbers render in the terminal face because the pixel body font draws 5 like 8; every block was run through IMD's free check across all launch types with zero blockers. Changes in 1.1: single beige theme; the pond is a background scene behind the landing copy with a "describe your hook" box; the Builder has two modes (describe, stack blocks) and three launch types (new token, hook only, existing token); the block catalog covers every v4 hook capability; the preview is a short summary with the exact text behind a disclosure; search by keyword and by address; no footer; the top bar never wraps on desktop. --- ## 0. One paragraph The Hooks Store is a website where anyone can get a Uniswap v4 hook built, audited and launched by the IMD swarm, either by describing it in their own words or by stacking reviewed building blocks. It is a front door to IMD's existing launches: `launch.open` with `onchain: "univ4_hook"` for a hook with a new token and pool, and `launch.open` with `onchain: "evm_contracts"` for a hook on its own or for a token that already exists. The site never writes Solidity, never holds funds, and has no backend. Paying is one wallet transaction to IMD's Intake contract. Browsing reads public chain data. A retro-pixel Steam-style storefront with a character-tile fisherman on a pond gives it a personality. ## 1. Goals and non-goals Goals, in priority order: 1. A newcomer goes from "I want a hook that does X" to a paid launch request in under five minutes, without reading documentation, in words or with blocks. 2. Every launch made through the site is a standard IMD launch: audited by IMD's panel, deployed by IMD's factory, listed on IMD's explorer. The site adds no trust assumptions of its own. 3. Browsing and search show real hooks launched through IMD, read from chain, with honest status. 4. The site is a static export (IPFS) that works on phones. 5. Hooks deploy on Robinhood Chain or Ethereum from the first launch. There is no test-network phase: testing happens with real launches on those chains (decision 12). Non-goals for version 1: - No store-operated review pipeline, requester or gated deployer. The `AutoHookJobRequester`, `HookRequestCodec` and `GatedHookDeployer` contracts from the foundation repo are **not used**. - No "buy this hook from its creator" marketplace. Free listings and paid #ad slots (`HookListingRegistry`, `FeaturedPlacements` from the foundation repo) are phase 2 (job 10); the carousel slot exists from day one. - No AI chat. "Describe it" is a text box whose contents become requirements in the launch request; IMD's free check reports what the swarm understood. The site does not interpret text itself. - No backend, database or accounts. A small optional relay (7.4) is the only server-side piece and the site works without it. ## 2. Users and the three journeys **Describe journey.** Type a sentence in the landing box → Builder opens in Describe mode with the text → pick the launch type and chain → read the short summary and the exact text → run the free check and read what the swarm understood → Launch (connect, approve, send) → watch status → share the hook page. **Blocks journey.** Open Builder → choose Stack blocks → add blocks and set numbers → same from the summary onwards. **Browse journey.** Scroll past the landing scene to the catalog, or search by name, token, block or address → open a hook page → "Build one like this" preloads the Builder. ## 3. Pages All pages are hash routes on one static page: `#store` (home), `#catalog` (scrolls to the grid), `#builder`, `#launch`, `#hook//`, `#mine`, `#learn`. There is no footer. A sticky top bar is on every page. Every number on every page uses the terminal face (section 8). ### 3.0 Top bar Logo, Store, Builder, My hooks, Learn, the search box, Connect. Nothing sits between the search box and Connect: the bar shows no chain pill and does not display the deploy chain (decision 13). The logo is the drawn pixel fishing hook from the mockup's top bar (an inline SVG with class `hook`: a 12 by 16 cell grid drawn at 2 px per cell, 24 by 32 px, `fill="currentColor"`, `shape-rendering="crispEdges"`), followed by the wordmark THE HOOKS STORE in Press Start 2P at 12 px with a 10 px gap. The mark is not a font character: VT323 has no glyph for ʃ, so a character mark depends on each visitor's system fonts. On desktop (above 820 px) nothing wraps: the nav links have fixed widths, the search box shrinks first (minimum 120 px), and the Connect button keeps one width (168 px) whether it shows "CONNECT" or the shortened address `0x5a7e…f00d`; disconnect is a tooltip and click on that same button. Below 820 px the bar intentionally stacks into three rows (brand and connect, nav, search). ### 3.1 Store (home) **Landing.** Full-bleed (edge to edge of the viewport, never a centered box), at least 660 px tall, with the character-tile pond of section 9 behind the copy. Centered on top, inside a soft beige glow: eyebrow, headline "Stack blocks. Cast a hook.", a two-sentence explanation, the describe box (one input, placeholder "What should your hook do? e.g. 1% of every buy goes to a prize pot", button DESCRIBE IT), the line "or open the block builder · browse hooks", and three live numbers from `GET /swarm` (hidden if the call fails). Nothing else sits in the landing: no counter, no demo button. The ticker is one boxed line at the bottom edge of the scene (`role="status"`). Submitting the box opens the Builder in Describe mode with the text filled in. Clicking the water casts (the manual trigger for tests). **Featured strip (#ad).** No class name, id or attribute value on the strip, the Promotions panel or anywhere else on the site may match a generic ad-blocker cosmetic filter (EasyList rules such as ##.adcard, ##.adslot, ##.ad-card; decision 46): the #AD label is text, the elements are named placement-card, placement-slot, featured-band. Directly under the landing, inside the content column: a heading "FEATURED #AD", the sentence "Paid placements, 0.1 IMD for 24 hours, ranked by total paid. Managed from My hooks. Not a review or endorsement.", and a strip that scrolls by itself (a seamless marquee, about 42 s per loop, paused on hover or keyboard focus, a plain scrollable row under reduced motion). Each card is one compact line: the #AD pill, the hook name, the symbol, "by {creator}" (the launch payer as an ENS name when reverse lookup on Ethereum mainnet finds one, else the shortened address) and the hours left. No chain, no description, no block labels, no buttons; the card links to the hook page. Cards are sorted by total paid (base price plus boosts), ties by the earlier purchase, and the strip ends with an open-slot card ("OPEN SLOT · 0.1 IMD / 24h · promote from My hooks"). Buying, boosting and cancelling happen only in My hooks (3.5). Cards come from `FeaturedPlacementsV2` (6.6) once it is deployed in phase 2; until then only the open-slot card shows. **Catalog.** Below the carousel, Steam layout: a left rail of filters and a card grid. The grid lists only hooks launched through this site: launch requests whose description ends with the fixed last sentence of 5.1. Hooks launched elsewhere on IMD never appear in the grid, in the search results or in the featured strip, and the site ships no snapshot of them; the address search below can still describe one when its address is typed. Of those, the grid, its count, the search results, the status rail, the landing's launch list and casts, and the strip's hook lookup show only public launches (decision 49): a launch is public while it is live, in progress (IMD's record says its job is running or its launch is deploying) or requested for less than 24 hours since its Requested log's block time with no record saying it stopped. A launch whose Completed event carries status 1 or 2, whose job IMD reports blocked (a needs_input node), cancelled, failed or superseded, whose launch record is parked or failed, or that has waited 24 hours or more without a record saying it is running, is not public: it stays in My hooks for its payer and at its direct link (3.4). The age rule needs no relay, so a stopped request leaves the grid even while IMD's record plane is down. While the pond has no hooks the grid is replaced by one panel in the Learn style (paper-3, 3 px ink border, hard shadow): the heading NOTHING ON THE POND YET, the line "Hooks launched through this store appear here. Be the first to cast.", and a primary button DESCRIBE A HOOK that opens #builder; the count beside the heading reads 0 hooks. - Filters: block type (Fees, Protection, Treasury and rewards, Liquidity rules, Timing and access, Advanced, Own source), chain (Robinhood Chain, Ethereum), status (All, meaning every public launch; Live only; In progress, meaning in progress, checking and young requested launches), sort (Newest, Oldest, Name). Every filter changes the grid immediately. - A card: name and symbol (or hook name for token-less hooks), chain pill, status pill (Requested, In progress, Checking…, Live; Failed appears only in My hooks and on the hook page), the block labels parsed from the request, one sentence, launch number when live, View. - Empty state for a search or filter with no match: "No hooks match. Clear search and filters or build the first one." (The empty pond panel above is for a catalog with no hooks at all.) **Search.** The top-bar box searches as you type and shows results in the catalog grid under the heading "Results for …": - Keywords match name, symbol, chain, block labels and the request text; all words must match. - A 40-hex address matches the hook address, the token address, the distributor address or the pool id's currencies of any known launch. A 64-hex value matches a request id or a transaction hash. - An address that matches nothing is resolved on chain through the public RPCs: if it answers `poolManager()` and `getHookPermissions()` it is shown as "A v4 hook not launched through this store" with its permissions decoded (hooks such as Hamster Hour were launched through IMD, only not through this site); if it answers `name()` and `symbol()` it is shown as a token with "Build a hook for this token" (opens the Builder with launch type C and the address filled in); otherwise "No contract found at this address on Robinhood Chain or Ethereum". - Enter also navigates to `#catalog`. ### 3.2 Builder (v2, decision 34) Two zones: the steps on the left, in order, and the hook being built on the right as a card. The mockup is `mockup/builder-v2.html` (screenshots in `mockup/shots/`); its words, colours, fonts and prices are the site's. Version 1 of this section (three columns, palette left, preview right) is superseded. **Page head.** The title, the one-line note ("Drafts save in this browser. Nothing is sent until you launch."), and a progress row of five pills (1 TYPE, 2 HOW, 3 BLOCKS, 4 SETTINGS, 5 CHECK & LAUNCH) that read done, now or pending from the draft and scroll to their step. **Step 1. What are you launching?** The three type cards, unchanged in words (A: HOOK + NEW TOKEN + POOL, B: HOOK ONLY, C: HOOK FOR MY EXISTING TOKEN); the chosen type is pine and is named beside the step title. **Step 2. How do you want to build it?** Two choices, one above the other. *Starting point:* FROM SCRATCH (default) or FROM MY OWN CODE; the repository URL and commit fields exist only while own code is chosen, under the sentence "The request pins this exact commit. Your blocks and words become the changes the swarm makes to it." Own code composes exactly as before: the line "Start from the creator's own source at commit : audit it as imported code, adapt it to this launch, and keep its behavior." and then the rules; the Advanced disclosure leaves Settings. *How you describe the rules:* the DESCRIBE IT | STACK BLOCKS control with the note "Blocks are checked sentences the swarm already knows. Words are yours and riskier. Switching never loses anything: blocks stay as numbered rules under your words." The first-visit two-card chooser is dropped. In Describe mode the text area (3,000 characters), the example chips, the words warning and checklist (4.16 item 26) and the added-blocks chips sit inside this step. **Step 3. Stack your blocks.** *The shelf:* category tabs (Token for type A only, Fees, Protection, Treasury, Liquidity, Timing, Advanced, Games, Custom), each with its colour swatch and an added/total count; under the chosen tab, one card per block with the title, the one-line blurb and ADD, or the grey state pill (ADDED, ALWAYS, NEEDS SWAP FEE, REMOVE … FIRST, OWN POOLS ONLY, TOKEN LAUNCH ONLY) in place of ADD; on phones the shelf scrolls sideways. *The stack:* one row per rule: the number, the title, the short label with the numbers filled in (mono face), a chip saying who uses it (RUNS ITSELF, OWNER, ANYONE PRESSES, derived from the block's who line of 4.12), up, down, remove and EDIT. A rule opens to show its fields, its who-uses-it sentence and the hook-page note; a block just added opens; "Open all / Close all" at the top of the stack. For type A the token block is row 0 with STANDARD or MY OWN. The six-block notice (4.16 item 27) sits under the stack. Category colours, used for the shelf swatches, the row stripes and the card tiles: token and treasury pine, fees gold (#c9a227), protection red (#a3382b), liquidity blue (#3b6ea5), timing purple (#7a4e9e), advanced ink-2, games orange (#c2612e), custom grey (ink-3). **Step 4. Settings.** As before without the Advanced disclosure: deploy chain, pair with, pool share and remainder (type A), existing token and symbol (C), hook name (B and C), owner. The step title shows "N to fill in" while fields are missing. **The hook card** (right, sticky on desktop, first on phones). A pine header: "YOUR HOOK, AS IT STANDS", the token name and symbol or the hook name, and pills for the type, the chain and the pair. One tile per rule in stack order (swatch, title, short label), the token as the base tile for type A; the tile list scrolls inside the card beyond about eight tiles. "Where the money goes": one line composed from the fee blocks and the pool fee (rates, destinations, the pair currency) plus the fee-side sentence. A readiness list from `validate()`: ticks for what is set, red arrows with a "fill it in" link for what is missing; the link focuses the field or opens the rule. The cost row (0.5 IMD, 2 tx, the wait line of 4.17), CHECK WITH THE SWARM (FREE), CONTINUE TO LAUNCH (disabled until ready), and "The request the swarm receives, N characters · SHOW EXACT TEXT". After a check, the "What the swarm understood" facts and plan appear under the card in their own scrolling box. **Findings** ("FIX BEFORE PAYING · N"), under the card, in a box with a red stripe that scrolls inside itself (about a third of the screen high): one finding per row: a plain title, a JUMP TO IT button, and an excerpt of at most two lines that quotes only the words at fault (the placeholder, or the phrase the consistency check names), never a sentence or a paragraph. Jump focuses the field for settings findings, opens and scrolls to the rule for block findings, and opens the exact-text sheet at the highlighted words for text findings. With no findings the box turns pine and reads "Nothing to fix. The request is consistent." **The exact text.** A modal sheet over the page with COPY and CLOSE: the composed request in the mono face, the words any open finding points at highlighted, scrolling inside itself. It replaces the always-open disclosure. **Layout and sizing.** The Builder is fluid (decision 38, replacing the 1240 px cap of 1.41): its content spans the window minus gutters of clamp(16px, 3vw, 48px) up to 1800 px, then centres, which is what application dashboards do (a 4K window at full scale would otherwise stretch a form across 3,700 px); at 1100 px and wider two zones (steps minmax(0, 1fr), card clamp(420px, 28vw, 520px)); paragraph text keeps its 65ch measure while boxes, rule rows and fields fill their column; below 1100 px one column with the card first; below 720 px the shelf scrolls sideways, the type and starting-point cards stack, and the rule rows wrap their tools under the text. Type sizes are fixed, never scaled with the viewport (17 px body, 13 px step titles, 9 px labels, 19 to 20 px mono numbers). Every panel has min-width 0, addresses and mono text wrap anywhere, no horizontal page scroll at any width from 320 to 2560 px, and the card never exceeds the viewport height. The card's details area (tiles, money line, readiness) is sized from where the card sits on screen, recomputed on scroll and resize, so it is largest while the column is stuck under the header and never collapses when the page re-renders (job 11 shipped it sized from the document position, which collapsed it to 140 px after any scroll; decision 35). On phones that area is at least 40 percent of the viewport height, and the card may then run past the first screen. Every scroll container in the column (the card's details area, the findings list, the understood box) and elsewhere (the shelf, the marquee) is positioned (position: relative), because the visually hidden labels are absolutely positioned and would otherwise escape the container's clipping and extend the sticky column's scroll range past its last box (decision 37); a test asserts that the column's scroll height never exceeds its content plus padding. **What does not change.** `compose()`, `body()`, `validate()`, `checkConsistency()`, the relay calls, the free check, the proven-launch line and import (4.17), drafts in `localStorage` under `hooks-store:draft:v2`, the launch review page. The job rewrites the browser tests for the new screen and keeps every unit test. ### 3.3 Launch A review page: summary table (type, token or hook, existing token for C, chain, pairing and pool for A, owner, rules, description length); the fee table (6.3, varies by type); the one-sentence note that payment happens on Robinhood or Ethereum; for type C a note that the site prepares the pool-opening transaction after the hook is live. Three numbered steps with real wallet actions: Connect, Approve 0.5 IMD, Launch; each shows its hash and the next stays disabled until the previous receipt succeeds. After the launch confirms: the request id, "The swarm has your request…", links to the hook page and the IMD explorer. ### 3.4 Hook page Header (name, symbol or hook name, chain, status pill, launch number, BUILD ONE LIKE THIS), the five-step timeline (Requested, Building, Audit panel, Deploying, Live or Failed), addresses with copy buttons (hook, token, distributor, pool id when known; "unknown until live" otherwise), evidence (source job link, audit, launch page, trade link from the operator template), then two side-by-side panels built from the hook's blocks (section 4.11): A launch that is not public (decision 49) opens with one line under the header, "Not on the pond: IMD stopped this launch" or "Not on the pond: this launch failed", and is otherwise the same page, reachable by its link and from My hooks. - **OWNER CONTROLS.** One row per owner function: a plain-language label, the function signature in the terminal face, and a SEND button (or OPEN FORM when the function takes arguments; the form has one field per argument, address fields validated). Enabled only when the connected wallet is the hook's owner (read from `owner()`); otherwise the rows show with a note "Connect the owner wallet to use these". Hooks with no owner powers say "No owner powers. This hook runs itself." Hooks launched outside the store with unknown functions say "Managed outside the store". - **TRADERS, ANYONE, AND READINGS.** One row per public function (trigger a buyback, run a draw, settle a batch, place or cancel a limit order, rebalance) and one row per view (jar balance, pot, next draw, paused, window end, TWAP) read on load and refreshed every 20 s. Then the block labels and the request text as sent. For type C hooks that are live, the OPEN THE POOL panel (6.4). The ABI for these rows is built by the site from the function signatures in section 4.11; the swarm is told to keep the names exactly. ### 3.5 My hooks Requires a connected wallet. Lists every launch request whose payer is the connected address (7.3), newest first, with the compact status and a MANAGE link for live hooks, plus the saved draft with Resume and Discard. Under it, **Promotions (#ad)**, a balance first, then hooks (decision 44, replacing the four-step flow of 1.50 that hid the deposit inside a step): one sentence of terms (0.1 IMD for 24 hours, ranked by total paid, top-ups never refunded, cancelling refunds the unused base); the promotion chain selector, meaning where the wallet pays (Robinhood Chain or Ethereum), never a filter on the hooks; two balance lines, "In your wallet on : N IMD" from the IMD token and "Deposited for promotions: N IMD" from the placements contract; a DEPOSIT box with a typed IMD amount (an approval only when the allowance is short, then one deposit) and a WITHDRAW button for the unspent deposit; then the wallet's live hooks on either chain as rows, each with PROMOTE (24 hours for 0.1 IMD paid from the deposit; the free listing is created first and silently when the hook has none yet on that chain; disabled with "Deposit at least 0.1 IMD first" when the deposit is short), and for a running placement its hours left, total paid and rank, BOOST (a typed amount from the deposit, with the non-refundable sentence and a confirmation) and CANCEL (the pro-rata refund of the base to the deposit, boosts forfeited, explained before the transaction); proceeds earned by the wallet as recipient appear as a third line with WITHDRAW PROCEEDS. Every transaction is simulated first and the panel re-reads all balances after each one. Requires a connected wallet. Lists every launch request whose payer is the connected address (7.3), newest first, with the compact status, plus the saved draft with Resume and Discard. Launches that are not public (decision 49: stopped by IMD, failed, or requested for a day without a record saying they run) are listed here only, each with its status, the builder's question and Fix in Builder, so the payer always finds them. ### 3.6 Learn Seven short answers (What is a hook? Describe it or stack blocks? What happens after I pay? What happens to a feature after launch? What does it cost? Which chains? Who audits it?), then **Read the sources**: links to the IMD docs, the Uniswap v4 overview, the hooks concept page, the PoolManager page, dynamic fees, "Your first hook", hook deployment and address mining, and the Hooks library reference (all at docs.uniswap.org/contracts/v4/…, checked live on 2026-10-21), then **What a hook can do: the 14 permissions**, a table that explains each callback in plain words (beforeInitialize/afterInitialize, beforeSwap, afterSwap, before/afterAddLiquidity, before/afterRemoveLiquidity, before/afterDonate, beforeSwapReturnDelta, afterSwapReturnDelta, afterAddLiquidityReturnDelta/afterRemoveLiquidityReturnDelta) and names the builder blocks that use each, ending with the note that a hook's address encodes its permissions. ## 4. Block catalog, version 1 A block is a reviewed, named piece of hook behavior with a few numbers to set. The site turns each block into one numbered sentence in the launch description. The swarm reads sentences, not block ids, so every sentence must be unambiguous on its own. "Types" says which launch types may use the block: A (hook + new token + pool), B (hook only), C (hook for an existing token). The catalog covers every v4 hook capability: pool initialization rules, swap-time rules (fees, limits, access, timing, dynamic fees, custom curves, async swaps), liquidity-time rules (locks, penalties, gating, minimums), donation rules, return-delta fee extraction, oracles, limit orders, and hook-owned or re-hypothecated liquidity. It is the Uniswap hook-type table (BaseHook, BaseAsyncSwap, BaseCustomAccounting, BaseCustomCurve, BaseDynamicFee, BaseOverrideFee, BaseDynamicAfterFee, BaseHookFee, AntiSandwichHook, LiquidityPenaltyHook, LimitOrderHook, ReHypothecationHook, BaseOracleHook) expressed as plain-English blocks. ### 4.1 Token (type A only; exactly one) | Block | Parameters | Sentence | |---|---|---| | **Launch token** (default) | name (1–40), symbol (2–11 uppercase A–Z0–9) | "Token: {name} ({symbol}), the standard launch token: 1,000,000,000 supply, 18 decimals, plain transfers, no owner functions." | | **Bring your own token** | name, symbol, supply, decimals (6–18) | "Token: my own token {name} ({symbol}), supply {supply}, {decimals} decimals, minted once to the launch factory, no transfer fee, nothing minted after launch, no privileged address can move, freeze, pause or seize a holder's tokens." Sets `customToken: true`. | ### 4.2 Fees | Block | Types | Parameters (defaults) | Sentence | |---|---|---|---| | Swap fee | ABC | rate % (1.0; 0.1–5), applies to (buy and sell / buy / sell), send to (my wallet / IMD's treasury 0x3f252e859a277a86A3967c11D4E054B2f07a1F22 / burn) | "Take {rate}% of the ETH side of every {applies} as a hook fee, settled in ETH inside the swap that paid it (mint a claim when the PoolManager cannot pay it directly), and send it to {to}." | | Fee that fades | ABC, needs Swap fee | start % (10), end % (1), minutes (30) | "The hook fee starts at {start}% of the ETH side of every swap when the pool opens and decreases linearly to {end}% over the first {minutes} minutes, then stays at {end}%." | | Different buy and sell fees | ABC, not with Swap fee | buy % (0.5), sell % (2.0), send to (my wallet / IMD's treasury) | "Take {buy}% of the ETH side of every buy and {sell}% of the ETH side of every sell as a hook fee, settled in ETH inside the swap that paid it (…), and send it to {to}." | | Whale fee | ABC | above ETH (1), extra % (1.0) | "Any swap whose ETH side is larger than {above} ETH pays an extra {extra}% of its ETH side as a hook fee, on top of any other hook fee, settled the same way." | | Volatility fee | ABC | move % (5), within blocks (20), fee % (3) | "When the pool price has moved more than {move}% within the last {blocks} blocks, the hook fee on the ETH side of every swap is {fee}% until the move settles below that; otherwise the normal hook fee applies." | | Split the fee | ABC, needs Swap fee | share to my wallet % (70), second address | "Split every hook fee: {a}% to my wallet and {100−a}% to {b}." | | Dynamic LP fee | BC | low % (0.3), high % (1.0) | "The pool uses a dynamic LP fee that the hook sets per swap between {lo}% and {hi}% based on recent price volatility (the BaseDynamicFee pattern)." Not on IMD launch pools, whose LP fee is fixed at 1.25% by policy. | ### 4.3 Protection | Block | Types | Parameters | Sentence | |---|---|---|---| | Anti-sniper window | ABC | minutes (10), max buy % of supply (1.0) | "For the first {minutes} minutes after the pool opens, each wallet may buy at most {pct}% of the total supply in total; larger buys revert." | | Max buy per wallet | ABC | % of supply (2.0) | "Each wallet may buy at most {pct}% of the total supply through this pool in total, for the life of the pool; larger buys revert. Sells are never blocked." | | Max swap size | ABC | tokens (1,000,000) | "Reject any single swap whose specified amount is above {tokens} {symbol}, in either direction. This caps only the amount the swapper specifies." | | Anti-sandwich | ABC | none | "Protect swappers from sandwich attacks: within one block, swaps against the direction of the block's first swap execute at no better than the block's opening price (the AntiSandwichHook pattern)." | | Cooldown | ABC | seconds (30) | "A wallet must wait at least {seconds} seconds between its swaps in this pool." | | Circuit breaker | ABC | move % (20), within blocks (10), pause minutes (5) | "If the pool price moves more than {move}% within {blocks} blocks, reject every swap for the next {pause} minutes, then resume automatically. Liquidity can still be removed during the pause." | ### 4.4 Treasury and rewards | Block | Types | Parameters | Sentence | |---|---|---|---| | Buyback jar | ABC | jar rate % (1.0), trigger ETH (0.1), then (burn / send to IMD's treasury) | "Collect {rate}% of the ETH side of every swap into a jar held by the hook. Whenever the jar holds at least {trigger} ETH, anyone may trigger it to buy {symbol} from this pool with the whole jar and {action}. The swap that fills the jar does not revert if the buyback fails." | | Prize pot (Games and lotteries group) | ABC | pot rate % of buys (2.0), draw every hours (1) | "Collect {rate}% of the ETH side of every buy into a prize pot. Every {hours} hour(s), anyone may trigger a draw that pays the whole pot to one buyer from that period chosen at random, weighted by how much ETH each buyer spent; randomness comes from recent block data, which is acceptable for small pots and must be stated in the README." | | Share fees with LPs | ABC, needs Swap fee | share % (50) | "Donate {pct}% of every hook fee back to the pool's liquidity providers in proportion to their liquidity (through the PoolManager's donate), and send the rest as the fee block says." | ### 4.5 Liquidity rules | Block | Types | Parameters | Sentence | |---|---|---|---| | Liquidity lock | ABC | days (7) | "Liquidity added to the pool is locked for {days} days from the time it was added; removing it earlier reverts. The launch factory's own zero-change fee collection is always allowed." | | JIT penalty | ABC | blocks (10) | "Penalize just-in-time liquidity: liquidity removed within {blocks} blocks of being added forfeits the fees it earned in that time to the remaining liquidity providers (the LiquidityPenaltyHook pattern)." | | LP allowlist | ABC, owner power | none | "Only addresses the owner has added to a liquidity allowlist may add liquidity; anyone may remove their own. The launch factory's initial seeding is always allowed." | | Minimum position | ABC | ETH value (0.05) | "Reject adding liquidity worth less than {eth} ETH at the current price. Removing is never blocked by this rule." | ### 4.6 Timing and access | Block | Types | Parameters | Sentence | |---|---|---|---| | Scheduled open | ABC | date-time UTC | "Reject every swap before {when} UTC. Liquidity may be added before then." | | Trading hours | ABC | from hour, to hour (UTC) | "Allow swaps only between {from}:00 and {to}:00 UTC each day, judged by block timestamp; reject all others. Liquidity changes are always allowed." | | Allowlist opening | ABC, owner power | minutes (15) | "For the first {minutes} minutes after the pool opens, only addresses the owner has added to an allowlist may buy. After that, anyone." | | Pause switch | ABC, owner power | none | "The owner may pause and unpause swaps. Liquidity can always be removed while paused." | | Sunset | ABC | date-time UTC | "After {when} UTC every rule above stops applying and the hook lets every swap and liquidity change through unchanged, forever." | ### 4.7 Advanced | Block | Types | Parameters | Sentence | |---|---|---|---| | Price oracle | ABC | none | "Record a time-weighted average price of the pool that other contracts can read (the BaseOracleHook pattern, with a Uniswap v3 compatible observe interface)." | | Limit orders | ABC | none | "Let users place limit orders in the hook that fill automatically when the pool price crosses the order price, and withdraw unfilled orders at any time (the LimitOrderHook pattern)." | | Yield on idle liquidity | ABC | protocol (Aave / Morpho) | "Liquidity the pool is not using at the current price is lent on {protocol} and recalled when needed for swaps (the ReHypothecationHook pattern). If {protocol} has no deployment on the chosen chain, the swarm must report that as a blocker instead of substituting another protocol." | | Batch auctions | BC | seconds (60) | "Swaps are not filled immediately: the hook collects them and settles each batch at one clearing price every {seconds} seconds (the BaseAsyncSwap pattern)." | | Stable curve | BC | none | "Replace the pool's pricing with a constant-sum curve suited to two assets of the same value (the BaseCustomCurve pattern)." | | Pool rules | BC | fee tier (0.05 / 0.3 / 1 %) | "Only pools where one currency is native ETH and the fee tier is {tier}% may be initialized with this hook; any other pool initialization reverts." | | Donation rules | ABC, owner power | who (only the owner / allowlisted / nobody) | "Donations to the pool are allowed from {who}; all other donations revert." | ### 4.8 Custom | Block | Types | Parameters | Sentence | |---|---|---|---| | Custom rule | ABC | one or two sentences (text, cleaned) | the text itself | ### 4.9 Owner powers The description always ends the rules with one of: "No owner functions, no backend signer, no upgrade path." or "Owner {address} may only: {powers}. No other admin function, no backend signer, no upgrade path." `{address}` is the owner field, else the connected wallet. ### 4.10 Combination rules (enforced in the palette and the stack) - At most one of each block. Fee that fades, Split the fee and Share fees with LPs need Swap fee and are removed with it. Different buy and sell fees cannot coexist with Swap fee. - The sum of all ETH-side rates (Swap fee or the larger of buy/sell, Whale fee extra, Buyback jar, Prize pot) must be ≤ 5%. A red notice blocks Continue above that. - Blocks marked BC are dimmed under type A with the pill OWN POOLS ONLY and are left out of the request; token blocks exist only under type A. - Blocks are a JSON file, `src/blocks/catalog.json`: `{id, group, title, blurb, types, params[], short, sentence, ownerPower?, requires?, excludes?, replaces?, owner?[], anyone?[], user?[], view?[]}`. Adding a block changes only that file plus a test that its sentence renders. ### 4.11 Management interface (what each block asks the swarm to expose) Every block whose behavior can be operated or read after launch appends to its sentence the exact functions the hook must expose, so the hook page can offer buttons without reading the delivered source. The composed text ends with "Keep the function names exactly as written so the storefront can show them." The site builds a minimal ABI from these signatures. Owner functions are gated by the hook's `owner()`. | Block | Owner functions | Anyone / user functions | Views | |---|---|---|---| | Swap fee, Different buy and sell fees | | `claimFees()` redeems minted claims to the recipient | | | Fee that fades | | | `currentFeeBps()` | | Volatility fee | | | `isVolatile()` | | Dynamic LP fee | | | `currentLpFee()` | | Anti-sniper window | | | `windowEndsAt()` | | Max buy per wallet | | | `boughtBy(address)` | | Cooldown | | | `nextSwapAt(address)` | | Circuit breaker | | | `haltedUntil()` | | Buyback jar | | `buyback()` | `jar()` | | Prize pot | | `draw()` | `pot()`, `nextDrawAt()` | | Liquidity lock | | | `unlockAt(address)` | | LP allowlist | `setLpAllowed(address[] accounts, bool allowed)` | | `isLpAllowed(address)` | | Scheduled open | | | `opensAt()` | | Trading hours | | | `isOpen()` | | Allowlist opening | `setAllowed(address[] accounts, bool allowed)` | | `isAllowed(address)` | | Pause switch | `pause()`, `unpause()` | | `paused()` | | Sunset | | | `sunsetAt()` | | Price oracle | | | `twap(uint32 secondsAgo)` returning a Q64.96 sqrt price | | Limit orders | | `placeOrder(bool zeroForOne, int24 tick, uint256 amount)` returning an id, `cancelOrder(uint256 id)`, `claim(uint256 id)` | | | Yield on idle liquidity | | `rebalance()` | | | Batch auctions | | `settleBatch()` | | | Donation rules | `setDonor(address account, bool allowed)` | | | Blocks with no row (Whale fee, Split the fee, Max swap size, Anti-sandwich, Share fees with LPs, JIT penalty, Minimum position, Stable curve, Pool rules, Custom rule) run themselves and show nothing to manage. ### 4.12 Who uses each block (shown on every block card) Most blocks run by themselves inside the pool and need nothing from anyone. The card says which kind each block is, in these words: | Kind | Blocks | What the hook page shows | |---|---|---| | Runs itself | Swap fee, Fee that fades, Different buy and sell fees, Whale fee, Volatility fee, Split the fee, Dynamic LP fee, Anti-sniper window, Max buy per wallet, Max swap size, Anti-sandwich, Cooldown, Circuit breaker, Share fees with LPs, Liquidity lock, JIT penalty, Minimum position, Scheduled open, Trading hours, Sunset, Stable curve, Pool rules | Readings only, where the block has a view | | You (the owner) flip a switch or keep a list | Pause switch, Allowlist opening, LP allowlist, Donation rules | Owner controls | | Anyone presses a button when it is time | Buyback jar, Prize pot, Yield on idle liquidity, Batch auctions | Traders, anyone, and readings | | For traders | Limit orders | An order form under Traders, anyone, and readings | | For other contracts and apps | Price oracle | A reading (the average price); nothing for the owner to do | So "Price oracle" is not a setting the creator operates: it makes the pool publish its average price so other apps can rely on it. "Limit orders" gives the people who trade the pool a new ability. Neither needs managing; the hook page just exposes them. ### 4.13 Blocks that need a heads-up Some blocks can be worked around by determined users or need care. They stay in the catalog, and the site flags them quietly: a small yellow warning sign (`!`) next to the block's name in the palette, on the block card and in the summary list. Hovering or focusing the sign shows a tooltip, "Heads up: this block is easy to work around or needs care." followed by the block's own sentence below. No red labels, no extra text on the card. The Prize pot lives in the palette group "Games and lotteries". The hook page shows the same sign next to the block label. | Block | Warning shown | |---|---| | Prize pot | The draw uses block data for randomness. A well-funded player can tilt the odds. Keep pots small and say so on your token's page. | | Circuit breaker | A large trader can trip the breaker on purpose to stall trading for everyone. | | Anti-sniper window, Max buy per wallet, Cooldown | Per-wallet limits can be sidestepped by splitting across several wallets. | | Max swap size | A trader can split one large trade into several smaller ones. | The catalog entry carries the text in a `risk` field; a block with a `risk` field shows the sign everywhere in the site. ### 4.14 Tested marks The site does not hold hook code: every launch is written, tested and audited by the swarm for that request (section 6). So the proof that a block works is a real launch that used it. `evidence/blocks-verified.json` in the spec repository records that proof: an array of `{block, launchId, chainId, hook, passed, checkedAt, notes}`, one entry per block per real launch that Claude checked (15.4). The Builder fetches `https://raw.githubusercontent.com/the-hook-store/hooks-store-spec/main/evidence/blocks-verified.json` at load (that host allows browser reads) and falls back to the bundled copy `public/blocks-verified.json`. A block with an entry whose `passed` is true shows a small tick and the words "proven in a live launch" on its card; any other block shows "not yet proven in a live launch" in its tooltip and nothing else changes. The file ships empty and fills as real launches on Robinhood Chain or Ethereum are checked. Blocks are not pre-verified on a test network (decision 12): every launch is written, tested and audited by the swarm for that request, and a block that fails in a real launch has its sentence or its 4.11 functions rewritten, or leaves the catalog. ### 4.16 Builder-proof catalog (deep review, 2026-10-21) IMD's builders stop and ask when a request leaves them a guess, the audit panel rates a flawed design high, and either costs the fee after payment. Three blocked launches, the panels' findings on all four, a free-check sweep of every block (122 checks) and an independent review (18 more checks, evidence/review-2026-10-21/) produced these rules. Every rule binds the composer and the catalog; job 8 ships them. Two facts frame them: the free check does not catch interactions between blocks, so only the site's own consistency check (6.7) can; and the panel is a launch gate, so a sentence that predictably earns a high finding is a launch risk. **Deployment and the pool** 1. **Deployer for hook-only and existing-token launches (6.5).** The request opens with `Deployer`: its constructor takes the PoolManager address, `$owner` and the fee recipient, takes no salt, starts at salt 0 and increments until the CREATE2 address carries the hook's permission flags, then creates the hook in the launch transaction, passing those arguments unchanged; neither constructor calls any other contract; there is no deploy function to call afterwards; `hook()` returns the address. The salt cannot be mined off-chain because the deployer's own address is salted by the launch number and unknown until deployment. The site shows "hook not deployed yet" when `hook()` is zero (launch 1 was built with a later `deploy(salt)` step before this rule). Whether IMD's manifest verifier accepts a constructor that deploys is confirmed by the first hook-only launch after job 8. 2. **Type A accepts initialization only from the launch factory.** "beforeInitialize accepts only the IMD launch factory, passed as the constructor argument $factory; any other initialization reverts; the hook serves only the pool the factory initializes. Constructor arguments: the {chain} PoolManager {address}, $owner and $factory." Without it anyone can bind the hook to a junk pool first; launch 1's panel rated that high, and IMD's own hook launch on Robinhood Chain uses exactly this gate. The free check accepts the sentence. 3. **Hook-only and existing-token hooks serve ETH pairs.** Every B and C request says "Only pools whose currency0 is native ETH may be initialized with this hook; any other initialization reverts" (Pool rules then only narrows the fee tier), and for type C "only pools of native ETH and {token}". Every pool-dependent view and limit is keyed by PoolId (windowEndsAt(PoolId), haltedUntil(PoolId), boughtBy(PoolId, address), …), the sentences say so, and the hook page passes the pool id. One definition line: "In this hook, the token is the pool's non-ETH currency and its total supply is that token's totalSupply()." 4. **Pair currency everywhere.** Every fee side, settlement, threshold and unit follows the pool's pair currency: IMD for an IMD-paired type A launch, ETH otherwise; IMD-paired requests add "do not convert the fee to ETH and do not use any other pool" and "the launch token may be currency0 or currency1; handle both orders". 5. **No outside dependencies.** Every request without the Yield block says "The hook uses no outside pool, token, price feed or service; every price it needs comes from its own pool." References `uniswap-v4-hooks` and `uniswap-v4-security` ride on every launch kind. 6. **The launch factory, named.** Type A: "the launch factory ($factory) may always add liquidity and collect fees with a zero-change modify." Types B and C never mention a factory. 7. **The owner, in the body too.** `body()` always sends `owner` = the address the text names, so `$owner` and the sentences agree. **Fees** 8. **Fees on what traded.** Every fee sentence adds "computed on the amount actually traded; when the swap specifies its {pair currency} amount and the pool fills only part of it, the swap reverts rather than overpaying" (in v4 a fee on the filled pair-currency amount cannot be reconciled after the swap; launch 4's panel rated the overcharge high three times and accepted the revert). Same trade, same fee (launch 7's audit, launch 2450, 2026-10-22): when the swap specifies its pair-currency amount, the fee is taken from it so that identical pool execution pays the same fee as a token-specified swap, exact input A pays A × rate ÷ (10000 + rate) and exact output A pays A × rate ÷ (10000 − rate), with mulDiv; the sentence says "the same pool execution pays the same fee whichever amount the swap specifies". 9. **No fee, say so.** With no fee block the composer writes "Hook fee rate: 0%." instead of the fee footer; the footer alone makes the checker refuse (open_rate) and otherwise leaves the fee rule to assumption. 10. **One rate, one destination.** Whale fee requires Swap fee or Different buy and sell fees and uses their recipient. Fee that fades has no end field: it starts at its start rate and fades to the Swap fee's rate, replacing it for the swaps the Swap fee applies to. Volatility fee requires Swap fee and applies to the same swaps; when a fade and a volatility rate both apply, the higher wins. Split the fee and Share fees with LPs split the Swap fee and the Whale fee only (never the jar or the pot), LP share first, then the split; Split replaces the Swap fee's destination and the composer drops "send it to". Fee destinations are the paying wallet, written as its address, or IMD's treasury (decision 17); "burn" leaves the fee blocks. 11. **Buyback jar.** The buyback happens only inside buyback(), never inside a swap, with a price limit 3% above the pool price at the start of the call; it keeps what it could not spend and anyone may call it again; the "does not revert" clause is gone. Standard launch token: "keep the bought tokens in the hook forever; no function can move them" (the token has no burn function and a dead-address transfer is refused by IMD). Own token: the token sentence adds "with a public burn(uint256) that burns the caller's own tokens" and the jar burns. Existing token: "burn with the token's burn(uint256); if it has none, keep them in the hook forever". 12. **Share fees with LPs** adds "if no liquidity is in range, the whole fee goes to the recipient" (donate reverts otherwise). 13. **Prize pot.** The winner withdraws with claimPrize(); a period with no buyers rolls its pot into the next; draw() settles the most recent complete period; the buyer list per period is bounded by paying only the weighted winner from recorded totals. **Protection and timing** 14. **Anti-sandwich, decided.** "Within one block, no swap executes at a better price than the block's opening price in its own direction; the difference the cap withholds is donated to the pool's liquidity providers through the PoolManager's donate, never to the fee recipient or the owner." 15. **When a pool opens.** Every window (Anti-sniper window, Allowlist opening, Fee that fades, Prize pot periods) starts at the pool's first swap, not at initialization. 16. **What a wallet is.** Per-wallet rules (Anti-sniper window, Max buy per wallet, Cooldown, Allowlist opening, LP allowlist, Prize pot buyers, Donation rules) define the wallet as tx.origin and say that several wallets or a shared relayer can defeat the limit. 17. **Time in seconds.** Volatility fee, Circuit breaker and JIT penalty measure in seconds of block timestamp, not blocks (Robinhood Chain's block.number is the L1's). Anti-sandwich keeps "one block" and says "the chain's own block number (ArbSys(address(100)).arbBlockNumber() on Robinhood Chain)". 18. **Max swap size** applies when the swapper specifies a token amount; swaps that specify the pair-currency amount are not capped by this rule; amounts are in the token's own decimals. 19. **Liquidity rules by position.** Liquidity lock is keyed per position (sender, tickLower, tickUpper, salt) with unlockAt(bytes32 positionKey); Minimum position and JIT penalty likewise; the hook's own positions (Limit orders, Yield) are exempt from every liquidity rule and the sentences say so. 20. **Sunset** says "every other rule in this request stops applying" wherever the block sits, and "funds already collected stay withdrawable through their functions". 21. **Donation rules, one sentence per option.** Only the owner: only the owner may donate, no function. Allowlisted: addresses on the owner's donor allowlist, setDonor(address, bool) for the owner. Nobody: every donation reverts, no function, no owner power. Always: "the hook's own donations (Share fees with LPs, the anti-sandwich difference) are always allowed". **Advanced blocks** 22. **Incompatible blocks exclude each other** (`excludes` becomes a list): Stable curve excludes Volatility fee, Circuit breaker, Anti-sandwich, Price oracle, Limit orders and Dynamic LP fee; Limit orders excludes Batch auctions; Yield is type B and C only, holds all liquidity itself and excludes every liquidity rule. 23. **Batch auctions leaves version 1** (dimmed "not in this version"): it had no claim function, no exact-output rule and no definition of the clearing price. 24. **Dynamic LP fee** says "the pool must be initialized with the dynamic-fee flag (fee 0x800000)", excludes Pool rules, names its mapping ("the fee moves linearly with the absolute price move over the last {seconds} seconds"), and 6.4 initializes a pool for it with the flag. Every request without that block says "a pool initialized with the dynamic-fee flag (0x800000) is rejected at initialization", because the hook would never set an LP fee and liquidity providers would earn nothing while the hook fee is still taken (launch 7's audit). 25. **Yield on idle liquidity** stays dimmed on every chain until the catalog lists verified protocol addresses; if re-enabled it is B and C only, says "the hook holds all liquidity; LPs deposit through the hook; direct adds revert", and names the Aave v3 Pool address or the Morpho Blue market id on the deploy chain. **Words, size and process** 26. **Words mode and Custom rule** show the warning "Written rules are riskier than blocks. If the builder has to stop and ask a question after you pay, the 0.5 IMD is spent. Blocks are checked sentences; words are yours." and the checklist: name every address, currency, amount and time; say who may call what, or say there are no owner functions; name no outside pool, token or service unless with its address on the deploy chain. Words mode composes no blocks unless the stack is shown, the summary lists every rule in both modes, the footer never denies owner functions the description grants, and a numbered block rule wins over the Custom rule and the description. 27. **Fewer rules, safer launch.** Above six blocks the summary says every extra rule is more for the audit panel to refuse and that the contract has a 24 KB size limit. 28. **Arithmetic.** Percent rules use mulDiv; the own-token supply is capped at 1,000,000,000,000. 29. **Copy to verify before advertising:** whether `poolBps` is a share of the creator's 90% or of the total supply (IMD's own launch describes 8,000 as "80% pool, 10% remainder, 10% swarm"). 30. **One launch per kind after a fix,** every builder question into `src/blocks/known-questions.json` (6.7), and every audit read on the explorer and turned into a rule here. 31. **Buy-side limits and range orders** (launch 5's audit, launch 2448, 2026-10-22). Allowlist opening, Open at, Trading hours, Anti-sniper, Max buy, Max swap and Cooldown are checked when someone swaps, and a one-sided liquidity position placed just past the current price is a buy order that no swap check sees and no hook fee touches: when anyone sells into it, its pair currency turns into the token and the position's owner withdraws the token. For the limits that have a window (Allowlist opening, Open at, Trading hours, and Anti-sniper's opening minutes) the block sentence adds: "While this rule is active, adding liquidity is allowed only to the factory and the owner" (for type B and C hooks: only the owner), "removing liquidity is never restricted", and the hook enables beforeAddLiquidity, which changes the permission bits and the address mask. For the limits with no end (Max buy, Max swap, Cooldown) the sentence states the accepted limit instead: "A one-sided liquidity position is not a swap: it is not limited by this rule and pays no hook fee." The audit panel accepts either; it refuses the silent gap. ### 4.17 Proven launches: start from audited source Index form (2026-10-22, after job 9): each entry's `repoUrl` is written exactly as IMD's `/import` returns it, with `.git` at the end, because the delivered site compares the two strings as they are (the reviewers' high finding; the normalisation rides in job 10). Type A entries list the token block (`token` or `own-token`) with the behaviour blocks, because a type A draft's block set includes it. Every launch through the store leaves a public, audited repository (IMD delivers each launch to `identity-md-launches/launch--…`). The spec repository keeps `evidence/proven-launches.json`, one entry per launch the owner has verified: `{launchId, launchNumber, chainId, type, blocks: [block ids], repoUrl, commit, hookAddress, deployerAddress, name, verifiedAt}`. The site fetches it at load from raw.githubusercontent.com, like the tested marks of 4.14, and never bundles it. Since decision 39 the file updates itself: `scripts/proven_index.py` runs every six hours on GitHub Actions in this repository, reads IMD's launch list, keeps the launches whose request ends with the store's composer line, and adds a launch only when IMD's record is live with its attestation and every admission check passed, no artifact failed verification, every numbered rule of the request matches a catalog block sentence, the hook has code on chain (for a deployer launch, the deployer's `hook()` names it), every function the matched blocks promise is present in the deployed code (its selector in the runtime bytecode) and `owner()` answers when a block grants an owner power. Entries already in the file are never changed or removed by the script; the owner removes one by hand if a hook ever proves bad. Matching in the Builder is exact first (the same block set); from job 12, when no exact match exists, the Builder picks the proven launch of the same type with the most blocks in common and the request tells the swarm to start from it, naming the blocks to remove and to add, so the swarm adapts and re-audits instead of writing from scratch. - **Matching, automatic.** A draft matches a proven launch when the launch type and the set of block ids are the same and the chain is the same; parameters may differ, and extra words or a Custom rule are allowed. Nothing is asked: the match is applied in the background, the summary shows one line, "Built from a proven launch: {name} (launch {number}, audited {date})", and the hook page carries the same line after launch. A creator's own repository in Advanced (start from my own code) overrides the match; there is no other control. - **The request.** When a match applies, the body carries `repoUrl` and `baseCommit` from the entry and its steps are `audit-imported-code`, `adapt-contract-project`, `adversarial-review` (decision 51: IMD refuses a repository launch whose first step is not the audit of the code as it arrived; the same rule applies to own code), resolved first through the relay's `/import` (7.4); if the import fails or the relay is absent, the request is composed from scratch with no notice beyond the longer wait estimate, and the composed text says, after the first sentence: "Start from the audited source at {repoUrl} commit {commit}: keep its behavior, apply the parameters and rules of this request, adapt the deployer to 4.16 item 1 if it mines its salt after deployment, and audit it as imported code." Every other sentence of 5.1 and 4.16 stays, so the request is complete on its own. - **The wait, stated.** The Builder's summary and the Launch page say "Typically 1 to 3 hours: the swarm writes, tests, audits with four auditors and a judge, then deploys. From a proven launch, usually less." The hook page offers Copy link, asks once for permission to show a browser notification when the launch goes live, and the pond announces it. Nothing promises a time IMD does not promise. - **Owner's step.** After verifying a launch the way 15.4 describes, the owner adds its entry; a launch whose audit left an unresolved finding is never listed. ## 5. Composing the launch request ### 5.1 The description text (at most 7,500 characters) Type A: ``` Launch {name} ({symbol}) with a Uniswap v4 hook on {chain}, paired with {IMD|ETH}. {token sentence} ``` Type B: ``` Deploy one contract: {hookName}, a standalone Uniswap v4 hook with no token and no pool of its own, on {chain}. It must be usable by anyone initializing a new pool with it on the PoolManager passed as a constructor argument ({chain} PoolManager {address}). ``` Type C: ``` Deploy one contract on {chain}: {hookName}, a Uniswap v4 hook for the existing token {tokenAddress} ({symbol}), paired with ETH. No new token and no pool created by the launch; the creator will initialize the pool with this hook afterwards on the PoolManager passed as a constructor argument ({address}). ``` Then, for every type: ``` The hook does exactly the following, and nothing else: (with words only: "exactly what the creator describes below, and nothing else"; with words and rules: "exactly what the creator describes, plus the numbered rules, and nothing else. If the description and a numbered rule disagree, the numbered rule wins.") In the creator's own words: {words} (only when words were written) 1. {rule} 2. {rule} … (only when blocks were stacked) {owner powers sentence, ending "through the functions named above" when there are powers} Fees are always taken from the {ETH|IMD} (pair currency) side, never from {symbol}; a fee the PoolManager cannot pay directly is minted as a claim the recipient can redeem. A: Enable beforeInitialize (required by the IMD launch factory) and only the callbacks the behavior above needs. Take the PoolManager as a constructor argument. Owner, if any, is $owner. A: Pool: {poolShare}% of my 90% share seeds the pool; the remainder goes to {remainderTo}. (only once the user has chosen; never a default) B, C: Take the PoolManager as a constructor argument and enable only the callbacks the behavior above needs. The deployed address must carry the correct hook permission bits (mine a CREATE2 salt). Owner, if any, is $owner. Start from the creator's own source at {repo} commit {commit}: audit it as imported code, adapt it to this launch, and keep its behavior. (only with Advanced: own code) Keep the function names exactly as written so the storefront can show them. Composed with The Hooks Store block builder. ("block builder" only when blocks were used; the fixed last sentence marks cards Built here) ``` Words are cleaned: `"`, `\`, `<`, `>` and line breaks become spaces; 3,000 characters maximum. PoolManager addresses: Ethereum `0x000000000004444c5dc75cB358380D2e3dE08A90`, Robinhood `0x8366a39CC670B4001A1121B8F6A443A643e40951`. ### 5.2 The request body Type A: ```json { "objective": "", "shape": "chain", "references": ["uniswap-v4-hooks", "uniswap-v4-security"], "steps": [ { "skill": "build-contract-project" }, { "skill": "adversarial-review" } ], "onchain": "univ4_hook", "chainId": 4663, "pairWith": "eth", "owner": "0x…", "economics": { "poolBps": 5000, "remainderTo": "0x…" }, "customToken": true, "github": true } ``` `customToken` only when Bring your own token is used; `owner` and `remainderTo` only when set. Types B and C: ```json { "objective": "", "onchain": "evm_contracts", "chainId": 4663, "owner": "0x…", "github": true } ``` With Advanced: own code, both add `repoUrl` and `baseCommit` (resolved first with `POST /requests/import` when the relay exists). The compact JSON must stay under 16 KiB. ### 5.3 Evidence that IMD accepts these Run through `POST https://api.imd.fun/requests/check` on 2026-10-20 and 2026-10-21, all with zero blockers and the full plan (build, independent tests, four audit specialists, judge, deploy on Sepolia); the exact bodies and answers are saved under `evidence/`. On 2026-10-21, after decision 12, the A1, B and C bodies were checked again with `chainId` 1 and 4663: zero blockers, and the plan's last step reads "Deploy on Ethereum mainnet" or "Deploy on Robinhood Chain" (`evidence/check-launch-mainnet-*.json`): - `check-A-default-blocks`: type A from the default stack (kind "hook"; token name, symbol, fee rule, fee destination and admin recognised). - `check-liquidity`: type A with Liquidity lock and JIT penalty. `check-prompt`: type A written in plain words inside ``. - `check-standalone`: type B (kind "contracts"). `check-attach`: type C for an existing token (kind "contracts", recipient recognised). - `check-all-blocks-A1`, `A2`, `B`, `C`: every block in section 4 with its management functions, split across four requests so each stays under the limit; together they cover all 31 behavior blocks under every launch type that allows them. - `check-job-0` and `check-job-1`: the hero art job and the first website job. ## 6. Chains, payment and fees ### 6.1 Where the hook deploys Robinhood Chain and Ethereum, both from the first launch (decision 12). `site-config.json` enables deploy chains `[4663, 1]` and lists only those two chains; the Builder offers exactly those, with no default. Sepolia is not offered anywhere. ### 6.2 Where the payment happens A launch is paid on **Ethereum or Robinhood Chain** in that chain's IMD, while the body's `chainId` says where the hook deploys; the two need not match (IMD held on Ethereum pays for a hook on Robinhood Chain). Intake v2 on every chain: `0xa43e6f75ee006411f79ac1c84120606c2330de82`. IMD on Ethereum `0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7`, on Robinhood `0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127`. Action id `launch.open@launch-1` as bytes32 `0x6c61756e63682e6f70656e406c61756e63682d31000000000000000000000000`. Price from `priceOf(action, asset)`, today 0.5 IMD; refuse to send if 0 or above 0.5 IMD. Two transactions: `IMD.approve(intake, price)` then `Intake.request(actionId, bodyBytes, (0x0, 0x00000000), imd, price)`. The Intake ABI is in the foundation repo at `docs/checks/consolidated/sources/intake-abi.json`. Without the relay the Launch page warns that a refused body spends the fee and offers Copy request. ### 6.3 Fee table The swarm fee is 0.5 IMD per launch, once, and the audit panel is included in it. (The earlier foundation design paid 0.5 IMD for a build plus 0.5 IMD for a separate oracle review; IMD's own launch replaces that second step, so there is no 1 IMD total.) Every amount on the site is rendered in the terminal face (section 8), never in the pixel body font. Type A: swarm fee 0.5 IMD; gas for two transactions; supply 10% swarm / 90% you with {poolShare}% of yours in the pool; pool trading fee 1.25% (1% to you, 0.25% network) plus the hook's own fee; deployment paid by IMD. Types B and C: swarm fee; gas; "Token and pool: none created. You get the audited hook contract only."; for C "Opening the pool: one more transaction from your wallet after the hook is live, prepared by the site"; deployment paid by IMD. A featured #ad slot is 0.1 IMD for 24 hours, optional, phase 2; anyone can boost a placement with more IMD to rank it higher, and boosts are not refunded (6.6). ### 6.4 Opening a pool for a type C hook (phase 2b) After a type C hook is live, the hook page shows OPEN THE POOL: the site builds `PoolManager.initialize(poolKey, sqrtPriceX96)` for currencies (ETH, token) sorted, the fee tier the Pool rules block chose (else 0.3%), the matching tick spacing, the hook address, and a starting price the user types as "1 token = x ETH". The wallet sends it. Then a link to add liquidity on a v4 interface for the chain. Until phase 2b the hook page shows the same panel with the values and a copy button but tells the user to send it from a v4 interface. ### 6.5 Hook-only address bits (answered by launch 4) A v4 hook's address must encode its permissions. The `univ4_hook` launch mines the address. An `evm_contracts` launch does not: it deploys each listed contract at a fixed address salted with the launch number, where a hook's constructor reverts (launch 4, 2026-10-21, blocked at the manifest step with exactly that question). So every type B and type C request asks for two contracts in one: a deployer, listed in the launch, whose constructor creates the hook with CREATE2 at an address that carries the hook's permission bits, using a salt mined off-chain against the deployer's own address when that address can be computed in advance, or mined inside the constructor otherwise; the deployer exposes `hook()` as a view returning the hook's address, and the request says all of this in its first sentence. The site treats the launch's verified contract as the deployer and reads `hook()` to find the hook for the hook page, My hooks, the pool-opening call (6.4) and the tested marks (4.14). The browser-side CREATE2 fallback that this section used to describe is dropped. ### 6.6 Featured placements, version 2 (phase 2) The foundation repo's `FeaturedPlacements` (0.1 IMD for 7 days, ordered by purchase) is too cheap for a week and invites spam. Version 2 keeps its custody model and changes the terms: - Duration 24 hours; base price 0.1 IMD per placement; one live placement per listing; `#ad` on every placement and event; the same deposit, withdraw, cancel with pro-rata refund of the base price, settle, proceeds recipient and two-step owner as before. No proxy, no upgrade, no self-destruct. - `boost(placementId, amount)`: anyone may add IMD from their deposited balance to an open placement. Boosts are never refunded: on cancel they go to the pinned proceeds recipient, on settle both base and boosts do. `rankOf(id)` returns base plus boosts; `featured(cursor, scanLimit)` returns each placement's total so the site sorts by total paid, ties by the earlier purchase. - Deployed through an IMD contracts launch (`jobs/job-10a-ads-contract.json`, `evm_contracts`, one launch per chain: Robinhood Chain, then Ethereum), which includes the audit panel; the free check's answer is saved as `evidence/check-job-6a`. The listing registry deploys unchanged beside it. - Pay on any chain (decision 43): a listing names the hook's own chain id and address, so the registry and placements contract on either chain may carry a hook deployed on the other. The site's promotion chain is the chain the wallet pays on; its hook list offers every live hook the wallet launched through the store on either chain, labelled with the hook's chain; the strip reads `featured()` on both chains, merges by hook (chain id and address) and ranks by the total paid across both; one card per hook. The flow's transactions still need the wallet on the promotion chain, which the panel switches to on request. - Deployed (decision 36, 2026-10-22 14:40 UTC): Robinhood Chain, launch 2454 at commit 952e62de: `HookListingRegistry` 0x366c84a2971713fc8fa24d3d4b8abd2f4f085625, `FeaturedPlacementsV2` 0x63b59cbf5b535e46815ae5d4cf126bf409dba69c; Ethereum, launch 2456 at commit fcca6490: `HookListingRegistry` 0xff8b7110525c1ec39d6df825880890e7d9184f06, `FeaturedPlacementsV2` 0x1da6f19ee08365813651de6420603fea4fbcdc9a. Job 10 puts them into `public/site-config.json` as an `ads` map by chain id and the ABIs under `src/abi`. The contracts ship unconfigured on purpose: the owner calls `configureToken(IMD)` once per chain (locked forever) and `setProceedsRecipient(address)` before any placement can be bought; the recipient is the owner's wallet 0x5bff2a2fecd1d6152676813b21346238733beab1 (decision 40), and only that address can withdraw the proceeds, which accrue inside the contract on settle and cancel. The operator's local page tools/ads-setup.html sends those four transactions from the owner's wallet. ### 6.7 Request safety: never pay for a question IMD keeps the fee once a request is on chain, refunds nothing, and lets nobody answer a builder's question after payment. Every such failure is a request that was ambiguous, contradictory or missing a fact, and the store writes every word of the request, so the store guarantees the request before payment and owns the mistake when one slips through. - **The composer writes complete requests.** Every fee sentence names the pair currency for the fee side and the settlement (5.1); every recipient is an address; every pool, token or service a block depends on is named with its address on the deploy chain, or the block is refused for that chain; IMD-paired requests say "do not convert the fee to ETH and do not use any other pool". - **A consistency check before Launch.** It runs on the composed request before the free check and before Launch and locks Launch with a red notice naming the sentence when a fee side or settlement does not match the pair currency, a recipient is not an address, a block depends on something with no address on the chosen chain, or two rules contradict each other. Unit tests compose every block under every type and pairing and assert the check passes. - **IMD's fixed facts are read.** The free check answers with the rules IMD will apply (for example that an IMD-side fee is sold for ETH unless told otherwise). When a fixed fact conflicts with the request, the "What the swarm understood" panel shows the conflict and Launch stays locked until the request opts out explicitly; the composer adds the opt-out for IMD pairs by itself. - **A blocked launch is explained, not guessed at.** The hook page shows the builder's question as IMD reports it (`blockedReason`, the failed node's `failureReason`, read through the relay) under Needs attention, with a Fix in Builder button that reopens the draft that produced the request with the question beside it. The question joins `src/blocks/known-questions.json`, which the check applies, so the same question never costs a second person. - **Plain copy.** The fee table (6.3) and the Launch page say that a request the swarm has to stop on still spends the fee, and that these checks exist to make that rare. ### 6.8 Phase 3: the proven lane; factories parked Decision 39 (2026-10-22): every launch goes through the swarm, pays 0.5 IMD and gets its own audit; the store never offers a lane that skips IMD. Speed comes from proven launches (4.17): the index updates itself, the Builder matches exactly or to the nearest proven launch, and the request pins the audited repository and commit so the swarm adapts and re-audits. Phase 3 is therefore one website job (job 12) with no contracts launch. Factories (the earlier plan: an audited factory per block set per chain, created through a contracts launch, so later hooks of that exact set are created for gas only without a swarm job) are parked, not dropped. The owner's conditions for revisiting them: a tally of the block sets people actually launch (computed from the public chain and IMD records, nothing stored) shows several sets worth a factory at once, so no factory is ever launched empty; factories get their own section of the site beside the Builder rather than a button inside it; and each use charges 0.25 IMD paid to the owner's wallet (decision 40). Token launches would never take that lane: IMD creates the token and the pool. ## 7. Data: how a static site reads everything ### 7.1 Browser access, tested 2026-10-20 `api.imd.fun` sends no CORS header on `/launches`, `/jobs`, `/intake/…`, `/requests/check` or `/publications`, so a page cannot call them. `GET /swarm`, the IPFS gateway `ipfs.imd.fun` and the public RPCs for Ethereum, Sepolia and Robinhood do allow it. ### 7.2 Catalog from chain The Intake emits `Requested(bytes32 requestId, address payer, bytes32 action, bytes body, address callbackTarget, bytes4 selector, address asset, uint256 amount)` and `Completed(bytes32 requestId, uint8 status, bytes32 resultHashOrReason, string uri, bool delivered)`. The site reads `Requested` logs for the `launch.open@launch-1` action on Robinhood Chain and Ethereum from the Intake's deployment block forward, in windows sized per chain and cached in memory and `localStorage`, decodes the body JSON, keeps only the `onchain` `univ4_hook` and `evm_contracts` bodies whose objective ends with the fixed last sentence of 5.1 (a launch made through this site) and ignores every other launch, then reads `Completed` for those ids: status 0 with an `ipfs://CID/result.json` uri → fetch it from `https://ipfs.imd.fun/ipfs/CID/result.json` for the launch id and addresses; status 1 or 2 → Failed with the reason decoded as ASCII. There is no build-time snapshot of other launches: a hook launched outside this site is never listed. Launch numbers and addresses come from `result.json`. RPCs and windows (`site-config.json`, measured 2026-10-21): Ethereum reads go to `https://rpc.mevblocker.io` for everything (logs, `eth_call`, `eth_getCode`, blocks and the ENS registry; it serves the Intake's whole history, 346 `Requested` logs from block 26164897, in one call and sends CORS headers for this site); `https://ethereum-rpc.publicnode.com` must not be used for logs because it answers HTTP 403 "Archive requests require a personal token" for any window older than about 10,000 blocks. Robinhood Chain reads go to `https://rpc.mainnet.chain.robinhood.com`, which accepts 400,000-block windows (68 `Requested` logs in total). `intakeFromBlock` is 26164897 on Ethereum (the block where the Intake's code first appears) and 85300000 on Robinhood Chain (before the Intake's first event at block 85318832, 2026-10-10 21:30 UTC); `logRange` is 100000 on Ethereum and 400000 on Robinhood Chain. `Completed` logs are read once per chain over the same windows and matched to known request ids, not walked per request. A window that fails for any reason is split in half down to 1,000 blocks before the chain is reported unreadable. On a fresh browser the status line must reach "Read from Ethereum and Robinhood Chain" within 30 seconds. Block labels on cards come from the numbered rules: the site matches each rule against the catalog's sentence templates and falls back to "Custom". ### 7.3 Status for the user's own launches Same logs filtered by payer, plus pending request ids saved in `localStorage` when launched from this site. Contracts-only launches (types B and C): the result file that the chain's Completed event points at is written when the job completes, before deployment, and names no address (launch 1, 2026-10-22). So the Completed event with status 0 is the chain confirmation that the launch finished, and the deployed addresses come from the launch record (`GET /launches/:id` through the relay, `artifacts[].address`, with `attestation.contracts` naming each contract). When the artifact is a deployer (4.16 item 1, and launch 1's `deploy(salt)` design), the hook page reads `hook()` from it and shows both addresses; while `hook()` is zero it says "hook not deployed yet". The Live step turns green when the Completed event has status 0 and the launch record reads live; the hook's address is a separate line, never a condition for Live. Links from a launch (2026-10-22, after launch 7): the notice shown after payment and the hook page both link the request's build page, `https://explorer.imd.fun/jobs/`, where the job id is the intake fate's `admission.result.jobId` that the site already reads for the Source job link; until the request is admitted the link is labelled "IMD explorer" and opens `https://explorer.imd.fun/`. The IMD explorer has no per-launch page (`/launches/`, `/launches/` and `/launch/` answered 404 on 2026-10-22; `/launch` is the list), so the hook page's "Launch page" link is replaced by the same build page link, one per job id. The notice carries no link to the page it is shown on; the request id and the Copy link of 4.17 stay. The hook contract itself is reached from the Hook row, whose address links to the chain's block explorer (etherscan.io, robin.etherscan.io) once `hook()` is non-zero. Public and not public (decision 49). Stopped: the relay's job record for the request (the intake fate's job id) has state blocked, cancelled, failed or superseded, or a node whose failure reason says needs_input, or the launch record is parked or failed. Running: the job is executing, or completed with the launch not yet live, or the launch is deploying. Young: less than 24 hours since the Requested log's block time (the timestamp 7.2 reads for the Newest sort). A requested launch is public while young and not stopped; after 24 hours only while its record says running. Failed (Completed status 1 or 2) is never public. Checking: a live type A launch whose hook code has not yet been verified on chain reads "Checking…" (status class status-checking), is public and counts as In progress in the rail; it never reads In progress. The snapshot of item (r), evidence/launch-records.json in the spec repository, read at load like the proven index, supplies the job and launch states when the relay fails, so the public rule and Live survive IMD's record plane being busy. ### 7.4 Optional relay A single-file Cloudflare Worker forwarding `POST /check` and `POST /import` to `api.imd.fun/requests/…`, and `GET /launches*`, `/jobs/:id`, `/intake/:chain/:tx` to the same paths, adding `Access-Control-Allow-Origin: *`, caching GETs 30 seconds, allowing nothing else. `site-config.json` has `relayUrl` (null by default). With it: the free check button and the "What the swarm understood" panel work live, the pond ticker names launches, the timeline shows Building / Audit panel / Deploying, and own-code imports are resolved. The swarm writes and tests it; the operator deploys it. The operator will also ask IMD to enable CORS on those routes. Job states: `GET /jobs/:id` reports each node as `waiting` (queued), `working` (in progress) or `accepted` (done), with `failed` possible, and the job as `executing` or `completed`. The timeline marks Building active while an implement node is working, Audit panel active while a review or audit node is working, and Deploying active once the job is completed and the launch is not yet live; it never shows Live from the relay alone, only from the chain. ## 8. Design system **Identity.** A retro-pixel Steam storefront on beige paper: Steam's bones (sticky bar with search, a full-bleed featured scene, a carousel, a filter rail and a dense grid, boxy panels) in a pixel skin (hard corners, 3 px ink outlines, hard 4 px offset shadows, no blur, no gradients outside the painted scene, no rounded corners). The painted pond is the one soft element and is confined to the landing. **One theme, by choice.** Beige for everyone; the site does not switch to a dark palette. Tokens: paper `#e9dfc3` (page), paper-2 `#dfd3b0` (rails, panels), paper-3 `#f3ecd8` (cards, inputs), ink `#15130f` (text, borders, shadows), ink-2 `#5b5342` (muted), ink-3 `#8a816a` (dashed rules, state pills), pine `#1e5a3a` (top bar, primary buttons, links, active states), pine-2 `#2c7a50` (hover), on-pine `#f3ecd8`, ad `#c9a227` (the #AD pill only), bad `#a3382b`, warn `#a1731c`. **Type.** Press Start 2P for the bar, headings, buttons, pills, chips and labels at 7–26 px, never for paragraphs. Reading text (paragraphs, rule lines, findings, fields, notices, the catalog cards) is Atkinson Hyperlegible 15–18 px, weight 400, bold 700 where a word is emphasised. Numbers, addresses, hashes, the exact request text, function signatures and every reading in a panel are JetBrains Mono 15–18 px, weight 400 or 500, tabular figures. Both faces are self-hosted as latin woff2 subsets beside the existing Press Start 2P file, with system fallbacks (Verdana; ui-monospace) declared. Decision 41 (2026-10-22) replaced Pixelify Sans and VT323, which the owner found hard to read at length; sizes, line heights and the layout did not change. **Components.** Button, pill, state pill (grey, wraps, 96 px max), #AD pill, input, numeric input in VT323, notice (left stripe in the semantic color), segmented control, collapsible group, card, ad card (warm paper), open-slot card (dashed), choice card, type card (pine when selected), summary list, timeline, steps, manage rows. Content column 1,180 px, 16 px gutters, one breakpoint at 820 px. The top bar never wraps above 820 px (3.0). No footer. **Copy.** Plain and short; buttons start with a verb; never "audited" for a hook that is not live; never invented numbers. ## 9. The pond, drawn in ASCII tiles on a canvas The landing background is a live ASCII scene in the manner of the open-source "ascii sharks" demo (silhouettes rasterised into a character grid, lit from above, dense glyphs in shadow and sparse glyphs in light, drawn on a canvas with no dependencies): Pepe the frog stands in a rowboat at the right, rod out over the water, line down to a hook; fish swim below the surface; every launch is a cast. The earlier painted-pond and anime-reference compositions are gone. Monochrome by choice: ink on beige, three tile shades, no colour. The technique is written for this site; no code is copied from the demo. - **Grid and tiles.** A `` fills the landing behind the copy (`aria-hidden`, pointer events pass through to the landing). Cells are 9 × 15 px. A cell holds one glyph and one of three tile shades behind it: `8` `#` `%` `@` the darkest (26% ink), `S` `&` `o` the middle (15%), `t` `:` `;` the lightest (7%); `.` `,` `'` `"` `-` `_` `|` `\` `/` `*` `J` `><>` are untiled strokes in one of three inks (ink, muted, faint). Font VT323 13 px on the canvas; never `~`, which that face draws like an N. - **Pepe.** IMD's own Pepe sprite (imd.fun; the owner chose to copy the shape) converted to a 24 × 18-cell character grid in three poses, mirrored to face the water: *idle* (side profile, frame 7 of the sheet), *windup* (turned away, frame 11) and *cheer* (arms up, frame 17). The conversion keeps the drawn outline as dark cells, the eye as `o`, skin as light tiles, shirt as middle tiles and shorts as dark `%`; the grids ship as data in `src/scene/pepe.json` exactly as in the mockup, with `meta` (width, feet row, hand cell). He stands in a rowboat whose gunwale hides his feet: a bow and stern `\` `/`, a dark `8` gunwale row, a `#` hull row and a dotted keel line; the boat spans his width plus one cell each side, anchored to the right edge with a four-cell margin. - **Water.** The surface sits at 62% of the landing height: a sparse drifting line of `-` and `.` (30% of cells, re-rolled 1.6 times a second), broken where the boat sits. Below it a faint field of `.` and `,` that gets denser with depth (1% to 6%), re-rolled every two seconds. - **Fish.** Three large fish are drawn silhouettes: a body profile (half-width `0.03 + 0.13·sin(π·u^0.75)^0.9` of the length to the tail root at 84%), a forked tail, a dorsal fin at 32–56%, a pectoral at 30–42%, an `o` eye, and a travelling wave growing toward the tail; every cell in the bounding box is sampled at five points, cells with three or more hits become skin (`#8%SSt::` by shade, lit from above), fewer become edge strokes (`'` above the spine, `.` below). Lengths 100, 136 and 172 px, speeds 22–38 px/s, turning round off-screen at a new depth. Forty-two small fish are single glyphs (`-`, or `'` `,` when rising or diving) in a flocking school: separation, alignment, cohesion, a slow random drift, flight from the pointer within 110 px and from a splash within 150 px (ink-dark while panicking), and an attraction to the hook while it waits. - **Rod, line, hook.** The grip is at chest height, one cell left and four rows above the sprite grid's `meta.hand` (a rod held at hip height with the line dropping beside him read as something else entirely). The rod runs from the grip to a tip sixteen cells left and eleven up, above head height, drawn as `_` with `\` where it steps a row. The line falls from the tip, four cells further left, to the surface and continues as `|` to the hook, `|` over `J`. Idle, the hook bobs one cell five rows under the surface. - **Cast.** `cast()` runs a timed sequence; casts queue up to three. windup 0.35 s (Pepe turns, the rod sweeps back and up) → fly 0.55 s (the rod snaps forward to eighteen cells left and six up, the hook flies on an arc of `.` to a spot 36–48 cells left of the grip); the wind-up swings the rod up and to the left of Pepe's head with the hook dangling under the tip, never across his face → splash 0.45 s (`*` then `( o )` then a fading ring at the surface) → sink 0.6 s (six rows down) → wait 1.3 s (the small fish gather) → bite 0.5 s (the hook jitters) → reel 0.6 s (hook and `><>` rise to the boat) → cheer 0.9 s (arms up, the fish beside him) → idle. About 5.2 s in all. - **Triggers.** (a) a new `Requested` log with the launch action on Ethereum or Robinhood (polled every 20 s), (b) the user's own launch transaction confirming, (c) with the relay, a new live hook, (d) a click on the water, which also splashes where it lands. Each cast updates the ticker ("{name} was launched on {chain} · {relative time}" or "A new hook was requested on {chain}"). - **Life on the left.** A fish jumps out of the water every 6–14 s somewhere in the left 60% (an arc of `><>` 5.5 rows high over 1.1 s, a splash where it leaves and where it lands); the big fish let out bubbles (`.` `:` `'` rising 11 px/s); three birds (`\v/` and `_v_` flapping three times a second) cross the top-left sky every 18–32 s; a frog sits on a lily pad at 6% from the left on the surface (`(o)(o)` over `.:SSSS:.`) and blinks every 3–7 s. - **Motion and cost.** A 30 fps timer loop (setInterval, not requestAnimationFrame, which stalls in some embeds and in headless capture), skipped while the landing is off-screen (IntersectionObserver) or the tab is hidden, resized with a ResizeObserver, device pixel ratio capped at 2; about two thousand cells drawn per frame. The first sample cast runs three seconds after load and then every eighteen seconds while the Store is open; real triggers replace the samples in job 3. Reduced motion: one still frame, no fish movement, no cast, and a `▶ animate the pond` button in the ticker that starts the loop for that visit; the ticker still updates. Phones: the same scene; the copy sits over it with its beige glow and Pepe keeps the right edge. - **Phase 2 polish.** A second boat far off, a bird or two. Still monochrome. No hat on Pepe: the sprite is drawn as in the mockup, with nothing added over his head (decision 16). ## 10. Technical choices Vite + React + TypeScript, hash routing, `base: "./"`, static export; wagmi + viem with the wallet layer reused from the polished repo, three chains; one `styles.css` of tokens; configuration only through the committed `public/site-config.json` (enabled deploy chains, payment chains, relayUrl, featuredRequestId, trade URL templates, pool-opening defaults); tests as in section 12; never fabricate addresses, launch numbers or audit results. ## 11. What to reuse From `launch-2254-build-hooks-store-polished` at `11f246f`: `src/wallet/*`, `src/lib/route.ts`, `vite.config.ts`, `tsconfig.json`, the `tests/` harness shape. From `launch-1811-build-technical-foundation-hooks` at `46e17ea`: `docs/checks/consolidated/sources/intake-abi.json` and the chain references in `deployment/manifest.json`. Phase 3 may reuse `HookListingRegistry.sol` and `FeaturedPlacements.sol`. ## 12. Acceptance criteria Store: no horizontal scroll at 320, 390 and 1440 px; the scene renders behind the copy and the copy stays readable; the top bar never wraps above 820 px with a connected address and shows no chain pill; filters and search change the grid; an unknown address query resolves on chain or says nothing was found; `/swarm` numbers appear or are hidden. Builder: first visit shows the chooser; both modes work and switching keeps words and blocks; every catalog block is addable under the right types and renders its sentence with defaults; the three launch types change the request shape; the composed text for the fixture (standard token, Swap fee 1%, Anti-sniper 10 min 1%, Buyback jar 1% 0.1 ETH burn, Robinhood Chain, pool 50%) equals `tests/fixtures/compose-A.txt` byte for byte, and likewise `compose-B.txt` (hook only, Anti-sniper, Cooldown) and `compose-C.txt` (existing token, Swap fee); the 5% rule and dependency rules are enforced; drafts survive a reload. Launch: on an Anvil fork of Robinhood with a mocked IMD balance, the three steps run in order for type A and type B bodies, the bytes sent equal the preview, the request id is parsed; wrong chain, rejected signature and zero price are covered; no live payment in tests. Hook page and My hooks: decode real `Requested` and `Completed` fixtures from both chains; Live with addresses for a real result.json fixture; Failed with the decoded reason. Chain reads: on a fresh browser against the live RPCs, both chains finish (the status line reads "Read from Ethereum and Robinhood Chain") within 30 seconds, with the window sizes and start blocks of 7.2. Blocks: `evidence/blocks-verified.json` records every block a checked real launch has exercised; the Builder reads the file at load and shows the proven mark for passing blocks and the not-yet-proven tooltip for the rest, with the fetch failure falling back to the bundled copy. Build: `npm run build` and `npm test` pass; `dist/` works from an IPFS gateway subpath; Lighthouse accessibility ≥ 95 on Store and Builder. ## 13. Delivery plan for the swarm One IMD website project, built as `job.open` then `job.continue` jobs so each job starts from the previous commit, plus one contracts launch for the phase 2 ad contracts. Bodies in `jobs/`, checked with the free `POST /requests/check` before paying. The spec and mockup live in a public GitHub repository the first website job imports with `repoUrl` + `baseCommit`. | # | Job | Delivers | |---|---|---| | 1 | Shell, Store and the pond | Top bar, routing, tokens, full-bleed landing with the character-tile scene and the describe box, the self-scrolling #ad strip with the open-slot card, Learn with the sources and the permissions table, catalog from the snapshot with working filters and keyword search, Learn, the cast animation with the click trigger, placeholders for the other routes | | 2 | Builder and composer | Launch type first, modes and chooser, palette groups and state pills, all catalog blocks with their management notes, settings, summary preview with the exact-text disclosure, composer and body encoder, draft persistence, unit tests with the three fixtures | | 3 | Chain reads, search by address, hook page, My hooks | Intake log decoding on both chains, result.json fetch, block-label matching, address resolution on chain, the Manage and Anyone-can panels built from section 4.11, pond triggers from chain, snapshot script | | 4 | Launch flow | The job 3 corrections of 7.2 (Ethereum RPC, start blocks and windows) and 3.0 (no chain pill), wallets on Ethereum and Robinhood Chain, price read, approve, request, receipt parsing, Launch page per type, pool-opening panel (copy-only in phase 1) | | 5 | Relay, polish and the review | A two-step chain: `build-website` (pairing order IMD first, worker, live free check and facts panel, stage names, import of own code, scene polish with reeds, hat and birds, copy pass) then `adversarial-review` by a different seat, read-only, of the money path from jobs 4 and 5; IMD's free check accepts the shape with no blockers and adds the design critic as well, all inside one 0.5 IMD job | | 6 | Go live | The seven findings of job 5's reviews (timeline node states, the pair-currency fee sentence, relay error text, cancelled-approval retry, reorged events dropped, import kept on full storage, ticker timestamp), then `relayUrl` set to the owner's deployed worker and `inProgress` false: the banner goes and Approve and Launch work | | 7 | Launch safety | The fee wording of 5.1 for every block and pairing, the consistency check and the fixed-facts comparison of 6.7, the builder's question on the hook page with Fix in Builder, the known-questions list, the spent-fee copy, plus the carry-overs: no hat, cancelled launch transaction retry, reorged ticker | | 8 | Catalog hardening | A two-step chain: `build-website` for everything in 4.16: the deployer for hook-only and existing-token launches, pair-currency thresholds, the Yield block by chain, the fee destinations, the Volatility fee dependency, the token definition, references on every kind, the words-mode warning and checklist, the hidden-stack and owner-footer fixes; then `adversarial-review` by a different seat, read-only, of the composed requests for every block under every type and pairing, the consistency check, the fixed-facts comparison and the deployer wording | | 9 | Proven launches and the wait | A two-step chain: `build-website` for 4.17 (the proven index, matching, the import through the relay, the request sentence, the wait copy, Copy link and the notification) plus the carry-overs (the owner lowercase regression, the written-fee detector, Live and addresses for contracts-only launches from the launch record, the residuals of job 8), then `adversarial-review` of the composer and the import path | | 10a | Ad contracts (contracts launch, phase 2) | `HookListingRegistry` and `FeaturedPlacementsV2` per 6.6, audited and deployed by IMD on Robinhood Chain and Ethereum (live 2026-10-22, launches 2454 and 2456, decision 36) | | 10 | Listings and #ad (phase 2) | Delivered 2026-10-22 (decision 42). The Promotions panel in My hooks: promote a hook (free listing, deposit, 24-hour placement), boost, cancel, withdraw; the strip reading `featured()` with creator ENS names; the hook page's #ad state | | 13 | Promotions redone, markup, RPC fallbacks | Delivered 2026-10-22 (decision 48). The balance-first Promotions panel of 3.5, ad-blocker-safe class names with the EasyList check, RPC fallback lists, the proven line for an exact stack on the other chain | | 14 | Public pond and resilience | Delivered 2026-10-23 (decision 50). The public-launch rule of 3.1 with the "Not on the pond" line and My hooks (u), the Checking… pill (v), the launch-records snapshot (r), log windows and backoff (s), findings grouped by cause (t); verified locally during IMD's domain outage | | 12 | The proven lane and the carry-overs | Delivered 2026-10-22 (decision 47). Nearest-match reuse with the request's start-from sentence, PROVEN marks, the wait line, no instant lane; Atkinson Hyperlegible and JetBrains Mono; the fluid Builder; the contracts-launch page; the Promotions balances and paying on either chain | | 11 | Builder v2 | The 3.2 layout of 1.41 from mockup/builder-v2.html: two zones, the hook card, the shelf, one line per rule, settings last, own code in step 2, findings boxed, the exact-text sheet, fixed content width; browser tests rewritten; no composer, body or relay change | ## 14. Decisions taken by the owner (2026-10-21) 1. Fonts: Press Start 2P for titles, buttons, pills and labels. Kept. Pixelify Sans and VT323 replaced by Atkinson Hyperlegible (text) and JetBrains Mono (numbers, addresses) on 2026-10-22, decision 41. 2. Pool share: no default. The user chooses it, and can change it freely within IMD's 10–90% range. 3. Trade links: Uniswap's interface on every chain where it lists the pool. No code or asset from any other product is used anywhere in this project; other sites were only looked at for ideas. 4. Going live: see section 15. Partly superseded by decision 12: there is no test-network phase, and phase 2 now means only the ads (6.6). 5. Hook-only test: one real type B launch on Robinhood Chain or Ethereum, early, paid by the owner (0.5 IMD), before any fallback is built. 6. Prize pot stays in version 1, inside the Games and lotteries group; every block with a risk field carries the warning sign of 4.13. 7. Ads: 0.1 IMD base per 24 hours with boosts. Kept. 8. GitHub: not the owner's personal account. A new GitHub account is created for the project (IMD only imports from public GitHub repositories and delivers to GitHub, so a project account is the practical home); the push happens from it. 9. Catalog scope (2026-10-21, after job 1): the store lists only hooks launched through this site, recognized on chain by the fixed last sentence of the description (5.1). Other IMD launches are not shown anywhere, not in the grid, the search results, the featured strip or the pond ticker; the pond casts for this site's launches and the practice cast. The top bar stays as the mockup draws it (its chain pill was removed later, decision 13). 10. Site name: the live site is `the-hooks-store.sites.imd.fun` (`the-hooks-store.site.identitymd.eth`). The label was free on 2026-10-21 and is claimed by job 1, so every delivery publishes there; while the site is unfinished the page says so in its banner. 11. Brand mark (2026-10-21, after job 2): the drawn pixel fishing hook of 3.0 replaces the ʃ character, which VT323 cannot draw; the owner asked for the mockup's mark or anything but the fallback glyph the live site showed. 12. No test network (2026-10-21, after job 2): Sepolia is dropped. Hooks deploy on Robinhood Chain or Ethereum from the first launch, the user chooses the chain in the Builder with no default, and testing is done with real launches on those chains; the tested marks record real launches instead of a verification round. IMD's free check accepts the sample launch bodies with chain ids 1 and 4663 with no blockers (5.3). The owner's IMD sits on Ethereum, which can pay for launches on either chain (6.2). 13. No chain pill (2026-10-21, after job 3): the owner asked to remove the ETHEREUM · ROBINHOOD pill from the top bar. The bar is the logo, the nav, the search box and Connect; the chosen deploy chain shows in the Builder's settings and summary, on the launch page and on each card. Job 4 carries the removal. 14. Pairing order (2026-10-21, after job 4): the Pair with select lists IMD first and starts on IMD, ETH second. IMD's launch policy offers ETH, IMD and, on Ethereum only, FWA as the pool pairing for a new token; the store does not offer FWA. Job 5 carries the order. 15. Go live (2026-10-21, after job 5): the owner's agent read job 5's adversarial review and the critic's findings; all seven ride in job 6, which removes the banner. The owner deploys the relay worker on a free Cloudflare account before job 6 so the free check works live from the first real launch. 16. No hat (2026-10-21, after job 5): the owner saw the pointed hat the swarm drew over Pepe and asked for it to go. Nothing is drawn over the sprite; job 7 removes the hat and keeps the far boat and the birds. 17. Treasury (2026-10-21, before the first launch, revised the same day): wherever a block offers a treasury (Swap fee and Different buy and sell fees "send to", Buyback jar "then"), the treasury is IMD's fee recipient 0x3f252e859a277a86A3967c11D4E054B2f07a1F22 on Ethereum and Robinhood Chain, the wallet IMD's docs list for the network's share of pool fees. The option reads "IMD's treasury" and shows the address; the composed sentence says "send it to 0x3f252e859a277a86A3967c11D4E054B2f07a1F22"; there is no address field. A creator who wants their own second wallet uses "my wallet" from that wallet, or the words mode. Until job 7 lands, creators choose "my wallet", because the delivered site still writes "treasury address" with no address. 18. Fee currency (2026-10-21, after launch 2 blocked): in every block sentence the fee side and the settlement currency are the pair currency (ETH for ETH pairs and for types B and C, IMD for IMD pairs), IMD-paired requests say "do not convert the fee to ETH and do not use any other pool", and "my wallet" is written as the paying wallet's address. The composer, the fixtures and the mockup follow 5.1. 19. Launch safety (2026-10-21, after launch 2 blocked): the owner asked that a user never pays twice for the same mistake. 6.7 defines the checks; job 7 builds them before the store is advertised; the owner's test launches absorb such failures first. 20. Hook-only deployment (2026-10-21, after launch 4 blocked): type B and C launches deploy a deployer contract that CREATE2-deploys the hook at a permission-bit address; the site reads hook() from the deployer. The composer says so in every B and C request. No type B or C launch is paid until job 8 ships the wording, unless its words carry the deployer sentence by hand. 21. Deep review before more spending (2026-10-21): the owner asked for every remaining way a launch could block to be found first. Section 4.16 is the result; job 8 ships it; the ads wait as jobs 9a and 9. 22. Reviews, not audits (2026-10-21): no standalone audit job is planned. Website jobs that touch the money path or the composer carry a read-only adversarial review as their second step (jobs 5 and 8); every hook launch and the ad contracts launch carry IMD's audit panel by themselves. 23. Audit-derived wording (2026-10-21): the Anti-sandwich rule caps every swap at the block's opening price in its own direction and donates the withheld difference to liquidity providers; fees are computed on the amount traded; windows start at the first swap; wallets are tx.origin with the limit stated; standalone hooks key everything by pool (4.16 items 11 to 16). 24. Independent review adopted (2026-10-21): section 4.16 is rewritten in full from the review's findings; the type A factory gate, the in-constructor salt, the revert on partial fills and the jar without burn are decided as written there. 25. Live for contracts-only launches (2026-10-22, after launch 1): Live comes from the Completed event plus the launch record through the relay; addresses from the record's artifacts and the deployer's hook(); the result file is not the source of addresses (7.3). Job 9 carries it. 26. Proven launches (2026-10-22, amended the same day): every verified launch is indexed and a draft that stacks the same blocks on the same chain is built from its audited repository automatically, in the background, with one line in the summary and no switch; a failed import falls back silently (4.17). Job 9 ships it. 27. Instant hooks (2026-10-22): for stacks with a proven launch the swarm builds an audited factory per chain and the Builder offers an instant, gas-only lane beside the swarm launch, inside the stack-blocks mode, for hook-only and existing-token hooks (6.8). Phase 3, after the ads; the open decisions are listed there. 28. Build-page links (2026-10-22, after launch 7): the owner found that "Open IMD explorer" in the post-payment notice opened the explorer's front page and "Hook page" opened the page it sat on. Both, and the hook page's "Launch page" link (a 404: the explorer has no per-launch page), now point at the job's build page on the IMD explorer (7.3); the hook contract is reached through the Hook row's block-explorer link. Rides at the top of job 10, the next website job; job 9 was already building. 29. Range orders (2026-10-22, after launch 5 passed its audit): the test token's auditors found that a buyer kept off the allowlist can still obtain the token through a one-sided liquidity position during the opening window, fee-free, and rated it low because the brief had not claimed otherwise. Rule 31 in 4.16 closes it for every buy-side block: windowed limits gate liquidity adds while active, permanent limits state the route as an accepted limit. Rides at the top of job 10 with the link fixes. 30. Same trade, same fee (2026-10-22, after launch 7 passed its audit): LilyGuard's auditors found that a swap specified in ETH paid a slightly different hook fee than the same trade specified in IMD (one basis point of the trade), and that a dynamic-fee pool would be accepted and leave LPs fee-less. Both low, both now catalog rules (4.16 items 8 and 24), riding at the top of job 10. Launch 7 also settled the open question: IMD's manifest and deployment steps accept a deployer whose constructor creates the hook (4.16 item 1); the salt search cost 1.8M to 10.9M gas in the auditors' measurements, which IMD's deployer sizes from a simulation. 31. Job 9 accepted (2026-10-22, 04:05 UTC delivery, commit 7e57a835): verified on the live site. The owner and every address go out lowercase; PondsideGuard's page reads Live with the deployer and hook addresses from the launch record; Copy link and the wait line are in place; the proven-launch line and the "Start from the audited source" sentence appear for PondsideGuard's stack once the index carries IMD's URL form (4.17). Four items from its reviewers stay open and ride at the top of job 10: the site must normalise the `.git` ending itself; the written-fee reader still writes a silent 0% for "1 pct", "1/100" and "one in every hundred", and lets an unknown payee followed by "receives", "gets" or "earns" default to the paying wallet; browser notifications watch every in-progress page the visitor opens, not only launches paid from that browser. The wait line is shortened to what the clean launches measured. 32. Ad contracts audited (2026-10-22, launch 2454 on Robinhood Chain, admitted 04:59 UTC): HookListingRegistry unchanged from the pinned upstream, FeaturedPlacementsV2 the brief's delta, 78 tests, no critical, high or medium finding. The judge kept three low notes and two informational ones, all design choices rather than bugs: a same-block purchase and cancel refunds in full and leaves a dead id that featured() still lists; a cancel right after a third-party boost refunds the base and forfeits the boost to the recipient with no ranking delivered (documented); boost() has no deadline, so a late-included boost pays in full for the seconds left; the proceeds recipient can boost at zero net cost; rankOf reports the nominal base for cancelled placements. The contracts stay as built on both chains, and the site handles the notes: the strip and the Promotions panel never render a cancelled or expired placement whatever featured() lists, and the BOOST step states that a boost is non-refundable, has no deadline and pays for whatever time remains, with a confirmation. Rides in job 10 as item 11. 33. Controls before a pool exists (2026-10-22, after the owner tried PondsideGuard's page): a hook-only hook has no pool until someone initializes one with it, and every pool-keyed reading and action (windowEndsAt, jar, buyback, per-pool claims) has nothing to read or do until then. The page showed "Reading unavailable" and "The contract refused this call" instead of saying so. Job 10 item 12: rows that need a pool say "No pool uses this hook yet" with the Send buttons off until a valid pool id is chosen; the pool selector lists pools initialized with this hook from the PoolManager's Initialize events and accepts a typed id; before a Send the page simulates the call and shows the catalog's plain reason for a refusal (nothing to claim yet, jar below its threshold, paused); and the management rows are built from the contract's own ABI at the launch's source commit, matched to catalog rows by name, so a hook built before the catalog changed (launch 2424: claimFees() takes no pool id) keeps working controls. Verified the same hour: the owner's pause transaction took effect, paused() reads true on 0x74bb…. 34. Builder v2 (2026-10-22): the owner found the Builder cramped on a large screen and the findings panel flooding the page with quoted paragraphs, and approved mockup/builder-v2.html after three rounds: two zones, the hook as a card, the shelf, one line per rule, settings last, own code as a starting point in step 2 with its fields shown only when chosen, findings boxed and one line each, the exact text in a sheet, a fixed content width and no viewport-scaled type. Nothing sent to IMD changes. Job 11, right after the ads. 35. Job 11 accepted (2026-10-22, 07:31 UTC delivery, commit e1112f9b, one review round: the design critic's six findings fixed, zero on its second pass): verified on the live site at 320, 390, 768, 1100, 1440 and 2560 px with no horizontal scroll, no clipped control and no console error; the steps in order; the own-code fields only while chosen, stashed when not, and the launch locked while they are empty; findings one line each with working jumps; the exact-text sheet with its highlights; the shelf, the stack and the card as 3.2; the composer, the body, the relay and the hook page byte-identical to job 9's commit. Three misses ride at the top of job 10: the card's details area is sized from the card's document position, so on desktop it collapses to 140 px once the sticky column is stuck and anything re-renders (182 to 140 px at 1440×900, 582 to 140 px at 2560×1300); on phones that area is a 64 px window for about 680 px of content; and the job 7, 8 and 9 browser specs were switched off instead of ported. The two sizing rules are now in 3.2. 36. First launches live (2026-10-22, 14:40 UTC): IMD's deployer stalled from 02:37 UTC, when launch 2448's broadcast failed inside IMD's Robinhood Chain RPC provider, until 14:40 UTC, with nine launches from four projects waiting; then everything deployed within three minutes. Verified on chain from the public RPCs: launch 2448 (test, TEST, type A) TestHook 0x5a9e7a61c45d2c615162fa57931bcbc06d9b20cc, TestToken 0xe7244bfb99c7ad49c5a40eaeedb215cb47dde85b, MerkleDistributor 0x29760ee79ca59cec260da5e476ef03a137fc66b6, the TEST/IMD pool (fee 1.25 percent, tick spacing 60, pool id 0xccab6575b5cdd5798731f62f9299c442f954366e91b9513386d83e249cb99843) seeded in the launch transaction; launch 2449 (ReedGuard, type B) hook 0xc97c5b51eb09961aedebea6a1eb41b62fff560cc from deployer 0x66be840c62c9ef735c2a3bdf44760514810258e4; launch 2450 (LilyGuard, type C on IMD) hook 0xb3373d1d202449c07fc6a51c5fc38483b409a0cc from deployer 0x327733d178813c3bf06d4c254522c7371bb46f83; the ad contracts of 6.6 on both chains. Every hook's owner() is the owner's wallet and no pool is paused. The test token's pool is open the moment it is live, so the owner pauses it first (pause(poolId) from the owner wallet; the hook page cannot do it until job 10's item (e), so the operator's local page tools/pause-test.html sends that one transaction). Three hooks join evidence/proven-launches.json; three site misses go to job 10 as items (e) to (g). 37. Phantom scroll gap under the findings (2026-10-22, reported by the owner from a second computer, after job 10 was paid): on the live Builder at 1920×1080 the sticky right column scrolled 243 px past the bottom of the findings box (about 350 px at 1366×768), an empty strip with its own scrollbar. Cause, found by hiding elements one at a time on the live page: the seven visually hidden `.sr` labels (position absolute) inside the card's details area and the findings list, both scroll containers without a position, take the sticky column as their containing block and sit at their unclipped static positions, up to the full height of the clipped content below; hiding them removes the gap, and `position: relative` on the scroll containers removes it too, verified on the live page. The fix rides at the top of the next job (jobs/next-job-carry-overs.md, item h); job 10 was already running. The owner also asked why the Builder does not fill a wide screen: the 1240 px cap of 3.2, approved with the Builder v2 mockup; the owner decides whether to keep it, raise it to 1600 px (the operator's recommendation: the steps column grows to about 1100 px, the card to 480 px) or let the page be fluid (item i, pending). 38. The Builder goes fluid (2026-10-22): the owner asked for the Builder to fill the screen and what normal websites do. Reading sites cap text at a measure; application dashboards (the kind of page the Builder is) fill the window with fluid columns and stop at a generous ceiling so a 4K window does not stretch controls across the whole screen. Decided: fluid up to 1800 px with scaling gutters (clamp(16px, 3vw, 48px)), the steps column taking the rest, the card clamp(420px, 28vw, 520px), paragraph text keeping its 65ch measure, type sizes unchanged. The rest of the site keeps its 1180 px reading width for now. Implemented by the next job (jobs/next-job-carry-overs.md item i), with the width sweep extended to 1920×1080 and 2560×1440. 39. Phase 3 is the proven lane, not factories (2026-10-22): the owner wants every launch to pay the swarm and burn IMD and every launch audited, and saw that a factory removes all three from every repeat of a design while a reader of code could already redeploy any audited hook. Decided: no factory for now; the proven index updates itself (scripts/proven_index.py, scheduled on GitHub Actions in this repository; tested the same day against IMD's list: it re-found launches 2448, 2449 and 2450 with the same addresses, blocks and pool id as the hand-made entries and added nothing foreign); job 12 adds the nearest match, the proven marks and the wait line. The owner's factory plan is recorded in 6.8 for later: tally first, several factories at once, their own section, 0.25 IMD per use. 40. Where the store's money goes (2026-10-22): the only income the store has is the featured placements (0.1 IMD per 24 hours plus boosts); launch fees go to IMD, hook fees go where each creator chose, listings are free. The owner chose their own wallet as the proceeds recipient on both chains (withdrawable at will, to burn or keep), and the same wallet for a factory charge should factories ever be built. Found on the way: both ad contracts were live with no token configured and no recipient set, so a deposit or purchase would have failed on the live site; four owner transactions (tools/ads-setup.html) switch them on, each simulated from the owner's address before this decision was written. 41. Readable text (2026-10-22): the owner found the site's reading font hard to read and asked to keep the pixel face for titles and bold labels with a more readable face for everything else, uniform and easy on the eye. Compared on a mockup of the hook card and a finding (today's fonts beside three pairings, every title in Press Start 2P): the owner chose Atkinson Hyperlegible for text and JetBrains Mono for numbers and addresses, a pairing built for legibility where every letter and digit has a distinct shape. Sizes and layout unchanged; Press Start 2P unchanged wherever it is used today. Job 12 ships it (carry-over j). 42. Job 10 accepted (2026-10-22, delivered 17:44 UTC at commit 9ce94388, live by 17:56; one review round: the design critic's three findings fixed, zero on its second pass; 1,167 unit, 9 relay and 252 Chromium tests, the Anvil fork flow for listing, deposit, purchase, boost, cancel and withdraw): verified on the live site: the Builder's column has no scroll range past its last box at 1920×1080 and 1440×900 and the card's details area grows after scrolling and typing instead of collapsing (339 to 486 px at 1920×1080), on a phone it is 352 px against a 338 px floor; the test token page reads Live with every address, the pool id prefilled and "Paused right now? Yes" from the chain; the LilyGuard page reads its owner and says "No pool yet; open one below"; the ReedGuard page links each job's build page and nothing to explorer.imd.fun/launches; the catalog hides the contracts launches; the featured strip reads the real contracts and shows the open-slot card; the Promotions panel asks for a wallet; the finding excerpts dropped "in the request."; the wait line reads as decided; the exact text carries the same-trade formula and the dynamic-fee rejection; "Alice receives 1%" in Describe mode raises the recipient finding and locks Launch; no six specs are ignored; no console error on any page. The critic found the phantom scroll gap on its own and the builder fixed it, so decision 37's fix shipped here and job 12 drops item (h). Carried to job 12 as item (k): the not-found page for a non-hook request shows the plain contracts-launch page only when the request is matched to the connected wallet's launches; without a wallet it says "Hook not found" with a Browse hooks link, which is a soft dead end for a shared link. 43. Promotions pay on any chain, and the panel shows the wallet's IMD (2026-10-22): the owner, holding hooks on Robinhood Chain and IMD on Ethereum, found that the delivered panel let a hook be promoted only on its own chain and showed only the contract's deposit balance ("Available balance: 0 IMD"), which read as a broken balance. Reproduced on the live site with a read-only stand-in for the owner's wallet: selecting Ethereum as the promotion chain empties the hook list ("No live hooks launched by this wallet on this chain"). The contracts already allow a listing for a hook on another chain (createListing takes the chain id). Decided: the promotion chain is where the IMD is paid; any live hook on either chain may be promoted there; the strip merges both chains; the panel shows the wallet's IMD on the promotion chain and the deposit as two labelled lines. Job 12 items (l) and (m). 44. Promotions: balance first, then hooks (2026-10-22): the owner bridged 0.1 IMD to Robinhood Chain to test the delivered panel and found the four-step "Promote a hook" flow confusing, with the deposit hidden inside its second step and a balance line that reads 0 until then. The owner proposed, and the spec now says: type an amount and deposit it, then promote, boost or cancel from that balance, each hook a row with its own button. The contracts already separate deposit from purchase, so this is a site change only. Job 13 item (n); job 12 is already running and keeps its two panel items. 45. First paid placement (2026-10-22): the owner bridged 0.1 IMD to Robinhood Chain and promoted the test token through the delivered flow: deposit at 19:16:41 UTC (0xe18b2078…), purchase at 19:17:42 (0x207962c0…), placement 1 for TestHook, 24 hours, the owner's wallet as recipient; within two minutes the Store's strip showed "#AD test $TEST by 0x5Bff…EAB1 24h left" beside the open-slot card. The end-to-end money path works on the live contracts. Found on the way: the strip's Ethereum read fails intermittently because rpc.mevblocker.io rate-limits (HTTP 429), so visitors can see "Could not read promotions on Ethereum. Retry placements" under the strip; 7.2 now requires a fallback list of public RPCs per chain (Ethereum: ethereum-rpc.publicnode.com first, rpc.mevblocker.io and eth.llamarpc.com after) with one silent retry before an error line, for every chain read on the site. Job 13 item (o). 46. Ad blockers hide the strip (2026-10-22): after the first placement went live the owner saw an empty featured band while a clean browser showed the card. Cause: the cards' class name adcard matches EasyList's generic cosmetic rule ##.adcard (line 8121 of the list on that day; ##.ad-card and ##.adslot are there too), which uBlock Origin, AdBlock Plus, AdGuard and Brave apply on every site, so both the placement card and the open-slot card were hidden for anyone running a blocker, which is most of this audience. Decided: no class, id or attribute value on the site may match a generic cosmetic filter; the strip's and the panel's elements are renamed (placement-card, placement-slot, featured-band and the like, never ad, ads, adcard, adslot, sponsored or promo as a class), the #AD label stays as text; a unit test fetches the current EasyList generic rules (or a pinned copy) and asserts that no class or id in the built site matches one. Job 13 item (p). The owner's report was right and the first reply was wrong to suggest reloading. 47. Job 12 accepted (2026-10-22, delivered 19:43 UTC at commit a551d1d5, live by 19:50; one builder pass, the design critic accepted with no high finding: 1,177 unit, 9 relay and 277 Chromium cases, 23 job 12 cases, the Anvil fork flow). Verified on the live site: Atkinson Hyperlegible for reading text and JetBrains Mono for numbers, addresses and inputs on the Store, Builder, hook page, My hooks and Learn, Press Start 2P unchanged on titles, buttons and pills, the old font files gone and both licences shipped; the Builder fills the window to 1800 px (60 px margins at 1920, centred at 2560, full width with 43 px gutters at 1440, 16 px on a phone), the card 514 px wide on large screens, no sideways scroll, no phantom scroll range; a non-hook request id reaches "Contracts launch 2454, live · Not a hook: a contracts launch for the store" with its explorer link; PROVEN pills on the four indexed hooks only; the wait line as decided; in a browser with clean storage the default stack shows "Adapted from a proven launch: TestHook (launch 2448…): the swarm removes Allowlist opening, Pause switch and adds Anti-sniper window, Buyback jar", the card says "This stack starts from audited code" and the exact text carries the start-from sentence with the repository and commit (the relay's import answers ok for all four entries); the Promotions panel with a read-only stand-in wallet shows "In your wallet on " and "Deposited for promotions" with their notes, the live placement row with rank, BOOST and CANCEL, the hook list with every live hook labelled by chain, and the Ethereum wallet balance when Ethereum is the promotion chain. Carried to job 13 as item (q): an exact stack on the other chain reads "the swarm removes none and adds none" (the critic's finding) instead of "the same blocks, redeployed on ". 48. Job 13 accepted (2026-10-22, delivered 20:48 UTC at commit 199ffd1a, live by 20:52; one builder pass, the design critic accepted; the explorer could not show the review texts at acceptance time because IMD's record plane was busy). Verified on the live site in fresh browser contexts: the strip's elements renamed (featured-band, placement-card, placement-slot), none of the 72 class names and ids on the Store matching a generic EasyList rule, the cards visible with every matching rule injected; the RPC lists in site-config.json and the fallback transport in the clients; the Promotions panel with a read-only stand-in wallet showing both balances with their notes, the earned-proceeds line, the DEPOSIT box with its approval note, one row per live hook with PROMOTE disabled and "Deposit at least 0.1 IMD first" at zero deposit, the running placement with BOOST and CANCEL, and the Ethereum balance when Ethereum is the promotion chain; the proven line and the start-from sentence for an exact stack on the other chain, offline against the delivered module. Not verifiable at the time: Live statuses, because IMD's /jobs and /intake answered 503 and 502; the job 12 build served locally behaved identically in the same minute, so the build is not at fault. Carried for a later job 14: (r) Live must not depend on IMD's record plane being up: the scheduled index script also publishes a snapshot of every store launch's record (id, number, status, chain, artifacts, request id) to the spec repository, and the site reads it when the relay's /jobs or /intake fail, so a fresh visitor sees Live during an IMD outage; (s) log windows sized to what each endpoint accepts (the publicnode endpoints refuse the current ranges with 403), a backoff on 429 instead of hopping to an endpoint that will refuse, a cap on concurrent scans, and a validation step against the real endpoints. 49. Stopped and failed launches leave the public pond (2026-10-23): the owner asked for a fast way to remove the failed test launches from the website. The Store's grid showed seven hooks: four live and proven, and three REQUESTED cards for the test launches of 2026-10-21 (WordsmithHook, ImdGuard and a second test $TEST; intake requests 0x43921ed5…, 0x4208cbe0…, 0x8c7a2972…) that IMD's builders stopped with a question. They cannot be deleted: the site reads the Intake's Requested logs, which are permanent, and the Intake never writes a Completed event for a stopped launch, so nothing marks them Failed; IMD closing them would only turn them into Failed cards. Decided: the public surfaces (grid, count, search, rail, landing list and casts, strip lookup) list only public launches as 3.1 defines them (live, in progress, or requested under 24 hours with no record saying it stopped); stopped, failed and stale requests stay in My hooks for their payer and at their direct link with a "Not on the pond" line; the live test token hook (launch 2448, paused, carrying placement 1) is live and proven and stays. No owner-maintained hide list: the rule is automatic and covers any future user's stopped launch. Job 14 carries it at the top (item u) with the Checking… pill (item v) and the carry-overs (r), (s), (t); the owner chose to pay for the job. 50. Job 14 accepted (2026-10-23, delivered 13:44 UTC at commit 956e22dc, pull request 14, site pinned as bafybeiglhncli…; one builder pass, the design critic accepted; IMD's own evidence: 1,228 unit tests and 105 Playwright cases passed, two fork cases skipped, zero failed RPC calls on a full-history scan of both chains). IMD's domain imd.fun went on client hold at its registrar (Namecheap) at 05:45 UTC, 47 minutes after the job was paid, so every imd.fun name (api, explorer, *.sites.imd.fun, ipfs.imd.fun) has answered NXDOMAIN since. IMD's servers kept running: the API answers at its numeric address and at https://api-imd.up.railway.app, whose /sites/by-label/the-hooks-store returns the new CID; but no public IPFS gateway served the file and the ENS gateway failed its certificate, so the live site could not be checked. Verified instead from IMD's delivery served locally in fresh headless-browser contexts with the stand-in wallet, with api.imd.fun, the relay's origin and ipfs.imd.fun unreachable: the grid reads 4 hooks (LilyGuard, ReedGuard, test $TEST, PondsideGuard), all LIVE and PROVEN, resolved through the launch-records snapshot and the chain; searches for Wordsmith and ImdGuard return 0 hooks and "test" returns only the live token; My hooks for the owner's wallet lists the three stopped launches as REQUESTED with "Needs attention · IMD stopped this launch" and Fix in Builder; their direct links open with "Not on the pond: IMD stopped this launch" under the header; LilyGuard's page shows Launch #2450 with its hook, token and pool addresses; no script errors; 390 px has no horizontal scroll (evidence/job-14-local-checks.txt and the job-14-local-*.png screenshots). Confirmed on the live site at 16:25 UTC, once the registrar lifted the hold (15:57 UTC) and IMD attached its site hosting to the new Railway address (16:21 UTC; the host now serves the job 14 CID): the same checks in fresh browser contexts gave the same results, with the Checking… pill observed on the test token at 2.8 seconds and LIVE at 3.0 seconds (evidence/job-14-live-checks.txt, job-14-live-*.png). Carried to job 15: (w) the contracts launches' rows in My hooks without IMD, (x) the notification offer on a stopped launch's page. 51. Live-site sweep after the outage, and the proven-lane refusal (2026-10-23, 16:30 to 17:10 UTC). The whole site was read again in fresh browser contexts after IMD moved its hosting: Store (4 hooks, the swarm metrics, the pond caption, the featured strip with the owner's placement), Learn (the seven answers, the sources, the 14 permissions in nine rows), Builder (the block groups, findings, the exact text, the free check end to end through the relay), My hooks (every row, the Promotions panel whose balances match the chain: 0 IMD on Robinhood Chain for the owner, read directly), the live hook pages (IMD's stage details are back: 3 of 3 build steps, 5 of 5 review steps, Deployment live; addresses; evidence links; LilyGuard's record carries no pool, so its page reads "No pool yet; open one below" as designed), no script errors, no horizontal scroll at 390 px (evidence/site-sweep-2026-10-23.txt). One burst of parallel reads made the primary Robinhood Chain endpoint drop 9 of 61 calls and the grid said "Could not finish reading Robinhood Chain. Showing what is cached"; a quiet reload was clean and the endpoint answered 20 of 20 direct calls. Found: IMD's free check refuses the Builder's default draft, which the proven lane adapts from launch 2448, with blocker audit_required ("a launch from your own repository starts with an audit of it as it arrived: add an audit-imported-code step before it is adapted"), because compose() sends steps build-contract-project, adversarial-review together with repoUrl and baseCommit, while IMD's docs say a repository launch runs through audit-imported-code, adapt-contract-project and the audit panel. The same body with steps audit-imported-code, adapt-contract-project, adversarial-review passes with no blockers and a plan of audit, adapt, tests, four audit specialists, judge and review (evidence/check-live-proven-lane-2026-10-23.*, evidence/check-proven-lane-steps-*.json). From-scratch launches are unaffected; own-code launches share the fault. The Builder shows the blocker once the free check is run and then locks Continue, but the check is optional (decision 19 locks Launch on the consistency check only) and the launch page enables Approve without it, so a user who skips the check could pay for a request IMD refuses and lose the fee. No proven-lane or own-code launch has been paid since the proven lane shipped, so no fee was lost. Decided: carry-over (z) leads job 15: every launch with repoUrl and baseCommit composes the audit-first steps; and, recommended to the owner for the same job, Approve stays locked until a free check with no blockers has run on the exact body being paid. ## 15. From mockup to live The mockup is a drawing of the product, not the product. The swarm builds the real site from this spec and the mockup. Here is the whole path, and who does what at each step. 1. **Publish the brief.** Done on 2026-10-21: the `spec/` folder is the public repository https://github.com/the-hook-store/hooks-store-spec under a GitHub account created for the project (not the owner's personal one). Job 1 names it with `repoUrl` and `baseCommit`. IMD accepts only public GitHub repositories as a starting point and delivers every job to GitHub, which is why a project account is needed at all. Two consequences, both already in job 1's body: IMD hosts a site built from your own repository only after a `site-content-check` step, so job 1 is a two-step chain (content check, then `build-website`); and the repository needs an `index.html` at its root, which introduces the spec and opens the mockup. IMD's free check passes job 1 with no blockers (evidence/check-job-1.response.json). 2. **Pay for job 1.** Done on 2026-10-21: paid 07:21 UTC, delivered 08:48 UTC, details in jobs/log.md. The explorer's Launch form cannot send this job: when it is given a repository it only hosts that repository as it is, and it has no way to paste a full body. So the owner pays on-chain instead, through IMD's Intake contract on Ethereum (where the owner's IMD is), from a local page Claude prepared (`tools/pay-onchain.html`, outside this repository): connect the wallet, approve 0.5 IMD, send the request. The page holds the exact checked body; its transaction encoding was verified against an independent encoder and a dry run on Ethereum. Claude cannot pay: every job and every test launch is paid by the owner's wallet, and Claude prepares the exact text each time. Within hours the swarm delivers source and a built site to a GitHub repository under IMD's launches organisation, and IMD pins the built site to IPFS and serves it at `the-hooks-store.sites.imd.fun`. That is the final name, claimed from day one; until the site is finished its banner says it is in progress. 3. **Iterate.** Jobs 2 to 6 are `job.continue` requests on the same project: each one starts from the previous commit, and each delivery replaces what is served at the site name. Claude reads every delivery, screenshots it and drives it by script, and carries any miss to the top of the next job rather than paying for a separate fix-up. There is no audit after every job: website jobs already include IMD's design critic, every hook launch gets IMD's audit panel, and the money path (jobs 4 and 5) gets one adversarial review as the second step of job 5, before the first real launch. Budget: six website jobs at 0.5 IMD plus a few continuations, so roughly 4 IMD for the site. 4. **Test on chain with real money.** After job 6 removes the banner, the owner makes real launches from the site on Robinhood Chain or Ethereum, each 0.5 IMD plus gas, paid in IMD on Ethereum or Robinhood Chain (decision 12: no test network). - *The three journeys.* One hook with a new token (type A), one hook only (type B), one hook for an existing token (type C), each from a small default stack. This proves the launch flow end to end and settles the type B address question (6.5). - *Block coverage.* There is no separate verification round. Claude checks every real launch the way 4.14 describes: the 4.11 function names are in the deployed ABI and do what the sentence says, the owner-only ones refuse other callers, and where a rule can be exercised with a swap (fees, limits, windows, cooldowns, jars) it is exercised and the effect is read back. Every block the launch used gets an entry in `evidence/blocks-verified.json` with passed true or false and notes, and the Builder shows the proven mark from then on. A failing block has its sentence or its 4.11 functions rewritten, or leaves the catalog. The Custom rule block cannot be pre-verified; the user's own launch audit covers it, and the card says so. Budget: three launches, 1.5 IMD plus gas; the owner may add the four all-blocks launches of 5.3 (2 IMD) at any time to cover every block sooner. 5. **Go live, phase 1.** The site is static files, so "live" is a hosting decision, and two options can run at once: - *IMD hosting.* Every delivery already publishes to `the-hooks-store.sites.imd.fun` and its ENS name `the-hooks-store.site.identitymd.eth`. Done on 2026-10-21 by job 6: the banner is gone and `relayUrl` is set (7.4), the owner having deployed the worker from the delivered `relay/` folder with one wrangler command on a free Cloudflare account; Claude runs that command with the owner. The old `the-hooks-store-uniswap-v4-hooks` name stays on its offline page or is pointed at the new site later. - *The owner's own domain.* Cloudflare Pages (free) connected to a fork of the delivered repository under the owner's GitHub account, building `dist/` on every push, with a custom domain such as thehooksstore.xyz. Claude sets it up with the owner in about fifteen minutes. The relay worker (7.4) lives on the same Cloudflare account. Both serve the identical build. The IMD name is the canonical, audit-trailed copy; the custom domain is the one to advertise. 6. **Phase 2.** The ad contracts are deployed by the job 10a launch (one launch per chain: Robinhood Chain, then Ethereum), job 10 wires the Promotions panel and the strip, and the same republish puts it live. Job 11 restyles the Builder (3.2, decision 34) right after (delivered 2026-10-22, decision 35). 7. **Phase 3.** The proven lane (6.8, decision 39): the self-updating index runs from this repository at no cost; job 12 adds the nearest match, the proven marks and the wait line, and carries the scroll-gap fix and the fluid Builder. Factories are parked under the owner's conditions in 6.8. 8. **After that.** Changes are new `job.continue` jobs; the owner never edits code. Operating costs are zero: no server, no database, the relay is within Cloudflare's free tier, and users pay IMD directly for their own launches and placements.