Fraxlend (Frax Finance) contest - gogo's results

Fraxlend: A permissionless lending platform and the final piece of the Frax Finance Defi Trinity.

General Information

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

Frax Finance

Findings Distribution

Researcher Performance

Rank: 38/120

Findings: 2

Award: $69.44

๐ŸŒŸ Selected for report: 0

๐Ÿš€ Solo Findings: 0

2022-08-FRAX

Low Risk and Non-Critical Issues

Adding a return statement when the function defines a named return variable, is redundant

There is 1 instance of this issue:

File: src/contracts/FraxlendPairDeployer.sol

191:  function _deployFirst(
201:  ) private returns (address _pairAddress) {
233:      return _pairAddress;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

Events not emmited on important state changes

Emmiting events is recommended each time when a state variable's value is being changed or just some critical event for the contract has occurred. It also helps off-chain monitoring of the contract's state.

There are 2 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

245:  function initialize(

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

170:  function setCreationCode(bytes calldata _creationCode) external onlyOwner {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

public functions not called by the contract should be declared external instead

There are 5 instances of this issue:

File: src/contracts/FraxlendPair.sol

74:   function name() public view override(ERC20, IERC20Metadata) returns (string memory) {

78:   function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) {

84:   function decimals() public pure override(ERC20, IERC20Metadata) returns (uint8) {

89:   function totalSupply() public view override(ERC20, IERC20) returns (uint256) {

100:  function totalAssets() public view virtual returns (uint256) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

Non-library/interface files should use fixed compiler versions, not floating ones

There are 7 instances of this issue:

File: src/contracts/FraxlendPair.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairConstants.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairConstants.sol

File: src/contracts/FraxlendPairCore.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/FraxlendWhitelist.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

File: src/contracts/LinearInterestRate.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

File: src/contracts/VariableInterestRate.sol

2:    pragma solidity ^0.8.15;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/VariableInterestRate.sol

Event is missing indexed fields

Each event that contains more than 2 fields should have 3 fields marked as indexed There are 4 instances of this issue:

File: src/contracts/FraxlendPair.sol

event      WithdrawFees(uint128 _shares, address _recipient, uint256 _amountToTransfer)

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairCore.sol

event      UpdateRate(uint256 _ratePerSec, uint256 _deltaTime, uint256 _utilizationRate, uint256 _newRatePerSec)

event      AddCollateral(address indexed _sender, address indexed _borrower, uint256 _collateralAmount)

event      RepayAsset(address indexed _sender, address indexed _borrower, uint256 _amountToRepay, uint256 _shares)

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

Not used import

There are 6 instances of this issue:

File: src/contracts/FraxlendPair.sol

30:   import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

31:   import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

36:   import "./interfaces/IERC4626.sol";

37:   import "./interfaces/IFraxlendWhitelist.sol";

38:   import "./interfaces/IRateCalculator.sol";

39:   import "./interfaces/ISwapper.sol";

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

2022-08-FRAX

Gas Optimizations Report

The usage of ++i will cost less gas than i++. The same change can be applied to i-- as well.

This change would save up to 6 gas per instance/loop.

There are 9 instances of this issue:

File: src/contracts/FraxlendPair.sol

289:  for (uint256 i = 0; i < _lenders.length; i++) {

308:  for (uint256 i = 0; i < _borrowers.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairDeployer.sol

130:  i++;

158:  i++;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/FraxlendWhitelist.sol

51:   for (uint256 i = 0; i < _addresses.length; i++) {

66:   for (uint256 i = 0; i < _addresses.length; i++) {

81:   for (uint256 i = 0; i < _addresses.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

File: src/contracts/libraries/SafeERC20.sol

24:   i++;

27:   for (i = 0; i < 32 && data[i] != 0; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

State variables should be cached in stack variables rather than re-reading them.

The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.

There are 5 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

      /// @audit Cache `oracleMultiply`. Used 3 times in `_updateExchangeRate`
523:  if (oracleMultiply != address(0)) {
524:  (, int256 _answer, , , ) = AggregatorV3Interface(oracleMultiply).latestRoundData();
526:  revert OracleLTEZero(oracleMultiply);

      /// @audit Cache `oracleDivide`. Used 3 times in `_updateExchangeRate`
531:  if (oracleDivide != address(0)) {
532:  (, int256 _answer, , , ) = AggregatorV3Interface(oracleDivide).latestRoundData();
534:  revert OracleLTEZero(oracleDivide);

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/LinearInterestRate.sol

      /// @audit Cache `UTIL_PREC`. Used 4 times in `getNewRate`
84:   uint256 _slope = ((_vertexInterest - _minInterest) * UTIL_PREC) / _vertexUtilization;
85:   _newRatePerSec = uint64(_minInterest + ((_utilization * _slope) / UTIL_PREC));
87:   uint256 _slope = (((_maxInterest - _vertexInterest) * UTIL_PREC) / (UTIL_PREC - _vertexUtilization));
88:   _newRatePerSec = uint64(_vertexInterest + (((_utilization - _vertexUtilization) * _slope) / UTIL_PREC));

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

File: src/contracts/VariableInterestRate.sol

      /// @audit Cache `INT_HALF_LIFE`. Used 4 times in `requireValidInitData`
70:   uint256 _decayGrowth = INT_HALF_LIFE + (_deltaUtilization * _deltaUtilization * _deltaTime);
71:   _newRatePerSec = uint64((_currentRatePerSec * INT_HALF_LIFE) / _decayGrowth);
77:   uint256 _decayGrowth = INT_HALF_LIFE + (_deltaUtilization * _deltaUtilization * _deltaTime);
78:   _newRatePerSec = uint64((_currentRatePerSec * _decayGrowth) / INT_HALF_LIFE);

      /// @audit Cache `INT_HALF_LIFE`. Used 4 times in `getNewRate`
70:   uint256 _decayGrowth = INT_HALF_LIFE + (_deltaUtilization * _deltaUtilization * _deltaTime);
71:   _newRatePerSec = uint64((_currentRatePerSec * INT_HALF_LIFE) / _decayGrowth);
77:   uint256 _decayGrowth = INT_HALF_LIFE + (_deltaUtilization * _deltaUtilization * _deltaTime);
78:   _newRatePerSec = uint64((_currentRatePerSec * _decayGrowth) / INT_HALF_LIFE);

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/VariableInterestRate.sol

Using != 0 on uints costs less gas than > 0.

This change saves 3 gas per instance/loop

There are 6 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

477:  if (_currentRateInfo.feeToProtocolRate > 0) {

754:  if (_collateralAmount > 0) {

1002: if (_leftoverBorrowShares > 0) {

1094: if (_initialCollateralAmount > 0) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/LinearInterestRate.sol

66:   _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0,

67:   "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0"

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

It costs more gas to initialize non-constant/non-immutable variables to zero than to let the default of zero be applied

Not overwriting the default for stack variables saves 8 gas. Storage and memory variables have larger savings

There are 9 instances of this issue:

File: src/contracts/FraxlendPair.sol

289:  for (uint256 i = 0; i < _lenders.length; i++) {

308:  for (uint256 i = 0; i < _borrowers.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairCore.sol

265:  for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {

270:  for (uint256 i = 0; i < _approvedLenders.length; ++i) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

402:  for (uint256 i = 0; i < _lengthOfArray; ) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/FraxlendWhitelist.sol

51:   for (uint256 i = 0; i < _addresses.length; i++) {

66:   for (uint256 i = 0; i < _addresses.length; i++) {

81:   for (uint256 i = 0; i < _addresses.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

File: src/contracts/libraries/SafeERC20.sol

22:   uint8 i = 0;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

Functions that are access-restricted from most users may be marked as payable

Marking a function as payable reduces gas cost since the compiler does not have to check whether a payment was provided or not. This change will save around 21 gas per function call.

There are 8 instances of this issue:

File: src/contracts/FraxlendPair.sol

204:  function setTimeLock(address _newAddress) external onlyOwner {

234:  function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {

274:  function setSwapper(address _swapper, bool _approval) external onlyOwner {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairDeployer.sol

170:  function setCreationCode(bytes calldata _creationCode) external onlyOwner {

398:  function globalPause(address[] memory _addresses) external returns (address[] memory _updatedAddresses) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/FraxlendWhitelist.sol

50:   function setOracleContractWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {

65:   function setRateContractWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {

80:   function setFraxlendDeployerWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

<array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop \ - storage arrays incur a Gwarmaccess (100 gas) \ - memory arrays use MLOAD (3 gas) \ - calldata arrays use CALLDATALOAD (3 gas) \ \ Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

There are 7 instances of this issue:

File: src/contracts/FraxlendPair.sol

289:  for (uint256 i = 0; i < _lenders.length; i++) {

308:  for (uint256 i = 0; i < _borrowers.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairCore.sol

265:  for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {

270:  for (uint256 i = 0; i < _approvedLenders.length; ++i) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendWhitelist.sol

51:   for (uint256 i = 0; i < _addresses.length; i++) {

66:   for (uint256 i = 0; i < _addresses.length; i++) {

81:   for (uint256 i = 0; i < _addresses.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

++i/i++ should be unchecked{++I}/unchecked{I++} in for-loops

When an increment or any arithmetic operation is not possible to overflow it should be placed in unchecked{} block. \This is because of the default compiler overflow and underflow safety checks since Solidity version 0.8.0. \In for-loops it saves around 30-40 gas per loop

There are 8 instances of this issue:

File: src/contracts/FraxlendPair.sol

289:  for (uint256 i = 0; i < _lenders.length; i++) {

308:  for (uint256 i = 0; i < _borrowers.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairCore.sol

265:  for (uint256 i = 0; i < _approvedBorrowers.length; ++i) {

270:  for (uint256 i = 0; i < _approvedLenders.length; ++i) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendWhitelist.sol

51:   for (uint256 i = 0; i < _addresses.length; i++) {

66:   for (uint256 i = 0; i < _addresses.length; i++) {

81:   for (uint256 i = 0; i < _addresses.length; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendWhitelist.sol

File: src/contracts/libraries/SafeERC20.sol

27:   for (i = 0; i < 32 && data[i] != 0; i++) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

<x> += <y> costs more gas than <x> = <x> + <y> for state variables

There are 6 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

773:  totalCollateral += _collateralAmount;

815:  totalCollateral -= _collateralAmount;

772:  userCollateralBalance[_borrower] += _collateralAmount;

813:  userCollateralBalance[_borrower] -= _collateralAmount;

724:  userBorrowShares[msg.sender] += _sharesAdded;

867:  userBorrowShares[_borrower] -= _shares;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

'When using elements that are smaller than 32 bytes, your contractโ€™s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.' \ https://docs.soliditylang.org/en/v0.8.15/internals/layout_in_storage.html \ Use a larger size then downcast where needed

There are 39 instances of this issue:

File: src/contracts/FraxlendPair.sol

165:  uint64 _DEFAULT_INT,

166:  uint16 _DEFAULT_PROTOCOL_FEE,

211:  event ChangeFee(uint32 _newFee);

215:  function changeFee(uint32 _newFee) external whenNotPaused {

228:  event WithdrawFees(uint128 _shares, address _recipient, uint256 _amountToTransfer);

234:  function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPair.sol

File: src/contracts/FraxlendPairConstants.sol

41:   uint64 internal constant DEFAULT_INT = 158247046;

47:   uint16 internal constant DEFAULT_PROTOCOL_FEE = 0;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairConstants.sol

File: src/contracts/FraxlendPairCore.sol

106:  uint64 lastBlock;

107:  uint64 feeToProtocolRate;

108:  uint64 lastTimestamp;

109:  uint64 ratePerSec;

116:  uint32 lastTimestamp;

400:  uint64 _newRate

415:  uint64 _newRate

561:  uint128 _amount,

562:  uint128 _shares,

625:  uint128 _amountToReturn,

626:  uint128 _shares,

707:  function _borrowAsset(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) {

857:  uint128 _amountToRepay,

858:  uint128 _shares,

951:  uint128 _sharesToLiquidate,

967:  uint128 _borrowerShares = userBorrowShares[_borrower].toUint128();

993:  uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_sharesToLiquidate, true)).toUint128();

997:  uint128 _sharesToAdjust;

998:  uint128 _amountToAdjust;

1001: uint128 _leftoverBorrowShares = _borrowerShares - _sharesToLiquidate;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/libraries/SafeERC20.sol

22:   uint8 i = 0;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

File: src/contracts/libraries/VaultAccount.sol

5:    uint128 amount;

6:    uint128 shares;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/VaultAccount.sol

File: src/contracts/LinearInterestRate.sol

76:   function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

File: src/contracts/VariableInterestRate.sol

35:   uint32 private constant MIN_UTIL = 75000;

36:   uint32 private constant MAX_UTIL = 85000;

37:   uint32 private constant UTIL_PREC = 1e5;

40:   uint64 private constant MIN_INT = 79123523;

41:   uint64 private constant MAX_INT = 146248508681;

63:   function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec) {

64:   (uint64 _currentRatePerSec, uint256 _deltaTime, uint256 _utilization, ) = abi.decode(

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/VariableInterestRate.sol

Splitting require() statements that use && saves gas

Instead of using && on single require check using two require checks can save gas

There are 3 instances of this issue:

File: src/contracts/LinearInterestRate.sol

57:   require(
58:       _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT,
59:       "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT"
60:   );

61:   require(
62:       _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT,
63:       "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT"
64:   );

65:   require(
66:       _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0,
67:       "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0"
68:   );

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

Use calldata instead of memory for function parameters

If a reference type function parameter is read-only, it is cheaper in gas to use calldata instead of memory. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. Try to use calldata as a data location because it will avoid copies and also makes sure that the data cannot be modified.

There are 23 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

      /// @audit Store `_totalAsset` in calldata.
295:  function _totalAssetAvailable(VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow)

      /// @audit Store `_totalBorrow` in calldata.
295:  function _totalAssetAvailable(VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow)

560:  VaultAccount memory _totalAsset,

624:  VaultAccount memory _totalAsset,

856:  VaultAccount memory _totalBorrow,

1067: address[] memory _path

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

193:  bytes memory _configData,

194:  bytes memory _immutables,

243:  string memory _name,

245:  bytes memory _configData,

246:  address[] memory _approvedBorrowers,

247:  address[] memory _approvedLenders

273:  string memory _name,

275:  bytes memory _configData,

310:  function deploy(bytes memory _configData) external returns (address _pairAddress) {

356:  string memory _name,

357:  bytes memory _configData,

362:  address[] memory _approvedBorrowers,

363:  address[] memory _approvedLenders

398:  function globalPause(address[] memory _addresses) external returns (address[] memory _updatedAddresses) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/libraries/SafeERC20.sol

18:   function returnDataToString(bytes memory data) internal pure returns (string memory) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

File: src/contracts/libraries/VaultAccount.sol

17:   VaultAccount memory total,

34:   VaultAccount memory total,

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/VaultAccount.sol

Replace x <= y with x < y + 1, and x >= y with x > y - 1

In the EVM, there is no opcode for >= or <=. When using greater than or equal, two operations are performed: > and =. Using strict comparison operators hence saves gas

There are 12 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

196:  if (_maxLTV >= LTV_PRECISION && !_isBorrowerWhitelistActive) revert BorrowerWhitelistRequired();

315:  return _ltv <= maxLTV;

472:  _interestEarned + _totalBorrow.amount <= type(uint128).max &&

473:  _interestEarned + _totalAsset.amount <= type(uint128).max

525:  if (_answer <= 0) {

533:  if (_answer <= 0) {

988:  _collateralForLiquidator = _leftoverCollateral <= 0

1000: if (_leftoverCollateral <= 0) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

365:  require(_maxLTV <= GLOBAL_MAX_LTV, "FraxlendPairDeployer: _maxLTV is too large");

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/libraries/SafeERC20.sol

19:   if (data.length >= 64) {

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/libraries/SafeERC20.sol

File: src/contracts/LinearInterestRate.sol

58:   _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT,

62:   _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT,

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

Use immutable & constant for state variables that do not change their value

There are 5 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

86:   address public TIME_LOCK_ADDRESS;
      
92:   string internal nameOfContract;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

File: src/contracts/FraxlendPairDeployer.sol

57:   address public CIRCUIT_BREAKER_ADDRESS;

58:   address public COMPTROLLER_ADDRESS;

59:   address public TIME_LOCK_ADDRESS;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

require()/revert() strings longer than 32 bytes cost extra gas

Each extra memory word of bytes past the original 32 incurs an MSTORE which costs 3 gas

There are 8 instances of this issue:

File: src/contracts/FraxlendPairDeployer.sol

205:  require(deployedPairsBySalt[salt] == address(0), "FraxlendPairDeployer: Pair already deployed");

228:  require(_pairAddress != address(0), "FraxlendPairDeployer: create2 failed");

253:  require(deployedPairsByName[_name] == address(0), "FraxlendPairDeployer: Pair name must be unique");

365:  require(_maxLTV <= GLOBAL_MAX_LTV, "FraxlendPairDeployer: _maxLTV is too large");

366:  require(
367:      IFraxlendWhitelist(FRAXLEND_WHITELIST_ADDRESS).fraxlendDeployerWhitelist(msg.sender),
368:      "FraxlendPairDeployer: Only whitelisted addresses"
369:  );

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/LinearInterestRate.sol

57:   require(
58:       _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT,
59:       "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT"
60:   );

61:   require(
62:       _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT,
63:       "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT"
64:   );

65:   require(
66:       _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0,
67:       "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0"
68:   );

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

Use custom errors rather than revert()/require() strings to save gas

Custom errors are available from solidity version 0.8.4. Custom errors save ~50 gas each time they're hitby avoiding having to allocate and store the revert string. Not defining the strings also save deployment gas

There are 9 instances of this issue:

File: src/contracts/FraxlendPairDeployer.sol

205:  require(deployedPairsBySalt[salt] == address(0), "FraxlendPairDeployer: Pair already deployed");

228:  require(_pairAddress != address(0), "FraxlendPairDeployer: create2 failed");

253:  require(deployedPairsByName[_name] == address(0), "FraxlendPairDeployer: Pair name must be unique");

365:  require(_maxLTV <= GLOBAL_MAX_LTV, "FraxlendPairDeployer: _maxLTV is too large");

366:  require(
367:      IFraxlendWhitelist(FRAXLEND_WHITELIST_ADDRESS).fraxlendDeployerWhitelist(msg.sender),
368:      "FraxlendPairDeployer: Only whitelisted addresses"
369:  );

399:  require(msg.sender == CIRCUIT_BREAKER_ADDRESS, "Circuit Breaker only");

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairDeployer.sol

File: src/contracts/LinearInterestRate.sol

57:   require(
58:       _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT,
59:       "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT"
60:   );

61:   require(
62:       _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT,
63:       "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT"
64:   );

65:   require(
66:       _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0,
67:       "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0"
68:   );

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/LinearInterestRate.sol

Using bools for storage variables incurs overhead

Use uint256(1) and uint256(2) for true/false to avoid a Gwarmaccess (100 gas) for the extra SLOAD, and to avoid Gsset (20000 gas) when changing from 'false' to 'true', after having been 'true' in the past

There are 2 instances of this issue:

File: src/contracts/FraxlendPairCore.sol

133:  bool public immutable borrowerWhitelistActive;

136:  bool public immutable lenderWhitelistActive;

https://github.com/code-423n4/2022-08-frax/blob/main/src/contracts/FraxlendPairCore.sol

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax ยฉ 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter