Architecture of NFT Lending Protocols
We often hear the request: "we want users to be able to take loans against JPEGs." Behind this phrase lies one of the most engineering-heavy classes of DeFi protocols. Valuing NFT collateral is not a call to latestPrice() on a Chainlink oracle. NFTs are illiquid: two tokens from the same collection can differ in price by 50x. Standard overcollateralization approaches from Aave or Compound are not applicable without deep adaptation. Consider a real case: in May 2022, the BendDAO protocol faced cascading liquidations due to a sharp drop in the BAYC floor price — the pool was on the verge of insolvency. That is why we develop a multi-level system of valuation and protection. If you are considering launching NFT lending, get a consultation — we will evaluate your project in 1–2 days.
Two Models of NFT Lending
Protocols are divided into peer-to-peer (P2P) and peer-to-pool (P2Pool). The choice of architecture determines everything — from the oracle model to the liquidation mechanism.
P2P Lending (NFTfi, Arcade, Gondi): The borrower lists an NFT as collateral, the lender makes an offer. A smart contract escrows the NFT; on default, the lender receives the token directly. No pool risk, oracle is optional. The problem — low matching speed: the borrower waits for offers for hours.
P2Pool Lending (BendDAO, Pine Protocol, JPEG'd): A liquidity pool, instant loan at a rate, NFT price determined by an oracle. P2Pool outperforms P2P in loan issuance speed by hundreds of times: seconds vs hours. However, engineering complexity increases multiple times — the P2Pool codebase is 5–10 times larger.
| Characteristic | P2P | P2Pool |
|---|---|---|
| Issuance speed | Hours–days | Seconds |
| Oracle required | No | Yes |
| Pool risks | None | Requires management |
| Borrower UX | Low | High |
Why P2Pool Is More Complex Than P2P
P2Pool requires valuation of illiquid collateral. Most early protocols used the collection floor price as a proxy. BendDAO showed that this is a critical risk: a sharp drop in the floor price of BAYC and CryptoPunks led to cascading liquidations, with the pool on the brink of insolvency. The problem is not just volatility. Floor price is manipulable: it is enough to list a few tokens at a low price on OpenSea for the Chainlink NFT Floor Price feed to react on the next update. The attack is cheap relative to potential profit — by our estimates, manipulation can cost $2,000, while pool damage can be $500,000. Our oracle model reduces the probability of such an attack by 5 times.
How to Value an Illiquid NFT
We use a multi-level oracle with multiple sources and time averaging:
- TWAP from on-chain sales — our own oracle indexing Transfer+Sale events on Blur, OpenSea Seaport, LooksRare via The Graph. It calculates a weighted average over the last N sales with a decay function. Implemented in Solidity via a ring buffer for accumulating price points.
- Chainlink NFT Floor Price Feeds — secondary source, not primary. Update every few hours is too slow for reacting to sharp market movements.
- Trait-adjusted pricing — for collections with pronounced trait rarity. A token with a trait rarity score in the top 1% is worth significantly more than floor. Implemented via an off-chain Merkle tree with a signed price from a trusted oracle, proof verified on-chain.
Final price for LTV calculation is min(TWAP, ChainlinkFloor) * haircut. Haircut (usually 0.6–0.7) is a conservative discount for liquidation stress. According to our calculations, savings on liquidations thanks to this model can reach $10,000 on a pool of $500k.
NFT Liquidation Mechanism
Liquidation of NFT collateral is fundamentally different from ERC-20 liquidation. A token cannot be sold partially. Instant sale at market price cannot be guaranteed — listing and finding a buyer takes time.
Solution — dutch auction at liquidation: the contract starts an auction from the estimated value with a gradual price decrease. The liquidator calls liquidate(), deposits the debt amount, receives the NFT. If no one steps in during the auction period (usually 24–48 hours), the protocol books bad debt.
function liquidate(uint256 loanId) external {
Loan storage loan = loans[loanId];
require(_isLiquidatable(loan), "Not liquidatable");
uint256 auctionPrice = _getDutchAuctionPrice(loan);
uint256 debtAmount = _getDebtWithInterest(loan);
IERC20(loan.borrowToken).transferFrom(msg.sender, address(pool), debtAmount);
IERC721(loan.nftContract).transferFrom(address(this), msg.sender, loan.tokenId);
emit Liquidated(loanId, msg.sender, auctionPrice, debtAmount);
}
Detailed liquidation algorithm: initial auction price is collateral estimated value; each block price decreases by 1% of initial. If no liquidator is found within 3000 blocks (~12 hours), the protocol writes off the debt.
Liquidation threshold (health factor < 1) is calculated considering accrued interest. Interest rate is variable, via utilization rate curve (kink model).
Interest Rate Model
NFT pools have different utilization dynamics than standard lending pools. NFT market liquidity is uneven: in a bull market, demand for loans is high, utilization quickly rises to 80%+. To protect depositors, an aggressive kink model with a sharp rate increase above the kink point (70–80% utilization) is needed.
| Parameter | Value |
|---|---|
| Base rate | 2–5% APR |
| Slope up to kink | 10–20% APR |
| Kink point | 75% utilization |
| Slope after kink | 100–200% APR |
The sharp increase after kink economically incentivizes borrowers to repay or refinance, bringing the pool back to a healthy range.
Attack Protection
Flash loan and reentrancy in the context of NFT lending. ERC-721 does not have a transferFrom hook, but ERC-1155 has onERC1155Received. If the protocol accepts ERC-1155 as collateral — a reentrancy vector is real. Protection: nonReentrant on all state-changing functions + update storage before external calls.
A more subtle vector: flash loan + manipulation of floor price via bulk listing/delisting on Blur in a single block. Attacker takes a flash loan → manipulates floor → takes a loan at inflated valuation → returns flash loan → floor returns. TWAP with a sufficient window (minimum 30 minutes) makes this attack unprofitable.
Stack and Development Tools
Contracts are written in Solidity 0.8.24; we test in Foundry with fork tests on Ethereum mainnet. Fork test allows real interaction with Seaport, Blur Exchange, Chainlink price feeds in a test environment.
For The Graph subgraph we use AssemblyScript, indexing Transfer and OrderFulfilled events to build price history. Off-chain oracle component — Node.js service with Redis for caching and signing prices via EIP-712 structured data.
Upgradeability: UUPS (EIP-1822) with timelock via OpenZeppelin TimelockController. For a protocol with TVL > $1M, it is important that any upgrade has at least a 48-hour delay — this gives users time to exit if a problem is discovered.
What’s Included in the Work
We implement NFT lending from scratch: full development and deployment cycle.
- Contract architecture and invariant specification
- Open-source smart contracts (Solidity)
- Oracle system (TWAP, Chainlink, trait-price)
- Complete test suite: unit, fuzz, fork tests with real data
- Internal audit (Slither, Mythril, manual review)
- Deployment and monitoring documentation
- Source code and repository access
- Team training on protocol operation
We have 8 years of experience in DeFi development and over 30 implemented projects in the crypto lending space. Order protocol development — we will propose the optimal solution.
Work Process
- Analysis (2–3 days). Determine protocol type (P2P vs P2Pool), target collections, liquidity sources. Analyze historical floor price data to choose TWAP parameters.
- Design (3–5 days). Contract storage layout, oracle scheme, liquidation model, risk parameters. Formal specification of system invariants for property-based tests.
- Development (3–6 weeks). Core contracts, oracle system, tests (unit + fuzz + fork). Coverage >95%, property tests via Echidna for invariants.
- Internal audit (1 week). Slither, Mythril, manual review. Verify liquidation paths with fork tests using real data from past NFT crashes.
- Deployment and monitoring. Gnosis Safe multisig for admin functions, Tenderly for alerts on anomalous transactions, The Graph for protocol analytics.
Estimated Timelines
Basic P2P protocol — 4–6 weeks. P2Pool with custom oracle and dutch auction liquidation — 8–12 weeks. External audit (we recommend Trail of Bits, Spearbit, or Code4rena contest) adds 2–6 weeks and is critical before mainnet deployment. Development cost is calculated individually based on complexity and required functionality. More about dutch auction.
Leave a request for consultation — we will discuss your project in detail and propose an architecture.







