[GIP-28] Update stETH oracle


Gearbox v2 uses Chainlink USD oracles. So when you are dealing with ETH debt <> stETH position, the Chainlink price tick is different for both and might cause liquidation even if stETH does not actually depeg relative to ETH. So stETH/ETH can be at 1:1 yet the oracle deviations can lead to HF dropping <1.

That has happened 3 times so far, and while this behavior & risk were communicated and elaborated on - such behavior is still unsatisfying from the user perspective. The goal is to fix that.

This is not an exploit in any way. This does not lead to bad debt. The protocol works as communicated and coded. But, this behavior is not what users “expect” and it can be improved. These unwanted liquidations arising from the mechanics as explained in this proposal, can and should be mitigated.

Churning users in liquidations is not the strategy & not the goal!


Some Credit Accounts that hold decent amount of stETH was liquidate as their Health Factor falls below 1 during big price movements for stETH/USD. Liquidation happens as it’s designed on smart contract while there is very low changes in stEth/Eth price.
Links to the Credit Accounts in questions:

  1. https://etherscan.io/tx/0x2d9244abe624c4edd3ec78a9db739342078463e0bbc5455b1891337a45984ebf
  2. https://etherscan.io/tx/0x8e5e1052cc38df27d336f91f6da9c6fc15578dead8a5f4bd971b0b0771945c87
  3. https://etherscan.io/tx/0xa6705997b63e0d87376cd5fe5c49b4f5bf4ecfdf3b6052fcfb50812bb107434d


As you know, Gearbox uses Chainlink USD oracles as price feeds. At the same time, to calculate the Health Factor, we need the price in an underlying debt asset, so in fact the PriceOracle contract implements the following logic: it divides 2 prices Price_asset/Price_underlying, where Price_asset, Price_underlying - USD price-feeds from Chainlink oracle (this logic works for ordinary trading tokens, for LP tokens the logic is a little more complicated, but the principle is the same).

Due to the fact that Chainlink oracles have deviation (you can see here deviations for different assets), multiplication leads to error accumulation. However, for DAI, USDC pools, this is not a problem, because the prices of DAI / USD and USDC / DAI are very stable and there is practically no volatility here. The problem arises for stETH - due to the fact that stEth/USD and ETH/USD both are volatile, the accumulation of error can be significant even if the price of stEth/Eth has hardly changed. This could lead to liquidation of user’s positions which should not be happened if Chainlink oracle has instant update.

We checked deviations in comparison of using direct stEth/Eth oracles:

Source you can find here or you can check yourself loading Chainlink pricefeeds events from DataStudio or Dune.
Negative deviations (less than -2.5% as 2% is standard devitation of stEth/Eth price oracle) can lead to undesirable liquidations of users’ Credit Accounts, positive deviations more than 3% can be used as potential attack vector.


Update stEth/USD price feed from Chainlink to composite oracle (stEth/ETH) / (ETH/USD), where each value is taken from Chainlink oracle. This solution will allow estimating stEth more correctly.



1 Like

Great writeup, and very elegant solution.

One downside here is that the theoretical price deviation (which chainlink commits to) changes from to 2.5% for stETH vs USD (as stETH/ETH is being updated when price changes by 2%, and ETH/USD at 0.5% price change).
If we add to that additional 2% deviation for assets like FXS, we get 4.5% potential deviation, which is higher then the liquidation incentive, and could in theory block the option to have profitable liquidations).

However, in practice assets like FXS have super low liquidation threshold, and the system could afford waiting a bit until the next oracle update.

Has ChainLink anythoughts on this. wouldn’t it be much easier to just have them use the same timestamp for their price feeds?

no, it’s technically impossible in current Chainlink design

Chainlink has pretty well thought out architecture - price feeds are updated by decentralized oracles network. There are different operators that provide different data feeds, so there can’t be guarantee that each price feed will be updated at the same time. Besides we should take into account:

  1. no one can guarantee that tx will be added to block - only nodes can decide on it and they can prioritize it. Data operators don’t have power here.
  2. different price feeds have different deviations and heartbeat - so they should be updated in different situations. For example price feed X/USD has deviation 1%, price feed Y/USD has deviation 2%. If price for both assets change on 1.5% first price feed should be updated, second one - not.

Ofc it’s better to read yourself - all documentation is public Decentralized Data Model | Chainlink Documentation

Do you have a link to the stETH/ETH chainlink oracle feed?