AbraNFT contest - reassor'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: 22/59

Findings: 2

Award: $232.66

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

170.9951 MIM - $171.00

Labels

bug
QA (Quality Assurance)
sponsor disputed

External Links

1. Risky external calls as NFTPair contract

Risk

Low

Impact

Function NFTPair.cook allows executing external calls and can be trigger by anyone. For security reasons it is forbidden to invoke calls to addresses of bentoBox, collateral and the NFTPair contract itself.

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

Analyzing other contracts that could be called by NFTPair leads to LendingClubMock that has in some of its functions logic that checks if the msg.sender is NFTPair contract.

  • LendingClubMock.willLend
if (msg.sender != address(nftPair)) { return false; }
  • LendingClubMOck.lendingConditions
if (_nftPair != address(nftPair)) { TokenLoanParams memory empty; return empty; } else {

Functions willLend and lendingConditions are view functions so in current implementation there is direct security risk in bypassing implemented checks but that will depend on the future development of LendingClub contracts.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to add documentation that will clearly describe mechanism that anyone can trigger external calls as NFTPair contract and its address should not be used for any checks.

2. Lost NFT if claimed to contract that does not support ERC721

Risk

Low

Impact

Function NFTPair.removeCollateral allows deleting a loan and removing collateral by borrower if the loan was not received or by lender as a part of liquidation process. It is possible to pass as an argument address of to account that will receive the removed collateral. If the value of to is an address of contract that does not support handling ERC721 tokens the NFT will be lost and it will be not possible to recover it.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to use safeTransferFrom that checks if the address that should receive NFT is able to handle ERC721 tokens.

3. Missing zero address checks

Risk

Low

Impact

Multiple functions of NFTPair contract do not check for zero addresses which might lead to loss of funds, failed transactions and can break the protocol functionality.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to add zero address checks for listed parameters.

4. Reentrancy and checks-effects-interactions pattern

Risk

Low

Impact

Contract NFTPair in multiple functions does not follow checks-effects-interactions pattern which might lead to reentrancy attacks and potential loss of funds.

Vulnerable functions:

  • _requestLoan triggered through requestLoan
  • _lend triggered through lend
  • withdrawFees

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to follow checks-effects-interactions pattern or add openzeppelin's reentrancy guard to protect against reentrancy attacks.

5. Old solidity version

Risk

Non-Critical

Impact

Contract NFTPair uses old solidity version 0.6.12. Using the latest versions might make contracts susceptible to undiscovered compiler bugs.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to use newer solidity version such as 0.8.10.

6. Event LogRemoveCollateral unindexed parameter

Risk

Non-Critical

Impact

Event LogRemoveCollateral is missing indexing for parameter address recipient.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to add indexed into address recipient parameter.

7. Emitting LogWithdrawFees for 0 _shares

Risk

Non-Critical

Impact

Function NFTPair.withdrawFees allows withdrawing fees. Even if the number of _shares is 0 and there is no transfer still event LogWithdrawFees is emitted which might confuse off-chain monitoring applications.

Proof of Concept

It is recommended not to emit LogWithdrawFees if the value of _shares is 0.

8. Using max type should be used instead of magic number

Risk

Non-Critical

Impact

Function NFTPair.calculateInterest checks if final interest fits into 128bit value. Following if statement is implemented:

if (interest >= 2**128) {

Proof of Concept

It is recommended to use type(int256).max for comparison instead of magic number 2*128.

Awards

61.6553 MIM - $61.66

Labels

bug
G (Gas Optimization)

External Links

1. Cache Array Length Outside of Loop

Impact

Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to cache array length outside of loop

uint256 length = actions.length;

2. Use != 0 instead of > 0 for unsigned integer comparison

Impact

When dealing with unsigned integer types, comparisons with != 0 are cheaper that with > 0.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to use != 0 instead of > 0:

if(_share != 0) { (..) }

3. Long revert strings

Impact

Shortening revert strings to fit in 32 bytes will decrease gas costs for deployment and gas costs when the revert condition has been met.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to decrease revert messages to maximum 32 bytes.

4. ++i costs less gas compared to i++ or i += 1

Impact

++i costs less gas compared to i++ or i += 1 for unsigned integer, as pre-increment is cheaper (about 5 gas per iteration).

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to use ++i instead of i++ to increment value of an unsigned integer variable.

5. Unnecessary calculations

Impact

Function NFTPair.calculateInterest checks if final interest fits into 128bit value. Following if statement is implemented:

if (interest >= 2**128) {

Statement performs unnecessary calculations 2**128 which can be simply replaced with type(int256).max.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to use type(int256).max for comparison instead of magic number 2*128.

6. No need to explicitly initialize variables with default values

Impact

If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for addresses). Explicitly initializing it with its default value is an anti-pattern and waste of gas.

As an example: for (uint256 i = 0; i < numIterations; ++i) { should be replaced with for (uint256 i; i < numIterations; ++i) {.

Proof of Concept

Tools Used

Manual Review / VSCode

It is recommended to remove explicit initializations with default values.

#0 - cryptolyndon

2022-05-14T01:15:50Z

Seen, thanks

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