Cyberpunk GameFi on Base. Mint augmented fighters, battle in high-stakes arenas, climb the global rank, earn $ARENA.
- Overview
- Live Contracts
- Architecture
- Repository Layout
- Quick Start
- Frontend
- Backend / Indexer
- Smart Contracts
- Database Schema
- NFT Collection
- Audit & Security
- Testnet Workflow (Base Sepolia)
- Deployment
- Environment Variables
- Scripts Reference
- License
Arena Protocol is a fully on-chain combat game with off-chain analytics. Players mint ERC-721 fighters with random stats, enter PvE/PvP battles backed by an ERC-20 reward economy, trade fighters on a 2 % fee marketplace, and stake $ARENA for daily rewards. The whole thing runs in a Telegram Mini App or a desktop browser via wagmi + RainbowKit.
The smart-contract layer is deployed and verified on Base Mainnet. A separate Express + WebSocket indexer reads on-chain events into PostgreSQL so the front-end can render leaderboards and battle history without RPC round-trips.
All addresses verified on Basescan with solc 0.8.25, EVM
paris, optimizer 200 runs, OpenZeppelin v4.9.6.
| Contract | Address | Verified |
|---|---|---|
ArenaCoin (ARENA) |
0x3b855F88…14b5 |
✅ |
ArenaChampion |
0x68f08b00…486A |
✅ |
ArenaBattle |
0xF6fc2B6a…71CF |
✅ |
ArenaPvP |
0xd0C4Af12…7533 |
✅ |
ArenaMarketplace |
0x67817157…698E |
✅ |
Pending (require ETH top-up): ArenaStaking, ArenaRewardVault, ArenaLeaderboard. A successor token ArenaCoinV2 is also drafted in contracts/ — see the audit for the rationale.
┌─────────────────────────────────────────────────────────────────────┐
│ BASE MAINNET (8453) │
│ │
│ ArenaCoin ArenaChampion ArenaBattle ArenaPvP Marketplace │
│ ▲ ▲ │ │ ▲ │
│ │ │ ▼ ▼ │ │
└──────┼──────────────┼──────────────┼────────────┼───────────┼───────┘
│ │ │ │ │
│ Events │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────┐
│ backend/indexer.js — ethers.js WebSocket subscription │
│ ↓ writes to PostgreSQL (Drizzle ORM) │
├─────────────────────────────────────────────────────────────────────┤
│ artifacts/api-server (Express 5, REST, OpenAPI 3.1) │
│ ↑ orval-generated React Query hooks │
├─────────────────────────────────────────────────────────────────────┤
│ artifacts/arena-protocol (React 19 + Vite 7 + wagmi 2) │
│ ↳ also runnable as a Telegram Mini App via @twa-dev/sdk │
└─────────────────────────────────────────────────────────────────────┘
artifacts-monorepo/
├── artifacts/
│ ├── api-server/ # Express 5 REST API (TS)
│ ├── arena-protocol/ # React + Vite frontend
│ └── mockup-sandbox/ # Vite preview server for component variants
├── contracts/ # Solidity sources (Hardhat)
│ ├── ArenaCoin.sol # V1 ERC20 (already on-chain)
│ ├── ArenaCoinV2.sol # V2: cap + permit + roles + burnable
│ ├── ArenaFighterNFT.sol
│ ├── ArenaBattle.sol
│ ├── ArenaStaking.sol
│ ├── ArenaRewardVault.sol
│ ├── ArenaMarketplace.sol
│ ├── ArenaLeaderboard.sol
│ └── deployed/ArenaCoin.sol # Verified source for the live V1 token
├── backend/
│ ├── server.js # Express + WebSocket relay
│ └── indexer.js # ethers.js event indexer → Postgres
├── hardhat-scripts/
│ ├── deploy.js
│ ├── deploy-arena-coin-v2.js
│ ├── verify.js
│ ├── verify-arenacoin.js
│ ├── match-bytecode.js
│ ├── check-verified.js
│ └── inspect-bytecode.js
├── lib/
│ ├── api-spec/ # OpenAPI 3.1 spec + orval codegen config
│ ├── api-client-react/ # Generated React Query hooks
│ ├── api-zod/ # Generated Zod schemas
│ └── db/ # Drizzle schema + DB client
├── scripts/
│ └── seed-marketplace.ts # Insert 6 demo NFT listings
├── contracts.deployed.json # Single source of truth for on-chain addresses
├── AUDIT.md # Audit of all 7 local contracts
├── AUDIT_ArenaCoin.md # Focused audit of the live ArenaCoin V1
├── hardhat.config.js
├── pnpm-workspace.yaml
└── replit.md
# 1. Install (root)
pnpm install
# 2. Configure env
cp .env.example .env
# fill in DATABASE_URL, ETHERSCAN_API_KEY (works for Basescan), and PRIVATE_KEY only if deploying
# 3. Push DB schema
pnpm --filter @workspace/db push
# 4. Seed the marketplace with the 6 Arena Champions
pnpm tsx scripts/seed-marketplace.ts
# 5. Run everything (3 separate terminals or use the configured workflows)
pnpm --filter @workspace/api-server dev # → http://localhost:5001
pnpm --filter @workspace/arena-protocol dev # → http://localhost:5173
node backend/indexer.js # → on-chain event indexerartifacts/arena-protocol/ is a React 19 + Vite 7 SPA wired with wagmi 2 + viem for chain reads/writes. Key pages:
| Route | Purpose |
|---|---|
/ |
Hero, live stats, marketing CTAs |
/mint |
Mint a fighter NFT (random stats + rarity) |
/arena |
PvE & PvP battles — entry 10 ARENA, 18 ARENA reward, 2 ARENA burn |
/leaderboard |
Top players sorted by wins |
/market |
Fighter NFT marketplace (browse / buy / list / cancel) |
/profile |
Per-address stats + battle history |
- React Query hooks auto-generated by orval from the OpenAPI spec — never write a fetcher by hand.
- Tailwind + shadcn/ui + framer-motion for the cyberpunk look.
- Telegram WebApp SDK initialised at boot — works seamlessly inside
@twa-dev. - Wagmi config in
src/lib/wagmi.tsships mainnet-only by default; setVITE_USE_TESTNET=trueto add Base Sepolia for staging builds.
backend/indexer.js opens a WebSocket subscription to Base Mainnet, decodes BattleResult, Listed, Sold, Cancelled, and Transfer events, and writes them to PostgreSQL via Drizzle. The Express API in artifacts/api-server/ then serves them as REST endpoints described in lib/api-spec/openapi.yaml:
| Method | Path | Description |
|---|---|---|
GET |
/api/healthz |
Health check |
GET |
/api/leaderboard |
Top players by wins |
GET |
/api/players/:address |
Single-player stats |
GET |
/api/battles |
Battle history (filter by player) |
POST |
/api/battles |
Record a battle |
GET |
/api/market/listings |
Active marketplace listings |
Every change to openapi.yaml regenerates both the Zod schemas (@workspace/api-zod) and the React Query hooks (@workspace/api-client-react) via pnpm --filter @workspace/api-spec codegen.
Built with Hardhat 2.28, dual-compiled with solc 0.8.24 + 0.8.25 (Cancun), tested via the verification scripts in hardhat-scripts/.
| File | Role |
|---|---|
ArenaCoin.sol |
V1 ERC20 — fixed supply, 1 M ARENA, currently live on Base |
ArenaCoinV2.sol |
V2 ERC20 — capped, mintable via MINTER_ROLE, permit, burnable |
ArenaFighterNFT.sol |
ERC721 fighter with strength / speed / intelligence / rarity |
ArenaBattle.sol |
PvE battle engine (10 entry / 18 reward / 2 burn) |
ArenaStaking.sol |
Stake ARENA, accrue 1 % daily (treasury-funded) |
ArenaRewardVault.sol |
Reward pool for battles |
ArenaMarketplace.sol |
NFT trading, 2 % protocol fee |
ArenaLeaderboard.sol |
On-chain leaderboard write surface |
# Status check across all 5 deployed contracts
node hardhat-scripts/check-verified.js
# Brute-force compiler settings against on-chain bytecode (handy when you didn't deploy it yourself)
npx hardhat run hardhat-scripts/match-bytecode.js --network baseDrizzle ORM, PostgreSQL. See lib/db/src/schema/arena.ts.
players ─ address (pk), total_wins, total_battles, total_rewards, fighters, updated_at
battles ─ id (pk), player, fighter_id, win, reward, mode (PvE|PvP), tx_hash, timestamp
market_listings ─ token_id (pk), seller, price, rarity, strength, speed, intelligence,
wins, losses, active, listed_at
Six on-brand Champions live in artifacts/arena-protocol/public/nfts/ with metadata in public/nfts/metadata.json (OpenSea-compatible, 2 % seller_fee_basis_points).
| # | Slug | Class | Rarity | STR / SPD / INT |
|---|---|---|---|---|
| 1 | cyber-samurai | Samurai | Epic | 78 / 92 / 71 |
| 2 | neon-brawler | Brawler | Common | 88 / 64 / 42 |
| 3 | quantum-mage | Mage | Rare | 38 / 71 / 96 |
| 4 | chrome-assassin | Assassin | Epic | 67 / 99 / 74 |
| 5 | void-titan | Titan | Legendary | 100 / 38 / 62 |
| 6 | pulse-knight | Knight | Rare | 84 / 70 / 76 |
Run pnpm tsx scripts/seed-marketplace.ts to insert them as marketplace listings; the front-end will render them via src/lib/fighters.ts.
| Document | What it covers |
|---|---|
AUDIT.md |
All 7 local contracts — 5 High, 6 Medium, 7+ Low. Risk 4/10 — DO NOT mainnet-launch the rest as-is. |
AUDIT_ArenaCoin.md |
Focused audit of the live ArenaCoin V1. No code bugs. Two HIGH operational findings (treasury concentration & inflationary reward economy). Risk 3/10. |
Top action items:
- Move 970k ARENA from the deployer EOA into a Safe multisig (https://app.safe.global/).
- Decide on the reward-pool funding model (treasury-funded fixed pool vs.
ArenaCoinV2withMINTER_ROLE). - Test
ArenaStaking/ArenaRewardVaultend-to-end on Base Sepolia before any further mainnet deploy.
base-sepolia (chainId 84532) is configured in hardhat.config.js. Get free ETH from:
Deploy V2 to testnet:
ARENA_TREASURY_ADDRESS=0xYourMultisig \
npx hardhat run hardhat-scripts/deploy-arena-coin-v2.js --network base-sepoliaVerify the same way you would on mainnet — the Etherscan v2 multichain endpoint is already wired. Set VITE_USE_TESTNET=true in artifacts/arena-protocol/.env.local to flip the front-end to staging.
The repo is set up for Replit autoscale deployment. Each artifact runs on its own port and is reverse-proxied behind a single domain. To publish:
- Push the database schema in production:
pnpm --filter @workspace/db push. - Set production env vars (especially
DATABASE_URL,ETHERSCAN_API_KEY, RPC URLs). - Click Publish in the workspace; Replit handles TLS, health checks, and routing.
For the front-end alone you can also build a static bundle: pnpm --filter @workspace/arena-protocol build and serve dist/ from any CDN. The contract layer is already on-chain and requires no further deployment.
| Variable | Required by | Notes |
|---|---|---|
DATABASE_URL |
api-server, indexer, seed | Postgres connection string |
ETHERSCAN_API_KEY |
hardhat verify | One key works for Base Mainnet & Base Sepolia (v2 API) |
PRIVATE_KEY |
hardhat deploy | Without 0x prefix; only for deploys |
BASE_RPC_URL |
indexer | Defaults to https://mainnet.base.org |
BASE_SEPOLIA_RPC_URL |
hardhat | Defaults to https://sepolia.base.org |
ARENA_TREASURY_ADDRESS |
deploy V2 | Multisig (Safe). All initial supply + admin role goes here |
ARENA_INITIAL_SUPPLY |
deploy V2 | Default 1000000 |
ARENA_MAX_SUPPLY_CAP |
deploy V2 | Default 100000000 (immutable after deploy) |
ARENA_GRANT_MINTER_TO |
deploy V2 | Comma-separated addresses to receive MINTER_ROLE |
VITE_USE_TESTNET |
frontend | true to enable Base Sepolia |
VITE_BASE_RPC_URL |
frontend | Optional override |
VITE_BASE_SEPOLIA_RPC_URL |
frontend | Optional override |
ARENA_*_ADDRESS |
indexer / api | Per-contract, with sensible mainnet defaults |
A complete template lives in .env.example.
| Command | Purpose |
|---|---|
pnpm install |
Install all workspace dependencies |
pnpm run typecheck |
Project-wide TS typecheck (composite refs) |
pnpm run build |
Typecheck, then build every package |
pnpm --filter @workspace/api-spec codegen |
Regenerate Zod + React Query from OpenAPI |
pnpm --filter @workspace/db push |
Apply Drizzle schema to the configured DB |
pnpm tsx scripts/seed-marketplace.ts |
Seed the 6 Arena Champion marketplace listings |
npx hardhat compile |
Compile all Solidity sources |
npx hardhat run hardhat-scripts/deploy.js --network base-sepolia |
Deploy the original 7-contract suite |
npx hardhat run hardhat-scripts/deploy-arena-coin-v2.js --network base-sepolia |
Deploy ArenaCoinV2 to testnet |
node hardhat-scripts/check-verified.js |
Check Basescan verification status |
MIT — see LICENSE.