Skip to content

Repository files navigation

Base Quest Milestones

Base Quest Milestones is a browser-based ERC-721 runner game deployed on GitHub Pages. Players run through milestone checkpoints, unlock NFT mint opportunities, connect an EVM wallet, and mint milestone NFTs on Base Mainnet.

The project is designed for desktop EVM wallets, mobile wallet browsers, WalletConnect QR/mobile connection, Base Mainnet NFT minting, and static deployment through Vite and GitHub Pages.

Current status: experimental MVP. This project is suitable for learning, demo use, portfolio use, and early community testing. Do not attach high-value rewards without stronger server-side or on-chain verification.


Live Site

https://jefmark.github.io/base-quest-milestones/

Repository

https://github.com/jefmark/base-quest-milestones

Network

Chain ID: 8453
Chain Name: Base
RPC URL: https://mainnet.base.org
Explorer: https://basescan.org

Core Features

Game

  • Runner-style browser game
  • Six NFT milestones
  • Mobile and desktop gameplay support
  • Mobile tap-to-jump support
  • Sound toggle
  • Start / Restart control
  • Mint button only appears when an NFT milestone is unlocked
  • If a milestone NFT is already minted, the game should not unnecessarily lock the player at that milestone
  • If a milestone NFT is not yet minted, the game preserves the mint opportunity and prevents accidental restart/jump from skipping the claim

NFT Minting

  • ERC-721 minting on Base Mainnet
  • Milestone-specific mint flow
  • Wallet network validation
  • Mobile wallet browser support
  • Direct transaction request flow for mobile wallets
  • Persistent mint status messages
  • NFT preview after successful mint
  • No paid mint
  • No ERC-20 approval flow
  • No token transfer request from the player wallet

NFT images are expected to exist in:

public/nft/1.png
public/nft/2.png
public/nft/3.png
public/nft/4.png
public/nft/5.png
public/nft/6.png

Metadata files are expected to exist in:

public/metadata/

How the Game Works

The player controls a runner. The game includes obstacles, jump input, score gain, milestone checkpoint detection, mint eligibility checks, game-over state, and mint-preserve behavior when an unminted milestone is unlocked.

Normal loop:

Play
→ Reach required score/time
→ Unlock milestone
→ Connect wallet
→ Mint milestone NFT
→ Show minted NFT image

Milestone Rules

Recommended milestone settings:

Milestone Name Required Score Required Play Time
1 Rookie Runner 1,200 20 seconds
2 Chain Jumper 5,000 45 seconds
3 Base Sprinter 10,000 70 seconds
4 Gasless Ghost 18,000 95 seconds
5 Block Master 30,000 125 seconds
6 Onchain Legend 45,000 160 seconds

These values must match both places:

  1. src/game.js
  2. The deployed contract milestone settings

If the frontend and contract values do not match, the app may show a milestone as ready while the contract rejects the transaction.


NFT Metadata and Images

Metadata files are stored here:

public/metadata/

Images are stored here:

public/nft/

When deployed to GitHub Pages, the public folder is served from the site root.

Correct image URL:

https://jefmark.github.io/base-quest-milestones/nft/1.png

Wrong image URL:

https://jefmark.github.io/base-quest-milestones/public/nft/1.png

Each metadata file should use the ERC-721 metadata shape:

{
  "name": "Rookie Runner #1",
  "description": "A milestone badge for the Base Quest game.",
  "image": "https://jefmark.github.io/base-quest-milestones/nft/1.png",
  "external_url": "https://jefmark.github.io/base-quest-milestones/",
  "attributes": [
    {
      "trait_type": "Milestone",
      "value": "1"
    }
  ]
}

Wallet Support

The wallet connection system uses a custom EVM wallet picker.

Supported wallet options:

MetaMask
Trust Wallet
Coinbase Wallet
Rabby Wallet
Rainbow
OKX Wallet
Zerion Wallet
Browser Wallet
WalletConnect

The wallet picker is EVM-focused. Non-EVM wallet flows such as Solana, Keplr, Cosmos, etc. are not used for this game.

Desktop wallet behavior

Desktop wallets are selected through injected EVM providers when available. The app avoids blindly choosing the wrong injected provider when multiple extensions exist.

Mobile wallet behavior

Mobile wallets usually work in one of these ways:

  1. The site is opened directly inside the wallet browser.
  2. The user starts from Android Chrome and is deeplinked into the wallet browser.
  3. The user connects with WalletConnect.
  4. The user scans WalletConnect QR from another device.

Important Mobile Wallet Behavior

There is one known mobile wallet behavior that still exists.

When the user opens the site from Android Chrome and chooses a wallet such as MetaMask or Trust Wallet, the wallet app may open the site inside its own internal browser.

Because Android Chrome and the wallet internal browser are two separate browser contexts, the original page state and injected provider are not automatically transferred.

The user may need to do this:

1. Open site in Android Chrome.
2. Tap Connect Wallet.
3. Choose MetaMask or Trust Wallet.
4. Wallet app opens the site inside its internal browser.
5. Tap Connect Wallet again inside the wallet browser.
6. Choose the same wallet again.
7. The wallet connects successfully because the provider is now injected inside the wallet browser.

This is not ideal UX, but it is a known limitation of mobile wallet deeplink flows.

Why this happens

Mobile wallets usually inject window.ethereum only inside their own browser. When the site is opened in normal Chrome, Trust Wallet or MetaMask Mobile cannot always inject the provider directly into that Chrome tab.

So the first tap only moves the user into the wallet browser. The second tap connects inside the correct browser context.

What was tried

Several approaches were implemented or tested during development:

  • Direct window.ethereum connection
  • Custom EVM wallet picker
  • EIP-6963 provider detection for desktop wallets
  • MetaMask / Trust Wallet mobile deeplinks
  • WalletConnect fallback
  • WalletConnect QR/modal support
  • Closing the custom wallet modal before opening WalletConnect
  • Keeping game state safer after mobile wallet redirects
  • Preventing accidental restart when a mintable NFT is available
  • Direct transaction request flow for mobile minting
  • Persistent mint status messages so mobile wallet errors remain visible
  • NFT preview after successful mint

Current status

The remaining issue is:

Mobile deeplink wallets may still require two taps:
one tap to open the wallet browser,
another tap inside the wallet browser to connect.

Recommended workaround:

Use WalletConnect when the user wants to stay in the same browser context,
or open the site directly inside MetaMask Mobile / Trust Wallet Browser.

WalletConnect Setup

WalletConnect requires a valid Reown / WalletConnect Project ID.

The project uses this environment variable:

VITE_WALLETCONNECT_PROJECT_ID

This value must be added in GitHub Repository Variables.

Go to:

Repository Settings
→ Secrets and variables
→ Actions
→ Variables
→ New repository variable

Add:

Name:
VITE_WALLETCONNECT_PROJECT_ID
Value:
YOUR_REAL_REOWN_PROJECT_ID

Do not write Value: inside the value field. Do not wrap the ID in quotes. Do not leave it empty. Do not use stars.

Correct example format:

a1b2c3d4e5f6g7h8i9j0

Important: the value should be placed in Repository Variables, not only in Secrets.

The GitHub Actions workflow reads:

VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }}

So the value must exist under vars.


Required GitHub Repository Variables

VITE_CHAIN_ID=8453
VITE_CHAIN_NAME=Base
VITE_RPC_URL=https://mainnet.base.org
VITE_EXPLORER_URL=https://basescan.org
VITE_CONTRACT_ADDRESS=YOUR_DEPLOYED_CONTRACT_ADDRESS
VITE_WALLETCONNECT_PROJECT_ID=YOUR_REOWN_PROJECT_ID

VITE_CONTRACT_ADDRESS must be the actual deployed ERC-721 contract address on Base Mainnet.


GitHub Actions Deployment

The deployment workflow must pass all Vite variables into the build.

File:

.github/workflows/deploy-pages.yml

The workflow must include:

env:
  VITE_CHAIN_ID: ${{ vars.VITE_CHAIN_ID }}
  VITE_CHAIN_NAME: ${{ vars.VITE_CHAIN_NAME }}
  VITE_RPC_URL: ${{ vars.VITE_RPC_URL }}
  VITE_EXPLORER_URL: ${{ vars.VITE_EXPLORER_URL }}
  VITE_CONTRACT_ADDRESS: ${{ vars.VITE_CONTRACT_ADDRESS }}
  VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }}

Without this line:

VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }}

the site will build without the WalletConnect Project ID, even if the variable exists in GitHub.

Resulting error:

WalletConnect Project ID is missing.

Correct Deploy Workflow

Use this workflow if deployment fails because of missing cache lock file or missing environment variables.

name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

env:
  VITE_CHAIN_ID: ${{ vars.VITE_CHAIN_ID }}
  VITE_CHAIN_NAME: ${{ vars.VITE_CHAIN_NAME }}
  VITE_RPC_URL: ${{ vars.VITE_RPC_URL }}
  VITE_EXPLORER_URL: ${{ vars.VITE_EXPLORER_URL }}
  VITE_CONTRACT_ADDRESS: ${{ vars.VITE_CONTRACT_ADDRESS }}
  VITE_WALLETCONNECT_PROJECT_ID: ${{ vars.VITE_WALLETCONNECT_PROJECT_ID }}

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 24

      - name: Install dependencies
        run: npm install

      - name: Build site
        run: npm run build

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: ./dist

  deploy:
    needs: build
    runs-on: ubuntu-latest

    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}

    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Why npm cache was removed

An earlier workflow used:

cache: npm

GitHub then expected a dependency lock file such as:

package-lock.json

Because the repository did not have a lock file, GitHub Actions failed with:

Dependencies lock file is not found

The current workflow removes npm cache to avoid that failure.


Local Development

npm install
npm run dev
npm run build
npm run preview

Project Structure

.
├── .github/
│   └── workflows/
│       └── deploy-pages.yml
├── contracts/
│   └── BaseQuestMilestones.sol
├── public/
│   ├── favicon.svg
│   ├── site.webmanifest
│   ├── nft/
│   │   ├── 1.png
│   │   ├── 2.png
│   │   ├── 3.png
│   │   ├── 4.png
│   │   ├── 5.png
│   │   └── 6.png
│   └── metadata/
├── scripts/
├── src/
│   ├── config.js
│   ├── game.js
│   ├── main.js
│   ├── style.css
│   └── wallet.js
├── hardhat.config.cjs
├── index.html
├── package.json
├── vite.config.js
└── README.md

Main Files

src/config.js

Responsible for chain configuration, RPC URL, explorer URL, contract address, WalletConnect Project ID, and contract ABI.

Important line:

walletConnectProjectId: import.meta.env.VITE_WALLETCONNECT_PROJECT_ID || ''

src/wallet.js

Responsible for wallet detection, EVM wallet picker, wallet icons, desktop wallet provider selection, mobile deeplink handling, WalletConnect initialization, network switching, balance reading, mint transaction creation, minted milestone checking, and disconnect handling.

src/game.js

Responsible for game loop, character jump, obstacle collision, milestone unlocks, mint-preserve behavior, and restart behavior.

src/main.js

Responsible for UI binding, button handlers, wallet modal UI, mint button state, status messages, mobile tap-to-jump behavior, and mint success NFT preview.

src/style.css

Responsible for main layout, wallet modal design, mobile responsive layout, game panel styling, mint panel styling, and NFT preview modal styling.


Security Notes

This project uses a low-risk mint model:

  • No paid mint
  • No ERC-20 approval
  • No NFT approval
  • No token transfer request from players
  • No direct ETH acceptance

The user should only see a wallet confirmation for:

mintMilestone(...)

The user should reject any wallet popup that asks for:

  • ERC-20 approval
  • NFT approval
  • Unlimited spending
  • Token transfer
  • transferFrom
  • setApprovalForAll
  • Any unknown contract interaction unrelated to minting

Never commit:

Private key
Seed phrase
Wallet recovery phrase
RPC admin key
Secret deployment key

