Halborn Logo

icon

Bundles/Airdrop - NFTfi


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: November 15th, 2022 - December 1st, 2022

Summary

93% of all REPORTED Findings have been addressed

All findings

14

Critical

0

High

0

Medium

2

Low

3

Informational

9


1. INTRODUCTION

NFTfi engaged Halborn to conduct a security audit on their smart contracts beginning on 2022-11-15 and ending on 2022-12-01. The security assessment was scoped to the smart contracts provided to the Halborn team.

2. AUDIT SUMMARY

The team at Halborn was provided two weeks for the engagement and assigned a full-time security engineer to audit the security of the smart contract. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this audit is to:

    • Ensure that smart contract functions operate as intended

    • Identify potential security issues with the smart contracts

In summary, Halborn identified some security risks that were mostly addressed by the NFTfi team. The critical security finding was identified and fixed by the NFTfi team, and Halborn verified the fix.

3. TEST APPROACH & METHODOLOGY

Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of this audit. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of the contracts' solidity code and can quickly identify items that do not follow security best practices. The following phases and associated tools were used throughout the term of the audit:

    • Research into architecture and purpose.

    • Smart contract manual code review and walkthrough.

    • Manual assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic related vulnerability classes.

    • Manual testing by custom scripts. (Brownie).

    • Static Analysis of security for scoped contract, and imported functions manually.

    • Testnet deployment (Ganache).

4. SCOPE

Repository: eth.immutable-bundles

The security assessment was scoped to every smart contract in the audit-09-11-2022 branch.

    1. Initial Commit ID: 2c7ef51f7f820c65d93f57490c77bf67a4578773

    2. Changes to Initial Commit ID:

compare/audit-09-11-2022...3140329bec42b848dc008fd037aba1fec4a77ef5 - Added Pausing capabilities to ImmutableBundle.sol - Fixed wrong event parameter in NftfiBundler.sol line 215 - Changed variable names on NftfiBundler.sol - Added URI metadata to NftfiBundler.sol, PersonalBundler.sol, and ImmutableBundle.sol - Added token name and symbol for PersonalBundler.sol - Added _setOwner() call in PersonalBundler.sol's initialize() function

    1. Remediations Commit ID: 29be173454e816c58d98dea2614750dbc27bc39a

    2. Remediations Commit ID 2: dc376d9ba96db9f9ac3718599f05b5bf3f8f7c61

    3. Retest Commit ID: b39030551b8492b78f3fa1827ad840fcd01daace

5. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

6. SCOPE

Out-of-Scope: New features/implementations after the remediation commit IDs.

7. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

2

Low

3

Informational

9

Impact x Likelihood

HAL-03

HAL-02

HAL-07

HAL-09

HAL-04

HAL-05

HAL-06

HAL-01

HAL-10

HAL-11

HAL-12

HAL-13

HAL-14

HAL-08

Security analysisRisk levelRemediation Date
MISTAKENLY SENT BUNDLE TOKENS CAN NOT BE RESCUEDMediumSolved - 12/09/2022
POSSIBLE LOSS OF OWNERSHIPMediumSolved - 12/09/2022
SENDELEMENTSTOPERSONALBUNDLER() FUNCTION CAN RUN INTO AN INFINITE LOOPLowSolved - 12/09/2022
ADD OR REMOVE BUNDLE ELEMENTS FUNCTIONS MAY RUN OUT OF GASLowRisk Accepted
MISSING PARAMETER VALIDATIONLowRisk Accepted
BUNDLES INSIDE IMMUTABLEBUNDLES CONTRACT CAN BE EXTRACTEDInformational-
USE OF INLINE ASSEMBLYInformationalAcknowledged
LOOP GAS USAGE OPTIMIZATIONInformationalSolved - 12/09/2022
SOLC 0.8.4 COMPILER VERSION CONTAINS MULTIPLE BUGSInformationalSolved - 12/09/2022
SPLITTING REQUIRE() STATEMENTS THAT USES AND OPERATOR SAVES GASInformationalSolved - 12/09/2022
UNNECESSARY IMPORTSInformationalSolved - 12/09/2022
ANYONE CAN ADD TOKENS TO ANY BUNDLE OR PERSONALBUNDLEInformationalAcknowledged
OPEN TODOsInformationalSolved - 12/09/2022
INCOMPLETE NATSPEC DOCUMENTATIONInformationalSolved - 12/09/2022

