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: 83/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
Number | Issues Details |
---|---|
[L-01] | Initial value check is missing in Set Functions |
[L-02] | Critical address changes should use two-step procedure |
[L-03] | Missing event for critical parameter change |
[L-04] | Missing checks for address(0x0) |
[L-05] | No storage gap for upgradable contracts might lead to storage slot collision |
Number | Issues Details |
---|---|
[NC-01] | Use latest Solidity version |
[NC-02] | Use stable pragma statement |
[NC-03] | Use named imports instead of plain import FILE.SOL |
[NC-04] | Update external dependency to latest version |
[NC-05] | Solidity compiler optimizations can be problematic |
[NC-06] | Add NatSpec documentation |
[NC-07] | Commented code |
[NC-08] | Omissions in Events |
[NC-09] | Constants should be defined rather than using magic numbers |
[NC-10] | Constant values such as a call to keccak256(), should used to immutable rather than constant |
[NC-11] | You can use named parameters in mapping types |
Ethos-Core/contracts/BorrowerOperations.sol: 110: function setAddresses( 282: function _adjustTrove(address _borrower, address _collateral, uint _collTopUp, uint _collWithdrawal, uint _LUSDChange, bool _isDebtIncrease, address _upperHint, address _lowerHint, uint _maxFeePercentage) internal { Ethos-Core/contracts/TroveManager.sol: 1562: function setTroveStatus(address _borrower, address _collateral, uint _num) external override { 1567: function increaseTroveColl(address _borrower, address _collateral, uint _collIncrease) external override returns (uint) { 1574: function decreaseTroveColl(address _borrower, address _collateral, uint _collDecrease) external override returns (uint) { 1581: function increaseTroveDebt(address _borrower, address _collateral, uint _debtIncrease) external override returns (uint) { 1588: function decreaseTroveDebt(address _borrower, address _collateral, uint _debtDecrease) external override returns (uint) { Ethos-Core/contracts/ActivePool.sol 125: function setYieldingPercentage(address _collateral, uint256 _bps) external onlyOwner { 132: function setYieldingPercentageDrift(uint256 _driftBps) external onlyOwner { 138: function setYieldClaimThreshold(address _collateral, uint256 _threshold) external onlyOwner { 144: function setYieldDistributionParams(uint256 _treasurySplit, uint256 _SPSplit, uint256 _stakingSplit) external onlyOwner { 486: function updateRewardSum(address _collateral, uint _collToAdd) external override { Ethos-Vault/contracts/ReaperVaultV2.sol 617: function setLockedProfitDegradation(uint256 degradation) external {
Checking whether the current value and the new value are the same should be added.
The critical procedures should be a two-step process.
Ethos-Vault/contracts/ReaperVaultV2.sol 627: function updateTreasury(address newTreasury) external {
Recommended Mitigation Steps Lack of two-step procedure for critical operations leaves them error-prone. Consider adding a two- step procedure on the critical functions.
Emitting events allows monitoring activities with off-chain monitoring tools.
Ethos-Core/contracts/BorrowerOperations.sol: 412: function claimCollateral(address _collateral) external override { 419: function _triggerBorrowingFee(ITroveManager _troveManager, ILUSDToken _lusdToken, uint _LUSDAmount, uint _maxFeePercentage) internal returns (uint) { 455: function _updateTroveFromAdjustment 476: function _moveTokensAndCollateralfromAdjustment Ethos-Core/contracts/TroveManager.sol: 1016: function redeemCollateral( 1562: function setTroveStatus(address _borrower, address _collateral, uint _num) external override { 1567: function increaseTroveColl(address _borrower, address _collateral, uint _collIncrease) external override returns (uint) { 1574: function decreaseTroveColl(address _borrower, address _collateral, uint _collDecrease) external override returns (uint) { 1581: function increaseTroveDebt(address _borrower, address _collateral, uint _debtIncrease) external override returns (uint) { 1588: function decreaseTroveDebt(address _borrower, address _collateral, uint _debtDecrease) external override returns (uint) { Ethos-Core/contracts/ActivePool.sol 190: function increaseLUSDDebt(address _collateral, uint _amount) external override { 197: function decreaseLUSDDebt(address _collateral, uint _amount) external override { Ethos-Vault/contracts/ReaperVaultV2.sol 627: function updateTreasury(address newTreasury) external { Ethos-Vault/contracts/ReaperBaseStrategyv4.sol 156: function setEmergencyExit() external {
Lack of zero-address validation on address parameters may lead to transaction reverts, waste gas, require resubmission of transactions and may even force contract redeployments in certain cases within the protocol.
Ethos-Core/contracts/BorrowerOperations.sol: 412: function claimCollateral(address _collateral) external override { 455: function _updateTroveFromAdjustment 476: function _moveTokensAndCollateralfromAdjustment Ethos-Core/contracts/TroveManager.sol: 513: function liquidateTroves(address _collateral, uint _n) external override { 939: function redeemCloseTrove( 962: function updateDebtAndCollAndStakesPostRedemption( 982: function burnLUSDAndEmitRedemptionEvent( 1016: function redeemCollateral(
Consider adding explicit zero-address validation on input parameters of address type.
For upgradeable contracts, there must be storage gap to âallow developers to freely add new state variables in the future without compromising the storage compatibility with existing deploymentsâ. Otherwise, it may be very difficult to write new implementation code. Without storage gap, the variable in the contract contract might be overwritten by the upgraded contract if new variables are added. This could have unintended and very serious consequences to the child contracts.
This contracts are intended to be upgradeable contract in the code base: https://github.com/code-423n4/2023-02-ethos/blob/main/Ethos-Vault/contracts/abstract/ReaperBaseStrategyv4.sol#L14
Without storage gap, the variable in the contract contract might be overwritten by the upgraded contract if new variables are added. This could have unintended and very serious consequences to the child contracts.
Consider defining an appropriate storage gap in each upgradeable parent contract at the end of all the storage variable definitions as follows:
uint256[50] __gap; // gap to reserve storage in the contract for future variable additions
Solidity pragma versioning should be upgraded to latest available version.
Using a floating pragma statement ^0.8.0
is discouraged as code can compile to different bytecodes with different compiler versions. Use a stable pragma statement to get a deterministic bytecode.
IMPORT FILE.SOL
Recommendation: `import {contract1 , contract2} from "filename.sol";
The latest version is 4.8.2
Ethos-Core/package.json:
34: "@openzeppelin/contracts": "^3.3.0",
Ethos-Vault/package.json:
41: "@openzeppelin/contracts": "^4.7.3", 42: "@openzeppelin/contracts-upgradeable": "^4.7.3",
hardhat.config.js:
compilers: [ Â Â Â { Â Â Â Â version: "0.8.11", Â Â Â Â settings: { Â Â Â Â Â optimizer: { Â Â Â Â Â Â enabled: true, Â Â Â Â Â Â runs: 200, Â Â Â Â Â }, Â Â Â Â }, Â Â Â }, Â Â ],
Protocol has enabled optional compiler optimizations in Solidity. There have been several optimization bugs with security implications. Moreover, optimizations are actively being developed. Solidity compiler optimizations are disabled by default, and it is unclear how many contracts in the wild actually use them.
NatSpec documentation to all public/external functions and variables is essential for better understanding of the code by developers and auditors and is strongly recommended.
Ethos-Core/contracts/TroveManager.sol 14: // import "./Dependencies/Ownable.sol"; 18: /*Ownable,*/ 19: // string constant public NAME = "TroveManager";
Throughout the codebase, events are generally emitted when sensitive changes are made to the contracts. However, some events are missing important parameters
The events should include the new value and old value where possible:
Events with no old value;
Ethos-Core/contracts/LUSDToken.sol 150: emit GovernanceAddressChanged(_newGovernanceAddress); 157: emit GuardianAddressChanged(_newGuardianAddress); 172: emit TroveManagerAddressChanged(_newTroveManagerAddress); 176: emit StabilityPoolAddressChanged(_newStabilityPoolAddress); 180: emit BorrowerOperationsAddressChanged(_newBorrowerOperationsAddress);
Ethos-Vault/contracts/ReaperVaultV2.sol 125: lockedProfitDegradation = (DEGRADATION_COEFFICIENT * 46) / 10**6
There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts.
While it doesnât save any gas because the compiler knows that developers often make this mistake, itâs still best to use the right tool for the task at hand.
Constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.
Ethos-Vault/contracts/ReaperVaultV2.sol 73: bytes32 public constant DEPOSITOR = keccak256("DEPOSITOR"); 74: bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); 75: bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); 76: bytes32 public constant ADMIN = keccak256("ADMIN"); Ethos-Vault/contracts/ReaperBaseStrategyv4.sol 49: bytes32 public constant KEEPER = keccak256("KEEPER"); 50: bytes32 public constant STRATEGIST = keccak256("STRATEGIST"); 51: bytes32 public constant GUARDIAN = keccak256("GUARDIAN"); 52: bytes32 public constant ADMIN = keccak256("ADMIN");
From Solidity 0.8.18 you can use named parameters in mapping types. This will make the code much more readable.
For example check code below:
In TroveManager.sol
:
From:
85: // user => (collateral type => trove) 86: mapping (address => mapping (address => Trove)) public Troves;
To:
86: mapping (address user => mapping (address collateralType=> Trove)) public Troves;
This way you can put named parameters in mapping types everywhere in the code.
#0 - c4-judge
2023-03-08T15:55:07Z
trust1995 marked the issue as grade-b
#1 - c4-sponsor
2023-03-17T22:49:24Z
0xBebis marked the issue as sponsor confirmed