The problem
Around 1.4 billion adults have a phone but no credit history, so no bank will lend to them. NexusFi scores people on their on-chain history instead, and keeps the sensitive parts of that decision off-chain.
How it works
Three layers on three networks:
- Chainlink CRE workflows (TypeScript compiled to WASM, run by a Chainlink DON): proof of reserves, credit scoring, risk monitoring, and a confidential eligibility check.
- Solidity on Sepolia: four attestation contracts that only accept writes from the CRE forwarder.
- Soroban on Stellar testnet: the nUSD and nBRL stablecoins, a score registry, and the credit line itself.
user wallet ─► CRE WF2: every node fetches tx history ─► median consensus ─► score
─► CreditScoreAttestation.sol (Sepolia, onlyForwarder)
─► backend reads score ─► credit_line.open_credit_line() on Soroban
CRE WF1 every 30s: reserves ÷ supply ─► ReserveAttestation.sol ─► alert below 0.95Design decisions
- Consensus before anything is written. Each CRE node fetches its own data; the workflow takes the median (or requires identical answers for eligibility), so one bad node can't move a score.
- No personal data on-chain. The eligibility check keeps the API key in CRE secrets and
publishes only
keccak256(userId)and a boolean. - Limits come from tiers. The credit line maps a score to a fixed limit and rate, so the contract never does open-ended math on an external number.
What could go wrong
| Vector | Status |
|---|---|
| Forged attestations | Mitigated: onlyForwarder on every Sepolia write |
| One oracle node lying | Mitigated: median or identical aggregation across the DON |
| PII leakage | Mitigated: hashed user id plus boolean only |
| Unauthorised credit use | Mitigated: require_auth() on use_credit and repay |
| API trusted a client header (High) | Fixed (Oct 2026). The backend took the user's address from an x-stellar-address header and moved funds with the admin key, so anyone could send or burn another user's tokens. Now transfers, burns and credit use are signed by the user's passkey and only relayed by a fee payer with no contract role |
| Forged MoonPay webhooks | Fixed (Oct 2026): the signature check was skipped when the header was missing; it is now required, with a timestamp window against replays |
| Trusted bridge between chains (Medium) | Open. The Soroban credit line trusts the admin key to submit the score; it doesn't verify the Sepolia attestation. Fix: relay the attestation with a signature the Soroban contract checks |
| Gas growth in attestation history | Open: history shifts the whole array on each write; replace with a ring-buffer index |
Where it stands
A hackathon project, back online on Vercel. Moving it off Railway meant dropping the Express server's in-memory sessions, and the review on the way turned up the header-trust bug above. The Soroban contracts are the original testnet deployment; the Chainlink CRE workflows run as simulations, and the bridge's non-Stellar side is simulated.
