Platform: Code4rena
Start Date: 12/08/2022
Pot Size: $50,000 USDC
Total HM: 15
Participants: 120
Period: 5 days
Judge: Justin Goro
Total Solo HM: 6
Id: 153
League: ETH
Rank: 44/120
Findings: 2
Award: $67.35
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0x1f8b
Also found by: 0x52, 0xA5DF, 0xDjango, 0xNazgul, 0xNineDec, 0xSmartContract, 0xmatt, 0xsolstars, Aymen0909, Bnke0x0, CertoraInc, Chom, CodingNameKiki, Deivitto, Dravee, ElKu, EthLedger, Funen, IllIllI, JC, Junnon, Lambda, LeoS, MiloTruck, Noah3o6, PaludoX0, ReyAdmirado, Rohan16, RoiEvenHaim, Rolezn, SaharAP, Sm4rty, SooYa, The_GUILD, TomJ, Waze, Yiko, _Adam, __141345__, a12jmx, ak1, asutorufos, auditor0517, ayeslick, ballx, beelzebufo, berndartmueller, bin2chen, brgltd, c3phas, cRat1st0s, cccz, cryptonue, cryptphi, d3e4, delfin454000, dipp, djxploit, durianSausage, dy, erictee, fatherOfBlocks, gogo, gzeon, hyh, ignacio, kyteg, ladboy233, medikko, mics, minhquanym, oyc_109, pfapostol, rbserver, reassor, ret2basic, robee, sach1r0, simon135, sryysryy, tabish, yac, yash90, zzzitron
46.1819 USDC - $46.18
src/contracts/FraxlendPairCore.sol 172: CIRCUIT_BREAKER_ADDRESS = _circuitBreaker; 173: COMPTROLLER_ADDRESS = _comptrollerAddress; 174: TIME_LOCK_ADDRESS = _timeLockAddress; 175: FRAXLEND_WHITELIST_ADDRESS = _fraxlendWhitelistAddress;
Not all IERC20 implementations revert() when there's a failure in approve(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything.
src/contracts/FraxlendPairCore.sol 1103: _assetContract.approve(_swapperAddress, _borrowAmount); 1184: _collateralContract.approve(_swapperAddress, _collateralToSwap);
There is nothing preventing another account from calling the initializer before the contract owner. In the best case, the owner is forced to waste gas and re-deploy. In the worst case, the owner does not notice that his/her call reverts, and everyone starts using a contract under the control of an attacker.
src/contracts/FraxlendPairCore.sol 245: function initialize(
Use alternative variants, e.g. allowlist/denylist instead of whitelist/blacklist
src/contracts/FraxlendPairCore.sol 89: address public immutable FRAXLEND_WHITELIST_ADDRESS; 133: bool public immutable borrowerWhitelistActive; 136: bool public immutable lenderWhitelistActive;
src/contracts/FraxlendPairCore.sol 194: dirtyLiquidationFee = (_liquidationFee * 9000) / LIQ_PRECISION;
Each event should use three indexed fields if there are three or more fields
src/contracts/FraxlendPairCore.sol 376: event AddInterest( 389: event UpdateRate(uint256 _ratePerSec, uint256 _deltaTime, uint256 _utilizationRate, uint256 _newRatePerSec); 504: event UpdateExchangeRate(uint256 _rate); 696: event BorrowAsset( 760: event AddCollateral(address indexed _sender, address indexed _borrower, uint256 _collateralAmount); 846: event RepayAsset(address indexed _sender, address indexed _borrower, uint256 _amountToRepay, uint256 _shares); 897: event Liquidate( 1045: event LeveragedPosition( 1143: event RepayAssetWithCollateral(
Consider changing the variable to be an unnamed one
src/contracts/FraxlendPairCore.sol 320: function _isPastMaturity() internal view returns (bool) {
src/contracts/FraxlendPairCore.sol 422: return (_interestEarned, _feesAmount, _feesShare, _newRate);
src/contracts/FraxlendPairCore.sol 86: address public TIME_LOCK_ADDRESS;
#0 - gititGoro
2022-10-06T17:08:23Z
For number 3, the attacker would have to know that the contract has just been deployed which would be an inside job. This is beyond the scope of the audit. For number 2, nonconforming tokens are explicitly beyond the scope of the audit. For number 4, contemporary politics has no bearing on security or code quality
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xA5DF, 0xDjango, 0xNazgul, 0xSmartContract, 0xackermann, 0xbepresent, 0xc0ffEE, 0xkatana, 2997ms, Amithuddar, Aymen0909, Bnke0x0, Chinmay, Chom, CodingNameKiki, Deivitto, Diraco, Dravee, ElKu, EthLedger, Fitraldys, Funen, IgnacioB, JC, Junnon, Lambda, LeoS, Metatron, MiloTruck, Noah3o6, NoamYakov, PaludoX0, Randyyy, ReyAdmirado, Rohan16, Rolezn, Ruhum, SaharAP, Sm4rty, SooYa, TomJ, Tomio, Waze, Yiko, _Adam, __141345__, a12jmx, ajtra, ak1, asutorufos, ballx, brgltd, c3phas, cRat1st0s, carlitox477, chrisdior4, d3e4, delfin454000, dharma09, djxploit, durianSausage, erictee, fatherOfBlocks, find_a_bug, flyx, francoHacker, gerdusx, gogo, gzeon, hakerbaya, ignacio, jag, kyteg, ladboy233, ltyu, m_Rassska, medikko, mics, mrpathfindr, newfork01, nxrblsrpr, oyc_109, pfapostol, rbserver, reassor, ret2basic, robee, sach1r0, saian, simon135, sryysryy, zeesaw
21.1705 USDC - $21.17
When dealing with unsigned integer types, comparisons with != 0 are cheaper than with > 0 as we can save 3 gas
src/contracts/FraxlendPairCore.sol 477: if (_currentRateInfo.feeToProtocolRate > 0) { 754: if (_collateralAmount > 0) { 1002: if (_leftoverBorrowShares > 0) { 1094: if (_initialCollateralAmount > 0) {
If a variable is not set/initialized, it is assumed to have the default value (0 for uint/int, false for bool, address(0) for address and so on). Explicitly initializing it with its default value is an anti-pattern and wastes gas as It costs more gas to initialize variables to zero than to let the default of zero be applied
src/contracts/FraxlendPairCore.sol 265: for (uint256 i = 0; i < _approvedBorrowers.length; ++i) { 270: for (uint256 i = 0; i < _approvedLenders.length; ++i) {
The operators “||” and “&&” apply the common short-circuiting rules.
src/contracts/FraxlendPairCore.sol (Before) 308: if (maxLTV == 0) return true; uint256 _borrowerAmount = totalBorrow.toAmount(userBorrowShares[_borrower], true); if (_borrowerAmount == 0) return true; uint256 _collateralAmount = userCollateralBalance[_borrower];
(After) 308: if (maxLTV == 0 || _borrowerAmount == 0) return true; uint256 _borrowerAmount = totalBorrow.toAmount(userBorrowShares[_borrower], true); uint256 _collateralAmount = userCollateralBalance[_borrower];
Reading an array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack. Caching the array length in the stack saves around 3 gas per iteration.
src/contracts/FraxlendPairCore.sol (Before) 265: for (uint256 i = 0; i < _approvedBorrowers.length; ++i) { (After) 265: uint256 len = _approvedBorrowers.length for (uint256 i = 0; i < len; ++i) {
(Before) 270: for (uint256 i = 0; i < _approvedLenders.length; ++i) { (After) 270: uint256 len = _approvedLenders.length for (uint256 i = 0; i < len; ++i) {
For-Loops: Index increments can be left unchecked From Solidity v0.8 onwards, all arithmetic operations come with implicit overflow and underflow checks. In for-loops, as it is impossible for the index to overflow, it can be left unchecked to save gas every iteration.
src/contracts/FraxlendPairCore.sol (Before) 265: for (uint256 i = 0; i < _approvedBorrowers.length; ++i) { (After) 265: for (uint256 i = 0; i < _approvedBorrowers.length;) { unchecked { ++i; }
(Before) 270: for (uint256 i = 0; i < _approvedLenders.length; ++i) { (After) 270: for (uint256 i = 0; i < _approvedLenders.length;) { unchecked { ++i; }