Cheerful Gunmetal Moose
Medium
_reserves
is an internal storage variable that maps reserves to their DataTypes.ReserveData
. in ReserveData
struct, deficit took a storage position,
that in a previous version (piror to 3.2) was occupied by currentStableBorrowRate
, which was deprecated on version 3.2 :
// OLD VERSION
struct ReserveData {
//stores the reserve configuration
ReserveConfigurationMap configuration;
//the liquidity index. Expressed in ray
uint128 liquidityIndex;
//the current supply rate. Expressed in ray
uint128 currentLiquidityRate;
//variable borrow index. Expressed in ray
uint128 variableBorrowIndex;
//the current variable borrow rate. Expressed in ray
uint128 currentVariableBorrowRate;
//the current stable borrow rate. Expressed in ray
> uint128 currentStableBorrowRate;
//timestamp of last update
uint40 lastUpdateTimestamp;
//the id of the reserve. Represents the position in the list of the active reserves
uint16 id;
//...
}
// v3.3
struct ReserveData {
//stores the reserve configuration
ReserveConfigurationMap configuration;
//the liquidity index. Expressed in ray
uint128 liquidityIndex;
//the current supply rate. Expressed in ray
uint128 currentLiquidityRate;
//variable borrow index. Expressed in ray
uint128 variableBorrowIndex;
//the current variable borrow rate. Expressed in ray
uint128 currentVariableBorrowRate;
/// @notice reused `__deprecatedStableBorrowRate` storage from pre 3.2
// the current accumulate deficit in underlying tokens
> uint128 deficit;
//timestamp of last update
uint40 lastUpdateTimestamp;
//the id of the reserve. Represents the position in the list of the active reserves
uint16 id;
//...
}
This approach is actually a good one, to avoid corrupting the state of storage data. currentStableBorrowRate
was uint128 (16 bytes) and deficit
is uint128 (16 bytes), so it fits perfectly.
But the problem is that currentStableBorrowRate
could still hold data, and there is no method or logic to clear it's storage which will break the invariant : The deficit of all reserves should initially be zero, even if bad debt was created before the protocol upgrade..
The upgrade reuses the storage slot of currentStableBorrowRate
as deficit
without clearing old data.
Invariant Violation
Clear Data on Upgrade: Reset deficit for all reserves during the migration.