dash.foogy
entity updated 2026-07-25 live

depunks

Summary: In-house Web3 NFT project on Ethereum mainnet — ERC-721 characters + ERC-1155 traits with on-chain PRNG and equip/burn, plus a shipped agent-identity layer (ERC-8004 via Adapter8004; the OpenSea “Agents list” a.k.a. opensea-erc8217). Homepage v2 is now a simulator-first “build → price → buy → upgrade” experience (SHIPPED 2026-07-05).

What it is

DePunks is a fully in-house NFT project: contracts, the app, and tooling are all built and owned by the team (repo depunks). Base characters are ERC-721 “DePunks”; collectible traits are ERC-1155 “Attributes” sold in random packs and burned when equipped onto a character. On top of the collection sits an agent layer that promotes individual DePunks to on-chain AI-agent identities.

Stack / where it lives

  • Repo: ~/Documents/localhost/depunks (contracts + app/ + tooling; remote github.com/damir5/depunks)
  • Chain: Ethereum mainnet
  • Contracts: Solidity + Foundry, OpenZeppelin, UUPS upgradeable proxies. contracts/README.md
  • App: RedwoodSDK-style worker app under app/ (agent manifest surface, admin, metadata hosting).

Contracts (from contracts/src/*.sol and contracts/README.md)

  • DePunk.sol — ERC-721 upgradeable character NFT: blind mint, delayed reveal, ERC-2981 royalty enforcement (5%), dynamic metadata that updates when attributes are equipped. Also carries the new agent fields (agentTier, applyMetadataWithSig, metadataNonce). contracts/src/DePunk.sol
  • Attribute.sol — ERC-1155 traits (caps, eye patches, pipes) sold in random packs, burned on equip. contracts/README.md
  • Equipment system — equips attributes to a DePunk, burns the attribute, updates metadata. contracts/README.md
  • DePunkBot.sol — transferable unrevealed “Bot” line minted from DePunk base burns, revealed by linking to an upgraded DePunk. contracts/src/DePunkBot.sol
  • DePunkToken.sol — fixed-supply ERC-20 for the Phase 2 token launch. contracts/src/DePunkToken.sol
  • DePunkClaim.sol — server-signed one-time claim contract for upgrade and base-burn claims. contracts/src/DePunkClaim.sol
  • LibPRNG.sol — on-chain pseudo-random number library used for fair pack/trait distribution (with MEV considerations). contracts/src/LibPRNG.sol, contracts/README.md

Live mainnet address

  • DePunk ERC-721 proxy: 0xce7a61D155623f5Eb867a322100a5a6e2C917311 — the production proxy, referenced as DEPUNK_PROXY / PRODUCTION_DEPUNK. contracts/script/UpgradeDePunkMinimal.s.sol, contracts/test/OpenSeaTransferSecurityFork.t.sol

Agent identity — ERC-8004 + Adapter8004 (SHIPPED on mainnet)

The “agent NFT” feature is built on ERC-8004 (“Trustless Agents”), not literally “ERC-8217” (that string does not appear in the repo). The public-facing / OpenSea framing of the same idea is the Agent NFT Identity Binding — see opensea-erc8217.

  • Adapter8004 (interface IERCAgentBindings) holds the ERC-8004 identity token and delegates control to whoever currently owns the bound DePunk — the DePunk never leaves the holder’s wallet; binding is permanent and follows the punk on transfer. app/plan/agents-8004.md
  • Adapter8004 mainnet: 0xde152AfB7db5373F34876E1499fbD893A82dD336 (chain 1); Sepolia 0x7621630cB63a73a194f45A3E6801B8C6A7eC2f92. app/src/app/abi/adapter8004.ts
  • ERC-8004 Identity Registry (mainnet) 0x8004a169fb4a3325136eb29fa0ceb6d2e539a432. app/plan/agents-8004-scoring.md
  • Key functions (app/src/app/abi/adapter8004.ts):
    • register(uint8 standard, address tokenContract, uint256 tokenId, string agentURI) → agentId — called by the current NFT holder; standard 0 = ERC-721; emits AgentBound.
    • bindingOf(agentId) → (standard, tokenContract, tokenId) — view; lets anyone verify which punk an agent is.
    • setAgentURI / setMetadata revert unless the caller currently owns the bound punk (NotController).
  • The agentURI points at /agents/:slug/registration.json, hosted by the DePunks app itself — deliberately kept as an app-level kill switch over the permanent on-chain binding. app/src/app/lib/agents/identity.ts

Status (as of repo notes, 2026-06-13)

  • Binding is live on mainnet: 7 of 20 Foundation agent roles are on-chain bound + trait-inscribed (SEED #904/agentId 34421, PROOF #945, COUNCIL #7486, VAULT #951, TRADER #5345, SPEAKER #160, ARTIST #414). app/docs/foundation-agents.md
  • The broader agents plan is marked SHIPPED on the agents branch (Phases 0–7, fork-proven, Sepolia dress rehearsals). app/plan/agents-8004.md

Homepage v2 — Simulator-first “build → price → buy → upgrade” (SHIPPED 2026-07-05)

The homepage now shows the product instead of telling: a live punk simulator is the hero — pick a base + attribute slots, see a composite preview, get a live build cost, buy the missing pieces, and upgrade — followed by the story sections (Upgrade → Types → Agent promotion) above the existing data/mint boxes. Plan: app/plan/homepage-simulator.md. Component: app/src/app/components/home/HomeSimulator.tsx (wraps the existing /club/simulator internals; that route still works unchanged). Two base modes: archetype (logged out) and my-punk (wallet connected — makes “Upgrade now” real).

Pricing engine (the new core)

Pure, dependency-free pricing math in app/src/app/lib/simulatorPricing.ts runs both client-side (live rail) and in tests (app/src/tests/app/lib/simulator-pricing.test.ts). Given the selected build + the viewer’s ERC-1155 balances + the cached OpenSea listing map, it emits a per-line breakdown — OWNED (wallet has it → 0 ETH), BUY (cheapest cached OpenSea listing, ETH-denominated only), or UNLISTED (no listing; VRF-random mint means a specific trait can’t be minted on purpose, so secondary is the only deterministic source) — plus an exact wei total. Prices carry freshness (“as of Nm ago”).

  • Slot-aware base pricing (d1b69fd): a 3-slot and a 6-slot punk are different assets because upgradeMint requires the base’s slot count to equal the number of attributes equipped. Base keys are shaped female_albino (any slot count) vs female_albino:6 (exactly 6 slots), with per-key floor, listed count, total supply, and still-base count. app/src/app/lib/simulatorPrices.ts.
  • Per-attribute scarcity + full-basket buy (base punk included in the total) — 4cdf596.

Instant Buy — phased, and Instant Upgrade hand-off

  • v1 deep-links to OpenSea per missing item (plus in-app buy when the item is a Console listing).
  • v2 in-app OpenSea fulfill via a server proxy (/api/simulator/fulfill, API key server-side) reusing the existing fulfillOrder path — same one-card, one-confirm UX as Console buys.
  • v3 / phase 4 batch (40d6903): fulfillAvailableAdvancedOrders buys all missing items in one tx, partial-fill tolerant (skips vanished listings instead of reverting). app/src/app/lib/seaportBatch.ts, app/src/tests/app/lib/seaport-batch.test.ts.
  • Instant Upgrade: when everything is owned and the base is the viewer’s own punk, the CTA flips to “Upgrade now” and runs the existing signature + mintWithUpgrade flow inline.
  • Design intent: a simulator build is shaped so it can later become a bounded agent mandate (“ask your agent to watch/buy the missing pieces”) — tie-in to agents4fun / the agent layer.

Console — inline signing

The on-chain step is now signed inline in the chat rather than in the Trade tab (822e78d); create-flows are gated on a connected wallet and unsigned bundle drafts are hidden from the feed (b56cca3). app/src/app/components/console/*.

Agent Rep & Trust — reputation instead of escrow (partly SHIPPED 2026-07-07)

Rewards (raffle/quest) have no prize escrow; the owner chose not to build one, so trust is manufactured from an accountable Agent identity + a native reputation score + minus-point penalties + a 48h bad-actor flag instead of a contract holding the NFT. Full write-up: agent-rep-trust. Key points:

  • Trust gating: creating a prize reward requires operating through an Agent you own (trust-sensitive = an off-chain promise to honor); trustless actions (Seaport list/bundle, bounded a2a/mandate) and participating stay open to anyone. app/plan/agent-rep-trust.md.
  • Agent Rep replaces the displayed 8004scan number — a decayed sum (90-day half-life, floored at 0) of an append-only agent_rep_event ledger, materialised onto agent_role.agentRep for fast reads. app/src/app/lib/console/agentRep.ts.
  • Point events (REP_POINTS): honored +40, on-time (≤24h) +15, listing_published +1 (cap 5/day), listing_sold +4, participant +1 (per unique non-creator wallet, ≥3-entrant sybil gate MIN_ENTRANTS_FOR_REP=3), default (no release in 72h grace) −100 + flag, wrong-address −150 + flag, rug −30. All earn events reuse the audit’s on-chain verification (verifyPrizeTransfer/verifyListingFillOnChain) so points can’t be farmed with fake tx hashes.
  • SHIPPED slice (commit 64c8247): rep points for published listings, sold listings, entrant turnout, and rugs + the schema (agent_rep_event table; agentRep/flaggedUntil/honoredCount/defaultedCount on agent_role). Honored/on-time/default, streak multiplier, repeat-offense ×2, and the reconciler cron are drafted but not all wired.
  • Bad-actor flag: flaggedUntil = now+48h, red FLAGGED badge, blocks new trust-sensitive actions; honoring the outstanding obligation clears it early. Tiers New/Bronze/Silver/Gold/Elite gate prize-value caps, concurrency caps, discovery ranking, and fee tier.

Agent Activity tab — real interaction timeline (66a803f, 2026-07-07)

The agent’s Activity tab was rebuilt from the old wire-only feed into an honest timeline of what the agent actually DID, newest first, each row annotated with the rep points it earned (joins agent_rep_event by refId). It merges domain events (rewards run, listings/bundles drafted) with real settled fee wires and deliberately excludes “simulated” a2a wires (v1 records a console_wire with status “simulated” and the callee’s x402 price but no money moves — surfacing it as a payment would mislead). buildAgentActivity() is pure over pre-fetched rows so the merge + rep annotation is unit-testable. app/src/app/lib/console/agentActivity.ts, app/src/app/components/console/AgentActivityCard.tsx.

Console UX pass — Feed-first, on-chain Agent role, Promote-any-Alpha (a2ad5cc, 2026-07-08)

A broad console/UX + wallet-stability + performance pass (20 files, +843/−195):

  • Feed is now the first + default Console tab, with a new “Upgrades” feed tab that unifies two on-chain transitions keyed off the DePunk cache: Upgrade (base → upgraded) and Promotion (a DePunk minted into an agent + bound on-chain, labelled @handle · tier). MarketFilter gained "upgrades"; rows are derived retroactively from existing tables (has_upgraded/upgraded_at). app/src/app/lib/console/market.ts.
  • On-chain “Agent role” trait inscription: the Agent page inscribes the agent-role trait on-chain when not yet applied; Configure now holds Instructions, a Chat pill replaces Instruct, an inline for-sale row + a big Chat CTA (hidden for owners). app/src/app/components/console/AgentView.tsx, ConsoleAgentSettingsPage.tsx.
  • Promote-to-Agent for any Alpha owner: new PromoteDePunkButton on the DePunk detail page — promotion is open to every Alpha holder (server routes non-whitelisted wallets through the open Supporter tier), reusing the shared PromotionWizard; self-hides when the token isn’t a promotable Alpha for the viewer. Marketplace now shows only the connected wallet’s Agents & Alphas with links (Agent → agent page, Alpha → DePunk page). app/src/app/components/console/PromoteDePunkButton.tsx, MarketFeedClient.tsx, DePunkDetailClient.tsx.
  • Admin link in the wallet dropdown, gated to the server-derived admin wallet.
  • Wallet-connect churn fixes: getNonce preserves an existing login instead of clobbering the session; injected({ shimDisconnect: true }) + an explicit-disconnect flag stop auto re-prompt; a rejected signature keeps the wallet connected (no disconnect/reload churn); auto-login vs auto-reconnect guards decoupled. ConnectWalletButton.tsx, wagmi.ts, WalletConnectRender.tsx.
  • Speed: edge-cache + Cache-Control on /api/depunk-image, feed/market, and ticker; ConsolePage skips the per-load live-RPC Punky detail unless deep-linked and parallelizes independent reads; liveOwnersFor IN() chunked under D1’s 100-param cap; TradeClient behind a lazy guard. app/src/worker.tsx, app/src/app/pages/console/ConsolePage.tsx.

Perf + security hardening & wallet/deploy fixes (2026-07-09 → 07-10)

A large speed + security pass (kept test/prod on shared D1 for live-data testing — see depunks-env-db), then two wallet/deploy correctness fixes.

  • Security/perf audit follow-up (aec7da5): defer wagmi for anonymous visitors, embed cached image URLs directly in feed/trade JSON, add worker memory caches, materialize burn counts, enable CSP, harden SIWE rate limits + production secret checks, and migration 0100 (is_burned, fill_tx_hash unique, burn_address_count). app/src/worker.tsx, app/src/db/schema.ts.
  • Console/API speed phases (b2740b5da42c70, a0e112a, bb13b8e): kill the /api/depunk-image redirect hop, lazy-load console tabs, narrow the getMyDePunks select, dedupe + parallelize chunked D1 reads, index the fill_tx_hash replay guard, edge-cache agent-detail/giveaways/token-image routes, drop the 665KB siwe/ethers chunk on login + dedupe font loading, enable multicall batching on the standalone viem clients, and stop shipping sourcemaps + dead media in the public deploy.
  • Cron cadence split (522c176): the cron ran ~12 subsystems sequentially every 2 min (720×/day); a conservative split slows only non-critical subsystems while keeping correctness-/safety-sensitive ones frequent.
  • 0099 index migration made idempotent for remote D1 (b396b22).
  • Wallet SSR saga — route-scoped wagmi deferral was added to stop SSR 500s (18cd404), then reverted to mount wagmi eagerly on all routes (7244f65): lazy client components on “deferred” routes (e.g. HomeSimulator on / calling useAccount()) still mount client-side and threw WagmiProviderNotFoundError, blanking the page white. Deferring the provider is only safe if every consumer is boot-gated — too large an effort, so eager-everywhere for correctness (kept the boot-safe ChainMismatchWarning). app/src/app/ui/{ClientWrapper,DeferredWalletShell}.tsx.
  • Deploy target fix (974457a): the provisioned workers with secrets+bindings are depunks-test / depunks-prod; deploying to the bare depunks name landed on an auto-created empty worker missing ADMIN_WALLET_ADDRESS + 15 more secrets → Cloudflare 1101 on every request. Restored ${base}-${env} naming (config-driven, no --env, so used verbatim). app/wrangler.jsonc.

Agent Rep expansion — craft/bundle/reward events + retroactive backfill (2026-07-11)

The agent-rep-trust ledger grew from just listing/turnout/rug points into a fuller economy of small, idempotent credits so every activity row shows a rep chip, while the big honored +40 stays entrant-gated (≥3 distinct entrants) so a 1-entrant self-deal can’t farm it. New agent_rep_event types (app/src/app/lib/console/agentRep.ts, no migration — type is free-text):

  • name_changed +10 (0deb7ce): first custom name per agent (refId=slug; renames free). Wired in agentSettingsHandler.
  • metadata_inscribed +15 (0deb7ce): on-chain trait inscribed (refId=registrationId). Wired at all three inscription paths in agentTrait.ts (cheap setAgentTier, blob setTokenIdURL, holder-broadcast markTraitInscribed).
  • bundle_created +5 / bundle_executed +20 (0deb7ce): bundle-sale published / filled on-chain — additive on top of the generic listing_published/listing_sold. Gated on kind === "bundle" in activateListing/markFilled; self-buy guard covers the bonus.
  • bundle_cancelled −5 (8d9c2d9): exact reversal of bundle_created when a LIVE bundle is cancelled (pending_signature never earned it). A created→cancelled net of 0 shows no rep chip (not “+0”).
  • reward_created +5 / reward_released +5 (d09f445): small UNGATED base credits so 1-entrant releases (which the anti-sybil gate zeroes) still show a chip; the +40 honored bonus stays entrant-gated. Wired via emitRewardCreated (arm) + emitRewardReleased (before the entrant gate).
  • Retroactive backfill (d81a263): backfillActivityRep() awards all the new events for pre-existing activity (named agents, applied inscriptions, published/filled/cancelled bundles, armed/released rewards) — idempotent per refId, exposed as admin-only POST /api/console/admin/backfill-rep (one run backfills the shared test+prod D1 — see depunks-env-db).
  • Activity feed now emits standalone “Agent named” + “Trait inscribed” rows straight from the ledger and folds the additive premiums into the bundle/reward rows (e.g. listing_created = published+created+cancelled). New icons in AgentActivityCard.tsx. Suite green (710 passed). app/src/app/lib/console/{agentRep,agentActivity,giveaways,listings}.ts, app/src/app/lib/agents/agentTrait.ts.

Supporter tier fully public — promote every Alpha (2026-07-11, 07f074b)

The 2026-07-08 “Promote-any-Alpha via the open Supporter tier” note is now uncapped: MAX_SUPPORTER_PER_WALLET and both enforcement points (the eligibility gate in getFoundationPromotionContext + the count check in startPromotion) are removed, so an Alpha holder can promote every one of their unbound Alphas, not just 3. The concurrency-safe deterministic slot assignment (supporter_claim_wallet + partial unique index) is kept but unbounded. Unchanged: one-agent-per-punk hard lock, Alpha-only requirement, curated whitelist tiers. supporterCapReached stays in the DTO (always false) for consumer stability; no migration; suite green (703). app/src/app/pages/club/foundationFunctions.ts.

  • UX: the per-token Promote-to-Agent CTA moved from the Details button grid into the title row next to the status pill, rendered as a bare Sparkles icon+text link, right-aligned (ml-auto) and colored blue (agent accent) with a pink hover (865eeb4, fd8d0ab). app/src/app/components/DePunkDetailClient.tsx.

Holder / wallet OG card redesign — square adaptive grid + change-highlight (2026-07-11, a657157)

The /holder OG + X-post image (holderCard.ts) was redesigned from stacked base/agents/upgraded sections into one square (1200×1200) adaptive grid of all holdings: tile size is computed to fill the centre, so a small wallet gets big tiles and a large one gets smaller ones (capped 6×6, “+N more” beyond). Each tile carries a marker dot — blue #2563eb = agent, pink #d646ab = alpha. A new highlightTokenIds param renders affected tiles at 2× and floats them to front — the render layer for a “something changed” X-post variant (wiring the upgrade/promote post to pass affected ids is the noted next step). R2 cache key bumped to -v2 so old landscape cards re-render. (An earlier same-day attempt reshaped the in-app Download/Selection grid DePunksListClient instead, c6980b8; that was reverted — the export grid was fine.) app/src/app/lib/holderCard.ts, app/src/tests/app/lib/holder-card.test.ts.

Zero-touch auto-inscribe + OpenSea refresh hardening (SHIPPED 2026-07-13, d77c939)

Promotion is now fully automatic end-to-end so a holder no longer has to manually inscribe the agent trait or wait/nudge for the marketplace to catch up. This landed as commit d77c939 (2026-07-13, +360/−44 across 13 files) — the previously-uncommitted 2026-07-11 working tree, plus a desync-hardening layer on top:

  • Auto-inscribe on AgentBound (agentEventSync.ts): after an AgentBound confirmation the app now fires the inscribeAgentTraitCore(registrationId) path async (manager wallet pays the tiny gas), so the on-chain Agent: <Tier> trait lands automatically — zero-touch for the holder, idempotent, safe to re-call. Gated by a new AGENT_AUTO_INSCRIBE_ENABLED env flag (default true; set "false" to disable for testing/staged rollout — manual inscribe + admin force-refresh stay available). Flag added to the serverConfig schema.
  • Refresh with retries (openseaApi.ts): requestOpenseaMetadataRefresh now retries the OpenSea .../refresh POST up to with increasing backoff (2s, 4s), because the OpenSea indexer lags right after a tx mines. Non-fatal as before; logs attempt/attempts on the outcome.
  • nudgeOpenseaRefresh now double-taps (agentTrait.ts): exported and, after the direct refresh POST, also kicks the agent-specific reconciler reconcileAgentOpenseaMetadata([token], chainId, {force:true}) — it GETs OpenSea’s current view and re-POSTs if the Agent trait is still missing there. Immediate second chance for stragglers.
  • Nudge on any TokenURIUpdated (eventSync.ts): when a TokenURIUpdated event fires for a confirmed Foundation agent token, best-effort nudgeOpenseaRefresh (fire-and-forget) — covers auto-inscribe, legacy setTokenIdURL, and any future metadata writes on bound punks. Never lets a nudge failure affect event processing.
  • Admin “Force OS Refresh” (agentRoleFunctions.ts + AdminFoundationManagement.tsx): a new admin-only forceAgentOpenseaRefresh(registrationId) server fn (confirmed-only) runs the retrying refresh + the forced reconciler, surfaced as a per-registration Force OS Refresh button in the Foundation admin panel. Unaffected by the auto-inscribe flag.
  • Desync-hardening added at commit time (the part that wasn’t in the 07-11 working tree): hasAgentTrait / isAgentTraitInscribedOnChain are now lenient — they check for trait presence, not an exact tier value, so supporters and manual inscriptions don’t read as “missing”; the reconciler uses the same presence check to avoid false resets. A new healAgentTraitFlags() cron healer (slow cadence, scheduledHandler.ts) plus an opportunistic self-heal of metadataTraitApplied in getRegistrationFacts fix the console/agent + club views when a trait is on-chain but the DB flag is stale-false. Addresses the desync where agents showed inscribed on OpenSea but the app still prompted for inscription.

Manual prize-send desync fix — mark-sent + ownership pre-checks (2026-07-13, ffa3816)

Fixes a giveaway UI desync where a creator transferred a prize NFT manually (Etherscan / direct wallet call / after a failed in-app send) but the card still showed “Send prize” because the app’s /released step never ran. In TradeClient.tsx (+89): the ERC-721 ABI gains ownerOf for pre-flight checks — the Send-prize button reads the current on-chain owner before transferFrom, and if the prize is already with the winner it routes the creator to the detail modal, or if the wrong wallet holds it shows a clear error instead of a raw revert. When a “won” reward isn’t yet released, the GiveawayDetailModal now shows a tx-hash input + “Mark as sent” (releaseTxInput + markReleasedManually) so a manual transfer can be recorded. app/src/app/components/console/TradeClient.tsx.

Motion pass — transitions-dev micro-transitions + marketing PDFs (2026-07-20)

A design-quality pass that spreads a single, disciplined micro-animation system across the Console, plus a marketing-asset drop. Landed as a run of commits 97d0c40cf56ca7 (2026-07-20).

  • transitions-dev in-repo skill (03d6a44): twelve portable, framework-free CSS transitions (card resize, number pop-in, notification badge, text swap, dropdown, modal, panel reveal, page side-by-side, icon swap, success check, avatar-group hover, error shake), each namespaced under t-* with semantic CSS custom properties and a prefers-reduced-motion guard. Lives under app/.agents/skills/transitions-dev/ + app/skills-lock.json; rollout tracked in app/docs/transitions-rollout-plan.md.
  • Design discipline (the reason it doesn’t feel bouncy): one shared :root token block in src/styles/tailwind.css (components never hardcode a duration/ease — they read --* names, tune once); reduced-motion collapses every animation to 0.01ms globally; single-accent discipline — pink stays the interaction colour, green/red keep status meaning only and are never animated for decoration; “don’t double-animate” surfaces that already move; skip data-dense admin tables / long lists.
  • Shipped surfaces: Phase 1 — modal scale in/out (Composer overlay, GiveawayDetailModal), icon swap (theme toggle sun↔moon, Explore list↔grid), text swap (copy→copied, Send→Sent). Phase 2 — number pop-in on giveaway entrants/entries + mint progress count (skipped the moving ticker + static legacy leaderboard by design). Phase 4 — success check on the ActionWizard Done card, error shake on the wizard, and a hover “comb” on the main-nav TabSwitcher. Reusable primitives seeded in src/app/components/ui/: useModalPresence, IconSwap, TextSwap, PopNumber, SuccessCheck, useShake, useHoverComb.
  • Sliding-pill segmented tabs (97d0c40, 0bcf7bf): AgentView got a sliding-pill segmented tab switcher + avatar-group comb-hover (the reference implementation), and the Console main TabSwitcher got the same sliding pill.
  • Deferred (Phase 3, tokens seeded but not wired): full page-side-by-side (needs the Console nav restructured — the agent directory → agent page is the “marquee” one) and card-resize (can’t tween to height:auto without extra measuring machinery); both want visual verification.
  • Marketing PDFs (cf56ca7): DePunks-CMO-Brief.pdf, DePunks-One-Pager.pdf, and Agents4Fun-CMO-Brief.pdf served statically at /assets/pdf/ (app/public/assets/pdf/). Note the DePunks repo also carries the agents4fun CMO brief.

OpenSea collections (slugs)

  • depunks-club — main ERC-721 collection. app/migrations/0034_backfill_depunks_opensea_slug.sql
  • depunks-traits — the ERC-1155 Attribute collection. app/migrations/0035_married_nemesis.sql
  • depunks-bots — the Bot collection. app/src/app/components/club/BotsBox.tsx

Decisions & lessons

  • 2026-07-05: Homepage rebuilt simulator-first — the pricing engine is pure/testable (simulatorPricing.ts) and slot-count is treated as identity (a 3-slot ≠ 6-slot punk, because upgradeMint demands slots == equipped attrs). Batch buy (fulfillAvailableAdvancedOrders) is partial-fill tolerant so vanished listings don’t revert the whole tx.

  • 2026-07: The team’s own naming inside the repo is ERC-8004 + Adapter8004; “ERC-8217” is the OpenSea-facing label for the binding standard, tracked separately in opensea-erc8217. Don’t conflate the two in code references.

  • 2026-07-07: Trust-without-escrow — chose reputation + penalties + a 48h flag over a prize-escrow contract for giveaways; rewards gated to Agents. See agent-rep-trust.

  • 2026-07-07: D1 100-bound-param cap gotcha (recurring): Cloudflare D1 rejects a query with >100 bound params, so every unbounded IN(...) list must be chunked ≤100. Bit the holder listing-cache (f52b7d0) then every other unbounded IN() across botCache/communityTraits/console listings/depunkCache/simulatorData/upgradeUtils/trait allocation (20e1c07). Default to chunking any IN() built from a variable-length array. app/src/app/lib/holderActivity.ts et al.

  • 2026-07-08: Console made Feed-first + promotion opened to all Alpha holders (a2ad5cc) — the Feed (with a retroactive “Upgrades” tab merging base→upgraded Upgrades and agent-bound Promotions) is now the default console surface, the agent-role trait is inscribed on-chain, and any Alpha owner can Promote-to-Agent via the open Supporter tier. Same commit hardened wallet-connect against session-clobber/churn and added edge-caching + lazy/parallel reads for speed.

  • 2026-07-07: Worker input/fetch hardening (14eb4ba) — tightened input validation + fetch boundaries across the console handlers (agentChat, mandate/post/talk/skills handlers, mcpServer), apiInput.ts, ogImage, and OpenSea fulfillment/upgrade paths. app/src/tests/app/lib/api-input.test.ts.

  • 2026-07-09: Speed + security audit executed as one pass (aec7da5 et al.) — CSP enabled, SIWE rate limits + prod secret checks hardened, wagmi deferred for anon visitors, worker memory caches + materialized burn counts, and a wave of D1/edge-cache/bundle-size wins (drop 665KB siwe/ethers login chunk, multicall batching, cron cadence split from 720×/day). Migration 0100 added is_burned / fill_tx_hash unique / burn_address_count.

  • 2026-07-10: Don’t defer the wagmi provider (7244f65) — since wagmi hooks are used in client components on essentially every route, a route-scoped provider deferral blanks pages white (WagmiProviderNotFoundError after hydration, e.g. HomeSimulator.useAccount()). Deferral is only safe if every consumer is boot-gated; not worth it → eager providers everywhere.

  • 2026-07-10: Deploy to ${base}-${env}, never the bare worker name (974457a) — secrets/bindings live on depunks-test/depunks-prod; deploying to bare depunks hits an empty auto-created worker missing ADMIN_WALLET_ADDRESS (+15 secrets) → CF 1101 on every request. Confirmed/fixed via wrangler tail.

  • 2026-07-11: Rep economy broadened, honored bonus kept scarce (0deb7ce/d81a263/8d9c2d9/d09f445) — added small idempotent credits (name +10, inscription +15, bundle +5/+20, bundle cancel −5, reward create/release +5 ea, all UNGATED) so every Activity row shows a chip, but the +40 honored bonus stays entrant-gated (≥3) so 1-entrant self-deals can’t farm it. Retroactive backfillActivityRep() is idempotent per refId (admin POST /api/console/admin/backfill-rep). See agent-rep-trust.

  • 2026-07-11: Supporter tier is now uncapped (07f074b) — dropped MAX_SUPPORTER_PER_WALLET; any Alpha holder can promote all their unbound Alphas. Supersedes the “3 per wallet” limit implied by the 2026-07-08 open-Supporter note. One-agent-per-punk lock unchanged.

  • 2026-07-13: Promotion goes zero-touch (d77c939, SHIPPED) — auto-inscribe the agent trait on AgentBound (manager pays gas, AGENT_AUTO_INSCRIBE_ENABLED kill switch) instead of relying on a manual holder step, and stop trusting a single OpenSea refresh call: retry the refresh 3× with backoff, double-tap with the forced agent reconciler, nudge on every TokenURIUpdated for confirmed agents, and add an admin Force OS Refresh button. Landed with a desync-hardening layer: presence-based trait checks (not exact tier) in hasAgentTrait/reconciler so supporters/manual inscriptions don’t false-reset, a healAgentTraitFlags() cron healer, and opportunistic metadataTraitApplied self-heal in getRegistrationFacts. (Supersedes the earlier “uncommitted working tree” note.)

  • 2026-07-13: Manual prize sends are reconcilable (ffa3816) — added ownerOf pre-flight checks before transferFrom and a tx-hash “Mark as sent” path so a giveaway prize transferred outside the app (Etherscan / direct call / after a failed send) can be recorded, killing the “Send prize” card desync. See agent-rep-trust.

  • 2026-07-14: Trait self-heal must also award rep (08baccb) — the +15 metadata_inscribed rep and the “Trait inscribed” activity row are the same event (the feed derives the row from agent_rep_event), but two self-heal write paths flipped metadata_trait_applied=true without awarding it, so agents inscribed via those paths showed ✓ inscribed with an empty ledger (no points, no activity line). Both self-heal paths (getRegistrationFacts page-load + healAgentTraitFlags cron) now call the idempotent inscription-rep award; ~9 diverged prod agents recovered by re-running the admin backfill. See agent-rep-trust.

  • 2026-07-17→18: Holder “wallet card” reworked into a text-free full-bleed grid (697e64c6deb354) — the square 1200×1200 holder OG card dropped its labelled rows and the agent/alpha dots + change-highlight (added 2026-07-11) in favour of a single full-bleed grid capped at 8×8 = 64 cells (120px padding), the last cell a lightened “+N more” badge when a wallet exceeds the cap; tile size auto-scales to fill (fewer holdings ⇒ bigger tiles). Tiles now read base-body/attribute images from R2 in-process (512f138) rather than re-fetching, staying inside the Worker subrequest/CPU budget; results still cached in R2 keyed by a holdings hash that self-invalidates on any transfer/upgrade/promotion. Serves the /holder/:address OG card + the automated X promotion/upgrade posts. app/src/app/lib/holderCard.ts.

  • 2026-07-18: X sale posts attach the buyer’s wallet card as native media (b24698b) — automated sale-announcement tweets now upload the buyer’s composite holder card as native media (mirrors the promotion/upgrade posts). Best-effort: any upload failure still sends the post text-only, whose link preview carries the OG image. app/src/app/lib/xSalePosts.ts.

  • 2026-07-17: “Mark as sent” accepts a pasted explorer URL (fa4b627) — the manual prize mark-sent input (added 2026-07-13) now pulls the first 0x…-prefixed 64-hex run out of whatever’s pasted, so an etherscan.io/tx/0x… URL works, not just a bare hash. app/src/app/components/console/TradeClient.tsx.

  • 2026-07-20: Motion pass via a disciplined transitions-dev system (03d6a44cf56ca7) — twelve reusable CSS micro-transitions behind one :root token block, rolled out in phases (1/2/4 shipped: modals, icon/text swaps, number pop-in, success check, error shake, hover-comb + sliding-pill segmented tabs; phase 3 page-slide/card-resize deferred pending nav restructure + visual verification). Kept restrained on purpose: single-accent (pink) discipline, global reduced-motion, no double-animation, skip long lists. Also dropped marketing PDFs (DePunks-CMO-Brief, DePunks-One-Pager, Agents4Fun-CMO-Brief) at /assets/pdf/ (cf56ca7). A good design→code portfolio artifact — see foogy-dev.

  • 2026-07-24→25: Agents transact on their own (f38ec48cbcfb0a) — the agent layer went from identity/reputation to spending real money: x402_fetch / a2a_call pay via EIP-3009 signed by the agent’s Turnkey wallet (payer stays gasless, payee’s facilitator broadcasts), and a create-whitelist skill lets an agent create an allowlist on agents4fun as the artist (first agent-broadcast tx). Every paid tool is owner-only, capped per-call + per-turn, and writes a console_wire audit row for paid and refused decisions; unknown chain / non-USDC / over-cap all hard-refuse. See agent-x402-payments.

Agents spend real money — x402 payments + a2a settlement + cross-app allowlist creation (SHIPPED 2026-07-24→25, f38ec48cbcfb0a)

The agent layer crossed from identity/reputation into agents transacting on their own: a DePunk agent can now pay external services, settle with other agents, and create an allowlist on agents4fun using its own Turnkey wallet. This is the depunks half of the agent-x402-payments bridge.

  • Real x402 payments (f38ec48, app/src/app/lib/agents/x402Pay.ts): two tools replace the old “simulated” placeholders. x402_fetch pays an external x402 endpoint and returns what it serves; a2a_call settles real USDC between two agent wallets. payX402Request signs EIP-3009 with the agent’s Turnkey wallet and the payee’s facilitator broadcasts, so the payer stays gasless (Base mainnet, no facilitator of ours). Handles both x402 v1 terms and the v2 header + CAIP-2 network form live services emit. Fences on every paid tool: owner-initiated only, host allowlist, hard per-call cap, per-turn budget, and a console_wire row for every decision — paid or refused, each carrying its reason. Refuses (never guesses) on unknown chain, non-USDC asset, malformed amount, over-cap price; fixed a latent bug where eip3009TypedData fell back to chainId 8453 for unrecognised networks.
  • Outbound payments turned on in test + prod (5556dcc); X402_ALLOWED_HOSTS gains wildcard entries (9ec8684) and now covers *.agents4.fun + *.depunks.club alongside the Bazaar discovery host (81280c7) — the depunks.club wildcard lets one agent pay another DePunk agent’s own /agents/:slug/x402 endpoint (prod depunks.club, test test.depunks.club).
  • Agents create allowlists on agents4.fun (e031694): new catalogue skill create-whitelist (rung P3, Market-floor reputation) grants whitelist_fees (reads live setup + entry fee) and create_whitelist (signs the EIP-712 wallet intent → agents4.fun counter-signs → broadcasts from the agent’s own wallet). The agent must be the artist (msg.sender == artist), so this is the first path where an agent wallet broadcasts a transaction via turnkeySignTransaction rather than handing off a gasless authorization. Spends real ETH → carries the full x402 fences (owner-only, one create per turn, live fee vs. a hard ceiling and the wallet balance before signing, console_wire per decision). The intent types / paramsHash / hook-config layout are hand-mirrored from agents4.fun’s @aw/intent into app/src/app/lib/agents/agents4fun.ts + app/scripts/agents4fun-create-wl.mjs; a test diffs the worker copy against the CLI copy every run so the two can’t drift silently.
  • Wallet provisioning on skill acquisition (359b019): acquiring a skill that grants x402_fetch / a2a_call / create_whitelist now provisions the agent’s Turnkey wallet — acquiring the skill is the wallet request. ensureAgentWallet (agentWallet.ts) is the single write path, shared with the admin buttons (kills the risk of a second path omitting x402Wallet). Best-effort: a Turnkey failure doesn’t roll back the grant; the admin batch and next acquisition retry, paid tools refuse cleanly meanwhile; the detail page reports the new address so the owner knows where to send USDC. Skills now gate on repPredicate, not rung (8d34e4e).
  • Agents-first Console shell (cb00b85, 2b06ba3): Agents is now the first/default Console tab (Agents / Marketplace / Feed / Skills). A new AgentHomePanel.tsx (“Have your agent do something” + “My Agents & Alphas”) moved to the top of the Agents tab, replacing the old Promote-an-Alpha panel (PromoteAlphasPanel.tsx deleted) and vacating Marketplace (now just giveaways + listings). The agent directory shows only agents the viewer does not own (own agents live in the home panel — no duplication); each directory entry holding the pay-x402-service skill gets an “x402” chip (hasX402Skill, one indexed read), and agents that can open allowlists are labelled too.
  • Skills tab shows Offers + Acquired (7ee05a9): the tab previously read only skillsOf(role) (the ERC-8257 manifest — what the agent offers), so a just-acquired catalogue skill looked like it vanished. Now shows Offers (manifest → tool JSON) and Acquired (catalogue → skill page) under separate headings; the Overview chip row merges both deduped by slug. Learning a skill earns +5 rep, idempotent per (agent, skill) via the (agentSlug, type, refId) unique index (dropping + re-adding pays nothing).
  • Trait-inscription fix — stop dropping inscriptions (cbcfb0a): found by measuring mainnet + the prod DB, 24 of 82 confirmed agents had no Agent trait and no tx hash — nothing was ever attempted. On confirmation the code fired the inscription as a floating (un-awaited) promise so the holder got an instant response, but in a Worker an un-awaited promise dies with the isolate when the request finishes first; healAgentTraitFlags couldn’t help (it only fixes flags for traits already on-chain). Three fixes so the holder isn’t the only safety net: a cron sweeper (inscribes the backlog 3 per slow tick, sequential — one manager wallet, one nonce), an admin batch button (clears 10 on demand, names failures), and holder copy explaining what’s missing / unchanged / the cost. Separately, skills.json served an empty list for Speaker/Trader/Critic + 16 other roles while those agents ran a full tool set (only hand-written agent_role.skills was published, never the catalogue skills) — now fixed.
  • Worker session fix (926f917): authed routes served under public prefixes now load the session correctly (previously dropped). Backed by new tests: agent-x402-fetch.test.ts, agents4fun-create.test.ts, console-a2a-settlement.test.ts, security-audit.test.ts, viewer-paths.test.ts.

Open questions

  • Only 7/20 mainnet roles are confirmed bound; the other 13 have souls/wallets but no recorded on-chain binding. app/docs/foundation-agents.md
  • Base mapping gap: a Base Adapter8004 address is mentioned in prose but NOT wired into the app config (ADAPTER8004_ADDRESSES maps only mainnet + Sepolia). app/src/app/abi/adapter8004.ts
  • Mainnet UUPS upgrade + pre-upgrade audit still pending per app/docs/foundation-agents.md.

⚠️ needs reconciliation (flagged by weekly lint 2026-07-12): the two “gaps” above conflict with opensea-erc8217, which records foogy’s 2026-07-05 correction that (a) on-chain binding is complete on production — the 7/20 in app/docs/foundation-agents.md is only the stale local repo view — and (b) Base is not a network DePunks uses, so the missing ADAPTER8004_ADDRESSES Base entry is not a gap. This page (updated through 2026-07-11) still lists both as open gaps sourced to the repo docs. Not silently resolving: foogy to confirm whether prod binding is fully done (drop the 7/20 open item) and whether Base is out of scope (drop the Base item), or whether the repo docs are right and the opensea-erc8217 corrections were premature.

Sources

  • ~/Documents/localhost/depunks/contracts/README.md, contracts/DEPLOYMENT.md, contracts/src/*.sol, contracts/script/UpgradeDePunkMinimal.s.sol
  • ~/Documents/localhost/depunks/app/plan/homepage-simulator.md, app/src/app/components/home/HomeSimulator.tsx, app/src/app/lib/simulatorPricing.ts, app/src/app/lib/simulatorPrices.ts, app/src/app/lib/seaportBatch.ts, app/src/tests/app/lib/{simulator-pricing,seaport-batch}.test.ts
  • ~/Documents/localhost/depunks/app/plan/agents-8004.md, app/plan/agents-8004-scoring.md, app/docs/foundation-agents.md, app/docs/agents-features.md
  • ~/Documents/localhost/depunks/app/src/app/abi/adapter8004.ts, app/src/app/lib/agents/identity.ts
  • ~/Documents/localhost/depunks/app/plan/agent-rep-trust.md, app/src/app/lib/console/{agentRep,agentActivity,giveaways,listings}.ts, app/src/db/schema.ts, app/src/app/components/console/AgentActivityCard.tsx, app/src/tests/app/lib/{agent-rep,agent-activity}.test.ts
  • ~/Documents/localhost/depunks/app/src/app/lib/agents/agentEventSync.ts, app/src/app/lib/agents/agentTrait.ts, app/src/app/lib/openseaApi.ts, app/src/app/lib/eventSync.ts, app/src/app/lib/serverConfig.ts, app/src/app/lib/console/agentDetail.ts, app/src/scheduledHandler.ts, app/src/app/pages/admin/agentRoleFunctions.ts, app/src/app/components/AdminFoundationManagement.tsx (auto-inscribe + OS-refresh hardening + trait-flag healer — d77c939 2026-07-13; rep self-heal 08baccb 2026-07-14), app/src/app/components/console/TradeClient.tsx (manual prize mark-sent + ownerOf pre-checks — ffa3816 2026-07-13; pasted-URL mark-sent — fa4b627 2026-07-17)
  • ~/Documents/localhost/depunks/app/src/app/lib/holderCard.ts (text-free full-bleed 8×8 wallet card + R2 in-process tiles — 697e64c6deb354 2026-07-17→18), app/src/app/lib/xSalePosts.ts (buyer wallet card as native media — b24698b 2026-07-18), app/src/tests/app/lib/holder-card.test.ts
  • ~/Documents/localhost/depunks/app/.agents/skills/transitions-dev/ (12-transition CSS system), app/docs/transitions-rollout-plan.md, app/src/styles/tailwind.css (:root token block), app/src/app/components/ui/{useModalPresence,IconSwap,TextSwap,PopNumber,SuccessCheck,useShake,useHoverComb}, app/src/app/components/console/{AgentView,TabSwitcher,ActionWizard,TradeClient}.tsx (motion pass — 97d0c400bcf7bf 2026-07-20), app/public/assets/pdf/*.pdf (marketing PDFs — cf56ca7 2026-07-20)
  • ~/Documents/localhost/depunks/app/src/app/lib/agents/{x402Pay,agents4fun,agentWallet,turnkeyWallet,identity,agentTrait}.ts, app/src/app/lib/console/{tools,skills,agents,agentDetail,agentRep}.ts, app/src/app/components/console/{AgentHomePanel,ConsoleShell,AgentView,ExploreClient,SkillDetailClient}.tsx, app/src/app/lib/serverConfig.ts, app/src/app/lib/viewerPaths.ts, app/src/scheduledHandler.ts, app/src/worker.tsx, app/wrangler.jsonc, app/scripts/agents4fun-create-wl.mjs, deleted PromoteAlphasPanel.tsx; tests app/src/tests/app/lib/{agent-x402-fetch,agents4fun-create,console-a2a-settlement,security-audit,viewer-paths,agent-trait-inscribe}.test.ts (agents spend real money: x402 payments + a2a settlement f38ec48/5556dcc/9ec8684/81280c7, wallet provisioning 359b019/8d34e4e, cross-app create-whitelist e031694, Agents-first shell cb00b85/2b06ba3, Offers+Acquired skills 7ee05a9, trait-inscription fix cbcfb0a, worker session fix 926f917 — 2026-07-24→25; local depunks, branch dev)
  • Related: agent-x402-payments
  • Related: opensea-erc8217, agents4fun, agent-rep-trust