Aggregating on-chain lending: making fragmented credit comparable

Why DeFi lending data isn't comparable across protocols - and how the 1delta API turns 3,000+ disjoint markets into a single, queryable surface.


If you've ever tried to answer a question as simple as "what's the best USDC supply rate right now?" across DeFi lending protocols, you already know the punchline: the question has no clean answer. Not because the rates aren't on-chain - they are - but because every protocol exposes them differently. Different ABIs. Different math. Different units. Different definitions of risk. Different opinions of what a "market" even is.

A single chain (Ethereum) lists 533 lending markets in our index today, spread across Aave V3, Spark, Compound V3, Morpho Blue, Euler V2, Fluid, Silo V2, and a long tail of smaller deployments. Each one is its own integration. None of them speak the same language.

This article is about why that fragmentation exists, why "just compare the APYs" doesn't work, and how the 1delta API turns those 3,000+ disjoint markets into a single, queryable surface.

Without an aggregator

Every lender on-chain ships its own schema. The fields are named differently, defined differently, and unit-encoded differently. There's no middle layer, so your application either supports one protocol or builds the normalization itself.

Aave V3 ltv liqThreshold healthFactor eModeCategory borrowCap Compound V3 collateralFact. borrowCollat. liquidateFact. baseTrackApr netCashUsd Morpho Blue lltv (WAD) liqIncentive marketParams borrowShares irmPerSecond Spark ltv liqThreshold dsrRate netAprDsr sDaiBalance Euler V2 borrowLTV liquidationLTV vaultGovernor irmKind unitOfAccount Fluid collateralFact. withdrawalGap borrowMaxAvail dexFee positionT1T2 Your team writes the normalization layer unit conversion · semantic mapping · per-protocol risk math · health-factor reconciliation …re-implemented every time a new lender ships Your App

The chaos has three flavours, all of which the chart hints at:

1. Fragmentation

"DeFi lending" is no longer a single product category with a few implementations. It's six or seven different products that happen to share the words deposit, borrow, collateral. Aave V3 is a multi-asset cross-margin pool. Morpho Blue is a registry of isolated (collateral, debt) markets. Spark is a CDP attached to a savings rate. Euler V2 is a vault factory. Fluid splits collateral and debt into separate ledgers. Integrating one is roughly the cost of integrating any other - and the long tail keeps growing.

2. Non-unified data: cross-margin vs. isolated markets

The deepest fork is between cross-margin and isolated market designs.

Cross-margin - one shared pool Aave · Compound · Spark WETH USDC wstETH USDT One health factor across every asset Isolated markets - one pair each Morpho Blue · Lista · Fluid · Silo V2 wstETH ↓ USDC cbBTC ↓ USDC WBTC ↓ USDC PT-sUSDe ↓ DAI rETH ↓ WETH weETH ↓ USDC sUSDe ↓ USDT ↓ … A separate health factor per market

In a cross-margin protocol (Aave V3, Compound V2/V3, Spark, Fluid), every asset you deposit contributes to one shared collateral pot, and every asset you borrow draws from it. There's a single health factor and a single liquidation event. The market is the protocol.

In an isolated protocol (Morpho Blue, Euler V2 vaults, Silo V2), each (collateral, debt) pair is its own market with its own oracle, its own interest-rate model, its own LLTV, its own liquidation. The market is the pair. On Ethereum we currently index dozens of distinct Morpho Blue markets - Morpho wstETH-USDT, Morpho cbBTC-USDC, Morpho WBTC-USDC, each $200M–$700M TVL on its own.

These two designs aren't returning the same data because they aren't modelling the same thing. A "supply rate for USDC on Morpho" isn't well-defined - there are dozens of Morpho USDC markets, each with a different counter-asset and risk profile. Aave's "supply rate for USDC" is one number per network.

3. Comparability: same word, different math

Even where the protocols do model the same concept, they don't agree on how to name or compute it.

ConceptAaveCompoundMorpho Blue
Max borrow against a unit of collateralLTVCollateral FactorLLTV
Level at which liquidation can fireLiquidation Threshold (separate from LTV)same as Collateral Factorsame as LLTV
Penalty paid by the borrower at liquidationLiquidation Bonus (paid to liquidator)Liquidation IncentiveLiquidation Incentive Factor (different formula)
Per-asset cap on supply / borrowsupplyCap / borrowCap (token units)supplyCap onlyper-market, implicit in IRM
Interest accrual unitpercent APY per secondper-block rateper-second WAD rate
What "net" yield means after rewards & costsnet APR including incentivesbase + tracking APRbase only (rewards via curators)

