The UpliftOnlyExample.getPoolLPTokenValue() function uses the UpdateWeightRunner.getData() function to fetch asset prices from the corresponding oracle and uses them to calculate the value of the LP token. To protect the protocol from using stale prices, a threshold mechanism has been implemented to prevent the use of outdated data. This threshold value is retrieved from the pool through QuantAMMWeightedPool.getOracleStalenessThreshold(). Tracing the function shows that there is a variable oracleStalenessThreshold that stores this value.
The problem is that this threshold is shared across all assets in the pool, even though they may have different characteristics. For example, BTC, ETH, and altcoins are more volatile than stablecoins, and their prices need to be updated more frequently. That’s why in Chainlink, each token has a Deviation Threshold (indicating the percentage price change that triggers an update) and a Heartbeat (indicating the mandatory update interval, even if the deviation threshold hasn’t been reached).
In pools with assets of very different characteristics, a large threshold must be set, which could allow stale prices for more volatile assets. Conversely, setting a lower threshold could cause issues for low-volatility assets with longer heartbeat intervals. These inconsistencies lead to incorrect calculation of the LP token price in UpliftOnlyExample, which subsequently results in incorrect calculation of uplift fees. This directly causes a loss of funds for the protocol.
Here’s an example with the MNT token on Arbitrum and the Chainlink oracle:
https://data.chain.link/feeds/arbitrum/mainnet/mnt-usd
It can be seen that this oracle has a 10% deviation threshold and a 1-day heartbeat. This means that if it is used in a pool along with other tokens, a very high staleness threshold must be set, potentially resulting in stale prices for the other tokens.
Another example is the rETH token, which has a 2% deviation threshold, still allowing significant price deviations.
Loss of funds for the protocol or users due to incorrect uplift fee calculations.
Manual review
In UpdateWeightRunner, there is an array called ruleOracleStalenessThreshold that can be used to set individual thresholds for each asset. The code should be updated to utilize this array for setting specific thresholds for each asset.
This is by design, staleness is a strategy aspect: it requires all data to have been updated within n minutes. No more precision needed.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.