Platform: Code4rena
Start Date: 16/02/2023
Pot Size: $144,750 USDC
Total HM: 17
Participants: 154
Period: 19 days
Judge: Trust
Total Solo HM: 5
Id: 216
League: ETH
Rank: 65/154
Findings: 1
Award: $61.26
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: GalloDaSballo
Also found by: 0x3b, 0xAgro, 0xSmartContract, 0xTheC0der, 0xackermann, 0xnev, 0xsomeone, ABA, BRONZEDISC, Bjorn_bug, Bnke0x0, Breeje, Co0nan, CodeFoxInc, CodingNameKiki, DadeKuma, DeFiHackLabs, IceBear, Josiah, Kaysoft, Lavishq, MohammedRizwan, PaludoX0, PawelK, Phantasmagoria, Raiders, RaymondFam, Rickard, Rolezn, Sathish9098, SleepingBugs, SuperRayss, UdarTeam, Udsen, Viktor_Cortess, arialblack14, ast3ros, bin2chen, brgltd, btk, catellatech, ch0bu, chaduke, chrisdior4, codeislight, cryptonue, delfin454000, descharre, dontonka, emmac002, fs0c, hacker-dom, hansfriese, imare, lukris02, luxartvinsec, martin, matrix_0wl, peanuts, rbserver, shark, tnevler, trustindistrust, tsvetanovv, vagrant, yongskiws, zzzitron
61.2601 USDC - $61.26
Issue | Description | Instances |
---|---|---|
1 | Use require rather than assert where appropriate | 19 |
2 | Issues re warning should be resolved before deployment | 1 |
Total | 20 |
Issue | Description | Instances |
---|---|---|
1 | Avoid redundant return statement | 5 |
2 | Constant definitions that include call to keccak256 should use immutable | 8 |
3 | Invalid import syntax | 1 |
4 | Duplicate import statement | 1 |
5 | Long single-line comments | 68 |
6 | Update sensitive terms in both comments and code | 4 |
7 | Typos not included in Automated Findings output | 8 |
8 | Inconsistent returns syntax | 6 |
9 | Inconsistent require statement syntax | 2 |
10 | pragma solidity version should be upgraded to latest version | 12 |
11-1 | Natspec is partially missing for some functions | 3 |
11-2 | @return alone is missing for some functions | 8 |
11-3 | Natspec is wholly missing for some functions | 142 |
11-4 | Natspec is partially missing for some constructors | 2 |
11-5 | Natspec is wholly missing for some constructors | 3 |
Total | 273 |
No. | Explanation + cases |
---|---|
1 | Use require rather than assert where appropriate |
On failure, the assert function causes a Panic error and, unlike require , does not generate an error string. According to Solidity v0.8.18, "properly functioning code should never create a Panic." Therefore, an assert should be used only if, based on the relevant associated code, it is never expected to throw an exception. |
Below is an example of valid use of assert
in Ethos Reserve:
* The following assert() holds true because: * - The system always contains >= 1 trove * - When we close or liquidate a trove, we redistribute the pending rewards, so if all troves were closed/liquidated, * rewards would’ve been emptied and totalCollateralSnapshot would be zero too. */ assert(totalStakesSnapshot[_collateral] > 0);
On the other hand, the following assert
function does not to meet the criteria and should be replaced by a require
:
assert(MIN_NET_DEBT > 0);
Similarly for the following assert
functions:
No. | Explanation + cases |
---|---|
2 | Issues regarding warning should be resolved before deployment |
A strong warning is given regarding function updateCollateralRatios , however inadequate guardrails are in place to prevent abuse of this function. For example, there is nothing to prevent the function from being used to reduce _MCR and _CCR to their allowed minimums even for a collateral that formerly had fairly stringent collateral requirements |
// Admin function to lower the collateralization requirements for a particular collateral. // Can only lower, not increase. // // !!!PLEASE USE EXTREME CARE AND CAUTION. THIS IS IRREVERSIBLE!!! // // You probably don't want to do this unless a specific asset has proved itself through tough times. // Doing this irresponsibly can permanently harm the protocol. function updateCollateralRatios( address _collateral, uint256 _MCR, uint256 _CCR ) external onlyOwner checkCollateral(_collateral) { Config storage config = collateralConfig[_collateral]; require(_MCR <= config.MCR, "Can only walk down the MCR"); require(_CCR <= config.CCR, "Can only walk down the CCR"); require(_MCR >= MIN_ALLOWED_MCR, "MCR below allowed minimum"); config.MCR = _MCR; require(_CCR >= MIN_ALLOWED_CCR, "CCR below allowed minimum"); config.CCR = _CCR; emit CollateralRatiosUpdated(_collateral, _MCR, _CCR); }
No. | Description |
---|---|
1 | Avoid redundant return statement when function defines named return variable |
Adding a return statement when a function defines a named return variable is superfluous |
Below, function _liquidateNormalMode
defines named return variable singleLiquidation
function _liquidateNormalMode( IActivePool _activePool, IDefaultPool _defaultPool, address _collateral, address _borrower, uint _LUSDInStabPool ) internal returns (LiquidationValues memory singleLiquidation)
An unnecesssary return
statement for singleLiquidation
is given at the end of the function:
return singleLiquidation; }
Similarly for function `_liquidateRecoveryMode':
return singleLiquidation; }
Below, function _addLiquidationValuesToTotals
defines named return variable newTotals
function _addLiquidationValuesToTotals(LiquidationTotals memory oldTotals, LiquidationValues memory singleLiquidation) internal pure returns(LiquidationTotals memory newTotals) {
An unnecesssary return
statement for newTotals
is given at the end of the function:
return newTotals; }
Below, function _addTroveOwnerToArray
defines named return variable index
function _addTroveOwnerToArray(address _borrower, address _collateral) internal returns (uint128 index) {
An unnecesssary return
statement for index
is given at the end of the function:
return index; }
Below, function _computeRewardsPerUnitStaked
defines named return variables collGainPerUnitStaked
and LUSDLossPerUnitStaked
function _computeRewardsPerUnitStaked( address _collateral, uint _collToAdd, uint _debtToOffset, uint _totalLUSDDeposits ) internal returns (uint collGainPerUnitStaked, uint LUSDLossPerUnitStaked)
An unnecesssary return
statement for these variables is given at the end of the function:
return (collGainPerUnitStaked, LUSDLossPerUnitStaked); }
No. | Description |
---|---|
2 | Constant definitions that include call to keccak256 should use immutable |
While it does not save gas to use immutable instead of constant (since the compiler recognizes and makes up for the error), it is better form to use the correct variable type. Constant variables are intended to be used for literal values written into the code, whereas immutable variables are for expressions |
bytes32 public constant DEPOSITOR = keccak256("DEPOSITOR"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");
ReaperBaseStrategyv4.sol: L49-52
bytes32 public constant KEEPER = keccak256("KEEPER"); bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); bytes32 public constant ADMIN = keccak256("ADMIN");
No. | Description |
---|---|
3 | Invalid import syntax |
// import "./Dependencies/Ownable.sol";
Recommendation:
import "./Dependencies/Ownable.sol";
No. | Description |
---|---|
4 | Duplicate import statement |
import './Interfaces/IBorrowerOperations.sol'; import "./Interfaces/ICollateralConfig.sol"; import './Interfaces/IStabilityPool.sol'; import './Interfaces/IBorrowerOperations.sol';
Recommendation: Remove duplicate import of IBorrowerOperations.sol
No. | Description |
---|---|
5 | Long single-line comments |
In theory, comments over 80 characters should wrap using multi-line comment syntax. Even if longer comments (up to 120 characters) are becoming acceptable and a scroll bar is provided, very long comments can interfere with readability |
Below are examples of extra-long comments (over 120 characters) whose readability could be improved by wrapping, as shown. Note that a small indentation is used in each continuation line (which flags for the reader that the comment is ongoing), along with a period at the close (to signal the end of the comment). Also, if an extra-long line contains both code and a comment, it makes sense to put the comment in the line above.
// Confirm the operation is either a borrower adjusting their own trove, or a pure collateral transfer from the Stability Pool to a trove
Suggestion:
// Confirm the operation is either a borrower adjusting their own trove, // or a pure collateral transfer from the Stability Pool to a trove.
* Where L_Collateral(0) and L_LUSDDebt(0) are snapshots of L_Collateral and L_LUSDDebt for the active Trove taken at the instant the stake was made
Suggestion:
* Where L_Collateral(0) and L_LUSDDebt(0) are snapshots of L_Collateral and * L_LUSDDebt for the active Trove taken at the instant the stake was made.
[TroveManager.sol: L934-937(https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/TroveManager.sol#L934-L937)
* The redeemer swaps (debt - liquidation reserve) LUSD for (debt - liquidation reserve) worth of collateral, so the LUSD liquidation reserve left corresponds to the remaining debt. * In order to close the trove, the LUSD liquidation reserve is burned, and the corresponding debt is removed from the active pool. * The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove. * Any surplus collateral left in the trove, is sent to the Coll surplus pool, and can be later claimed by the borrower.
Suggestion:
* The redeemer swaps (debt - liquidation reserve) LUSD for (debt - liquidation reserve) worth of collateral, * so the LUSD liquidation reserve left corresponds to the remaining debt. * In order to close the trove, the LUSD liquidation reserve is burned, * and the corresponding debt is removed from the active pool. * The debt recorded on the trove's struct is zero'd elsewhere, in _closeTrove. * Any surplus collateral left in the trove, is sent to the Coll surplus pool, * and can be later claimed by the borrower.
// Return the nominal collateral ratio (ICR) of a given Trove, without the price. Takes a trove's pending coll and debt rewards from redistributions into account.
Suggestion:
// Return the nominal collateral ratio (ICR) of a given Trove, without the price — // takes a trove's pending coll and debt rewards from redistributions into account.
shares = previewWithdraw(assets); // previewWithdraw() rounds up so exactly "assets" are withdrawn and not 1 wei less
Suggestion:
// previewWithdraw() rounds up so exactly "assets" are withdrawn and not 1 wei less shares = previewWithdraw(assets);
Similarly for the extra-long comment lines referenced below:
BorrowerOperations.sol
TroveManager.sol
ActivePool.sol
StabilityPool.sol
L99-103, L109, L130, L136, L142
LUSDToken.sol
ReaperVaultERC4626.sol
L89, L93-94, L103, L119, L130, L136
No. | Description |
---|---|
6 | Update sensitive terms in both comments and code |
Terms incorporating "black," "white," "slave" or "master" are potentially problematic. Substituting more neutral terminology is becoming common practice |
* 1) Transfer protection: blacklist of addresses that are invalid recipients (i.e. core Liquity contracts) in external
Suggestion: Change blacklist of
to disallow
* Houses whitelist of allowed collaterals (ERC20s) for the entire system. Also provides access to collateral-specific
Suggestion: Change whitelist
to allowlist
Similarly for the following instances of whitelisted
:
No. | Description |
---|---|
7 | Typos not included in Automated Findings output |
require(_CCRs.length == _collaterals.length, "Array lenghts must match");
Change lenghts
to lengths
* The debt recorded on the trove's struct is zero'd elswhere, in _closeTrove.
Change elswhere
to elsewhere
* this indicates that rewards have occured since the snapshot was made, and the user therefore has
Change occured
to occurred
*Not necessarily equal to the the contract's raw collateral balance - collateral can be forcibly sent to contracts.
Remove repeated word the
* {KEEPER} - Stricly permissioned trustless access for off-chain programs or third party keepers.
Change Stricly
to Strictly
require(_amount <= balanceOf(), "Ammount must be less than balance");
Change Ammount
to Amount
ReaperBaseStrategyv4.sol: L185
* @dev This function must be overriden simply for access control purposes.
Change overriden
to overridden
ReaperBaseStrategyv4.sol: L235
* difference is due to a realized loss, or if there is some other sitution at play
Change sitution
to situation
No. | Description |
---|---|
8 | Inconsistent returns syntax |
While most functions incorporating returns have a space after returns (as recommended), some are missing the space. Below, both syntax types occur within a few lines of code (in function _getUSDValue and function _getCollChange ). The syntax should be made consistent throughout |
BorrowerOperations.sol: L432-444
function _getUSDValue(uint _coll, uint _price, uint256 _collDecimals) internal pure returns (uint) { uint usdValue = _price.mul(_coll).div(10**_collDecimals); return usdValue; } function _getCollChange( uint _collReceived, uint _requestedCollWithdrawal ) internal pure returns(uint collChange, bool isCollIncrease)
The returns
spacing should be corrected for the following:
No. | Description |
---|---|
9 | Inconsistent require statement syntax |
While most require statements have no space after require (as recommended), two have a space. Below, both syntax types occur within a few lines of code (in function _requireAtLeastMinNetDebt and function _requireValidLUSDRepayment ). The syntax should be made consistent |
BorrowerOperations.sol: L632-637
function _requireAtLeastMinNetDebt(uint _netDebt) internal pure { require (_netDebt >= MIN_NET_DEBT, "BorrowerOps: Trove's net debt must be greater than minimum"); } function _requireValidLUSDRepayment(uint _currentDebt, uint _debtRepayment) internal pure { require(_debtRepayment <= _currentDebt.sub(LUSD_GAS_COMPENSATION), "BorrowerOps: Amount repaid must not be larger than the Trove's debt");
The require
spacing should be corrected for the following statements:
No. | Description |
---|---|
10 | pragma solidity version should be upgraded to latest version |
The solidity version used in the in-scope contracts is ^0.8.0 or 0.6.11 , compared to the latest version of 0.8.19 |
There are specific upgrades that have been applied since 0.8.9
. For example, a version of at least 0.8.10 is required to have external calls skip contract existence checks if the external call has a return value, 0.8.12 is necessary for string.concat
to be used instead of abi.encodePacked
, and 0.8.13 or later is needed for the ability to use using for
with a list of free functions. In addition, only the latest version receives security fixes.
No. | Description |
---|---|
11-1 | Natspec is partially missing for some functions |
/** * @notice Only DEFAULT_ADMIN_ROLE can update treasury address. */ function updateTreasury(address newTreasury) external { _atLeastRole(DEFAULT_ADMIN_ROLE); require(newTreasury != address(0), "Invalid address"); treasury = newTreasury; }
Missing: @param newTreasury
ReaperBaseStrategyV4.sol: L90-95
/** * @dev Withdraws funds and sends them back to the vault. Can only * be called by the vault. _amount must be valid and security fee * is deducted up-front. */ function withdraw(uint256 _amount) external override returns (uint256 loss) {
Missing: @param _amount
and @return
ReaperStrategyGranarySupplyOnly.sol: L210-216
/** * @dev Attempts to safely withdraw {_amount} from the pool. */ function authorizedWithdrawUnderlying(uint256 _amount) external { _atLeastRole(STRATEGIST); _withdrawUnderlying(_amount); }
Missing: @param _amount
No. | Description |
---|---|
11-2 | @return alone is missing for some functions |
The function below has complete Natspec, with the exception of a missing @return statement: |
/** * @notice Called by a strategy to determine the amount of capital that the vault is * able to provide it. A positive amount means that vault has excess capital to provide * the strategy, while a negative amount means that the strategy has a balance owing to * the vault. */ function availableCapital() public view returns (int256) { address stratAddr = msg.sender; if (totalAllocBPS == 0 || emergencyShutdown) { return -int256(strategies[stratAddr].allocated); }
@return
is also the only missing Natspec for the following functions:
ReaperBaseStrategyV4.sol: L109
ReaperBaseStrategyV4.sol: L144
ReaperStrategyGranarySupplyOnly.sol: L218-222
No. | Description |
---|---|
11-3 | Natspec is wholly missing for some functions |
Natspec is completely missing for some public or external functions . Note that Natspec is required for public or external functions but not for private or internal ones. Many of the functions listed below are preceded by comments that could be converted to @notice or @dev statements |
CollateralConfig.sol
BorrowerOperations.sol
TroveManager.sol
L232, L299, L303, L310, L513, L715
L939, L957, L962, L982, L1016, L1045
L1054, L1076, L1111, L1123, L1138, L1152
L1164, L1183, L1195, L1273, L1316, L1361
L1366, L1397, L1425, L1429, L1440
L1444, L1456, L1460, L1471, L1475
L1485, L1544, L1548, L1552, L1556
L1562, L1567, L1574, L1581, L1588
ActivePool.sol
L71, L125, L132, L138, L144, L159
L164, L171, L190, L197, L204, L214
StabilityPool.sol
L261, L307, L311, L323, L367, L410
CommunityIssuance.sol
LQTYStaking.sol
LUSDToken.sol
L133, L141, L146, L153, L160, L185
ReaperVaultERC4626.sol
ReaperStrategyGranarySupplyOnly.sol
No. | Description |
---|---|
11-4 | Natspec is partially missing for some constructors |
constructors
/** * @notice Initializes the vault's own 'RF' token. * This token is minted when someone does a deposit. It is burned in order * to withdraw the corresponding portion of the underlying assets. * @param _token the token to maximize. * @param _name the name of the vault token. * @param _symbol the symbol of the vault token. * @param _tvlCap initial deposit cap for scaling TVL safely */ constructor( address _token, string memory _name, string memory _symbol, uint256 _tvlCap, address _treasury, address[] memory _strategists, address[] memory _multisigRoles
Missing: @param _treasury
, @param _strategists
and @param _multisigRoles
Similarly for ReaperVaultERC4626.sol's constructor:
ReaperVaultERC4626.sol: L15-23
No. | Description |
---|---|
11-5 | Natspec is wholly missing for some constructors |
Natspec is completely missing for the following constructors : |
#0 - c4-judge
2023-03-08T15:17:18Z
trust1995 marked the issue as grade-b
#1 - c4-sponsor
2023-03-17T03:54:17Z
0xBebis marked the issue as sponsor confirmed