An integrator who normalizes only the names (e.g. relabels Aave's liquidationThreshold as collateralFactor) ships a footgun: Aave's LTV and Liquidation Threshold are different numbers, and pretending they're the same as Compound's collateral factor will mis-estimate how close a position is to liquidation by hundreds of basis points.

This is what we mean by non-unified data. The fields don't just have different names; they have different definitions.

With the 1delta API

We wrote one response shape - LendingMarket - and an opinionated mapping from every supported protocol into it. Every numeric field is in the same unit, computed the same way, regardless of where the market came from.

Aave V3 Compound V3 Morpho Blue Spark Euler V2 Fluid 1delta UNIFIED · API LendingMarket - one schema, every market config.collateralFactor - max borrow as fraction of collateral · 0..1 config.liquidationThreshold - fraction at which liquidation fires · 0..1 config.liquidationIncentive - bonus paid to the liquidator · 0..1 depositRate · variableBorrowRate - APR · percent utilization · totalLiquidityUsd · risk.score - uniform units, uniform scale Your App

Three things this gives you that the raw protocols don't:

  • A universal handle. Every market is keyed by (chainId, lenderKey, underlying). An Aave V3 cross-margin entry and a Morpho Blue isolated market live in the same address space, even though one is a 12-asset shared pool and the other is a single isolated pair.
  • Unit-normalized numbers. APR is always in percent. Utilization is always in [0, 1]. USD values are priced through one shared oracle, so two pools aren't being compared at two different USDC prices. Per-block rates, RAY, WAD, per-second WAD - converted once, on the server.
  • A unified risk surface. Aave's split (LTV, Liquidation Threshold), Compound's (Collateral Factor, Liquidation Factor), and Morpho's single LLTV collapse into the same (config.collateralFactor, config.liquidationThreshold, config.liquidationIncentive) triple, with the protocol-specific quirks normalized out. A health factor is therefore comparable across protocols.

What this looks like in practice

The lending data surface lives under /v1/data/lending/. Three endpoints carry most of it:

A lender enumeration endpoint returns every (chainId, lenderKey) pair we index on the chains you ask for, sorted by TVL. Ethereum returns 533 entries today - Aave V3 at the top with $11.2B, Spark at $3.0B, then a long tail of Morpho Blue isolated markets, Compound V3 deployments, Euler V2 vaults, and so on.

A latest-data endpoint takes a page of those lender keys and returns one flat array of unified market items. A WETH market in Aave V3 and a WETH market in Compound V3 come back as two entries with the same field names, the same units, and the same risk-score scale - even though under the hood one is IPool.getReserveData and the other is Comet.getAssetInfo.

A pools endpoint pre-filters and pre-sorts across all of them in one call. "USDC supply opportunities above $1M TVL, paying more than 4% APR, with risk score 3 or better" is one request - not seven SDKs and a normalization layer you write yourself.

Why this matters beyond data

Unified data unlocks unified actions. Once a position on Aave and a position on Morpho Blue look the same to your code, you can do things that previously required custom integration per pair:

  • Optimize across protocols. Find the cheapest borrow and best supply for a target leverage across every lender we index - not just within one.
  • Migrate atomically. A withdraw from Aave → deposit into Compound → borrow on Compound → repay Aave sequence becomes a single signed transaction through the Composer.
  • Compare apples to apples in your UI. Health factors, liquidation prices, and net APRs are computed off the same fields, so a "Position" row in your front-end doesn't lie about how risky a Morpho position is relative to an Aave one.

The fragmentation isn't going away - every new credit primitive in DeFi makes the surface area bigger, not smaller. But it doesn't have to be your integration team's problem.

Where to go next

  • API reference - endpoints, response shapes, and x-api-key setup.
  • Docs site - narrative guides, supported protocols, network coverage.
  • The rest of this series digs into looping, atomic migrations, and the Composer contract that makes the cross-protocol actions atomic.
← All posts