Inspiration
Over 63 million MSMEs in India alone have more than ₹10 Lakh Crore locked in unpaid B2B invoices at any given time. When a small supplier delivers goods to a corporate giant, they're forced into Net-90 day payment terms — and traditional banks reject 80% of their loan applications. Legacy factoring companies charge predatory 15%+ fees with mountains of paperwork.
I asked myself: what if an MSME could tokenise a verified invoice and receive funding from a global investor in under 60 seconds — with no bank, no middleman, and no paperwork?
That's FactorFi.
What It Does
FactorFi is a decentralised invoice factoring protocol that bridges Web2 financial truth with Web3 liquidity:
- Sellers verify a real Stripe invoice and mint it as an ERC-721 NFT on Ethereum
- Investors browse a marketplace of verified invoices and fund them at a seller-controlled discount in USDC
- Buyers repay the face value into a trustless smart contract escrow
- Investors claim their yield after repayment — all on-chain, all verifiable
The protocol charges a transparent 1% fee (hardcoded in the smart contract), and the seller controls their own discount rate via a slider (2–15%).
How I Built It
Frontend: React 19 + TypeScript + Tailwind CSS v4 + Ethers.js v6, deployed on Vercel.
Backend: A Node.js/Express proxy server (deployed on Railway) that handles all sensitive API calls server-side. The frontend never touches a single secret key.
Smart Contract: InvoiceEscrow.sol — built with Solidity ^0.8.20,
OpenZeppelin (ERC721URIStorage, SafeERC20, ReentrancyGuard),
deployed on Polygon Amoy Testnet.
Integrations:
- Stripe API — Server-side invoice verification (checks existence, ownership, and open status)
- Pinata — IPFS pinning for immutable NFT metadata
- Resend — Automated email notifications to buyers when their invoice is funded
Challenges I Ran Into
The Oracle Problem: How do you verify that a real-world invoice exists without trusting the seller? I solved this for V1 by building a direct Stripe API bridge — the backend verifies the invoice is real and unpaid before allowing tokenisation.
Double-Financing Prevention: A malicious seller could try to mint the
same invoice twice. I solved this by hashing the Web2 invoice ID on-chain
in a processedInvoices mapping — the smart contract mathematically
prevents duplicate minting.
RPC Performance: Naively fetching invoices with unbounded loops took
15+ seconds. I implemented a getNextTokenId() getter in the contract
and used Promise.all() on the frontend, dropping load times to
under 1 second.
Security without Complexity: I needed to keep Stripe/Pinata keys hidden from the client without overengineering the architecture. The Express proxy pattern gave me bank-grade key isolation with minimal overhead.
Accomplishments That I'm Proud Of
- End-to-end working protocol — Not a mockup. Every transaction (mint, fund, repay, claim) executes on a live Ethereum testnet smart contract.
- Trustless two-step escrow — Buyers repay into the contract, investors claim separately. No party has to trust the other.
- Zero secret key exposure — The entire Stripe/Pinata/Resend integration runs through a secured backend proxy. The React frontend never sees a single API key.
- Sub-second marketplace loading — Bounded RPC fetching with
Promise.all()brought page load from 15s down to under 1s. - Anti-fraud by default — Double-financing is impossible at the smart contract level, not just the UI level.
What I Learned
- How to architect a full-stack Web3 application with proper separation of concerns
- The real-world challenges of RWA (Real World Asset) tokenisation — especially around fraud prevention and oracle design
- How to build a trustless two-step escrow mechanism (repay → claim) that eliminates counterparty risk
- The importance of bounded on-chain data fetching for frontend performance
What's Next for FactorFi
| Milestone | Description |
|---|---|
| Chainlink Oracle | Automate repayment by listening to bank webhooks |
| GSTN API | Verify invoices against India's government tax portal for zero-fraud origination |
| The Graph | Replace RPC loops with indexed GraphQL queries |
| UUPS Proxy | Make the contract upgradeable for production deployment |
Built With
- ethereum
- ethers.js
- express.js
- ipfs
- node.js
- openzeppelin
- pinata
- railway
- react
- resend
- solidity
- stripe
- tailwind-css
- typescript
- vercel
- vite
Log in or sign up for Devpost to join the conversation.