moveQuoteToken updates pool state using intermediary LUP, biasing pool's interest rate calculations
Summary
In LenderActions's moveQuoteToken() LUP is being evaluated after liquidity removal, but before liquidity addition. This intermediary LUP doesn't correspond to the final state of the pool, but is returned as if it does, leading to a bias in pool target utilization and interest rate calculations.
Vulnerability Detail
moveQuoteToken() calculates LUP after deposit removal only instead of doing so after the whole operation, being atomic removal from one index and addition to another, and then updates the pool accounting _updateInterestState(poolState, newLup) with this intermediary newLup, that doesn't correspond to the final state of the pool.
Impact
moveQuoteToken() is one of the base frequently used operations, so the state of the pool will be frequently enough updated with incorrect LUP and EMA of LUP * t0 debt internal accounting variable be systematically biased, which leads to incorrect interest rate dynamics of the pool.
There is no low-probability prerequisites and the impact is a bias in interest rate calculations, so setting the severity to be high.
Code Snippet
moveQuoteToken() calculates the LUP right after the deposit removal:
function _calculateInterestRate(
PoolState memory poolState_,
uint256 debtEma_,
uint256 depositEma_,
uint256 debtColEma_,
uint256 lupt0DebtEma_
) internal pure returns (uint256 newInterestRate_) {
// meaningful actual utilization
int256 mau;
// meaningful actual utilization * 1.02
int256 mau102;
if (poolState_.debt != 0) {
// calculate meaningful actual utilization for interest rate update
mau = int256(_utilization(debtEma_, depositEma_));
mau102 = mau * PERCENT_102 / 1e18;
}
// calculate target utilization
int256 tu = (lupt0DebtEma_ != 0) ?
int256(Maths.wdiv(debtColEma_, lupt0DebtEma_)) : int(Maths.WAD);
Tool used
Manual Review
Recommendation
Consider calculating LUP in moveQuoteToken() after deposit addition to the destination bucket. Deposit fee can be calculated from initial LUP only, so only one, final, LUP recalculation looks to be necessary.
hyh
high
moveQuoteToken updates pool state using intermediary LUP, biasing pool's interest rate calculations
Summary
In LenderActions's moveQuoteToken() LUP is being evaluated after liquidity removal, but before liquidity addition. This intermediary LUP doesn't correspond to the final state of the pool, but is returned as if it does, leading to a bias in pool target utilization and interest rate calculations.
Vulnerability Detail
moveQuoteToken() calculates LUP after deposit removal only instead of doing so after the whole operation, being atomic removal from one index and addition to another, and then updates the pool accounting
_updateInterestState(poolState, newLup)
with this intermediarynewLup
, that doesn't correspond to the final state of the pool.Impact
moveQuoteToken() is one of the base frequently used operations, so the state of the pool will be frequently enough updated with incorrect LUP and
EMA of LUP * t0 debt
internal accounting variable be systematically biased, which leads to incorrect interest rate dynamics of the pool.There is no low-probability prerequisites and the impact is a bias in interest rate calculations, so setting the severity to be high.
Code Snippet
moveQuoteToken() calculates the LUP right after the deposit removal:
https://github.com/sherlock-audit/2023-04-ajna/blob/main/ajna-core/src/libraries/external/LenderActions.sol#L237-L312
Intermediary LUP is then being used for interest rate state update:
https://github.com/sherlock-audit/2023-04-ajna/blob/main/ajna-core/src/base/Pool.sol#L176-L207
https://github.com/sherlock-audit/2023-04-ajna/blob/main/ajna-core/src/base/Pool.sol#L675-L680
https://github.com/sherlock-audit/2023-04-ajna/blob/main/ajna-core/src/libraries/external/PoolCommons.sol#L148-L152
This will lead to a bias in target utilization and interest rate dynamics:
https://github.com/sherlock-audit/2023-04-ajna/blob/main/ajna-core/src/libraries/external/PoolCommons.sol#L269-L289
Tool used
Manual Review
Recommendation
Consider calculating LUP in moveQuoteToken() after deposit addition to the destination bucket. Deposit fee can be calculated from initial LUP only, so only one, final, LUP recalculation looks to be necessary.