Skip to content

JimwellC/InternProof

Repository files navigation

InternProof

Blockchain-verified OJT logbook system built on Ethereum Sepolia.

Live Demo → internproof.vercel.app
Contract → 0x9A8BD5059F3ec602c9b54D2C78d1f11eE0580bf4


What is InternProof?

InternProof replaces paper-based OJT (On-the-Job Training) logbooks with smart-contract-enforced records on Ethereum. Every clock-in, task description, supervisor signature, and coordinator confirmation is permanently recorded on-chain — independently verifiable by anyone, falsifiable by no one.

Built as a portfolio project demonstrating full-stack Web3 development: Solidity smart contracts, UUPS upgradeability, IPFS proof storage, and a production Next.js frontend.


Live Features

Student

  • Clock in/out on-chain with block timestamp enforcement
  • 4-hour minimum session before clock-out
  • Task description + proof image uploaded to IPFS on every entry
  • Submit past log entries (max 3 days back)
  • Real-time session timer in the dashboard
  • Download Supervisor Sign-off Certificate (PDF) at status 2
  • Download OJT Completion Certificate (PDF) at status 3

Supervisor

  • Approve or reject student log entries with reason
  • View proof images from IPFS inline
  • Expandable student cards with paginated log history
  • Search/filter students by name, ID, or course
  • Issue final sign-off when student reaches required hours

Coordinator

  • Confirm OJT completion after supervisor sign-off
  • View all students under their school
  • Expandable student detail cards with progress tracking

Admin

  • Register schools, courses, companies
  • Approve or revoke supervisor registrations
  • Register coordinators per school

Public

  • Verify any student's OJT record by student ID or wallet address
  • No wallet or account required
  • Displays full log history with IPFS proof images

Tech Stack

Smart Contract

Language Solidity 0.8.24
Upgrade pattern UUPS (EIP-1822) via OpenZeppelin
Access control OwnableUpgradeable + custom role modifiers
Network Ethereum Sepolia testnet
Deployment Hardhat + @openzeppelin/hardhat-upgrades
Bytecode size 23,531 / 24,576 bytes

Frontend

Framework Next.js 14 (App Router)
Language TypeScript
Web3 ethers.js v6
Wallet MetaMask (EIP-1193)
IPFS Pinata — CID stored on-chain
Styling Tailwind CSS + custom CSS variables
Fonts Syne + IBM Plex Mono
PDF jsPDF (landscape certificates, client-side)
Deployment Vercel

Contract Architecture

InternProof (UUPS Proxy) ├── Proxy: 0x9A8BD5059F3ec602c9b54D2C78d1f11eE0580bf4 ├── Implementation: 0xCdFe7B03B4A2C26f5D1f3e010a539056d27684fE ├── Network: Ethereum Sepolia (Chain ID: 11155111) └── Pattern: UUPS upgradeable (EIP-1822)

Roles (on-chain)

Value Role Description
1 Admin System operator, one per deployment
2 Coordinator School representative, confirms completion
3 Supervisor (pending) Awaiting admin approval
4 Supervisor (approved) Can verify entries and issue sign-off
5 Supervisor (revoked) Access removed by admin
6 Student Intern, clocks in/out daily

Key Functions

// Student
registerStudent(fullName, studentId, schoolCode, courseId, supervisorCode)
clockIn()
clockOut(taskDescription)  // taskDescription includes IPFS CID: [proof:CID]
submitPastLogEntry(date, timeIn, timeOut, taskDescription)

// Supervisor
verifyLogEntry(entryId)
rejectLogEntry(entryId, reason)
issueFinalSignOff(studentWallet)

// Coordinator
coordinatorConfirm(studentWallet)

// Admin
registerSchool(name, code)
registerCourse(name, schoolCode, requiredHours)
registerCompany(name, code)
approveSupervisor(wallet)
registerCoordinator(wallet, schoolCode, name)

LogEntry Struct

struct LogEntry {
    uint256 entryId;
    address student;
    uint256 date;
    uint256 timeIn;
    uint256 timeOut;
    uint256 hoursWorked;
    string  taskDescription;  // includes [proof:CID] for IPFS
    uint8   status;           // 0=pending, 1=verified, 2=rejected
    string  rejectionReason;
    address resolvedBy;
    uint256 submittedAt;
    uint256 resolvedAt;
    uint256 linkedToEntryId;
    bool    isClockedIn;
}

IPFS Proof System

Every clock-out and past log entry requires a proof image. The flow:

Roles (on-chain)

Value Role Description
1 Admin System operator, one per deployment
2 Coordinator School representative, confirms completion
3 Supervisor (pending) Awaiting admin approval
4 Supervisor (approved) Can verify entries and issue sign-off
5 Supervisor (revoked) Access removed by admin
6 Student Intern, clocks in/out daily

Key Functions

// Student
registerStudent(fullName, studentId, schoolCode, courseId, supervisorCode)
clockIn()
clockOut(taskDescription)  // taskDescription includes IPFS CID: [proof:CID]
submitPastLogEntry(date, timeIn, timeOut, taskDescription)

