MedTrustFund is a full-stack decentralized medical crowdfunding platform that combines AI-assisted document verification, JWT-based RBAC, MongoDB persistence, and an Ethereum escrow smart contract so donors can contribute with clearer transparency and milestone-gated fund movement.
This document is written for technical interviews: it maps what the product does, how the code is organized, how services talk to each other, and how to run everything locally. For day-to-day setup tips, see SETUP.md. For requirements-level depth (SRS-style), see MedTrustFund_Documentation.md.
- Elevator pitch (30 seconds)
- Problem and solution
- Tech stack (as implemented)
- Repository layout
- System architecture
- End-to-end flows
- Feature catalog
- REST API surface
- Smart contract:
MedTrustFundEscrow - AI verification service
- Data model (MongoDB)
- Security, privacy, and compliance-oriented features
- Blockchain indexer
- Run locally (detailed)
- Tests and quality checks
- Honest implementation notes for interviews
- Team and license
Patients create campaigns and upload medical documents. A Python FastAPI service extracts text (OCR / PDF), runs heuristic fraud signals, and returns a 0–100 risk score with a recommendation. The Node/Express API stores campaigns, donations, and audit logs in MongoDB, enforces role-based access, and can deploy and interact with a Solidity escrow using ethers.js v6 and Hardhat artifacts. Donors connect MetaMask from a React 19 + TypeScript + Vite app; funds sit in contract escrow until hospital-confirmed milestones and patient/admin-triggered releases (see contract section for exact on-chain behavior).
| Pain in traditional crowdfunding | How this project approaches it |
|---|---|
| Weak or manual document checks | Automated pipeline: PDF/image text extraction, tampering heuristics, cross-document consistency, weighted risk score |
| Donors cannot see a structured risk signal | Risk breakdown + badges in the UI; thresholds drive auto vs escalated paths |
| Funds are not programmatically tied to milestones | Escrow contract: per-milestone amounts; donate() locks value on-chain; release is gated by confirmMilestone + releaseMilestone |
| Weak traceability for governance | AuditLog model with TTL aligned to a 5-year retention policy; API rate limiting and security middleware on the backend |
| Layer | Technologies | Notes |
|---|---|---|
| Frontend | React 19, TypeScript ~5.9, Vite 7, React Router 6 | UI: Chakra UI v3, Tailwind CSS v4, Framer Motion, TanStack Query, react-hook-form + Zod |
| Backend | Node.js, Express 4, Mongoose 8 | Auth: JWT (jsonwebtoken), passwords bcryptjs; HTTP hardening: helmet, express-rate-limit, express-mongo-sanitize, xss-clean |
| Blockchain | Solidity 0.8.24, Hardhat 2, ethers.js v6 | Local RPC default http://127.0.0.1:8545; optional Polygon Amoy in hardhat.config.js |
| AI service | Python, FastAPI, PyMuPDF (fitz), Pillow, pytesseract | Requires Tesseract OCR installed on the host OS for image OCR |
| Storage | MongoDB | Campaigns, users, donations, risk assessments, smart contract records, audit logs |
| File uploads | multer (backend), disk under configurable UPLOAD_DIR |
Static /uploads route served by Express |
Versions are taken from frontend/package.json, backend/package.json, hardhat/package.json, and ai-service/requirements.txt in this repository.
decentralizedCrowdFund/
├── frontend/ # React + Vite SPA
│ └── src/
│ ├── pages/ # Route-level screens (campaigns, admin, milestones, etc.)
│ ├── components/ # Shared UI (Navbar, wallet button, uploaders, badges)
│ ├── contexts/ # Auth + theme
│ ├── services/api.ts # Axios client (JWT header, 401 → login)
│ └── utils/web3.ts # MetaMask helpers, wei/eth, network helpers
├── backend/
│ ├── server.js # Express app, middleware, route mounting, indexer start
│ ├── routes/ # auth, campaigns, donations, milestones, admin
│ ├── models/ # Mongoose schemas
│ ├── middleware/auth.js # JWT auth, RBAC, audit response hook
│ ├── utils/
│ │ ├── contractUtils.js # Artifact load, deploy, on-chain calls
│ │ └── indexer.js # Polls chain → syncs milestone state to MongoDB
│ └── services/ # Wallet-related helpers
├── hardhat/
│ ├── contracts/MedTrustFundEscrow.sol
│ ├── scripts/deploy.js # Example deploy script (placeholder addresses)
│ └── hardhat.config.js # solidity 0.8.24, hardhat + amoy networks
├── ai-service/
│ ├── main.py # FastAPI app, /verify, /health
│ └── requirements.txt
├── uploads/ # Uploaded documents (created at runtime; gitignored as appropriate)
├── .env.example # Environment template (root; backend uses dotenv from cwd)
├── SETUP.md # Operational setup guide
├── MedTrustFund_Documentation.md # SRS-style documentation
└── IMPLEMENTATION_SUMMARY.md # Changelog-style implementation notes
flowchart LR
subgraph client [Browser]
FE[React SPA]
MM[MetaMask]
end
FE -->|REST + JWT| API[Express API :5000]
FE --> MM
MM -->|signed txs| CHAIN[EVM network :8545]
API --> DB[(MongoDB)]
API -->|HTTP multipart /verify| AI[FastAPI :8001]
API -->|JSON-RPC + deployer key| CHAIN
API -->|read contract state| CHAIN
IDX[indexer in API process] -->|poll milestones| CHAIN
IDX -->|sync| DB
- Patient submits campaign + files → Express (
multer) persists files and metadata. - Express (or flow initiated from campaign route) calls AI service → risk JSON returned.
- Express persists Campaign, RiskAssessment, document hashes, status transitions.
- Admin approves / deploys contract → ethers deploys
MedTrustFundEscrow, stores address + ABI (seeSmartContractmodel). - Donor sends ETH via MetaMask to
donate()→ Express records Donation with tx hash. - Hospital confirms milestone on-chain → Patient/Admin calls
releaseMilestone→ DB updated; indexer also reconciles chain → DB.
- Sign up with role
patient(POST /api/auth/signup). - Create campaign with up to 5 document files (
POST /api/campaigns,patientonly, multipart). - Campaign schema supports document types:
identity,diagnosis,admission_letter,cost_estimate, each with optional SHA-256 hash for integrity. - AI verification produces a final risk score and recommendation (
approvevsescalate) used by the backend/UI.
- All
/api/admin/*routes useauthMiddleware+roleMiddleware(['admin']). - Review pending/high-risk campaigns, make approve/reject decisions, inspect audit logs, list deployed contracts, manage users.
- Browse public campaign routes (
GET /api/campaigns,GET /api/campaigns/:id). - Connect wallet in the frontend (
utils/web3.ts,WalletConnectButton). - Donation may be recorded via
POST /api/donationsand/or backend-assistedPOST /api/donations/:campaignId/donate-direct(see route implementation for prerequisites such as contract deployment).
- Hospital confirms:
POST /api/milestones/:campaignId/confirm(hospital role). - Release:
POST /api/milestones/:campaignId/release(patient or admin). - Indexer (
backend/utils/indexer.js) periodically callsgetContractMilestonesand merges on-chainconfirmedflags intoSmartContractdocuments in MongoDB.
| Role | Representative capabilities in this codebase |
|---|---|
| Patient | Create/update/delete own campaigns; view My Campaigns; trigger milestone release (with contract rules); profile |
| Donor | Donate (wallet + API recording); My Donations; refunds where implemented |
| Hospital | Confirm milestones; list hospital-scoped campaigns (GET /api/milestones/hospital/my-campaigns); hospital profile page |
| Admin | Dashboard metrics; pending review queues; campaign decisions; user admin CRUD; audit log views + export; deployed contract listing |
| Path | Purpose |
|---|---|
/, /login, /signup |
Marketing/home and auth |
/campaigns, /campaign/:id |
Public discovery and detail + donation UX |
/dashboard, /create-campaign |
Authenticated home flows |
/my-campaigns, /my-donations |
Patient/donor hubs |
/milestones |
Hospital milestone workflow |
/profile, /analytics, /hospital-profile |
Account and analytics-style pages |
/admin/dashboard, /admin/users, /admin/audit-logs, /admin/contracts |
Admin console sections |
The app uses a lightweight
ProtectedRoutewrapper: presence oflocalStorage.tokengates private pages (no server-side render guard).
- Global audit hook:
auditLogMiddlewarewrapsres.jsonfor authenticated users and persistsAuditLogentries (best-effortsave()). - Rate limiting:
100requests / 15 minutes / IP on/api/*. - Static uploads:
GET /uploads/...for stored files. - Health:
GET /api/healthreturns Mongo connection state.
Base URL (local): http://localhost:5000. JSON body unless noted. Bearer JWT: Authorization: Bearer <token>.
| Method | Path | Access | Purpose |
|---|---|---|---|
| POST | /signup |
Public | Register |
| POST | /login |
Public | Login → JWT |
| GET | /profile |
Auth | Profile |
| PUT | /profile |
Auth | Update profile |
| POST | /verify-wallet |
Auth | Associate wallet |
| GET | /users/:id |
Admin | Fetch user |
| Method | Path | Access | Purpose |
|---|---|---|---|
| POST | / |
Patient | Create (+ file upload) |
| GET | / |
Public | List |
| GET | /:id |
Public | Detail |
| PUT | /:id |
Patient, Admin | Update |
| DELETE | /:id |
Auth (see route) | Delete |
| POST | /:id/deploy-contract |
Admin | Deploy escrow from server signer |
| GET | /:id/donations |
Public | Donations for campaign |
| POST | /:id/admin-review |
Admin | Admin review actions |
| Method | Path | Access | Purpose |
|---|---|---|---|
| POST | / |
Donor | Create donation record |
| GET | / |
Auth | List (context depends on implementation) |
| GET | /:id |
Auth | Donation detail |
| POST | /:id/refund |
Auth | Refund flow |
| GET | /campaign/:campaignId |
Public | By campaign |
| POST | /:campaignId/donate-direct |
Donor | Backend-driven donation path |
| Method | Path | Access | Purpose |
|---|---|---|---|
| POST | /:campaignId/confirm |
Hospital | Confirm milestone |
| POST | /:campaignId/release |
Patient, Admin | Release funds for milestone |
| GET | /:campaignId |
Public | Milestone snapshot |
| GET | /hospital/my-campaigns |
Hospital | Scoped campaign list |
| Method | Path | Purpose |
|---|---|---|
| GET | /dashboard |
Aggregate stats + recent audits |
| GET | /campaigns/pending-review |
Queue |
| GET | /campaigns/:id/review-details |
Detail for decisioning |
| POST | /campaigns/:id/decision |
Approve/reject |
| GET | /users |
User list |
| PUT | /users/:id |
Update user |
| DELETE | /users/:id |
Deactivate/delete (see handler) |
| GET | /audit-logs |
Query logs |
| GET | /audit-logs/export |
Export format for compliance workflows |
| GET | /contracts |
List smart contract deployments |
File: hardhat/contracts/MedTrustFundEscrow.sol
Compiler: Solidity ^0.8.24 (configured in Hardhat as 0.8.24)
| Field | Meaning in code |
|---|---|
owner |
msg.sender at deploy time — typically the deployer account (in this project, the backend’s PRIVATE_KEY wallet when deploying from contractUtils.deployEscrowContract) |
patient |
Constructor arg _patient — end recipient of released milestone funds |
hospital |
Constructor arg _hospital — only this address may call confirmMilestone |
milestones[]: each milestone hasdescription,amount(wei),confirmed,releasedAt.totalDonated: incremented on eachdonate()payment.isActive: whenfalse,donate()reverts.
| Function | Who can call | Behavior |
|---|---|---|
donate() |
Anyone | Payable; requires isActive; increases totalDonated; emits Donated |
confirmMilestone(index) |
hospital |
Sets milestones[index].confirmed = true; emits MilestoneConfirmed |
releaseMilestone(index) |
owner OR patient |
Requires milestone confirmed, not already released, sufficient balance; transfers milestone.amount wei to patient; sets releasedAt; emits FundsReleased |
refund(donor, amount) |
owner only |
Decrements totalDonated (guarded by require), transfers amount to donor; emits Refunded |
getMilestones() |
View | Returns full milestone array |
Donated(donor, amount)MilestoneConfirmed(index)FundsReleased(index, amount)Refunded(donor, amount)
npx hardhat compile→ artifact athardhat/artifacts/contracts/MedTrustFundEscrow.sol/MedTrustFundEscrow.json.- Backend
loadContractArtifact()reads ABI + bytecode. deployEscrowContract(patientAddress, hospitalAddress, milestones)buildsContractFactorywithPRIVATE_KEYsigner onRPC_URL.- Deployment tx hash and address are persisted on the related Campaign / SmartContract documents (see routes in
campaigns.js).
hardhat/scripts/deploy.js demonstrates getContractFactory + deploy with placeholder patient/hospital addresses and static milestone arrays — useful for manual testing; the productized path is the admin API deployment.
Entry: ai-service/main.py (FastAPI)
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
Liveness + version metadata |
| POST | /verify |
Multipart upload of files + hospital_verified flag → structured risk result |
- Ingest files to
./uploadsunder the AI service working directory. - Text extraction: PDF via PyMuPDF; images via Tesseract (
pytesseract). - Document typing: keyword scoring into
identity,diagnosis,admission_letter,cost_estimate, orunknown. - Signals:
- Tampering heuristics (
check_image_tampering): file size, missing EXIF, resolution/compression heuristics. - “AI-like” text heuristics (
analyze_ai_generated_content): generic phrases, repetition, medical keyword coverage, length. - Cross-document metadata (
validate_metadata_consistency): creator/producer variance, patient name consistency across extracted text.
- Tampering heuristics (
- Weighted score (SRS-style)
RiskScore = 0.35 × avg_tampering + 0.35 × avg_ai + 0.30 × metadata_mismatch
Ifhospital_verified, multiply final score by 0.8 (20% reduction, floored at 0). - Verdict bands (as coded):
- Low Risk: score < 40 → recommendation
approve - Medium Risk: 40–69 →
escalate(advisory to donors) - High Risk: ≥ 70 →
escalate, admin review messaging
- Low Risk: score < 40 → recommendation
- Tesseract must be installed and on the PATH used by the Python process (e.g.
apt install tesseract-ocron Debian/Ubuntu). Without it, OCR paths for images degrade (errors logged; scores may skew).
High-signal entities (see backend/models/*.js for full fields):
- User —
role∈patient | donor | hospital | admin, optionalwalletAddress, bcrypt password, hospital fields, profile/KYC subdocs. - Campaign — documents[], milestones[],
riskAssessmentId,smartContractAddress, status enum (draft,pending_verification,active, etc.). - RiskAssessment — linked from campaign; stores scoring breakdown (see model).
- Donation — amount, status (
pending,locked_in_escrow, …), tx metadata, escrow subdoc. - SmartContract —
contractAddress, network enum, embedded milestone snapshot + full ABI (Mixed), deployment metadata. - AuditLog — typed
actionenum (signup, donation, deploy, admin actions, …), optional entity linkage, TTLexpiresAtwith Mongo TTL index (~5 years from creation in schema default).
- JWT authentication; role middleware on sensitive routers (explicitly on all
/api/admin/*routes). - Password hashing via bcrypt (pre-save hook on
User). - Helmet security headers, rate limiting, NoSQL injection sanitization, XSS-oriented sanitization middleware.
- Document hashing on campaign documents (integrity reference, not encryption by itself).
- Audit logging with time-bounded retention via TTL (design goal: 5-year window; verify organizational policy vs Mongo TTL behavior in production).
- Private keys: backend uses
PRIVATE_KEYfor deployment and some on-chain transactions — treat.envas secret; never commit.
startIndexer(30) runs after Mongo connects (server.js):
- Loads active
SmartContractrecords. - For each address, reads on-chain milestones via
getContractMilestones. - If chain shows
confirmedwhile DB does not, updates embedded milestone flags.
This bridges direct on-chain activity (e.g., external calls) back into MongoDB for UI consistency.
- Node.js 18+ and npm
- MongoDB running locally or Atlas URI
- Python 3.9+ and
pip - Tesseract OCR (for image OCR in AI service)
- MetaMask (for donor flows)
- Git
cd /path/to/decentralizedCrowdFund
cd backend && npm install && cd ..
cd frontend && npm install && cd ..
cd hardhat && npm install && cd ..
cd ai-service && pip install -r requirements.txt && cd ..Copy the template and edit values:
cp .env.example .envImportant: backend/server.js calls require("dotenv").config() with no path — it loads .env from the current working directory when you start Node. Prefer starting the backend from the repository root (where .env.example lives) or place a .env in backend/ and start from backend/. Align:
MONGODB_URIJWT_SECRET,JWT_EXPIRYRPC_URL,PRIVATE_KEY(local Hardhat account #0 key is common for dev; never mainnet)AI_SERVICE_URL(defaulthttp://localhost:8001)PORT(default5000)
Frontend: frontend/.env (or shell env) should define VITE_API_URL if you are not using the Vite dev proxy. The repo’s frontend/vite.config.ts proxies /api → http://localhost:5000, and api.ts defaults to "/api" when VITE_API_URL is unset — that combination works for local dev.
cd hardhat
npx hardhat compile
cd ..Without this step, contractUtils.loadContractArtifact() throws: run npx hardhat compile first.
cd hardhat
npx hardhat nodeKeep this terminal open. Default JSON-RPC: http://127.0.0.1:8545.
Local example:
mongod --dbpath /path/to/your/dbOr use Atlas and set MONGODB_URI accordingly.
cd ai-service
# Either:
uvicorn main:app --reload --host 0.0.0.0 --port 8001
# Or (module also boots uvicorn on 8001):
python main.pyFrom the directory that contains your effective .env (commonly repo root):
cd backend
npm run dev
# or: npm startVerify: GET http://localhost:5000/api/health
cd frontend
npm run devOpen http://localhost:5173.
- Signup as
patient→ create campaign with documents. - Ensure AI service responded; observe risk output in UI or network tab.
- Signup as
admin→ approve / deploy contract from admin UI (callsPOST /api/campaigns/:id/deploy-contractwhen configured). - Signup as
donor, connect MetaMask to Hardhat network (add RPChttp://127.0.0.1:8545, chain ID from Hardhat). - Donate on-chain via campaign detail flow; confirm Donation stored with tx hash.
- Hospital user confirms milestone; patient or admin releases per contract rules.
hardhat/hardhat.config.js includes an amoy network. Set PRIVATE_KEY to a funded test key, adjust RPC_URL / network selection, and run deploy scripts with --network amoy. Use a public faucet for test MATIC (URLs change over time; search “Polygon Amoy faucet”).
| Area | Command / location |
|---|---|
| Backend unit/integration | cd backend && npm test (Jest) |
| AI service | ai-service/test_main.py (pytest-style / manual — inspect file for usage) |
| Frontend lint | cd frontend && npm run lint |
| Frontend build | cd frontend && npm run build |
These points demonstrate codebase literacy (valuable in senior interviews):
- Fund recipient on
releaseMilestone: the Solidity contract transfers topatient, not tohospital. Narrative docs sometimes describe hospital payouts; the on-chain code pays the patient wallet for each milestone amount. - Deployer-centric permissions:
owneris the deployer.refundand certain flows areowner-only;releaseMilestoneallowsownerORpatient. - Hospital confirmation signing:
confirmMilestoneOnChainincontractUtils.jsconstructs a signer fromhospitalWallet.privateKey. In production you would typically have the hospital user sign in-browser (MetaMask) or use a custodial HSM — storing raw private keys server-side is not a production pattern. - AI layer: current “AI probability” uses heuristics, not a trained deep model — still a valid microservice boundary and scoring contract, but describe it accurately.
- Audit middleware: logging triggers on
res.jsonand authenticatedreq.userpaths; not every endpoint may populate richaction/entityTypefields (often defaults toapi_call). - Risk thresholds: implemented cutoffs are 40 and 70 in
ai-service/main.py(some older tables in docs use 0–39 vs 0–40 wording).
- Dungar Soni (B23CS1105) — Architecture & Blockchain
- Prakhar Goyal (B23CS1106) — AI Verification & Backend
- Raditya Saraf (B23CS1107) — Frontend & UX
License: MIT (as stated in prior project documentation; add a root LICENSE file if you publish the repo publicly).
| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:5000 |
| API health | http://localhost:5000/api/health |
| AI service | http://localhost:8001 |
| AI health | http://localhost:8001/health |
| Hardhat JSON-RPC | http://127.0.0.1:8545 |
Generated from the current repository layout and source files for interview preparation.