Version History
Early Access · Public Development Log
GUNZscope is under active development. This page tracks every meaningful release since the first commit. Features ship fast — if something’s missing, request it.
v0.8.1
CurrentMar 14, 2026
- Cross‑wallet favorite labels — PinnedFavoritesRow now shows source wallet label (from portfolioAddresses) with title attr for full address; falls back to truncateAddress(), then "other wallet" for unresolved stubs
- Favorite metadata now stores walletAddress — FavoritePinActions passes walletAddress into FavoriteButton metadata; usePinnedFavorites stub path reads fav.metadata.walletAddress instead of hardcoding "unknown"
- walletLabels prop — PortfolioClient builds Map<string, string> from portfolioAddresses; threaded through PinnedFavoritesRow for label‑over‑address display
- NFTGalleryGridCardProps.walletAddress — new optional prop threaded from NFTGallery → NFTGalleryGridCard → FavoritePinActions for metadata capture
- Matrix decode entrance animation — CSS keyframes: matrix‑scanline (lime sweep), matrix‑card‑decode (clip‑path + scaleY + blur + brightness), matrix‑rain‑col (vertical lines), matrix‑label‑decode (letter‑spacing + blur flicker), matrix‑container‑enter (inward glow), matrix‑divider‑decode (scaleX sweep)
- DecodeText component — character‑by‑character scramble effect using requestAnimationFrame interval; resolves left‑to‑right with randomized probability; 18‑frame cycle (~300ms)
- MatrixRain component — generates N random vertical gradient lines with staggered delays/durations; positioned absolutely within matrix‑container
- Animation gating — hasAnimated ref in PinnedFavoritesRow prevents replay on re‑renders; prefers‑reduced‑motion media query disables all matrix animations
- Admin panel: Fees & Revenue tab — new AdminTab "fees" with FeesRevenueTab component; 3 sections: Revenue Streams (lime gradient, dynamic stats from /api/admin/revenue), User‑Facing Costs (purple gradient, static table), Operator Costs (warning gradient, static table with cache notes)
- /api/admin/revenue route — GET endpoint, Bearer $ADMIN_SECRET auth; returns attestationCount, handleChangeCount, avaxCollected (placeholder values, TODO: wire to on‑chain events)
- WalletAddressInput default border — border‑transparent → border‑white/[0.12]; focus: border‑[var(‑‑gs‑lime)]/40
- useUserProfile event handler fix — gs:profile‑updated handler wrapped in queueMicrotask() to prevent "setState during render" warning when FavoriteButton dispatch triggers setProfile in WalletDropdown’s useUserProfile instance
- UsersTools.tsx placeholder fix — \u2026 in JSX string attr rendered literally; wrapped in {""} expression for proper unicode interpretation
- NFTGalleryPagination debug info — gated behind ?debug=1 URL param instead of NODE_ENV === "development"
- usePinnedFavorites resolvedCacheRef — caches previously resolved NFT+wallet pairs so walletMap re‑fetches don’t downgrade full cards to stubs
v0.7.5
Mar 10, 2026
- NFTDetailPositionCard text readability pass — row labels 9px/40% → 10px/50%, section headers 10px → 11px, GUN values 14px → 15px, USD conversions 50% → 60% opacity, arrows 30% → 40%, italic subtexts switched to text-white/50 + tracking-wide
- Purple accent visibility — VIA SALES label, waterfall dropdown buttons, Reference Estimate header, warning icon all bumped to full opacity (were /60–/80)
- Low-confidence waterfall dropdown: labels 20% → 30%, values 12px/40% → 13px/50%; full-confidence: labels 25% → 35%, values same bump
- NFT description subtitle in modal: added tracking-wide letter spacing
- Image lightbox — click NFT image in modal to open fixed z-[200] overlay; scroll-to-zoom (0.5×–5×, 0.15 step), click-to-toggle (1×/2×); bg-black/95 solid (no backdrop-blur to avoid GPU load); zoom % indicator at bottom; plain <img> tag to avoid Next.js Image fill constraints
- Modal image size bump — 180×180/220×220 → 220×220/280×280 (mobile/desktop); container changed from aspect-square+max-w to explicit dimensions; parent switched to flex justify-center for proper centering
- High-res image pipeline — imageHires field added to NFT type, CachedMetadataData, and avalanche.ts cache read path; /api/opensea/orders extracts image_url from maker_asset_bundle; threaded through getNFTListings → enrichment orchestrator → metadata cache (7d TTL); lightbox uses imageHires || image
- Acquisition timeline tooltip — glass effect: background rgba(22,22,22,0.6), backdrop-blur 6px, border 12% white opacity
v0.7.4
Mar 10, 2026
- /build-games landing page overhaul — reordered sections: "The Market" (13M players, 450K DAU, zero existing trackers) now leads as section 01 before Architecture; judges see the "why" before the "how"
- GitHub repo link added to hero CTAs and bottom CTA section — links to Gunzilla-NYC/gunzscope for judge access to source code
- Attestation explorer link — "View on Snowtrace" button in On-Chain Proof section links directly to the attestation contract on AVAX C-Chain
- Market section copy refined — "OTG and GUNZscope Phase I is a sequenced strategic entry point, not the ceiling" replaces generic framing
- Footer — CRYPTOHAKI text now links to /cryptohaki profile page
v0.7.3
Mar 8, 2026
- CSS design token system — 12-step neutral scale (--gs-n-0 through --gs-n-11), 14 semantic aliases (--gs-bg, --gs-surface, --gs-card, --gs-text-*, --gs-border, --gs-status-*), 3 transition tokens (--gs-ease, --gs-dur-fast, --gs-dur-norm); enables single-point theme switching
- Variable-driven corner-cut utilities — .cut-sm (4px), .cut-md (8px), .cut-lg (12px) classes backed by --gs-cut-* custom properties; replaces hardcoded clip-path polygons
- Accessibility: scroll-behavior forced to auto in prefers-reduced-motion media query — smooth scrolling was still active for users with reduced motion enabled (WCAG 2.1 SC 2.3.3)
- Compositing layer merge — grid-bg + scanlines collapsed into single .page-bg element across 8 page files; eliminates one full-viewport GPU texture per page (~2-4 MB VRAM savings)
- IntersectionObserver auto-disconnect — observers in home page social proof, brand page, and build-games page now call unobserve() after element reveal; ~29 persistent observer callbacks eliminated post-animation
- rAF-gated scroll handlers — Navbar, PublicNav, and ScrollToTopButton scroll listeners wrapped in requestAnimationFrame ticking gate with passive: true; caps setState calls at 1/frame (8× reduction on 120Hz displays)
- content-visibility: auto on 3 below-fold home page sections (Features, Social Proof, Dashboard Preview) — browser skips layout+paint for offscreen content; ~200-400 DOM nodes deferred on initial load
v0.7.2
Mar 7, 2026
- Wallet modal UX overhaul — "Connect Whitelisted Wallet" → "Get Started"; two lanes now labeled "View Only" (paste address) and "Full Access" (Dynamic connect) with badge indicators; description copy rewritten for clarity
- Middleware view‑only bypass — /portfolio with ?address= query param now skips gs_session cookie validation; applies to both missing‑cookie and expired‑cookie paths in middleware.ts
- connectionMode fix — PortfolioClient.tsx compares activeWalletData.address against primaryWallet.address (case‑insensitive); returns "view‑only" when viewing a different address than the connected wallet; fixes View Only badge, attestation button gating, and ShareDropdown signing controls
- useIsViewOnly() hook — new selector in PortfolioContext.tsx; reads connectionMode from context; consumed by WalletIdentity.tsx (badge) and ShareDropdown.tsx (attestation gating)
- Portfolio initializing hard timeout — 15s setTimeout in isPortfolioInitializing effect; fires regardless of gunPrice state; prevents permanent "Calculating…" when CoinGecko times out; existing 10s enrichment timeout preserved as inner guard
- Error boundaries — app/global‑error.tsx (root‑level, wraps <html>/<body>) and app/portfolio/error.tsx (route‑level with brand styling); both show error digest + retry button
- Body scroll lock — useEffect in app/page.tsx sets document.body.style.overflow="hidden" when showWalletModal is true; cleanup restores on unmount
- NFTGalleryPagination "All N NFTs loaded" message gated behind process.env.NODE_ENV === "development"
- E2E test suite — 57 Playwright specs across 5 files (wallet‑modal, view‑only‑flow, hard‑refresh, network‑edge‑cases, data‑integrity‑responsive); desktop‑chrome + mobile‑chrome (Pixel 7) projects; runs against BASE_URL || gunzscope.xyz
- Tweet thread automation — compose‑tweet.mjs gains ‑‑version flag for targeting specific update entries; post‑tweet.mjs auto‑loads .env.local credentials; tweet.mjs interactive CLI with preview/edit/post flow
v0.7.1
Mar 5, 2026
- Server‑side whitelist enforcement — middleware.ts verifies Bearer JWT (API routes) and gs_session cookie (page routes) against whitelist_entries via checkWhitelistEdge() on every request; 7 page routes + 26 API routes in matcher
- Session cookie (gs_session) — lib/auth/sessionCookie.ts creates HS256‑signed JWT via jose SignJWT; set by /api/access/validate on 3 success paths (permanent, trial, promoted); cleared by /api/auth/logout POST; 7‑day maxAge, httpOnly + Secure + SameSite=Strict
- Soft delete for whitelist — WhitelistEntry.isActive column; removeFromWhitelist() sets isActive=false instead of DELETE; banService.ts fixed to use update() not delete(); addToWhitelist() upserts with isActive=true for re‑activation
- whitelistService.edge.ts — Edge‑compatible whitelist check via @neondatabase/serverless HTTP queries; predicate: isActive=true AND (expiresAt IS NULL OR expiresAt > NOW())
- useAutoLogin whitelist pre‑check — calls /api/access/validate before /api/me; non‑whitelisted wallets get handleLogOut() immediately, preventing ghost profile creation
- promoteFromWaitlist() fix — upsert update clause now sets isActive: true (survives prior admin soft‑delete)
- KonamiOverlay trial‑expired UX — parses trialExpired from /api/access/konami 403 response; shows "Trial already used" instead of generic error
- Navbar + WalletButton disconnect — fire‑and‑forget fetch("/api/auth/logout") before handleLogOut()
- Ghost profile cleanup — 4 zero‑engagement UserProfile rows deleted; Digital Panoply whitelisted via direct DB upsert
v0.7.0
Mar 5, 2026
- Portfolio Pins — FavoriteButton wired onto NFTGalleryGridCard image overlay (bottom‑right, opacity‑0 group‑hover:opacity‑100); PinButton component reads useUserProfile().favorites, PATCH /api/favorites/[id] toggles pinned; useNFTGalleryFilters partitions by pinnedRefIds Set, floats pinned to top preserving sort order within each group
- isOwnPortfolio prop threaded NFTGalleryProps → NFTGalleryInner → NFTGalleryGridCard; computed in PortfolioClient via connectedWallets.includes(activeWalletData.address.toLowerCase())
- Wishlist model — FavoriteItem extended with externalContract, externalTokenId, externalChain (String?), lastKnownValue (Float?), lastValueAt (DateTime?); type union includes "wishlist"
- GET /api/favorites — listFavorites() splits by type !== "wishlist" vs type === "wishlist", returns { favorites, wishlist }
- GET /api/favorites/refresh‑wishlist — updates lastValueAt for all wishlist items
- AccountPanel Wishlist tab — TabId union extended; wishlistItems/favoriteItems computed from profile.favorites.filter(); star icon, lastKnownValue GUN display, lastValueAt date
- Admin Users tab — listUsers() in userService with Prisma OR search (displayName, email, wallet address); GET /api/admin/users with verifyAdmin + isWhitelisted cross‑reference; UsersTools component with debounced search, 7‑column grid, whitelist dot indicator
- Feature Requests GlitchLink in desktop navbar — gated behind hasWallet, positioned after ExperimentsDropdown
- useUserProfile — togglePin() action with optimistic update; FavoriteItem interface gains pinned: boolean + wishlist fields; AddFavoriteInput gains optional externalContract/externalTokenId/externalChain
- addFavorite() return type includes pinned: boolean; upsert create/update now persists wishlist fields
- Display name fallback — UsersTools, ShareLeaderboard, admin shares page now show truncateAddress(primaryWallet) instead of raw email or "Anonymous"
- shareService getShareLeaderboard() includes primaryWallet via wallets relation (orderBy isPrimary desc, take 1)
- Duplicate cryptohaki UserProfile cleaned via raw SQL script (scripts/cleanup‑duplicate‑user.ts) with correct FK column mapping per table
v0.5.4
Mar 4, 2026
- PortfolioSparkline component — new chart in ValueHeader showing % change from cost basis over time; dual‑clip split‑gradient fill (profit above zero, loss below), zero baseline dashed line, pulsing endpoint dot, edge‑aware hover tooltip as absolute div sibling
- PortfolioSparkline colors — profit fill #A6F700 (brand lime), loss fill #B44AFF (soft orchid / ‑‑gs‑rarity‑epic); endpoint glyph + drop‑shadow glow match; tooltip % text uses same palette
- ValueHeader 7d badge loss state — switched from var(‑‑gs‑loss) red to #B44AFF orchid (bg/border/text)
- PnLScatterPlot refinements — floor cluster annotation (horizontal dashed line for dots < 5 GUN), simplified legend (4 items), zone label opacity 0.18 + fontSize 11, break‑even label rotation via atan2, break‑even dot treatment (white stroke within 5%)
- PnLScatterPlot + AcquisitionTimeline margins normalized to { top: 16, right: 24, bottom: 32, left: 40 } in embedded mode
- AcquisitionTimeline — removed vertical grid lines; lollipop stems converted to gradient opacity (5 linearGradient defs per venue, stemGradient() helper)
- AcquisitionTimeline stem gradient fix — gradientUnits="userSpaceOnUse" with explicit pixel y1/y2 coordinates; default objectBoundingBox caused zero‑width line degenerate bounding box
- AcquisitionTimeline horizontal crosshair — dashed line from hovered dot to Y‑axis on proximity lock
- AcquisitionTimeline tooltip — added acquisition date (MMM DD, YYYY) and current USD value fields
- AcquisitionTimeline stem bottom stopOpacity reduced from 0.25 to 0.10
v0.5.3
Mar 4, 2026
- 40‑item performance audit across 5 phases — parallelization, caching, architecture, DRY, decomposition
- Parallelized 6 critical paths: fetchGunPricesForDates (5‑concurrent batches), /api/leaderboard (Promise.all DB+price), floor‑drop/portfolio‑digest/whale‑tracker crons, avalanche.ts RPC calls
- Cache‑Control headers on 6 API routes: /api/price/gun, /api/portfolio/[wallet]/pnl, /api/nft/pnl/[tokenId], /api/marketplace/purchases/*
- Deferred Vercel Analytics + SpeedInsights after hydration via AnalyticsDeferred component
- motion/react added to optimizePackageImports — tree‑shakes Framer Motion bundle
- useGunPrice migrated to SWR — built‑in dedup, stale‑while‑revalidate, background refresh
- Shared server‑side GUN price cache (lib/server/gunPrice.ts) — single in‑memory cache across all API routes
- /api/scarcity — in‑memory cache + parallel mint‑count lookups
- React.cache() on share/referral server pages for same‑request dedup
- PortfolioContext — 14 prop‑drilled values lifted into context provider, eliminates drilling cascade
- GalleryFilterContext — filter state moved from prop threading to dedicated context
- WalletIdentity — removed redundant boolean guards
- Deduplicated timeAgo, validateSlugLocally, slug constants into lib/utils/
- OpenSea cache‑control helpers consolidated to app/api/opensea/cacheHelpers.ts
- memo() on NFTGalleryControls + chart sub‑components (AcquisitionTimeline, PnLScatterPlot)
- animate‑spin SVGs wrapped in <span> for GPU compositing
- .map().filter() → single‑pass .reduce() in hot paths
- Leaderboard DB‑level DISTINCT instead of JS dedup
- Cron logAlert via after() for non‑blocking response
- AdminPanel.tsx decomposed: 1,342 → 155 lines + 8 sub‑components in components/account/admin/
- useNFTAcquisitionPipeline: types + candidates extracted to lib/hooks/acquisition/ (1,747 → 1,360 lines)
- brand/page.tsx: 5 static sections extracted to app/brand/sections/ (1,682 → 195 lines)
- PortfolioClient: PortfolioSummaryBar + Footer converted to dynamic() imports (5 total lazy‑loaded components)
- Passive scroll listeners + startTransition for scroll‑driven state
- 10s timeout on all CoinGecko fetches
- /api/price/history restored to 24h revalidate cache
v0.5.2
Mar 2, 2026
- /build‑games landing page — hero, stats bar (live from /api/stats/site), BuildVelocityChart, multi‑chain architecture diagram, 9‑card feature grid, on‑chain proof section (live attestation count), valuation waterfall, dashboard preview, roadmap CTA; layout matches homepage patterns (max‑w‑7xl, section‑number, section‑line)
- /build‑games/roadmap — public fork of /strategy with admin gate removed; DocBadge, BuildVelocityChart, 6 phases, ScopeBar, vision banner; minimal nav with Logo + back link
- PublicNav component — reusable nav for public pages (no Dynamic Labs dependency); Logo, VersionBadge, GlitchLink items, Login CTA, mobile hamburger, scroll backdrop blur; matches Navbar layout exactly
- /explore now uses PublicNav instead of Navbar — anonymous visitors see Home + Onchain ID links instead of empty bar
- /explore/attestation/[cid] viewer page — renders attestation metadata as branded UI instead of raw JSON; summary cards (wallet, value, items, block), merkle root with copy, holdings table (first 100 + expand), Autonomys DSN badge; getMetadataLink() extracts CID and links to viewer
- Strategy page — added BuildVelocityChart section above vision banner
- Homepage — Onchain ID link now uses GlitchLink (bracket + scramble effect on hover)
- KonamiOverlay bug fixes — input bg‑black/60 → bg‑black/85 (bottom transparency fix); border‑1 → border‑2 with higher opacity (0.15 empty, 0.50 valid); input text‑white/90 → text‑[var(‑‑gs‑white)] + tracking‑wider + caret‑lime; focus‑within state on empty input
- globals.css — input::selection override: rgba(166,247,0,0.3) instead of solid lime; fixes green flash on paste
- DynamicProvider — .modal‑card bg rgba(22,22,22,0.5) → rgba(22,22,22,0.88); auth modal readability fix
v0.5.1
Mar 2, 2026
- Nav restructure — Leaderboard, Scarcity, Market grouped under “Experiments” dropdown; new ExperimentsDropdown component with GlitchLink‑style trigger (useGlitchScramble, brackets, chevron), close‑on‑outside/Escape/route‑change
- “Explore” renamed to “Onchain ID” across navbar (desktop GlitchLink + mobile menu) and home page
- Explore page — Autonomys DSN changed from external link to static label; Avalanche link color → AVAX red (#E84142); Autonomys label color → brand blue (#4A7AFF); per‑row metadata links blue
- Autonomys attestation count fix — isAutonomysURI() helper matches both direct gateway URLs (gateway.autonomys.xyz) and proxy URLs (/api/attestation/metadata/{cid}); tooltip now shows correct count
- Admin notification badge on WalletDropdown — fetches /api/feature‑requests, counts status=open; red pill badge on trigger + inline badge next to Feature Requests nav item; admin‑only, refreshes on route change
v0.5.0
Mar 2, 2026
- /api/market/listings — parallelized getActiveListingsDetailed + getCollectionSaleEvents via Promise.all; previously sequential
- Module‑level collection sales cache in OpenSeaService.getCollectionSaleEvents — keyed by slug::afterDateMs, 30‑min TTL; findUsableSalesCache() cross‑key reuse (unfiltered 200‑entry cache serves 50‑entry unfiltered requests)
- Module‑level GunzScan name cache in resolveTokenMetadata — keyed by contract:tokenId, 24h TTL, 500‑entry max; batch fetch only uncached tokens; logs hit/miss ratio
- New lib/api/marketCache.ts — MarketReferencePrice / MarketReferencePriceCache interfaces; module‑level singleton with 5‑min TTL; populated by /api/market/listings, consumed by /api/market/reference‑prices
- New /api/market/reference‑prices route — lightweight GET returning per‑item‑name floor prices from shared cache; no OpenSea calls; s‑maxage=300, stale‑while‑revalidate=60; returns empty if market data not cached
- MarketReferencePriceData type added to lib/types.ts
- applyValuationTables() — new optional 5th param marketReference?: MarketReferencePriceData; fills currentLowestListing from market bulk data only when per‑NFT enrichment hasn’t provided one (nft.currentLowestListing === undefined)
- PortfolioClient.tsx — 4th parallel fetch for /api/market/reference‑prices alongside rarity/comparable/floor; passed to applyValuationTables()
- NFTDetailPositionCard — expandable waterfall tier list in both Reference Estimate and Market Reality sections; builds waterfallTiers from currentLowestListing/comparableSalesMedian/rarityFloor/floorPrice; best tier marked, others behind toggle
v0.4.9
Mar 2, 2026
- /api/attestation/status — now returns contractAvaxBalance (contract’s AVAX balance from collected fees) and attestFee (current fee in AVAX); batched via Promise.all with existing totalAttestations query
- ATTESTATION_ABI extended with owner() view and withdraw() — new exports: withdrawFees(signer) calls contract.withdraw() and waits for receipt; getContractOwner(provider) reads on‑chain owner address
- OnChainTools admin component — AVAX Earned + Current Fee rows in Live Status; Withdraw button with 5‑state flow (idle/switching/signing/confirming/success); chain switch via ensureAvalancheChain, signer from Dynamic’s primaryWallet.connector.getWalletClient(); tx hash links to Snowtrace on success
- Admin panel auth fix — production showed empty whitelist/waitlist because NEXT_PUBLIC_ADMIN_SECRET was not set in Vercel; fetch callbacks use data.entries ?? [] which treats 401 as empty data silently
v0.4.8
Mar 1, 2026
- PortfolioAttestation.sol v3 — attest() now takes explicit `address wallet` as first parameter instead of using msg.sender; enables delegation (wallet A pays, wallet B gets the attestation); require(wallet != address(0)) guard
- UUPS proxy upgraded on C‑Chain: implementation 0x80A6C9661Fb0fEd1cCEBf568bCb709D548B98358; proxy address unchanged (0xEBE8FD7d40724Eb84d9C888ce88840577Cc79c16)
- Frontend ABI + submitAttestation() updated to pass wallet param; usePortfolioAttestation hook passes tracked walletAddress (in‑game wallet) as attestation target
- Attestation flow reordered: chain switch → get signer → fetch blockNumber from signer’s provider (fixes RPC mismatch causing estimateGas failures)
- Contract ownership transferred to Ledger wallet (0x8ABF…488C); hardhat‑ledger plugin + upgrade script configured for future UUPS upgrades
- Implementation contract verified on Snowtrace via Routescan API
- Test suite expanded to 29 tests: delegation (alice pays for bob), multi‑payer, zero‑address rejection
v0.4.7
Mar 1, 2026
- PortfolioAttestation.sol v2 — configurable attestFee (0.01 AVAX default), owner/withdraw/transferOwnership/setFee; attest() now payable with require(msg.value >= attestFee); totalFeesCollected counter; FeeUpdated + OwnerTransferred events
- Contract redeployed to Avalanche C‑Chain: 0xf8f5aa3D940009987F02AD92e44A5434Bab748bf
- Frontend contract layer — ABI updated with payable attest + attestFee() view; submitAttestation() reads fee from contract and sends as tx value; exported getAttestFee() for UI display
- Metadata URI now routes through gunzscope.xyz/api/attestation/metadata/{cid} instead of gateway.autonomys.xyz — /api/attestation/metadata/[cid] proxy route with 24h immutable cache
- Test suite expanded to 26 tests: fee enforcement, overpayment, owner withdraw with gas accounting, ownership transfer, fee update, zero‑address guard
v0.4.6
Mar 1, 2026
- GUN Δ hollow dots on Cost vs Value scatter chart — NFTs without market valuation (no listing, comparable sale, rarity floor, or collection floor) now appear as hollow circles positioned via synthetic floor derived from GUN token appreciation: syntheticFloor = cost × (gunPrice / historicalGunUsd)
- Hollow dots use profit/loss green/red coloring (not amber) with dashed stems to distinguish from filled market‑valued dots; fixed 4px radius, no quantity scaling
- Grouped GUN Δ NFTs (×2, ×3) expanded into individual hollow dots with deterministic ±2% cost jitter to prevent visual stacking
- Legend restructured with MARKET / GUN Δ sections separated by pipe dividers; each section shows filled vs hollow profit/loss indicators
- portfolioInsights.ts — generateInsights now includes GUN Δ items in total unrealized P&L; added getGunDeltaPnlUsd() helper for Track A currency appreciation; new gun_delta insight type with isNeutral amber styling
- Insight quantity counting uses nft.quantity ?? 1 instead of array length — grouped ×2 NFTs count as 2 individual items in "below cost basis" and totalTracked denominators
- InsightsPanel.tsx — gun_delta icon (swap arrows SVG) with amber accent when isNeutral
- portfolioInsights test suite expanded to 12 tests; added quantity>1 coverage
v0.4.5
Mar 1, 2026
- Autonomys Auto Drive integration — attestation metadata uploaded to Autonomys DSN via @autonomys/auto‑drive SDK; /api/attestation/upload route accepts JSON payload, returns CID + gateway URL; usePortfolioAttestation uploads before on‑chain tx, falls back to inline data: URI if upload fails
- On‑Chain Explorer — public /explore page queries PortfolioAttested events from C‑Chain contract; /api/attestation/events route with chunked queryFilter (49k block range per call, public RPC 50k limit) + 5‑min server‑side cache + stale fallback on error; useExplorer hook
- Explorer UI — stats banner (attestations/wallets/GUN attested), desktop table + mobile cards, Snowtrace tx/address links, Autonomys gateway metadata links, loading skeletons, empty state with portfolio CTA
- Navbar + sitemap + home page Explorer links — public page, no auth gate; added to isInApp check, desktop GlitchLink, mobile menu, sitemap at hourly/0.7
- Exported ATTESTATION_ABI and getContractAddress() from lib/attestation/contract.ts for event query reuse
v0.4.4
Mar 1, 2026
- Seaport v1.6 ABI fix — extractCostFromOrderFulfilled now tries v1.5 ABI first, falls back to v1.6 (which adds address recipient to OrderFulfilled non‑indexed data); previous v1.5‑only decode silently failed on GunzChain’s Seaport, causing tx.value fallback to assign total batch cost to every item
- NFT detail cache schema v24→v25 — invalidates all stale entries with incorrect batch‑purchase prices
- On‑chain portfolio attestation — usePortfolioAttestation hook builds Merkle tree of NFT holdings, submits root+totalValue to PortfolioAttestation contract on Avalanche C‑Chain; ensureAvalancheChain() handles wallet_switchEthereumChain / wallet_addEthereumChain
- ShareDropdown attestation UI — "Attest On‑Chain" button with 7‑state flow (idle/building/switching‑chain/signing/confirming/success/error); existing attestation indicator; Snowtrace tx link on success
- WalletIdentity passes Dynamic primaryWallet connector + isOwnWallet to ShareDropdown for attestation signing
- Attestation status API — GET /api/attestation/status reads deployer GUN balance (GunzChain) + AVAX balance (C‑Chain) + totalAttestations from contract
- AdminPanel OnChainTools — added C‑Chain AVAX balance display
- Hardhat config — added avalanche mainnet network (chainId 43114) + Routescan etherscan verification; deploy:avalanche npm script
v0.4.3
Feb 28, 2026
- Konami trial access — Konami code now grants 72‑hour trial whitelist instead of permanent access; WhitelistEntry gains expiresAt DateTime? field (null = permanent, non‑null = trial)
- getWhitelistStatus() discriminated union — returns { status: permanent | trial | expired | none, expiresAt? }; replaces boolean isWhitelisted() in validate and waitlist/status endpoints
- Konami API rewrite — checks existing whitelist status before granting; permanent → no‑op, active trial → return existing info, expired → 403 "trial already used", none → creates 72h trial entry + waitlist entry with threshold 1
- joinWaitlistForTrial() — creates or updates waitlist entry with promotionThreshold: 1 (vs default 3); idempotent downgrade if already on waitlist
- Tiered referral thresholds — DEFAULT_PROMOTION_THRESHOLD=3, TRIAL_PROMOTION_THRESHOLD=1, EXPIRED_TRIAL_PROMOTION_THRESHOLD=2
- bumpExpiredTrialThreshold() — lazy idempotent upgrade 1→2 on expired trial detection; called from validate and waitlist/status endpoints
- promoteFromWaitlist() upsert pattern — converts expired trial to permanent whitelist (sets expiresAt: null) when referral threshold met
- Validate API trial‑aware responses — { trial: true, expiresAt } for active trials; { trialExpired: true, waitlisted: true } with threshold bump for expired
- Home page trialExpired redirect — expired trial users redirect to /waitlist?address=xxx&trialExpired=true; active trial stores expiresAt in localStorage
- KonamiOverlay messaging — "TRIAL ACCESS GRANTED" with "72 hours of full access activated" and "Refer 1 friend to keep it forever"
- WaitlistClient trialExpired state — reads ?trialExpired=true URL param; shows "Trial Ended" header with dynamic referral count based on per‑entry promotionThreshold
v0.4.2
Feb 28, 2026
- Ban/reset system — BanEntry model in Prisma; banService with isBanned/banAddress/unbanAddress/resetAddress/listBans; ban guards on /api/access/validate, /api/access/konami, /api/access/reconcile, /api/waitlist/status, and joinWaitlist(); banned users get 403 with { banned: true }
- Admin PATCH /api/admin/whitelist — { address, action: "ban"|"unban"|"reset", reason? }; ban removes from whitelist + waitlist + blocks re‑enrollment; reset clears whitelist + waitlist without ban (user can rejoin); GET ?view=banned returns paginated ban list
- AdminPanel ban/reset UI — Ban/Reset buttons on whitelist entries, Ban button on waitlist entries, dedicated Banned Users section with unban capability
- WaitlistClient banned state — useWaitlist hook returns isBanned; banned users see "ACCESS REVOKED" page with support contact instead of join form
- ReferralRedirect banned handling — email‑only validate returning { banned: true } shows revoked message instead of redirect
- KonamiOverlay wallet flow fix — submitting state was never reset after successful wallet submission, permanently disabling the Confirm button in the handle phase; added setSubmitting(false) + initial handle availability check via /api/referral/check‑slug
- Home page validation refactor — separated wallet and email validation into independent useEffect blocks; wasConnectedOnMount now tracks wallet only (email users always validate); emailValidatingRef prevents duplicate email validation calls
- Attestation status API — GET /api/attestation/status route stub
- Hardhat config — updated GunzChain testnet RPC URL; added Fuji (Avalanche C‑Chain testnet) network
v0.4.1
Feb 28, 2026
- Hero redesign — split title into super‑label ("YOUR OTG" at text‑2xl‑3xl with 0.15em tracking) + dominant scramble headline (text‑6xl‑[104px]); removed "Arsenal" line; purple glow text‑shadow on OTG (40px blur, 0.3 opacity)
- Scramble words updated — Intelligence, Lore, Legacy, Edge (was Intelligence, Dominance, Advantage, Edge)
- Hero subtitle rewrite — "The tactical intelligence layer for Off The Grid / Start your legacy, analyze the market, dominate the meta."
- CTA section hierarchy — "Early access — whitelist only" label above Connect Wallet button; Konami hint below at white/25 opacity; removed redundant "Connect here" link
- KonamiOverlay v2 — "Ready Player Zero" title with rotating subtitles (3 variants, module‑level counter); custom input accepting EVM/Solana/email with type badge; onSubmit returns Promise<boolean>; confirmed state with "CLEARANCE CONFIRMED" card; auto‑proceeds to Dynamic auth after 2.5s via onProceed callback
v0.4.0
Feb 27, 2026
- Konami code easter egg — useKonamiCode hook listens for ↑↑↓↓←→←→BA on home page; 2s inactivity timeout resets progress; triggers KonamiOverlay
- KonamiOverlay tactical scan — full‑screen overlay with hex matrix rain (120ms refresh), horizontal scan line (2.5s linear sweep), corner bracket framing, 5‑line sequential reveal with motion/react stagger
- Konami whitelist input — after scan animation completes, WalletAddressInput slides in with clip‑path ENTER button; validates via detectChain; ESC or backdrop click to dismiss
- POST /api/access/konami — accepts { address, email }; attempts promoteFromWaitlist first, falls back to addToWhitelist with "Konami code" label; reconciles email+wallet if both provided
v0.3.9
Feb 27, 2026
- Email waitlist gate — /api/access/validate accepts { email } alongside { address }; emails stored as email:user@example.com identifier in waitlist/referrer tables; deriveAutoSlug handles email: prefix (username before @)
- Email‑to‑wallet reconciliation — new POST /api/access/reconcile endpoint; promoted email user connects wallet → whitelists wallet address with reconciled:{email} note; WaitlistClient detects wallet connection and auto‑reconciles
- WaitlistClient email mode — identifier resolution from wallet OR ?email= search param; "Signed in as" email notice; promotion celebration shows Connect Wallet CTA instead of auto‑redirect for email users
- useWaitlist type parameter — hook accepts type: "wallet" | "email"; fetches /api/waitlist/status with ?address= or ?email= accordingly
- Email gate in app/page.tsx — email‑only Dynamic users (no wallet) now go through /api/access/validate instead of bypassing straight to /portfolio; non‑whitelisted emails redirect to /waitlist?email=
- GlitchText container stability — glitch hover effect no longer resizes the login button; fixed by preserving container dimensions during letter scramble animation
- SEO: robots.txt — blocks /api/, /admin/, /brand, /roadmap, /strategy; references sitemap.xml
- SEO: dynamic sitemap — app/sitemap.ts with 13 public pages, priority‑weighted (1.0 home → 0.1 legal)
- SEO: root layout metadata — Viewport export (themeColor #0A0A0A), metadataBase, title.template "%s | GUNZscope", default OG/Twitter cards, JSON‑LD WebApplication schema
- SEO: page metadata — added Metadata exports to changelog, updates, credits, privacy, terms, cookies; created layout.tsx wrappers for client‑component pages (market, insanity); noindex on brand/roadmap/strategy
- SEO: canonical URL on /portfolio layout to prevent ?address= duplicates
- SEO: alt text on 4 NFT images in scarcity and market pages (was empty string)
- Admin‑gated /strategy page — 6‑phase strategic roadmap; vertical timeline with phase nodes, pill‑tag items, market scope expansion bar
- Admin panel Links tab — card grid linking to all admin/internal pages with colored dot indicators
- Brand page Working Links section — Build Games landing preview link; Waitlist Flow section 06 with gate/state/promotion test panels
v0.3.8
Feb 26, 2026
- Waitlist redirect fix — paste‑address flow (non‑Dynamic‑SDK users) now passes address as query param to /waitlist; WaitlistClient reads from primaryWallet?.address || searchParams.get(‘address’); previously redirected back to / because no Dynamic wallet was connected
- Waitlist page Suspense boundary — wrapped WaitlistClient in <Suspense> in app/waitlist/page.tsx; required by Next.js for useSearchParams() during static prerender
- Waitlist status API auth removed — GET /api/waitlist/status no longer requires Dynamic JWT; position and referral count are non‑sensitive; paste‑address users have no auth token
- useWaitlist hook auth optional — getAuthToken() now called inside try block; token sent as Authorization header only when available; missing token no longer causes early return that left isLoading stuck at true
- Reusable WalletAddressInput component — components/ui/WalletAddressInput.tsx; unified chain detection (GunzChain/Solana badge, validation border, hint text) across 5 input locations: home page, admin panel, account page, insanity mode, portfolio search
- WalletAddressInput validateChain prop — when false, disables red border for non‑matching input; used by Handle Tools where slugs are valid input alongside wallet addresses
- Admin panel column layout standardization — WhitelistTools, WaitlistTools, ShareLeaderboard all use same toolbar‑at‑top pattern with shrink‑0 pb‑3 mb‑3 border‑b divider; address + action button on same row
- Stale .next cache diagnosis — dev server was serving compiled chunks with old error text "This address isn’t on the early access list yet." from cached .next/dev/ files while source had been updated
v0.3.7
Feb 23, 2026
- Referral register GET handler — added try/catch around getReferrerByWallet() DB call; was the only API route handler without error wrapping, causing unhandled throws to return HTML 500 instead of JSON
- useReferral client hook — added regRes.ok guard before .json() parse; non‑2xx responses now log status + body via console.warn("[Referral]") and show status code in error message instead of generic "Failed to load referral data"
- Admin‑gated /roadmap page — app/roadmap/page.tsx; converted gunzscope‑blockchain‑architecture.html to React/Tailwind with brand CSS vars, clip‑path corners, proper typography; isAdminWallet() gate with redirect
v0.3.6
Feb 22, 2026
- Updates page accordion refactor — extracted UPDATES data + UpdateEntry interface to lib/data/updates.ts; page.tsx slimmed from 449 → 48 lines, imports data and delegates to new UpdateTimeline client component
- UpdateTimeline component — components/updates/UpdateTimeline.tsx; ‘use client’ accordion with useState‑based open set, CSS height transition (200ms ease‑in‑out), chevron rotation; tag:’current’ entry auto‑expanded, others collapsed, multiple can be open simultaneously
- Push‑to‑main workflow updated — CLAUDE.md and docs/notes/push‑to‑main‑workflow.md now reference lib/data/updates.ts instead of app/updates/page.tsx for user‑facing update entries
v0.3.5
Feb 22, 2026
- Card/modal market data unification — NFTDetailPositionCard Track B now uses same waterfall as deriveCardData(): marketExitGun → comparableSalesMedian → rarityFloor → currentLowestListing; previously modal only checked marketExitGun + computeMarketInputs (which excluded rarityFloor)
- Tier‑confidence gating — new trackBIsSalesBased boolean on NFTCardData; gallery cards and list rows only show Track B MARKET line for sales‑based tiers (1‑4: EXACT, VIA SALES, VIA SKIN, VIA WEAPON); statistical proxies (tiers 5‑6: RARITY, FLOOR, LISTED, SIMILAR) suppressed from card display
- Modal low‑confidence treatment — tiers 5‑6 render "Reference Estimate" card instead of "Market Reality": dimmer styling (opacity‑80), no VS COST row, no MARKET P&L percentage, warning text explaining it’s a proxy not sales data
- exitTierLabel fallback chain — modal’s Track B exitTierLabel now falls back through VIA SALES → RARITY → LISTED when marketExitTierLabel is null, matching card behavior
v0.3.4
Feb 22, 2026
- Dual‑track P&L card redesign — NFTDetailPositionCard restructured into two distinct cards: Track A ("Your Deal", lime border) shows GUN token appreciation since purchase; Track B ("Market Reality", purple border) shows market‑based P&L from comparable sales waterfall
- Cost Basis merged into Track A — standalone COST BASIS section removed; cost basis row now lives inside the Track A card as the first data row, followed by TODAY’S VALUE row, providing a natural reading flow from "what you paid" → "what it’s worth" → "your P&L"
- P&L hero treatment — both track cards use a border‑top divider to separate data rows from the P&L value, displayed in font‑display 20px bold with profit/loss coloring; italic subtext below explains the number in plain English
- Label‑left / value‑right row pattern — all data rows use flex justify‑between with shrink‑0 labels (mono 9px uppercase gray) and right‑aligned values (display 14px semibold white tabular‑nums); arrow separator (→) between GUN and USD amounts
- Track B confidence line — shows data quality indicator (green dot + tier label like "VIA SALES" + sample count), plus ABOVE/BELOW FLOOR pill when market exit differs from collection floor
- Card styling standardized — bg‑[var(‑‑gs‑dark‑3)] with subtle border, 3px colored left border (lime for Track A, purple for Track B), p‑5 padding, rounded‑lg corners
- Removed Observed Market Range section — NFTDetailObservedMarketRange component no longer rendered in modal; getPositionOnRange helper removed
- Admin gate on /brand page — /brand now restricted to admin wallets using existing isAdminWallet() utility; non‑admin users redirected to /
v0.3.3
Feb 22, 2026
- Tiered valuation waterfall (Track B) — 6‑tier Market Exit estimate per NFT: EXACT (same tokenId), VIA SALES (same baseName), VIA SKIN (same skinDesign), VIA WEAPON (same weapon), SIMILAR (deferred), FLOOR (collection floor)
- Time‑weighted median — comparable sales weighted by recency (7d = 1.0, 7‑30d = 0.75, 30‑90d = 0.50, 90+ = 0.25); weighted‑median walk instead of simple median
- Item name parser — new parseItemName() extracts skinDesign and weapon from "X for the Y" naming pattern, enabling Tier 3 and Tier 4 waterfall groupings
- Pure valuation service — lib/portfolio/valuationService.ts walks waterfall with minimum‑sale‑count gates (1 for EXACT, 2 for all others), returns estimatedGun + tier + tierLabel
- Waterfall data in comparable‑sales API — /api/opensea/comparable‑sales now returns waterfall (byTokenId, byName, bySkin, byWeapon) alongside existing items; backward‑compatible optional field
- applyValuationTables enhanced — calls getMarketExitValuation() per NFT, writes marketExitGun, marketExitTier, marketExitTierLabel to NFT objects
- Track B on gallery cards — grid cards (medium+) and list rows show "~592 GUN · VIA SALES" below existing P&L and ValuationLabel
- Track B in modal QuickStats — 4th column "Market Exit" shows estimated GUN, USD conversion, tier label, and P&L vs cost basis; grid adapts 3→ 4 columns when data available
- Scarcity tracking prep — useNFTEnrichmentOrchestrator now tracks max observed mint number per baseName in scarcityMapRef for future Tier 5 matching
- NFT type extended — 3 new fields: marketExitGun, marketExitTier (1‑6), marketExitTierLabel
v0.3.2
Feb 21, 2026
- Full‑pagination enrichment — enrichment now defers until all NFT pages are loaded (was firing per‑50‑item page, causing concurrent enrichment races and backward progress jumps on wallets with 50+ NFTs)
- Generation‑guarded enrichment — startEnrichment increments a generation counter; all state updates (setProgress, setEnrichedNFTs, updateCallback, setIsEnriching) check gen === generationRef.current before writing, preventing stale enrichments from wallet switches
- Enrichment diagnostic summary — console.info after completion logs total/cached/fresh/failed counts, date/costGUN/costUSD/listing resolution percentages, free transfer count, and venue breakdown
- Incremental refresh — handleRefresh uses new invalidateListingPrices() instead of clearWalletCache(); only clears listingFetchedAt/currentLowestListing/currentHighestListing on each cached entry, preserving all acquisition data
- Removed groupNFTsByMetadata import from PortfolioClient — handleLoadMoreNFTs now passes mergedNFTs (already grouped) directly to startEnrichment instead of re‑grouping per page
- cumulativeBaseRef reset — new startEnrichment calls reset cumulativeBaseRef to 0 alongside generation bump, preventing stale cumulative offsets
v0.3.1
Feb 21, 2026
- Server‑side GUN price history cache — new GunPriceHistory Prisma model stores confirmed historical GUN/USD rates in Neon PostgreSQL; shared across all users so the first person to resolve a date’s price populates it for everyone
- Waterfall tier 2: server cache — resolveHistoricalGunPrice now checks the shared server table between localStorage and CoinGecko (3s timeout), with write‑through to localStorage on hit and fire‑and‑forget write‑back on CoinGecko/DefiLlama success
- GET /api/gun‑price/history — public endpoint with CDN caching (1h fresh, 24h stale‑while‑revalidate); 404s cached for 5 min to avoid hammering DB for missing dates
- POST /api/gun‑price/history — validated write endpoint with ATH guard, confidence‑based upgrade logic (won’t overwrite daily with estimated), rejects estimated prices from shared table
- "Synced X ago" indicator — ValueHeader shows when the portfolio was last loaded from server cache, with staleness coloring (>24h = brighter gray)
- Manual refresh button — spinning refresh icon next to the synced timestamp clears localStorage cache and re‑triggers full wallet fetch + enrichment
- Refresh disabled during enrichment — button grays out and spins while NFT enrichment is active to prevent redundant requests
v0.3.0
Feb 21, 2026
- Modal P&L reorganization — separated market valuation from GUN appreciation into two distinct P&L stories: market-based (via listings/sales/floor) when available, xGUN fallback otherwise
- Unified P&L computation — QuickStats UNREALIZED and YOUR POSITION P&L now always agree (both use market-first, xGUN fallback)
- Valuation method labels — QuickStats and YOUR POSITION show specific source: VIA LISTING, VIA SALES, VIA FLOOR, or GUN Δ
- GUN Based Performance sub-section — when market data drives headline P&L, a separate section shows pure GUN token appreciation with explanatory narrative
- CoinGecko fetch guard — modal acquisition pipeline skips redundant historical price fetches when enrichment data already provides confirmed USD values
- Valuation method badges on gallery cards — 6-tier taxonomy (LISTED, SALES, RARITY, FLOOR, COST, UNLISTED) shown on NFT cards
- Hidden redundant GUN @ line — cost basis section no longer shows "GUN @ $X.XXXX at time of purchase" when GUN Based Performance section is visible
v0.2.9
Feb 21, 2026
- xGUN P&L formula — PnL now purely reflects GUN/USD price appreciation: Y = historicalGunPrice, Z = currentGunPrice, P&L = costGun × (Z‑Y). Removed market‑data waterfall (listing/comparable‑sales/rarity‑floor) from cards, modal, sort, and portfolio summary
- Removed pnlSource labels — "vs listing" / "vs sales" / "vs floor" badges no longer appear on NFT cards since PnL is now single‑source
- OpenSea event_timestamp fix — 4 parse sites were treating Unix seconds as milliseconds, producing dates in January 1970 and triggering $0.0776 fallback prices
- Stale closure overwrite fix — async modal loadItemDetails captured resolvedAcquisitions at effect start time; cache‑rendered data was overwritten ~1s later. Fixed via resolvedAcquisitionsRef pattern in both NFTDetailModal and useNFTAcquisitionPipeline
- Transfer chain tracing fix — buildCandidateFromHoldingRaw now uses senderAcquiredAtIso, senderVenue, and senderTxHash when using sender cost data, instead of the transfer date/venue
- Transaction fee extraction — txFeeGun and senderTxFeeGun computed from receipt.gasUsed × receipt.gasPrice in avalanche.ts; propagated through ResolvedAcquisition, selectBestAcquisition, and all candidate builders
- Gas fees display — YOUR POSITION section in NFTDetailModal shows purchase and transfer gas fees when available
- Server cache hydration sanitization — PortfolioClient strips legacy purchasePriceUsd values from server‑cached NFTs when purchasePriceUsdEstimated !== false
- CoinGecko ATH sanity check — /api/price/history rejects prices above $0.12 (GUN ATH ~$0.115) and logs a warning
- Stale Next.js server cache bypass — /api/price/history temporarily switched to cache: 'no‑store' to purge incorrect CoinGecko historical data (revert to revalidate: 86400 after confirmation)
- Enrichment trust guard — modal no longer overwrites confirmed purchasePriceUsd (purchasePriceUsdEstimated === false) with its own recomputation
- Cache schema v24 — full client‑side re‑enrichment forced after CoinGecko data correction and server cache purge
- MetaMask fallback — main page wallet connect falls back to MetaMask deep link when Dynamic SDK fails to trigger wallet
- wGUN acquisition support — enrichment orchestrator handles wGUN‑based purchases for cost extraction
- useNftPnL hook rewrite — portfolio summary P&L now uses xGUN formula instead of floor‑based calculation
- PnL sort rewrite — useNFTGalleryFilters pnl‑desc sort uses xGUN unrealized USD gain with currentGunPrice threading
v0.2.8
Feb 19, 2026
- Historical price CORS fix — new /api/price/history server‑side proxy routes CoinGecko historical price requests through the server, fixing silent CORS failure that left purchasePriceUsd undefined on all client‑side lookups
- 14‑day sparkline — /api/price/gun now fetches 14d market_chart alongside 7d sparkline; PriceData type extended with sparkline14d; bootstrap and performance hooks prefer 14d data
- Portfolio history backwards extension — bootstrapPortfolioHistory can now prepend synthetic points from the sparkline when the sparkline reaches further back than stored history, with 30‑min buffer gap and ~24‑point sampling
- On‑chain cost extraction for transfers — acquisition pipeline now captures costGunFromChain for cross‑wallet transfers with wGUN payment, calculates USD from historical GUN price
- Universal GUN→USD fallback — any item with finalPurchasePriceGun > 0 and no USD value gets historical price lookup as a last‑resort conversion
- Transfer cost basis propagation — NFTDetailModal costBasisGun now falls through to traced original purchase price for TRANSFER acquisitions instead of always returning null
- Enrichment cache invalidation — marketplace purchases (opensea, in_game_marketplace) with missing price are treated as incomplete and retried on next enrichment cycle
- USD‑first acquisition card — NFTDetailAcquisitionCard shows $USD as primary line with GUN as secondary when USD is available, for both decode cost and purchase price sections
- Chart tooltip dynamic positioning — BackdropChart tooltip now renders above the point when point is in lower half of chart, below when in upper half, with hoverY null‑check guard
- ValueHeader pointer‑events — elements with [title] attribute now receive pointer‑events for native tooltip hover
- Item origins expansion — new categories early_access, reward, content_pack; Pioneer Set, Player Zero Set, Prankster Set, Anarchist Set reclassified; Going Ape Shit, Hump for Dominance added to Aperil Fools
- Empty wallet state — portfolios with 0 GUN and 0 NFTs show centered "Nothing Detected" message with inline search bar + Leaderboard/Market CTAs instead of empty $0.00 dashboard
- CLAUDE.md — documented Production Whitelist API endpoints and admin workflow
v0.2.7
Feb 18, 2026
- Item origin registry scaled to 35+ releases — added Enforcer BP, Pink Fury BP, Mr Fuckles BP, Hopper Pilot BP, Mad Biker BP, Neotokyo event, Trick Treat or Die event expansion, plus dozens of individual items
- AIRDROP label — any NFT with a known origin and sub‑1‑GUN acquisition cost now displays "AIRDROP" instead of "0 GUN"; unknown‑origin items keep their raw price
- Contains match rules — Don DeLulu CP and Mrs Crackhead Santa CP items now use fuzzy‑contains matching to catch blockchain name variations that exact‑match missed
- ItemRelease description field — releases can now carry rich event descriptions (lore, mechanics, reward details); Trick, Treat or Die is the first with full event metadata
- Trick, Treat or Die event badge — Halloween items now show the actual event name instead of generic "Halloween"
- Loading messages refreshed — three new OTG‑themed quips in the portfolio loading rotation
- Welcome popup streamlined — merged feedback paragraphs, removed redundant bug‑report CTA; single "Got it, let me explore" button
v0.2.6
Feb 18, 2026
- Search bar validation — invalid addresses (e.g. trailing special characters) now show inline chain detection badge and hint text instead of silently failing; "Go" button properly gates on address validity
- Wallet actions relocated — Watch and Portfolio buttons moved from search dropdown to the wallet identity bar, where users have context after viewing a wallet’s data
- Item origin database — curated lookup table mapping NFT items to their release origin (Battle Pass, Content Pack, Event) with 26 releases catalogued
- Navbar dropdown contrast — darker background and stronger shadow on wallet dropdown so it no longer blends into the page behind it
v0.2.5
Feb 18, 2026
- Hook extraction — five custom hooks pulled from PortfolioClient (loading messages, chart milestone gating, portfolio snapshots, wallet search actions, multi‑wallet gallery), reducing component from 1,203 to 1,012 lines
- Lazy‑load below‑fold — ChartInsightsRow and NFTGallery dynamically imported with skeleton placeholders for faster initial paint
- Accessibility — skip‑to‑content link, aria‑live on metrics grid, ARIA tablist with roving tabIndex and arrow‑key navigation on chart tabs
- Wallet switch race fix — request ID ref pattern on handleWalletSubmit discards stale responses when rapidly switching wallets
- Gallery card stagger — first 24 NFT cards fade‑in with translateY stagger animation (30ms grid, 20ms list); cards beyond 24 render instantly
v0.2.4
Feb 17, 2026
- Cost basis sparkline — dashed white line on the portfolio value chart shows historical cost basis alongside market value, visually revealing unrealized P&L gap
- Star‑appear animation — chart dots fade in like stars appearing in a night sky as NFTs enrich, growing from tiny pinpoints to full size over ~10 seconds
- Random dot stagger — new dots appear in randomized order across the chart (Fisher‑Yates shuffle) instead of chronologically, for a more organic night‑sky feel
- "Under Active Dev" label — moved from chart tab headers into the Insights section for a cleaner chart UI
v0.2.3
Feb 17, 2026
- Share image redesign — tactical HUD aesthetic OG image with dot‑grid background, corner brackets, gradient accent line, metric cards for GUN balance / NFTs / cost basis
- Download portfolio image — new button in share dropdown fetches the OG image and saves it as PNG
- Cost basis in share links — GUN spent on NFTs is now stored in share snapshots and displayed on the OG card
- Chart zoom fix — zooming no longer pushes dots off‑screen; scales use base width so data positions stay stable
- Zoom to cursor — Shift+scroll zooms toward the mouse pointer; zoom buttons auto‑scroll to the densest data cluster
- Gallery performance — React.memo on card components + memoized card data prevents unnecessary re‑renders
- Search debounce — 200ms debounce on gallery search input eliminates lag from keystroke‑driven re‑filtering
- content‑visibility: auto on NFT cards — browser skips painting offscreen cards, reducing compositor work on large galleries
- Enrichment speed — batch delay reduced from 800ms to 200ms, priority window expanded from 12 to 18 above‑fold NFTs
v0.2.2
Feb 17, 2026
- Chart crossfade — Timeline and Cost‑vs‑Value charts stay mounted simultaneously, opacity crossfade via motion/react eliminates DOM rebuild flicker
- Aligned chart dimensions — embedded Timeline now matches Scatter plot margins and height so content doesn’t shift during crossfade
- Multi‑wallet NFT total fix — portfolio wallets that load after the primary wallet now correctly update the NFT count
- NFT count includes duplicates — gallery item count sums quantities instead of unique token count
- Holdings card cleanup — removed per‑category GUN amounts, combined Bought+Minted on one row, consistent vertical spacing
- Navbar wallet dropdown — converted from slide‑out panel to positioned dropdown with spring animation, click‑outside and ESC‑key close
- Navbar layout fix — three‑group flex prevents wallet address width changes from shifting navigation links
- Transition demo — interactive comparison of four chart transition styles added to brand page
v0.2.1
Feb 17, 2026 · 6:30 AM EST
- Spring‑physics animations site‑wide — all panels, modals, drawers, and accordions now use motion/react with consistent spring config
- Custom green arrow cursor — replaces default pointer everywhere, zero‑lag tracking via direct mousemove transform
- Drop‑panel close fix — resolved race condition where trigger button click re‑opened panel immediately after click‑outside closed it
- Active‑state indicators — wallet‑switcher and share trigger icons stay green while their panels are open
- Navbar layout stability — three‑group flex prevents wallet address from shifting navigation links
- BreakdownDrawer accordion — smooth height expand/collapse replaces instant show/hide
- UnlockBanner trust section — spring‑animated height reveal instead of instant toggle
- WeaponLabDrawer exit animation — drawer now slides out instead of vanishing on close
- ConnectPromptModal entrance — scale + fade spring animation replaces no‑animation mount
v0.2.0
Feb 16, 2026 · 5:48 AM EST
- wGUN cost extraction fix — OpenSea offer fills now resolve acquisition cost from ERC‑20 receipt logs
- Offer fill detection — NFTs acquired via pre‑signed OpenSea offers show "OpenSea (Offer)" as source
- Hardcoded wGUN contract address as constant — no longer depends on env var for cost extraction
v0.1.9
Feb 16, 2026 · 3:21 AM EST
- Bundle diet — removed 15 unused dependencies (210 packages), including Nivo, GSAP, tsparticles, force‑graph, reaviz
- Replaced axios with native fetch across all API services (−30 KB)
- Replaced framer‑motion with a single CSS keyframe for page transitions (−45 KB)
- Code‑split 13 heavy components via next/dynamic — charts, modals, debug panels load on demand
- Deferred PostHog initialization to after hydration for faster first paint
- Added optimizePackageImports for visx, ethers, posthog‑js — tree‑shakes unused exports
- AVIF image format enabled site‑wide (20‑50% smaller than WebP on supported browsers)
- Converted barrel‑file imports to direct imports in key components for better tree‑shaking
v0.1.8
Feb 16, 2026 · 1:10 AM EST
- Market page — search all active OpenSea listings, drill into individual items with buy links
- Scarcity upgrades — quality badges, Best Deal sort, price range filter, cross‑links to Market
- OpenSea listing coverage tripled — fetches up to 3,000 listings (was 1,000)
- PnL scatter plot redesign — gradient stems, lighter grid, boosted zone labels, legend row, bordered data strip
- Sqrt‑aware Y‑axis ticks — labels evenly spaced in visual space instead of bunching at the bottom
- Chart height increase — both charts get more vertical breathing room
- Fixed zoom height jump — switching between charts no longer causes jarring layout shift
- Quality metadata pipeline — GunzScan rarity trait extracted and propagated end‑to‑end
v0.1.7
Feb 15, 2026 · 8:23 PM EST
- Acquisition Timeline — log‑scale Y‑axis with curated tick marks for better dot distribution
- Data‑driven dot entrance animation — new dots materialize as enrichment discovers them
- Portfolio sparkline stability — snapshots only record after enrichment completes, eliminating jagged reloads
- Backdrop sparkline clipping fix — increased top margin and enforced minimum container height
- Hydration fix — loading text no longer mismatches between server and client renders
- CSS shorthand/longhand conflict resolved in chart metadata card and insights border
- Chart zoom no longer inflates container height — fixed‑height chart area with hidden scrollbar
- ShareDropdown cleanup — removed dead code, simplified conditional logic
- SimpleMetrics and usePortfolioSummaryData reduced by ~700 lines of dead code
- PnLScatterPlot simplified — removed unused tooltip state and redundant computations
v0.1.6
Feb 14, 2026 · 8:57 PM EST
- Social sharing — Share on X, Discord, or copy link with rich OG preview cards showing portfolio value, P&L, and NFT count
- Valuation waterfall upgrade — per‑item listing > comparable sales median > rarity‑tier floor > cost basis
- Insights panel expansion — unrealized P&L, most valuable, biggest loss (5 insight types total)
- Acquisition Timeline chart — interactive visx timeline of NFT purchases by venue and date
- P&L Scatter Plot promoted to main portfolio view (was insanity‑only)
- All charts now use full valuation waterfall (listing > comparable > rarity > floor)
- Chart visual overhaul — sqrt scales, glow effects, gradient zones, smarter axis formatting
v0.1.5
Feb 14, 2026 · 4:05 PM EST
- NFT valuation waterfall: per‑item listings, rarity‑tier floors, comparable sales medians
- Dual‑value display — cost basis vs market value side by side
- Per‑item P&L with visx interactive charts
- Feature request system with community voting, bug reports, and screenshot attachments
- Collapsible request cards with lightbox image viewer
- UXR welcome popup for new testers with onboarding guidance
- Crosshair cursor performance: removed backdrop‑blur from overlays, cached DOM walks, targeted cursor rules
- Display name support for wallet profiles
- Portfolio history bootstrap with sparkline seeding
- Hybrid portfolio: aggregated summary + per‑wallet gallery with SWITCH
- Read‑only portfolio access via ?address= param — browse any wallet without logging in
- Migrate from SQLite to Neon PostgreSQL — full read/write in production
v0.1.4
Feb 13, 2026 · 4:56 AM EST
- Scramble‑decode loading text matching home hero animation
- 10pm Easter egg — because someone had to
- NFT Holdings sparkline toggle on first wallet search
- Server‑side RPC proxy for reliable production wallet loading
- View transitions with framer‑motion page animations
- Wallet address help panel for new users
- Auto‑populate credits from completed feature requests
v0.1.3
Feb 12, 2026 · 4:35 PM EST
- NFT sparkline toggle with historical hover counts
- Dynamic Labs SDK upgrade (4.59.1 → 4.61.2)
- Crosshair cursor performance fix
- UX polish: onboarding flow, nav, login gate, multi‑admin
- Grouped NFT visual overhaul: dynamic rarity accents, mergeIntoGroups
- Decode cost extraction fix for relayer‑submitted transactions
v0.1.2
Feb 11, 2026 · 10:37 PM EST
- GunzScan API migration with infinite scroll
- Ambient backdrop sparkline with smooth curves and overlay toggles
- Auto‑load portfolio on wallet connect
- Component decomposition: Navbar, PortfolioSummaryBar, scarcity, feature‑requests
- Wallet dropdown enhancements + identity bar refactor
- SEO metadata for all pages
- Standardized API response types
v0.1.1
Feb 10, 2026 · 11:47 PM EST
- Confidence indicator overhaul with enrichment reliability fixes
- Insanity Mode toggle + clip‑corner card design
- Sticky accent lines and container transparency polish
- Email auth flow + adaptive onboarding
- Scarcity page UX improvements
- Disconnect UX and network switch visibility fixes
v0.1.0
Feb 9, 2026 · 7:20 PM EST
- Public feature request and management system
- Dynamic wallet onboarding with styled connect flow
- Redesigned footer with social links
- Leaderboard page with access gate and active wallet display
- Nav glitch effect + gallery refactor
v0.0.3
Feb 5–8, 2026
- NFTDetailModal decomposition (3,163 → 1,069 lines via 4 extracted hooks)
- Portfolio context + hooks architecture refactor
- useWalletDataFetcher, useNFTEnrichmentOrchestrator, useWalletAggregation hooks
- WaffleChart composition visualization with stagger animation
- Marketplace price enrichment pipeline
- Portfolio three‑section layout with Simple/Detailed toggle
v0.0.2
Jan 31 – Feb 1, 2026
- NFT P&L pipeline with historical prices, rarity floors, and comparable sales
- Interactive rarity filter pills in NFT gallery
- YOUR POSITION section in NFT detail modal
- Floor price enrichment + metadata caching
- Security vulnerability fixes (31 → 9)
- Functional tier support from raw metadata
- Native GUN balance fetch fix
v0.0.1
GenesisJan 19–22, 2026
- Initial release — GUNZscope is born
- Multi‑chain portfolio tracker for Off The Grid
- NFT Armory/Lab feature with weapon compatibility
- Acquisition truth layer using RPC‑only fingerprints
- Progressive accounts implementation
- OpenSea + in‑game marketplace data integration
Built for the Off The Grid community · Not affiliated with GUNZILLA Games