For the complete documentation index, see llms.txt. This page is also available as Markdown.

Price Feeds

How the Lending Protocol prices your collateral, and where those prices come from

What a price feed is, and why Borrow needs one

Borrow lets you post assets as collateral and take out a loan against them. To do that, the Lending Protocol has to answer one question continuously: what is your collateral worth in US dollars right now?

That number decides three things:

  • How much you can borrow. Your borrowing power starts with the USD value of your collateral multiplied by each asset's Product LTV, before any stricter account or market limits are applied.

  • Whether you are safe. Your health factor is a comparison of what your collateral is worth against what you owe.

  • When you get liquidated. If your collateral's value falls far enough relative to your debt, a liquidator can repay part of your loan and take some of your collateral.

A smart contract cannot look up a price by itself. It has no internet connection and no view of any exchange. So the price has to be delivered on-chain by an oracle — a service that publishes prices to a contract that anyone can read. The contract we read for a given asset is that asset's price feed.

Every price in the Lending Protocol is reported in US dollars with 8 decimal places, so a raw on-chain reading of 100000000 means $1.00.

Who publishes the prices

We do not invent prices. Every price starts life at an independent third-party publisher, and different assets need different kinds of publisher.

Chainlink is the most widely used oracle network in DeFi and the source for most of our major assets.

It works like this: a network of independent node operators each fetch the price of an asset from many exchanges and data providers. They report their answers on-chain, and the network aggregates them into a single published price. No single operator can move the result, and a price that only exists on one exchange cannot drag the aggregate with it.

Chainlink publishes a new price on either of two triggers:

  • Deviation — the price has moved more than a set percentage since the last publish.

  • Heartbeat — a maximum amount of time has passed with no publish, so the feed proves it is still alive even in a flat market.

Both thresholds are Chainlink's, published per feed on their own documentation, and differ from asset to asset: a heavily-traded major pair updates far more often than a thinly-traded one.

Chainlink also publishes exchange-rate feeds rather than dollar prices, which is what we use for weETH: a feed that reports how much ETH one weETH is worth. We turn that into a dollar price ourselves — see composition below.

Veda accountants

Some collateral is a receipt token from an ether.fi Liquid vault, such as liquidETH or liquidUSD. These do not trade on the open market in any meaningful volume, so an exchange price would be the wrong thing to use even if one existed.

Instead, the price comes from the vault's own Accountant contract, which is the on-chain record of what one vault share is worth in the vault's base asset. Read more in Veda Vault Architecture.

So a liquidETH price is built in two steps: the Accountant says one liquidETH is worth some amount of ETH, and Chainlink says what ETH is worth in dollars.

Midas

The same idea applies to the Midas-issued receipt tokens. Midas publishes a rate for each token through its own on-chain price proxy, which we read the same way. See Midas Vault Architecture. These publish less often than a market feed does, and we allow for that in the staleness bounds below.

The OracleSink (tokenized equities)

For the tokenized equity assets, the authoritative price lives on Ethereum mainnet, not on Optimism where the Lending Protocol runs. A relay reads the mainnet price and delivers it across to an Optimism contract called the OracleSink using LayerZero. Our feed reads the sink and checks how old the underlying mainnet reading was when it was taken, so a slow delivery cannot pass off an old price as a fresh one.

How a price actually gets read

There are three layers between a publisher and a borrowing decision. Each one does exactly one job.

Layer 1 — the ether.fi price feed

We deploy one small feed contract per asset. Its job is to read the publisher and refuse to answer if anything looks wrong. It fails closed: rather than returning a bad number, it reverts, and the transaction that needed the price fails.

It rejects a reading when:

  • The price is zero or negative. A publisher that has broken or is reporting nothing at all should never be treated as "this asset is worth nothing."

  • The price is stale. Every feed has a hard maximum age. If the publisher's last update is older than that, the feed refuses to answer.

  • The source reverts. Any failure propagates instead of being swallowed.

For assets pegged to the US dollar, the feed also snaps the price to exactly $1.00 when it is within a narrow band of it. This stops ordinary market noise around the peg from moving borrowing power around. The band is fixed in the feed contract.

These feed contracts were audited by Paladin.

Staleness bounds

Every feed carries a maximum age, set from how often that particular publisher actually updates. A market feed that publishes many times a day gets a tight bound measured in hours; a vault accountant or a Midas proxy that publishes on a slower cycle gets a correspondingly looser one, because holding it to a market-feed bound would make the asset unusable for no safety gain.

The bound is fixed at deployment and cannot be changed afterwards. The live value for any feed can be read on-chain with rateMaxStaleness() (or maxStaleness(), depending on the feed type).

Composed prices (rate x underlying)

Many of these assets are not quoted in dollars at source. A feed can therefore run in two modes:

  • Direct — the source is already a dollar price. Scale it to 8 decimals and return it.

  • Composed — the source is a rate in some other asset. Multiply it by that asset's dollar price, which is itself another ether.fi feed with its own staleness check.

So liquidETH / USD is the Veda accountant's liquidETH-per-ETH rate multiplied by the ETH / USD feed. If either leg is stale, the whole thing fails. This is why the feed descriptions read like Capped liquidETH / ETH / USD — that is the full chain, right to left.

Layer 2 — the price cap adapter

Most of our collateral is yield-bearing: one liquidETH buys slightly more ETH each day, one weETH buys slightly more ETH each day. That growth is real, but it is also a place an attacker would like to lie. If someone could make an Accountant report that a share is suddenly worth twice as much, they could borrow against that inflated value and never come back.

So on top of the feed we use a standard, battle-tested price cap adapter, unmodified and already in production use on major lending markets elsewhere in DeFi. It enforces a simple rule:

This asset's exchange rate is allowed to grow by at most X% per year, measured from a fixed snapshot taken at a known past date. If the reported rate is above that ceiling, use the ceiling instead.

Normal yield sits comfortably under the ceiling and passes through untouched. A sudden jump — from a bug, a manipulated vault, or a compromised publisher — gets clipped. The attacker cannot borrow against a number the protocol will not accept.

Worth being precise about what this does and does not do:

  • It only caps the price upward. A genuine fall in value passes through immediately and in full, because under-reporting a fall is what creates bad debt.

  • It is a ceiling, not a smoother. Day-to-day price movement is not dampened at all.

  • If real yield ever outran the ceiling for a sustained period, the asset would price slightly below its true value, making it conservative collateral. That is the intended trade.

The yearly growth ceiling is set per asset by risk review, sized above the yield the asset can plausibly earn but far below the jump an attack would need. A conservative, slow-growing asset gets a tight ceiling; a higher-yielding strategy gets a looser one. The live ceiling for any asset can be read from its adapter with getMaxYearlyGrowthRatePercent().

For the dollar-pegged assets the cap is a flat price ceiling slightly above $1.00 rather than a growth rate, on the reasoning that a dollar stable trading meaningfully above its peg is a signal of something wrong rather than of genuine appreciation. Euro-pegged assets are capped the same way, as a small multiple of the EUR/USD price. These are readable with getPriceCap().

Layer 3 — the Lending Protocol oracle

The Lending Protocol's oracle contract is its single point of reference for prices. It maps each listed asset to its price feed and is what the protocol calls when it needs to value a position. It requires every feed to report 8 decimals, and it rejects a price of zero.

Only the Spoke contract can change which feed an asset points to, and that change is held on the configurator's domain-admin role — an Owner Safe multisig transaction, not something an automated risk process can do.

How each asset class is priced

Which publisher an asset uses follows from what kind of asset it is, not from a case-by-case decision. All contracts are on Optimism.

Asset class
Price chain
Publisher

Major crypto assets and dollar stables

direct USD price

Chainlink

Liquid staking receipt tokens

rate × underlying USD

Chainlink rate feed + Chainlink USD feed

Veda vault receipt tokens

vault rate × underlying USD

Veda accountant + Chainlink

Midas receipt tokens

proxy rate × underlying USD

Midas proxy + Chainlink

Tokenized equities

relayed rate × underlying USD

OracleSink relay + Chainlink

Dollar-pegged assets carry a flat price ceiling; yield-bearing receipt tokens carry a yearly growth ceiling; assets that are neither are uncapped, since there is no exchange rate for an attacker to inflate.

The authoritative, current list of what is listed and which feed each asset uses is on-chain, not in this document. Read it from the oracle: getReserveSource(reserveId) gives the feed address for a reserve, and getReservePrice(reserveId) gives the price it currently reports. Each feed's description() names its full composition chain — for example Capped liquidETH / ETH / USD.

Contract addresses

Contract
Address

Oracle

0xe8cbd37210bF1E29436dAe183d7b9fe45E886fA8

Spoke

0xdffcC3536D932eb51Df51a7F5FA407c4270d5308

Hub

0x66753c4e3fC84f1eD0e3C267C927284E9d90C572

For the current collateral factors and liquidation settings per asset, see Lending Market Parameters.

A worked example: weETH

weETH is the clearest illustration, because it uses every layer.

  1. Chainlink publishes the weETH/ETH exchange rate. This is a rate, not a dollar price — it says how much ETH one weETH is worth. It sits a little above 1 and rises slowly as staking rewards accrue.

  2. Chainlink separately publishes ETH/USD.

  3. Our ETH / USD feed reads the ETH/USD aggregator and refuses to answer if it is older than its staleness bound.

  4. Our weETH / ETH rate feed reads the rate aggregator under its own bound.

  5. The cap adapter takes the rate, compares it against a ceiling that grows at a fixed maximum rate from a snapshot taken at a known past date, uses whichever is lower, and multiplies by the ETH price.

  6. The oracle returns the result to the Lending Protocol.

In normal conditions the exchange rate sits below the ceiling with room to spare, and the cap does nothing at all — it is there for the day something goes wrong, not for ordinary operation. You can check this yourself at any time: an adapter's isCapped() reports whether the ceiling is currently binding, and getRatio() against the ceiling implied by getSnapshotRatio(), getSnapshotTimestamp() and getMaxRatioGrowthPerSecond() shows how much headroom is left.

What can go wrong, and what happens then

Situation
What happens

A publisher stops updating

The feed's staleness check fails. Borrowing and liquidations against that asset revert rather than using an old price.

A publisher reports zero or a negative number

Rejected outright; the feed reverts.

A vault rate jumps implausibly high

The cap adapter clips it to the ceiling. No inflated borrowing power.

An asset genuinely falls in price

Passed through in full and immediately. Health factors fall and liquidations become possible — this is the system working as intended.

One leg of a composed price is stale

The whole composed price fails. There is no partial answer.

The consistent theme: when the protocol is not confident about a price, it stops rather than guessing. That can mean a borrow or a liquidation temporarily reverts, which is the deliberate trade — a paused market is recoverable, a market that acted on a wrong price is not.

Further reading

Last updated