8. Findings & Tech Details

8.1 MISTAKENLY SENT BUNDLE TOKENS CAN NOT BE RESCUED

// Medium

Description

Users can lock their bundles (created with the NftfiBundler or PersonalBundler contracts) by transferring them to the ImmutableBundle contract with the safeTransferFrom() function. However, these contracts rely on users sending tokens to them with the appropriate functions (e.g., safeTransferFrom or getChild instead of transfer and transferFrom) to properly record those transactions.

The ImmutableBundle contract allows the admin to recover ERC721 tokens with the rescueERC721 function. However, this function does not allow rescuing NftfiBundler or PersonalBundler tokens; therefore, it is impossible to recover bundles that were accidentally transferred with the wrong transfer functions (e.g., transfer or transferFrom).

Code Location

ImmutableBundle.sol

    /**
     * @notice used by the owner account to be able to drain ERC721 tokens received as airdrops
     * for the locked  collateral NFT-s
     * @param _tokenAddress - address of the token contract for the token to be sent out
     * @param _tokenId - id token to be sent out
     * @param _receiver - receiver of the token
     */
    function rescueERC721(
        address _tokenAddress,
        uint256 _tokenId,
        address _receiver
    ) external onlyOwner {
        IERC721 tokenContract = IERC721(_tokenAddress);
        require(
            _tokenAddress != address(bundler) &&
                !PersonalBundlerFactory(personalBundlerFactory).personalBundlerExists(msg.sender),
            "token is a bundle"
        );
        require(tokenContract.ownerOf(_tokenId) == address(this), "nft not owned");
        tokenContract.safeTransferFrom(address(this), _receiver, _tokenId);
    }

As a proof of concept, user2 bundles NFTs 1, 2, and 3 with NftfiBundler and transfers them to the ImmutableBundle contract with the transferFrom() function, which locks the bundle (and the NFTs contained in it) forever:

rescuePoC.png
Score
Impact: 3
Likelihood: 3
Recommendation

SOLVED: The NFTfi team solved the issue by requiring immutableOfBundle[_tokenId] or immutableOfPersonalBundler[_tokenAddress] to be 0, which means that NftfiBundler or PersonalBundler tokens not associated with no immutable bundle can be extracted.

Commit ID: 52f68e41a729e83f27c1cb747a464a2367132d5b

8.2 POSSIBLE LOSS OF OWNERSHIP

// Medium

Description

When transferring the ownership of the protocol, no checks are performed on whether the new address is valid and active. In case there is a mistake when transferring the ownership, the whole protocol may lose all of its ownership functionalities.

Code Location

