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: 33/120
Findings: 2
Award: $81.49
🌟 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
55.7393 USDC - $55.74
lenght, so that the index composition can be fetched in batches without running out of gas. If there are thousands of index components (e.g. like the Wilshire 5000 index), the function may revert):-
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 122-132):
function getAllPairAddresses() external view returns (address[] memory) { string[] memory _deployedPairsArray = deployedPairsArray; uint256 _lengthOfArray = _deployedPairsArray.length; address[] memory _addresses = new address[](_lengthOfArray); uint256 i; for (i = 0; i < _lengthOfArray; ) { _addresses[i] = deployedPairsByName[_deployedPairsArray[i]]; unchecked { i++; } }
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 144-161):
function getCustomStatuses(address[] calldata _addresses) external view returns (PairCustomStatus[] memory _pairCustomStatuses) { uint256 _lengthOfArray = _addresses.length; uint256 i; _pairCustomStatuses = new PairCustomStatus[](_lengthOfArray); for (i = 0; i < _lengthOfArray; ) { _pairCustomStatuses[i] = PairCustomStatus({ _address: _addresses[i], _isCustom: deployedPairCustomStatusByAddress[_addresses[i]] }); unchecked { i++; } }
decimals()
, name()
and symbol()
are optional parts of the ERC20 specification, so there are tokens that
do not implement them. It’s not safe to cast arbitrary token addresses
in order to call these functions. If IERC20Metadata
is to be relied on, that should be the variable type of the token variable, rather than it being address
, so the compiler can verify that types correctly match, rather than this being a runtime failure. ):-
File: fraxlend/src/contracts/interfaces/IFraxlendPair.sol (line 133):
function name() external view returns (string calldata);
File: fraxlend/src/contracts/interfaces/IFraxlendPair.sol (line 70):
function decimals() external pure returns (uint8);
File: fraxlend/src/contracts/interfaces/IFraxlendPair.sol (line 190):
function symbol() external view returns (string calldata);
File: fraxlend/src/contracts/interfaces/IRateCalculator.sol (line 5):
function name() external pure returns (string memory);
File: fraxlend/src/contracts/LinearInterestRate.sol (line 40):
function name() external pure returns (string memory) {
address(0x0)
when assigning values to address
state variables:-File: fraxlend/src/contracts/FraxlendPair.sol (line 170-177):
_LTV_PRECISION = LTV_PRECISION; _LIQ_PRECISION = LIQ_PRECISION; _UTIL_PREC = UTIL_PREC; _FEE_PRECISION = FEE_PRECISION; _EXCHANGE_PRECISION = EXCHANGE_PRECISION; _DEFAULT_INT = DEFAULT_INT; _DEFAULT_PROTOCOL_FEE = DEFAULT_PROTOCOL_FEE; _MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE;
File: fraxlend/src/contracts/FraxlendPair.sol (line 206):
TIME_LOCK_ADDRESS = _newAddress;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 278):
rateInitCallData = _rateInitCallData;
File: fraxlend/src/contracts/FraxlendPairCore.sol(line 510):
_exchangeRate = _updateExchangeRate();
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 571):
totalAsset = _totalAsset;totalAsset = _totalAsset;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 647):
totalAsset = _totalAsset;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 815):
totalCollateral -= _collateralAmount;
approve
should be replaced with safeApprove
or safeIncreaseAllowance()
/ safeDecreaseAllowance()
(approve
is subject to a known front-running attack. Consider using safeApprove
instead:):-File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1103):
_assetContract.approve(_swapperAddress, _borrowAmount);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1184):
_collateralContract.approve(_swapperAddress, _collateralToSwap);
##Non-Critical Issues
return
statement when the function defines a named return variable, is redundant:-return _addresses;
return _pairAddress;
_assetsPerUnitShare = totalAsset.toAmount(1e18, false);
` uint256 internal constant LTV_PRECISION = 1e5; // 5 decimals uint256 internal constant LIQ_PRECISION = 1e5; uint256 internal constant UTIL_PREC = 1e5; uint256 internal constant FEE_PRECISION = 1e5; uint256 internal constant EXCHANGE_PRECISION = 1e18;
// Default Interest Rate (if borrows = 0) uint64 internal constant DEFAULT_INT = 158247046; // 0.5% annual rate 1e18 precision // Dependencies address internal constant FRAXSWAP_ROUTER_ADDRESS = 0xE52D0337904D4D0519EF7487e707268E1DB6495F; // Protocol Fee uint16 internal constant DEFAULT_PROTOCOL_FEE = 0; // 1e5 precision uint256 internal constant MAX_PROTOCOL_FEE = 5e4; // 50% 1e5 precision`
3. File: fraxlend/src/contracts/FraxlendPairCore.sol (line 198):
dirtyLiquidationFee = (_liquidationFee * 9000) / LIQ_PRECISION; // 90% of clean fee
File: fraxlend/src/contracts/FraxlendPairCore.sol(line 468):
_interestEarned = (_deltaTime * _totalBorrow.amount * _currentRateInfo.ratePerSec) / 1e18;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 522):
uint256 _price = uint256(1e36);
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 49-51):
uint256 public DEFAULT_MAX_LTV = 75000; // 75% with 1e5 precision uint256 public GLOBAL_MAX_LTV = 1e8; // 1000x (100,000%) with 1e5 precision, protects from rounding errors in LTV calc uint256 public DEFAULT_LIQ_FEE = 10000; // 10% with 1e5 precision
File: fraxlend/src/contracts/FraxlendPairDeployer.sol(line 171):
bytes memory _firstHalf = BytesLib.slice(_creationCode, 0, 13000);
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 173-174):
if (_creationCode.length > 13000) { bytes memory _secondHalf = BytesLib.slice(_creationCode, 13000, _creationCode.length - 13000);
File: fraxlend/src/contracts/FraxlendPairHelper.sol (line 212):
_interestEarned = (_totalBorrow.amount * _newRate * (_timestamp - _currentRateInfo.lastTimestamp)) / 1e18;
File: fraxlend/src/contracts/LinearInterestRate.sol (line 35-42):
uint256 private constant MAX_INT = 146248508681; // 10,000% annual rate uint256 private constant MAX_VERTEX_UTIL = 1e5; // 100% uint256 private constant UTIL_PREC = 1e5;
` uint32 private constant MIN_UTIL = 75000; // 75% uint32 private constant MAX_UTIL = 85000; // 85% uint32 private constant UTIL_PREC = 1e5; // 5 decimals
// Interest Rate Settings (all rates are per second), 365.24 days per year uint64 private constant MIN_INT = 79123523; // 0.25% annual rate uint64 private constant MAX_INT = 146248508681; // 10,000% annual rate uint256 private constant INT_HALF_LIFE = 43200e36; // given in seconds, equal to 12 hours, additional 1e36 to make math simpler`
12. File: fraxlend/src/contracts/VariableInterestRate.sol (line 69):
uint256 _deltaUtilization = ((MIN_UTIL - _utilization) * 1e18) / MIN_UTIL;
uint256 _deltaUtilization = ((_utilization - MAX_UTIL) * 1e18) / (UTIL_PREC - MAX_UTIL);
' bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol() bytes4 private constant SIG_NAME = 0x06fdde03; // name() bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()'
'return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;'
Event
is missing indexed
fields(Each event
should use three indexed
fields if there are three or more fields):-
event SetTimeLock(address _oldAddress, address _newAddress);
event WithdrawFees(uint128 _shares, address _recipient, uint256 _amountToTransfer);
File: fraxlend/src/contracts/FraxlendPair.sol (line 198):
event SetSwapper(address _swapper, bool _approval);
File: fraxlend/src/contracts/FraxlendPair.sol(line 468):
event SetApprovedLender(address indexed _address, bool _approval);
File: fraxlend/src/contracts/FraxlendPair.sol (line 522):
event SetApprovedBorrower(address indexed _address, bool _approval);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 376-382):
event AddInterest( uint256 _interestEarned, uint256 _rate, uint256 _deltaTime, uint256 _feesAmount, uint256 _feesShare );
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 389):
event UpdateRate(uint256 _ratePerSec, uint256 _deltaTime, uint256 _utilizationRate, uint256 _newRatePerSec);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 696-701):
event BorrowAsset( address indexed _borrower, address indexed _receiver, uint256 _borrowAmount, uint256 _sharesAdded );
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 760):
event AddCollateral(address indexed _sender, address indexed _borrower, uint256 _collateralAmount);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 846):
event RepayAsset(address indexed _sender, address indexed _borrower, uint256 _amountToRepay, uint256 _shares);
event Liquidate( address indexed _borrower, uint256 _collateralForLiquidator, uint256 _sharesToLiquidate, uint256 _amountLiquidatorToRepay, uint256 _sharesToAdjust, uint256 _amountToAdjust );
event LeveragedPosition( address indexed _borrower, address _swapperAddress, uint256 _borrowAmount, uint256 _borrowShares, uint256 _initialCollateralAmount, uint256 _amountCollateralOut );
event RepayAssetWithCollateral( address indexed _borrower, address _swapperAddress, uint256 _collateralToSwap, uint256 _amountAssetOut, uint256 _sharesRepaid );
event SetOracleWhitelist(address indexed _address, bool _bool);
event SetRateContractWhitelist(address indexed _address, bool _bool);
event SetFraxlendDeployerWhitelist(address indexed _address, bool _bool);
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
bool _isCustom;
Rename to '_isCustom'
bool _borrowerWhitelistActive; bool _lenderWhitelistActive;
Rename to '_borrowerWhitelistActive,' Rename to '_lenderWhitelistActive'
>
0.9.0, not >=
):-pragma solidity >=0.8.15;
pragma solidity >=0.8.15;
File: fraxlend/src/contracts/interfaces/IFraxlendWhitelist.sol (line 2):
pragma solidity >=0.8.15;
File: fraxlend/src/contracts/interfaces/IRateCalculator.sol (line 2):
pragma solidity >=0.8.15;
File: fraxlend/src/contracts/interfaces/ISwapper.sol (line 2):
pragma solidity >=0.8.15;
public
ذfunctions not called by the contract should be declared external
instead:-' function name() public view override(ERC20, IERC20Metadata) returns (string memory) { return nameOfContract; }
function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) { // prettier-ignore // solhint-disable-next-line max-line-length return string(abi.encodePacked("FraxlendV1 - ", collateralContract.safeSymbol(), "/", assetContract.safeSymbol())); } function decimals() public pure override(ERC20, IERC20Metadata) returns (uint8) { return 18; } // totalSupply for fToken ERC20 compatibility function totalSupply() public view override(ERC20, IERC20) returns (uint256) { return totalAsset.shares; }'
2. File: fraxlend/src/contracts/FraxlendPair.sol (line 100):
function totalAssets() public view virtual returns (uint256) {
File: fraxlend/src/contracts/LinearInterestRate.sol (line 52):
function requireValidInitData(bytes calldata _initData) public pure {
'pragma solidity ^0.8.15;'
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 2):
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 2):
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/FraxlendPairHelper.sol (line 2):
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 2):
'pragma solidity ^0.8.15;'
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/VariableInterestRate.sol (line 2):
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 2):
pragma solidity ^0.8.15;
File: fraxlend/src/contracts/libraries/VaultAccount.sol (line 2):
pragma solidity ^0.8.15;
#0 - gititGoro
2022-10-06T09:52:01Z
Non standard token support out of scope. Contemporary politics has no bearing on security and is also out of scope.
🌟 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
25.7456 USDC - $25.75
x = x + y
is cheaper than x += y
:-File: fraxlend/src/contracts/FraxlendPair.sol (line 252):
_totalAsset.amount -= uint128(_amountToTransfer);
File: fraxlend/src/contracts/FraxlendPair.sol (line 253):
_totalAsset.shares -= _shares;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 643-644):
_totalAsset.amount -= _amountToReturn; _totalAsset.shares -= _shares;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 813-815):
userCollateralBalance[_borrower] -= _collateralAmount; // Following line will revert on underflow if totalCollateral < _collateralAmount totalCollateral -= _collateralAmount;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 863-867):
` _totalBorrow.amount -= _amountToRepay; _totalBorrow.shares -= _shares;
// Effects: write to state userBorrowShares[_borrower] -= _shares;`
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1008-1012):
` totalAsset.amount -= _amountToAdjust;
// Note: Ensure this memory stuct will be passed to _repayAsset for write to state _totalBorrow.amount -= _amountToAdjust; _totalBorrow.shares -= _sharesToAdjust;`
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 475-476):
_totalBorrow.amount += uint128(_interestEarned); _totalAsset.amount += uint128(_interestEarned);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 484):
_totalAsset.shares += uint128(_feesShare);
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 566-567):
_totalAsset.amount += _amount; _totalAsset.shares += _shares;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 718-719):
_totalBorrow.amount += _borrowAmount; _totalBorrow.shares += uint128(_sharesAdded);
userBorrowShares[msg.sender] += _sharesAdded;
userCollateralBalance[_borrower] += _collateralAmount; totalCollateral += _collateralAmount;
<array>.length
should not be looked up in every loop of a for
loop (Even memory arrays incur the overhead of bit tests and bit shifts tocalculate the array length. Storage array length checks incur an extra Gwarmaccess (100 gas) PER-LOOP.):-
File: fraxlend/src/contracts/FraxlendPair.sol (line 289):
for (uint256 i = 0; i < _lenders.length; i++) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 308):
for (uint256 i = 0; i < _borrowers.length; i++) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 265):
for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 270):
for (uint256 i = 0; i < _approvedLenders.length; ++i) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 51):
`for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 66):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 81):
for (uint256 i = 0; i < _addresses.length; i++) {
++i/i++
should be unchecked{++i}
/unchecked{++i}
when it is not possible for them to overflow, as is the case when used in for
and while
loops:-File: fraxlend/src/contracts/FraxlendPair.sol (line 289):
for (uint256 i = 0; i < _lenders.length; i++) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 308):
for (uint256 i = 0; i < _borrowers.length; i++) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 265):
for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 270):
for (uint256 i = 0; i < _approvedLenders.length; ++i) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 51):
`for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 66):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 81):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 27):
for (i = 0; i < 32 && data[i] != 0; i++) {
require
()/revert()
strings longer than 32 bytes cost extra gas:-File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 205):
require(deployedPairsBySalt[salt] == address(0), "FraxlendPairDeployer: Pair already deployed");
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 228):
require(_pairAddress != address(0), "FraxlendPairDeployer: create2 failed");
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 253):
require(deployedPairsByName[_name] == address(0), "FraxlendPairDeployer: Pair name must be unique");
File: fraxlend/src/contracts/LinearInterestRate.sol (line 57-68):
require(_maxLTV <= GLOBAL_MAX_LTV, "FraxlendPairDeployer: _maxLTV is too large"); require( IFraxlendWhitelist(FRAXLEND_WHITELIST_ADDRESS).fraxlendDeployerWhitelist(msg.sender), "FraxlendPairDeployer: Only whitelisted addresses" );
File: fraxlend/src/contracts/LinearInterestRate.sol (line 57-68):
` require( _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT, "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT" ); require( _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT, "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT" ); require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" );'
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 133):
return _addresses;
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 233):
return _pairAddress;
> 0
costs more gas than != 0
when used on a uint in a require()
statement:-File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 65-68):
require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" );
File: fraxlend/src/contracts/FraxlendPair.sol (line 289):
for (uint256 i = 0; i < _lenders.length; i++) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 308):
for (uint256 i = 0; i < _borrowers.length; i++) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 265):
for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 270):
for (uint256 i = 0; i < _approvedLenders.length; ++i) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 51):
`for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 66):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 81):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 27):
for (i = 0; i < 32 && data[i] != 0; i++) {
File: fraxlend/src/contracts/FraxlendPairConstants.sol (line 47):
uint16 internal constant DEFAULT_PROTOCOL_FEE = 0; // 1e5 precision
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 127):
`for (i = 0; i < _lengthOfArray; ) {'
for (i = 0; i < _lengthOfArray; ) {
uint256 private constant MIN_INT = 0; // 0.00% annual rate
uint8 i = 0;
for (i = 0; i < 32 && data[i] != 0; i++) {
++i
costs less gas than i++
, especially when it’s used in forloops (--i/i-- too):-File: fraxlend/src/contracts/FraxlendPair.sol (line 289):
for (uint256 i = 0; i < _lenders.length; i++) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 308):
for (uint256 i = 0; i < _borrowers.length; i++) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 265):
for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 270):
for (uint256 i = 0; i < _approvedLenders.length; ++i) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 51):
`for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 66):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 81):
for (uint256 i = 0; i < _addresses.length; i++) {
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 27):
for (i = 0; i < 32 && data[i] != 0; i++) {
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 27):
for (i = 0; i < 32 && data[i] != 0; i++) {
require()
statements that use &&
Cost gas:-File: fraxlend/src/contracts/LinearInterestRate.sol (line 57-68):
` require( _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT, "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT" ); require( _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT, "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT" ); require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" );'
uints
/ints
smaller than 32 bytes (256 bits) incurs overhead:-File: fraxlend/src/contracts/FraxlendPair.sol (line 84):
function decimals() public pure override(ERC20, IERC20Metadata) returns (uint8) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 137):
return type(uint128).max;
File: fraxlend/src/contracts/FraxlendPair.sol (line 141):
return type(uint128).max;
File: fraxlend/src/contracts/FraxlendPair.sol (line 165-166):
uint64 _DEFAULT_INT, uint16 _DEFAULT_PROTOCOL_FEE,
File: fraxlend/src/contracts/FraxlendPair.sol (line 211):
`event ChangeFee(uint32 _newFee);'
File: fraxlend/src/contracts/FraxlendPair.sol (line 215):
function changeFee(uint32 _newFee) external whenNotPaused {
File: fraxlend/src/contracts/FraxlendPair.sol (line 240):
if (_shares == 0) _shares = uint128(balanceOf(address(this)));
File: fraxlend/src/contracts/FraxlendPair.sol (line 252):
_totalAsset.amount -= uint128(_amountToTransfer);
File: fraxlend/src/contracts/FraxlendPairConstants.sol (line 41):
uint64 internal constant DEFAULT_INT = 158247046;
File: fraxlend/src/contracts/FraxlendPairConstants.sol (line 47):
uint16 internal constant DEFAULT_PROTOCOL_FEE = 0;
uint64 lastBlock; uint64 feeToProtocolRate; // Fee amount 1e5 precision uint64 lastTimestamp; uint64 ratePerSec;
uint32 lastTimestamp; uint224 exchangeRate
`uint64 _newRate'
uint64 _newRate
_currentRateInfo.lastTimestamp = uint64(block.timestamp); _currentRateInfo.lastBlock = uint64(block.number);
_newRate = uint64(penaltyRate);
_currentRateInfo.lastTimestamp = uint64(block.timestamp); _currentRateInfo.lastBlock = uint64(block.number);
_interestEarned + _totalBorrow.amount <= type(uint128).max && _interestEarned + _totalAsset.amount <= type(uint128).max ) { _totalBorrow.amount += uint128(_interestEarned); _totalAsset.amount += uint128(_interestEarned);
`_totalAsset.shares += uint128(_feesShare);'
if (_exchangeRate > type(uint224).max) revert PriceTooLarge(); _exchangeRateInfo.exchangeRate = uint224(_exchangeRate); _exchangeRateInfo.lastTimestamp = uint32(block.timestamp);
uint128 _amount, uint128 _shares,
uint128 _amountToReturn, uint128 _shares,
_totalBorrow.shares += uint128(_sharesAdded);
uint128 _amountToRepay, uint128 _shares,
`uint128 _sharesToLiquidate,'
uint128 _borrowerShares = userBorrowShares[_borrower].toUint128();
` uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_sharesToLiquidate, true)).toUint128();
// Determine if and how much debt to writeoff // Note: ensures that sharesToLiquidate is never larger than borrowerShares uint128 _sharesToAdjust; uint128 _amountToAdjust;`
28. File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1001):
uint128 _leftoverBorrowShares = _borrowerShares - _sharesToLiquidate;
uint64 lastBlock; uint64 feeToProtocolRate; // Fee amount 1e5 precision uint64 lastTimestamp; uint64 ratePerSec;
uint128 _totalAssetAmount, uint128 _totalAssetShares, uint128 _totalBorrowAmount, uint128 _totalBorrowShares,
(uint64 lastBlock, uint64 feeToProtocolRate, uint64 lastTimestamp, uint64 ratePerSec) = _fraxlendPair
(uint128 _totalAssetAmount, uint128 _totalAssetShares) = _fraxlendPair.totalAsset();
`(uint128 _totalBorrowAmount, uint128 _totalBorrowShares) = _fraxlendPair.totalBorrow();'
_newRate = uint64(_fraxlendPair.penaltyRate());
(, uint64 _feeToProtocolRate, , ) = _fraxlendPair.currentRateInfo();
(uint128 _totalAssetAmount, uint128 _totalAssetShares) = _fraxlendPair.totalAsset();`
uint128 _sharesToLiquidate
(uint128 _totalBorrowAmount, uint128 _totalBorrowShares) = _fraxlendPair.totalBorrow();
function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec) {
_newRatePerSec = uint64(_minInterest + ((_utilization * _slope) / UTIL_PREC));
_newRatePerSec = uint64(_vertexInterest + (((_utilization - _vertexUtilization) * _slope) / UTIL_PREC));
`_newRatePerSec = uint64(_vertexInterest);'
` uint32 private constant MIN_UTIL = 75000; // 75% uint32 private constant MAX_UTIL = 85000; // 85% uint32 private constant UTIL_PREC = 1e5; // 5 decimals
// Interest Rate Settings (all rates are per second), 365.24 days per year uint64 private constant MIN_INT = 79123523; // 0.25% annual rate uint64 private constant MAX_INT = 146248508681; // 10,000% annual rate`
44. File: fraxlend/src/contracts/VariableInterestRate.sol (line 63):
function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec) {
'_newRatePerSec = uint64((_currentRatePerSec * INT_HALF_LIFE) / _decayGrowth);'`
_newRatePerSec = uint64((_currentRatePerSec * _decayGrowth) / INT_HALF_LIFE);
uint8 i = 0;
function safeDecimals(IERC20 token) internal view returns (uint8) {
`return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;'
` uint128 amount; // Total amount, analogous to market cap uint128 shares; // Total shares, analogous to shares outstanding'
uint64 _newRate
function changeFee(uint32 _newFee) external;
` uint64 lastBlock, uint64 feeToProtocolRate, uint64 lastTimestamp, uint64 ratePerSec );
function decimals() external pure returns (uint8);'
54. File: fraxlend/src/contracts/interfaces/IFraxlendPair.sol (line 89-90):
uint64 _DEFAULT_INT, uint16 _DEFAULT_PROTOCOL_FEE,
uint128 _sharesToLiquidate,
require()
/revert()
checks should be refactored to a modifier or function:-File: fraxlend/src/contracts/FraxlendPair.sol (line 65-68):
require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" );
require()
or revert()
statements that check input arguments should be at the top of the function (Checks that involve constants should come before checks that involve state variables):-File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 228):
require(_pairAddress != address(0), "FraxlendPairDeployer: create2 failed");
revert()
/require()
strings to save deployment gas:-File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 205):
require(deployedPairsBySalt[salt] == address(0), "FraxlendPairDeployer: Pair already deployed");
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 228):
require(_pairAddress != address(0), "FraxlendPairDeployer: create2 failed");
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 253):
require(deployedPairsByName[_name] == address(0), "FraxlendPairDeployer: Pair name must be unique");
File: fraxlend/src/contracts/LinearInterestRate.sol (line 57-68):
require(_maxLTV <= GLOBAL_MAX_LTV, "FraxlendPairDeployer: _maxLTV is too large"); require( IFraxlendWhitelist(FRAXLEND_WHITELIST_ADDRESS).fraxlendDeployerWhitelist(msg.sender), "FraxlendPairDeployer: Only whitelisted addresses" );
File: fraxlend/src/contracts/LinearInterestRate.sol (line 57-68):
` require( _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT, "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT" ); require( _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT, "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT" ); require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" );'
payable
(If a function modifier such as onlyOwner
is used, the function will revert if a normal user tries to pay the function. Marking the function as payable
will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided.):-File: fraxlend/src/contracts/FraxlendPair.sol (line 204):
function setTimeLock(address _newAddress) external onlyOwner {
File: fraxlend/src/contracts/FraxlendPair.sol (line 234):
function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {
File: fraxlend/src/contracts/FraxlendPair.sol (line 274):
function setSwapper(address _swapper, bool _approval) external onlyOwner {
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 170):
function setCreationCode(bytes calldata _creationCode) external onlyOwner {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 50):
function setOracleContractWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 65):
function setRateContractWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 80):
function setFraxlendDeployerWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {
<, or ==. Replacing the >= and ≤ operators with a comparison operator that has an opcode in the EVM saves gas.):-
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 196):
if (_maxLTV >= LTV_PRECISION && !_isBorrowerWhitelistActive) revert BorrowerWhitelistRequired();
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 525-527):
if (_answer <= 0) { revert OracleLTEZero(oracleMultiply); }
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1000):
if (_leftoverCollateral <= 0) {
File: fraxlend/src/contracts/libraries/SafeERC20.sol (line 165-19):
if (data.length >= 64) {
`Recommended Mitigation Steps
Replace the comparison operator and reverse the logic to save gas using the suggestions above.`
There are several cases of function arguments using memory instead of calldata):-
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 295):
function _totalAssetAvailable(VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow)
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 559-564):
function _deposit( VaultAccount memory _totalAsset, uint128 _amount, uint128 _shares, address _receiver )
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 623-629):
function _redeem( VaultAccount memory _totalAsset, uint128 _amountToReturn, uint128 _shares, address _receiver, address _owner )
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 855-861):
function _repayAsset( VaultAccount memory _totalBorrow, uint128 _amountToRepay, uint128 _shares, address _payer, address _borrower ) internal {
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 1062-1068):
` function leveragedPosition( address _swapperAddress, uint256 _borrowAmount, uint256 _initialCollateralAmount, uint256 _amountCollateralOutMin, address[] memory _path )'
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 191-201):
function _deployFirst( bytes32 _saltSeed, bytes memory _configData, bytes memory _immutables, uint256 _maxLTV, uint256 _liquidationFee, uint256 _maturityDate, uint256 _penaltyRate, bool _isBorrowerWhitelistActive, bool _isLenderWhitelistActive )
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 242-248):
function _deploySecond( string memory _name, address _pairAddress, bytes memory _configData, address[] memory _approvedBorrowers, address[] memory _approvedLenders ) private {
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 272-279):
function _logDeploy( string memory _name, address _pairAddress, bytes memory _configData, uint256 _maxLTV, uint256 _liquidationFee, uint256 _maturityDate ) private {
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 310):
function deploy(bytes memory _configData) external returns (address _pairAddress) {
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 355-363):
function deployCustom( string memory _name, bytes memory _configData, uint256 _maxLTV, uint256 _liquidationFee, uint256 _maturityDate, uint256 _penaltyRate, address[] memory _approvedBorrowers, address[] memory _approvedLenders
function globalPause(address[] memory _addresses) external returns (address[] memory _updatedAddresses) {
function returnDataToString(bytes memory data) internal pure returns (string memory) {
function toShares( VaultAccount memory total, uint256 amount, bool roundUp )
function toAmount( VaultAccount memory total, uint256 shares, bool roundUp ) internal pure returns (uint256 amount) {
While this is done at some places, it’s not consistently done in the solution.
I suggest adding a non-zero-value check here):-
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 574):
assetContract.safeTransferFrom(msg.sender, address(this), _amount);
ools
for storage incurs overhead:-File: fraxlend/src/contracts/FraxlendPairHelper.sol (line 33-34):
bool _borrowerWhitelistActive; bool _lenderWhitelistActive;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 78):
mapping(address => bool) public swappers;
File: fraxlend/src/contracts/FraxlendPairCore.sol (line 133-137):
` bool public immutable borrowerWhitelistActive; mapping(address => bool) public approvedBorrowers;
bool public immutable lenderWhitelistActive; mapping(address => bool) public approvedLenders;`
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 94):
mapping(address => bool) public deployedPairCustomStatusByAddress;
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 138):
`bool _isCustom;'
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 32-38):
` mapping(address => bool) public oracleContractWhitelist;
// Interest Rate Calculator Whitelist Storage mapping(address => bool) public rateContractWhitelist;
// Fraxlend Deployer Whitelist Storage mapping(address => bool) public fraxlendDeployerWhitelist;`
block should do something useful, such as emitting an event or
reverting. If the contract is meant to be extended, the contract should
be abstract
and the function signatures be added without
any default implementation. If the block is an empty if-statement block
to avoid doing subsequent checks in the else-if/else conditions, the
else-if/else conditions should be nested under the negation of the
if-statement, because they involve different classes of checks, which
may lead to the introduction of errors when the code is later modified (if(x){}else if(y){...}else{...}
=> if(!x){if(y){...}else{...}})
):-
File: fraxlend/src/contracts/FraxlendPair.sol (line 68):
{}
File: fraxlend/src/contracts/FraxlendPairDeployer.sol (line 406):
} catch {}
File: fraxlend/src/contracts/FraxlendWhitelist.sol (line 40):
constructor() Ownable() {}
File: fraxlend/src/contracts/VariableInterestRate.sol (line 57):
function requireValidInitData(bytes calldata _initData) external pure {}
abi.encode()
is less efficient than abi.encodePacked()
:-File: fraxlend/src/contracts/FraxlendPairCore.sol (line 450-455):
bytes memory _rateData = abi.encode( _currentRateInfo.ratePerSec, _deltaTime, _utilizationRate, block.number - _currentRateInfo.lastBlock );
File: fraxlend/src/contracts/FraxlendPairHelper.sol (line 201-206):
abi.encode( _currentRateInfo.ratePerSec, _timestamp - _currentRateInfo.lastTimestamp, (_totalBorrow.amount * _UTIL_PREC) / _totalAsset.amount, _blockNumber - _currentRateInfo.lastBlock ),
File: fraxlend/src/contracts/LinearInterestRate.sol (line 47):
return abi.encode(MIN_INT, MAX_INT, MAX_VERTEX_UTIL, UTIL_PREC);
File: fraxlend/src/contracts/VariableInterestRate.sol (line 53):
return abi.encode(MIN_UTIL, MAX_UTIL, UTIL_PREC, MIN_INT, MAX_INT, INT_HALF_LIFE);