Open code423n4 opened 1 year ago
thereksfour marked the issue as primary issue
tbrent marked the issue as sponsor acknowledged
users can always use convertStaticToDynamic
and convertDynamicToStatic
to get the exchange rates as they both use getUpdatedSupplyIndicies()
. the issue being flagged here (rebase rate is dynamic) is inherent to the comet itself (and pretty much any rebasing token for that matter), and not something the wrapper needs to be concerned about.
thereksfour marked the issue as satisfactory
thereksfour marked the issue as selected for report
Lines of code
https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CusdcV3Wrapper.sol#L127-L170
Vulnerability details
Impact
When unwrapping the
wComet
to its rebasingcomet
, users with an equivalent amount ofwComet
invokingCusdcV3Wrapper._withdraw
at around the same time could end up having different percentage gains becausecomet
is not linearly rebasing.Moreover, the rate-determining
getUpdatedSupplyIndicies()
is an internal view function inaccessible to the users unless they take the trouble creating a contract to inherit CusdcV3Wrapper.sol. So most users making partial withdrawals will have no clue whether or not this is the best time to unwrap. This is because the public view function underlyingBalanceOf is only directly informational whenamount
has been entered astype(uint256).max
.Proof of Concept
https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CusdcV3Wrapper.sol#L134-L170
As can be seen in the code block of function
_withdraw
above,underlyingBalanceOf(src)
is first invoked.https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CusdcV3Wrapper.sol#L225-L231
Next, function
convertStaticToDynamic
is invoked.https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CusdcV3Wrapper.sol#L241-L244
And next, function
getUpdatedSupplyIndicies
is invoked. As can be seen in its code logic, the returned value ofbaseSupplyIndex_
is determined by the changingsupplyRate
.https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CusdcV3Wrapper.sol#L298-L313
The returned value of
baseSupplyIndex
is then inputted into functionprincipalValueSupply
where the lower the value ofbaseSupplyIndex
, the higher theprincipalValueSupply
or simply put, the lesser theburn
amount.https://github.com/reserve-protocol/protocol/blob/9ee60f142f9f5c1fe8bc50eef915cf33124a534f/contracts/plugins/assets/compoundv3/CometHelpers.sol#L29-L35
Tools Used
Manual
Recommended Mitigation Steps
Consider implementing slippage protection on
CusdcV3Wrapper._withdraw
so that users could opt for the minimum amount ofcomet
to receive or the maximum amount ofwComet
to burn.Assessed type
Timing