Back to projects
Senior Design II capstone · Oct – Dec 2025

A credit score is only worth something if someone else can check it.

A risk engine for thin-file borrowers in emerging markets: alternative mobile-money data, a stacking ensemble, SHAP attributions for every decision, and a Node.js oracle that commits the result on-chain.

XGBoostSMOTEOptunaSHAPSolidityEthereumIPFSNode.js
0.984
AUC — stacking ensemble on the held-out set
0.948
F1 score — ensemble, after SMOTE balancing
>99.9%
IPFS retrieval rate via sequential gateway fallback
ICIICE '26
Presented and published on IEEE Xplore
The problem

Thin-file borrowers are invisible to conventional credit scoring — and invisible decisions can't be audited.

Millions of people in emerging markets have years of mobile financial services history and no formal credit file. A model can read that alternative data. The harder question is what stops the lender changing the answer afterwards.

  • Severe class imbalance. Defaults are rare, so a naive model can score 95% accuracy by predicting 'no default' every time and be completely useless.
  • Off-chain inference has no witness. A prediction produced in a notebook is trivially editable after the fact — nothing records what the model actually said, or when.
  • Regulators don't accept black boxes. An automated decision that affects credit access has to come with a reason, per applicant, not just a global feature-importance chart.
  • Blockchains are terrible databases. Storing the applicant's data on-chain is both prohibitively expensive and a privacy violation.
The approach

Score off-chain where compute is cheap. Commit the receipt on-chain where tampering is expensive.

The architecture splits into four planes so PII never touches the chain and the chain never has to run a model.

01

SMOTE balancing

Interpolates between minority-class samples to synthesise a balanced training distribution.

02

Stacking ensemble

Level-0: XGBoost (tuned with Optuna) and a 64/32 MLP. Level-1: logistic regression aggregates their probabilities.

03

PDO score transform

Converts probability of default into an industry-standard 300–850 score via points-to-double-the-odds.

04

SHAP + IPFS

Per-applicant Shapley attributions bundled into a JSON payload and pinned to IPFS, returning a content ID.

05

Oracle → Ethereum

A Node.js oracle polls the contract for requests, fetches from IPFS, and writes back the score and CID on-chain.

Results

Model performance, and the engineering decision that came with it.

ModelAUCF1PrecisionTraining time
Stacking ensemble0.9840.9480.95649.2 s
XGBoost (shipped)0.9830.9390.9611.70 s
Random Forest0.9180.8320.8405.90 s

The ensemble wins on AUC by 0.001 and costs 29× the training time. XGBoost went to production — the accuracy difference was not worth the latency for real-time inference.

MODELLING

A 0.001 AUC gain wasn't worth 29× the compute

The ensemble is the better model on paper. XGBoost shipped because it trains in 1.70s with a higher precision score, and precision is what matters when a false approval costs real money.

EXPLAINABILITY

Every score carries its own reasons

SHAP produces local and global attributions per applicant, so the system can state which variables raised or lowered a specific risk profile — not just which features matter on average.

RELIABILITY

The oracle assumes the network will fail

Sequential gateway fallback (ipfs.io → Cloudflare → Pinata) lifts an 88% first-attempt success rate to over 99.9% overall. Request IDs are deduplicated in memory so a retry never double-spends gas.

COST

The contract stores receipts, not data

Emitting optimised events instead of writing strings on-chain keeps a request at ~115,000 gas and a fulfilment at ~145,000 — the design constraint that made the whole thing viable.

From the repo

Model evaluation, tracked in Weights & Biases.

Charts logged during the training runs in the repository. Click to enlarge.

Built with
Pythonscikit-learnXGBoostOptunaSHAPpandasNode.jsExpressSolidityethers.jsIPFSPinata