diff --git a/README.md b/README.md index 3c35bc2..e71e166 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,29 @@ # MLSA Cards DApp +Play. Learn. Collect. Mint. A full-stack NFT collectible card game where users solve problems to earn points, purchase digital cards, and mint them as ERC-721 NFTs on the blockchain. +A full-stack Web3-powered gamified learning platform where users solve domain-based challenges, earn points, unlock collectible cards, and mint them as ERC-721 NFTs with verifiable on-chain ownership. +Developed as a collaborative MLSA project, showcasing secure authentication, blockchain integration, and real-world full-stack architecture. + +## 🎯 Project Vision + +MLSA Cards aims to bridge learning and ownership by transforming skill acquisition into an engaging, collectible-driven experience. + +- 1.Instead of passive learning, users: + +- 2.actively solve problems + +- 3.earn verifiable rewards + +- 4.gain permanent digital ownership of achievements via NFTs. + +## 📑 Table of Contents +- [Project Vision](#-project-vision) +- [What Can Users Do?](#-what-can-users-do) +- [Core Features](#-core-features) +- [UI & User Experience](#-ui--user-experience) +- [- [Tech Stack](#-tech-stack)] +- [Why This Project Stands Out (MLSA Impact)](#-why-this-project-stands-out-mlsa-impact) ## 🎮 About @@ -10,6 +33,21 @@ MLSA Cards is a gamified learning platform that combines education with blockcha - **Collect Cards**: Purchase unique skill cards from the store using earned points - **Mint NFTs**: Convert owned cards into blockchain-backed NFTs with permanent ownership - **Hybrid Authentication**: Login with Google OAuth for easy access or MetaMask for Web3 natives +## 🎮 What Can Users Do? + 🧠 Solve Problems + - Answer curated questions across multiple categories (Programming, CS, Web Dev, AI/ML, Science, Geography, etc.) + + ⭐ Earn & Track Points + - Real-time scoring, accuracy tracking, streaks, and level progression + + 🃏 Collect Skill Cards + - Spend earned points to purchase collectible cards with defined rarity + + 🎨 Mint NFTs + - Convert owned cards into ERC-721 NFTs backed by blockchain verification + + 🔐 Hybrid Authentication + - Login via Google OAuth or MetaMask with optional wallet linking ## ✨ Features @@ -37,6 +75,16 @@ MLSA Cards is a gamified learning platform that combines education with blockcha - IPFS metadata storage via Pinata - ERC-721 compliant smart contract - Transaction history and on-chain verification + + ## 🖥 UI & User Experience + +- Designed using Figma and implemented with a cslean, responsive UI. +- Key interfaces include: +- Authentication (Login / Signup) +- User Dashboard (stats, points, cards, level) +- Quiz Interface (category-based questions) +- Card Marketplace +- NFT Collection with blockchain links ## 🛠 Tech Stack @@ -65,7 +113,7 @@ MLSA Cards is a gamified learning platform that combines education with blockcha ## 📋 Prerequisites - **Node.js** 18+ and npm -- **Python** 3.10+ +- **Python** Python 3.10–3.11 required. Python 3.13 not supported. - **MetaMask** browser extension - **Google OAuth Credentials** (for OAuth login) - **Pinata Account** (for IPFS storage) @@ -323,3 +371,10 @@ This project is licensed under the MIT License. - Verify redirect URI matches exactly: `http://localhost:8000/auth/google/callback` - Check CLIENT_ID and CLIENT_SECRET are correct - Ensure OAuth consent screen is configured +## 🌟 Why This Project Stands Out (MLSA Impact) + +- Demonstrates real-world Web3 integration +- Implements secure hybrid authentication +- Uses production-style backend architecture +- Encourages learning through gamification +- Built via collaborative, contribution-based development \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index e17e099..6df8c8c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -8,3 +8,6 @@ httpx==0.27.0 pydantic==2.6.3 requests==2.31.0 pytest==8.1.1 +itsdangerous +sqlalchemy +authlib diff --git a/backend/static/app.js b/backend/static/app.js index 5eddee1..15b23f4 100644 --- a/backend/static/app.js +++ b/backend/static/app.js @@ -23,6 +23,7 @@ let signer; let wallet; let token; let currentChainId; +let providerEventsBound = false; const networkNames = { "31337": "Hardhat (Localhost)", @@ -40,6 +41,43 @@ function showStatus(message, type = "info") { statusEl.appendChild(badge); } +function applyNetworkDisplay(chainIdValue) { + currentChainId = chainIdValue; + chainIdEl.textContent = currentChainId; + networkNameEl.textContent = networkNames[currentChainId] || "Unknown Network"; +} + +function bindProviderEvents() { + if (!window.ethereum || providerEventsBound) return; + // Keep UI in sync with MetaMask changes. + window.ethereum.on("chainChanged", (chainIdHex) => { + const parsed = parseInt(chainIdHex, 16).toString(); + applyNetworkDisplay(parsed); + // Full reload guarantees fresh provider/signer/token state. + window.location.reload(); + }); + + window.ethereum.on("accountsChanged", (accounts) => { + if (!accounts || !accounts.length) { + wallet = undefined; + walletEl.textContent = "Not connected"; + signinBtn.disabled = true; + mintBtn.disabled = true; + showStatus("Please connect MetaMask", "error"); + return; + } + wallet = accounts[0]; + walletEl.textContent = wallet; + // Token remains tied to the prior account; require fresh sign-in. + token = undefined; + signinBtn.disabled = false; + mintBtn.disabled = true; + showStatus("Account changed. Please sign in again.", "info"); + }); + + providerEventsBound = true; +} + function updateMintPreview() { const name = nameInput.value; const desc = descInput.value; @@ -60,17 +98,15 @@ async function connect() { showStatus("MetaMask not found", "error"); return; } + bindProviderEvents(); provider = new ethers.BrowserProvider(window.ethereum); const accounts = await provider.send("eth_requestAccounts", []); wallet = accounts[0]; signer = await provider.getSigner(); const network = await provider.getNetwork(); - currentChainId = network.chainId.toString(); - + applyNetworkDisplay(network.chainId.toString()); walletEl.textContent = wallet; - chainIdEl.textContent = currentChainId; - networkNameEl.textContent = networkNames[currentChainId] || "Unknown Network"; connectionInfoEl.style.display = "grid"; signinBtn.disabled = false; showStatus("Wallet connected", "connected"); diff --git a/smart-contracts/scripts/deploy.js b/smart-contracts/scripts/deploy.js index 5467f1c..edcd630 100644 --- a/smart-contracts/scripts/deploy.js +++ b/smart-contracts/scripts/deploy.js @@ -7,9 +7,11 @@ async function main() { const GameCollectible = await hre.ethers.getContractFactory("GameCollectible"); const gameCollectible = await GameCollectible.deploy(name, symbol, baseURI); - await gameCollectible.deployed(); + await gameCollectible.waitForDeployment(); - console.log("GameCollectible deployed to:", gameCollectible.address); + + const address = await gameCollectible.getAddress(); + console.log("GameCollectible deployed to:", address); } main().catch((error) => {