Lido intends to provide secure and reliable price feed for stETH for protocols that intend to integrate it. Unfortunately, Chainlik is not available for stETH and Uniswap TWAP is not feasible at the moment: we'd want deep liquidity on stETH/ETH pair for this price, but Uni v2 doesn't allow tight curves for similaraly-priced coins.
stETH has deep liquidity in the Curve pool but it doesn't have a TWAP capability, so that's out, too. In the moment Curve price is flashloanable, if not easily. We decided that in a pinch we can provide a "price anchor" that would attest that "stETH/ETH price on Curve used to be around in recent past" (implemented using the StableSwapStateOracle) and a price feed that could provide a reasonably safe estimation of current stETH/ETH price.
- Current price—current price of stETH on Curve pool. Flashloanable.
- Historical price—the price of stETH on Curve pool that was at least 15 blocks ago. May be older than 15 blocks: in that case, the pool price that was 15 blocks ago differs from the "historical price" by no more than
- Safe price range—the range from
historical price - N%to
min(historical price + N%, 1).
- Safe price—the price that's within the safe price range.
N is configured by the price feed admin; we're planning to initially set it to
The feed is used to fetch stETH/ETH pair price in a safe manner. By "safe" we mean that the price should be expensive to significantly manipulate in any direction, e.g. using flash loans or sandwich attacks.
The feed interfaces with two contracts:
The pool is used as the main price source, and the oracle provides time-shifted price from the same pool used to establish a safe price range.
The price is defined as the amount of ETH wei needed to buy 1 stETH. For example, a price equal to
10**18 would mean that stETH is pegged 1:1 to ETH.
The safe price is defined as the one that satisfies all of the following conditions:
- The absolute value of percentage difference between the safe price and the time-shifted price fetched from the Merkle oracle is at most
- The safe price is at most
10**18, meaning that stETH cannot be more expensive than ETH.
Price feed can give incorrect data in, as far as we can tell, three situations:
- stETH/ETH price moving suddenly and very quickly. There is at least 15 blocks delay between price drop and offchain oracle feed providers submitting a new historical price, and likely more bc tx are not mined instanteously. That should not happen normally: while stETH/ETH is volatile, it's not 5%-in-four-minutes volatile.
- oracle feed going stale because feed providers go offline. This is mitigated by the fact it's operated by several very experienced professionals (all of which, e.g., are Chainlink operators too) - and we only need one operational provider to maintain the feed. The only realistic scenario where this feed goes offline is deprecating the oracle alltogether.
- Multi-block flashloan attack. An block producer who is able to reliably get 2 blocks in a row can treat two blocks as an atomic transaction, leading to what is essentially a multiblock flashloan attack to manipulate price. That can lead to a short period of time (a few blocks) where stETH/ETH price feed is artificially manipulated. This attack is not mitigated, but in our opinion, not very realistic. It's very hard to pull off.
Returns the cached safe price and its timestamp.
Returns the current pool price and whether the price is safe.
Sets the cached safe price to the
max(current pool price, 1) given that the latter is safe.
Returns the cached safe price and its timestamp. Calls
update_safe_price() prior to that if
the cached safe price is older than
|Amount of seconds last value of safe price considered to be valid|