chore(deps): bump next from 16.2.1 to 16.2.2 - #2
Closed
dependabot[bot] wants to merge 232 commits into
Closed
Conversation
Multi-chain creator fee tracker with Solana/Base support, real-time search, profile pages, cron-powered indexing, Supabase backend, security-hardened middleware, and mobile-optimized UI with Lottie animations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hobby plan only supports daily cron jobs. Changed: - refresh-prices: every 5min → every 6h - index-fees: every 15min → every 8h - cleanup: unchanged (daily at 3am) Upgrade to Pro for more frequent scheduling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- refresh-prices: daily at midnight UTC - index-fees: daily at noon UTC - cleanup: daily at 3am UTC Pro plan needed for sub-daily frequency. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Platforms like Bags.fm allow designating social handles (Twitter, GitHub) as fee recipients without requiring a wallet connection. This restructures the resolve pipeline to query handle-based fees in parallel with wallet resolution, so fees are discovered even when no wallet is linked. - Add getFeesByHandle to PlatformAdapter interface - Implement handle-based fee lookup in Bags adapter with corrected API paths - Run fetchFeesByHandle + resolveWallets in parallel in freshResolve - Merge and dedup fees from both handle-based and wallet-based lookups - Create creator records even when wallet resolution returns empty - Fix canonical URL from claimscan.io to claimscan.com Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Bags API v2 wraps all responses in { success, response: T } but the
adapter was reading from `data.walletAddress` and `data.fees`. Fixed:
- BagsWalletResponse now reads `response.wallet` (not `data.walletAddress`)
- BagsClaimablePosition uses `baseMint` + `totalClaimableLamportsUserShare`
- claim-stats requires tokenMint param (not wallet), so removed from
wallet-based queries — only claimable-positions is used per-wallet
- getHistoricalFees returns [] (no Bags endpoint for this)
- Removed unused sanitize imports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bridge social handles to EVM wallet addresses via public Farcaster APIs (Warpcast search + Farcaster Hub verifications). This enables multi-chain fee discovery — e.g. searching "VitalikButerin" now returns both SOL (via Bags.fm) and Base/ETH (via Farcaster verified addresses → Clanker). Uses dual search strategy (full handle + half-length prefix) with reputation-weighted scoring (follower count) to prefer real accounts over impersonators. No API keys required. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r token discovery, sort fees descending - TokenFeeTable/ChainBreakdown/FeeSummaryCard: compute USD at display time using unclaimed amounts × SOL/ETH price when total_earned_usd is null - Pass solPrice/ethPrice through component tree (page → PlatformBreakdown/ ChainBreakdown → TokenFeeTable) - Sort TokenFeeTable by computed USD descending (largest fees first) - Fix Clanker getCreatorTokens: use /search-creator?q=WALLET instead of broken /tokens?deployer= which ignores the deployer parameter - Deduplicate and filter Clanker tokens by admin match Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TokenFeeTable: add client-side pagination (15 tokens per page) with page navigation buttons, ellipsis for large page counts, and "X–Y of Z tokens" indicator - fetchAllFees: also call getLiveUnclaimedFees() from adapters that support it (pump, zora, heaven, bankr) so their fees appear in platform tabs, not just in the live summary card - Clanker getHistoricalFees: filter out tokens where availableFees=0 since we can't distinguish "claimed" from "not yet distributed" - Cleaned stale zero-amount Clanker records from DB Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add coinbarrel and raydium platform adapters for Solana fee discovery - Add eth.ts chain module for Zora multi-chain ProtocolRewards support - Add ScanStatusLog component showing per-platform scan results - Update PlatformBreakdown to show all platform tabs regardless of data - Expand platform_type to include coinbarrel and raydium - Update bankr, believe, revshare, zora adapters with improved fee logic Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract PROTOCOL_SOLANA addresses from Farcaster Hub alongside existing EVM addresses. This enables all Solana platform adapters (Heaven, Believe, Coinbarrel, Raydium, RevShare) to be queried for creators discovered via Farcaster identity resolution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gent The AI agent API (api.bankr.bot/agent) was unreliable and returned 0 results. Switched to the same structured API that bankr.bot frontend uses: - /token-launches/search for handle/wallet lookup - /public/doppler/token-fees for fee amounts (claimable/claimed WETH) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clanker: /search-creator returns `searchedAddress` not `walletAddress`. Also extract verified addresses from `users[]` for better wallet coverage. Heaven: api.heaven.xyz DNS no longer resolves. Added warning comment. All calls fail silently (returns empty arrays). Kept adapter for if/when Heaven restores their API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /search endpoint caps at 5 results per group, causing missing tokens. Switch to /search/paginated with cursor-based pagination (up to 3 pages). Also handle "<0.000001" format from Bankr API in wethToWei. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bags, Believe, and RevShare adapters returned null tokenSymbol causing the fee table to show truncated contract addresses instead of tickers. Now fetches name/symbol from Metaplex Token Metadata PDAs in a single batched RPC call (no new dependencies). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- solana-metadata: use Helius DAS getAssetBatch (1000 mints/call) as primary metadata source when HELIUS_API_KEY is set, with on-chain Metaplex fallback. Handles Token-2022 and tokens without Metaplex. - prices: add x-api-key header to Jupiter Price API calls when JUP_API_KEY is set. Jupiter now requires API key for all tiers. - believe: consolidate 3 separate getProgramAccounts GPA calls into 1 cached call shared across getCreatorTokens, getHistoricalFees, and getLiveUnclaimedFees. Add isMigrated filtering to skip graduated pools. 60s in-memory cache prevents redundant GPA queries within a scan cycle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix all 22 audit issues across 10 platform adapters - Add silent failure logging to all empty catch blocks (C1-C3, H1-H7) - Add HTTP status logging to all fetch helpers (M2-M9, M12) - Log Promise.allSettled rejections in pump, zora, revshare, prices - Sync OrbitingLogos with PlatformIcon (add heaven, bankr, revshare) - Fix loading skeleton breakpoint mismatches with actual components - Add aria-expanded, aria-hidden for accessibility compliance - Remove unused PillNav component (491 lines) and gsap dependency - Add scan-line to prefers-reduced-motion media query - Add Coinbarrel and Raydium platform icons - Cross-chain Farcaster resolution (Solana + EVM) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redesign Coinbarrel and Raydium SVG icons to use filled shapes instead of stroke-only paths so they remain visible at small sizes (h-3 w-3). Split homepage platform pills into two rows of 5 for balanced layout. Sync OrbitingLogos with the updated fill-based icon designs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js requires inline scripts for client-side hydration. Without 'unsafe-inline' in the script-src CSP directive, all 'use client' components fail silently (TrueFocus, Grainient, motion animations). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bels - Fix claim_status logic: don't mark as "claimed" when totalEarned is also 0 (no fee data ≠ claimed). Only mark "claimed" when earned > 0 and unclaimed = 0. - Filter out all-zero fee rows from Bankr adapter (API returned no data) - Add SOL/ETH currency indicator after token amounts in fee table Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Primary method: Bankr Agent API (POST /agent/prompt) with structured JSON prompts. Resolves Twitter handles directly and returns fee data without needing the legacy bearer-token search. - Agent API submits natural language prompt, polls for async job completion - Multi-strategy response parser: JSON extraction → pipe-delimited → regex - Legacy search + Doppler kept as fallback when BANKR_BEARER_TOKEN is set - Requires BANKR_API_KEY env var (bk_ key from bankr.bot/api) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change poll interval from 3s to 2s per Bankr API docs - Increase max polls from 10 to 60 (30s → 2min timeout) - Add `data.success` check on submit response per API spec - Use lowercase `x-api-key` header per reference client - Support BANKR_API_URL env var with fallback to production URL - Log job.error field on failed/cancelled jobs for debugging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Bankr Agent API rewrite introduced 2-min polling windows per call, causing the entire resolve pipeline to exceed the 30s RESOLVE_TIMEOUT_MS. This blocked Base chain fees from appearing on first page loads. - Reduce AGENT_POLL_MAX from 60 to 5 (10s polling window) - Reduce submit timeout from 15s to 8s - Total per-call budget: ~18s (fits within 30s pipeline) - Background resolve still catches slow responses on next load Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agent API jobs take 12s+ even with reduced polling, causing the entire resolve pipeline to exceed the 30s timeout. Legacy APIs (search + Doppler) complete in 2-5s. Swap all three adapter methods to use legacy APIs as primary path with Agent API as fallback only when legacy is unavailable or returns empty results. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: Promise.allSettled waits for ALL adapters. Without
BANKR_BEARER_TOKEN on Vercel, only the Agent API path runs — each call
burns 12-18s polling a job that never completes (jobs take 30s-2min).
Three calls across the 2-stage pipeline = ~36s, exceeding the 30s
RESOLVE_TIMEOUT_MS and returning creator=null ("No results found").
Fix: Single 5s AbortController wrapping the entire submit+poll cycle.
Agent API gets a fair shot but can't starve pump/bags/clanker/zora.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getFeesByHandle runs in parallel with resolveWallets via Promise.all, NOT inside Promise.allSettled with other adapters. A 20s timeout won't block pump/bags/clanker results. The 5s cap stays for resolveIdentity and getHistoricalFees which DO run inside Promise.allSettled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bankr doesn't offer bearer tokens — only API keys (x-api-key header). Added bankrAuthHeaders() helper and HAS_BANKR_AUTH constant so the Search/Doppler pipeline works with the existing BANKR_API_KEY on Vercel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show $SYMBOL with $ prefix when token_symbol is available - Fallback to shortened contract address when symbol is missing - Replace pagination with "Show More" button (15 initial, +15 per click) - Extracted tokenDisplay() helper for consistent formatting Also added HELIUS_API_KEY to Vercel for reliable Solana token metadata resolution (tickers will populate on next fresh resolve). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ProfileHeader: full wallet addresses with copy-to-clipboard, chain-colored badges (purple SOL, blue BASE), collapsible wallet list, platform source icons - PlatformBreakdown: compact 5x2 icon grid with badge counts, summary stats, filter tabs only for platforms with data, improved empty state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add aria-label to copy button and aria-pressed to platform grid toggles - Fix setTimeout cleanup on unmount (useRef + useEffect) - Check execCommand return value before showing success - Limit keyboard tab navigation to only visible tabs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
react was at 19.2.3 while react-dom was at 19.2.4, causing npm install to fail on Vercel with ERESOLVE peer dependency conflict. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Response no longer exposes internal creator_id UUIDs, uses public handle instead (prevents enumeration of internal IDs) - Platform/chain filters validated against explicit enum sets instead of regex sanitization (rejects invalid values with 400) - Creators without a public handle are excluded from results Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mismatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redis was instantiated at module load time, causing Vercel page data collection to fail when UPSTASH_REDIS_REST_URL had trailing whitespace. Now uses lazy init with .trim() on env vars. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds handle_type field ('twitter' | 'github') to leaderboard response.
GitHub handles display as "user (GitHub)" instead of "@user" to avoid
confusion with Twitter handles. Prioritizes twitter_handle over github.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clanker adapter returns inflated total_earned values for some tokens (e.g. 157M ETH worth), producing $566B USD in the leaderboard. Skip records exceeding $50M per token until the adapter is fixed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
availableFees(owner, token) returns fees in the token's own units, not WETH. This caused 157M ETH worth of phantom fees for creators with high-volume meme tokens. Now queries availableFees(owner, WETH) on Base and availableFees(owner, WBNB) on BSC to get correct ETH/BNB-denominated fees. Reset all 6 inflated Clanker records in DB. Root cause: the 2nd param of availableFees is the FEE CURRENCY (what you receive), not the Clanker token you deployed. Passing the token address returned fees in that token's units (e.g. 2.4M CSKILLS) which were then treated as 2.4M ETH ($5B+). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rer links - Remove Heaven from help text (platform removed in migration 015) - Add BSC/ETH chain support: VALID_CHAINS, CHAIN_CONFIG-based native token/decimals/prices, Clanker API chain_id detection, chain-agnostic EVM address lookup - Fix getCreatorUnclaimedUsd: was summing total_earned_usd instead of computing USD from total_unclaimed — alerts fired at wrong thresholds - Show Pump.fun v2 metadata (cashback, fee locked, recipient count) - Show Zora auto-distributed fees instead of misleading $0 unclaimed - Accept wallet addresses in /scan (Solana + EVM) - Add Explorer buttons (Solscan/Basescan/Etherscan/Bscscan) to CA scan and claim notifications Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix BigInt precision loss for EVM 18 decimals (10**18 → 10n**18n) - Fix PostgREST filter injection in /alert set path (unquoted handle) - Consolidate channel membership gate in middleware (remove copy-paste from 3 handlers) - Add logging to all empty catch blocks (bot.ts, poll.ts, lookup.ts, require-channel.ts) - Guard snapshot updates behind successful notifications (prevent lost claim alerts) - Fix deleteAlertRule always returning true (now checks affected rows) - Fix getGroupsForToken returning [] on DB error (now throws to prevent snapshot advance) - Fix getCreatorUnclaimedUsd returning 0 on DB error (now returns null, skips rule) - Fix stopPolling race condition during startup (use pollHandle pattern) - Add error checking to parallel Supabase queries in lookupTokenByAddress - Move Supabase client to module-level in poll worker - Remove dead nativeAmount* fields from LookupResult - Add safeBigInt parse failure logging - Use price map instead of nested ternaries in format.ts - Log full stack trace in poll cycle errors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Integrate three technologies for the OWS Hackathon (Track 05: The Observatory): - x402 pay-per-query endpoints: /api/v2/fees ($0.01), /api/v2/export ($0.05), /api/v2/intelligence ($0.02) — USDC payments on Base via x402 protocol - Allium enrichment: cross-chain transaction history and PnL data layered on top of ClaimScan fee intelligence - OWS wallet resolution: /api/v2/resolve maps OWS wallet names to multi-chain addresses via CLI - Updated docs page with V2 API section and agent access examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- x402 server: fail loud on missing X402_WALLET_ADDRESS instead of empty default - Allium client: guard against empty API key inside fetch function - OWS resolver: distinguish ENOENT from real errors, cache isOWSAvailable - All v2 routes: add maxDuration=60, handle Supabase .single() errors properly (PGRST116 = 404, everything else = 500), add console.error logging - Export route: validate format param against allowlist - Intelligence route: collect Allium errors as array instead of overwriting, surface fee query availability in response - Resolve route: add server-only import, catch OWS errors as 500 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nite guard - OWS resolve: remove prefix matches for base/bsc that collided with testnets (eip155:84532 was mis-classified as mainnet base) - Intelligence route: only include 'allium' in dataSources when data was actually fetched successfully - Fees route: add missing Number.isFinite guard on totalUnclaimedUsd Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- /terms: updated service description to 10 platforms on 4 chains, added V2 API pricing section (x402 per-request USDC payments), added V2 API data collection entry in privacy section, added Allium as sub-processor, updated fee changes clause - /docs: updated counts to 10 launchpads and 4 chains, updated meta descriptions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vercel build collects page data by evaluating route modules, but env vars are not available during that phase. Changed from throw-on-load to log-on-request so the build completes and the check runs at runtime when env vars are available. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PlatformBreakdown called useWallet() during SSR when crawlers hit /og-docs.png (which fell through to [handle] catch-all). Two fixes: - Add ssr: false to PlatformBreakdown dynamic import - Add static og-docs.png to public/ so the URL resolves directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes from full security audit (0 critical, 3 high, 6 medium, 3 low): - H-1: SSRF prevention in /api/flex — host allowlist blocks env var injection - H-3: HMAC replay — require Redis in prod, reject if unavailable - M-1: Anti-enumeration now covers anonymous IPs (40 handle limit) - M-3: CORS no longer reflects non-allowed origins - M-4: x-real-ip only trusted when running on Vercel - M-8: x402 fails closed when wallet address missing in prod - L-1: Upstash limiter retries import on failure instead of caching miss - L-2: Turnstile IP uses trusted Vercel IP, not spoofable x-forwarded-for Reverts ssr:false on PlatformBreakdown (not allowed in Server Components in Next.js 16 — the og-docs.png static file fix from previous commit resolves the WalletContext SSR crash). No exploitation detected in 7-day log audit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…removal M-9: Remove NEXT_PUBLIC_SOLANA_RPC_URL from client bundle. Wallet adapter uses free public RPC (no client components call RPC directly). Paid keys stay server-side only. M-5: Cap tarpit delay to 500ms on handle routes (user-facing pages). Full 5s tarpit reserved for API routes only. Saves serverless budget. M-7: Replace 50k-row JS aggregation with Postgres function get_leaderboard(). Joins fee_records + creators + token_prices in DB, returns pre-ranked paginated results. Migration 024 adds the function. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Postgres enum chain_type needs explicit ::TEXT cast for JOIN and CASE comparisons with text literals. Fixes migration 024 apply failure. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sions, lifecycle hooks - Fix accepts format: single object → array (protocol spec conformance) - Fix CSV injection in /api/v2/export: prefix dangerous chars with single quote - Add dev warning when X402_WALLET_ADDRESS is unset - Add network/facilitator compatibility validation at boot - Add lifecycle hooks (onAfterVerify, onAfterSettle, onSettleFailure) with structured logging - Add bazaar extension for AI agent discoverability on all v2 routes - Add payment-identifier extension for idempotency support - Pin x402 packages to exact 2.8.0 (protocol still young, avoid surprise breaks) - Add @x402/extensions as direct dependency Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps [next](https://github.com/vercel/next.js) from 16.2.1 to 16.2.2. - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](vercel/next.js@v16.2.1...v16.2.2) --- updated-dependencies: - dependency-name: next dependency-version: 16.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Contributor
Author
LabelsThe following labels could not be found: Please fix the above issues or remove invalid values from |
Contributor
Author
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
LW-ARTS
pushed a commit
that referenced
this pull request
May 12, 2026
March 2026 audit cleanup, security hardening & dead code removal
LW-ARTS
pushed a commit
that referenced
this pull request
May 12, 2026
…#43) * fix(avatar): restore rate limit (120/min) + add Upstash negative cache for 404s PR #41 exempted /api/avatar from rate limiting to fix a 429 regression on leaderboard fan-out, but left an amplification vector: ~37^50 distinct cache keys (handle regex 1-50 chars) × up to 2 outbound fetches × 2 MB per miss. An attacker iterating arbitrary handles burns Vercel function minutes and egress bandwidth without ever hitting a real abuse signal. This restores rate limiting on a dedicated bucket sized for the legitimate leaderboard use case (10 avatars/page × 12 pages/min = 120) and adds a 1h negative cache so repeat 404s for the same handle don't re-trigger unavatar+fxtwitter. Changes: - lib/constants.ts: RATE_LIMIT_AVATAR = 120 - lib/rate-limit.ts: avatarLimiter (sliding window, prefix claimscan:rl:avatar) - proxy.ts: include /api/avatar in isRateLimitedPath, dispatch to avatarLimiter - app/api/avatar/route.ts: Upstash get/setex around the unavatar+fxtwitter waterfall (404 cache only, never positive — positives use Vercel CDN s-maxage=86400 already) Closes HIGH #2 from security audit 2026-04-15. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(avatar): apply avatar limit to in-memory fallback path When Upstash is unreachable, avatarLimiter is null and the dispatch falls through to the else branch. Without an isAvatar case there, /api/avatar would inherit the in-memory general limit (15 req/min in prod), tripping on the first leaderboard page load (10 avatars/page). Adds isAvatar branch in the in-memory limiter at 60 req/min (half of the Upstash 120, matching the existing pattern where in-memory limits are roughly halved). Imports RATE_LIMIT_AVATAR for the dev-mode fallback. Caught by advisor review on PR-B before merge. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LW-ARTS
pushed a commit
that referenced
this pull request
May 12, 2026
… allowlist) (#61)
* security(ssrf): share host allowlist between /api/flex and /api/og-download
/api/og-download built its internal fetch from request.nextUrl.origin with
no allowlist, while /api/flex already validated the host against a fixed
set. Extracts the allowlist into lib/internal-fetch.ts and uses it from
both routes so a misconfigured VERCEL_URL / spoofed X-Forwarded-Host can
no longer steer the internal fetch at an attacker-controlled destination.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* security(claim): require SIWS ownership proof in /api/claim/bags
Before: any Turnstile-solving caller could open pending claim_attempts for
a third-party wallet, saturating MAX_ACTIVE_CLAIMS_PER_WALLET or locking
specific mints for 5-minute windows and keeping the real owner in
"Claim already in progress" loops.
After: the client now runs signMessage once per batch, producing an ed25519
signature over a short message bound to {wallet, sorted-mints hash prefix,
issued-at, TTL}. The server re-derives the mints hash, rebuilds the
message, and verifies via Node 24's native WebCrypto Ed25519 — no new
dep. Invalid proofs return 403 before any DB write.
- lib/claim/wallet-proof-msg.ts: shared helpers (hash prefix + message
builder) used by both client and server. No native deps — just
TextEncoder + crypto.subtle.digest, isomorphic.
- lib/claim/wallet-proof.ts: server-only verifier. Parses the 4-line
message, re-checks the mints prefix, enforces a 5-minute TTL with 1-min
clock skew, and runs Ed25519 verify via WebCrypto.
- app/api/claim/bags/route.ts: runs the proof check immediately after
Turnstile and before any Supabase write.
- lib/hooks/useClaimBags.ts: derives the mints prefix, builds the message,
calls wallet.signMessage, and sends the base64 signature in the body.
Rejected signMessage popups short-circuit execute() cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* security(claim): decouple fee token and harden /api/claim/confirm feeTx
Two tangled issues fixed together because the feeTx hardening depends on
the token decoupling:
Token decoupling (Fix 4):
- /api/claim/bags now mints a dedicated feeConfirmToken per batch using
the `fee:<attemptId>` HMAC scope. Returned alongside feeAttemptIdForAuth
in the response.
- /api/claim/confirm feeTx branch verifies with the same `fee:` scope, so
a status-transition token cannot be replayed against the fee branch and
vice versa.
- Client captures the first non-null token pair from its chunked fetches
and uses them for the single feeTx POST.
- consumeHmacToken removed from the status-transition branch. The client
reuses the per-attempt confirmToken across signing → submitted →
confirmed, and the old single-use enforcement silently failed the 2nd
and 3rd transitions (claim succeeded on-chain but claim_attempts.status
stayed at 'signing'). Remaining protections — HMAC, VALID_TRANSITIONS,
optimistic lock, and the new SIWS gate in /api/claim/bags — are enough
to keep an attacker from mutating state they don't own.
feeTx hardening (Fix 2):
- Fetch the claim_attempt and require attempt.wallet_address === feeWallet
so a valid fee token cannot be used with a different feeWallet.
- Check the on-chain fee payer (static_account_keys[0]) matches feeWallet,
blocking submission of someone else's legit fee tx as if it were paid
by the caller.
- consumeHmacToken now runs on the verified success path (single-use),
AFTER on-chain verification but BEFORE the claim_fees insert. The RPC-
outage fallback path does NOT consume so the user can retry.
- claim_fees inserts populate claim_attempt_id (FK added in migration
014), closing the reconciliation gap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(types): TS 5.7+ buffer narrowing + claim_fees.claim_attempt_id
Two strict-typing fixups for the preceding security commits:
- lib/claim/wallet-proof.ts allocates dedicated ArrayBuffers for the
WebCrypto Ed25519 calls. TypeScript 5.7+ narrowed Uint8Array to
`Uint8Array<ArrayBufferLike>`, which failed the BufferSource overload
on crypto.subtle.importKey/verify.
- lib/supabase/types.ts records claim_attempt_id on claim_fees so the
new FK-populating insert passes type checking. The column was added
by migration 014 but the generated types had not been refreshed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(claim): unit tests for SIWS wallet proof verifier
11 cases covering the happy path plus every rejection reason:
- bad_format (wrong line count, tampered line)
- bad_signature (wrong key, wrong length, malformed base64)
- expired (stale + future-dated beyond skew)
- mints_mismatch (wrong mints prefix)
- wallet_mismatch (signed by different key vs claimed)
- computeMintsHashPrefix is order-independent
Uses tweetnacl (already transitively available via @solana/web3.js) to
generate keypairs and real signatures, so these exercise the full
WebCrypto Ed25519 verify path end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(claim): SIWS proof verifies against full mint set, not chunk
Review found a HIGH regression introduced by Fix 1: the client signs a
hash over the FULL selected mint set (always), then chunks into
API_BATCH_SIZE=10 slices and POSTs each to /api/claim/bags. The server
was recomputing the expected hash from the CHUNK in body, so any claim
with >10 mints failed chunk #2+ with 'Invalid wallet ownership proof'.
This broke exactly the high-value case — creators with many unclaimed
tokens.
- Client now sends `fullTokenMints` (the full selected array) alongside
`tokenMints: chunk` on every chunk request.
- Server hashes `fullTokenMints` for proof verification, and separately
verifies that `tokenMints` is a subset of `fullTokenMints` so the
proof can't be reused to create attempts for mints the user didn't
sign over.
- Length bound 200 on `fullTokenMints` to cap hashing work and reject
pathological inputs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* security(claim): consume confirmToken on terminal state transitions
Review found that removing consumeHmacToken entirely from the status
branch left a grief vector: an attacker with a leaked confirmToken could
race the real user and flip a live attempt into 'confirmed' with a junk
txSignature. No funds move (fee path uses the separate fee:-scoped
token) but the attempt row pins to bad state and invalidatePositionsCache
fires unnecessarily.
Restore single-use consumption but narrow it to the two terminal states
the client only writes once — 'confirmed' and 'finalized'. Non-terminal
transitions ('signing', 'submitted', 'failed', 'expired', recovery to
'submitted') stay idempotent so the intended token reuse across
signing → submitted → confirmed still works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(review): fee-scope constant + tighter tests + VERCEL_URL edge case
Review follow-ups (LOW + NIT):
- lib/claim/hmac.ts exports FEE_TOKEN_SCOPE_PREFIX + feeScopedAttemptId so
bags/confirm share a single source of truth for the fee-token scope
instead of hand-typing `fee:` on both sides.
- wallet-proof.test.ts: renamed the "tamper message" case to
"bad_format" (which is what it actually asserts) and added a proper
Ed25519 verify case that tampers the Issued timestamp post-sign (format
still parses, signature must reject). Plus a regression guard ensuring
computeMintsHashPrefix(full) !== computeMintsHashPrefix(chunk).
- lib/internal-fetch.ts: `||` instead of `??` on VERCEL_URL so an empty
string (local vercel dev without linked env) falls through to
NEXT_PUBLIC_APP_URL instead of tripping the SSRF-blocked path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps next from 16.2.1 to 16.2.2.
Release notes
Sourced from next's releases.
Commits
52faae3v16.2.28d0f77bBackport: #92177e151e5fFix CI for glibc linux builds1a319ea[backport] Fix CSS HMR on Safari (#92174)c0edad2Turbopack: exclude metadata routes from server HMR (#92034)d644699Turbopack: enable server HMR for app route handlers (#91466)34de2canext.config.js: Accept an option for serverFastRefresh (#91968)c4779d1[create-next-app] Skip interactive prompts when CLI flags are provided (#91840)edcf19aBackport: TypeScript v6 deprecations for baseUrl and moduleResolution (#92130)eee3f52backport: Move expanded adapters docs to API reference (#92115) (#92129)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)