Matching Engine
The RevvFi matching engine (RevvFiOfferBook.getBestOffers()) fills a borrow request from the lowest-APR active offers available in that market — there’s no pooled rate, and matching is entirely order-book driven.
The Matching Algorithm
When a borrower calls borrow(amount, useSeniorOnly, maxApr), the OfferBook executes:
1. Offer Selection
The engine scans all active offers where:
active == trueremainingAmount > 0expiry > block.timestamp- If
useSeniorOnly == true: only offers withseniority == 0(senior) are considered
2. Sorting
Offers are bucketed by APR internally (aprBuckets), and buckets are sorted lowest APR first. Within a bucket, offers are filled in insertion order. Seniority is not an automatic priority tier in the fill order — it’s purely a filter the borrower can opt into via useSeniorOnly. A senior offer at a higher APR does not get filled before a junior offer at a lower APR.
3. Execution
- The OfferBook (
executeDrawdown) walks buckets lowest-APR-first, taking as much as needed from each offer (supporting partial fills) untilamountis satisfied, and returns the filled offers plus their weighted-average APR. - The Market checks that weighted-average APR against the borrower’s
maxAprceiling — if it’s exceeded (or exceeds the protocol-wideMAX_APR_BPScap), the entire borrow reverts, not just the offers above the ceiling. - On success, the requested principal is transferred to the borrower.
- One
RevvFiPositionNFTis minted per lender whose offer was (partially or fully) filled, each carrying that lender’s own APR — the weighted average is only used for themaxAprcheck and theBorrow/DrawdownExecutedevents; it is never stored as what any individual position accrues at.
Seniority Tiers
- Senior (
seniority == 0): Paid back first during liquidation loss distribution. Borrowers can restrict a draw to senior-only liquidity viauseSeniorOnly. - Junior (
seniority == 1): Absorbs losses first during liquidation (first-loss position), in exchange for lenders being free to quote whatever APR they want without a senior-only restriction working against them.
Seniority affects loss distribution during liquidation, not the order offers are matched in during a normal borrow.
Matching Example
Borrower Request: 10,000 USDC, useSeniorOnly = false, maxApr = 800 (8%)
Available Offers:
- Offer A: 5,000 USDC @ 5% APR (Senior)
- Offer B: 3,000 USDC @ 6% APR (Junior)
- Offer C: 5,000 USDC @ 10% APR (Senior)
Result:
- Engine fills 100% of Offer A (5,000 @ 5%), 100% of Offer B (3,000 @ 6%), and 2,000 of Offer C (@ 10%) to reach the full 10,000 requested — lowest APR first, regardless of seniority.
- Weighted-average APR of the fill:
(5,000×5% + 3,000×6% + 2,000×10%) / 10,000 = 6.3%. 6.3% ≤ maxApr (8%), so the borrow succeeds. Three position NFTs are minted, accruing at 5%, 6%, and 10% respectively — each lender earns exactly what they quoted, independently of the others and of the 6.3% figure (which only exists for themaxAprcheck and the emitted event).- Had the borrower instead set
maxApr = 550(5.5%), this exact same fill would revert in full — even though Offers A and B alone (8,000 of the 10,000 needed) are individually well under 5.5% — because completing the requested 10,000 requires touching Offer C, and the resulting blended rate (6.3%) exceeds the ceiling.