Platform: Code4rena
Start Date: 05/04/2022
Pot Size: $30,000 USDC
Total HM: 10
Participants: 47
Period: 3 days
Judge: gzeon
Total Solo HM: 4
Id: 106
League: ETH
Rank: 26/47
Findings: 2
Award: $103.29
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xkatana, BouSalman, CertoraInc, Dravee, FSchmoede, Hawkeye, Kenshin, Meta0xNull, PPrieditis, Ruhum, TerrierLover, VAD37, WatchPug, berndartmueller, csanuragjain, hake, horsefacts, hubble, m9800, rayn, reassor, robee, samruna, securerodd, shenwilly, sorrynotsorry, t11s, teryanarmen, tintin, z3s
54.2825 USDC - $54.28
The seizeCollateral function allows a lender to receive the loan collateral if a borrowed does not repay before the end of the loan duration. But this function is not automated by a keeper network, so the lender must manually call this function. There will be instances where the lender does not call seizeCollateral until hours or days after the loan period has ended. In this event, the lender will call the seizeCollateral function and expect to receive the collateral. But the borrower can frontrun this with a repayAndCloseLoan call, paying back the loan before the collateral can be seized.
The seizeCollateral function can be called by a lender after the loan duration has passed. If this happens, the lender will expect to receive the collateral. If this call is frontrun, the lender will lose out on receiving the collateral and instead the borrower gets a free loan extension with the originally agreed upon interest rate. This is most problematic for short-term loans of a few days, because a loan extension of 1-2 days, perhaps during a weekend when there is no one available to execute the transaction on an EOA, would be a substantial duration extension to the loan. This could disincentivize lenders from lending in the future, especially for short term loans.
The seizeCollateral call can be called by the lender when the loan duration has ended https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L253
But if the loan is repayed and closed earlier in the same block as the seizeCollateral call, the loan.closed value will be set to true https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L240
The lender will only receive interest for the period of the loan PLUS the period after loan expiration, while the borrower gets a "free" loan extension. Because there is no keeper system in place, the lender must manually trigger this call when they noticed the loan has not been repayed.
Example scenario #2:
Manual analysis
Consider integrating with the gelato network for automated keepers: https://www.gelato.network/
Another option is to create a function that seizes collateral for overdue loans and sends the collateral to the lender. This function should have the onlyOwner modifier and it will allow the Backed Protocol team to prevent loans from sitting expired for extended periods with the potential of free loan extensions for the borrowers.
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xkatana, CertoraInc, FSchmoede, Funen, IllIllI, Kenshin, Meta0xNull, TerrierLover, Tomio, csanuragjain, joshie, obront, rayn, rfa, robee, saian, securerodd, sorrynotsorry, t11s, z3s
49.0121 USDC - $49.01
Strings in solidity are handled in 32 byte chunks. A require string longer than 32 bytes uses more gas. Shortening these strings will save gas.
Several cases of this gas optimization were found. These are a few examples, but more may exist
Manual analysis
Shorten require strings
Using > 0
uses slightly more gas than using != 0
. Use != 0
when comparing uint variables to zero, which cannot hold values below zero
There were two instances of this issue https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L198 https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L321
grep
Replace > 0
with != 0
to save gas
Identifying a function as payable saves gas. Functions that have the onlyOwner modifier cannot be called by normal users and will not mistakenly receive ETH. These functions can be payable to save gas.
There are many functions that have the onlyOwner modifier and can be payable setLendTicketContract https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L279 setBorrowTicketContract https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L289 withdrawOriginationFees https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L296 updateOriginationFeeRate https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L306 updateRequiredImprovementRate https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L320
Manual analysis
Add payable to these functions for gas savings
Solidity errors introduced in version 0.8.4 can save gas on revert conditions https://blog.soliditylang.org/2021/04/21/custom-errors/ https://twitter.com/PatrickAlphaC/status/1505197417884528640
Some error messages are used, but many require blocks still exist in the code. A few examples are
Manual analysis
Replace require blocks with new solidity errors described in https://blog.soliditylang.org/2021/04/21/custom-errors/
Declaring a function as external instead of public saves gas
One function can be declared external instead of public
Manual analysis
Change function from public to external to save gas
#0 - gzeoneth
2022-04-15T14:49:29Z
3,4,5 is valid
#1 - wilsoncusack
2022-04-16T01:48:45Z
3 - yes but these are infrequently called so do not care about optimizing + I think labeling functions that are not intended to take eth as payable is an anti pattern 4 - I shorted revert messages to free up byte space for the optimizer but in general do not care about optimizing revert gas 5 - function needs to be marked public in order to override the inherited