AbraNFT contest - IllIllI'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: 6/59

Findings: 4

Award: $2,418.07

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: cccz

Also found by: IllIllI, Ruhum, WatchPug, antonttc, berndartmueller, catchup, hyh, plotchy

Labels

bug
duplicate
3 (High Risk)

Awards

533.6961 MIM - $533.70

External Links

Lines of code

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L287 https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L321

Vulnerability details

The INFTOracle interface's NatSpec says that there are two return values, one of which indicates whether the rate returned can be trusted:

File: contracts/interfaces/INFTOracle.sol

8       /// @return success if no valid (recent) rate is available, return false else true.
9       /// @return rate The rate of the requested asset / pair / pool.
10      function get(address pair, uint256 tokenId) external returns (bool success, uint256 rate);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/interfaces/INFTOracle.sol#L8-L10

Impact

The NFTPairWithOracle ignores the success return code of calls to INFTOracle.get(), which may lead to the misspricing of assets and loss of funds/tokens

Proof of Concept

File: contracts/NFTPairWithOracle.sol   #1

287                   (, uint256 rate) = loanParams.oracle.get(address(this), tokenId);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L287

File: contracts/NFTPairWithOracle.sol   #1

321               (, uint256 rate) = params.oracle.get(address(this), tokenId);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L321

Tools Used

Code inspection

Store the success return value and require(success) before using the rate

#0 - 0xm3rlin

2022-05-04T14:06:46Z

Findings Information

🌟 Selected for report: Ruhum

Also found by: BowTiedWardens, IllIllI, WatchPug, gzeon, plotchy, scaraven

Labels

bug
duplicate
3 (High Risk)

Awards

1045.8477 MIM - $1,045.85

External Links

Lines of code

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L286-L288

Vulnerability details

Impact

The borrower can lose his/her collateral immediately

Proof of Concept

The code that determines whether or not the collateral can be seized when it's underwater is as follows:

File: contracts/NFTPairWithOracle.sol

277               if (uint256(loan.startTime) + tokenLoanParams[tokenId].duration > block.timestamp) {
278                   TokenLoanParams memory loanParams = tokenLoanParams[tokenId];
279                   // No underflow: loan.startTime is only ever set to a block timestamp
280                   // Cast is safe: if this overflows, then all loans have expired anyway
281                   uint256 interest = calculateInterest(
282                       loanParams.valuation,
283                       uint64(block.timestamp - loan.startTime),
284                       loanParams.annualInterestBPS
285                   ).to128();
286                   uint256 amount = loanParams.valuation + interest;
287                   (, uint256 rate) = loanParams.oracle.get(address(this), tokenId);
288                   require(rate.mul(loanParams.ltvBPS) / BPS < amount, "NFT is still valued");
289               }

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L277-L289

If loanParams.ltvBPS on line 288 is zero then the left side of the inequality will always be zero, the require() condition will pass, and the collateral will be seizable.

updateLoanParams() allows ltvBPS to be set to zero as long as the loan is still outstanding:

File: contracts/NFTPairWithOracle.sol

198       function updateLoanParams(uint256 tokenId, TokenLoanParams memory params) public {
199           TokenLoan memory loan = tokenLoan[tokenId];
200           if (loan.status == LOAN_OUTSTANDING) {
201               // The lender can change terms so long as the changes are strictly
202               // the same or better for the borrower:
203               require(msg.sender == loan.lender, "NFTPair: not the lender");
204               TokenLoanParams memory cur = tokenLoanParams[tokenId];
205               require(
206                   params.duration >= cur.duration &&
207                       params.valuation <= cur.valuation &&
208                       params.annualInterestBPS <= cur.annualInterestBPS &&
209                       params.ltvBPS <= cur.ltvBPS,
210                   "NFTPair: worse params"
211               );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L198-L211

If an attacker wants to pay the loan amount minus the open fee plus the protocol fee, in a single transaction the attacker can call lend() with the right arguments, call updateLoanParams() with a ltvBPS of zero, and call removeCollateral().

Tools Used

Code inspection

In updateLoanParams() add the following requirement: require(rate.mul(loanParams.ltvBPS) / BPS >= amount)

#0 - cryptolyndon

2022-05-05T04:44:07Z

Duplicate of #51 but confirmed.

Low Risk Issues

1. Should ensure loan collateral is not immediately seizable

For the Oracle version there are checks to make sure that the current valuation is above the amount loaned. There should be a similar check that the loan duration is not zero. Zero is not useful for flash loans because of the origination fees.

File: contracts/NFTPairWithOracle.sol   #1

224          tokenLoanParams[tokenId] = params;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L224

File: contracts/NFTPairWithOracle.sol   #2

244          tokenLoanParams[tokenId] = params;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L244

2. Pairs do not implement ERC721TokenReceiver

According to the README.md, NFTPairs specifically involve ERC721 tokens. Therefore the contract should implement ERC721TokenReceiver, or customer transfers involving safeTransferFrom() calls will revert

File: contracts/NFTPair.sol   #1

59  contract NFTPair is BoringOwnable, Domain, IMasterContract {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L59

File: contracts/NFTPairWithOracle.sol   #2

69  contract NFTPairWithOracle is BoringOwnable, Domain, IMasterContract {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L69

3. Incorrect comments

Skimming involves excess balances in the bentobox, not in the contract itself. This comment will lead to clients incorrectly passing tokens to the pair, rather than the bentobox. In addition, overall, there should be more comments devited to the interactions with the bentobox

File: contracts/NFTPair.sol   #1

320      /// @param skim True if the funds have been transfered to the contract

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L320

File: contracts/NFTPairWithOracle.sol   #2

355      /// @param skim True if the funds have been transfered to the contract

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L355

4. Comments should be enforced by require()s

The comments below should be enforced by require(block.timestamp < uint64(-1))

File: contracts/NFTPair.sol   #1

311          loan.startTime = uint64(block.timestamp); // Do not use in 12e10 years..

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L311

File: contracts/NFTPairWithOracle.sol   #2

346          loan.startTime = uint64(block.timestamp); // Do not use in 12e10 years..

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L346

5. Calls incorrectly allow non-zero msg.value

The comments below say that msg.value is "only applicable to" a subset of actions. All other actions should have a require(!msg.value). Allowing it anyway is incorrect state handling

File: contracts/NFTPair.sol   #1

631      /// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions.
632      /// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`.

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L631-L632

File: contracts/NFTPairWithOracle.sol   #2

664      /// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions.
665      /// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`.

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L664-L665

6. Missing checks for address(0x0) when assigning values to address state variables

File: contracts/NFTPair.sol   #1

729           feeTo = newFeeTo;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L729

File: contracts/NFTPairWithOracle.sol   #2

751           feeTo = newFeeTo;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L751

7. ecrecover not checked for zero result

A return value of zero indicates an invalid signature, so this is both invalid state-handling and an incorrect message

File: contracts/NFTPair.sol   #1

383              require(ecrecover(_getDigest(dataHash), v, r, s) == lender, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L383

File: contracts/NFTPair.sol   #2

419          require(ecrecover(_getDigest(dataHash), v, r, s) == borrower, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L419

File: contracts/NFTPairWithOracle.sol   #3

417              require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == lender, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L417

File: contracts/NFTPairWithOracle.sol   #4

452          require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == borrower, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L452

Non-critical Issues

1. Consider supporting CryptoPunks and EtherRocks

The project README.md says that NFTPairs are specifically ERC721 tokens, but not all NFTs are ERC721s. CryptoPunks and EtherRocks came before the standard and do not conform to it.

File: README.md   #1

58  - NFT Pair are a version of Cauldrons where the collateral isn't an ERC20 token but an ERC721 token, the deal OTC, the parameters of the loan themselves pre-defined.

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/README.md?plain=1#L58

2. Contracts should be refactored to extend a base contract with the common functionality

$ fgrep -xf NFTPair.sol NFTPairWithOracle.sol | wc -l 686 $ wc -l NFTPair.sol NFTPairWithOracle.sol 732 NFTPair.sol 754 NFTPairWithOracle.sol
686 / 732 = 93.7% 686 / 754 = 91.0%

About 92% of the lines in each file are exactly the same as the lines in the other file. At the very least the shared constants, the common state variables, and the pure functions should be moved to a common base contract.

File: contracts/NFTPair.sol (various lines)   #1

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol

File: contracts/NFTPairWithOracle.sol (various lines)   #2

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol

3. Some compilers can't handle two different contracts with the same name

Some compilers only compile the first one they encounter, ignoring the second one. If two contracts are different (e.g. different struct argument definitions) then they should have different names

File: contracts/NFTPair.sol   #1

37  interface ILendingClub {
38      // Per token settings.
39      function willLend(uint256 tokenId, TokenLoanParams memory params) external view returns (bool);
40  
41      function lendingConditions(address nftPair, uint256 tokenId) external view returns (TokenLoanParams memory);
42  }

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L37-L42

File: contracts/NFTPairWithOracle.sol   #2

47  interface ILendingClub {
48      // Per token settings.
49      function willLend(uint256 tokenId, TokenLoanParams memory params) external view returns (bool);
50  
51      function lendingConditions(address nftPair, uint256 tokenId) external view returns (TokenLoanParams memory);
52  }

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L47-L52

4. Calls to BoringMath.to128() are redundant

All calls to to128() occur on the result of calculateInterest(), which itself already checks that the value fits into a uint128

File: contracts/NFTPairWithOracle.sol   #1

285                  ).to128();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L285

File: contracts/NFTPairWithOracle.sol   #2

552          uint256 interest = calculateInterest(principal, uint64(block.timestamp - loan.startTime), loanParams.annualInterestBPS).to128();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L552

File: contracts/NFTPair.sol   #3

519          uint256 interest = calculateInterest(principal, uint64(block.timestamp - loan.startTime), loanParams.annualInterestBPS).to128();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L519

5. require()/revert() statements should have descriptive reason strings

File: contracts/NFTPair.sol   #1

501               revert();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L501

File: contracts/NFTPairWithOracle.sol   #2

534               revert();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L534

6. public functions not called by the contract should be declared external instead

Contracts are allowed to override their parents' functions and change the visibility from external to public.

File: contracts/NFTPair.sol   #1

181       function updateLoanParams(uint256 tokenId, TokenLoanParams memory params) public {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L181

File: contracts/NFTPair.sol   #2

713       function withdrawFees() public {
714           address to = masterContract.feeTo();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L713-L714

File: contracts/NFTPair.sol   #3

728       function setFeeTo(address newFeeTo) public onlyOwner {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L728

File: contracts/NFTPairWithOracle.sol   #4

198       function updateLoanParams(uint256 tokenId, TokenLoanParams memory params) public {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L198

File: contracts/NFTPairWithOracle.sol   #5

735       function withdrawFees() public {
736           address to = masterContract.feeTo();

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L735-L736

File: contracts/NFTPairWithOracle.sol   #6

750       function setFeeTo(address newFeeTo) public onlyOwner {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L750

7. Interfaces should be moved to separate files

Most of the other interfaces in this project are in their own file in the interfaces directory. The interfaces below do not follow this pattern

File: contracts/NFTPairWithOracle.sol   #1

47  interface ILendingClub {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L47

File: contracts/NFTPairWithOracle.sol   #2

54  interface INFTPair {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L54

File: contracts/NFTPair.sol   #3

37  interface ILendingClub {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L37

File: contracts/NFTPair.sol   #4

44  interface INFTPair {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L44

8. 2**<n> - 1 should be re-written as type(uint<n>).max

Earlier versions of solidity can use uint<n>(-1) instead. Expressions not including the - 1 can often be re-written to accomodate the change (e.g. by using a > rather than a >=, which will also save some gas)

File: contracts/NFTPair.sol   #1

500           if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L500

File: contracts/NFTPairWithOracle.sol   #2

533           if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L533

9. constants should be defined rather than using magic numbers

File: contracts/NFTPair.sol   #1

500           if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L500

File: contracts/NFTPairWithOracle.sol   #2

533           if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L533

10. Numeric values having to do with time should use time units for readability

There are units for seconds, minutes, hours, days, and weeks

File: contracts/NFTPair.sol   #1

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L111

File: contracts/NFTPairWithOracle.sol   #2

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L128

11. Use a more recent version of solidity

Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>)

File: contracts/NFTPair.sol   #1

20   pragma solidity 0.6.12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L20

File: contracts/NFTPairWithOracle.sol   #2

20   pragma solidity 0.6.12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L20

12. Constant redefined elsewhere

Consider defining in only one contract so that values cannot become out of sync when only one location is updated. A cheap way to store constants in a single location is to create an internal constant in a library. If the variable is a local cache of another contract's value, consider making the cache variable internal or private, which will require external users to query the contract with the source of truth, so that callers don't get out of sync.

File: contracts/NFTPairWithOracle.sol   #1

93       IBentoBoxV1 public immutable bentoBox;

seen in contracts/NFTPair.sol https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L93

File: contracts/NFTPairWithOracle.sol   #2

94       NFTPairWithOracle public immutable masterContract;

seen in contracts/NFTPair.sol https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L94

13. Fee mechanics should be better described

When reading through the code the first time, it wasn't clear exactly what openFeeShare was for and why it's being subtracted from totalShare. Add to this the fact that the protocolFee is based on the openFeeShare and it seems like this area needs more comments, specifically that openFeeShare is the fee paid to the lender by the borrower during loan initiation, for the privilege of being given a loan.

File: contracts/NFTPair.sol   #1

295          if (skim) {
296              require(
297                  bentoBox.balanceOf(asset, address(this)) >= (totalShare - openFeeShare + protocolFeeShare + feesEarnedShare),
298                  "NFTPair: skim too much"
299              );
300          } else {
301              bentoBox.transfer(asset, lender, address(this), totalShare - openFeeShare + protocolFeeShare);
302          }

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L295-L302

File: contracts/NFTPairWithOracle.sol   #2

330          if (skim) {
331              require(
332                  bentoBox.balanceOf(asset, address(this)) >= (totalShare - openFeeShare + protocolFeeShare + feesEarnedShare),
333                  "NFTPair: skim too much"
334              );
335          } else {
336              bentoBox.transfer(asset, lender, address(this), totalShare - openFeeShare + protocolFeeShare);
337          }

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L330-L337

14. Typos

File: contracts/NFTPair.sol   #1

90       // Track assets we own. Used to allow skimming the excesss.

excesss https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L90

File: contracts/NFTPair.sol   #2

114       // `calculateIntest`.

calculateIntest https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L114

File: contracts/NFTPair.sol   #3

233       /// @param skim True if the token has already been transfered

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L233

File: contracts/NFTPair.sol   #4

320       /// @param skim True if the funds have been transfered to the contract

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L320

File: contracts/NFTPair.sol   #5

351       /// @param skimCollateral True if the collateral has already been transfered

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L351

File: contracts/NFTPair.sol   #6

389       /// @notice Take collateral from a pre-commited borrower and lend against it

commited https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L389

File: contracts/NFTPair.sol   #7

394       /// @param skimFunds True if the funds have been transfered to the contract

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L394

File: contracts/NFTPair.sol   #8

434       /// of the above inquality) fits in 128 bits, then the function is

inquality https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L434

File: contracts/NFTPair.sol   #9

446           // (NOTE: n is hardcoded as COMPOUND_INTEREST_TERMS)

hardcoded https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L446

File: contracts/NFTPairWithOracle.sol   #10

107       // Track assets we own. Used to allow skimming the excesss.

excesss https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L107

File: contracts/NFTPairWithOracle.sol   #11

131       // `calculateIntest`.

calculateIntest https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L131

File: contracts/NFTPairWithOracle.sol   #12

253       /// @param skim True if the token has already been transfered

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L253

File: contracts/NFTPairWithOracle.sol   #13

355       /// @param skim True if the funds have been transfered to the contract

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L355

File: contracts/NFTPairWithOracle.sol   #14

386       /// @param skimCollateral True if the collateral has already been transfered

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L386

File: contracts/NFTPairWithOracle.sol   #15

423       /// @notice Take collateral from a pre-commited borrower and lend against it

commited https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L423

File: contracts/NFTPairWithOracle.sol   #16

428       /// @param skimFunds True if the funds have been transfered to the contract

transfered https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L428

File: contracts/NFTPairWithOracle.sol   #17

467       /// of the above inquality) fits in 128 bits, then the function is

inquality https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L467

File: contracts/NFTPairWithOracle.sol   #18

479           // (NOTE: n is hardcoded as COMPOUND_INTEREST_TERMS)

hardcoded https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L479

15. NatSpec is incomplete

File: contracts/NFTPair.sol   #1

346       /// @notice Caller provides collateral; loan can go to a different address.
347       /// @param tokenId ID of the token that will function as collateral
348       /// @param lender Lender, whose BentoBox balance the funds will come from
349       /// @param recipient Address to receive the loan.
350       /// @param params Loan parameters requested, and signed by the lender
351       /// @param skimCollateral True if the collateral has already been transfered
352       /// @param anyTokenId Set if lender agreed to any token. Must have tokenId 0 in signature.
353       function requestAndBorrow(
354           uint256 tokenId,
355           address lender,
356           address recipient,
357           TokenLoanParams memory params,
358           bool skimCollateral,
359           bool anyTokenId,
360           uint256 deadline,
361           uint8 v,
362           bytes32 r,
363           bytes32 s

Missing: @param deadline https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L346-L363

File: contracts/NFTPair.sol   #2

346       /// @notice Caller provides collateral; loan can go to a different address.
347       /// @param tokenId ID of the token that will function as collateral
348       /// @param lender Lender, whose BentoBox balance the funds will come from
349       /// @param recipient Address to receive the loan.
350       /// @param params Loan parameters requested, and signed by the lender
351       /// @param skimCollateral True if the collateral has already been transfered
352       /// @param anyTokenId Set if lender agreed to any token. Must have tokenId 0 in signature.
353       function requestAndBorrow(
354           uint256 tokenId,
355           address lender,
356           address recipient,
357           TokenLoanParams memory params,
358           bool skimCollateral,
359           bool anyTokenId,
360           uint256 deadline,
361           uint8 v,
362           bytes32 r,
363           bytes32 s

Missing: @param v https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L346-L363

File: contracts/NFTPair.sol   #3

346       /// @notice Caller provides collateral; loan can go to a different address.
347       /// @param tokenId ID of the token that will function as collateral
348       /// @param lender Lender, whose BentoBox balance the funds will come from
349       /// @param recipient Address to receive the loan.
350       /// @param params Loan parameters requested, and signed by the lender
351       /// @param skimCollateral True if the collateral has already been transfered
352       /// @param anyTokenId Set if lender agreed to any token. Must have tokenId 0 in signature.
353       function requestAndBorrow(
354           uint256 tokenId,
355           address lender,
356           address recipient,
357           TokenLoanParams memory params,
358           bool skimCollateral,
359           bool anyTokenId,
360           uint256 deadline,
361           uint8 v,
362           bytes32 r,
363           bytes32 s

Missing: @param r https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L346-L363

File: contracts/NFTPair.sol   #4

346       /// @notice Caller provides collateral; loan can go to a different address.
347       /// @param tokenId ID of the token that will function as collateral
348       /// @param lender Lender, whose BentoBox balance the funds will come from
349       /// @param recipient Address to receive the loan.
350       /// @param params Loan parameters requested, and signed by the lender
351       /// @param skimCollateral True if the collateral has already been transfered
352       /// @param anyTokenId Set if lender agreed to any token. Must have tokenId 0 in signature.
353       function requestAndBorrow(
354           uint256 tokenId,
355           address lender,
356           address recipient,
357           TokenLoanParams memory params,
358           bool skimCollateral,
359           bool anyTokenId,
360           uint256 deadline,
361           uint8 v,
362           bytes32 r,
363           bytes32 s

Missing: @param s https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L346-L363

File: contracts/NFTPair.sol   #5

389       /// @notice Take collateral from a pre-commited borrower and lend against it
390       /// @notice Collateral must come from the borrower, not a third party.
391       /// @param tokenId ID of the token that will function as collateral
392       /// @param borrower Address that provides collateral and receives the loan
393       /// @param params Loan terms offered, and signed by the borrower
394       /// @param skimFunds True if the funds have been transfered to the contract
395       function takeCollateralAndLend(
396           uint256 tokenId,
397           address borrower,
398           TokenLoanParams memory params,
399           bool skimFunds,
400           uint256 deadline,
401           uint8 v,
402           bytes32 r,
403           bytes32 s

Missing: @param deadline https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L389-L403

File: contracts/NFTPair.sol   #6

389       /// @notice Take collateral from a pre-commited borrower and lend against it
390       /// @notice Collateral must come from the borrower, not a third party.
391       /// @param tokenId ID of the token that will function as collateral
392       /// @param borrower Address that provides collateral and receives the loan
393       /// @param params Loan terms offered, and signed by the borrower
394       /// @param skimFunds True if the funds have been transfered to the contract
395       function takeCollateralAndLend(
396           uint256 tokenId,
397           address borrower,
398           TokenLoanParams memory params,
399           bool skimFunds,
400           uint256 deadline,
401           uint8 v,
402           bytes32 r,
403           bytes32 s

Missing: @param v https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L389-L403

File: contracts/NFTPair.sol   #7

389       /// @notice Take collateral from a pre-commited borrower and lend against it
390       /// @notice Collateral must come from the borrower, not a third party.
391       /// @param tokenId ID of the token that will function as collateral
392       /// @param borrower Address that provides collateral and receives the loan
393       /// @param params Loan terms offered, and signed by the borrower
394       /// @param skimFunds True if the funds have been transfered to the contract
395       function takeCollateralAndLend(
396           uint256 tokenId,
397           address borrower,
398           TokenLoanParams memory params,
399           bool skimFunds,
400           uint256 deadline,
401           uint8 v,
402           bytes32 r,
403           bytes32 s

Missing: @param r https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L389-L403

File: contracts/NFTPair.sol   #8

389       /// @notice Take collateral from a pre-commited borrower and lend against it
390       /// @notice Collateral must come from the borrower, not a third party.
391       /// @param tokenId ID of the token that will function as collateral
392       /// @param borrower Address that provides collateral and receives the loan
393       /// @param params Loan terms offered, and signed by the borrower
394       /// @param skimFunds True if the funds have been transfered to the contract
395       function takeCollateralAndLend(
396           uint256 tokenId,
397           address borrower,
398           TokenLoanParams memory params,
399           bool skimFunds,
400           uint256 deadline,
401           uint8 v,
402           bytes32 r,
403           bytes32 s

Missing: @param s https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L389-L403

File: contracts/NFTPairWithOracle.sol   #9

381       /// @notice Caller provides collateral; loan can go to a different address.
382       /// @param tokenId ID of the token that will function as collateral
383       /// @param lender Lender, whose BentoBox balance the funds will come from
384       /// @param recipient Address to receive the loan.
385       /// @param params Loan parameters requested, and signed by the lender
386       /// @param skimCollateral True if the collateral has already been transfered
387       /// @param anyTokenId Set if lender agreed to any token. Must have tokenId 0 in signature.
388       function requestAndBorrow(
389           uint256 tokenId,
390           address lender,
391           address recipient,
392           TokenLoanParams memory params,
393           bool skimCollateral,
394           bool anyTokenId,
395           SignatureParams memory signature

Missing: @param signature https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L381-L395

File: contracts/NFTPairWithOracle.sol   #10

423       /// @notice Take collateral from a pre-commited borrower and lend against it
424       /// @notice Collateral must come from the borrower, not a third party.
425       /// @param tokenId ID of the token that will function as collateral
426       /// @param borrower Address that provides collateral and receives the loan
427       /// @param params Loan terms offered, and signed by the borrower
428       /// @param skimFunds True if the funds have been transfered to the contract
429       function takeCollateralAndLend(
430           uint256 tokenId,
431           address borrower,
432           TokenLoanParams memory params,
433           bool skimFunds,
434           SignatureParams memory signature

Missing: @param signature https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L423-L434

16. Event is missing indexed fields

Each event should use three indexed fields if there are three or more fields

File: contracts/NFTPair.sol   #1

65       event LogRequestLoan(address indexed borrower, uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L65

File: contracts/NFTPair.sol   #2

66       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L66

File: contracts/NFTPair.sol   #3

68       event LogRemoveCollateral(uint256 indexed tokenId, address recipient);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L68

File: contracts/NFTPair.sol   #4

73       event LogWithdrawFees(address indexed feeTo, uint256 feeShare);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L73

File: contracts/NFTPairWithOracle.sol   #5

75       event LogRequestLoan(
76           address indexed borrower,
77           uint256 indexed tokenId,
78           uint128 valuation,
79           uint64 duration,
80           uint16 annualInterestBPS,
81           uint16 ltvBPS
82       );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L75-L82

File: contracts/NFTPairWithOracle.sol   #6

83       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS, uint16 ltvBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L83

File: contracts/NFTPairWithOracle.sol   #7

85       event LogRemoveCollateral(uint256 indexed tokenId, address recipient);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L85

File: contracts/NFTPairWithOracle.sol   #8

90       event LogWithdrawFees(address indexed feeTo, uint256 feeShare);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L90

17. A best practice is to check for signature malleability

File: contracts/NFTPair.sol   #1

383              require(ecrecover(_getDigest(dataHash), v, r, s) == lender, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L383

File: contracts/NFTPair.sol   #2

419          require(ecrecover(_getDigest(dataHash), v, r, s) == borrower, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L419

File: contracts/NFTPairWithOracle.sol   #3

417              require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == lender, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L417

File: contracts/NFTPairWithOracle.sol   #4

452          require(ecrecover(_getDigest(dataHash), signature.v, signature.r, signature.s) == borrower, "NFTPair: signature invalid");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L452

18. Consider making contract Pausable to have some protection against ongoing exploits

File: contracts/NFTPair.sol   #1

59  contract NFTPair is BoringOwnable, Domain, IMasterContract {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L59

File: contracts/NFTPairWithOracle.sol   #2

69  contract NFTPairWithOracle is BoringOwnable, Domain, IMasterContract {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L69

19. States/flags should use Enums rather than separate constants

File: contracts/NFTPair.sol   #1

96      uint8 private constant LOAN_INITIAL = 0;
97      uint8 private constant LOAN_REQUESTED = 1;
98      uint8 private constant LOAN_OUTSTANDING = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L96-L98

File: contracts/NFTPairWithOracle.sol   #2

113      uint8 private constant LOAN_INITIAL = 0;
114      uint8 private constant LOAN_REQUESTED = 1;
115      uint8 private constant LOAN_OUTSTANDING = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L113-L115

File: contracts/NFTPair.sol   #3

545      uint8 internal constant ACTION_REPAY = 2;
546      uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;
547  
548      uint8 internal constant ACTION_REQUEST_LOAN = 12;
549      uint8 internal constant ACTION_LEND = 13;
550  
551      // Function on BentoBox
552      uint8 internal constant ACTION_BENTO_DEPOSIT = 20;
553      uint8 internal constant ACTION_BENTO_WITHDRAW = 21;
554      uint8 internal constant ACTION_BENTO_TRANSFER = 22;
555      uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;
556      uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;
557  
558      // Any external call (except to BentoBox)
559      uint8 internal constant ACTION_CALL = 30;
560  
561      // Signed requests
562      uint8 internal constant ACTION_REQUEST_AND_BORROW = 40;
563      uint8 internal constant ACTION_TAKE_COLLATERAL_AND_LEND = 41;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L545-L563

File: contracts/NFTPairWithOracle.sol   #4

578      uint8 internal constant ACTION_REPAY = 2;
579      uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;
580  
581      uint8 internal constant ACTION_REQUEST_LOAN = 12;
582      uint8 internal constant ACTION_LEND = 13;
583  
584      // Function on BentoBox
585      uint8 internal constant ACTION_BENTO_DEPOSIT = 20;
586      uint8 internal constant ACTION_BENTO_WITHDRAW = 21;
587      uint8 internal constant ACTION_BENTO_TRANSFER = 22;
588      uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;
589      uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;
590  
591      // Any external call (except to BentoBox)
592      uint8 internal constant ACTION_CALL = 30;
593  
594      // Signed requests
595      uint8 internal constant ACTION_REQUEST_AND_BORROW = 40;
596      uint8 internal constant ACTION_TAKE_COLLATERAL_AND_LEND = 41;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L578-L596

20. Non-exploitable re-entrancies

Code should follow the best-practice of check-effects-interaction

Reentrancy in NFTPair._lend(address,uint256,TokenLoanParams,bool) (contracts/NFTPair.sol#271-315): #1 External calls: - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) State variables written after the call(s): - feesEarnedShare += protocolFeeShare (contracts/NFTPair.sol#307) - tokenLoan[tokenId] = loan (contracts/NFTPair.sol#312)
Reentrancy in NFTPairWithOracle._lend(address,uint256,TokenLoanParams,bool) (contracts/NFTPairWithOracle.sol#300-350): #2 External calls: - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) State variables written after the call(s): - feesEarnedShare += protocolFeeShare (contracts/NFTPairWithOracle.sol#342) - tokenLoan[tokenId] = loan (contracts/NFTPairWithOracle.sol#347)
Reentrancy in NFTPair._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPair.sol#205-227): #3 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) State variables written after the call(s): - tokenLoan[tokenId] = loan (contracts/NFTPair.sol#223)
Reentrancy in NFTPairWithOracle._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPairWithOracle.sol#225-247): #4 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) State variables written after the call(s): - tokenLoan[tokenId] = loan (contracts/NFTPairWithOracle.sol#243)
Reentrancy in NFTPairWithOracle.removeCollateral(uint256,address) (contracts/NFTPairWithOracle.sol#267-297): #5 External calls: - (rate) = loanParams.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#287) State variables written after the call(s): - delete tokenLoan[tokenId] (contracts/NFTPairWithOracle.sol#294)
Reentrancy in NFTPair.repay(uint256,bool) (contracts/NFTPair.sol#505-543): #6 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPair.sol#532) State variables written after the call(s): - delete tokenLoan[tokenId] (contracts/NFTPair.sol#537)
Reentrancy in NFTPairWithOracle.repay(uint256,bool) (contracts/NFTPairWithOracle.sol#538-576): #7 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPairWithOracle.sol#565) State variables written after the call(s): - delete tokenLoan[tokenId] (contracts/NFTPairWithOracle.sol#570)
Reentrancy in NFTPair.requestAndBorrow(uint256,address,address,TokenLoanParams,bool,bool,uint256,uint8,bytes32,bytes32) (contracts/NFTPair.sol#353-387): #8 External calls: - _requestLoan(msg.sender,tokenId,params,recipient,skimCollateral) (contracts/NFTPair.sol#385) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) - _lend(lender,tokenId,params,false) (contracts/NFTPair.sol#386) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) State variables written after the call(s): - _lend(lender,tokenId,params,false) (contracts/NFTPair.sol#386) - tokenLoan[tokenId] = loan (contracts/NFTPair.sol#312)
Reentrancy in NFTPairWithOracle.requestAndBorrow(uint256,address,address,TokenLoanParams,bool,bool,SignatureParams) (contracts/NFTPairWithOracle.sol#388-421): #9 External calls: - _requestLoan(msg.sender,tokenId,params,recipient,skimCollateral) (contracts/NFTPairWithOracle.sol#419) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) - _lend(lender,tokenId,params,false) (contracts/NFTPairWithOracle.sol#420) - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) State variables written after the call(s): - _lend(lender,tokenId,params,false) (contracts/NFTPairWithOracle.sol#420) - tokenLoan[tokenId] = loan (contracts/NFTPairWithOracle.sol#347)
Reentrancy in NFTPair.takeCollateralAndLend(uint256,address,TokenLoanParams,bool,uint256,uint8,bytes32,bytes32) (contracts/NFTPair.sol#395-422): #10 External calls: - _requestLoan(borrower,tokenId,params,borrower,false) (contracts/NFTPair.sol#420) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPair.sol#421) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) State variables written after the call(s): - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPair.sol#421) - tokenLoan[tokenId] = loan (contracts/NFTPair.sol#312)
Reentrancy in NFTPairWithOracle.takeCollateralAndLend(uint256,address,TokenLoanParams,bool,SignatureParams) (contracts/NFTPairWithOracle.sol#429-455): #11 External calls: - _requestLoan(borrower,tokenId,params,borrower,false) (contracts/NFTPairWithOracle.sol#453) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPairWithOracle.sol#454) - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) State variables written after the call(s): - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPairWithOracle.sol#454) - tokenLoan[tokenId] = loan (contracts/NFTPairWithOracle.sol#347)
Reentrancy in NFTPair.withdrawFees() (contracts/NFTPair.sol#713-723): #12 External calls: - bentoBox.transfer(asset,address(this),to,_share) (contracts/NFTPair.sol#718) State variables written after the call(s): - feesEarnedShare = 0 (contracts/NFTPair.sol#719)
Reentrancy in NFTPairWithOracle.withdrawFees() (contracts/NFTPairWithOracle.sol#735-745): #13 External calls: - bentoBox.transfer(asset,address(this),to,_share) (contracts/NFTPairWithOracle.sol#740) State variables written after the call(s): - feesEarnedShare = 0 (contracts/NFTPairWithOracle.sol#741)
Reentrancy in NFTPair._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPair.sol#205-227): #14 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) State variables written after the call(s): - tokenLoanParams[tokenId] = params (contracts/NFTPair.sol#224)
Reentrancy in NFTPairWithOracle._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPairWithOracle.sol#225-247): #15 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) State variables written after the call(s): - tokenLoanParams[tokenId] = params (contracts/NFTPairWithOracle.sol#244)
Reentrancy in NFTPair.repay(uint256,bool) (contracts/NFTPair.sol#505-543): #16 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPair.sol#532) State variables written after the call(s): - feesEarnedShare += feeShare (contracts/NFTPair.sol#536)
Reentrancy in NFTPairWithOracle.repay(uint256,bool) (contracts/NFTPairWithOracle.sol#538-576): #17 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPairWithOracle.sol#565) State variables written after the call(s): - feesEarnedShare += feeShare (contracts/NFTPairWithOracle.sol#569)
Reentrancy in NFTPair._lend(address,uint256,TokenLoanParams,bool) (contracts/NFTPair.sol#271-315): #18 External calls: - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPair.sol#314)
Reentrancy in NFTPairWithOracle._lend(address,uint256,TokenLoanParams,bool) (contracts/NFTPairWithOracle.sol#300-350): #19 External calls: - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPairWithOracle.sol#349)
Reentrancy in NFTPair._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPair.sol#205-227): #20 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) Event emitted after the call(s): - LogRequestLoan(to,tokenId,params.valuation,params.duration,params.annualInterestBPS) (contracts/NFTPair.sol#226)
Reentrancy in NFTPairWithOracle._requestLoan(address,uint256,TokenLoanParams,address,bool) (contracts/NFTPairWithOracle.sol#225-247): #21 External calls: - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) Event emitted after the call(s): - LogRequestLoan(to,tokenId,params.valuation,params.duration,params.annualInterestBPS,params.ltvBPS) (contracts/NFTPairWithOracle.sol#246)
Reentrancy in NFTPair.removeCollateral(uint256,address) (contracts/NFTPair.sol#247-268): #22 External calls: - collateral.transferFrom(address(this),to,tokenId) (contracts/NFTPair.sol#266) Event emitted after the call(s): - LogRemoveCollateral(tokenId,to) (contracts/NFTPair.sol#267)
Reentrancy in NFTPairWithOracle.removeCollateral(uint256,address) (contracts/NFTPairWithOracle.sol#267-297): #23 External calls: - (rate) = loanParams.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#287) - collateral.transferFrom(address(this),to,tokenId) (contracts/NFTPairWithOracle.sol#295) Event emitted after the call(s): - LogRemoveCollateral(tokenId,to) (contracts/NFTPairWithOracle.sol#296)
Reentrancy in NFTPair.repay(uint256,bool) (contracts/NFTPair.sol#505-543): #24 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPair.sol#532) - bentoBox.transfer(asset,from,loan.lender,totalShare - feeShare) (contracts/NFTPair.sol#539) - collateral.transferFrom(address(this),loan.borrower,tokenId) (contracts/NFTPair.sol#540) Event emitted after the call(s): - LogRepay(from,tokenId) (contracts/NFTPair.sol#542)
Reentrancy in NFTPairWithOracle.repay(uint256,bool) (contracts/NFTPairWithOracle.sol#538-576): #25 External calls: - bentoBox.transfer(asset,msg.sender,address(this),feeShare) (contracts/NFTPairWithOracle.sol#565) - bentoBox.transfer(asset,from,loan.lender,totalShare - feeShare) (contracts/NFTPairWithOracle.sol#572) - collateral.transferFrom(address(this),loan.borrower,tokenId) (contracts/NFTPairWithOracle.sol#573) Event emitted after the call(s): - LogRepay(from,tokenId) (contracts/NFTPairWithOracle.sol#575)
Reentrancy in NFTPair.requestAndBorrow(uint256,address,address,TokenLoanParams,bool,bool,uint256,uint8,bytes32,bytes32) (contracts/NFTPair.sol#353-387): #26 External calls: - _requestLoan(msg.sender,tokenId,params,recipient,skimCollateral) (contracts/NFTPair.sol#385) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) - _lend(lender,tokenId,params,false) (contracts/NFTPair.sol#386) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPair.sol#314) - _lend(lender,tokenId,params,false) (contracts/NFTPair.sol#386)
Reentrancy in NFTPairWithOracle.requestAndBorrow(uint256,address,address,TokenLoanParams,bool,bool,SignatureParams) (contracts/NFTPairWithOracle.sol#388-421): #27 External calls: - _requestLoan(msg.sender,tokenId,params,recipient,skimCollateral) (contracts/NFTPairWithOracle.sol#419) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) - _lend(lender,tokenId,params,false) (contracts/NFTPairWithOracle.sol#420) - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPairWithOracle.sol#349) - _lend(lender,tokenId,params,false) (contracts/NFTPairWithOracle.sol#420)
Reentrancy in NFTPair.takeCollateralAndLend(uint256,address,TokenLoanParams,bool,uint256,uint8,bytes32,bytes32) (contracts/NFTPair.sol#395-422): #28 External calls: - _requestLoan(borrower,tokenId,params,borrower,false) (contracts/NFTPair.sol#420) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPair.sol#218) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPair.sol#421) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPair.sol#301) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPair.sol#305) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPair.sol#314) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPair.sol#421)
Reentrancy in NFTPairWithOracle.takeCollateralAndLend(uint256,address,TokenLoanParams,bool,SignatureParams) (contracts/NFTPairWithOracle.sol#429-455): #29 External calls: - _requestLoan(borrower,tokenId,params,borrower,false) (contracts/NFTPairWithOracle.sol#453) - collateral.transferFrom(collateralProvider,address(this),tokenId) (contracts/NFTPairWithOracle.sol#238) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPairWithOracle.sol#454) - (rate) = params.oracle.get(address(this),tokenId) (contracts/NFTPairWithOracle.sol#321) - bentoBox.transfer(asset,lender,address(this),totalShare - openFeeShare + protocolFeeShare) (contracts/NFTPairWithOracle.sol#336) - bentoBox.transfer(asset,address(this),loan.borrower,borrowerShare) (contracts/NFTPairWithOracle.sol#340) Event emitted after the call(s): - LogLend(lender,tokenId) (contracts/NFTPairWithOracle.sol#349) - _lend(msg.sender,tokenId,params,skimFunds) (contracts/NFTPairWithOracle.sol#454)
Reentrancy in NFTPair.withdrawFees() (contracts/NFTPair.sol#713-723): #30 External calls: - bentoBox.transfer(asset,address(this),to,_share) (contracts/NFTPair.sol#718) Event emitted after the call(s): - LogWithdrawFees(to,_share) (contracts/NFTPair.sol#722)
Reentrancy in NFTPairWithOracle.withdrawFees() (contracts/NFTPairWithOracle.sol#735-745): #31 External calls: - bentoBox.transfer(asset,address(this),to,_share) (contracts/NFTPairWithOracle.sol#740) Event emitted after the call(s): - LogWithdrawFees(to,_share) (contracts/NFTPairWithOracle.sol#744)

#0 - cryptolyndon

2022-05-12T04:56:49Z

Low-risk issues:

  1. Agreed; this does suggest ERC-20 transfers.

  2. If you think this is going to be an issue, then think of all the gas wasted until then by even that single check! Time enough to write a V2.

Non-critical issues:

  1. Nonstandard NFT types that are popular enough to use warrant their own contract type.

  2. bentoBox is not a constant that will necessarily be invariable across different master contracts. Clones already work as suggested.

#1 - 0xean

2022-05-21T17:13:35Z

I believe this to be the most complete QA report. I agree with the severities outlined, with the exception of the Low Risk number 4, which should be non critical.

#2 - liveactionllama

2022-07-11T23:06:23Z

Per discussion with @cryptolyndon from the AbraNFT team, documenting the additional sponsor comments below for inclusion in the final audit report.<br>

Calls incorrectly allow non-zero msg.value: "Simply requiring msg.value to be zero would break things when some, but not all, actions use it."

Missing checks for address(0x0) when assigning values to address state variables: "The zero address is pretty much the ONLY wrong address we could enter where actual loss of funds is not possible."

Some compilers can't handle two different contracts with the same name: "This is not some example project intended to be forked and used with a wide range of different compiler setups."

Use a more recent version of solidity: "As time ticks on 0.8.x becomes increasingly safe to use, but the suggested reason here does not even apply to our contract."

Fee mechanics should be better described: "The contract is not meant to serve as sole documentation of our fee schedule."

A best practice is to check for signature malleability: "We use nonces to prevent replay attacks, rather than storing used signatures. A different, equally valid, signature of the same data would be of no use to an attacker."

Awards

130.1306 MIM - $130.13

Labels

bug
G (Gas Optimization)

External Links

Gas Optimizations

1. State variables should be cached in stack variables rather than re-reading them from storage

The instances below point to the second or later access of a state variable within a function. Caching will replace each Gwarmaccess (100 gas) with a much cheaper stack read. Less obvious fixes/optimizations include having local storage variables of mappings within state variable mappings or mappings within state variable structs, having local storage variables of structs within mappings, or having local caches of state variable contracts/addresses.

File: contracts/NFTPair.sol   #1

178           require(address(collateral) != address(0), "NFTPair: bad pair");

collateral https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L178

File: contracts/NFTPair.sol   #2

297                   bentoBox.balanceOf(asset, address(this)) >= (totalShare - openFeeShare + protocolFeeShare + feesEarnedShare),

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L297

File: contracts/NFTPair.sol   #3

301               bentoBox.transfer(asset, lender, address(this), totalShare - openFeeShare + protocolFeeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L301

File: contracts/NFTPair.sol   #4

305           bentoBox.transfer(asset, address(this), loan.borrower, borrowerShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L305

File: contracts/NFTPair.sol   #5

524           uint256 feeShare = bentoBox.toShare(asset, fee, false);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L524

File: contracts/NFTPair.sol   #6

528               require(bentoBox.balanceOf(asset, address(this)) >= (totalShare + feesEarnedShare), "NFTPair: skim too much");

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L528

File: contracts/NFTPair.sol   #7

532               bentoBox.transfer(asset, msg.sender, address(this), feeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L532

File: contracts/NFTPair.sol   #8

539           bentoBox.transfer(asset, from, loan.lender, totalShare - feeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L539

File: contracts/NFTPairWithOracle.sol   #9

195           require(address(collateral) != address(0), "NFTPair: bad pair");

collateral https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L195

File: contracts/NFTPairWithOracle.sol   #10

332                   bentoBox.balanceOf(asset, address(this)) >= (totalShare - openFeeShare + protocolFeeShare + feesEarnedShare),

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L332

File: contracts/NFTPairWithOracle.sol   #11

336               bentoBox.transfer(asset, lender, address(this), totalShare - openFeeShare + protocolFeeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L336

File: contracts/NFTPairWithOracle.sol   #12

340           bentoBox.transfer(asset, address(this), loan.borrower, borrowerShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L340

File: contracts/NFTPairWithOracle.sol   #13

557           uint256 feeShare = bentoBox.toShare(asset, fee, false);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L557

File: contracts/NFTPairWithOracle.sol   #14

561               require(bentoBox.balanceOf(asset, address(this)) >= (totalShare + feesEarnedShare), "NFTPair: skim too much");

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L561

File: contracts/NFTPairWithOracle.sol   #15

565               bentoBox.transfer(asset, msg.sender, address(this), feeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L565

File: contracts/NFTPairWithOracle.sol   #16

572           bentoBox.transfer(asset, from, loan.lender, totalShare - feeShare);

asset https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L572

File: contracts/NFTPairWithOracle.sol   #17

278                   TokenLoanParams memory loanParams = tokenLoanParams[tokenId];

tokenLoanParams https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L278

2. <x> += <y> costs more gas than <x> = <x> + <y> for state variables

File: contracts/NFTPair.sol   #1

307           feesEarnedShare += protocolFeeShare;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L307

File: contracts/NFTPair.sol   #2

536           feesEarnedShare += feeShare;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L536

File: contracts/NFTPairWithOracle.sol   #3

342           feesEarnedShare += protocolFeeShare;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L342

File: contracts/NFTPairWithOracle.sol   #4

569           feesEarnedShare += feeShare;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L569

3. <array>.length should not be looked up in every loop of a for-loop

The overheads outlined below are PER LOOP, excluding the first loop

  • storage arrays incur a Gwarmaccess (100 gas)
  • memory arrays use MLOAD (3 gas)
  • calldata arrays use CALLDATALOAD (3 gas)

Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset

File: contracts/NFTPair.sol   #1

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L641

File: contracts/NFTPairWithOracle.sol   #2

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L674

4. require()/revert() strings longer than 32 bytes cost extra gas

File: contracts/NFTPair.sol   #1

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L366

File: contracts/NFTPairWithOracle.sol   #2

398               require(ILendingClub(lender).willLend(tokenId, params), "NFTPair: LendingClub does not like you");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L398

5. Use a more recent version of solidity

Use a solidity version of at least 0.8.0 to get overflow protection without SafeMath Use a solidity version of at least 0.8.2 to get compiler automatic inlining Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

File: contracts/NFTPair.sol   #1

20   pragma solidity 0.6.12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L20

File: contracts/NFTPairWithOracle.sol   #2

20   pragma solidity 0.6.12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L20

6. It costs more gas to initialize variables to zero than to let the default of zero be applied

File: contracts/NFTPair.sol   #1

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L641

File: contracts/NFTPairWithOracle.sol   #2

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L674

7. internal functions only called once can be inlined to save gas

Not inlining costs 20 to 40 gas because of two extra JUMP instructions and additional stack operations needed for function calls.

File: contracts/NFTPair.sol   #1

578       function _bentoDeposit(
579           bytes memory data,
580           uint256 value,
581           uint256 value1,
582           uint256 value2
583       ) internal returns (uint256, uint256) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L578-L583

File: contracts/NFTPair.sol   #2

591       function _bentoWithdraw(
592           bytes memory data,
593           uint256 value1,
594           uint256 value2
595       ) internal returns (uint256, uint256) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L591-L595

File: contracts/NFTPair.sol   #3

603       function _call(
604           uint256 value,
605           bytes memory data,
606           uint256 value1,
607           uint256 value2
608       ) internal returns (bytes memory, uint8) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L603-L608

File: contracts/NFTPairWithOracle.sol   #4

611       function _bentoDeposit(
612           bytes memory data,
613           uint256 value,
614           uint256 value1,
615           uint256 value2
616       ) internal returns (uint256, uint256) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L611-L616

File: contracts/NFTPairWithOracle.sol   #5

624       function _bentoWithdraw(
625           bytes memory data,
626           uint256 value1,
627           uint256 value2
628       ) internal returns (uint256, uint256) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L624-L628

File: contracts/NFTPairWithOracle.sol   #6

636       function _call(
637           uint256 value,
638           bytes memory data,
639           uint256 value1,
640           uint256 value2
641       ) internal returns (bytes memory, uint8) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L636-L641

8. Using calldata instead of memory for read-only arguments in external functions saves gas

When a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution. Structs have the same overhead as an array of length one

File: contracts/NFTPair.sol   #1

39       function willLend(uint256 tokenId, TokenLoanParams memory params) external view returns (bool);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L39

File: contracts/NFTPairWithOracle.sol   #2

49       function willLend(uint256 tokenId, TokenLoanParams memory params) external view returns (bool);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L49

9. ++i costs less gas than ++i, especially when it's used in for-loops (--i/i-- too)

File: contracts/NFTPair.sol   #1

494           for (uint256 k = 2; k <= COMPOUND_INTEREST_TERMS; k++) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L494

File: contracts/NFTPair.sol   #2

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L641

File: contracts/NFTPairWithOracle.sol   #3

527           for (uint256 k = 2; k <= COMPOUND_INTEREST_TERMS; k++) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L527

File: contracts/NFTPairWithOracle.sol   #4

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

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L674

10. Splitting require() statements that use && saves gas

See this issue for an example

File: contracts/NFTPair.sol   #1

188               require(
189                   params.duration >= cur.duration && params.valuation <= cur.valuation && params.annualInterestBPS <= cur.annualInterestBPS,
190                   "NFTPair: worse params"
191               );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L188-L191

File: contracts/NFTPair.sol   #2

283           require(
284               params.valuation == accepted.valuation &&
285                   params.duration <= accepted.duration &&
286                   params.annualInterestBPS >= accepted.annualInterestBPS,
287               "NFTPair: bad params"
288           );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L283-L288

File: contracts/NFTPair.sol   #3

622           require(callee != address(bentoBox) && callee != address(collateral) && callee != address(this), "NFTPair: can't call");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L622

File: contracts/NFTPairWithOracle.sol   #4

205               require(
206                   params.duration >= cur.duration &&
207                       params.valuation <= cur.valuation &&
208                       params.annualInterestBPS <= cur.annualInterestBPS &&
209                       params.ltvBPS <= cur.ltvBPS,
210                   "NFTPair: worse params"
211               );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L205-L211

File: contracts/NFTPairWithOracle.sol   #5

312           require(
313               params.valuation == accepted.valuation &&
314                   params.duration <= accepted.duration &&
315                   params.annualInterestBPS >= accepted.annualInterestBPS &&
316                   params.ltvBPS >= accepted.ltvBPS,
317               "NFTPair: bad params"
318           );

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L312-L318

File: contracts/NFTPairWithOracle.sol   #6

655           require(callee != address(bentoBox) && callee != address(collateral) && callee != address(this), "NFTPair: can't call");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L655

11. Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.

https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html Use a larger size then downcast where needed

File: contracts/NFTPair.sol   #1

32       uint128 valuation; // How much will you get? OK to owe until expiration.

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L32

File: contracts/NFTPair.sol   #2

33       uint64 duration; // Length of loan in seconds

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L33

File: contracts/NFTPair.sol   #3

34       uint16 annualInterestBPS; // Variable cost of taking out the loan

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L34

File: contracts/NFTPair.sol   #4

65       event LogRequestLoan(address indexed borrower, uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L65

File: contracts/NFTPair.sol   #5

65       event LogRequestLoan(address indexed borrower, uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L65

File: contracts/NFTPair.sol   #6

65       event LogRequestLoan(address indexed borrower, uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L65

File: contracts/NFTPair.sol   #7

66       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L66

File: contracts/NFTPair.sol   #8

66       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L66

File: contracts/NFTPair.sol   #9

66       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L66

File: contracts/NFTPair.sol   #10

96       uint8 private constant LOAN_INITIAL = 0;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L96

File: contracts/NFTPair.sol   #11

97       uint8 private constant LOAN_REQUESTED = 1;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L97

File: contracts/NFTPair.sol   #12

98       uint8 private constant LOAN_OUTSTANDING = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L98

File: contracts/NFTPair.sol   #13

102           uint64 startTime;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L102

File: contracts/NFTPair.sol   #14

103           uint8 status;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L103

File: contracts/NFTPair.sol   #15

162       uint8 private constant COMPOUND_INTEREST_TERMS = 6;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L162

File: contracts/NFTPair.sol   #16

361           uint8 v,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L361

File: contracts/NFTPair.sol   #17

401           uint8 v,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L401

File: contracts/NFTPair.sol   #18

443           uint64 t,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L443

File: contracts/NFTPair.sol   #19

444           uint16 aprBPS

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L444

File: contracts/NFTPair.sol   #20

515           uint128 principal = loanParams.valuation;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L515

File: contracts/NFTPair.sol   #21

545       uint8 internal constant ACTION_REPAY = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L545

File: contracts/NFTPair.sol   #22

546       uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L546

File: contracts/NFTPair.sol   #23

548       uint8 internal constant ACTION_REQUEST_LOAN = 12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L548

File: contracts/NFTPair.sol   #24

549       uint8 internal constant ACTION_LEND = 13;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L549

File: contracts/NFTPair.sol   #25

552       uint8 internal constant ACTION_BENTO_DEPOSIT = 20;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L552

File: contracts/NFTPair.sol   #26

553       uint8 internal constant ACTION_BENTO_WITHDRAW = 21;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L553

File: contracts/NFTPair.sol   #27

554       uint8 internal constant ACTION_BENTO_TRANSFER = 22;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L554

File: contracts/NFTPair.sol   #28

555       uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L555

File: contracts/NFTPair.sol   #29

556       uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L556

File: contracts/NFTPair.sol   #30

559       uint8 internal constant ACTION_CALL = 30;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L559

File: contracts/NFTPair.sol   #31

562       uint8 internal constant ACTION_REQUEST_AND_BORROW = 40;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L562

File: contracts/NFTPair.sol   #32

563       uint8 internal constant ACTION_TAKE_COLLATERAL_AND_LEND = 41;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L563

File: contracts/NFTPair.sol   #33

608       ) internal returns (bytes memory, uint8) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L608

File: contracts/NFTPair.sol   #34

609           (address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) = abi.decode(

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L609

File: contracts/NFTPair.sol   #35

642               uint8 action = actions[i];

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L642

File: contracts/NFTPair.sol   #36

659                   (address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) = abi.decode(

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L659

File: contracts/NFTPair.sol   #37

675                   (bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L675

File: contracts/NFTPair.sol   #38

691                       uint8 v,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L691

File: contracts/NFTPair.sol   #39

703                       uint8 v,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L703

File: contracts/NFTPairWithOracle.sol   #40

33       uint128 valuation; // How much will you get? OK to owe until expiration.

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L33

File: contracts/NFTPairWithOracle.sol   #41

34       uint64 duration; // Length of loan in seconds

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L34

File: contracts/NFTPairWithOracle.sol   #42

35       uint16 annualInterestBPS; // Variable cost of taking out the loan

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L35

File: contracts/NFTPairWithOracle.sol   #43

36       uint16 ltvBPS; // Required to avoid liquidation

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L36

File: contracts/NFTPairWithOracle.sol   #44

42       uint8 v;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L42

File: contracts/NFTPairWithOracle.sol   #45

78           uint128 valuation,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L78

File: contracts/NFTPairWithOracle.sol   #46

79           uint64 duration,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L79

File: contracts/NFTPairWithOracle.sol   #47

80           uint16 annualInterestBPS,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L80

File: contracts/NFTPairWithOracle.sol   #48

81           uint16 ltvBPS

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L81

File: contracts/NFTPairWithOracle.sol   #49

83       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS, uint16 ltvBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L83

File: contracts/NFTPairWithOracle.sol   #50

83       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS, uint16 ltvBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L83

File: contracts/NFTPairWithOracle.sol   #51

83       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS, uint16 ltvBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L83

File: contracts/NFTPairWithOracle.sol   #52

83       event LogUpdateLoanParams(uint256 indexed tokenId, uint128 valuation, uint64 duration, uint16 annualInterestBPS, uint16 ltvBPS);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L83

File: contracts/NFTPairWithOracle.sol   #53

113       uint8 private constant LOAN_INITIAL = 0;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L113

File: contracts/NFTPairWithOracle.sol   #54

114       uint8 private constant LOAN_REQUESTED = 1;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L114

File: contracts/NFTPairWithOracle.sol   #55

115       uint8 private constant LOAN_OUTSTANDING = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L115

File: contracts/NFTPairWithOracle.sol   #56

119           uint64 startTime;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L119

File: contracts/NFTPairWithOracle.sol   #57

120           uint8 status;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L120

File: contracts/NFTPairWithOracle.sol   #58

179       uint8 private constant COMPOUND_INTEREST_TERMS = 6;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L179

File: contracts/NFTPairWithOracle.sol   #59

476           uint64 t,

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L476

File: contracts/NFTPairWithOracle.sol   #60

477           uint16 aprBPS

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L477

File: contracts/NFTPairWithOracle.sol   #61

548           uint128 principal = loanParams.valuation;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L548

File: contracts/NFTPairWithOracle.sol   #62

578       uint8 internal constant ACTION_REPAY = 2;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L578

File: contracts/NFTPairWithOracle.sol   #63

579       uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L579

File: contracts/NFTPairWithOracle.sol   #64

581       uint8 internal constant ACTION_REQUEST_LOAN = 12;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L581

File: contracts/NFTPairWithOracle.sol   #65

582       uint8 internal constant ACTION_LEND = 13;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L582

File: contracts/NFTPairWithOracle.sol   #66

585       uint8 internal constant ACTION_BENTO_DEPOSIT = 20;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L585

File: contracts/NFTPairWithOracle.sol   #67

586       uint8 internal constant ACTION_BENTO_WITHDRAW = 21;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L586

File: contracts/NFTPairWithOracle.sol   #68

587       uint8 internal constant ACTION_BENTO_TRANSFER = 22;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L587

File: contracts/NFTPairWithOracle.sol   #69

588       uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L588

File: contracts/NFTPairWithOracle.sol   #70

589       uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L589

File: contracts/NFTPairWithOracle.sol   #71

592       uint8 internal constant ACTION_CALL = 30;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L592

File: contracts/NFTPairWithOracle.sol   #72

595       uint8 internal constant ACTION_REQUEST_AND_BORROW = 40;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L595

File: contracts/NFTPairWithOracle.sol   #73

596       uint8 internal constant ACTION_TAKE_COLLATERAL_AND_LEND = 41;

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L596

File: contracts/NFTPairWithOracle.sol   #74

641       ) internal returns (bytes memory, uint8) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L641

File: contracts/NFTPairWithOracle.sol   #75

642           (address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) = abi.decode(

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L642

File: contracts/NFTPairWithOracle.sol   #76

675               uint8 action = actions[i];

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L675

File: contracts/NFTPairWithOracle.sol   #77

692                   (address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) = abi.decode(

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L692

File: contracts/NFTPairWithOracle.sol   #78

708                   (bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2);

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L708

12. Duplicated require()/revert() checks should be refactored to a modifier or function

Saves deployment costs

File: contracts/NFTPair.sol   #1

251               require(msg.sender == loan.borrower, "NFTPair: not the borrower");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L251

File: contracts/NFTPair.sol   #2

405           require(block.timestamp <= deadline, "NFTPair: signature expired");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L405

File: contracts/NFTPairWithOracle.sol   #3

271               require(msg.sender == loan.borrower, "NFTPair: not the borrower");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L271

File: contracts/NFTPairWithOracle.sol   #4

436           require(block.timestamp <= signature.deadline, "NFTPair: signature expired");

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L436

13. Functions guaranteed to revert when called by normal users can be marked payable

If a function modifier such as onlyOwner is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2),DUP1(3),ISZERO(3),PUSH2(3),JUMPI(10),PUSH1(3),DUP1(3),REVERT(0),JUMPDEST(1),POP(2), which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost

File: contracts/NFTPair.sol   #1

728       function setFeeTo(address newFeeTo) public onlyOwner {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L728

File: contracts/NFTPairWithOracle.sol   #2

750       function setFeeTo(address newFeeTo) public onlyOwner {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L750

14. > uint128(-1) is cheaper than >= 2**128

File: contracts/NFTPair.sol   #1

500          if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPair.sol#L500

File: contracts/NFTPairWithOracle.sol   #2

533          if (interest >= 2**128) {

https://github.com/code-423n4/2022-04-abranft/blob/5cd4edc3298c05748e952f8a8c93e42f930a78c2/contracts/NFTPairWithOracle.sol#L533

#0 - cryptolyndon

2022-05-14T01:07:24Z

1 is valid.

11 probably contains a few opportunities, but suggesting that structs, one of which currently fits in 256 bits, use all 256-bit values casts suspicion over the rest of the entire report. Did that get in by accident?

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