Ownable.sol

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address _newOwner) public virtual onlyOwner {
        require(_newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(_newOwner);
    }
Score
Impact: 5
Likelihood: 1
Recommendation

SOLVED: The NFTfi team solved the issue by implementing a two-step ownership transfer process.

Commit ID: 52f68e41a729e83f27c1cb747a464a2367132d5b

8.3 SENDELEMENTSTOPERSONALBUNDLER() FUNCTION CAN RUN INTO AN INFINITE LOOP

// Low

Description

Users can call sendElementsToPersonalBundler() function to move every token inside a bundle to a personal bundle. This function uses a while loop to iterate through every childToken of every childContract until childContracts[_tokenId] and childTokens[_tokenId][childContrac] lengths are 0, meaning that no more child tokens are held in the bundle.

However, if tokens are already in a personal bundle, and they are transferred to the same bundle, or if they are in a NftfiBundler bundle with id = 1 and they're being transferred to the same NftfiBundler bundle (the second scenario is less likely than the first one), the function runs into an infinite loop, since the lengths mentioned above will never decrease, keeping the while loop running until it spends the max amount of gas allowed for the call, reverting the state and incurring unnecessary cost to the user.

Code Location

NftfiBundler.sol

    /**
     * @notice Remove all the children from the bundle and send to personla bundler.
     * If bundle contains a legacy ERC721 element, this will not work.
     * @dev This method may run out of gas if the list of children is too big. In that case, children can be removed
     *      individually.
     * @param _tokenId the id of the bundle
     * @param _personalBundler address of the receiver of the children
     */
    function sendElementsToPersonalBundler(uint256 _tokenId, address _personalBundler) external {
        _validateReceiver(_personalBundler);
        _validateTransferSender(_tokenId);

        //fix this actual personalBundlerExists
        require(
            IERC165(_personalBundler).supportsInterface(type(IERC998ERC721TopDown).interfaceId),
            "has to implement IERC998ERC721TopDown"
        );
        uint256 personalBundleId = 1;
        //make sure sendeer owns personal bundler token
        require(IERC721(_personalBundler).ownerOf(personalBundleId) == msg.sender, "has to own personal bundle token");

        // In each iteration all contracts children are removed, so eventually all contracts are removed
        while (childContracts[_tokenId].length() > 0) {
            address childContract = childContracts[_tokenId].at(0);

            // In each iteration a child is removed, so eventually all contracts children are removed
            while (childTokens[_tokenId][childContract].length() > 0) {
                uint256 childId = childTokens[_tokenId][childContract].at(0);

                _removeChild(_tokenId, childContract, childId);

                try
                    IERC721(childContract).safeTransferFrom(
                        address(this),
                        _personalBundler,
                        childId,
                        abi.encodePacked(personalBundleId)
                    )
                {
                    // solhint-disable-previous-line no-empty-blocks
                } catch {
                    revert("only safe transfer");
                }
                emit TransferChild(_tokenId, _personalBundler, childContract, childId);
            }
        }
    }

As a proof of concept, user2 bundles NFTs 1, 2, and 3 with the NftfiBundler contract. From there, the NFTs are being transferred to the user2's personal bundler with the sendElementsToPersonalBundler() function, and then they are transferred again to the same personalbundle. This makes the sendElementsToPersonalBundler() function to run into an infinite loop, which ends up with crashing the test environment. dosPoC.png

Score
Impact: 2
Likelihood: 2
Recommendation

SOLVED: The NFTfi team solved the issue by preventing sendElementsToPersonalBundler() from being called with msg.sender as the _personalBundler address.

Commit ID: 478ae0542a50367defd1f39047f418806205f7aa

8.4 ADD OR REMOVE BUNDLE ELEMENTS FUNCTIONS MAY RUN OUT OF GAS

// Low

Description

Users can use functions to add or remove multiple NFTs at the same time in the NftfiBundler or PersonalBundler contracts. These functions can have high gas costs based on the number of tokens transferred. Adding elements also calls an external validator contract to check whether the asset is permitted or not, further increasing the gas cost.

Many users use wallets with default gas limit configured. When the limit is reached, the users lose a significant amount of Ether in those failed transactions.

The affected functions:

NftfiBundler.sol

  • buildBundle
  • addBundleElements
  • removeBundleElements
  • addAndRemoveBundleElements
  • decomposeBundle
  • sendElementsToPersonalBundler
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The NFTfi team accepted the risk of this finding. In addition, gas limit and maximum bundle size checks will be implemented in the front-end.

8.5 MISSING PARAMETER VALIDATION

// Low

Description

The childContractByIndex and childTokenByIndex functions of the ERC998TopDown contract did not validate their parameters. Setting invalid values may result in reverts without error messages.

contracts/NftfiBundler.sol:

  • The constructor of the contract does not validate that the _permittedNfts parameter is not a zero address.
  • The constructor of the contract does not validate that the _airdropFlashLoan parameter is not a zero address.

contracts/ImmutableBundle.sol:

  • The constructor of the contract does not validate that the _bundler parameter is not a zero address.
  • The constructor of the contract does not validate that the _personalBundlerFactory parameter is not a zero address.

contracts/PersonalBundlerFactory.sol:

  • The constructor of the contract does not validate that the _personalBundlerImplementation parameter is not a zero address.

contracts/ERC998TopDown.sol:

  • The childContractByIndex function does not validate that the _index parameter is a valid index.
  • The childTokenByIndex function does not validate that the _index parameter is a valid index.

contracts/utils/Ownable.sol:

  • The constructor of the contract does not validate that the _initialOwner parameter is not a zero address.
Score
Impact: 2
Likelihood: 2
Recommendation

RISK ACCEPTED: The NFTfi team accepted the risk of this finding.

8.6 BUNDLES INSIDE IMMUTABLEBUNDLES CONTRACT CAN BE EXTRACTED

// Informational

Description
Finding description placeholder
Score
Impact:
Likelihood:

8.7 USE OF INLINE ASSEMBLY

// Informational

Description

Inline assembly is a way to access the Ethereum Virtual Machine at a low level. This discards several important safety features of Solidity and the static compiler. Because the EVM is a stack machine, it is often hard to address the correct stack slot and provide arguments to opcodes at the correct point on the stack. Solidity’s inline assembly tries to facilitate that and other issues arising when writing manual assembly. Assembly is much more difficult to write because the compiler does not perform checks, so the contract developer should be aware of this warning.

Code Location

ERC998TopDown.sol

assembly {
    parentTokenOwner := or(ERC998_MAGIC_VALUE, parentTokenOwnerAddress)
}

ERC998TopDown.sol

assembly {
    rootOwner := or(ERC998_MAGIC_VALUE, rootOwnerAddress)
}

ERC998TopDown.sol

assembly {
    tokenId := mload(add(_data, 0x20))
}
Score
Impact: 2
Likelihood: 1
Recommendation

ACKNOWLEDGED: The NFTfi team acknowledged this issue.

8.8 LOOP GAS USAGE OPTIMIZATION

// Informational

Description

Multiple gas cost optimization opportunities were identified in the loops of the NftfiBundler contract:

  • Unnecessary reading of the array length on each iteration wastes gas.

  • Using != consumes less gas than <.

  • It is possible to further optimize loops by using unchecked loop index incrementing and decrementing.

  • Loop counters do not need to be set to 0, since uint256 is already initialized to 0.

Code Location

contracts/NftfiBundler.sol

  • Line 180 for (uint256 i = 0; i < _bundleElements.length; ++i) {
  • Line 193 for (uint256 j = 0; j < _bundleElements[i].ids.length; ++j) {
  • Line 192 for (uint256 j = 0; j < _bundleElements[i].ids.length; ++j) {
  • Line 204 for (uint256 i = 0; i < _bundleElements.length; ++i) {
  • Line 206 for (uint256 j = 0; j < _bundleElements[i].ids.length; ++j) {

contracts/PermittedNFTs.sol

  • Line 120 for (uint256 i = 0; i < _nftContracts.length; ++i) {
Score
Impact: 1
Likelihood: 2
Recommendation

SOLVED: The NFTfi team implemented the recommended gas optimizations.

Commit ID: d93033e7d122168797981dfbd439374fbe5d4dd2

8.9 SOLC 0.8.4 COMPILER VERSION CONTAINS MULTIPLE BUGS

// Informational

Description

The scoped contracts have configured the fixed pragma set to 0.8.4. The latest solidity compiler version, 0.8.17, fixed important bugs in the compiler along with new native protections. The current version is missing the following fixes: 0.8.5, 0.8.6, 0.8.7, 0.8.8, 0.8.9, 0.8.12, 0.8.13, 0.8.14, 0.8.15, 0.8.16, 0.8.17.

The official Solidity's recommendations are that you should use the latest released version of Solidity when deploying contracts. Apart from exceptional cases, only the newest version receives security fixes.

Score
Impact: 2
Likelihood: 1
Recommendation

SOLVED: The NFTfi team bumped the Solidity compiler version to 0.8.17.

Commit ID: faa56c0d56293a7a43008a4c2f4f2500ba131cbf

8.10 SPLITTING REQUIRE() STATEMENTS THAT USES AND OPERATOR SAVES GAS

// Informational

Description

Instead of using the `&&`` operator in a single require statement to check multiple conditions, using multiple require statements with one condition per require statement saves 8 GAS per operation. The gas difference can only be realized if the revert condition is satisfied.

Code Location

ImmutableBundle.sol

    /**
     * @notice used by the owner account to be able to drain ERC721 tokens received as airdrops
     * for the locked  collateral NFT-s
     * @param _tokenAddress - address of the token contract for the token to be sent out
     * @param _tokenId - id token to be sent out
     * @param _receiver - receiver of the token
     */
    function rescueERC721(
        address _tokenAddress,
        uint256 _tokenId,
        address _receiver
    ) external onlyOwner {
        IERC721 tokenContract = IERC721(_tokenAddress);
        require(
            _tokenAddress != address(bundler) &&
                !PersonalBundlerFactory(personalBundlerFactory).personalBundlerExists(msg.sender),
            "token is a bundle"
        );
        require(tokenContract.ownerOf(_tokenId) == address(this), "nft not owned");
        tokenContract.safeTransferFrom(address(this), _receiver, _tokenId);
    }

The following tests were carried out in Remix with optimization turned both on and off

    require ( a > 1 && a < 5, "Initialized");
    return  a + 2;

Execution cost 21617 with optimization and using && 21976 without optimization and using &&

After splitting the require statement

    require (a > 1 ,"Initialized");
    require (a < 5 , "Initialized");
    return a + 2;

Execution cost 21609 with optimization and split require 21968 without optimization and using split require

Score
Impact: 1
Likelihood: 1
Recommendation

SOLVED: The NFTfi team solved the issue by refactoring the mentioned require statement.

Commit ID: 52f68e41a729e83f27c1cb747a464a2367132d5b

8.11 UNNECESSARY IMPORTS

// Informational

Description

The following library imports can be removed because they are redundant or not used in the contracts:

  • IERC20.sol is also included in SafeERC20.sol.
  • IERC1155.sol is not used in some contracts.
  • ERC721Holder.sol is not used in some contracts.

Code Location

contracts/NftfiBundler.sol

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

contracts/ImmutableBundle.sol

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

``` {language=solidity firstnumber="7 " caption="contracts/PersonalBundler.sol"} import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";


``` {language=solidity firstnumber="8" caption="contracts/airdrop/AirdropFlashLoan.sol"}
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
Score
Impact: 1
Likelihood: 1
Recommendation

SOLVED: The NFTfi team solved the issue by removing unnecessary imports.

Commit ID: fef3ac4bb4eb87a78e43082a560365a767178aae

8.12 ANYONE CAN ADD TOKENS TO ANY BUNDLE OR PERSONALBUNDLE

// Informational

Description

Users can add any whitelisted NFTs to their bundles or personal bundles with the safeTransferFrom() or getChild() functions.

However, it has been detected that no checks are in place to ensure that users can only add tokens to bundles they already own. Those kinds of checks are already implemented in functions such as sendElementsToPersonalBundler, in which the owner of any bundle can only send the tokens to a personal bundle they own:

NftfiBundler.sol

    /**
     * @notice Remove all the children from the bundle and send to personla bundler.
     * If bundle contains a legacy ERC721 element, this will not work.
     * @dev This method may run out of gas if the list of children is too big. In that case, children can be removed
     *      individually.
     * @param _tokenId the id of the bundle
     * @param _personalBundler address of the receiver of the children
     */
    function sendElementsToPersonalBundler(uint256 _tokenId, address _personalBundler) external {
        _validateReceiver(_personalBundler);
        _validateTransferSender(_tokenId);

        //fix this actual personalBundlerExists
        require(
            IERC165(_personalBundler).supportsInterface(type(IERC998ERC721TopDown).interfaceId),
            "has to implement IERC998ERC721TopDown"
        );
        uint256 personalBundleId = 1;
        //make sure sendeer owns personal bundler token
        require(IERC721(_personalBundler).ownerOf(personalBundleId) == msg.sender, "has to own personal bundle token");

        // In each iteration all contracts children are removed, so eventually all contracts are removed
        while (childContracts[_tokenId].length() > 0) {
            address childContract = childContracts[_tokenId].at(0);

            // In each iteration a child is removed, so eventually all contracts children are removed
            while (childTokens[_tokenId][childContract].length() > 0) {
                uint256 childId = childTokens[_tokenId][childContract].at(0);

                _removeChild(_tokenId, childContract, childId);

                try
                    IERC721(childContract).safeTransferFrom(
                        address(this),
                        _personalBundler,
                        childId,
                        abi.encodePacked(personalBundleId)
                    )
                {
                    // solhint-disable-previous-line no-empty-blocks
                } catch {
                    revert("only safe transfer");
                }
                emit TransferChild(_tokenId, _personalBundler, childContract, childId);
            }
        }
    }

This behavior allows the owner of the bundle to extract any token included in it, no matter who was the original owner. This can be used to use NFTfi reputation to perform phishing campaigns or any similar malicious activity that might have a reputational impact on the project.

Score
Impact: 1
Likelihood: 1
Recommendation

**ACKNOWLEDGED: ** The NFTfi team acknowledged this issue.

8.13 OPEN TODOs

// Informational

Description

Open To-dos can point to architecture or programming issues that still need to be resolved. Often these kinds of comments indicate areas of complexity or confusion for developers. This provides value and insight to an attacker who aims to cause damage to the protocol.

Code Location

NftfiBundler.sol

        //fix this actual personalBundlerExists
Score
Impact: 1
Likelihood: 1
Recommendation

SOLVED: The NFTfi team solved the issue by removing every TODO present in the code.

Commit ID: d93033e7d122168797981dfbd439374fbe5d4dd2

8.14 INCOMPLETE NATSPEC DOCUMENTATION

// Informational

Description

Natspec documentation are useful for internal developers that need to work on the project, external developers that need to integrate with the project, auditors that have to review it but also for end users given that many chain explorers have officially integrated the support for it directly on their site.

It has been detected that, while many contracts have a complete natspec documentation, other contracts or functions are little to no documented.

Score
Impact: 1
Likelihood: 1
Recommendation

SOLVED: The NFTfi team added the missing natspec documentation.

Commit IDs:

9. Automated Testing

STATIC ANALYSIS REPORT

Description

Halborn used automated testing techniques to enhance the coverage of certain areas of the scoped contracts. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified all the contracts in the repository and was able to compile them correctly into their ABI and binary formats, Slither was run on the all-scoped contracts. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.

Slither results

AirdropFlashLoan.sol

PersonalBundlerFactory.sol, PersonalBundler.sol, NftfiBundler.sol, and ERC998TopDown.sol

ImmutableBundle.sol

  • All the reentrancies flagged are false positives.

  • No major issues were found by Slither.

AUTOMATED SECURITY SCAN

Description

Halborn used automated security scanners to assist with detection of well-known security issues, and to identify low-hanging fruits on the targets for this engagement. Among the tools used was MythX, a security analysis service for Ethereum smart contracts. MythX performed a scan on all the contracts and sent the compiled results to the analyzers to locate any vulnerabilities.

MythX results

AirdropFlashLoan.sol

NftfiBundler.sol

PersonalBundler.sol

ImmutableBundler.sol

  • No major issues were found by MythX.

Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.