Skip to content

LookHookInfo/hashcoin

Repository files navigation

mhash — Mining Hash Game (hashcoin.farm)

A Web3 dApp on Base L2 where users stake NFT mining devices to earn $HASH tokens, trade gem launchpad tokens, and register on-chain names. Live at https://hashcoin.farm.


Architecture

┌─────────────────────┐      HTTPS       ┌──────────────────────┐
│  React SPA (Vite)   │ ───────────────> │  Fastify backend     │
│  Vercel             │                  │  Back4app Containers │
│  hashcoin.farm      │                  │  api.hashcoin.farm   │
└─────────────────────┘                  └──────────┬───────────┘
        │                                           │
        │ wagmi + viem (read/write)                 │ viem (read)
        ▼                                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Base L2 (Ankr RPC)                            │
│  Mining / Staking / Name / $HASH / Gem Launchpad contracts       │
└─────────────────────────────────────────────────────────────────┘
                                                    ▲
                                                    │ indexed events,
                                                    │ cached state
                                                    ▼
                                           ┌──────────────────┐
                                           │  Neon Postgres   │
                                           │  (Frankfurt)     │
                                           └──────────────────┘

Frontend (/, src/)

  • React 18 + TypeScript + Vite, Mantine UI, RainbowKit + wagmi + viem for wallet/web3, react-query for fetching, react-router for routing.
  • Reads on-chain data directly via Ankr RPC (browser-side viem).
  • Reads aggregated/indexed data from the backend over HTTP.
  • Deployed as a static bundle to Vercel.

Backend (backend/)

  • Fastify + drizzle-orm + postgres-js + viem.
  • Two responsibilities: HTTP API (/api/*) and chain indexer that follows Base head, persists token/trade/holdings state into Postgres.
  • IPFS upload proxy to Pinata with rate-limit + Origin allowlist.
  • Single-instance only — guarded by a Postgres session-level advisory lock so two replicas can't double-index.

Database: Neon Postgres 16, 9 tables, 6 indexes. Schema in backend/src/db/schema.ts, migration in backend/src/db/migrations/.

External services: Base via Ankr (RPC), Pinata (IPFS pinning), Back4app (container hosting), Vercel (static hosting), Neon (managed Postgres).


Repo layout

.
├── src/                    React SPA
│   ├── api/                react-query hooks + HTTP client
│   ├── components/         UI components
│   ├── views/              route-level pages
│   ├── hooks/              wallet/account hooks
│   └── source/             contract addresses, ABIs, constants
├── public/                 static assets + SPA fallback configs
│                           (_redirects, .htaccess, web.config)
├── backend/
│   ├── src/
│   │   ├── routes/         Fastify route handlers
│   │   ├── chain/          viem client + contract reads
│   │   ├── indexer/        chain head follower (gem launchpad, etc.)
│   │   ├── refreshers/     periodic state refreshers + LRU TTL cache
│   │   ├── db/             drizzle schema + migrations + client
│   │   └── config.ts       zod-validated env schema
│   └── Dockerfile          multi-stage build for Back4app
├── DEPLOY_BACK4APP.md      full deploy walkthrough
├── HANDOFF.md              owner takeover playbook
└── MIGRATION.md            router between the two

Local development

Prerequisites

  • Node.js 22+ (matches Dockerfile runtime)
  • npm 10+
  • Postgres 16 (local, or use a free Neon project)
  • Ankr Base RPC key (free at ankr.com)

Frontend

npm ci
cp .env.example .env       # then fill VITE_* vars
npm run dev                # http://localhost:3000

Backend

cd backend
npm ci
cp .env.example .env       # fill DATABASE_URL, ANKR_RPC_URL, PINATA_JWT
npm run db:push            # apply schema (only needed once per DB)
npm run dev                # http://localhost:3001

The frontend's VITE_API_URL points at http://localhost:3001 by default in .env.example.


Important caveats

  1. VITE_* env vars are baked into the JS bundle at build time — they are not secrets. The frontend's Ankr RPC URL is exposed to anyone who opens DevTools; that's fine because Ankr gates by key with rate limits, not by secrecy. Backend PINATA_JWT and DATABASE_URL are server-only and never reach the bundle.

  2. Backend env vars are read at startup, validated by zod in backend/src/config.ts. In NODE_ENV=production the process refuses to boot unless CORS_ORIGINS and PINATA_JWT are set.

  3. Use Neon's direct (non-pooled) endpoint for DATABASE_URL. The indexer relies on Postgres session-level advisory locks to prevent double-indexing. Neon's pooled endpoint runs pgbouncer in transaction mode, which silently breaks session-level locks. With the direct endpoint and a single backend instance, the lock works correctly. See DEPLOY_BACK4APP.md §7 for details.

  4. Backend is single-instance. Indexer state lives in Postgres (indexer_state.cursor) but the in-process advisory lock means only one replica may run. Horizontal scaling requires either splitting the indexer into a separate worker process or moving the lock to a different mechanism.

  5. CORS allowlist is exact-match, comma-separated origins via CORS_ORIGINS. Wildcards/regex not supported. Each new public frontend URL must be added explicitly.

  6. Smart contract addresses live in src/source/contracts.ts (frontend) and are duplicated where the backend needs them (e.g. backend/src/refreshers/balances.ts). Treat the frontend file as the canonical source.

  7. Indexer health: monitor /api/health/ready — returns 503 if indexer.stale=true or DB ping fails. indexer.behind reports how many blocks behind chain head. Healthy steady state is behind < 5.


API surface

Public HTTP API exposed under /api/*:

  • GET /api/health — liveness (no DB touch).
  • GET /api/health/ready — readiness (DB ping + indexer status).
  • GET /api/hash/supply — $HASH total + circulating supply + per-wallet balances.
  • GET /api/wallet/:addr/{balances,name,role,galxe} — wallet aggregates.
  • GET /api/shop/tools — NFT mining device catalog.
  • GET /api/shop/user/:addr — user's owned tools.
  • GET /api/gems — paginated gem launchpad tokens.
  • GET /api/gems/:addr + /api/gems/:addr/user/:user — token details + user position.
  • GET /api/gems/rates, /api/gems/rate/:nftId — exchange rates.
  • GET /api/gems/mining/:user — user mining state.
  • GET /api/launch/allowance/:addr — launchpad allowance.
  • POST /api/ipfs/upload — IPFS pin via Pinata (Origin-allowlisted, rate-limited).

Deployment & handoff

  • DEPLOY_BACK4APP.md — full walkthrough to deploy backend to Back4app Containers + Neon, plus frontend rebuild.
  • HANDOFF.md — playbook for transferring the live deployment to a new owner's accounts (GitHub, Neon, Pinata, Ankr, Back4app, Vercel, custom domains).
  • MIGRATION.md — entry point pointing at the two above.

Credits

Originally developed by LookHook.info.

About

Development of #Mining Hash by the Look Hook Dev team

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages