Skip to content

moorecoin-bhs/Moorecoin

Repository files navigation

Moorecoin

A classroom / community reward system. Users earn “Moorecoins”, view leaderboards, lock coins in time-based bonds for yield, and redeem (exchange) coins. Admins can award coins, manage users, clear pending redemptions, and monitor system health.

Licensed under the GNU AGPL v3.

Table of Contents

Features

  • Firebase Authentication (validated server-side with Admin SDK)
  • Firestore data (user profiles, balances, stats, bonds, pending redemptions)
  • Bond mechanism (lock principal, earn interest based on total supply)
  • Hour grouping (assign class hour once; admins bulk-award per hour)
  • Secure admin endpoints (role flag on user doc)
  • Leaderboard + global stats
  • Lazy bond redemption (auto processes on read after maturity)
  • Pending redemption queue (manual fulfillment workflow)
  • Lightweight health/status endpoint

High-Level Data Model

Firestore collections / docs (simplified):

  • users/{uid}
    • profile: { displayName, email, photoURL, createdAt, hour }
    • balances: { coins, totalEarned }
    • bond: { amount, termDays, purchasedAt, payoutAt, interestRate, expectedReturn, redeemed, redeemedAt? }
    • flags: { admin }
  • stats/global
    • moorecoins (circulating supply)
    • users (count)
    • bondsOutstanding (locked principal)
  • stats/pending
    • { uid: { amount, createdAt } ... }

Legacy fallback fields (coins, totalEarned, admin, hour) still respected.

Backend (Flask)

File: app.py (Flask + firebase_admin + flask_cors)

Key helpers:

  • verify_token(): extracts & verifies Firebase ID token (Authorization: Bearer <token>)
  • compute*bond_interest_rate(totalSupply): 0.1 + 0.65 * e^(−0.000486 _ supply)
  • _maybe_redeem_matured_bond(): transactional bond payout

API Overview

Public:

  • GET / Basic message
  • GET /health Uptime + dependency latency

Authenticated (Bearer ID token):

  • GET /user/exists Creates default user if missing
  • GET /user/info Returns user doc (may auto-redeem matured bond)
  • PATCH /user/hour Set hour once (1–6)
  • GET /user/bond Current (or last) bond
  • POST /purchase/bond { amount:int, term?:int }
  • POST /purchase/exchange/{n} Spend n coins → queued in stats/pending

Stats / Leaderboard:

  • GET /stats/moorecoins/total
  • POST /stats/moorecoins/edit (admin)
  • GET /stats/users/total
  • GET /stats/users/leaderboard
  • GET /stats/users/leaderboard/{limit}

Admin:

  • GET /admin/users?limit=&order=&dir=
  • PATCH /admin/users/{uid} (coins, admin, hour)
  • POST /admin/hour/award { hour, coins }
  • GET /admin/pending
  • DELETE /admin/pending/{uid}

Bond lifecycle:

  1. POST /purchase/bond (locks principal, increments bondsOutstanding)
  2. After payoutAt, any read of /user/info or /user/bond triggers lazy redemption
  3. Redemption mints ONLY interest portion to global supply, releases locked principal to user balance

Error Conventions

JSON: { "error": "<message>" }
HTTP 401 (auth), 403 (not admin), 400 (validation), 404 (missing), 503 (/health dependency failure).

Getting Started (Local)

Prerequisites:

  • Python 3.11+
  • Firebase project (Firestore in Native mode enabled)
  • Service Account key (JSON) with Firestore + Auth admin rights

Project layout (partial):

app.py
public/
firebase.json
firestore.rules
firestore.indexes.json
LICENSE
package.json (frontend scripts optionally)
secrets/
  moorecoin-service-key.json  (NOT committed)

1. Clone

git clone <repo-url>
cd Moorecoin

2. Python env

(windows)

python -m venv .venv
.venv\Scripts\activate
pip install flask firebase-admin flask-cors gunicorn

(linux/mac)

python3 -m venv .venv
. ./.venv/bin/activate
pip install flask firebase-admin flask-cors gunicorn