Safe to expose publicly:

VITE_CHAIN_ID
VITE_CHAIN_NAME
VITE_RPC_URL
VITE_EXPLORER_URL
VITE_CONTRACT_ADDRESS
VITE_WALLETCONNECT_PROJECT_ID

The WalletConnect Project ID is not a private key, but it should still be configured properly with domain/origin settings where possible.


No-Server Anti-Cheat Model

This project is a static frontend with no backend. The anti-cheat model is therefore limited.

It can block casual cheating in the normal website UI, but it is not a full security boundary.

A technical user may bypass the frontend and call the contract directly from Remix, Basescan, a custom script, or another frontend.

For high-value rewards, use a stronger model such as backend signed mint authorization, EIP-712 signed score proofs, on-chain game verification, commit-reveal gameplay, or zero-knowledge / deterministic proof systems.


Known Issues and Limitations

1. Mobile wallet browser double-connect behavior

Status:

Still exists.

Description:

On mobile, choosing MetaMask or Trust Wallet from Chrome may open the site inside the wallet internal browser. The user then needs to tap Connect Wallet again inside that wallet browser.

Reason:

The wallet provider is injected inside the wallet browser, not necessarily inside Android Chrome.

Possible future improvements:

  • Add clearer UI instructions after mobile deeplink
  • Detect wallet browser and auto-open the wallet picker
  • Prefer WalletConnect as the default mobile path
  • Add a dedicated Open in wallet browser instruction screen
  • Use a full AppKit-style modal if the project later adopts a complete wallet UX SDK

2. Rabby risk warning

Rabby may show warnings such as:

Very Low popularity
Not listed on community platforms

This is not caused by the dApp code. It is related to domain reputation and wallet-side risk checks.

Recommended improvements:

  • Use a custom production domain instead of only GitHub Pages
  • Verify domain metadata where available
  • Publish project information publicly
  • Add clear project links and metadata
  • Avoid suspicious redirects
  • Keep contract and frontend source public

3. WalletConnect Project ID missing

Error:

WalletConnect Project ID is missing.

Cause:

VITE_WALLETCONNECT_PROJECT_ID is missing in GitHub Variables
VITE_WALLETCONNECT_PROJECT_ID is empty
VITE_WALLETCONNECT_PROJECT_ID is placed in Secrets instead of Variables
deploy-pages.yml does not pass vars.VITE_WALLETCONNECT_PROJECT_ID into env
site was not redeployed after adding the variable
browser is still showing cached old build

Fix:

1. Add VITE_WALLETCONNECT_PROJECT_ID to GitHub Repository Variables.
2. Make sure deploy-pages.yml passes it into env.
3. Deploy again.
4. Hard refresh the site.

4. GitHub Actions lock file error

Error:

Dependencies lock file is not found

Cause:

The workflow used npm cache but the repository had no package-lock.json.

Fix:

Remove:

cache: npm

from the actions/setup-node step.

5. Mobile mint popup does not appear

Earlier behavior:

Preparing mint...
Waiting for wallet...
No wallet gas popup appears

Fix implemented:

  • Direct transaction request flow
  • More persistent mint status messages
  • Better error visibility
  • Wallet browser support

If this happens again, check wallet network, ETH on Base for gas, contract address, milestone status, provider connection, and WalletConnect session.


Troubleshooting

The game says a milestone is ready, but the contract rejects the mint

Cause:

Frontend milestone settings do not match contract milestone settings.

Fix:

1. Check src/game.js.
2. Check getMilestone(n) in Remix.
3. Make both values the same.

NFT images do not show in OpenSea or Element

Common causes:

  • image field still uses YOUR_DOMAIN
  • Image URL returns 404
  • JSON URL returns 404
  • Base URI is wrong
  • Missing trailing slash in base URI
  • Marketplace metadata is cached

Fix:

1. Open tokenURI(tokenId).
2. Open the JSON.
3. Open the image URL.
4. Fix JSON if needed.
5. Use setBaseTokenURI if the base path is wrong.
6. Refresh metadata on the marketplace.

Wallet connection fails

Check:

Browser wallet is installed
Wallet is on the correct chain
VITE_CHAIN_ID is correct
VITE_RPC_URL is correct
VITE_CONTRACT_ADDRESS is set
The deployed contract address is on the same chain as the frontend config
VITE_WALLETCONNECT_PROJECT_ID is available in the build

GitHub Pages still shows the old version

Fix:

1. Wait for the GitHub Actions workflow to finish.
2. Check that the latest workflow is green.
3. Hard refresh with Command + Shift + R.
4. On mobile, close the browser and reopen the page.
5. Check that repository variables are correct.

Testing Checklist

Desktop

Open site in Chrome
Hard refresh with Command + Shift + R
Connect MetaMask
Connect Rabby
Connect Coinbase if installed
Open WalletConnect
Confirm QR/modal appears
Play game
Unlock NFT
Mint NFT
Confirm transaction in wallet
Confirm NFT preview appears after mint
Disconnect wallet
Reconnect

Mobile

Open site in Android Chrome
Tap Connect Wallet
Choose MetaMask or Trust Wallet
Confirm wallet browser opens
Tap Connect Wallet again inside wallet browser
Connect wallet
Play game
Tap anywhere on non-button page area to jump
Unlock NFT
Tap Mint
Confirm gas popup appears
Confirm NFT preview appears after mint

Mobile WalletConnect

Open site
Tap Connect Wallet
Tap WalletConnect
Confirm WalletConnect modal opens
Choose wallet or scan QR from another device
Connect wallet
Play and mint

Cache Notes

After deployment, old files may remain cached.

Desktop hard refresh:

Command + Shift + R

Mobile:

Close wallet browser tab
Close wallet app if needed
Open site again

If still stale:

Clear site data for jefmark.github.io

Reown / WalletConnect Domain Notes

Recommended app URL:

https://jefmark.github.io/base-quest-milestones/

Recommended origin if an allowlist/origin field exists:

https://jefmark.github.io

Correct origin:

https://jefmark.github.io

Full app URL:

https://jefmark.github.io/base-quest-milestones/

Current Final Status

Working:

Desktop wallet connection
Mobile wallet browser connection
Trust Wallet mobile browser connection
MetaMask mobile browser connection
Minting inside mobile wallet browser
Game milestone unlock flow
Mint-preserve behavior
Mobile tap-to-jump
NFT preview after successful mint
GitHub Pages deployment

Still known limitation:

Mobile deeplink wallets may require two connection taps:
one tap to open the wallet browser,
another tap inside the wallet browser to connect.

Primary remaining improvement:

Improve mobile wallet UX so the second connect step is clearer or avoided where possible.

Roadmap

Possible next steps:

  • Add a dedicated mobile wallet instruction screen
  • Make WalletConnect the recommended mobile path
  • Add automatic wallet-browser detection
  • Add clearer message after deeplink
  • Add custom production domain
  • Improve Rabby / wallet reputation by using verified metadata and public project links
  • Add package-lock.json for deterministic GitHub Actions installs
  • Add automated build checks before deploy
  • Add transaction hash display after mint
  • Add NFT marketplace / BaseScan links after mint
  • Farcaster Mini App support
  • Vercel deployment
  • IPFS-hosted metadata
  • Backend signed mint authorization
  • EIP-712 score signatures
  • Better marketplace refresh workflow
  • Leaderboard
  • Daily challenges
  • Better mobile controls
  • Sound and accessibility settings
  • Contract verification on Basescan

Official References


Contact

Have feedback, questions, bug reports, or collaboration ideas?

For bug reports, include:

Device
Browser
Wallet
Network
Screenshot
Transaction hash if available
Steps to reproduce

License

MIT License.

Asset License Notice

The source code is released under the MIT License.

NFT images, artwork, logos, and visual assets in public/nft/ are provided for this project and demo use. Unless otherwise stated, the artwork remains owned by the project creator and should not be reused as standalone commercial artwork without permission.

About

Base Mainnet NFT runner game with mobile wallet support, WalletConnect, and milestone-based ERC-721 minting.

Topics

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages