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:

How It Works

  1. Player connects wallet via Reown AppKit (MetaMask, TrustWallet, WalletConnect, etc.)
  2. Player starts a game session — 1 free play per day, or 50 GLCT for extra plays
  3. Player plays the 90-second arcade game and earns a score
  4. Score is submitted on-chain with an anti-tamper hash
  5. Top 50 scores appear on the leaderboard
  6. Every week, the owner distributes the reward pool to the #1 player

Requirements

ToolVersionPurpose
Node.js18+ (recommended 20+)Runtime for all build tools
npm9+Package manager (comes with Node)
A walletMetaMask, TrustWallet, etc. with BNB for gas
BNB (gas)~0.05 BNBFor deploying contracts on BSC
You do not need any other tools. No Docker, no database, no backend server. The game runs entirely as a static site + smart contracts.

Project Structure

galactic-invaders/
  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
Never share your private key. This wallet will be the contract owner and needs BNB for gas fees. The .env file is git-ignored by default.

2. Get BNB for gas

NetworkHow to get gas
BSC TestnetUse the BNB Testnet Faucet to get free tBNB
BSC MainnetPurchase BNB on any exchange and send to your deployer wallet

Get Reown Project ID

Reown AppKit requires a free project ID for wallet connections.

1

Go to cloud.reown.com and create a free account

2

Create a new project and copy the Project ID

3

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...";
Without a valid Reown Project ID, the wallet connect button will not work. This is free and takes 2 minutes.

Deploy to BSC Testnet

Test everything on BSC Testnet first before going to mainnet.

1

Make sure contracts/.env has your private key and the wallet has tBNB.

2

Deploy the contracts:

cd contracts
npx hardhat run scripts/deploy.js --network bscTestnet
3

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
Mainnet uses real BNB. Make sure your wallet has at least 0.05 BNB for deployment gas fees. Double-check all values before deploying — contracts cannot be modified after deployment.

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"),
};
The first network in the 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.

PropertyValue
Initial Supply1,000,000 tokens (minted to deployer)
Decimals18
MintableYes (owner only)
StandardOpenZeppelin ERC20 + Ownable

GalacticGame.sol

Game session manager, leaderboard, and reward distribution.

FeatureDetails
Free Play1 per day per wallet (24h cooldown)
Paid PlayCosts playCost tokens per session
LeaderboardTop 50 scores, sorted descending
Weekly RewardsOwner distributes reward pool to #1 player
Anti-TamperGame 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"));
These values are set at deployment time and cannot be changed after the contract is deployed. Plan your tokenomics carefully.

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:

Colors

The main colors are used throughout as Tailwind classes and inline styles:

ColorHexUsage
Neon Green#39ff14Primary text, borders, UI elements
Neon Blue#00f0ffAccents, scores, highlights
Red#ff3939Warnings, timer low, enemy bullets
Dark BG#0a0a0aBackground

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

1

Build the frontend locally to verify it works:

cd frontend
npm run build

This creates the frontend/dist/ folder with all static files.

2

Install the Vercel CLI:

npm install -g vercel
3

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)
4

Deploy to production:

vercel --prod

Your site will be live at https://your-project.vercel.app

Important: Make sure you update wagmi.js with your contract addresses and Reown Project ID before building and deploying.

Deploy on cPanel

1

Build the frontend:

cd frontend
npm run build
2

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/
3

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>
4

Visit your domain — the game should load.

Deploy on Netlify

1

Build the frontend:

cd frontend
npm run build
2

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
3

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!");
This must be called from the owner wallet (the one that deployed). It will fail if 7 days haven't passed since the last distribution.

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:

1

Start a local Hardhat node:

cd contracts
npx hardhat node

This gives you 20 accounts with 10,000 ETH each.

2

Deploy contracts locally (in a new terminal):

cd contracts
npx hardhat run scripts/deploy.js --network localhost
3

Update wagmi.js to use Hardhat addresses and put hardhat first in networks.

4

Start the frontend dev server:

cd frontend
npm run dev

Open http://localhost:5173

5

Add Hardhat network to MetaMask:

Network NameHardhat Local
RPC URLhttp://127.0.0.1:8545
Chain ID31337
CurrencyETH

Game Mechanics

Controls

KeyAction
/ AMove left
/ DMove right
Space / Shoot
EscapePause / Resume

Touch controls appear automatically on mobile devices.

Scoring

ActionPoints
Kill alien10 + current wave number
Clear wave200 bonus
Time bonus (on wave clear)Seconds remaining × 10

Power-ups (3% drop chance)

IconTypeEffect
SShieldAbsorbs one hit
RRapid FireFaster shooting for ~5 seconds
BBombDestroys 5 nearest aliens

Game over conditions

Tech Stack

LayerTechnologyVersion
Smart ContractsSolidity + Hardhat0.8.20
Token StandardOpenZeppelin ERC-205.x
BlockchainBNB Smart Chain
Frontend FrameworkReact19
Build ToolVite7.x
CSSTailwind CSS4.x
Web3 HooksWagmi3.x
Wallet UIReown AppKit1.x
Ethereum LibraryViem2.x
Game EngineHTML5 Canvas (custom)
AudioWeb 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().