Report
Gas Optimizations
|
Issue |
Instances |
GAS-1 |
Cache array length outside of loop |
6 |
GAS-2 |
Use Custom Errors |
2 |
GAS-3 |
Don't initialize variables with default value |
8 |
GAS-4 |
Functions guaranteed to revert when called by normal users can be marked payable |
5 |
GAS-5 |
++i costs less gas than i++ , especially when it's used in for -loops (--i /i-- too) |
6 |
GAS-6 |
Using private rather than public for constants, saves gas |
3 |
GAS-7 |
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: src//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] 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: src//Fees.sol
27: require(_profits != WETH, "not allowed");
File: src//utils/Ownable.sol
11: require(msg.sender == owner, "UNAUTHORIZED");
[GAS-3] Don't initialize variables with default value
Instances (8):
File: src//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: src//Staking.sol
14: uint256 public balance = 0;
16: uint256 public index = 0;
[GAS-4] 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: src//Beedle.sol
36: function mint(address to, uint256 amount) external onlyOwner {
File: src//Lender.sol
84: function setLenderFee(uint256 _fee) external onlyOwner {
92: function setBorrowerFee(uint256 _fee) external onlyOwner {
100: function setFeeReceiver(address _feeReceiver) external onlyOwner {
File: src//utils/Ownable.sol
19: function transferOwnership(address _owner) public virtual onlyOwner {
[GAS-5] ++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: src//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-6] 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: src//Fees.sol
16: ISwapRouter public constant swapRouter =
File: src//Lender.sol
59: uint256 public constant MAX_INTEREST_RATE = 100000;
61: uint256 public constant MAX_AUCTION_LENGTH = 3 days;
[GAS-7] Use != 0 instead of > 0 for unsigned integer comparison
Instances (5):
File: src//Staking.sol
63: if (totalSupply > 0) {
67: if (_diff > 0) {
69: if (_ratio > 0) {
83: if (_supplied > 0) {
87: if (_delta > 0) {
Non Critical Issues
|
Issue |
Instances |
NC-1 |
Typos |
23 |
[NC-1] Typos
Instances (23):
File: src//Fees.sol
- 24: /// @notice swap loan tokens for collateral tokens from liquidations
+ 24: /// @notice swap collateral tokens for loan tokens from liquidations
- 25: /// @param _profits the token to swap for WETH
+ 25: /// @param _profits the token to swap for ETH
File: src//Lender.sol
- 62: /// @notice the fee taken by the protocol in BIPs
+ 62: /// @notice the fee taken by the protocol in BPS
- 69: /// @notice mapping of poolId to Pool (poolId is keccak256(lender, loanToken, collateralToken))
+ 69: /// @notice mapping of poolId to Pool (poolId is keccak256(lender, loanToken, collateralToken)
- 545: /// @notice sieze a loan after a failed refinance auction
+ 545: /// @notice seize a loan after a failed refinance auction
- 547: /// @param loanIds the ids of the loans to sieze
+ 547: /// @param loanIds the ids of the loans to seize
- 635: // now lets deduct our tokens from the new pool
+ 635: // now let's deduct our tokens from the new pool
File: src//Staking.sol
- 13: /// @notice the balance of reward tokens
+ 13: /// @notice The balance of reward tokens
- 15: /// @notice the index of the last update
+ 15: /// @notice The index of the last update
- 18: /// @notice mapping of user indexes
+ 18: /// @notice Mapping of user indexes
- 21: /// @notice mapping of user balances
+ 21: /// @notice Mapping of user balances
- 23: /// @notice mapping of user claimable rewards
+ 23: /// @notice Mapping of user claimable rewards
- 26: /// @notice the staking token
+ 26: /// @notice The staking token
- 28: /// @notice the reward token
+ 28: /// @notice The reward token
- 36: /// @notice deposit tokens to stake
+ 36: /// @notice Deposit tokens to stake
- 37: /// @param _amount the amount to deposit
+ 37: /// @param _amount The amount to deposit
- 44: /// @notice withdraw tokens from stake
+ 44: /// @notice Withdraw tokens from stake
- 45: /// @param _amount the amount to withdraw
+ 45: /// @param _amount The amount to withdraw
- 52: /// @notice claim rewards
+ 52: /// @notice Claim rewards
- 60: /// @notice update the global index of earned rewards
+ 60: /// @notice Update the global index of earned rewards
- 78: /// @notice update the index for a user
+ 78: /// @notice Update the index for a user
- 79: /// @param recipient the user to update
+ 79: /// @param recipient The user to update
File: src//utils/Structs.sol
- 75: /// @notice the virtual balance based on the multipier
+ 75: /// @notice the virtual balance based on the multiplier
Low Issues
|
Issue |
Instances |
L-1 |
Unsafe ERC20 operation(s) |
24 |
[L-1] Unsafe ERC20 operation(s)
Instances (24):
File: src//Fees.sol
43: IERC20(WETH).transfer(staking, IERC20(WETH).balanceOf(address(this)));
File: src//Lender.sol
152: IERC20(p.loanToken).transferFrom(
159: IERC20(p.loanToken).transfer(
187: IERC20(pools[poolId].loanToken).transferFrom(
203: IERC20(pools[poolId].loanToken).transfer(msg.sender, amount);
267: IERC20(loan.loanToken).transfer(feeReceiver, fees);
269: IERC20(loan.loanToken).transfer(msg.sender, debt - fees);
271: IERC20(loan.collateralToken).transferFrom(
317: IERC20(loan.loanToken).transferFrom(
323: IERC20(loan.loanToken).transferFrom(
329: IERC20(loan.collateralToken).transfer(
403: IERC20(loan.loanToken).transfer(feeReceiver, protocolInterest);
505: IERC20(loan.loanToken).transfer(feeReceiver, protocolInterest);
563: IERC20(loan.collateralToken).transfer(feeReceiver, govFee);
565: IERC20(loan.collateralToken).transfer(
642: IERC20(loan.loanToken).transferFrom(
651: IERC20(loan.loanToken).transfer(feeReceiver, fee);
653: IERC20(loan.loanToken).transfer(msg.sender, debt - debtToPay - fee);
656: IERC20(loan.loanToken).transfer(feeReceiver, protocolInterest);
663: IERC20(loan.collateralToken).transferFrom(
670: IERC20(loan.collateralToken).transfer(
File: src//Staking.sol
39: TKN.transferFrom(msg.sender, address(this), _amount);
49: TKN.transfer(msg.sender, _amount);
55: WETH.transfer(msg.sender, claimable[msg.sender]);
Medium Issues
|
Issue |
Instances |
M-1 |
Centralization Risk for trusted owners |
12 |
[M-1] Centralization Risk for trusted owners
Impact:
Contracts have owners with privileged rights to perform admin tasks and need to be trusted to not perform malicious updates or drain funds.
Instances (12):
File: src//Beedle.sol
9: contract Beedle is Ownable, ERC20, ERC20Permit, ERC20Votes {
11: constructor() ERC20("Beedle", "BDL") ERC20Permit("Beedle") Ownable(msg.sender) {
36: function mint(address to, uint256 amount) external onlyOwner {
File: src//Lender.sol
10: contract Lender is Ownable {
73: constructor() Ownable(msg.sender) {
84: function setLenderFee(uint256 _fee) external onlyOwner {
92: function setBorrowerFee(uint256 _fee) external onlyOwner {
100: function setFeeReceiver(address _feeReceiver) external onlyOwner {
File: src//Staking.sol
11: contract Staking is Ownable {
31: constructor(address _token, address _weth) Ownable(msg.sender) {
File: src//utils/Ownable.sol
4: abstract contract Ownable {
19: function transferOwnership(address _owner) public virtual onlyOwner {