20,000 USDC
View results
Submission Details
Severity: gas
Valid

Gas Optimizations

Issue Instances
GAS-1 Cache array length outside of loop 6
GAS-2 For Operations that will not overflow, you could use unchecked 142
GAS-3 Use Custom Errors 2
GAS-4 Don't initialize variables with default value 8
GAS-5 Functions guaranteed to revert when called by normal users can be marked payable 5
GAS-6 ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too) 6
GAS-7 Using private rather than public for constants, saves gas 3
GAS-8 Use != 0 instead of > 0 for unsigned integer comparison 5

[GAS-1] Cache array length outside of loop

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

Instances (6):

File: Lender.sol
233: for (uint256 i = 0; i < borrows.length; i++) {
293: for (uint256 i = 0; i < loanIds.length; i++) {
359: for (uint256 i = 0; i < loanIds.length; i++) {
438: for (uint256 i = 0; i < loanIds.length; i++) {
549: for (uint256 i = 0; i < loanIds.length; i++) {
592: for (uint256 i = 0; i < refinances.length; i++) {

[GAS-2] For Operations that will not overflow, you could use unchecked

Instances (142):

File: Beedle.sol
4: import {Ownable} from "./utils/Ownable.sol";
4: import {Ownable} from "./utils/Ownable.sol";
5: import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5: import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5: import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5: import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
5: import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
6: import {ERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
7: import {ERC20Votes} from "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sol";
12: _mint(msg.sender, 1_000_000_000 * 1e18);
File: Fees.sol
4: import "./utils/Errors.sol";
4: import "./utils/Errors.sol";
5: import "./utils/Structs.sol";
5: import "./utils/Structs.sol";
7: import {IERC20} from "./interfaces/IERC20.sol";
7: import {IERC20} from "./interfaces/IERC20.sol";
9: import {ISwapRouter} from "./interfaces/ISwapRouter.sol";
9: import {ISwapRouter} from "./interfaces/ISwapRouter.sol";
File: Lender.sol
4: import "./utils/Errors.sol";
4: import "./utils/Errors.sol";
5: import "./utils/Structs.sol";
5: import "./utils/Structs.sol";
7: import {IERC20} from "./interfaces/IERC20.sol";
7: import {IERC20} from "./interfaces/IERC20.sol";
8: import {Ownable} from "./utils/Ownable.sol";
8: import {Ownable} from "./utils/Ownable.sol";
120: debt = loan.debt + interest + fees;
120: debt = loan.debt + interest + fees;
155: p.poolBalance - currentBalance
161: currentBalance - p.poolBalance
185: _updatePoolBalance(poolId, pools[poolId].poolBalance + amount);
201: _updatePoolBalance(poolId, pools[poolId].poolBalance - amount);
233: for (uint256 i = 0; i < borrows.length; i++) {
233: for (uint256 i = 0; i < borrows.length; i++) {
246: uint256 loanRatio = (debt * 10 ** 18) / collateral;
246: uint256 loanRatio = (debt * 10 ** 18) / collateral;
246: uint256 loanRatio = (debt * 10 ** 18) / collateral;
246: uint256 loanRatio = (debt * 10 ** 18) / collateral;
262: _updatePoolBalance(poolId, pools[poolId].poolBalance - debt);
263: pools[poolId].outstandingLoans += debt;
265: uint256 fees = (debt * borrowerFee) / 10000;
265: uint256 fees = (debt * borrowerFee) / 10000;
269: IERC20(loan.loanToken).transfer(msg.sender, debt - fees);
280: loans.length - 1,
293: for (uint256 i = 0; i < loanIds.length; i++) {
293: for (uint256 i = 0; i < loanIds.length; i++) {
312: pools[poolId].poolBalance + loan.debt + lenderInterest
312: pools[poolId].poolBalance + loan.debt + lenderInterest
314: pools[poolId].outstandingLoans -= loan.debt;
320: loan.debt + lenderInterest
359: for (uint256 i = 0; i < loanIds.length; i++) {
359: for (uint256 i = 0; i < loanIds.length; i++) {
381: uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
381: uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
384: uint256 loanRatio = (totalDebt * 10 ** 18) / loan.collateral;
384: uint256 loanRatio = (totalDebt * 10 ** 18) / loan.collateral;
384: uint256 loanRatio = (totalDebt * 10 ** 18) / loan.collateral;
384: uint256 loanRatio = (totalDebt * 10 ** 18) / loan.collateral;
387: _updatePoolBalance(poolId, pool.poolBalance - totalDebt);
388: pools[poolId].outstandingLoans += totalDebt;
398: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
398: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
400: pools[oldPoolId].outstandingLoans -= loan.debt;
409: loan.debt + lenderInterest + protocolInterest,
409: loan.debt + lenderInterest + protocolInterest,
438: for (uint256 i = 0; i < loanIds.length; i++) {
438: for (uint256 i = 0; i < loanIds.length; i++) {
471: if (block.timestamp > loan.auctionStartTimestamp + loan.auctionLength)
474: uint256 timeElapsed = block.timestamp - loan.auctionStartTimestamp;
475: uint256 currentAuctionRate = (MAX_INTEREST_RATE * timeElapsed) /
475: uint256 currentAuctionRate = (MAX_INTEREST_RATE * timeElapsed) /
485: uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
485: uint256 totalDebt = loan.debt + lenderInterest + protocolInterest;
489: _updatePoolBalance(poolId, pools[poolId].poolBalance - totalDebt);
490: pools[poolId].outstandingLoans += totalDebt;
500: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
500: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
502: pools[oldPoolId].outstandingLoans -= loan.debt;
511: loan.debt + lenderInterest + protocolInterest,
511: loan.debt + lenderInterest + protocolInterest,
549: for (uint256 i = 0; i < loanIds.length; i++) {
549: for (uint256 i = 0; i < loanIds.length; i++) {
558: loan.auctionStartTimestamp + loan.auctionLength
561: uint256 govFee = (borrowerFee * loan.collateral) / 10000;
561: uint256 govFee = (borrowerFee * loan.collateral) / 10000;
567: loan.collateral - govFee
575: pools[poolId].outstandingLoans -= loan.debt;
592: for (uint256 i = 0; i < refinances.length; i++) {
592: for (uint256 i = 0; i < refinances.length; i++) {
618: uint256 loanRatio = (debt * 10 ** 18) / collateral;
618: uint256 loanRatio = (debt * 10 ** 18) / collateral;
618: uint256 loanRatio = (debt * 10 ** 18) / collateral;
618: uint256 loanRatio = (debt * 10 ** 18) / collateral;
626: uint256 debtToPay = loan.debt + lenderInterest + protocolInterest;
626: uint256 debtToPay = loan.debt + lenderInterest + protocolInterest;
631: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
631: pools[oldPoolId].poolBalance + loan.debt + lenderInterest
633: pools[oldPoolId].outstandingLoans -= loan.debt;
636: _updatePoolBalance(poolId, pools[poolId].poolBalance - debt);
637: pools[poolId].outstandingLoans += debt;
645: debtToPay - debt
650: uint256 fee = (borrowerFee * (debt - debtToPay)) / 10000;
650: uint256 fee = (borrowerFee * (debt - debtToPay)) / 10000;
650: uint256 fee = (borrowerFee * (debt - debtToPay)) / 10000;
653: IERC20(loan.loanToken).transfer(msg.sender, debt - debtToPay - fee);
653: IERC20(loan.loanToken).transfer(msg.sender, debt - debtToPay - fee);
666: collateral - loan.collateral
672: loan.collateral - collateral
698: pools[poolId].poolBalance -= debt;
723: uint256 timeElapsed = block.timestamp - l.startTimestamp;
724: interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;
724: interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;
724: interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;
724: interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;
725: fees = (lenderFee * interest) / 10000;
725: fees = (lenderFee * interest) / 10000;
726: interest -= fees;
File: Staking.sol
4: import {IERC20} from "./interfaces/IERC20.sol";
4: import {IERC20} from "./interfaces/IERC20.sol";
5: import {Ownable} from "./utils/Ownable.sol";
5: import {Ownable} from "./utils/Ownable.sol";
41: balances[msg.sender] += _amount;
48: balances[msg.sender] -= _amount;
66: uint256 _diff = _balance - balance;
68: uint256 _ratio = _diff * 1e18 / totalSupply;
68: uint256 _ratio = _diff * 1e18 / totalSupply;
70: index = index + _ratio;
86: uint256 _delta = index - _supplyIndex;
88: uint256 _share = _supplied * _delta / 1e18;
88: uint256 _share = _supplied * _delta / 1e18;
89: claimable[recipient] += _share;

[GAS-3] Use Custom Errors

Source
Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.

Instances (2):

File: Fees.sol
27: require(_profits != WETH, "not allowed");
File: utils/Ownable.sol
11: require(msg.sender == owner, "UNAUTHORIZED");

[GAS-4] Don't initialize variables with default value

Instances (8):

File: Lender.sol
233: for (uint256 i = 0; i < borrows.length; i++) {
293: for (uint256 i = 0; i < loanIds.length; i++) {
359: for (uint256 i = 0; i < loanIds.length; i++) {
438: for (uint256 i = 0; i < loanIds.length; i++) {
549: for (uint256 i = 0; i < loanIds.length; i++) {
592: for (uint256 i = 0; i < refinances.length; i++) {
File: Staking.sol
14: uint256 public balance = 0;
16: uint256 public index = 0;

[GAS-5] Functions guaranteed to revert when called by normal users can be marked 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.

Instances (5):

File: Beedle.sol
36: function mint(address to, uint256 amount) external onlyOwner {
File: Lender.sol
84: function setLenderFee(uint256 _fee) external onlyOwner {
92: function setBorrowerFee(uint256 _fee) external onlyOwner {
100: function setFeeReceiver(address _feeReceiver) external onlyOwner {
File: utils/Ownable.sol
19: function transferOwnership(address _owner) public virtual onlyOwner {

[GAS-6] ++i costs less gas than i++, especially when it's used in for-loops (--i/i-- too)

Saves 5 gas per loop

Instances (6):

File: Lender.sol
233: for (uint256 i = 0; i < borrows.length; i++) {
293: for (uint256 i = 0; i < loanIds.length; i++) {
359: for (uint256 i = 0; i < loanIds.length; i++) {
438: for (uint256 i = 0; i < loanIds.length; i++) {
549: for (uint256 i = 0; i < loanIds.length; i++) {
592: for (uint256 i = 0; i < refinances.length; i++) {

[GAS-7] Using private rather than public for constants, saves gas

If needed, the values can be read from the verified contract source code, or if there are multiple values there can be a single getter function that returns a tuple of the values of all currently-public constants. Saves 3406-3606 gas in deployment gas due to the compiler not having to create non-payable getter functions for deployment calldata, not having to store the bytes of the value outside of where it's used, and not adding another entry to the method ID table

Instances (3):

File: Fees.sol
16: ISwapRouter public constant swapRouter =
File: Lender.sol
59: uint256 public constant MAX_INTEREST_RATE = 100000;
61: uint256 public constant MAX_AUCTION_LENGTH = 3 days;

[GAS-8] Use != 0 instead of > 0 for unsigned integer comparison

Instances (5):

File: Staking.sol
63: if (totalSupply > 0) {
67: if (_diff > 0) {
69: if (_ratio > 0) {
83: if (_supplied > 0) {
87: if (_delta > 0) {

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.