// Supervisor
verifyLogEntry(entryId)
rejectLogEntry(entryId, reason)
issueFinalSignOff(studentWallet)

// Coordinator
coordinatorConfirm(studentWallet)

// Admin
registerSchool(name, code)
registerCourse(name, schoolCode, requiredHours)
registerCompany(name, code)
approveSupervisor(wallet)
registerCoordinator(wallet, schoolCode, name)

LogEntry Struct

struct LogEntry {
    uint256 entryId;
    address student;
    uint256 date;
    uint256 timeIn;
    uint256 timeOut;
    uint256 hoursWorked;
    string  taskDescription;  // includes [proof:CID] for IPFS
    uint8   status;           // 0=pending, 1=verified, 2=rejected
    string  rejectionReason;
    address resolvedBy;
    uint256 submittedAt;
    uint256 resolvedAt;
    uint256 linkedToEntryId;
    bool    isClockedIn;
}

IPFS Proof System

Every clock-out and past log entry requires a proof image. The flow:

Student uploads image → Pinata API (pinFileToIPFS) → Returns IPFS CID → CID appended to task description: "Task text\n[proof:Qm...]" → Stored on-chain via clockOut() or submitPastLogEntry() → Displayed in supervisor dashboard and public verify page

The CID is permanently stored on the Ethereum blockchain. The image itself lives on IPFS via Pinata. Anyone can retrieve it at:

https://gateway.pinata.cloud/ipfs/{CID}


Verification Flow

01 Student registers → wallet linked to school + supervisor on-chain 02 Clock in → block timestamp recorded as timeIn 03 Clock out → hours calculated, proof CID stored on-chain 04 Supervisor verifies → totalVerifiedHours updated atomically 05 Hours reach target → student status auto-advances 06 Supervisor sign-off → issueFinalSignOff() called 07 Coordinator confirms → coordinatorConfirm() — status = OJT Complete 08 Certificate issued → student downloads PDF generated client-side


Running Locally

Prerequisites

  • Node.js 18+
  • MetaMask browser extension
  • Sepolia testnet ETH (faucet)
  • Pinata account (free tier)

Frontend

git clone https://github.com/InternProof/internproof.git
cd internproof/frontend
npm install

Create .env.local:

NEXT_PUBLIC_CONTRACT_ADDRESS=0x9A8BD5059F3ec602c9b54D2C78d1f11eE0580bf4
NEXT_PUBLIC_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
NEXT_PUBLIC_PINATA_JWT=your_pinata_jwt_here
npm run dev
# → http://localhost:3000

Smart Contract (read-only — already deployed)

cd internproof
npm install
npx hardhat compile

To deploy a new instance:

npx hardhat run scripts/deploy.js --network sepolia

Project Structure

internproof/ ├── contracts/ │ └── InternProof.sol # UUPS upgradeable contract ├── scripts/ │ └── deploy.js # Hardhat deploy script ├── hardhat.config.js └── frontend/ ├── app/ │ ├── page.tsx # Landing page │ ├── verify/ # Public verification page │ ├── pending/ # Supervisor pending page │ ├── register/ │ │ ├── student/ # Student registration │ │ └── supervisor/ # Supervisor registration │ └── dashboard/ │ ├── admin/ # Admin dashboard │ ├── supervisor/ # Supervisor dashboard │ ├── student/ # Student dashboard │ └── coordinator/ # Coordinator dashboard ├── hooks/ │ └── useWallet.ts # MetaMask + role detection hook └── lib/ ├── contract.ts # Contract helpers + ABI ├── wallet.ts # Wallet connection └── pinata.ts # IPFS upload helpers


Screenshots

Landing Page Student Dashboard
Landing Student
Supervisor Dashboard Public Verify
Supervisor Verify

Screenshots in /docs/ — add your own after deployment.


Design System

Dark amber theme (#0f0e0a background, #f59e0b amber accent) with:

  • Syne — geometric display font for headings
  • IBM Plex Mono — monospace for numbers, wallets, and on-chain data
  • Role colors: Admin/Student = amber, Supervisor = purple (#7c3aed), Coordinator = cyan (#22d3ee)
  • Shimmer progress bars, fade-up animations, KPI card hover accents

What I Learned

  • UUPS proxy pattern — upgradeable contracts without changing the proxy address
  • Bytecode size constraints — stayed under Ethereum's 24,576-byte limit through optimizer tuning
  • ethers.js v6 — BrowserProvider, getSigner, and provider refresh to avoid stale wallet state
  • IPFS via Pinata — storing CIDs on-chain as part of string fields rather than separate mappings
  • jsPDF — generating landscape A4 certificates with geometric shapes, typography, and seal graphics entirely client-side
  • Next.js App Router — static export compatibility with Web3 wallet hooks

Author

Jimwell Calma
Built for academic portfolio — demonstrating blockchain development, smart contract architecture, and full-stack Web3.


License

MIT — free to use as reference for academic and personal projects.

About

Blockchain-verified OJT logbook system on Ethereum Sepolia

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors