Platform: Code4rena
Start Date: 11/12/2023
Pot Size: $90,500 USDC
Total HM: 29
Participants: 127
Period: 17 days
Judge: TrungOre
Total Solo HM: 4
Id: 310
League: ETH
Rank: 99/127
Findings: 1
Award: $30.41
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: neocrao
Also found by: 0xStalin, Aymen0909, Byteblockers, Chinmay, The-Seraphs, TheSchnilch, Timenov, Varun_05, ether_sky, kaden, mojito_auditor, mussucal, nonseodion, rbserver, santipu_, thank_you, twcctop
30.4141 USDC - $30.41
In LendingTerm::debtCeiling()
function, it intends to return the minimum of creditMinterBuffer
, hardCap
, and debtCeiling
.
function debtCeiling( int256 gaugeWeightDelta ) public view returns (uint256) { ... ... // return min(creditMinterBuffer, hardCap, debtCeiling) if (creditMinterBuffer < _debtCeiling) { return creditMinterBuffer; } if (_hardCap < _debtCeiling) { return _hardCap; } return _debtCeiling; }
But the above implementation of min using the if
condition is incorrect, as it doesn't return the minimum of the three values.
The function LendingTerm::debtCeiling()
has the following documentation:
/// @notice returns the maximum amount of debt that can be issued by this term /// according to the current gauge allocations.
The function intends to return the maximum amount of debt that the term can issue. Because of the incorrect implementation of the if
statment for the min, the function can return higher debt than the maximum allowed by the term.
The function LendingTerm::debtCeiling()
is also used in GuildToken::_decrementGaugeWeight()
which has the following require statement:
... ... uint256 debtCeilingAfterDecrement = LendingTerm(gauge).debtCeiling(-int256(weight)); require( issuance <= debtCeilingAfterDecrement, "GuildToken: debt ceiling used" ); ... ...
The above check might pass if the returned debtCeiling
is higher than the actual value.
Textual representation of the if
statements
Check if creditMinterBuffer
is less than _debtCeiling
:
creditMinterBuffer
.Check if _hardCap
is less than _debtCeiling
:
_hardCap
.Return _debtCeiling
(default case).
Now, assume the following hypothetical values:
creditMinterBuffer = 200; _debtCeiling = 300; _hardCap = 100;
In this case the first if
condition will succeed, and creditMinterBuffer = 200
will be returned, even though the minimum value that should be returned is _hardCap = 100
... ... if (creditMinterBuffer < _debtCeiling) { return creditMinterBuffer; } ... ...
Manual review
... ... + import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; ... ... contract LendingTerm is CoreRef { ... ... function debtCeiling( int256 gaugeWeightDelta ) public view returns (uint256) { ... ... + return Math.min(Math.min(creditMinterBuffer, _debtCeiling), _hardCap); - if (creditMinterBuffer < _debtCeiling) { - return creditMinterBuffer; - } - if (_hardCap < _debtCeiling) { - return _hardCap; - } - return _debtCeiling; } ... ... }
Math
#0 - c4-pre-sort
2024-01-02T18:30:46Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-01-02T18:31:10Z
0xSorryNotSorry marked the issue as duplicate of #878
#2 - c4-pre-sort
2024-01-04T12:47:58Z
0xSorryNotSorry marked the issue as not a duplicate
#3 - c4-pre-sort
2024-01-04T12:48:12Z
0xSorryNotSorry marked the issue as duplicate of #708
#4 - c4-judge
2024-01-28T19:48:34Z
Trumpero marked the issue as satisfactory
#5 - c4-judge
2024-01-31T13:41:25Z
Trumpero changed the severity to 2 (Med Risk)