Fraxlend (Frax Finance) contest - Rolezn'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: 20/120

Findings: 2

Award: $248.17

๐ŸŒŸ Selected for report: 0

๐Ÿš€ Solo Findings: 0

(1) IERC20 approve() Is Deprecated

Severity: Low

approve is subject to a known front-running attack. It is deprecated in favor of safeIncreaseAllowance() and safeDecreaseAllowance(). If only setting the initial allowance to the value that means infinite, safeIncreaseAllowance() can be used instead.

Proof Of Concept

_assetContract.approve(_swapperAddress, _borrowAmount);

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

_collateralContract.approve(_swapperAddress, _collateralToSwap);

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

Consider using safeIncreaseAllowance() / safeDecreaseAllowance() instead.

(2) Use _safeMint instead of _mint

Severity: Low

According to openzepplin's ERC721, the use of _mint is discouraged, use _safeMint whenever possible. https://docs.openzeppelin.com/contracts/3.x/api/token/erc721#ERC721-_mint-address-uint256-

Proof Of Concept

_mint(address(this), _feesShare);

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

_mint(_receiver, _shares);

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

Use _safeMint whenever possible instead of _mint

(3) The Contract Should Approve(0) First

Severity: Low

Some tokens (like USDT L199) do not work when changing the allowance from an existing non-zero allowance value. They must first be approved by zero and then the actual allowance must be approved.

Proof Of Concept

_assetContract.approve(_swapperAddress, _borrowAmount);

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

_collateralContract.approve(_swapperAddress, _collateralToSwap);

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

Approve with a zero amount first before setting the actual amount.

(4) Missing Checks for Address(0x0)

Severity: Low

Proof Of Concept

function setTimeLock(address _newAddress) external onlyOwner {

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

Consider adding zero-address checks in the mentioned codebase.

(5) TransferOwnership Should Be Two Step

Severity: Low

The owner is the authorized user in the solidity contracts. Usually, an owner can be updated with transferOwnership function. However, the process is only completed with single transaction. If the address is updated incorrectly, an owner functionality will be lost forever.

Proof Of Concept

_fraxlendPair.transferOwnership(COMPTROLLER_ADDRESS);

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

Lack of two-step procedure for critical operations leaves them error-prone. Consider adding two step procedure on the critical functions.

(6) Avoid Floating Pragmas: The Version Should Be Locked

Severity: Non-Critical

Proof Of Concept

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPair.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairConstants.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairCore.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairDeployer.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendWhitelist.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/LinearInterestRate.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/VariableInterestRate.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/libraries/SafeERC20.sol#L2

Found usage of floating pragmas ^0.8.15 of Solidity https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/libraries/VaultAccount.sol#L2

(7) Variable Names That Consist Of All Capital Letters Should Be Reserved For Const/immutable Variables

Severity: Non-Critical

If the variable needs to be different based on which class it comes from, a view/pure function should be used instead.

Proof Of Concept

uint256 _LTV_PRECISION,

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

(8) Constants Should Be Defined Rather Than Using Magic Numbers

Severity: Non-Critical

Proof Of Concept

_assetsPerUnitShare = totalAsset.toAmount(1e18, false);

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

uint256 _price = uint256(1e36);

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

bytes memory _firstHalf = BytesLib.slice(_creationCode, 0, 13000);

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

_pairAddress := create2(0, add(bytecode, 32), mload(bytecode), salt)

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

uint256 _deltaUtilization = ((MIN_UTIL - _utilization) * 1e18) / MIN_UTIL;

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

(9) Event Is Missing Indexed Fields

Severity: Non-Critical

Each event should use three indexed fields if there are three or more fields. In addition, each event should have at least one indexed fields to allow easier filtering of logs.

Proof Of Concept

event SetTimeLock(address _oldAddress, address _newAddress);

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

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

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

event SetSwapper(address _swapper, bool _approval);

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

event AddInterest( uint256 _interestEarned, uint256 _rate, uint256 _deltaTime, uint256 _feesAmount, uint256 _feesShare );

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

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

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

event BorrowAsset( address indexed _borrower, address indexed _receiver, uint256 _borrowAmount, uint256 _sharesAdded );

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

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

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

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

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

event Liquidate( address indexed _borrower, uint256 _collateralForLiquidator, uint256 _sharesToLiquidate, uint256 _amountLiquidatorToRepay, uint256 _sharesToAdjust, uint256 _amountToAdjust );

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

event LeveragedPosition( address indexed _borrower, address _swapperAddress, uint256 _borrowAmount, uint256 _borrowShares, uint256 _initialCollateralAmount, uint256 _amountCollateralOut );

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

event RepayAssetWithCollateral( address indexed _borrower, address _swapperAddress, uint256 _collateralToSwap, uint256 _amountAssetOut, uint256 _sharesRepaid );

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

(10) Missing event for critical parameter change

Severity: Non-Critical

When changing state variables events are not emitted. Emitting events allows monitoring activities with off-chain monitoring tools.

Proof Of Concept

function setCreationCode(

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

(11) Implementation contract may not be initialized

OpenZeppelin recommends that the initializer modifier be applied to constructors. Per OZs Post implementation contract should be initialized to avoid potential griefs or exploits. https://forum.openzeppelin.com/t/uupsupgradeable-vulnerability-post-mortem/15680/5

Severity: Non-Critical

Proof Of Concept

contract FraxlendPair is FraxlendPairCore

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

contract FraxlendPairDeployer is Ownable

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

contract FraxlendWhitelist is Ownable

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

contract LinearInterestRate is IRateCalculator

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

contract VariableInterestRate is IRateCalculator

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

(12) Adding A Return Statement When The Function Defines A Named Return Variable, Is Redundant

Severity: Non-Critical

Proof Of Concept

function asset() external view returns (address) {

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

(13) Large multiples of ten should use scientific notation

Severity: Non-Critical

Use (e.g. 1e6) rather than decimal literals (e.g. 100000), for better code readability.

Proof Of Concept

uint256 public DEFAULT_LIQ_FEE = 10000;

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

(14) Use of Block.Timestamp

Severity: Non-Critical

Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.

Proof Of Concept

return maturityDate != 0 && block.timestamp > maturityDate;

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

if (_currentRateInfo.lastTimestamp == block.timestamp) {

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

_currentRateInfo.lastTimestamp = uint64(block.timestamp);

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

uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;

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

if (_exchangeRateInfo.lastTimestamp == block.timestamp) {

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

_exchangeRateInfo.lastTimestamp = uint32(block.timestamp);

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

if (block.timestamp > _deadline) revert PastDeadline(block.timestamp, _deadline);

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

block.timestamp

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

Block timestamps should not be used for entropy or generating random numbersโ€”i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.

Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.

(1) Abi.encode() Is Less Efficient Than Abi.encodepacked()

Severity: Gas Optimizations

Proof Of Concept

bytes memory _rateData = abi.encode(

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

abi.encode(

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

abi.encode(CIRCUIT_BREAKER_ADDRESS, COMPTROLLER_ADDRESS, TIME_LOCK_ADDRESS, FRAXLEND_WHITELIST_ADDRESS),

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

abi.encode(CIRCUIT_BREAKER_ADDRESS, COMPTROLLER_ADDRESS, TIME_LOCK_ADDRESS, FRAXLEND_WHITELIST_ADDRESS),

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

return abi.encode(MIN_INT, MAX_INT, MAX_VERTEX_UTIL, UTIL_PREC);

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

return abi.encode(MIN_UTIL, MAX_UTIL, UTIL_PREC, MIN_INT, MAX_INT, INT_HALF_LIFE);

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

(2) <Array>.length Should Not Be Looked Up In Every Loop Of A For-loop

Severity: Gas Optimizations

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

Proof Of Concept

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

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

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

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

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

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

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

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

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

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

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

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

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

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

(3) Using Calldata Instead Of Memory For Read-only Arguments In External Functions Saves Gas

Severity: Gas Optimizations

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Structs have the same overhead as an array of length one

Proof Of Concept

address[] memory _path

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

function getAllPairAddresses() external view returns (address[] memory) {

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

returns (PairCustomStatus[] memory _pairCustomStatuses)

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

address[] memory _approvedBorrowers, address[] memory _approvedLenders

https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairDeployer.sol#L246-L247

address[] memory _approvedBorrowers, address[] memory _approvedLenders

https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairDeployer.sol#L362-L363

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

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

(4) Empty Blocks Should Be Removed Or Emit Something

Severity: Gas Optimizations

The code should be refactored such that they no longer exist, or the 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{...}})

Proof Of Concept

} catch {}

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

function requireValidInitData(bytes calldata _initData) external pure {}

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

(5) ++i Costs Less Gas Than i++, Especially When Itโ€™s Used In For-loops (--i/i-- Too)

Severity: Gas Optimizations

Saves 6 gas per loop

Proof Of Concept

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

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

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

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

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

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

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

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

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

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

For example, use ++i instead of i++

(6) It Costs More Gas To Initialize Variables To Zero Than To Let The Default Of Zero Be Applied

Severity: Gas Optimizations

Proof Of Concept

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

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

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

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

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

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

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

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

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

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

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

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

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

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

(7) Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate

Severity: Gas Optimizations

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.

Proof Of Concept

mapping(address => bool) public swappers; mapping(address => bool) public approvedLenders;

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

mapping(address => bool) public oracleContractWhitelist; mapping(address => bool) public rateContractWhitelist; mapping(address => bool) public fraxlendDeployerWhitelist;

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

(8) <x> += <y> Costs More Gas Than <x> = <x> + <y> For State Variables

Severity: Gas Optimizations

Proof Of Concept

_totalAsset.amount -= uint128(_amountToTransfer);

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

_totalAsset.shares -= _shares;

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

_totalBorrow.amount += uint128(_interestEarned);

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

_totalAsset.amount += uint128(_interestEarned);

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

_totalAsset.shares += uint128(_feesShare);

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

_totalAsset.amount += _amount;

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

_totalAsset.shares += _shares;

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

_totalAsset.amount -= _amountToReturn;

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

_totalAsset.shares -= _shares;

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

_totalBorrow.amount += _borrowAmount;

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

_totalBorrow.shares += uint128(_sharesAdded);

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

userBorrowShares[msg.sender] += _sharesAdded;

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

userCollateralBalance[_borrower] += _collateralAmount;

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

totalCollateral += _collateralAmount;

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

userCollateralBalance[_borrower] -= _collateralAmount;

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

totalCollateral -= _collateralAmount;

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

_totalBorrow.amount -= _amountToRepay;

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

_totalBorrow.shares -= _shares;

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

userBorrowShares[_borrower] -= _shares;

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

totalAsset.amount -= _amountToAdjust;

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

_totalBorrow.amount -= _amountToAdjust;

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

_totalBorrow.shares -= _sharesToAdjust;

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

(9) Public Functions To External

Severity: Gas Optimizations

The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.

Proof Of Concept

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

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

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

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

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

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

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

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

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

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

function requireValidInitData(bytes calldata _initData) public pure {

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

(10) require()/revert() Strings Longer Than 32 Bytes Cost Extra Gas

Severity: Gas Optimizations

Proof Of Concept

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

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

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

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

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

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

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

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

(11) uint64 state variable is less efficient than uint256

Severity: Gas Optimizations

Lower than uint256 size storage variables are less gas efficient. Using uint64 does not give any efficiency, actually, it is the opposite as EVM operates on default of 256-bit values so uint64 is more expensive in this case as it needs a conversion. It only gives improvements in cases where you can pack variables together, e.g. structs.

Proof Of Concept

uint64 internal constant DEFAULT_INT = 158247046;

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

uint16 internal constant DEFAULT_PROTOCOL_FEE = 0;

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

uint64 lastBlock;

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

uint64 feeToProtocolRate;

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

uint64 lastTimestamp;

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

uint64 ratePerSec;

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

uint32 lastTimestamp;

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

uint224 exchangeRate;

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

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

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

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

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

uint128 _sharesToAdjust;

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

uint128 _amountToAdjust;

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

uint128 _leftoverBorrowShares = _borrowerShares - _sharesToLiquidate;

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

uint32 private constant MIN_UTIL = 75000;

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

uint32 private constant MAX_UTIL = 85000;

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

uint32 private constant UTIL_PREC = 1e5;

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

uint64 private constant MIN_INT = 79123523;

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

uint64 private constant MAX_INT = 146248508681;

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

uint8 i = 0;

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

uint128 amount;

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

uint128 shares;

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

(12) ++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

Severity: Gas Optimizations

The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP

Proof Of Concept

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

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

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

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

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

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

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

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

for (i = 0; i < _lengthOfArray; ) {

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

for (i = 0; i < _lengthOfArray; ) {

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

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

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

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

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

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

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

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

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

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

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

(13) Use assembly to check for address(0)

Severity: Gas Optimizations

Save 6 gas per instance if using assembly to check for address(0)

e.g. assembly { if iszero(_addr) { mstore(0x00, "AddressZero") revert(0x00, 0x20) } }

Proof Of Concept

function setTimeLock(address _newAddress) external onlyOwner {

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

(14) Using Bools For Storage Incurs Overhead

Severity: Gas Optimizations

Booleans are more expensive than uint256 or any type that takes up a full word because each write operation emits an extra SLOAD to first read the slot's contents, replace the bits taken up by the boolean, and then write back. This is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled.

Proof Of Concept

mapping(address => bool) public swappers;

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

bool public immutable borrowerWhitelistActive; https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairCore.sol#L133

mapping(address => bool) public approvedBorrowers;

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

bool public immutable lenderWhitelistActive; https://github.com/code-423n4/2022-08-frax/tree/main/src/contracts/FraxlendPairCore.sol#L136

mapping(address => bool) public approvedLenders;

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

mapping(address => bool) public deployedPairCustomStatusByAddress;

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

mapping(address => bool) public oracleContractWhitelist;

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

mapping(address => bool) public rateContractWhitelist;

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

mapping(address => bool) public fraxlendDeployerWhitelist;

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

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