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: 26/59
Findings: 2
Award: $140.53
π Selected for report: 0
π Solo Findings: 0
π 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.2308 MIM - $73.23
address(0) which is 20-bytes of 0βs is treated specially in Solidity contracts because the private key corresponding to this address is unknown.
Ether and tokens sent to this address cannot be retrieved and setting access control roles to this address also wonβt work (no private key to sign transactions).
Therefore zero addresses should be used with care and checks should be implemented for user-supplied address parameters.
functions in NFTPair.sol do not check for zero addresses in parameters a require statement should be added for these functions
function _requestLoan function removeCollateral function _lend function requestAndBorrow function takeCollateralAndLend
#0 - cryptolyndon
2022-05-12T03:53:18Z
Meh. Many of these will already revert at some point if the zero address is used. I suppose there are some edge cases, and it comes down to preventing user error -- a hypothetical legitimate owner of the zero address would know to tread very lightly.
I'm not aware of any interface that defaults to zero if you forget a parameter. There are indeed ways to "burn" funds by either sending them to zero, or locking them in a state where taking them out reverts. It is also possible to send them to the wrong address, or some contract that doesn't support taking them out, etc. Why single out zero?
Going to leave this up as my reference "why no zero checks in general" justification. See i.a. #1, #2, #3, #4 and #91 for a more detailed discussion of specific scenarios.
π 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
67.3009 MIM - $67.30
Due to how constant variables are implemented (replacements at compile-time), an expression assigned to a constant variable is recomputed each time that the variable is used, which wastes some gas.
NFTPair.sol
111: uint256 private constant YEAR_BPS = 3600 * 24 * 365 * 10_000;
The code can be optimized by minimising the number of SLOADs. SLOADs are expensive (100 gas) compared to MLOADs/MSTOREs (3 gas). Here, storage values should get cached in memory
NFTPair.sol
loan.status is used multiple times and should be cached
183: if (loan.status == LOAN_OUTSTANDING) {
params.duration is used multiple times and should be cached
189: params.duration >= cur.duration && params.valuation <= cur.valuation && params.annualInterestBPS <= cur.annualInterestBPS,
params.valuation is used multiple times and should be cached
189: params.duration >= cur.duration && params.valuation <= cur.valuation && params.annualInterestBPS <= cur.annualInterestBPS,
actions.length in the for loop should be cached
641: for (uint256 i = 0; i < actions.length; i++) {
require statements can be placed earlier to reduce gas usage on revert
NFTPair.sol
move lines to the top of their respective functions
186: require(msg.sender == loan.lender, "NFTPair: not the lender"); 251: require(msg.sender == loan.borrower, "NFTPair: not the borrower");
SSTORE from 0 to 1 (or any non-zero value), the cost is 20000; SSTORE from 1 to 2 (or any other non-zero value), the cost is 5000.
Therefore, switching between 1, 2 instead of 0, 1 will be more gas efficient.
in NFTPair.sol
change
96: uint8 private constant LOAN_INITIAL = 0; 97: uint8 private constant LOAN_REQUESTED = 1; 98: uint8 private constant LOAN_OUTSTANDING = 2;
to
96: uint8 private constant LOAN_INITIAL = 1; 97: uint8 private constant LOAN_REQUESTED = 2; 98: uint8 private constant LOAN_OUTSTANDING = 3;
values used only once should be calculated inline instead to save gas
NFTPair.sol
304: uint256 borrowerShare = totalShare - openFeeShare; 520: uint256 fee = (interest * PROTOCOL_FEE_BPS) / BPS;
using prefix increments save gas
NFTPair.sol
494: for (uint256 k = 2; k <= COMPOUND_INTEREST_TERMS; k++) { 641: for (uint256 i = 0; i < actions.length; i++) {
use ++k and ++i instead
saves gas
NFTPair.sol
641: for (uint256 i = 0; i < actions.length; i++) {
Issue Information: G003 - use !=0 for unsigned int comparison
NFTPair.sol::717 => if (_share > 0) {
Issue Information: G007 - long (revert) strings
NFTPair.sol::366 => require(ILendingClub(lender).willLend(tokenId, params), "NFTPair: LendingClub does not like you");
#0 - cryptolyndon
2022-05-03T04:02:37Z
#1 - cryptolyndon
2022-05-03T04:03:09Z