Platform: Code4rena
Start Date: 27/04/2022
Pot Size: $50,000 MIM
Total HM: 6
Participants: 59
Period: 5 days
Judge: 0xean
Id: 113
League: ETH
Rank: 5/59
Findings: 5
Award: $3,826.94
🌟 Selected for report: 1
🚀 Solo Findings: 0
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L287 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L321
_lend() and removeCollateral() functions use oracle.get() to get the value from the oracle. get() function returns 2 values; success and rate. Success indicates if there is a valid rate information for the pair. However, success value is not taken into consideration. Therefore, calculations may be done using a faulty rate data.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L287 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L321
Manual analysis
I'd suggest to include a check for the success and discard the rate information if success fails.
#0 - cryptolyndon
2022-05-05T04:22:44Z
Duplicate of #21
🌟 Selected for report: 0x1337
Also found by: BowTiedWardens, GimelSec, catchup, cccz, gzeon, horsefacts, hyh, kenzo
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L209 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L286-L288
Lender can update the loan terms using updateLoanParams() function. As it commented in the function: "The lender can change terms so long as the changes are strictly the same or better for the borrower" Regarding the LTV, condition is that the new LTV must be less than or equal to the current LTV: params.ltvBPS <= cur.ltvBPS, Lender has the right to seize the collateral if the loan is underwater. This check is done by the code below: uint256 amount = loanParams.valuation + interest; (, uint256 rate) = loanParams.oracle.get(address(this), tokenId); require(rate.mul(loanParams.ltvBPS) / BPS < amount, "NFT is still valued"); Hence, the lender can effectively downgrade the LTV, turn a valid loan to underwater and seize the collateral.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L209 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L286-L288
Manual analysis
The condition should be changed as: params.ltvBPS >= cur.ltvBPS,
#0 - cryptolyndon
2022-05-05T04:14:00Z
Duplicate of #51, but confirmed.
2510.6083 MIM - $2,510.61
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L316
It comments in the _lend() function that lender accepted conditions must be at least as good as the borrower is asking for. The line which checks the accepted LTV (lender's LTV) against borrower asking LTV is: params.ltvBPS >= accepted.ltvBPS, This means lender should be offering a lower LTV, which must be the opposite way around. I think this may have the potential to strand the lender, if he enters a lower LTV. For example borrower asking LTV is 86%. However, lender enters his accepted LTV as 80%. lend() will execute with 86% LTV and punish the lender, whereas it should revert and acknowledge the lender that his bid is not good enough.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L316
Manual analysis
The condition should be changed as: params.ltvBPS <= accepted.ltvBPS,
#0 - cryptolyndon
2022-05-05T04:17:31Z
Confirmed, and the first to note this particular issue.
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0xDjango, 0xf15ers, AuditsAreUS, BowTiedWardens, CertoraInc, Funen, GimelSec, MaratCerby, Ruhum, WatchPug, antonttc, berndartmueller, bobi, bobirichman, broccolirob, catchup, cccz, defsec, delfin454000, gs8nrv, gzeon, horsefacts, hubble, hyh, ilan, jah, joestakey, kebabsec, kenta, kenzo, m9800, mics, oyc_109, pauliax, reassor, robee, samruna, sikorico, simon135, throttle, unforgiven, z3s
73.4476 MIM - $73.45
Contracts are using 0.6.12 compiler which is a bit outdated. It would be better to use a version >= 0.8.4 to benefit from new features such as builtin safemath operations and bugfixes.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L20 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L20
I suggest to use at least compiler 0.8.4
It is a good practice to include 0 address check while updating an important address. I suggest to include a zero address check for setFeeTo() function.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L728 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L750
#0 - cryptolyndon
2022-05-12T04:17:20Z
Which compiler version is sufficiently battle-tested to be safe is subjective. Also, why not 0.8.13?
setFeeTo() is actually one of the recoverable "set the zero address on accident" cases. In fact, even if the protocol accidentally tries to withdraw to the zero address the transaction will revert in the BentoBox.
See #15 for the zero check
🌟 Selected for report: BowTiedWardens
Also found by: 0x1f8b, 0xNazgul, 0xf15ers, 0xkatana, CertoraInc, Funen, GimelSec, Hawkeye, IllIllI, Kulk0, NoamYakov, Tadashi, Tomio, TrungOre, antonttc, catchup, defsec, delfin454000, fatherOfBlocks, gzeon, horsefacts, joestakey, kenta, oyc_109, pauliax, reassor, robee, samruna, simon135, slywaters, sorrynotsorry, z3s
50.3021 MIM - $50.30
Private state variables are cheaper than public state variables. There are some instances where public variables can be private.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L76 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L77 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L84 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L85 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L91 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L94 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L165 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L93 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L94 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L101 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L102 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L108 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L111 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L182
Calling private functions are cheaper than calling internal functions. Therefore, it is better to declare functions private if they are not called from inherited contracts.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L276 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L573 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L583 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L595 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L608 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L276 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L305 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L606 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L616 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L628 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L641
Change the visibility to private where possible.
Some variables are initialised with their default values which cause unnecessary gas consumption
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L96 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L641 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L113 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L674
Error reason strings take space in the deployed bytecode. Every reason string takes at least 32 bytes so make sure your string fits in 32 bytes or it will become more expensive.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L366 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L398
Prefix increments are cheaper than postfix increments. So i++ can be replaced by ++i at the lines below.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L494 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPair.sol#L641 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L527 https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L674
amount variable is used only once. It can be deleted and replaced by "loanParams.valuation + interest" where amount is used.
https://github.com/code-423n4/2022-04-abranft/blob/main/contracts/NFTPairWithOracle.sol#L286-L288
Code can be changed as: // uint256 amount = loanParams.valuation + interest; (, uint256 rate) = loanParams.oracle.get(address(this), tokenId); require(rate.mul(loanParams.ltvBPS) / BPS < (loanParams.valuation + interest), "NFT is still valued");
#0 - cryptolyndon
2022-05-13T23:30:03Z
Cannot argue with the error string length. Public state variables are public so that they can be read by other entities than the contract. Others I need proof to be convinced they make a difference.