Credipro is a decentralized lending protocol that solves the "Sybil default paradox" in Web3 uncollateralized lending. It connects institutional underwriters with retail borrowers through a privacy-preserving, zero-knowledge proof-based underwriting model built on Midnight Network's Kachina protocol.
Traditional Web3 lending protocols face a critical vulnerability: malicious actors can:
- Generate multiple anonymous identities (DIDs)
- Obtain undercollateralized loans for each identity
- Default without real-world consequences
- Repeat the attack infinitely, bankrupting the protocol
Credipro solves this by binding cryptographic proofs of real-world identity to loans, enabling selective identity reveal only to affected underwriters upon default—backed by legal enforcement through an off-chain Master Loan Agreement (MLA).
Credipro uses three core innovations:
-
Zero-Knowledge Proof of Creditworthiness
- Borrowers prove their FICO score meets minimum thresholds without revealing the actual score
- Powered by zkTLS oracle integration (or mock for MVP)
- Generates zk-SNARKs using BLS12-381 elliptic curves
-
Cryptographic Identity Binding
- User identity is hashed and committed on-chain
- Actual name, ID, and biometrics remain strictly private (off-chain only)
- Prevents Sybil attacks through unique identity commitment per loan
-
Selective Identity Reveal with Oracle Consensus
- Upon default, a 2-of-3 trusted oracle committee votes
- Only when consensus (≥2 of 3) is reached does the identity decrypt
- Identity revealed exclusively to the affected underwriter for legal recourse
- Backed by signed Master Loan Agreement defining jurisdiction and enforcement terms
To transition Credipro from a hackathon proof-of-concept to a secure, auditable, production-ready protocol, we have completed a comprehensive architectural hardening sprint addressing key technical debt and security vectors:
-
Persistent SQLite Storage (
credipro.sqlite)- Eliminated volatile in-memory
Mapdata structures across the backend. - Initialized a robust SQLite database layer (
backend/src/db.ts) with dedicated relational schemas forborrowers,identities, andoracle_votes. - Enabled durable state persistence across server restarts and concurrent client requests.
- Eliminated volatile in-memory
-
ZK-Friendly Cryptography (
poseidon-goldilocks)- Deprecated vulnerable Node.js
crypto.createHash('sha256')mock implementations. - Integrated Plonky2-compatible
poseidon-goldilockshashing (hashNoPad) across the credit bureau, identity provider, prover witness context, and smart contract client layers. - Aligned off-chain hash generation with the exact cryptographic arithmetic required by Midnight ZK circuits.
- Deprecated vulnerable Node.js
-
Structured Observability (
winston)- Replaced all legacy
console.log,console.warn, andconsole.errorstatements with an enterprise-grade Winston logging pipeline (backend/src/logger.ts). - Configured custom formatting with timestamped console transports and dedicated file logs (
logs/error.log,logs/combined.log) for complete auditability.
- Replaced all legacy
-
Strict JWT Authentication & Zero-Bypass Security
- Removed the insecure
DISABLE_AUTHdeveloper bypass flag. - Enforced strict JWT verification middleware (
backend/src/server.ts) across all protected API routes. - Implemented a dedicated
/api/auth/tokenauthentication endpoint issuing cryptographically signed JSON Web Tokens (JWT_SECRET) for authorized borrower sessions.
- Removed the insecure
-
Fully Asynchronous Service Architecture
- Upgraded all oracle, bureau, and committee service methods (
MockOracleService,MockCreditBureau,MockIdentityProvider) to fully asynchronousasync/awaitsignatures to support real database I/O and future zkTLS network calls.
- Upgraded all oracle, bureau, and committee service methods (
- Node.js 18+ (for TypeScript SDK)
- Midnight Network testnet wallet (e.g., Lace Wallet)
- Compact language compiler (v0.16–v0.21)
midnight-jsSDK (latest version)
# Clone the repository
git clone https://github.com/nova-rishabh/Credipro.git
cd Credipro
# Install all workspace dependencies (backend + frontend)
npm install
# Copy environment template and set JWT_SECRET
cp .env.example .env
# Compile Compact smart contract & backend
npm run compile:contract
npm run buildOption A — Local development (two terminals)
Terminal 1: Backend API (nodemon — recompiles & restarts on backend/src changes, port 3001)
npm run start:backendTerminal 2: React frontend (CRA dev server on port 3000)
npm run start:frontendOption B — Docker Compose (frontend on :3000, backend API on :3001)
docker compose up --build
# Open http://localhost:3000(Note: The frontend uses react-app-rewired to polyfill Node.js core modules like crypto required by the Midnight SDK in the browser).
Credipro/
├── backend/
│ └── src/
│ ├── config/ # Client singleton & env
│ ├── lib/ # logger, db
│ ├── middleware/ # JWT auth
│ ├── routes/ # API routes
│ ├── services/ # contract, oracle, prover
│ ├── types/
│ ├── app.ts
│ └── server.ts
├── frontend/ # React SPA (port 3000)
├── contracts/
└── docker-compose.yml
Since Credipro is built on the Midnight Network, you must have the official wallet extension installed to interact with the decentralized application.
- Open a Chromium-based browser (Chrome, Edge, Brave).
- Go to the Chrome Web Store and search for Lace Wallet (or visit the official Midnight Network resources).
- Install the extension and pin it to your toolbar.
- Open the extension, follow the onboarding to create a new test wallet, and securely back up your seed phrase.
- In the Lace Wallet settings, ensure your network is set to Midnight Testnet.
- Refresh the Credipro application at
http://localhost:3000. The "Connect Wallet" button will now successfully link your Lace Wallet to the dApp!
import { CrediproContract } from './contracts/Credipro';
import { Lace } from '@midnight-ntwrk/lace-sdk';
// 1. Connect wallet
const wallet = await Lace.connect();
const borrower = wallet.getAddress();
// 2. Prepare witness data (off-chain, local)
const creditScore = 720; // Mock FICO score
const passport = await readEncryptedPassport(); // Local NFC or storage
// 3. Request loan
const loanId = await contract.requestLoan(
BigInt(100000), // loanAmount (in smallest denomination)
underwriterAddress, // poolAddress
BigInt(180) // defaultTermDays
);
console.log(`Loan approved! ID: ${loanId}`);┌─────────────────────────────────────────────────────────┐
│ Credipro Smart Contract Architecture │
├─────────────────────────────────────────────────────────┤
│ │
│ LEDGER CONTEXT (Public, On-Chain) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • liquidityPools: Pool TVL tracking │ │
│ │ • publicRiskParameters: Min credit scores, LTV │ │
│ │ • encryptedIdentityCommitments: Loan records │ │
│ │ • oracleCommitteeSignatures: Default votes │ │
│ └───────────────────────────────────────────────────┘ │
│ ↑ │
│ disclose() │
│ ↓ │
│ WITNESS CONTEXT (Private, Off-Chain) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • mock_zkTLS_CreditScore(): Borrower's FICO │ │
│ │ • read_Identity_NFC(): Encrypted passport │ │
│ │ • compute_identity_hash(): Local hash derivation │ │
│ │ • check_default_deadline_exceeded(): Time check │ │
│ └───────────────────────────────────────────────────┘ │
│ ↑ │
│ Assertions & Proofs │
│ ↓ │
│ CIRCUIT CONTEXT (ZK Prover) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • requestLoan(): Underwriting circuit │ │
│ │ • triggerSlashing(): Default resolution circuit │ │
│ │ • verify_master_loan_agreement(): MLA validation │ │
│ └───────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
Purpose: Generate ZK proof that borrower meets lender's risk parameters.
INPUT: loanAmount, poolAddress, defaultTermDays
OUTPUT: loanId (Bytes<32>)
LOGIC:
1. Retrieve witness data (creditScore, identity, lender)
2. Fetch public risk parameters from ledger
3. Assert: disclose(creditScore >= minCreditScore)
4. Assert: disclose(loanAmount <= poolTVL)
5. Compute identity commitment hash
6. Create loan record on ledger
7. Return loan ID
ZERO-KNOWLEDGE:
✓ Verifier learns: Loan was approved
✗ Verifier does NOT learn: Actual credit score, income, real identity
Purpose: Prove default conditions met, trigger identity reveal.
INPUT: loanId
OUTPUT: (none, updates ledger state)
LOGIC:
1. Retrieve loan record
2. Assert: disclose(!isDefaulted) [Not already defaulted]
3. Assert: disclose(deadlineExceeded) [Past due]
4. Assert: disclose(oracleApprovals >= 2) [Consensus reached]
5. Mark isDefaulted = true
6. Trigger off-chain oracle decryption
ZERO-KNOWLEDGE:
✓ Circuit proves conditions are met
✗ Circuit does NOT decrypt identity (off-chain oracle action)
- No raw credit data on-chain: Only ZK proofs of creditworthiness
- Identity hidden by default: Only hash commitment on ledger
- Witness data stays local: Never exposed without explicit
disclose()
- Cryptographic identity binding: Each loan tied to unique identity hash
- One identity = one loan per underwriter: Prevents duplicate loans
- Real-world enforcement: MLA + selective reveal creates legal deterrent
- Rational privacy framework: Data shielded by default, selectively disclosed for compliance
- Underwriter anonymity: Risk parameters are public; portfolio composition stays private
- Selective reveal only to affected lender: Other participants remain blind
- Mock zkTLS oracle: Simulates credit score verification (replaceable with real zkTLS)
- 2-of-3 oracle committee: Simplified threshold consensus (scalable to N-party MPC in Phase 2)
- Master Loan Agreement: Off-chain legal enforcement for real-world collections
Credipro/
├── backend/ # Express API + Midnight SDK (port 3001)
├── frontend/ # React SPA (port 3000)
├── contracts/
│ └── Credipro.compact # Smart contract (MVP-complete)
├── docs/ # Product & technical documentation
├── docker-compose.yml # Backend + frontend stack
└── README.md
- Compact smart contract (requestLoan + triggerSlashing circuits)
- Contract specification & documentation
- TypeScript witness implementations
- React frontend
- Lace Wallet integration
- Mock zkTLS oracle
- End-to-end testing
- Real zkTLS oracle integration (zkPass, Reclaim Protocol)
- True 2-of-3 threshold decryption (BLS signatures)
- Loan repayment logic
- Interest accrual & fees
- MerkleTree for identity proofs
- Zswap integration for fund transfers
- Full N-party MPC threshold decryption
- Secondary loan market & trading
- Cross-chain bridging (Ethereum, Polygon)
- Loan securitization
- DAO governance for oracle committee selection
Threat: Attacker generates multiple DIDs, gets loans, defaults on all.
Mitigation:
- Each loan is cryptographically bound to a unique identity hash
- Upon default, identity can be revealed (with oracle consensus)
- Master Loan Agreement provides legal jurisdiction for real-world enforcement
- Attacker's real identity becomes discoverable, creating deterrent
Threat: 2-of-3 oracle committee could be compromised or collude.
Mitigation:
- Circuit proves conditions are met (deadline + approvals) — oracle cannot forge proofs
- Identity reveal happens off-chain — oracle cannot leak to other participants
- Master Loan Agreement provides legal recourse against rogue oracles
- Phase 2: True N-party MPC threshold decryption eliminates single-point-of-failure
Threat: Underwriter's risk parameters leak to competitors.
Mitigation:
publicRiskParametersare intentionally public (set by underwriter)- Underwriter controls what parameters are exposed
- Portfolio composition and proprietary data never on-chain
-
Wallet Setup & Onboarding
- Install the Lace Wallet extension from the Chrome Web Store.
- Create a test wallet and switch to the Midnight Testnet.
- Navigate to
http://localhost:3000and click Connect Wallet. - Sign the Master Loan Agreement (off-chain, legal contract).
- Store encrypted identity locally (NFC passport chip or secure storage).
-
Loan Request
- Mock-authenticate with credit bureau (zkTLS or mock oracle)
- Prover generates ZK proof of creditworthiness locally
- Submit proof to contract via
requestLoan()circuit - Receive loan ID upon approval
-
Repayment
- Monitor loan term
- Repay via Zswap (private token transfer)
- Upon full repayment, loan closes and identity commitment removed
-
Pool Setup
- Deploy liquidity pool (set TVL)
- Define public risk parameters (min credit score, max LTV, min income)
-
Loan Approval
- Monitor incoming loan requests
- Disburse funds via Zswap upon circuit approval
-
Default Resolution
- Vote on default resolutions (2-of-3 oracle committee)
- Upon consensus, receive decrypted identity (via off-chain oracle)
- Verify Master Loan Agreement and pursue legal collections
-
Default Voting
- Monitor flagged loans (past deadline)
- Vote to approve or reject default resolution
-
Identity Decryption
- Upon 2-of-3 consensus, perform decryption
- Send encrypted identity to affected underwriter
-
Audit Trail
- Maintain logs of all votes and decryptions
- Support legal proceedings if needed
# Unit tests for smart contract
npm test
# Integration tests (end-to-end flow)
npm test:integration
# Compile & validate Compact syntax
npm run compile:contract
# Generate test coverage
npm test:coverage// ✓ requestLoan succeeds when credit score meets threshold
// ✓ requestLoan fails when credit score below threshold
// ✓ requestLoan creates identity commitment
// ✓ triggerSlashing succeeds when deadline exceeded + oracle consensus
// ✓ triggerSlashing fails if deadline not exceeded
// ✓ triggerSlashing fails if oracle consensus not reached
// ✓ verify_master_loan_agreement succeeds with valid signature
// ✓ Identity remains private until default resolution-
Fork the repository
git fork https://github.com/nova-rishabh/Credipro.git
-
Create a feature branch
git checkout -b feature/your-feature-name
-
Make changes and ensure tests pass
npm test ## Deployment & Running (Testnet) This project supports local development (mock mode), compiled local contract execution, and on-chain deployments to Midnight testnet. The `backend/scripts/deploy.ts` script (if present) will deploy the compiled contract artifact and return the deployed contract address. Environment variables (important) - `MIDNIGHT_RPC` — Midnight testnet RPC URL (required for on-chain mode) - `MIDNIGHT_WALLET_SEED` or `MIDNIGHT_PRIVATE_KEY` — Deployer credentials (provide locally; do NOT commit) - `MIDNIGHT_CONTRACT_ADDRESS` — Address written after successful deploy (optional) - `USE_COMPILED_CONTRACT` — `true` to run using the local compiled contract binding - `USE_ONCHAIN_CONTRACT` — `true` to use the on-chain contract via Midnight SDK - `WRITE_ENV_ON_SUCCESS` — `true` to append `MIDNIGHT_CONTRACT_ADDRESS` to `.env` after deploy Run the deploy script (PowerShell example) ```powershell $env:MIDNIGHT_RPC='https://your-midnight-testnet-rpc' $env:MIDNIGHT_WALLET_SEED='your twelve/24-word seed here' $env:WRITE_ENV_ON_SUCCESS='true' npx ts-node --esm backend/scripts/deploy.ts
If you prefer a private key:
$env:MIDNIGHT_RPC='https://your-midnight-testnet-rpc' $env:MIDNIGHT_PRIVATE_KEY='0xYOUR_PRIVATE_KEY' $env:WRITE_ENV_ON_SUCCESS='true' npx ts-node --esm backend/scripts/deploy.ts
Notes
- The deploy script is intentionally manual: do not paste secrets in public places. If you do not want the script to write
.env, omitWRITE_ENV_ON_SUCCESSand copy the printed contract address into your.envmanually. - You must fund the deployer address with testnet tokens (faucet) before running the script.
Frontend live mode
- The frontend runs in demo mode by default (no wallet required). To force real wallet mode and prevent the demo auto-connect, open the app with
?livein the URL (e.g.,http://localhost:3000?live). - The frontend reads
/api/healthon load to discoverMIDNIGHT_CONTRACT_ADDRESSand whether the backend is operating in mock or on-chain mode.
- The deploy script is intentionally manual: do not paste secrets in public places. If you do not want the script to write
-
Submit a pull request
- Describe your changes clearly
- Reference any related issues
- Ensure CI checks pass
-
Compact: Follow the official Compact style guide
- Use
disclose()explicitly for all sensitive data - No implicit disclosures
- Proper pragma syntax:
pragma language_version >= 0.16 && <= 0.21;
- Use
-
TypeScript: ESLint + Prettier configuration
npm run lint npm run format
-
SMART_CONTRACT_SPEC.md — Complete contract specification (500+ lines)
- Ledger/Witness/Circuit context details
- Data type definitions & invariants
- Security analysis & threat models
- Integration guide for SDK
-
MVP_DELIVERABLES.md — Integration checklist (300+ lines)
- TypeScript witness stubs
- Circuit call examples
- Next steps & priorities
- Known limitations
-
PRD.md — Product Requirements Document
- Problem statement & market opportunity
- Core mechanics & user flows
- Feature scope (MVP vs. Phase 2+)
-
TRD.md — Technical Requirements Document
- Technology stack (Compact, Midnight, Kachina)
- Architecture constraints
- Performance targets
MIT License — See LICENSE file for details.
- Documentation: See
/Docsfolder and.mdfiles - Issues: GitHub Issues
- Discussions: GitHub Discussions
- Official Docs: https://docs.midnight.network
- Compact Reference: https://docs.midnight.network/develop/reference/compact/lang-ref
- SDK: https://www.npmjs.com/package/@midnight-ntwrk/compact-js
Built with ❤️ for the Midnight Network hackathon (May 2026).
Special thanks to:
- Midnight Team for the Kachina protocol & Compact language
- Lace Wallet for seamless integration
- zkTLS/zkPass community for privacy-preserving oracle designs
| Milestone | Date | Focus |
|---|---|---|
| MVP Launch | May 2026 | Hackathon demo (requestLoan + triggerSlashing) |
| Phase 2 | Q3 2026 | Real zkTLS + True MPC threshold decryption |
| Phase 3 | Q4 2026 | Cross-chain bridging + Secondary market |
| Phase 4 | 2027 | Mainnet launch + DAO governance |
Status: ✅ MVP Complete — Ready for Development
Last Updated: May 16, 2026