3. Service Account

Place JSON at:

secrets/moorecoin-service-key.json

Keep this file out of version control (add secrets/ to .gitignore).

4. Run Dev Server

python app.py
# or with auto-reload
set FLASK_APP=app.py
flask run

5. Gunicorn (production style)

gunicorn -w 4 -b 0.0.0.0:8000 app:app

6. Frontend (if using Firebase Hosting)

npm i -g firebase-tools
firebase login
firebase emulators:start --only hosting
# deploy:
firebase deploy --only hosting,firestore:rules,firestore:indexes

Auth (Testing)

Obtain a Firebase ID token (client SDK signIn) then call:

Authorization: Bearer <ID_TOKEN>

Example Requests

Health:

curl https://your-host/health

User bootstrap:

curl -H "Authorization: Bearer $TOKEN" https://your-host/user/exists

Purchase bond:

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"amount":100,"term":14}' https://your-host/purchase/bond

Award hour (admin):

curl -X POST -H "Authorization: Bearer $ADMIN" -H "Content-Type: application/json" \
  -d '{"hour":3,"coins":5}' https://your-host/admin/hour/award

Exchange:

curl -X POST -H "Authorization: Bearer $TOKEN" https://your-host/purchase/exchange/25

Security Notes

  • All mutating endpoints verify Firebase ID token server-side.
  • Admin role is stored on user doc (flags.admin). Change via PATCH /admin/users/{uid}.
  • Do not expose service account JSON publicly.
  • Consider rate limiting (not included).
  • Ensure Firestore security rules block direct client writes to privileged fields (server performs authoritative updates).

Operational Notes

  • /health touches Auth (list_users) and stats/global (Firestore read).
  • Response header X-Response-Time added for latency insights.
  • Lazy bond redemption keeps cron jobs unnecessary.
  • Pending redemptions require an out-of-band fulfillment process (e.g., mark delivered then DELETE entry).

Extending

Ideas:

  • Add pagination for /admin/users
  • Add audit log collection
  • Allow multiple concurrent bonds with an array
  • Implement scheduled Cloud Function for bond redemption (optional)
  • Add caching layer for leaderboard

Troubleshooting

Issue: 401 errors
Check: Token expired, wrong Firebase project, missing Authorization header.

Issue: Bond purchase fails with "Global stats missing"
Create stats/global doc with fields:

{ "moorecoins": 0, "users": 0, "bondsOutstanding": 0 }

Issue: Hour award finds zero users
Ensure users set hour via PATCH /user/hour first.

License

Copyright (C) 2025 Noah Harper

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

See LICENSE.

Disclaimer

Educational / demonstration use; not financial currency.

Easter Eggs (Maintainers)

Lightweight, purely cosmetic easter eggs are included via public/easter-eggs.js and loaded on user-facing pages. They’re intentionally mild and do not change balances or grant coins.

Discoverables:

  • Konami code (↑ ↑ ↓ ↓ ← → ← → B A) → brief coin confetti + toast
  • Hotkeys:
    • Shift+R → Retro mode (persists via localStorage: egg-retro)
    • Shift+M → Monochrome mode
    • Shift+D → Disco overlay (reduced-motion friendly)
  • Click sequences:
    • Click the logo 5× within ~1.5s → playful spin + toast
    • Triple-click the Moorecoins balance number → pulse + randomized tip
    • Double-click the footer links area → small overlay credits card (auto-dismiss)
  • Long-press hint: hover/touch-hold the “Moorecoin value” area (~1.2s) → decay curve tip
  • Phrase triggers (type anywhere outside inputs):
    • moore, credits, stonks, retro (existing)
    • mono → enables monochrome
    • party → enables disco overlay
    • snow → ❄️ emoji confetti
    • bunny → ASCII bunny toast
    • idkfa → “no god mode” toast

Reduced Motion: if the user has “Reduce Motion” enabled, confetti is replaced by a simple toast.

Contact

Email me at noah@dingl.us

About

A money market for Mr. Moore's AP Economics Classes

Resources

License

Stars

Watchers

Forks