A peer-to-pool micro-lending DApp built on Solana blockchain. Borrowers connect their Phantom wallet, get a risk score based on on-chain activity, lock SOL as collateral, and receive a SOL loan — all governed by an Anchor smart contract with no intermediaries.
- Phase 1 is complete: the Anchor loan protocol is implemented and tested on localnet.
- Phase 2 is complete: the backend risk scoring and loan routes are live.
- Phase 3 is complete: the landing, borrow, and dashboard flows are in place.
- Phase 4 has started: the lender page and pool helpers exist, but the backend pool stats/activity routes are still pending.
- Phase 5 has not started: Devnet deployment, pool initialization, and final polish are still outstanding.
Quick env setup: copy .env.example → .env.local at the repo root, and backend/.env.example → backend/.env.
Known limitation: loan repay in the dashboard uses PDA metadata cached in browser localStorage after a borrow. Repay from the same browser where you created the loan; clearing site data removes that cache.
CI (optional): GitHub Actions workflow template lives in docs/github-ci.yml.example. Copy it to .github/workflows/ci.yml. If git push is rejected for “workflow scope”, run gh auth refresh -s workflow and push again (or add the workflow in the GitHub UI).
- Project Overview
- System Architecture
- Tech Stack
- Repository Structure
- Smart Contract Design
- Risk Scoring Algorithm
- Loan Lifecycle
- Prerequisites
- Installation & Setup
- Environment Variables
- Running the Project
- Deployment (Devnet)
- API Reference
- Roadmap
- Contributing
| Property | Details |
|---|---|
| Identity | Phantom wallet public key — no username/password |
| Loan logic | On-chain Anchor program — no bank or middleman |
| Collateral custody | Program Derived Address (PDA) — no one holds your funds |
| Repayment | On-chain transaction — auto-releases collateral |
| Liquidation | Triggered by smart contract — no human intervention |
| Loan records | Stored on Solana ledger — immutable and public |
The backend API exists only to run the risk scoring calculation and relay signed transactions. It holds no funds and cannot move anyone's SOL — only the Anchor program can do that, and only when the borrower signs.
SolLend solves micro-lending for users who have on-chain history but no traditional credit score. The protocol:
- Reads a borrower's wallet history (age, volume, balance, repayment record) to compute a risk score (0–100)
- Sets loan terms (interest rate, LTV ratio, duration) based on that score
- Locks SOL collateral in a smart-contract vault (PDA)
- Disburses the loan in SOL directly to the borrower's wallet
- Automatically releases collateral on full repayment, or liquidates on default
The MVP targets Solana Devnet for the hackathon and is designed to go Mainnet-ready with minimal changes.
┌─────────────────────────────────────────────────────┐
│ Frontend (React / Next.js) │
│ Loan form · Dashboard · Repayment · Wallet UI │
└────────────┬───────────────────────┬────────────────┘
│ wallet-adapter │ HTTP
▼ ▼
┌────────────────────┐ ┌───────────────────────────┐
│ Phantom Wallet │ │ Backend API (Node.js) │
│ Sign transactions │ │ Loan orchestration │
│ Read public key │ │ Risk score calculation │
│ Read SOL balance │ │ Loan state management │
└────────┬───────────┘ └──────────┬────────────────┘
│ signed tx │ approved terms
▼ ▼
┌─────────────────────────────────────────────────────┐
│ Anchor Smart Contract (Rust) │
│ • create_loan • repay_loan • liquidate_loan │
│ • deposit_pool • withdraw_pool │
└──────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Liquidity Pool PDA (Program Account) │
│ Lenders deposit SOL → earns interest │
│ Pool funds all approved loans │
└──────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Solana Blockchain (Devnet / Mainnet) │
│ ~400ms finality · ~$0.00025/tx · Immutable logs │
└─────────────────────────────────────────────────────┘
Frontend — User-facing React app. Connects Phantom wallet, submits loan requests, shows loan status and repayment schedule. Uses @solana/wallet-adapter-react to read wallet state and sign transactions.
Phantom Wallet — The borrower's identity and signing authority. No private keys ever leave the browser. The wallet adapter reads the public key and SOL balance, and prompts the user to sign each transaction.
Backend API — Stateless Node.js service. Accepts a wallet address, fetches on-chain history via Solana RPC, computes the risk score, and returns approved loan terms. Also stores loan metadata in a database (for the dashboard UI).
Risk Scoring Engine — A module inside the backend. Pulls wallet data from Solana RPC and scores the address 0–100. Score maps to loan LTV and interest rate.
Anchor Smart Contract — The heart of the protocol. Written in Rust using the Anchor framework. Handles all fund movements — locking collateral, disbursing loans, receiving repayments, liquidating defaults. Nobody can move funds without a valid signed instruction from the relevant wallet.
Liquidity Pool PDA — A Program Derived Address that holds the pool's SOL. Owned and controlled exclusively by the Anchor program. Neither the deployer nor any admin can drain it — only the program logic can.
| Package | Version | Purpose |
|---|---|---|
next |
14.x | React framework, SSR |
react |
18.x | UI library |
@solana/wallet-adapter-react |
^0.15 | Phantom wallet connection |
@solana/wallet-adapter-phantom |
^0.9 | Phantom adapter |
@solana/wallet-adapter-react-ui |
^0.9 | Connect button UI |
@solana/web3.js |
^1.87 | Solana RPC client |
@project-serum/anchor |
^0.29 | Interact with Anchor program |
tailwindcss |
^3.x | Styling |
axios |
^1.x | HTTP calls to backend |
| Package | Version | Purpose |
|---|---|---|
express |
^4.x | HTTP server |
@solana/web3.js |
^1.87 | Read on-chain data (RPC) |
@project-serum/anchor |
^0.29 | Program interaction |
prisma |
^5.x | ORM for loan metadata DB |
dotenv |
^16.x | Environment config |
cors |
^2.x | Cross-origin requests |
zod |
^3.x | Request validation |
| Package | Version | Purpose |
|---|---|---|
anchor-lang |
0.29.0 | Anchor framework (Rust) |
solana-program |
1.18 | Core Solana types |
spl-token |
4.x | SPL token support (future) |
| Tool | Purpose |
|---|---|
solana-cli |
Deploy programs, manage keypairs |
anchor-cli |
Build, test, deploy Anchor programs |
Rust (stable) |
Smart contract language |
Node.js 18+ |
Frontend + backend runtime |
PostgreSQL |
Loan metadata storage |
Docker |
Local DB setup |
sollend/
├── app/ # Next.js App Router frontend
│ ├── components/
│ ├── generated/vault/
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.tsx
│ └── lend/
│ └── page.tsx
├── lib/ # Shared frontend libs/hooks/wallet context
│ ├── hooks/
│ ├── wallet/
│ ├── lamports.ts
│ └── solana-client.ts
├── backend/ # Node.js API + Prisma
│ ├── src/
│ │ ├── db/prisma.ts
│ │ ├── index.ts
│ │ ├── routes/
│ │ │ ├── loan.ts
│ │ │ └── score.ts
│ │ ├── services/
│ │ │ ├── loanService.ts
│ │ │ └── riskScore.ts
│ ├── prisma/
│ │ └── schema.prisma
│ └── package.json
├── anchor/ # Anchor workspace
│ ├── Anchor.toml
│ ├── Cargo.toml
│ └── programs/vault/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # Program entrypoint + Accounts context structs
│ ├── errors.rs
│ ├── tests.rs
│ ├── instructions/
│ │ ├── mod.rs
│ │ ├── initialize_pool.rs
│ │ ├── deposit_pool.rs
│ │ ├── create_loan.rs
│ │ ├── repay_loan.rs
│ │ ├── liquidate.rs
│ │ └── withdraw_pool.rs
│ └── state/
│ ├── mod.rs
│ ├── loan.rs
│ └── pool.rs
├── codama.json
├── package.json
└── README.md
The compile-safe program currently wires these five entrypoints in anchor/programs/vault/src/lib.rs:
initialize_pooldeposit_poolcreate_loanrepay_loanliquidate_loan
withdraw_pool exists as a module placeholder at anchor/programs/vault/src/instructions/withdraw_pool.rs, but it is not wired as an active on-chain instruction yet.
Creates the global pool PDA and sets initial protocol state.
Borrower submits loan request. Program:
- Verifies collateral ≥ required amount (based on LTV from risk score)
- Transfers collateral SOL from borrower → collateral PDA
- Transfers loan SOL from pool PDA → borrower wallet
- Creates a
LoanAccountstoring: borrower key, amount, collateral, interest rate, due date, status
Borrower sends back principal + interest. Program:
- Verifies repayment amount = principal + accrued interest
- Transfers SOL from borrower → pool PDA
- Releases collateral from collateral PDA → borrower wallet
- Updates
LoanAccountstatus toRepaid - Records repayment on-chain (used by risk engine for future scoring)
Called by a permissioned liquidator (or a crank) when current_timestamp > due_date. Program:
- Verifies loan is past due
- Sells collateral: transfers collateral PDA → pool PDA
- Updates
LoanAccountstatus toLiquidated - Records default on-chain (penalises risk score on next loan request)
Lender deposits SOL into the liquidity pool PDA. Mints pool share tokens (future — for MVP just records deposit amount).
Reserved placeholder for future lender withdrawal flow.
// LoanAccount — one per active loan
pub struct LoanAccount {
pub borrower: Pubkey, // borrower's wallet
pub collateral_amount: u64, // lamports locked
pub loan_amount: u64, // lamports disbursed
pub interest_rate_bps: u16, // basis points (500 = 5%)
pub due_timestamp: i64, // Unix timestamp
pub status: LoanStatus, // Active | Repaid | Liquidated
pub created_at: i64,
pub bump: u8,
}
// PoolAccount — one global pool
pub struct PoolAccount {
pub total_deposited: u64,
pub total_loaned: u64,
pub total_interest_earned: u64,
pub bump: u8,
}The backend fetches the wallet's on-chain data via Solana RPC and computes a score from 0–100.
Final Score = Σ (factor_score × weight)
| Factor | Max Points | How it's measured |
|---|---|---|
| Wallet age | 20 | Days since first transaction (capped at 365 days = 20 pts) |
| SOL balance | 20 | Balance relative to loan amount (≥ 3× = full 20 pts) |
| Transaction volume | 20 | Total tx count (500+ tx = 20 pts, logarithmic scale) |
| Repayment history | 30 | Previous SolLend loans repaid on time (30 pts each, capped) |
| DeFi activity | 10 | Interactions with known Solana DeFi programs (Raydium, Marinade etc.) |
| Score Range | LTV | Interest Rate (APR) | Max Duration |
|---|---|---|---|
| 80–100 | 80% | 5% | 90 days |
| 60–79 | 65% | 10% | 60 days |
| 40–59 | 50% | 18% | 30 days |
| 20–39 | 35% | 28% | 14 days |
| 0–19 | Rejected | — | — |
LTV example: Score = 75. LTV = 65%. Borrower wants a 1 SOL loan → must lock 1/0.65 = 1.54 SOL as collateral.
1. CONNECT Borrower connects Phantom wallet
↓
2. SCORE Backend reads wallet history → computes risk score (0–100)
↓
3. TERMS Loan amount, rate, and duration shown to borrower
↓
4. APPROVE Borrower accepts terms, signs collateral lock transaction
↓
5. LOCK Smart contract moves collateral to PDA vault
↓
6. DISBURSE Smart contract sends SOL loan to borrower's wallet
↓
7a. REPAY Borrower sends principal + interest → collateral released ✓
OR
7b. DEFAULT Past due date → liquidator triggers liquidation → collateral → pool ✗
Install these before starting:
# 1. Rust (stable)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup component add rustfmt
# 2. Solana CLI
sh -c "$(curl -sSfL https://release.solana.com/v1.18.12/install)"
solana --version # should print 1.18.x
# 3. Anchor CLI
cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install 0.29.0
avm use 0.29.0
anchor --version # should print 0.29.0
# 4. Node.js 18+
node --version # should print v18.x or v20.x
# 5. Docker (for local PostgreSQL)
docker --versiongit clone https://github.com/your-org/sollend.git
cd sollend# From the project root (Anchor workspace)
anchor buildcd backend
npm installnpm install# From project root
docker run --name sollend-db \
-e POSTGRES_USER=sollend \
-e POSTGRES_PASSWORD=sollend \
-e POSTGRES_DB=sollend \
-p 5432:5432 -d postgres:15
# Run migrations
cd backend
npx prisma migrate dev --name initsolana config set --url devnet
solana-keygen new --outfile ~/.config/solana/devnet.json
solana config set --keypair ~/.config/solana/devnet.json
# Airdrop free devnet SOL
solana airdrop 5
solana balanceUse the checked-in templates so you do not have to copy blocks manually:
| Location | Template | Create |
|---|---|---|
| Frontend (Next.js) | .env.example |
.env.local at repo root |
| Backend (API) | backend/.env.example |
backend/.env |
Values below mirror those files; adjust for your deploy.
# Solana
SOLANA_RPC_URL=https://api.devnet.solana.com
PROGRAM_ID=<your_deployed_program_id>
DEPLOYER_KEYPAIR_PATH=~/.config/solana/devnet.json
# Database
DATABASE_URL=postgresql://sollend:sollend@localhost:5432/sollend
# Server
PORT=4000
FRONTEND_URL=http://localhost:3000
# Optional: Helius RPC for better rate limits (recommended)
HELIUS_API_KEY=your_helius_key_hereNEXT_PUBLIC_SOLANA_NETWORK=devnet
NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
NEXT_PUBLIC_PROGRAM_ID=<your_deployed_program_id>
NEXT_PUBLIC_API_URL=http://localhost:4000PROGRAM_ID=placeholder_will_update_after_deploy
POOL_PDA=to_be_initialized_on_devnet# From project root
anchor deploy --provider.cluster devnet
# Note the Program ID printed — add it to both .env files
# Example: Program Id: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsUanchor run init-pool --provider.cluster devnetcd backend
npm run dev
# Running on http://localhost:4000npm run dev
# Running on http://localhost:3000Navigate to http://localhost:3000. Click Connect Wallet, select Phantom, and make sure Phantom is set to Devnet network.
To get test SOL into your Phantom wallet:
solana airdrop 2 <your_phantom_wallet_address> --url devnetanchor build
anchor deploy --provider.cluster devnet# Set environment variables in your hosting dashboard
# Then deploy via Git push or CLInpx vercel --prod
# Set NEXT_PUBLIC_* env vars in Vercel dashboardThe public demo URL is not published yet.
- Vercel URL: placeholder
- Backend URL: http://localhost:4000 while developing locally
- Install the Phantom browser extension and set the wallet to Devnet.
- Get free devnet SOL from https://faucet.solana.com.
- Visit the app, connect your wallet, and request a loan.
Returns the risk score and recommended loan terms for a wallet.
Response:
{
"walletAddress": "7xKXtg...",
"score": 72,
"breakdown": {
"walletAge": 18,
"solBalance": 14,
"txVolume": 15,
"repaymentHistory": 20,
"defiActivity": 5
},
"terms": {
"ltv": 0.65,
"interestRateBps": 1000,
"maxDurationDays": 60
}
}Creates a loan request record and returns the transaction for the frontend to sign.
Body:
{
"walletAddress": "7xKXtg...",
"loanAmountLamports": 1000000000,
"durationDays": 30
}Response:
{
"loanId": "uuid",
"collateralRequired": 1538461538,
"interestLamports": 13698630,
"dueTimestamp": 1735689600,
"transactionBase64": "<serialised transaction for Phantom to sign>"
}Records a repayment intent and returns the repayment transaction.
Returns all active and historical loans for a wallet.
- Phantom wallet connect
- On-chain risk score from wallet history
- SOL collateral → SOL loan flow
- Smart contract: create, repay, liquidate
- Lender deposit pool
- Backend pool stats/activity routes
- USDC as loan currency (SPL token support)
- Pyth Network price feeds for real-time SOL/USD LTV monitoring
- Auto-liquidation crank (off-chain keeper service)
- Lender yield dashboard with APY display
- Under-collateralised loans via on-chain reputation score
- Credit delegation (high-score user backs another wallet)
- eSewa / mobile money repayment gateway (Nepal market)
- DAO governance for interest rate parameters
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Write tests for any new smart contract instructions
- Run
anchor test— all tests must pass - Open a pull request with a clear description
MIT License — see LICENSE
Built for Frontier Hackathon · Solana Devnet MVP · Open to Mainnet deployment