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: 94/120
Findings: 1
Award: $21.24
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xA5DF, 0xDjango, 0xNazgul, 0xSmartContract, 0xackermann, 0xbepresent, 0xc0ffEE, 0xkatana, 2997ms, Amithuddar, Aymen0909, Bnke0x0, Chinmay, Chom, CodingNameKiki, Deivitto, Diraco, Dravee, ElKu, EthLedger, Fitraldys, Funen, IgnacioB, JC, Junnon, Lambda, LeoS, Metatron, MiloTruck, Noah3o6, NoamYakov, PaludoX0, Randyyy, ReyAdmirado, Rohan16, Rolezn, Ruhum, SaharAP, Sm4rty, SooYa, TomJ, Tomio, Waze, Yiko, _Adam, __141345__, a12jmx, ajtra, ak1, asutorufos, ballx, brgltd, c3phas, cRat1st0s, carlitox477, chrisdior4, d3e4, delfin454000, dharma09, djxploit, durianSausage, erictee, fatherOfBlocks, find_a_bug, flyx, francoHacker, gerdusx, gogo, gzeon, hakerbaya, ignacio, jag, kyteg, ladboy233, ltyu, m_Rassska, medikko, mics, mrpathfindr, newfork01, nxrblsrpr, oyc_109, pfapostol, rbserver, reassor, ret2basic, robee, sach1r0, saian, simon135, sryysryy, zeesaw
21.2361 USDC - $21.24
unchecked{++i}
instead of i++
in loops ✅a = a + b
instead of a += b
✅Internal
functions can be inlined ✅private/internal
for constants/immutable
variables instead of public
✅uint
instead of uint8
, uint64
, if it's possible ✅calldataload
instead of mload
✅immutable/const
✅i++
, the compiler has to to create a temp variable to return i
(if there's a need) and then i
gets incremented.++i
, the compiler just simply returns already incremented value.Contracts:
file: src/contracts/FraxlendPair.sol .................................... ----------- // Origin // ----------- // Lines: [289-289] for (uint256 i = 0; i < _lenders.length; i++) {} // Lines: [308-308] for (uint256 i = 0; i < _borrowers.length; i++) {} ----------- // Updated // ----------- // Lines: [289-289] for (uint256 i = 0; i < _lenders.length; ++i) {} // Lines: [308-308] for (uint256 i = 0; i < _borrowers.length; ++i) {}
Also occured in the following files:
unchecked{++i};
instead of i++;
/++i;
in loops<a name="G-02"></a>Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default Therefore, unchecked
box can be used to prevent all unnecessary checks, if it's no a way to get a reversion.
There are ~1e80 atoms in the universe, so 2^256 is closed to that number, therefore it's no a way to be overflowed, because of the gas limit as well.
Contracts:
file: src/contracts/FraxlendPair.sol .................................... ----------- // Origin // ----------- // Lines: [289-289] for (uint256 i = 0; i < _lenders.length; i++) {} // Lines: [308-308] for (uint256 i = 0; i < _borrowers.length; i++) {} ----------- // Updated // ----------- // Lines: [289-289] for (uint256 i = 0; i < _lenders.length;) { // some logic unchecked { ++i; } } // Lines: [308-308] for (uint256 i = 0; i < _borrowers.length; ++i) { // some logic unchecked { ++i; } }
Also occured in the following files:
a = a + b
instead of a += b
<a name="G-03"></a>Contracts:
file: src/contracts/FraxlendPairCore.sol ......................................... ----------- // Origin // ----------- // Lines: [475-476] _totalBorrow.amount += uint128(_interestEarned); _totalAsset.amount += uint128(_interestEarned); // Lines: [645-646] _totalAsset.amount -= _amountToReturn; _totalAsset.shares -= _shares; ----------- // Updated // ----------- // Lines: [475-476] _totalBorrow.amount = _totalBorrow.amount + uint128(_interestEarned); _totalAsset.amount = _totalAsset.amount + uint128(_interestEarned); // Lines: [645-646] _totalAsset.amount = _totalAsset.amount - _amountToReturn; _totalAsset.shares =_totalAsset.shares - _shares;
Also occured in the following files:
onlyOwner
modifier will be reverted, if the user who is not an owner invokes following methods.Contracts:
file: src/contracts/FraxlendPair.sol ......................................... ----------- // Origin // ----------- // Lines: [475-476] function setTimeLock(address _newAddress) external onlyOwner { emit SetTimeLock(TIME_LOCK_ADDRESS, _newAddress); TIME_LOCK_ADDRESS = _newAddress; } ----------- // Updated // ----------- // Lines: [475-476] function setTimeLock(address _newAddress) external payable onlyOwner { emit SetTimeLock(TIME_LOCK_ADDRESS, _newAddress); TIME_LOCK_ADDRESS = _newAddress; } ```
Also occured in the following files:
src/contracts/FraxlendPairDeployer.sol
src/contracts/FraxlendWhitelist.sol
Internal
functions can be inlined<a name="G-07"></a>JUMP
s to find a function and also the gas consumption for pushing arguments into memory and etc.Contracts:
file: src/contracts/FraxlendPairCore.sol .................................... // Lines: [295-301] function _totalAssetAvailable(VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow) internal pure returns (uint256) { return _totalAsset.amount - _totalBorrow.amount; }
private/internal
for constants/immutable/state
variables instead of public
<a name="G-08"></a>public
instance.Contracts:
file: src/contracts/FraxlendWhitelist.sol ......................................... ----------- // Origin // ----------- // Lines: [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; ----------- // Updated // ----------- // Lines: [32-38] mapping(address => bool) internal oracleContractWhitelist; // Interest Rate Calculator Whitelist Storage mapping(address => bool) internal rateContractWhitelist; // Fraxlend Deployer Whitelist Storage mapping(address => bool) internal fraxlendDeployerWhitelist;
Also occured in the following files:
uint
instead of uint8
, uint64
, if it's possible<a name="G-12"></a>I deployed a contract with only a single error and function and compared between:
error VaultAlreadyUnderAuction(bytes12 vaultId, address witch);
.error VaultAlreadyUnderAuction(bytes32 vaultId, address witch);
.The second one is saving about ~10k units of gas for deployment and 10k per each transaction.
However, defaining further errors with !bytes32
doesn't give a huge optimization.
The stack slots are 32bytes, just use 32bytes, if you are not trying to tight up the storage.
In this case, it's totally fine and it's great that you're tighting up the structs:
struct CurrentRateInfo { uint64 lastBlock; uint64 feeToProtocolRate; // Fee amount 1e5 precision uint64 lastTimestamp; uint64 ratePerSec; }
Contracts:
file: src/contracts/FraxlendPairConstants.sol ............................................. // Lines: [41-41] uint64 internal constant DEFAULT_INT = 158247046; // 0.5% annual rate 1e18 precision // Lines: [47-47] uint16 internal constant DEFAULT_PROTOCOL_FEE = 0; // 1e5 precision // Update to: // Lines: [41-41] uint internal constant DEFAULT_INT = 158247046; // 0.5% annual rate 1e18 precision // Lines: [47-47] uint internal constant DEFAULT_PROTOCOL_FEE = 0; // 1e5 precision ```
Also occured in the following files:
calldataload
instead of mload
<a name="G-14"></a>calldataload
, or replace memory
with calldata
. If the args are pretty huge, allocating args in memory will cost a lot.Contracts:
file: src/contracts/FraxlendPair.sol ..................................... ----------- // Origin // ----------- // Lines: [45-68] constructor( bytes memory _configData, bytes memory _immutables, uint256 _maxLTV, uint256 _liquidationFee, uint256 _maturityDate, uint256 _penaltyRate, bool _isBorrowerWhitelistActive, bool _isLenderWhitelistActive ) ----------- // Updated // ----------- // Lines: [45-68] constructor( bytes calldata _configData, bytes calldata _immutables, uint256 _maxLTV, uint256 _liquidationFee, uint256 _maturityDate, uint256 _penaltyRate, bool _isBorrowerWhitelistActive, bool _isLenderWhitelistActive )
Also occured in the following files:
immutable/const
<a name="G-15"></a>SSTORE_SET_GAS
after Berlin hard fork costs around 22100 units of gas, therefore to get a huge optimization it's possible to define the following state variables as immutable/const
.SLOAD
s, since they are consts/immutable variables, which also saves sagnificant amount of gas.Contracts:
file: src/contracts/FraxlendPairCore.sol ........................................ ----------- // Origin // ----------- // Lines: [51-51] string public version = "1.0.0"; // Lines: [75-75] bytes public rateInitCallData; // Optional extra data from init function to be passed to rate calculator // Lines: [92-92] string internal nameOfContract; ----------- // Updated // ----------- // Lines: [51-51] string public immutable/const?? version = "1.0.0"; // Lines: [75-75] bytes public immutable rateInitCallData; // Optional extra data from init function to be passed to rate calculator // Lines: [92-92] string internal immutable nameOfContract; ```
Contracts:
file: src/contracts/LinearInterestRate.sol .......................................... ----------- // Origin // ----------- // Lines: [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" ); ----------- // Updated // ----------- // declaring errors // Lines: [57-68] if (_minInterest >= MAX_INT || _minInterest > _vertexInterest || _minInterest < MIN_INT) {revert C9(args)} // etc.. // And then in a comments you can write something like: C9 is "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT" C10 is "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT" // etc... ```
Also occured in the following files:
Contracts:
file: src/contracts/FraxlendPairDeployer.sol ............................................ // Lines: [407-409] // Original unchecked { i = i + 1; } // Updated unchecked { ++i; }