Platform: Code4rena
Start Date: 24/10/2023
Pot Size: $149,725 USDC
Total HM: 7
Participants: 52
Period: 21 days
Judge: ronnyx2017
Total Solo HM: 2
Id: 300
League: ETH
Rank: 2/52
Findings: 2
Award: $15,736.55
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: TrungOre
15619.0396 USDC - $15,619.04
https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/CdpManager.sol#L434-L443 https://github.com/code-423n4/2023-10-badger/blob/f2f2e2cf9965a1020661d179af46cb49e993cb7e/packages/contracts/contracts/CdpManager.sol#L209-L214
The CdpManager.redeemCollateral()
function is used to send the _debt
amount of EBTC to the system and redeem the corresponding amount of stETH. There are two important features to note about this function before delving into the problem:
SortedCdps
list.SortedCdps
list using the "hint" provided by the users/front-end.The protocol utilizes a new technique by employing a sortedCdps.batchRemove()
function to remove a consecutive sequence of Cdps in the sorted list after the redemption loop, instead of removing each one individually in each iteration of the loop for gas-saving purposes.
while ( currentBorrower != address(0) && totals.remainingDebtToRedeem > 0 && _maxIterations > 0 ) { ... SingleRedemptionValues memory singleRedemption = _redeemCollateralFromCdp( _redeemColFromCdp ); ... } // remove from sortedCdps if (_numCdpsFullyRedeemed == 1) { sortedCdps.remove(_firstRedeemed); } else if (_numCdpsFullyRedeemed > 1) { bytes32[] memory _toRemoveIds = _getCdpIdsToRemove( _lastRedeemed, _numCdpsFullyRedeemed, _firstRedeemed ); sortedCdps.batchRemove(_toRemoveIds); }
As we observe, in each iteration of the loop, the internal function CdpManager._redeemCollateralFromCdp()
is triggered, executing the following logic:
If there is no remaining debt, the Cdp should be closed. To accomplish this, the function CdpManagerStorage._closeCdpByRedemption()
will be triggered, assigning the new STORAGE debt and collateral for the corresponding Cdp:
Cdps[_cdpId].coll = 0;
Cdps[_cdpId].debt = 0;
However, no "remove" operation will be executed for the Cdp in the SortedCdps
list. Consequently, the removed Cdp will remain in the same position in the sorted list after the for-loop, with the new collateralization ratio (NICR), which is type(uint256).max
following these lines of code.
Otherwise, the Cdp will be assigned a remaining value of debt and collateral and will immediately be reinserted into the SortedCdps
list.
The flaw arises when the partially redeemed Cdp is reinserted while the fully redeemed Cdps haven't been removed from the sorted list yet.
To make it clearer, consider the following scenario: In a regular flow of the redemption process, we have:
The SortedCdps
list described by a sequence of n
elements $x_1, x_2, ..., x_n$ in which:
A redemption request fully redeems Cdps from position i+1 -> j-1
in the sorted list. Thus, the NICR of all Cdps in that range becomes inf = type(uint256).max
. In other words:
The Cdp in position i
is partially redeemed and updated with its NICR to $x_i'$. If the new $x_i'$ is greater than $x_{i-1}$, it should be reinserted in a smaller position than i-1
, assuming it should be inserted between $x_{i-3}$ and $x_{i-2}$. The new SortedCdps
list right after the loop should be:
_upperPartialRedemptionHint
and _lowerPartialRedemptionHint
should be i-2, i-3
.After that, the process will execute the batch removal to remove all the Cdps that are fully redeemed, and the sorted list will be:
To disrupt this flow, the attacker can interfere in STEP 3 by passing malicious values of _upperPartialRedemptionHint
and _lowerPartialRedemptionHint
. In the example above, the attacker should use:
_upperPartialRedemptionHint = j-1
_lowerPartialRedemptionHint = j
Since the Cdp at position j-1
hasn't been removed yet in STEP 3, all the conditions for a valid insertion for $x_{i}'$ will be satisfied because:
Thus, the SortedCdps
list will look like this after the redemption process:
This breaks the order of the SortedCdps
because $x_i' > x_{i-1}$ but has a larger position.
The sequence of the sortedCdps
list will be disrupted, and given that essential protocol functions operate in accordance with this sequence, the severity of the issue is significant.
Manual review
Instead of inserting the partially redeemed Cdp back in the for-loop, the function should reinsert it after removing the fully redeemed Cdp.
Other
#0 - bytes032
2023-11-16T08:41:59Z
Leaving for sponsor/judge review
Might be OOS: https://gist.github.com/GalloDaSballo/a0f9766bf7bac391f49d2d167e947de0#incorrect-sorting-due-to-pending-debt-and-yield
#1 - c4-pre-sort
2023-11-16T08:42:04Z
bytes032 marked the issue as sufficient quality report
#2 - c4-pre-sort
2023-11-16T08:42:08Z
bytes032 marked the issue as primary issue
#3 - c4-sponsor
2023-11-20T09:43:06Z
GalloDaSballo (sponsor) disputed
#4 - GalloDaSballo
2023-11-20T09:43:20Z
The finding is known and part of the known broken invariants + cantina report
#5 - GalloDaSballo
2023-11-20T09:44:00Z
We would be more lenient if any impact was demonstrated but afaict there's only an edge case for Redemptions not working when liquidations are more profitable which would lead me to dispute even in that case
We are interested in more impacts if those were found
#6 - GalloDaSballo
2023-11-22T17:59:16Z
We would want to ask the Warden for the POC as we have a concern for the issue being valid, but it seems like the finding is impossible in practice
#7 - rayeaster
2023-11-23T11:19:44Z
here is a working POC to verify the finding https://gist.github.com/CodingNameKiki/4925e433ee47be70e6a4ef3eebe75b81?permalink_comment_id=4770452#gistcomment-4770452
#8 - jhsagd76
2023-11-27T09:38:41Z
OOS
The finding is known and part of the known broken invariants + cantina report
#9 - c4-judge
2023-11-27T09:38:53Z
jhsagd76 marked the issue as unsatisfactory: Out of scope
#10 - jhsagd76
2023-11-27T11:37:26Z
I have noticed that a pull request https://github.com/ebtc-protocol/ebtc/pull/725 has been submitted to the main repo for this issue. Is this really OOS? @GalloDaSballo
#11 - GalloDaSballo
2023-11-27T12:33:33Z
The breaking of sorting is known but the mechanism through which this was achieved was not, so we believe the finding to be in scope
The impact of this finding is unclear
#12 - c4-judge
2023-11-28T02:05:45Z
jhsagd76 removed the grade
#13 - c4-judge
2023-11-28T02:06:04Z
jhsagd76 changed the severity to 2 (Med Risk)
#14 - c4-judge
2023-11-28T02:06:13Z
jhsagd76 marked the issue as satisfactory
#15 - c4-judge
2023-11-28T06:39:49Z
jhsagd76 marked the issue as selected for report
#16 - jhsagd76
2023-12-01T00:05:07Z
The attack scenario provided by the warden is very clear, but I don't see any actual profit or loss from it. Warden might argue that the CDP's position is incorrect after partial redemption, leading to damage upon the next redemption. However, in reality, it could have been fully redeemed initially, and the attacker wouldn't have gained any additional profits from it. But this clearly violates a critical invariant, especially when carefully crafted inputs are used as attack vectors. Therefore, I believe this falls into a med category.
#17 - CodingNameKiki
2023-12-01T06:45:43Z
Hey @jhsagd76, currently working alongside the badger team on some mitigations.
The breaking of sorting is known but the mechanism through which this was achieved was not, so we believe the finding to be in scope
With further research this report doesn't demonstrate any attack.
I believe this is not related to the empty nodes kept in the system while partially reInserting, it doesn't matter if you partially reInsert when there are empty nodes or not as the system is working correctly. The concern here is regarding the mathematical aspect of the ebtc system, it was proven in the broken invariants that there could be slightly difference in the NICR when redeeming happens and as a result a particular node could have a bigger NICR than his previous node in the sorted list.
There is no attack scenario here, whether if you remove the empty nodes or keep them or only partially redeeming the problem is still there and it's just how the system operates, there could be mathematical value difference between the NICR of the two nodes next to each other, but it is slim to nothing and doesn't lead to anything.
You can see that the problem still exist when there is no full redemption on redeeming and the partial redeemed node still has a bigger NICR than prev one in the sorted list.
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.17; import "forge-std/Test.sol"; import {eBTCBaseInvariants} from "./BaseInvariants.sol"; contract checkSorting is eBTCBaseInvariants { address wallet = address(0xbad455); function setUp() public override { super.setUp(); connectCoreContracts(); connectLQTYContractsToCore(); } function testSorting() public { uint256 grossColl = 11e18 + cdpManager.LIQUIDATOR_REWARD(); uint256 price = priceFeedMock.fetchPrice(); uint256 debtColl = _utils.calculateBorrowAmount( 11e18, price, COLLATERAL_RATIO ); // Cdp with the biggest NICR and the head of the sorted list bytes32 first = _openTestCDP(wallet, grossColl, debtColl - 10000); // Cdp with second best NICR in the sorted list bytes32 second = _openTestCDP(wallet, grossColl, debtColl - 7500); // Cdp with second lowest NICR in the sorted list bytes32 third = _openTestCDP(wallet, grossColl, debtColl); vm.startPrank(wallet); (bytes32 hint, uint256 partialNICR, , ) = hintHelpers.getRedemptionHints(debtColl / 2, priceFeedMock.fetchPrice(), 0); // Approve cdp manager to use ebtc tokens eBTCToken.approve(address(cdpManager), eBTCToken.balanceOf(wallet)); cdpManager.redeemCollateral(debtColl / 2 , hint, bytes32(0), bytes32(0), partialNICR, 0, 1e18); uint256 _secondNICR = cdpManager.getCachedNominalICR(second); uint256 _thirdNICR = cdpManager.getCachedNominalICR(third); assert(_thirdNICR > _secondNICR); vm.stopPrank(); } }
#18 - jhsagd76
2023-12-02T01:00:47Z
Alright, according to the prove from sponsor:
There is no attack scenario here, whether if you remove the empty nodes or keep them or only partially redeeming the problem is still there and it's just how the system operates, there could be mathematical value difference between the NICR of the two nodes next to each other, but it is slim to nothing and doesn't lead to anything.
The warden only show the known broken invariant, but doesn't provide a valid and new attack vector. So this is OOS.
#19 - c4-judge
2023-12-02T01:01:23Z
jhsagd76 marked the issue as unsatisfactory: Out of scope
#20 - CodingNameKiki
2023-12-02T09:56:43Z
First of all l apologise for the second ping, but l think l made a mistake here. If we add additional logs to the POC we can see that even tho the NICR of the partial redeem node changes and goes above the prev node in the sorted list, the system still finds its correct place when reInserting happens. So on second look l believe the issue in this report is valid and occurs only when fully redeeming node and partial redeeming from another. @jhsagd76
https://gist.github.com/CodingNameKiki/11103cc1921f18a3c600123f87ace4ab
Additional note and impact regarding this issue:
2: 2154011847065201038074 3: 2154011847065190493148
Given the small NICR mathematical difference between the two nodes and that this issue only applies for the first two nodes in the sorted list which have ICR >= MCR. l don't think its possible to have any rational impact here rather than the first two nodes having a small NICR difference between them.
#21 - jhsagd76
2023-12-04T03:29:36Z
Alright, I'll go back here, but waiting a final check:
The attack scenario provided by the warden is very clear, but I don't see any actual profit or loss from it. Warden might argue that the CDP's position is incorrect after partial redemption, leading to damage upon the next redemption. However, in reality, it could have been fully redeemed initially, and the attacker wouldn't have gained any additional profits from it. But this clearly violates a critical invariant, especially when carefully crafted inputs are used as attack vectors. Therefore, I believe this falls into a med category.
#22 - CodingNameKiki
2023-12-04T05:04:28Z
Alright, I'll go back here, but waiting a final check:
Will make a POC today to confirm the statement and let you know.
#23 - CodingNameKiki
2023-12-04T21:13:46Z
Hello again :) @jhsagd76
So on short explanation l understood that this issue:
The attack scenario provided by the warden is very clear, but I don't see any actual profit or loss from it.
Agree with you on this one, at the same time an interesting question is why would anyone want to do this if there is no profit from it, no impact and the sorted order is back to normal on the next redeem.
https://gist.github.com/CodingNameKiki/b3f36aacc704c082df2b974b2db89c78
#24 - jhsagd76
2023-12-04T23:25:30Z
Thanks for the poc and verification from @CodingNameKiki . And the sponsor finally convinced me. The invariant was only temporarily broken, and whatever the attacker does during this intermediate stage will get back to the previous state. This impact is so minimal that it can be downgraded to a QA
#25 - c4-judge
2023-12-04T23:26:32Z
jhsagd76 changed the severity to QA (Quality Assurance)
#26 - c4-judge
2023-12-04T23:26:42Z
jhsagd76 marked the issue as grade-a
#27 - WelToHackerLand
2023-12-05T02:18:50Z
Hello @jhsagd76, @rayeaster, thank you for reviewing the issue.
I would like to respectfully question the severity of the problem for the following reasons:
Initially, there doesn't seem to be an attacker involved in this issue, as a regular user seeking partial redemption could potentially disrupt the sorted cdps. The provided Proof of Concept (POC) demonstrates that the hints used by the sender are derived from the hintHelpers
contract rather than being predetermined by an attacker.
Additionally, the POC presented by @CodingNameKiki might be applicable only when the redemption occurs immediately after. What if other transactions take place before the fixed redemption? For instance, consider a scenario where the NICR of sortedCdps
post-redemption disrupts the order, such as [8, 12, 5]. If a user intends to open a new CDP with NICR = 9, the provided hints could be before NICR = 8, resulting in the new sorted cdps as [9, 8, 12, 5]. In this case, there are now two incorrect positions in the sorted list instead of one. The more openCdp()
transactions are initiated, the more incorrect positions could emerge, and the cost to rectify the order becomes higher.
Considering these factors, I believe the severity of this issue should be classified as medium.
#28 - CodingNameKiki
2023-12-05T07:11:02Z
Initially, there doesn't seem to be an attacker involved in this issue, as a regular user seeking partial redemption could potentially disrupt the sorted cdps. The provided Proof of Concept (POC) demonstrates that the hints used by the sender are derived from the hintHelpers contract rather than being predetermined by an attacker.
Do you mean providing wrong _firstRedemptionHint or wrong _partialRedemptionHintNICR, if that's the case the system will manually find the correct redeemable hint which is the first node with ICR >= MCR, on the other hand if you provide a wrong partial hint NICR the partial redeeming will be cancelled.
if (_isValidFirstRedemptionHint(_firstRedemptionHint, totals.price)) { currentBorrower = sortedCdps.getOwnerAddress(_firstRedemptionHint); } else { _cId = sortedCdps.getLast(); currentBorrower = sortedCdps.getOwnerAddress(_cId); // Find the first cdp with ICR >= MCR while (currentBorrower != address(0) && getSyncedICR(_cId, totals.price) < MCR) { _cId = sortedCdps.getPrev(_cId); currentBorrower = sortedCdps.getOwnerAddress(_cId); } }
if ( newNICR != _redeemColFromCdp.partialRedemptionHintNICR || collateral.getPooledEthByShares(newColl) < MIN_NET_STETH_BALANCE ) { singleRedemption.cancelledPartial = true; return singleRedemption; }
In case you are talking about the upper and lower hints used in the POC, l tried every other possible hints and the system is working correctly. So this scenario is only possible if someone provided the exact hints and only when the system fully redeems a node and partially redeems from another.
We would be more lenient if any impact was demonstrated but afaict there's only an edge case for Redemptions not working when liquidations are more profitable which would lead me to dispute even in that case We are interested in more impacts if those were found
As mentioned we are already aware and it's known that wrong sorting can happen from the broken invariants, the question should be what impact does this issue lead to? Would suggest for a further research on the impact here and coded POC.
#29 - huuducsc
2023-12-05T17:42:21Z
@jhsagd76 @rayeaster @GalloDaSballo I would like to explain the reasons why I believe this issue should be considered at least a medium severity. I also believe that these explanations can address the concerns raised from @codingnamekiki and others to downgrade this issue.
I believe the provided attack vector is very clear, indicating that an attacker can disrupt the sorting order of CDPs through partial redemption. The attacker just needs to call the function redeemCollateral with malicious values for _upperPartialRedemptionHint
and _lowerPartialRedemptionHint
to place the partially redeemed CDP in the wrong position. The scenario was described very clearly in the report, and there is another proof of concept (POC) mentioned in the comment by @rayeaster.
Additionally, I believe it's not a known issue because the known scenario that also breaks the order is from redistribution of debt and yield, which is different. This scenario allows an external attacker to interact with and manipulate the broken invariants. It has a significant impact, which will be described below.
An attacker can increase the NICR of a partially redeemed CDP to a very large value and place it in the wrong position. To achieve this, the attacker can calculate the redemption amount to leave the remaining debt of the partially redeemed CDP at 1. For example, if a CDP has $15e18 in collateral and $10e18 in debt, the attacker can calculate a partial redemption with a debt of $10e18 - 1, resulting in a remaining debt of 1 and remaining collateral of 5e18 (assuming remaining collateral can bypass MIN_NET_STETH_BALANCE). This manipulation causes the new NICR to become very large.

Consider the scenario in the report. After redemption, the sortedCdps list will be:
$x_{i}'$ may have a very large NICR (up to infinity), and it is the first node with ICR >= MCR because the ICR of $x_{j}$ is below MCR. Therefore, any new CDP that is opened with _upperHint
as dummy or invalid will be placed after the CDP $x_{i}'$. This is because the _findInsertPosition
function will find the position in ascending order if the _prevId
parameter is dummy or invalid.
You can read this code snippet to understand this flow.
For example, when the NICR of $x_{i}'$ is very large (considered infinity), the NICR list of sortedCdps is like:
300%, 250%, ..., 160%, 150%, inf, 125%, 124%, ...
When opening a CDP with a 200% NICR and _upperHint
as dummy or invalid, the list will become:
300%, 250%, ..., 160%, 150%, inf, 200%, 125%, 124%, ...
Therefore, the new CDP (200% NICR) will be inserted to a wrong position and become the first node (most right) with ICR > MCR and will be redeemed with priority, even though the NICR of that CDP is still good.
Here is a POC of this scenario: https://gist.github.com/huuducsc/c6885a06058d56cd4f6669f3b11c8f7d
Due to the above scenario, I disagree with the statement made by @jhsagd76: "The invariant was only temporarily broken, and whatever the attacker does during this intermediate stage will get back to the previous state."
The attacker can open numerous new Cdps into incorrect positions using dummy upperHint
and invalid lowerHint
(details are described in the scenario and PoC of section 2), disrupting the order of protocols. Moreover, the attacker can front-run any opening CDP transaction by users to break the order, make it becomes the first position to be redeemed.
Here are the details of the impacts that cause significant damage to protocol:
sortedCdps
and disrupt it by opening many new CDPs with _upperHint
is dummy, placing them in incorrect positions (as described above). Therefore, sortedCdps will have many incorrect positions that are not easy to resolve. This disruption can mess up the protocol's data and may result in incorrect returns from the HintHelpers contract._upperHint
passed to the openCdp
function of users is dummy or invalid, which can occur normally due to changes in on-chain data like front-running or incorrect returns from the HintHelper contract. It creates an unfair situation for protocol users, as they may be compelled to redeem their assets (acquiring debt assets and losing collateral assets) even when they have a CDP with a high NICR.In conclusion, this issue has a significant impact that can disrupt the ordered state of the protocol and put users at risk of having their assets forcibly redeemed. However, the severity may increase if attackers identify other attack vectors (which I haven't found until now) to manipulate the state and exploit vulnerabilities since attackers are able to break order invariants externally.
#30 - CodingNameKiki
2023-12-05T18:32:32Z
^ Thanks for the reply, will further make a POC to double confirm everything.
#31 - CodingNameKiki
2023-12-05T20:00:08Z
Check the below POC which confirms the above statements.
https://gist.github.com/CodingNameKiki/0f4bc68802cfcbfe255759554b735b38
#32 - jhsagd76
2023-12-06T08:18:05Z
Thank warden for providing additional information on the impact. This has made the report ultimately comprehensive and clear.
I have discussed the final rating based on the following facts, rules, and input from two senior judges. We ultimately elevate this issue to MED. Record I.:
As the original submission doesn't contain a PoC of the final impact, I think Medium is more appropriate.
Record II.:
for the impact I.
This disruption can mess up the protocol's data and may result in incorrect returns from the HintHelpers contract.
it meets the criteria of MED. Although it is not dos, it affected the availability of the protocol for normal users.
but the function of the protocol or its availability could be impacted
For impact II.
It creates an unfair situation for protocol users, as they may be compelled to redeem their assets (acquiring debt assets and losing collateral assets) even when they have a CDP with a high NICR.
Its based on impact I, but I think it can be a HIGH according to the criteria:
Assets can be
stolen/lost/compromiseddirectly orindirectly if there is a valid attack path that does not have hand-wavy hypotheticalsBecause attackers are able to break order invariants externally, we can believe the attack path is explicit (include front run). And the redemption from high NICR cdp won't result in a direct loss of assets, but it is clearly a form of compromise.
#33 - c4-judge
2023-12-06T08:18:55Z
This previously downgraded issue has been upgraded by jhsagd76
#34 - c4-judge
2023-12-06T08:19:11Z
jhsagd76 marked the issue as satisfactory
#35 - c4-judge
2023-12-06T08:19:16Z
jhsagd76 marked the issue as selected for report
#36 - huuducsc
2023-12-06T11:28:03Z
@jhsagd76 Thank you very much for your review. I would like to say that this issue deserves a high severity because users' assets can be compromised when order of Cdps is broken. It aligns with the criteria of a high-severity issue outlined in Code4rena's docs.
Assets can be stolen/lost/ compromised directly or indirectly if there is a valid attack path that does not have hand-wavy hypotheticals
The reason I didn't provide detailed descriptions of the impact in the initial report was my assumption that anyone could create several high impacts by exploiting the scenario of an attacker breaking the order externally. However, there were arguments against this view, necessitating a detailed explanation of the impact. And it is a fact that I provided a detailed explanations of this issue along with a PoC. Therefore, I believe this issue should be considered a valid high-severity issue.
Furthermore, I believe it doesn't make sense to downgrade this issue based on the rationale that "the original submission doesn't contain a PoC of the final impact." While the impact in the report may lack details and be unclear to some, I subsequently provided detailed explanations and a PoC to substantiate its high severity. The information I supplied doesn't alter the severity of this issue; rather, it serves to validate the impact and address concerns/suspects raised by others. Therefore, I believe that the lack of details in the report shouldn't be used as a basis for downgrading the issue, based on this statement from C4's docs:
At the judge's discretion, a highly-standard issue can be accepted with less detail provided.
#37 - jhsagd76
2023-12-06T12:38:32Z
In fact, the Record II. is my assessment, while the Record I. consists of recommendations from the senior judges. I find their suggestions reasonable, and they also provided me with precedents. However, I believe that discussing these precedents here may not be appropriate. And the rules you mentioned is for the case "Duplicate Report PoC Thoroughness".
still consider the recommendation from the senior judges. And I will submit a proposal to the org after the contest to formalize the rule.
Thank you for your understanding and perfect work.
#38 - huuducsc
2023-12-07T05:05:16Z
@jhsagd76 Thank you for the reply. I understand your situation, but I believe this issue shouldn't be downgraded since it was initially raised with high severity, and the PoC merely clarified its impact. Additionally, I don't see any reasonable grounds in the other judge's suggestions to downgrade this issue. I believe there were many valid issues on C4 that lacked a detailed PoC or explanation but were still accepted as high/medium severity issues because the judges could easily understand them. In this case, this issue faced arguments and misunderstandings, necessitating a detailed explanation of the impact. This is why the Post-judging period is necessary, as it assists the warden in proving and clarifying statements from the report. My additional PoC didn't increase the impact of my initial report; rather, it further clarified it and should not lead to a downgrade in the severity of the issue. Here are some examples from C4's judging for several issues that lacked a detailed PoC:
🌟 Selected for report: SpicyMeatball
Also found by: 0xBeirao, 7ashraf, LokiThe5th, OMEN, TrungOre, alexzoid, alpha, bdmcbri, ether_sky, fatherOfBlocks, ge6a, hihen, hunter_w3b, jasonxiale, ladboy233, lsaudit, niroh, nobody2018, nonseodion, peanuts, prapandey031, shaka, twcctop, twicek, wangxx2026
117.508 USDC - $117.51
Number | Issue |
---|---|
[L-01] | Redundant requirement require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!"); in function _openCdp() |
[L-02] | Unused array _liqFlags |
[L-03] | Function LiquidationLibrary._redistributeDebt() shouldn't return if _debt == 0 |
[L-04] | Typo in comments |
[L-05] | Inappropriate check in function ActivePool.flashLoan() |
require(vars.netStEthBalance > 0, "BorrowerOperations: zero collateral for openCdp()!");
in function _openCdp()
In the function BorrowerOperations._openCdp()
, the requirement vars.netStEthBalance > 0
in line 463 is redundant due to the check in line 454:
454 _requireAtLeastMinNetStEthBalance(vars.netStEthBalance);
Therefore, the requirement vars.netStEthBalance > 0
in line 463 is redundant.
_liqFlags
In the function LiquidationLibrary._getTotalFromBatchLiquidate_RecoveryMode()
, the array _liqFlags
is created to denote which iterations are liquidated successfully. However, this array is neither used to return a value nor utilized for event emission, resulting in unnecessary gas consumption for users.
751 bool[] memory _liqFlags = new bool[](_cnt);
LiquidationLibrary._redistributeDebt()
shouldn't return if _debt == 0
In the function LiquidationLibrary._redistributeDebt()
, when _debt == 0
, the function immediately returns, assuming there is no debt to distribute. However, before this function call, the totalStake
can possibly decrease, causing the value of lastEBTCDebtErrorRedistribution >= _totalStakes
and allowing redistribution even when _debt = 0
.
862 function _redistributeDebt(uint256 _debt) internal { 863 if (_debt == 0) { 864 return; 865 }
239 /// @notice Notify that stETH collateral shares have been received, updating internal accounting accordingly
108 /// @notice Set grace period duration
ActivePool.flashLoan()
Function ActivePool.flashLoan()
incorporates two checks to ensure the correct processing of the flash loan method:
294 require( 295 collateral.balanceOf(address(this)) >= collateral.getPooledEthByShares(systemCollShares), 296 "ActivePool: Must repay Balance" 297 ); 298 require( 299 collateral.sharesOf(address(this)) >= systemCollShares, 300 "ActivePool: Must repay Share" 301 );
However, using the value of systemCollShares
for these two checks doesn't make sense because the amount of stETH
present in the ActivePool is not solely represented by systemCollShares
. It also includes the liquidator reward and the value of feeRecipientCollShares
. Therefore, these checks should be replaced by verifying the balance of stETH before and after the flash loan's callback.
#0 - c4-pre-sort
2023-11-17T13:26:41Z
bytes032 marked the issue as sufficient quality report
#1 - jhsagd76
2023-12-08T08:32:41Z
L5
#2 - c4-judge
2023-12-08T08:32:48Z
jhsagd76 marked the issue as grade-a