Built on Ethereum · Stored on IPFS · Verified by Blockchain
"Once sealed on the chain, it cannot be forged, altered, or denied."
- Overview
- Architecture
- Features
- Tech Stack
- Project Structure
- Getting Started
- Smart Contract
- Frontend Pages
- Testing
- Deployment
- Roadmap
ChainSeal is a full-stack decentralized application (dApp) that enables academic institutes to issue tamper-proof credentials on the Ethereum blockchain. It eliminates the need for third-party verification services, manual email confirmations, or trust in centralized databases.
Institutes upload student record PDFs to IPFS and register their cryptographic fingerprint (SHA-256 hash) on a smart contract. Anyone — an employer, a university, or a visa officer — can verify the authenticity of a document by dragging and dropping it onto the public verification portal. No account. No login. No middleman.
Core Principle: The blockchain does not store your document — it stores its fingerprint. If even one character in the PDF is altered, the fingerprint changes completely. Forgery becomes mathematically impossible.
INSTITUTE (Admin Dashboard) ETHEREUM BLOCKCHAIN PUBLIC (Verification Portal)
─────────────────────────── ─────────────────── ────────────────────────────
1. Connect MetaMask wallet
2. Upload student PDF
3. PDF → SHA-256 Hash (local) ──→ Hash stored on-chain ──→ 4. Anyone drags PDF onto portal
4. PDF uploaded to IPFS 5. Browser computes SHA-256
5. QR Certificate Card generated 6. Hash queried from contract
7. ✅ VERIFIED or ❌ FORGED
- 🔐 Wallet-gated access via MetaMask — cryptographic identity, no passwords
- 📄 PDF upload to IPFS via Pinata — decentralized, permanent file storage
- ⛓️ On-chain hash registration — tamper-proof, time-stamped on Ethereum
- 🔁 Credential revocation — invalidate wrongly issued records on-chain
- 🔍 Duplicate detection — detects already-issued PDFs before transaction
- 📊 Live stats dashboard — total issued, active, revoked, on IPFS
- 🃏 Auto-generated QR certificate card — shareable, scannable, verified
- 💾 Session persistence — reloads all credentials from blockchain on reconnect
- 🌙 Dark / Light mode — full enterprise theme switching
- 📂 Drag & drop PDF verification — no account, no login required
- 🌐 QR-code deep link — opens pre-verified URL from student's card
- 🔗 IPFS document link — view original file for cross-reference
- ✅ Full credential details — student name, degree, issue date, institute wallet
⚠️ Revocation status — clearly displayed with amber warning- ❌ Forgery detection — not-found credentials shown with red verdict
- Role-based access control (owner + authorized institutes)
- Duplicate credential prevention
- Revocation mechanism with on-chain events
- Gas-free public read queries
- Full audit log via Ethereum events
| Layer | Technology | Purpose |
|---|---|---|
| Smart Contract | Solidity 0.8.19 | Core business logic on Ethereum |
| Development | Hardhat 2.22 | Compile, test, and deploy contracts |
| Local Blockchain | Ganache Desktop | Visual local Ethereum environment |
| File Storage | IPFS via Pinata | Decentralized PDF storage |
| Wallet | MetaMask | Authentication + transaction signing |
| Frontend | React 18 + Vite | Fast, modern UI |
| Blockchain SDK | ethers.js v6 | Contract interaction from browser |
| File Hashing | Web Crypto API | SHA-256 in-browser (no upload) |
| QR Codes | qrcode.react | Shareable verification cards |
| Drag & Drop | react-dropzone | Smooth file drop UX |
| HTTP Client | axios | Pinata API requests |
ChainSeal/
│
├── contracts/
│ └── CredentialVerifier.sol # Core smart contract
│
├── scripts/
│ └── deploy.js # Deployment script
│
├── test/
│ └── CredentialVerifier.test.js # 11 Hardhat + Chai unit tests
│
├── hardhat.config.js # Hardhat + Ganache config
├── deployment.json # Auto-generated after deploy (gitignored)
│
└── frontend/
├── public/
└── src/
├── pages/
│ ├── AdminPanel.jsx # Institute dashboard (wallet-protected)
│ └── VerifyPortal.jsx # Public verification portal
├── utils/
│ ├── contract.js # ethers.js contract helpers
│ ├── ipfs.js # Pinata upload utility
│ └── hash.js # SHA-256 file hashing (Web Crypto API)
├── App.jsx # React Router setup
├── index.css # Global styles + animations
└── main.jsx # React entry point
| Tool | Version | Download |
|---|---|---|
| Node.js | v18+ LTS | nodejs.org |
| Ganache Desktop | Latest | trufflesuite.com/ganache |
| MetaMask Extension | Latest | metamask.io |
| Pinata Account | Free | pinata.cloud |
1. Clone the repository
git clone https://github.com/YOUR_USERNAME/chainseal.git
cd chainseal2. Install root dependencies
npm install3. Install frontend dependencies
cd frontend
npm install
cd ..Create frontend/.env:
# Pinata IPFS — get from pinata.cloud → API Keys
VITE_PINATA_API_KEY=your_pinata_api_key_here
VITE_PINATA_API_SECRET=your_pinata_api_secret_here
# Paste after running deploy script
VITE_CONTRACT_ADDRESS=0xYourContractAddressHere
# Ganache RPC — check your Ganache desktop app
VITE_RPC_URL=http://127.0.0.1:7545
⚠️ Never commit.envto GitHub. It is already in.gitignore.
Step 1 — Open Ganache Desktop
Launch Ganache. Note your RPC URL (http://127.0.0.1:7545) and Network ID (1337).
Step 2 — Connect MetaMask to Ganache
- MetaMask → Add Network → Add Manually
- Network Name:
Ganache Local| RPC:http://127.0.0.1:7545| Chain ID:1337 - Import a Ganache account: copy private key from Ganache → MetaMask → Import Account
Step 3 — Compile and test
npx hardhat compile
npx hardhat testStep 4 — Deploy
npx hardhat run scripts/deploy.js --network ganacheCopy the printed contract address into frontend/.env as VITE_CONTRACT_ADDRESS.
Step 5 — Start the frontend
cd frontend
npm run dev| Page | URL |
|---|---|
| 🏛️ Admin Dashboard | http://localhost:5173/admin |
| 🔍 Verification Portal | http://localhost:5173/ |
Contract: contracts/CredentialVerifier.sol
| Function | Access | Description |
|---|---|---|
authorizeInstitute(address) |
Owner only | Whitelist an institute wallet |
issueCredential(bytes32, string, string, string) |
Any wallet | Store a credential hash on-chain |
revokeCredential(bytes32) |
Issuing wallet | Mark a credential as revoked |
verifyCredential(bytes32) |
Public (view) | Query credential data by hash |
getInstituteCredentials(address) |
Public (view) | List all hashes issued by a wallet |
event InstituteAuthorized(address indexed institute);
event CredentialIssued(bytes32 indexed fileHash, string studentName, address indexed issuedBy, uint256 issuedAt);
event CredentialRevoked(bytes32 indexed fileHash, address indexed revokedBy);Protected by MetaMask wallet connection. Features:
- Live stats: Total Issued / Active / Revoked / On IPFS
- Issue Credential tab: PDF dropzone + duplicate detection + 3-step progress
- My Credentials tab: grid of all issued certificate cards with QR codes
- Credential table with revoke and card preview actions
- Session restoration from blockchain on every reconnect
No authentication required. Features:
- Drag & drop PDF verification
- SHA-256 hashed entirely in browser
- On-chain query via read-only provider
- Verified / Revoked / Not Found verdict with full metadata
QR codes on certificate cards encode this URL. The portal auto-runs verification on load without needing a file upload.
npx hardhat test CredentialVerifier
✔ Owner is set correctly on deployment
✔ Owner is automatically an authorized institute
✔ Owner can authorize a new institute
✔ Non-owner cannot authorize institutes
✔ Authorized institute can issue a credential
✔ Unauthorized wallet cannot issue credentials
✔ Cannot issue the same hash twice
✔ Verification returns correct data for a valid credential
✔ Verification returns exists=false for unknown hash
✔ Institute can revoke their own credential
✔ Another institute cannot revoke someone else's credential
11 passing (~1s)
- Get free Sepolia ETH from sepoliafaucet.com
- Get an RPC URL from Infura or Alchemy
- Add to
hardhat.config.js:
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
}- Add to root
.env:
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
PRIVATE_KEY=0xYourPrivateKeyHere- Deploy:
npx hardhat run scripts/deploy.js --network sepolia
⚠️ Never hardcode private keys. Always use environment variables.
cd frontend
npm run build
# Push to GitHub and connect repo to vercel.comSet environment variables in Vercel dashboard matching your frontend/.env.
- Smart contract with issuance, revocation, and verification
- MetaMask wallet-gated admin dashboard
- IPFS upload via Pinata
- Drag-and-drop public verification portal
- QR code shareable certificate cards
- Duplicate PDF detection with toast notification
- Session persistence (reload credentials from blockchain)
- Dark / Light mode
- Credential revocation from dashboard
- Sepolia / Mainnet deployment
- Student dashboard (view own credentials by wallet)
- Batch credential issuance
- Credential expiry date support
- Email notification after issuance
- Mobile-optimized responsive UI
- ENS name support for institute addresses
# 1. Fork the repository
# 2. Create your feature branch
git checkout -b feature/your-feature-name
# 3. Commit your changes
git commit -m "feat: add your feature description"
# 4. Push and open a Pull Request
git push origin feature/your-feature-nameChainSeal — Built with ❤️ by Ahmad Raza · Powered by Ethereum, IPFS, and Solidity
Once sealed on the chain, it cannot be forged.