A production-ready decentralized education loan platform built on the Mantle Network. Students apply for loans, administrators manage approvals and disbursements, and all repayments are tracked transparently on-chain.
Frontend: https://eduloan-mantle.vercel.app
Contract (Mantle Sepolia): 0x0AeC48e885a9ec762A2653cB8866e35f1CEAcc8d
Verified Source: View on Mantlescan
Try it now! Connect your wallet (switch to Mantle Sepolia), get testnet MNT from the faucet, and test the full loan flow.
- Loan Applications - Students specify amount and educational purpose
- Admin Approval System - Approve/reject based on liquidity and criteria
- Direct Disbursement - Funds transfer directly to approved borrowers
- Flexible Repayments - Pay any amount, track progress transparently
- Multiple Loans - Students can have multiple active loans simultaneously
- Purpose Tracking - Every loan documents its educational purpose
- Custom Errors - Gas-optimized error handling (~91% savings on reverts)
- Input Validation - Min/max bounds for amounts (0.01-10 MNT)
- Fixed Duration - All loans have 365-day repayment period
- Status Tracking - Clear lifecycle: Pending → Approved → Active → Repaid/Defaulted
- Event-Driven - Complete audit trail via blockchain events
- Access Control - Admin-only functions properly protected
- 25/25 tests passing with 100% function coverage
- Gas-optimized custom errors save ~17,600 gas total
- HackQuest-compatible contract interface
- Verified source code on Mantlescan
- Production-ready security patterns
- Foundry (for smart contracts)
- Node.js 18+ (for frontend)
- Wallet with Mantle Sepolia MNT (Get from faucet)
# Clone the repository
git clone https://github.com/azharpratama/eduloan.git
cd eduloan
# Copy environment file
cp .env.example .env
# Edit .env and add your PRIVATE_KEY for contract deployment
# Install contract dependencies
cd contract
forge install
# Install frontend dependencies
cd ../frontend
npm installeduloan/
├── contract/ # Smart contract (Foundry)
│ ├── src/
│ │ └── EduLoan.sol # Main contract (338 lines)
│ ├── test/
│ │ └── EduLoan.t.sol # 25 comprehensive tests
│ ├── script/
│ │ └── EduLoan.s.sol # Deployment script
│ ├── lib/ # Foundry dependencies
│ └── foundry.toml # Foundry config
├── frontend/ # React frontend
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── config/ # Contract ABI & config
│ │ ├── hooks/ # Web3 hooks
│ │ ├── pages/ # App pages
│ │ └── lib/ # Utilities
│ └── package.json
├── .env.example # Environment template
└── README.md # This file
cd contract
forge buildcd contract
# Run all tests
forge test
# Run with gas report
forge test --gas-report
# Run with verbosity
forge test -vvvExpected: All 25 tests passing ✅
cd contract
# 1. Configure ../.env with your PRIVATE_KEY
# 2. Deploy to Mantle Sepolia
forge script script/EduLoan.s.sol \
--rpc-url https://rpc.sepolia.mantle.xyz \
--broadcast \
--verify \
-vvvv
# 3. Note the deployed contract address from output# Check contract balance
cast call <CONTRACT_ADDRESS> "getContractBalance()" --rpc-url https://rpc.sepolia.mantle.xyz
# Get total loans
cast call <CONTRACT_ADDRESS> "getTotalLoans()" --rpc-url https://rpc.sepolia.mantle.xyz
# Admin deposits funds
cast send <CONTRACT_ADDRESS> "depositFunds()" \
--value 10ether \
--private-key <YOUR_PRIVATE_KEY> \
--rpc-url https://rpc.sepolia.mantle.xyzThe frontend is built with React 19, TypeScript, Vite, and Wagmi v2 for Web3 integration.
cd frontend
# Copy environment file (optional - has default values)
cp .env.example .env
# Update contract address if you deployed your own:
# VITE_CONTRACT_ADDRESS=0xYourContractAddressnpm run devVisit http://localhost:5173
npm run build
npm run preview # Preview production build- Home (
/) - Landing page with project overview - Apply Loan (
/apply) - Loan application form - My Loans (
/my-loans) - View all your loans - Loan Detail (
/loan/:id) - Detailed loan view with payment - Admin (
/admin) - Admin dashboard (approve, disburse, manage funds)
| Function | Description | Parameters |
|---|---|---|
applyLoan |
Submit loan application | amount, purpose |
makePayment |
Make loan payment | loanId |
getMyLoans |
Get all your loan IDs | - |
getLoanDetails |
Get loan information | loanId |
getRemainingAmount |
Check remaining debt | loanId |
| Function | Description | Parameters |
|---|---|---|
approveLoan |
Approve pending loan | loanId |
rejectLoan |
Reject pending loan | loanId, reason |
disburseLoan |
Transfer funds to borrower | loanId |
depositFunds |
Add funds to contract | - |
withdrawFunds |
Withdraw excess funds | amount |
| Function | Returns | Description |
|---|---|---|
loans(loanId) |
Loan struct |
Get loan data |
borrowerLoans(address, index) |
uint256 |
Get loan ID by index |
getContractBalance() |
uint256 |
Check contract balance |
getTotalLoans() |
uint256 |
Total number of loans |
calculateInterest(amount) |
uint256 |
Calculate 5% interest |
admin() |
address |
Get admin address |
┌─────────┐
│ PENDING │ Student applies
└────┬────┘
│
├──────► [Admin Approves] ──────┐
│ │
└──────► [Admin Rejects] ─────► REJECTED
│
▼
┌──────────┐
│ APPROVED │
└─────┬────┘
│
[Admin Disburses Funds]
│
▼
┌────────┐
│ ACTIVE │
└────┬───┘
│
┌─────────────────┼─────────────────┐
│ │ │
[Full Payment] [Partial Payments] [Past Deadline]
│ │ │
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌───────────┐
│ REPAID │ │ ACTIVE │ │ DEFAULTED │
└────────┘ └────────┘ └───────────┘
- Constructor & Setup (2 tests)
- Apply Loan (4 tests)
- Approve Loan (3 tests)
- Reject Loan (1 test)
- Disburse Loan (2 tests)
- Make Payment (4 tests)
- Check Default (2 tests)
- View Functions (3 tests)
- Admin Functions (4 tests)
Coverage: 100% function coverage
# All tests
forge test
# Specific test
forge test --match-test test_ApplyLoan
# With gas report
forge test --gas-report
# Coverage report
forge coverage| Method | Gas Cost | Savings |
|---|---|---|
require("Error message") |
~2,400 gas | - |
revert CustomError() |
~200 gas | ~2,200 gas (91%) |
Total savings: ~17,600 gas across all error checks
- Deployment Cost: ~6,013,379 gas (~0.48 MNT on Mantle Sepolia)
- Contract Size: ~5.2 KB (well under 24KB limit)
- Input Validation - All user inputs validated against min/max bounds
- Access Control - Admin functions properly protected with modifiers
- Custom Errors - Gas-efficient error handling
- Event Emission - All state changes emit events for transparency
- Fixed Parameters - Interest rate (5%) and duration (365 days) are constants
- Status Checks - Proper status validation in modifiers
Note: This is a testnet project for educational purposes. For mainnet deployment, a professional security audit is recommended.
PRIVATE_KEY=your_private_key_here
RPC_URL=https://rpc.sepolia.mantle.xyz
ETHERSCAN_API_KEY= # OptionalVITE_CONTRACT_ADDRESS=0x0AeC48e885a9ec762A2653cB8866e35f1CEAcc8dNote: The frontend will use the hardcoded default if VITE_CONTRACT_ADDRESS is not set.
- Mantle Network: https://docs.mantle.xyz/
- Foundry Book: https://book.getfoundry.sh/
- Wagmi Docs: https://wagmi.sh/
- RainbowKit: https://www.rainbowkit.com/
- Faucet: https://faucet.sepolia.mantle.xyz/
Contributions welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing) - Open a Pull Request
Azhar Aditya Pratama
Built for HackQuest Indonesia: Co-Learning Camp 6 - Mantle
MIT License - See LICENSE file for details
Questions? Open an issue on GitHub!