A blockchain-based credential verification system for educational institutions, built with Flask (backend), Hardhat (smart contracts), and a small vanilla JS frontend.
- Overview
- Project Structure
- Features
- Prerequisites
- Installation
- Running the Application
- End-to-End Demo Flow
- Testing
- Smart Contract & Blockchain
- Selective Disclosure & Evidence Signing
- Troubleshooting
- Team
- License
CampusCred enables educational institutions to issue verifiable digital credentials as non-transferable NFTs on the Ethereum Sepolia testnet.
The system includes:
- Submit credential claims (micro-credential, course completion, diploma)
- Upload supporting evidence (PDF / images / docs)
- Evidence stored privately (local or S3), with a SHA-256 hash for integrity
- Review and approve or reject pending claims
- On approval, automatically:
- Upload credential metadata to IPFS (via Pinata, or a deterministic mock hash if not configured)
- Mint a non-transferable CampusCred NFT to the student's wallet
- Track statistics: total claims, pending, approved, minted
- Public page to verify credentials by Token ID
- Shows issuer, course, credential type, issuance date, on-chain transaction and metadata
- Uses on-chain data (ownerOf, tokenURI, isRevoked) plus local DB
- Credential owner can generate a time-limited verifier link (15 minutes)
- Recruiter following this link can:
- See legal name, email, and evidence file name
- Download a digitally signed PDF of the evidence (if evidence is a PDF)
- Solidity smart contract CampusCredNFT (non-transferable ERC-721)
- Deployed to Sepolia (see DEPLOYMENT.txt)
- Minting performed from a backend-controlled deployer wallet
End-to-end prototype is fully implemented:
- ✅ Student submission with file upload & private storage
- ✅ Instructor approval & NFT minting on Sepolia
- ✅ Public verification by token ID (no wallet required)
- ✅ Time-limited verifier links for PII + signed PDF download
- ✅ Hardhat-based contract deployment & tests
- ✅ Full E2E Browser Testing with mocked MetaMask injection
campuscred-nft-system/
├── DEPLOYMENT.txt # Sepolia deployment info (address, date)
├── README.md # This file
├── backend/ # Flask backend
│ ├── app/
│ │ ├── __init__.py # Flask app factory
│ │ ├── config.py # Configuration loader (env-based)
│ │ ├── models.py # SQLAlchemy models (Claim)
│ │ ├── routes/ # Blueprints (auth, claims, instructor, verify)
│ │ └── services/ # Logic (blockchain, storage, ipfs, signer)
│ ├── e2e/ # End-to-End Playwright Tests
│ │ ├── __init__.py
│ │ ├── conftest.py # Live server fixture & blockchain mocks
│ │ ├── mocks.py # Window.ethereum injection
│ │ └── test_full_flow.py # Full critical path tests
│ ├── tests/ # Backend Unit tests (pytest)
│ ├── .env.example # Backend environment template
│ ├── requirements.txt # Python dependencies
│ ├── run.py # Flask entry point
│ └── setup_database.py # DB initialisation helper
├── frontend/
│ └── static/ # CSS, JS, and Images
├── contracts/
│ ├── CampusCredNFT.sol # Solidity credential NFT contract
│ └── CampusCredNFT_ABI.json # ABI exported for backend Web3
├── test/
│ └── CampusCredNFT.test.js # Hardhat/Mocha tests for the contract
├── ignition/ # Hardhat Ignition deployment modules
├── scripts/ # Hardhat helper scripts (deploy, export ABI)
├── hardhat.config.ts # Hardhat 3 configuration (TypeScript)
└── package.json # Node/Hardhat dev dependencies
- Submit claims with name, email, course code, and credential type.
- Securely upload evidence files (stored off-chain).
- Connect wallet via MetaMask to associate credentials with Ethereum address.
- View status of submitted claims (Pending, Approved, Minted).
- Authenticated via specific instructor wallet address.
- Dashboard view of all pending claims.
- Approve claims → Triggers metadata generation, IPFS pin, and Blockchain mint.
- Reject claims → Records reason and notifies student.
- Public Verify: Enter Token ID to view immutable blockchain record (Issuer, Date, Course).
- Private Link: Credential owner generates a 15-minute link. Verifier can download digitally signed evidence and view PII (Name/Email).
- Python 3.12+
- Node.js 18+
- Git
- MetaMask (for manual testing)
- Playwright Browsers (for E2E testing)
git clone <repository-url>
cd campuscred-nft-systemcd backend
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies (Flask, Web3, Playwright)
pip install -r requirements.txt
# Install Playwright browsers for E2E testing
python -m playwright install chromium
# Create .env and initialize DB
cp .env.example .env
python setup_database.py# Install Node dependencies
npm installcd backend
source venv/bin/activate
python run.pyAccess the app at: http://localhost:5000
# Start local node (optional)
npx hardhat node- Student: Go to
/student/portal. Fill out the form, upload a PDF, and submit. - Instructor: Connect wallet (use the address in auth.py). Go to
/instructor/dashboard. Click Approve. - System: Uploads metadata to IPFS -> Mints NFT on Sepolia -> Updates DB.
- Verifier: Go to
/verify/, enter Token ID, see valid credential. - Private Share: Student generates link -> Verifier downloads signed PDF.
This project employs a "Testing Pyramid" strategy including Unit, Integration, and End-to-End (E2E) Browser tests.
Cover models, routes, storage, and services logic using pytest.
cd backend
python -m pytest tests/Cover minting, role-based access control, and revocation using Hardhat/Chai.
npx hardhat testCover the full lifecycle (Student -> Instructor -> Verify) using Playwright.
- Runs the Flask server in a background thread using an in-memory DB.
- Launches a headless Chromium browser.
- Mocks MetaMask: Injects a fake
window.ethereumprovider so tests can click "Connect Wallet" without a real browser extension. - Mocks Blockchain: Patches
BlockchainServiceto simulate minting without waiting for Sepolia confirmation.
cd backend
# Run in headed mode to see the browser actions
python -m pytest e2e/test_full_flow.py --headed- Contract:
CampusCredNFT.sol(ERC-721) - Network: Sepolia Testnet
MINTER_ROLE: Only the backend deployer can mint.revoke(): Allows the university to invalidate credentials.- Non-transferable: Overrides
_updateto prevent students from selling/transferring credentials (Soulbound).
To (re-)deploy with Hardhat:
npx hardhat run scripts/deploy.js --network sepolia- Time-limited tokens (15 mins) stored in backend memory.
- Generated only by the wallet owner of the credential.
PDFSignerServicegenerates a self-signed PKI certificate on the fly (stored inprivate_storage).- When a recruiter downloads evidence via a private link, the PDF is digitally signed to prove it came from the CampusCred system.
If E2E tests fail with browser errors, the binary installation might be corrupted. Force a clean reinstall:
# Delete cache
rm -rf ~/Library/Caches/ms-playwright
# Reinstall via python module
python -m playwright install chromiumThis is a dependency conflict between web3.py and newer versions of eth-typing. Fix by pinning the version:
pip install "eth-typing<5.0.0"Ensure you don't have the SQLite file open in a viewer/IDE while running tests or the server.
Your virtualenv is not active. Run source venv/bin/activate.
Group 10 – Fall 2025 Technical University of Denmark (DTU)
Course: Software Processes and Patterns (02369)
This system is a prototype for educational purposes and does not represent an official DTU credentialing system.
This project is part of academic coursework at DTU. Usage is limited to educational and demonstration purposes.