Explorer Code Review
Date: 2025-02
Scope: Backend (Go), Frontend (Next.js + SPA), libs, deployment, CI.
1. Architecture Overview
| Layer |
Tech |
Purpose |
| API |
Go 1.22, net/http |
REST API (blocks, transactions, addresses, search, stats, Etherscan compat, auth, feature flags). Tiered tracks (1–4) with optional/required auth. |
| Indexer |
Go, go-ethereum, pgx |
Listens to chain (RPC/WS), processes blocks/txs, writes to PostgreSQL. |
| Frontend (live) |
Vanilla JS SPA |
frontend/public/index.html — single HTML + inline script, deployed at https://explorer.d-bis.org. Uses Blockscout-style API, ethers.js from CDN, VMID 2201 RPC. |
| Frontend (dev) |
Next.js 15, React, TypeScript |
frontend/src/ — app + pages router, dev/build only; uses shared libs (api-client, ui-primitives). |
| Libs |
In-repo under backend/libs/, frontend/libs/ |
go-pgconfig, go-logging, go-chain-adapters, go-rpc-gateway, go-http-middleware, go-bridge-aggregator; frontend-api-client, frontend-ui-primitives. |
2. Backend (Go)
2.1 Strengths
- Clear layering:
cmd/main.go → rest.NewServer → SetupRoutes / SetupTrackRoutes. DB, chainID, and auth are configured in one place.
- Reusable libs: DB config (go-pgconfig), RPC gateway (go-rpc-gateway), security headers (go-http-middleware) are extracted and used consistently.
- Validation: Centralized in
validation.go: isValidHash, isValidAddress, validateBlockNumber, validateChainID, validatePagination, validateSearchQuery. Routes validate before calling handlers.
- Nil-DB safety:
requireDB(w) used in all DB-dependent handlers; tests run without a real DB and get 503 where appropriate.
- Error contract:
errors.go defines ErrorResponse / ErrorDetail and writeError, writeNotFound, writeInternalError, etc. Most handlers use them for consistent JSON errors.
- Track architecture: Track 1 (public RPC gateway), 2–4 (auth + tier) with middleware; track routes and auth are clearly separated in
track_routes.go.
- Auth: Wallet nonce + JWT in
auth/wallet_auth.go; address normalization, nonce expiry, and DB-backed nonce storage.
2.2 Issues and Recommendations
| Item |
Severity |
Location |
Recommendation |
| Block not found response |
Low |
blocks.go (handleGetBlockByNumber) |
Uses http.Error(w, fmt.Sprintf("Block not found: %v", err), 404) → plain text. Prefer writeNotFound(w, "Block") (or equivalent) for JSON consistency. Same for handleGetBlockByHash if it uses http.Error. |
| JWT default secret |
Medium |
server.go |
Default JWT_SECRET is logged as warning but still used. Ensure production always sets JWT_SECRET (deployment checklist / env template). |
| CORS for /api/ |
Info |
server.go |
Access-Control-Allow-Origin: * on all /api/ is permissive. Acceptable for public API; tighten if you need origin allowlist. |
| Track 1 routes in routes.go |
Info |
routes.go |
Track 1 handlers are commented out in SetupRoutes and only registered in SetupTrackRoutes. Intentional; no change needed. |
| Indexer chainID |
Low |
indexer/main.go |
chainID := 138 is hardcoded; CHAIN_ID env is not read. Add os.Getenv("CHAIN_ID") with fallback 138 for consistency with API. |
2.3 Security
- Input validation: Hash, address, block number, chain ID, and pagination are validated before DB/RPC.
- Security headers: Applied via go-http-middleware (CSP, X-Frame-Options, etc.); CSP is configurable via
CSP_HEADER env.
- Auth: Nonce is stored and checked; JWT is validated; track middleware enforces tier.
3. Frontend (Next.js / React)
3.1 Strengths
- API client: Single
createApiClient (frontend-api-client) with get / getSafe / post/put/delete; getSafe allows checking ok before setting state and avoids treating 4xx/5xx body as data.
- Response handling: Address and transaction detail pages use
getSafe; search checks response.ok; blocks API normalizes { data } / { items } in blocks.ts.
- UI primitives: Card, Table, Address, Button in frontend-ui-primitives; Table supports
keyExtractor; pages use stable keys (e.g. tx.hash, block.number).
- Hooks: Data-loading functions wrapped in
useCallback with correct deps; useEffect deps avoid exhaustive-deps warnings.
- Block number validation:
blocks/[number].tsx validates params.number and shows "Invalid block number" when invalid.
- No dangerouslySetInnerHTML in React components; no raw
dangerouslySetInnerHTML in the reviewed code.
3.2 Issues and Recommendations
| Item |
Severity |
Location |
Recommendation |
| Frontend test script |
Info |
frontend/package.json |
npm test runs lint + type-check only. Add unit tests (e.g. React Testing Library) if you want component/API coverage. |
| Next.js workspace warning |
Low |
Build output |
Multiple lockfiles (pnpm at parent, npm in repo) trigger Next.js warning. Set outputFileTracingRoot in next.config.js or align lockfile strategy. |
| Transactions list API |
Low |
pages/transactions/index.tsx |
Uses raw fetch + response.json(); no response.ok check before `setTransactions(data.data |
3.3 TypeScript and Lint
- Types are used for API responses (AddressInfo, Transaction, Block, SearchResult).
- Lint and type-check pass; no critical warnings.
4. SPA (frontend/public/index.html)
4.1 Strengths
- Ethers.js loading: Multiple CDNs with fallback;
ethersReady event for dependent code.
- RPC:
getRpcUrl() with health check and failover; rpcCall uses getRpcUrl().
- Constants:
FETCH_TIMEOUT_MS, RPC_HEALTH_TIMEOUT_MS, FETCH_MAX_RETRIES, RETRY_DELAY_MS, API_BASE; window.DEBUG_EXPLORER gates console.log/warn.
- rAF cleanup:
_blocksScrollAnimationId cancelled in switchToView and before re-running block list animation.
- XSS mitigation:
escapeHtml and encodeURIComponent used in breadcrumbs, watchlist, block cards, tx rows, and API error messages; block breadcrumb identifier escaped.
- CSP: Meta CSP present; comment notes
unsafe-eval required by ethers v5 UMD.
4.2 Issues and Recommendations
| Item |
Severity |
Location |
Recommendation |
| File size |
Info |
index.html |
~4.6k+ lines in one file. Hard to maintain. Consider splitting into separate JS files (e.g. app.js, views.js, rpc.js) or migrating critical flows to the Next app. |
| Duplicate logic |
Low |
SPA vs Next |
Blocks/transactions/addresses exist in both SPA and Next.js pages. Document which is canonical for production (SPA) and that Next is for dev/build only. |
| CSP unsafe-eval |
Info |
CSP |
Required by ethers v5 UMD. If you upgrade to a build (e.g. ethers v6 without UMD), you can remove unsafe-eval. |
5. Libs (backend/libs, frontend/libs)
- go-pgconfig: Used by API and indexer; loads from env, builds pool config. No issues.
- go-rpc-gateway: Cache (memory/Redis), rate limiter (memory/Redis), RPC gateway. Track 1 uses it; no duplicate in-tree.
- go-http-middleware: Security headers with configurable CSP. Used in server.
- frontend-api-client: Axios-based client with
get/getSafe/post/put/delete; ApiResponse/ApiError types. Used by blocks, addresses, transactions services.
- frontend-ui-primitives: Card, Table, Address, Button. Used by Next pages; Table has
keyExtractor.
6. Deployment and CI
- CI: Backend tests + build; frontend install +
npm test + build; lint job runs go vet, npm run lint, npm run type-check. Submodules checked out recursively.
- Deployment: Docs reference VMID 5000, nginx, Blockscout, RPC VMID 2201; env templates and systemd examples exist under
deployment/.
- Recommendation: Ensure production sets
JWT_SECRET, RPC_URL, CHAIN_ID, and DB env; run migrations before starting API/indexer.
7. Testing
- Backend:
go test ./... passes; api/rest tests run without DB and accept 200/503/404 as appropriate.
- Frontend:
npm test (lint + type-check) and npm run build succeed.
- E2E: Playwright (15 tests) against explorer.d-bis.org; paths, nav, breadcrumbs, block detail covered.
- Gaps: No backend integration tests with real DB; no frontend unit tests; indexer has no test files. Optional: add integration test job (e.g. with testcontainers) and a few React tests for critical pages.
8. Summary Table
| Area |
Grade |
Notes |
| Backend structure |
A |
Clear routes, validation, error handling, nil-DB safety. |
| Backend security |
A- |
Validation and auth in place; JWT default secret and block 404 JSON to tighten. |
| Frontend (Next) |
A |
Type-safe API usage, getSafe, stable keys, validation. |
| SPA (index.html) |
B+ |
Escape/encode in place, rAF cleanup, constants; single large file. |
| Libs |
A |
Used consistently; no duplication. |
| CI/CD |
A |
Tests, build, lint; submodules. |
| Docs |
A- |
README, testing, extraction plan, frontend task list. |
Overall: The explorer codebase is in good shape: clear architecture, consistent validation and error handling, and sensible use of shared libs. The main follow-ups are minor (block 404 JSON, indexer CHAIN_ID env, transactions list response.ok, and optional tests/docs).
9. Recommended next steps – completed
| Step |
Status |
| Transactions list: use safe response (ok check) |
Done: transactionsApi.listSafe used in frontend/src/pages/transactions/index.tsx. |
| Production: JWT_SECRET + migrations |
Done: docs/PRODUCTION_CHECKLIST.md and note in deployment/ENVIRONMENT_TEMPLATE.env. |
| Optional: frontend unit test |
Done: Vitest + frontend/libs/frontend-api-client/client.test.ts; run pnpm run test:unit (after pnpm install in frontend). |
| Optional: backend integration test |
Documented in backend/README_TESTING.md (current suite runs without DB; note added for optional DB/testcontainers). |
Run verification (as of last run):
cd backend && go test ./...: all pass (api/rest, benchmarks).
cd frontend && npm test (lint + type-check): pass. Test files excluded from tsconfig so type-check does not require Vitest.
cd frontend && pnpm run test:unit: 2 Vitest tests pass (api client getSafe).
make test-e2e: Playwright against live site; address/root tests made more resilient (timeouts and selector fallbacks). Run when needed; live URL may vary.
10. Remaining optional items (all completed)
| Item |
Status |
| Next.js workspace warning |
Done: Comment added in frontend/next.config.js; align package manager in frontend or ignore for dev/build. (Next 14 does not support outputFileTracingRoot in config; standalone trace uses project root.) |
| CORS |
Done: CORS_ALLOWED_ORIGIN env in server.go; default *, set to e.g. https://explorer.d-bis.org to restrict. Documented in deployment/ENVIRONMENT_TEMPLATE.env. |
| SPA file size |
Done: main app script extracted to frontend/public/explorer-spa.js (~3.5k lines); index.html now ~1.15k lines. Deploy scripts copy explorer-spa.js (e.g. deploy-frontend-to-vmid5000.sh, deploy.sh). |
| SPA vs Next canonical |
Done: README.md states production serves the SPA, Next.js is for local dev and build validation only. |
| CSP unsafe-eval |
Done: comment in index.html CSP meta updated: "Can be removed when moving to ethers v6 build (no UMD eval)." |
| Further product work |
See docs/EXPLORER_ADDITIONAL_RECOMMENDATIONS.md (i18n, event log decoding, token list, health endpoint, etc.). |