AbraNFT contest - oyc_109's results

A peer to peer lending platform, using NFTs as collateral.

General Information

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

Abracadabra Money

Findings Distribution

Researcher Performance

Rank: 26/59

Findings: 2

Award: $140.53

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

missing checks for zero address

description

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.

Findings

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.

Awards

67.3009 MIM - $67.30

Labels

bug
G (Gas Optimization)

External Links

do not calculate constants

Description

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.

Example

NFTPair.sol

111: uint256 private constant YEAR_BPS = 3600 * 24 * 365 * 10_000;

Cache in variables instead of loading

Description

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

Example

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 should be checked first

Description

require statements can be placed earlier to reduce gas usage on revert

Example

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");

switching between non-zero values saves gas

Description

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.

Example

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;

don't cache values only used once

Description

values used only once should be calculated inline instead to save gas

Example

NFTPair.sol

304: uint256 borrowerShare = totalShare - openFeeShare; 520: uint256 fee = (interest * PROTOCOL_FEE_BPS) / BPS;

Prefix increments are cheaper than postfix increments

Description

using prefix increments save gas

Example

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

Don't Initialize Variables with Default Value

Description

saves gas

Example

NFTPair.sol

641: for (uint256 i = 0; i < actions.length; i++) {

Use != 0 instead of > 0 for Unsigned Integer Comparison

Impact

Issue Information: G003 - use !=0 for unsigned int comparison

Example

NFTPair.sol::717 => if (_share > 0) {

Long Revert Strings

Impact

Issue Information: G007 - long (revert) strings

Example:

NFTPair.sol::366 => require(ILendingClub(lender).willLend(tokenId, params), "NFTPair: LendingClub does not like you");

#0 - cryptolyndon

2022-05-03T04:02:37Z

  • Using 1 instead of 0 for LOAN_INITIAL would break things spectacularly.
  • I would be surprised if the optimizer did not catch things like setting the loop counter to zero, uint "> 0" comparisons, literal calculations, etc, but I have not bothered to compare.

#1 - cryptolyndon

2022-05-03T04:03:09Z

  • I can't argue with the revert string
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter