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: 9/47
Findings: 3
Award: $586.89
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: WatchPug
Also found by: CertoraInc, hickuphh3
https://github.com/code-423n4/2022-04-backed/blob/main/contracts/NFTLoanFacilitator.sol#L177
Take other lender loanTicket without improving any of the loan conditions
The bug is in NftLoanFacilliator.sol
in the function lend()
(link1).
&& previousInterestRate - (previousInterestRate * requiredImprovementRate / SCALAR) >= interestRate),
for previousInterestRate < 10
(which is basiclly a rate that is < 1%) ,existing:
previousInterestRate * requiredImprovementRate < 10^3
and then previousInterestRate * requiredImprovementRate / SCALAR = 0
which means any user can take the loanTicket from a lender without really lowering the interest for the borrower. (Note it is only possible if previousInterestRate < 1% but this is a fair assumption).
#0 - wilsoncusack
2022-04-07T18:05:29Z
Same as #80 I believe
#1 - gzeoneth
2022-04-15T13:04:10Z
Duplicate #80
🌟 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
NFTLoanFacilitator
contract as the sendCollateralTo
argument of the closeLoan
function, the NFT will be locked in the contract. There are 2 ways to deal with it - either by checking that the given address is not the address of the NFTLoanFacilitator
contract, or by adding a withdrawERC721 function. The second way is better because there might be other ways to get an NFT locked in the contract.
This is also correct for the seizeCollateral
function.#0 - wilsoncusack
2022-04-07T12:27:25Z
many things can go wrong with these passed addresses and we will not try to prevent them
🌟 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
34.8992 USDC - $34.90
lend
function because we know that facilitatorTake < amountIncrease
(because facilitatorTake = (amountIncrease * originationFeeRate / SCALAR)
and originationFeeRate <= SCALAR
)
ERC20(loanAssetContractAddress).safeTransfer( IERC721(borrowTicketContract).ownerOf(loanId), amountIncrease - facilitatorTake );
// These 2 are redundant require(interestRate <= previousInterestRate, 'NFTLoanFacilitator: rate too high'); require(durationSeconds >= previousDurationSeconds, 'NFTLoanFacilitator: duration too low'); require((previousLoanAmount * requiredImprovementRate / SCALAR) <= amountIncrease || previousDurationSeconds + (previousDurationSeconds * requiredImprovementRate / SCALAR) <= durationSeconds || (previousInterestRate != 0 // do not allow rate improvement if rate already 0 && previousInterestRate - (previousInterestRate * requiredImprovementRate / SCALAR) >= interestRate), "NFTLoanFacilitator: proposed terms must be better than existing terms");
createLoan
function to save gas
require(collateralContractAddress != lendTicketContract && collateralContractAddress != borrowTicketContract, 'NFTLoanFacilitator: cannot use tickets as collateral');
_transfer
in the loanFacilitatorTransfer
function (simply replace the call to transfer
with the function call, the _transfer
function is an internal function which is not used in any other place except the loanFacilitatorTransfer
function)
function loanFacilitatorTransfer(address from, address to, uint256 loanId) external override loanFacilitatorOnly { _transfer(from, to, loanId); }
The initial value ofuint256 public constant override SCALAR = 10 ** INTEREST_RATE_DECIMALS; uint256 public override originationFeeRate = 10 ** (INTEREST_RATE_DECIMALS - 2); uint256 public override requiredImprovementRate = 10 ** (INTEREST_RATE_DECIMALS - 1);
INTEREST_RATE_DECIMALS
is 3 so these initial values can be calculated pre-deploy (1000, 10 and 100)#0 - wilsoncusack
2022-04-07T12:26:55Z
These 2 requires are redundant, because they are checked in the next require
no, the below is an OR. We need to make sure there is no regression in terms.
use one require instead of two in the createLoan function to save gas
does not save gas, is more
inline the call to _transfer in the loanFacilitatorTransfer function (simply replace the call to transfer with the function call, the _transfer function is an internal function which is not used in any other place except the loanFacilitatorTransfer function)
prefer to keep clear what is from solmate, but will consider
Calculate values pre-deploy instead of calculating them in the deployment
don't really care about optimizing deploy