Galactic Invaders
Play-to-Earn retro arcade game on BNB Smart Chain. Players connect wallets, play a Space Invaders-style game, and compete on a weekly leaderboard for token rewards.
Overview
Galactic Invaders is a complete Web3 game with two smart contracts and a React frontend:
- GalacticToken (GLCT) — A BEP-20 token used for paid play sessions and weekly rewards
- GalacticGame — Manages game sessions, leaderboard (top 50), and weekly reward distribution
- Frontend — React 19 app with HTML5 Canvas game engine, Wagmi wallet integration, and Reown AppKit for multi-wallet support
How It Works
- Player connects wallet via Reown AppKit (MetaMask, TrustWallet, WalletConnect, etc.)
- Player starts a game session — 1 free play per day, or 50 GLCT for extra plays
- Player plays the 90-second arcade game and earns a score
- Score is submitted on-chain with an anti-tamper hash
- Top 50 scores appear on the leaderboard
- Every week, the owner distributes the reward pool to the #1 player
Requirements
| Tool | Version | Purpose |
|---|---|---|
| Node.js | 18+ (recommended 20+) | Runtime for all build tools |
| npm | 9+ | Package manager (comes with Node) |
| A wallet | — | MetaMask, TrustWallet, etc. with BNB for gas |
| BNB (gas) | ~0.05 BNB | For deploying contracts on BSC |
Project Structure
contracts/
contracts/
GalacticToken.sol # BEP-20 token
GalacticGame.sol # Game logic & leaderboard
scripts/
deploy.js # Deployment script
hardhat.config.js # Network config
.env # Private key (you create this)
frontend/
src/
wagmi.js # Contract addresses & network config
main.jsx # App entry with wallet providers
App.jsx # Main app component
game/ # Game engine, audio, particles, etc.
components/ # React UI components
hooks/ # Contract interaction hooks
contracts/ # ABI JSON files
public/ # Static files
index.html # HTML entry point
vite.config.js # Vite build config
Install Dependencies
From the project root directory, install everything:
# Install all dependencies (contracts + frontend)
npm install
This uses npm workspaces to install both contracts/ and frontend/ packages.
Environment Setup
1. Create the contracts .env file
# contracts/.env
PRIVATE_KEY=0xYOUR_DEPLOYER_PRIVATE_KEY_HERE
.env file is git-ignored by default.
2. Get BNB for gas
| Network | How to get gas |
|---|---|
| BSC Testnet | Use the BNB Testnet Faucet to get free tBNB |
| BSC Mainnet | Purchase BNB on any exchange and send to your deployer wallet |
Get Reown Project ID
Reown AppKit requires a free project ID for wallet connections.
Go to cloud.reown.com and create a free account
Create a new project and copy the Project ID
Open frontend/src/wagmi.js and replace the placeholder:
// Change this line:
export const REOWN_PROJECT_ID = "YOUR_PROJECT_ID";
// To your actual project ID:
export const REOWN_PROJECT_ID = "abc123def456...";
Deploy to BSC Testnet
Test everything on BSC Testnet first before going to mainnet.
Make sure contracts/.env has your private key and the wallet has tBNB.
Deploy the contracts:
cd contracts
npx hardhat run scripts/deploy.js --network bscTestnet
You will see output like:
GalacticToken deployed to: 0xAbC123...
GalacticGame deployed to: 0xDeF456...
Reward pool funded with 100,000 GLCT
Save these addresses. You need them for the frontend config.
Deploy to BSC Mainnet
1. Add BSC Mainnet to Hardhat config
Open contracts/hardhat.config.js and add the mainnet network:
module.exports = {
solidity: "0.8.20",
networks: {
bscTestnet: {
url: "https://data-seed-prebsc-1-s1.binance.org:8545",
chainId: 97,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
// Add this block:
bscMainnet: {
url: "https://bsc-dataseed1.binance.org",
chainId: 56,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
},
};
2. Deploy
cd contracts
npx hardhat run scripts/deploy.js --network bscMainnet
Update Frontend Addresses
After deploying contracts, update the frontend to point to your new addresses.
Open frontend/src/wagmi.js and update:
// Replace with YOUR deployed addresses
export const CONTRACTS = {
token: "0xYOUR_TOKEN_ADDRESS",
game: "0xYOUR_GAME_ADDRESS",
};
Switch network to BSC Mainnet
In the same file, update the imports and network order:
import { http } from "wagmi";
import { bsc, bscTestnet } from "@reown/appkit/networks";
// Put your primary network first
export const networks = [bsc, bscTestnet];
export const transports = {
[bsc.id]: http("https://bsc-dataseed1.binance.org"),
[bscTestnet.id]: http("https://data-seed-prebsc-1-s1.binance.org:8545"),
};
networks array is the default. For mainnet, put bsc first. For testing, put bscTestnet first.
Contract Overview
GalacticToken.sol (BEP-20)
Standard ERC-20 token with owner-only minting.
| Property | Value |
|---|---|
| Initial Supply | 1,000,000 tokens (minted to deployer) |
| Decimals | 18 |
| Mintable | Yes (owner only) |
| Standard | OpenZeppelin ERC20 + Ownable |
GalacticGame.sol
Game session manager, leaderboard, and reward distribution.
| Feature | Details |
|---|---|
| Free Play | 1 per day per wallet (24h cooldown) |
| Paid Play | Costs playCost tokens per session |
| Leaderboard | Top 50 scores, sorted descending |
| Weekly Rewards | Owner distributes reward pool to #1 player |
| Anti-Tamper | Game hash submitted with score for verification |
Change Game Values
Token economics (smart contract)
Open contracts/scripts/deploy.js to change these values before deploying:
// Cost per paid game session (in tokens)
const PLAY_COST = ethers.parseEther("50");
// Weekly reward for #1 player
const WEEKLY_REWARD = ethers.parseEther("1000");
// Initial reward pool deposit
await game.depositRewards(ethers.parseEther("100000"));
Game settings (frontend)
Open frontend/src/game/constants.js to tune gameplay:
export const CANVAS_WIDTH = 800; // Game canvas width (px)
export const CANVAS_HEIGHT = 600; // Game canvas height (px)
export const SHIP_SPEED = 5; // Player movement speed
export const BULLET_SPEED = 7; // Player bullet speed
export const ALIEN_BULLET_SPEED = 4; // Enemy bullet speed
export const ALIEN_ROWS = 5; // Rows of aliens per wave
export const ALIEN_COLS = 10; // Columns of aliens per wave
export const INITIAL_LIVES = 3; // Starting lives
export const POINTS_PER_KILL = 10; // Base points per alien
export const WAVE_BONUS = 200; // Bonus for clearing a wave
export const SPEED_MULTIPLIER = 0.15; // Alien speed increase per wave
export const POWERUP_CHANCE = 0.03; // 3% drop chance per kill
export const GAME_DURATION = 90; // Game length in seconds
Token Name & Symbol
To change the token name and symbol, edit contracts/contracts/GalacticToken.sol before deploying:
constructor() ERC20("Galactic Token", "GLCT") Ownable(msg.sender) {
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}
Change "Galactic Token" to your token name and "GLCT" to your symbol. You can also change 1_000_000 to adjust the initial supply.
UI Branding & Colors
Game title
Search for GALACTIC INVADERS and GALACTIC / INVADERS in these files:
frontend/src/components/Header.jsx— Header titlefrontend/src/App.jsx— Home screen titlefrontend/index.html— Browser tab title
Colors
The main colors are used throughout as Tailwind classes and inline styles:
| Color | Hex | Usage |
|---|---|---|
| Neon Green | #39ff14 | Primary text, borders, UI elements |
| Neon Blue | #00f0ff | Accents, scores, highlights |
| Red | #ff3939 | Warnings, timer low, enemy bullets |
| Dark BG | #0a0a0a | Background |
To change colors, search and replace the hex codes across the frontend/src/ files.
Wallet modal accent
In frontend/src/main.jsx, change the AppKit theme:
themeVariables: {
"--w3m-accent": "#39ff14", // Change this color
},
Network Configuration
All network settings live in two files:
contracts/hardhat.config.js
Controls which networks you can deploy to. Add any EVM chain here.
frontend/src/wagmi.js
Controls which networks the frontend connects to. Must match where your contracts are deployed.
Supported networks from Reown AppKit
You can import any chain from @reown/appkit/networks:
import {
bsc, // BSC Mainnet (56)
bscTestnet, // BSC Testnet (97)
mainnet, // Ethereum (1)
polygon, // Polygon (137)
arbitrum, // Arbitrum (42161)
avalanche, // Avalanche (43114)
hardhat, // Local dev (31337)
} from "@reown/appkit/networks";
Deploy on Vercel
Build the frontend locally to verify it works:
cd frontend
npm run build
This creates the frontend/dist/ folder with all static files.
Install the Vercel CLI:
npm install -g vercel
Deploy from the frontend directory:
cd frontend
vercel
When prompted:
- Set up and deploy? Yes
- Which scope? Your account
- Link to existing project? No
- Project name? galactic-invaders (or your choice)
- Directory with source code? ./
- Override settings? No (Vercel auto-detects Vite)
Deploy to production:
vercel --prod
Your site will be live at https://your-project.vercel.app
wagmi.js with your contract addresses and Reown Project ID before building and deploying.
Deploy on cPanel
Build the frontend:
cd frontend
npm run build
Upload the dist/ folder contents to your cPanel hosting:
- Open cPanel → File Manager
- Navigate to
public_html/(or your domain's root folder) - Upload all files and folders from inside
frontend/dist/
Fix client-side routing — create a .htaccess file in public_html/:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
Visit your domain — the game should load.
Deploy on Netlify
Build the frontend:
cd frontend
npm run build
Drag and drop the frontend/dist/ folder onto app.netlify.com/drop
Or use the Netlify CLI:
npm install -g netlify-cli
cd frontend
netlify deploy --dir=dist --prod
Add a redirect rule for client-side routing. Create frontend/public/_redirects:
/* /index.html 200
Then rebuild and redeploy.
Weekly Reward Distribution
As the contract owner, you must call distributeWeeklyReward() once per week to pay the #1 player.
Using Hardhat console
cd contracts
npx hardhat console --network bscMainnet
Then run:
const game = await ethers.getContractAt(
"GalacticGame",
"0xYOUR_GAME_ADDRESS"
);
const tx = await game.distributeWeeklyReward();
await tx.wait();
console.log("Reward distributed!");
Mint Additional Tokens
The deployer (owner) can mint more tokens at any time:
cd contracts
npx hardhat console --network bscMainnet
const token = await ethers.getContractAt(
"GalacticToken",
"0xYOUR_TOKEN_ADDRESS"
);
// Mint 500,000 tokens to an address
const tx = await token.mint(
"0xRECIPIENT_ADDRESS",
ethers.parseEther("500000")
);
await tx.wait();
Local Development
For local testing without spending real BNB:
Start a local Hardhat node:
cd contracts
npx hardhat node
This gives you 20 accounts with 10,000 ETH each.
Deploy contracts locally (in a new terminal):
cd contracts
npx hardhat run scripts/deploy.js --network localhost
Update wagmi.js to use Hardhat addresses and put hardhat first in networks.
Start the frontend dev server:
cd frontend
npm run dev
Open http://localhost:5173
Add Hardhat network to MetaMask:
| Network Name | Hardhat Local |
| RPC URL | http://127.0.0.1:8545 |
| Chain ID | 31337 |
| Currency | ETH |
Game Mechanics
Controls
| Key | Action |
|---|---|
← / A | Move left |
→ / D | Move right |
Space / ↑ | Shoot |
Escape | Pause / Resume |
Touch controls appear automatically on mobile devices.
Scoring
| Action | Points |
|---|---|
| Kill alien | 10 + current wave number |
| Clear wave | 200 bonus |
| Time bonus (on wave clear) | Seconds remaining × 10 |
Power-ups (3% drop chance)
| Icon | Type | Effect |
|---|---|---|
| S | Shield | Absorbs one hit |
| R | Rapid Fire | Faster shooting for ~5 seconds |
| B | Bomb | Destroys 5 nearest aliens |
Game over conditions
- 90-second timer runs out
- All 3 lives lost
- Aliens reach the bottom of the screen
Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Smart Contracts | Solidity + Hardhat | 0.8.20 |
| Token Standard | OpenZeppelin ERC-20 | 5.x |
| Blockchain | BNB Smart Chain | — |
| Frontend Framework | React | 19 |
| Build Tool | Vite | 7.x |
| CSS | Tailwind CSS | 4.x |
| Web3 Hooks | Wagmi | 3.x |
| Wallet UI | Reown AppKit | 1.x |
| Ethereum Library | Viem | 2.x |
| Game Engine | HTML5 Canvas (custom) | — |
| Audio | Web Audio API (synthesized) | — |
Troubleshooting
Wallet connect button does nothing
You need a valid Reown Project ID in wagmi.js. Get one free at cloud.reown.com.
"Insufficient funds for gas" when deploying
Your deployer wallet needs BNB for gas. On testnet, use the BNB Testnet Faucet. On mainnet, you need real BNB (~0.05).
Leaderboard shows "No scores yet" after submitting
Wait a few seconds for the transaction to confirm on-chain. The leaderboard auto-refreshes after the transaction receipt is confirmed.
"Free play used today" but I haven't played
The 24-hour cooldown is tracked on-chain. If you played from a different device with the same wallet, the cooldown still applies. Wait 24 hours or use a paid play.
Build fails with module errors
Make sure you run npm install from the project root. If issues persist, delete node_modules/ and package-lock.json, then run npm install again.
Game canvas is black / not loading
Check the browser console for errors. Common cause: the "Press Start 2P" font fails to load due to a firewall. The game still works — text just uses the fallback monospace font.
Wrong network in wallet
Make sure your wallet is on the same network where your contracts are deployed. The AppKit modal shows a network switcher — click it to change.
Weekly rewards fail with "week not over"
You must wait 7 days after the last distribution (or initial deployment) before calling distributeWeeklyReward().