Base-network swap DApp with paste-CA token support, auto-detected holdings, and a cached Express backend.
- Frontend: React 19 + Vite 7, Tailwind, Privy auth, viem, 0x Protocol for swap routing
- Backend: Node.js + Express, Upstash Redis (serverless, no Docker)
- Data sources: Zerion API (holdings), GeckoTerminal (token logos), CryptoCompare (ETH price)
- Swap by contract address — paste any ERC-20 CA on Base to trade it
- Auto-detected holdings — your tokens with USD value > $1 show first in the token picker (via Zerion)
- Multi-source logo resolver — Zerion → TrustWallet → 1inch → GeckoTerminal → inline SVG, with localStorage cache
- SWR-style caching — Upstash holds responses for 5 minutes, refreshes in the background every 60 s, serves stale on upstream 429
- Clean disconnect —
wallet_revokePermissions(EIP-2255) clears Rabby / MetaMask / Coinbase Wallet site binding so the next login picks a fresh address
# 1. Install
npm run install:all
# 2. Configure backend secrets
cp backend/.env.example backend/.env
# Then fill in:
# UPSTASH_REDIS_REST_URL (console.upstash.com/redis → REST API tab)
# UPSTASH_REDIS_REST_TOKEN
# ZERION_API_KEY (developers.zerion.io — free tier 100 req/min)
# 3. Run frontend + backend together
npm run devFrontend boots at http://localhost:5173, backend at http://localhost:3001.
| Script | What it does |
|---|---|
npm run dev |
Backend + frontend in parallel (dev) |
npm run dev:backend |
Backend only |
npm run dev:frontend |
Frontend only |
npm start |
Build frontend, then serve prod preview + backend |
npm run build |
Build frontend for production |
npm run install:all |
Install deps in root, backend, and frontend |
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Upstash connectivity check |
| GET | /api/balance/:address |
Cached ETH balance (2 s TTL) |
| GET | /api/eth-price |
Cached ETH/USD (5 s TTL) |
| GET | /api/token-balance/:address/:token |
Cached ERC-20 balance (3 s TTL) |
| GET | /api/block-number |
Cached latest block (1 s TTL) |
| GET | /api/holdings/:address |
Base tokens > $1 via Zerion, SWR 60 s fresh / 300 s stale |
| GET | /api/token-info/:address |
Token metadata + logo via GeckoTerminal (24 h TTL) |
| POST | /api/invalidate-cache |
Manual cache flush (type: 'balance' | 'all') |
| GET | /api/cache-stats |
In-memory hit/miss counters + Upstash key count |
.
├── backend/ # Express + Upstash Redis API
│ ├── server.js
│ ├── .env.example
│ └── package.json
├── frontend/ # React + Vite SPA
│ ├── src/
│ │ ├── components/ # atoms/molecules/organisms
│ │ ├── hooks/ # useHoldings, useTokenFromAddress, useSwap, ...
│ │ ├── config/ # swap.js, privy.js
│ │ └── pages/ # HomePage, SwapPage
│ └── vite.config.js
├── docs/superpowers/specs/ # Design specs
└── package.json # Root: concurrently orchestration
MIT