potatopad-bench-2026-07-17
RPC proxy & upload endpoints trust a spoofable X-Forwarded-For for rate limiting (trivially bypassable)
What was found
AntFleet's two-model consensus review (Claude Opus 4.7 + GPT-5.5) ran against 3 PRs on [AntFleet/bench-potatopad](https://github.com/AntFleet/bench-potatopad), a methodology fork of [itsfriedpotato/potatopad](https://github.com/itsfriedpotato/potatopad) — a direct-to-Uniswap-V3 token launchpad. The review surfaced 5 unanimous findings (4 MEDIUM, 1 LOW) across the smart-contract, web-API, and on-chain trade surfaces.
---
MEDIUM — RPC proxy & upload endpoints trust a spoofable X-Forwarded-For for rate limiting
In web/app/api/rpc/route.ts (and the Pinata-backed api/upload endpoint), the rate-limit key is derived from x-forwarded-for / x-real-ip without verifying the request actually transited a trusted proxy. Any client can set X-Forwarded-For: <random> per request and evade the limit entirely. On a single-instance deploy with no CDN in front, this makes both the Alchemy RPC proxy and the Pinata-quota-burning upload endpoint effectively unlimited.
Fix: only trust x-forwarded-for when a fixed upstream proxy is guaranteed, and additionally cap by an unspoofable key (session / wallet signature) plus a global ceiling.
---
MEDIUM — Missing swap deadline / MEV window (swaps rely solely on amountOutMinimum)
web/lib/pool.ts uses SwapRouter02's ExactInputSingleParams, which has no deadline field. A signed-but-unmined swap can sit in the mempool and be executed later when it is sandwich-profitable, bounded only by slippage. The TradeWidget offers a 10% slippage tier on low-liquidity single-sided pools — extreme sandwich exposure.
Fix: route via multicall(deadline, exactInputSingle) on SwapRouter02 (which exposes a deadline-checked multicall) or a router variant with a native deadline, and reconsider the default slippage tier.
---
MEDIUM — /api/tokens thundering herd on cold cache (no single-flight)
When the cache is empty or just expired, N concurrent requests each run a full scan() — many getLogs + getBlock calls against the same Alchemy key, amplifying load exactly when the cache is meant to prevent it.
Fix: add an in-flight promise gate so concurrent waiters await one scan.
---
MEDIUM — Dev-buy vs anti-snipe MAX_WALLET interaction reverts the whole launch
In PotatoPad._devBuy / createToken, an over-sized dev-buy during the anti-snipe window produces output above MAX_WALLET (5%), which reverts inside PotatoToken._update — rolling back the entire launch (CREATE2, pool creation, LP mint) and wasting significant gas. Flagged as policy-review: bound the dev-buy output on-chain (partial-fill + refund) or exempt the creator's own dev-buy.
---
LOW — Buy flow accepts amounts above the user's ETH balance
TradeWidget validates sell amounts against tokenBalance but not buy amounts against native ETH balance, so an oversized buy submits and fails in the wallet/RPC instead of being caught in the UI.
Fix: add a buy-side balance check (including a gas buffer) to the button disabled state and the onBuy guard.
Evidence
- Benchmark repo (fork): AntFleet/bench-potatopad
- Smart-contract core bench PR: AntFleet/bench-potatopad#1 (1 finding)
- Web server / API bench PR: AntFleet/bench-potatopad#2 (2 findings)
- On-chain trade / pool bench PR: AntFleet/bench-potatopad#3 (2 findings)
- Source repo: itsfriedpotato/potatopad