Open c4-bot-1 opened 11 months ago
0xSorryNotSorry marked the issue as sufficient quality report
0xSorryNotSorry marked the issue as primary issue
0xSorryNotSorry marked the issue as high quality report
The issue is well demonstrated, properly formatted, contains a coded POC. Marking as HQ.
Confirming this issue, and thanks a lot for the quality of the report!
I wonder if this should be qualified as "Medium", as duplicates of this issue are qualified as Medium, and even though the protocol could end up with funds left in the PSM, all users are treated the same way, and there is always the possibility to recover the funds and organize and airdrop / multisend.
eswak (sponsor) confirmed
eswak marked the issue as disagree with severity
Trumpero changed the severity to 2 (Med Risk)
Trumpero marked the issue as satisfactory
Trumpero marked the issue as selected for report
Hey @Trumpero, I believe that this issue should be a high because the creditMultiplier directly influences the value of CREDIT in the system, using the incorrect method will lead to an inaccurate representation of the CREDIT value. Note that creditMultiplier can only decrease, and due to the current logical error, it's diminishing too quickly, resulting in direct losses for borrowers and holders.
I agree with @0xbtk that this issue should be a high.
@eswak Could you take a second look at this? Isn't the CreditToken.totalSupply() return the Total number of the tokens in existence.. If a slashing event occurs, why would the creditMultiplier
need to consider the future rewards (__unmintedRebaseRewards.targetValue), aren't those tokens locked until a date in the future? Why would the slashing consider those future rewards in the present?
Suppose a slashing occurs on day 2 of the reward's distribution, why would the rewards from day 3 to day 30 need to be considered as part of the existing supply? is not the purpose of the distribution of rewards to lock rewards and as time passes, slowly unlock them to be part of the existing supply?
I think the value of the creditMultiplier is correctly calculated by using the amount of existing supply at the moment when the slashing event occurs. I reiterate again, why a supply that is meant to be unlocked in the future would need to be considered in the present?
I just want to emphasize something, the unmintedRewards
are indeed considered when reading from CreditToken.totalSupply()
Understanding that unmintedRewards
are those rewards that have been unlocked based on the total time that has passed since they have been added to the distribution period. On the other hand, the future unminted rewards (__unmintedRebaseRewards.targetValue) are the ones that I believe should not be considered part of the supply in the present because those rewards are yet to be slowly added to the supply in the future, not in the present
I've taken a second look at the report and the problem seems to be a legit issue, if the undistributed rewards are not included as part of the existing supply at the moment of the slashing that would bring down the creditMultiplier more than what it should if all the undistributed rewards are included, this would cause that the PSM module has more PeggedTokens than what they could be claimed for using the total CreditTokens in existence.
As for the severity, I think @eswak has a point in his comment (severity should be medium), all the funds that would've been left in the PSM could've been recovered and distributed to the users so they ended up equal as if the creditMultiplier would've been updated considering all the supply, I mean, the users would not lose their assets permanently, right? It would be a temporary loss while the extra funds are correctly distributed, it may take some extra work, but in the end, all users could get the exact amount of peggedTokens they could've claimed if the creditMultiplier had been updated correctly. No PeggedTokens would be lost in reality, there would only be more PeggedTokens in the PSM module than the total PeggedTokens that could be claimed for, thus, the extra tokens can be claimed by governance and distributed accordingly, making whole to all the users. tldr; no funds are lost, the extra tokens left in the PSM module, and they can be claimed by governance and distributed accordingly.
Plus, all new minting and borrowing, and basically everything, from that point onwards will use the new value of the creditMultiplier, even though it was brought down more than what it should, all the accounting from that point and beyond it will use the new value of the creditMultiplier
Hey @stalinMacias the point of the credit multiplier update, and the goal of the math behind it, is to depreciate the synthetic versus the underlying so that:
This report proves how the second point is not respected, so value can remain locked in the protocol at the user’s expense. This should be clear in the PoC but you are very right that I should have point it out in the report.
@Trumpero A few wardens suggested this finding could be high and I tend to agree with that because the remediation of a governance action suggested by the sponsor may in practice not always be feasible in terms of:
@3docSec For the points that you've just mentioned is exactly why this fits as a medium severity. Yes, it may take some extra work to governance for preparing the distribution of the extra PeggedTokens left in the PSM module. As for the second point, how much gas does it take to make an ERC20 transfer? Include it inside a batch of transfers, and how much would it cost to make full the user? Unless the user's difference was less than 1-2 usd then it might be more expensive to credit the difference, but even though, that is doable, that's the reason why this is fine as a medium severity, for a report to be classified as a high severity, there should be a permanent loss of funds, a way that it is impossible to recover them
Hey @stalinMacias, not all borrowers will redeem their credit in the PSM. Those holding credit during a loss will incur more losses than they should. Any thoughts on how governance can refund them?
Check out #484 for more info.
@0xbtk Those incurred losses for the holders are exactly what it should be distributed by governance. As for the question of how could governance refund them, The SimplePSM contract inherits from the CoreRef contract, the CoreRef has the emergencyAction(), governance can simply craft a calldata to transfer the extra PeggedTokens that needs to be distributed to the holders who were impacted by the creditMultiplier going down more than what it should. So, governance transfers those PeggedTokens to an account of their own, they collect the information of how many PeggedTokens need to be sent to who to cover the difference and then they do those transfers in batch. That's it, governance recovered the extra tokens and distributed them.
That is because I consider this issue not to be a high severity. The tokens are not lost and they are not stuck forever in the PSM.
I consider this issue as medium because it doesn't cause a direct loss to the protocol or users. As the sponsor stated, the protocol could end up with funds left in the PSM, but all users are treated the same way, and Governor can still recover the funds by emergencyActions
Lines of code
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/2376d9af792584e3d15ec9c32578daa33bb56b43/src/governance/ProfitManager.sol#L330-L334
Vulnerability details
The function
notifyPnL
in ProfitManager is called by LendingTerms when a loan is closed to report a profit or loss.In case there is a loss, the amount in excess of the protocol's buffer is attributed to the credit token holders: this is done by slashing the
creditMultiplier
with the effect of depreciating the credit token relatively to the collateral.The math for slashing the
creditMultiplier
is the following:It is particularly interesting that
totalSupply()
is used, since in a similar high-level accounting calculation,totalBorrowedCredit()
,targetTotalSupply()
is used instead:The usage of
totalSupply()
here can be problematic when a significant portion of the supply is in undistributed rewards.In this case, the
creditMultiplier
slashing will be higher than it should becausetotalSupply()
will return a much lower value thantargetTotalSupply()
.Impact
creditMultiplier
slashing will always be higher than it should for correct accounting, penalizing credit token holders, and finally locking value in the protocol.The negative effects will be proportional to the relative supply of credit tokens in undistributed rewards versus the interpolated supply of the credit token.
Proof of Concept
The following PoC in Foundry (full setup here) shows how an overshot
creditMultiplier
slashing locks collateral value inSimplePSM
, with a net loss for protocol users, in a real-wold scenario:Tools Used
Code review, Foundry
Recommended Mitigation Steps
Consider using
targetTotalSupply()
instead oftotalSupply()
in thecreditMultiplier
slashing calculation:Assessed type
Math