ParaSpace contest - delfin454000's results

The First Ever Cross-Margin NFT Financialization Protocol.

General Information

Platform: Code4rena

Start Date: 28/11/2022

Pot Size: $192,500 USDC

Total HM: 33

Participants: 106

Period: 11 days

Judge: LSDan

Total Solo HM: 15

Id: 186

League: ETH

ParaSpace

Findings Distribution

Researcher Performance

Rank: 28/106

Findings: 1

Award: $882.55

QA:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

Summary

Low risk findings

IssueDescriptionInstances
1Unused/empty receive() function1
2Open TODO re incentives1
3Outdated Open Zeppelin versions1
Total3

Non-critical findings

IssueDescriptionInstances
1Constant value definitions including call to keccak256 should use immutable1
2nonReentrant modifier should appear before all other modifiers25
3Missing NatSpec for some constructors7
4Missing NatSpec for event1
5Partially missing NatSpec for some functions24
6Completely missing NatSpec for some functions66
7Long single-line comments17
8Typos9
9Unclear comment1
10Inconsistent comment // spacingmultiple
11Solidity version should be upgraded1
12TODOs and other open items4
Total> 156

Low risk findings

No.Explanation + instances
1Unused/empty receive() function`
Leaving the receive() below without a revert could result in a loss of funds for a user who sends Ether to the contract (having no way to get anything back)

NTokenUniswapV3.sol: L149

    receive() external payable {}

No.Explanation + instances
2Open TODO re incentives
Open items re incentives such as the TODO below should be addressed before deployment

LooksRareAdapter.sol: L59

            makerAsk.price, // TODO: take minPercentageToAsk into account

No.Explanation + instances
3Outdated Open Zeppelin versions
Outdated Open Zeppelin version are used (v4.7.0 and earlier)
The versions used have known vulnerabilities, including three with high severity. See Security Advisories.


Non-critical findings

No.Description
1Constant value definitions including call to keccak256 should use immutable
Constant value definitions such as the call to keccak256 below should use immutable instead of constant

NFTFloorOracle.sol: L70

    bytes32 public constant UPDATER_ROLE = keccak256("UPDATER_ROLE");

No.Description
2nonReentrant modifier should appear before all other modifiers
Adopting this rule protects against the possibilty of reentrancy in other modifiers

MintableIncentivizedERC721.sol: L458-462

    function setIsUsedAsCollateral(
        uint256 tokenId,
        bool useAsCollateral,
        address sender
    ) external virtual override onlyPool nonReentrant returns (bool) {

The nonReentrant modifier is similarly mispositioned in the following functions:

MintableIncentivizedERC721.sol: L474-484

MintableIncentivizedERC721.sol: L529-535

MintableIncentivizedERC721.sol: L540-546

NToken.sol: L79-82

NToken.sol: L87-91

NToken.sol: L119-123

NToken.sol: L199-205

NToken.sol: L214-220

NTokenApeStaking.sol: L102-106

NTokenApeStaking.sol: L159-163

NTokenBAYC.sol: L26-30

NTokenBAYC.sol: L39-43

NTokenBAYC.sol: L52-55

NTokenBAYC.sol: L66-70

NTokenBAYC.sol: L82-85

NTokenBAYC.sol: L97-100

NTokenMAYC.sol: L26-30

NTokenMAYC.sol: L39-43

NTokenMAYC.sol: L52-55

NTokenMAYC.sol: L66-70

NTokenMAYC.sol: L82-85

NTokenMAYC.sol: L97-100

NTokenMoonBirds.sol: L40-44

NTokenUniswapV3.sol: L123-130


No.Description
3Missing NatSpec for some constructors
NatSpec (consisting of an @notice or an @dev statement, as well as @param statements for any variables) is provided for most of the constructors in the contest. Below are instances of constructors with partially or completely missing NatSpec, which should be added to provide information and for consistency:

UniswapV3OracleWrapper.sol: L23-27

    constructor(
        address _factory,
        address _manager,
        address _addressProvider
    ) {

Missing: All NatSpec


DefaultReserveAuctionStrategy.sol: L50-57

    constructor(
        uint256 maxPriceMultiplier,
        uint256 minExpPriceMultiplier,
        uint256 minPriceMultiplier,
        uint256 stepLinear,
        uint256 stepExp,
        uint256 tickLength
    ) {

Missing: All NatSpec


MintableIncentivizedERC721.sol: L77-88

    /**
     * @dev Constructor.
     * @param pool The reference to the main Pool contract
     * @param name_ The name of the token
     * @param symbol_ The symbol of the token
     */
    constructor(
        IPool pool,
        string memory name_,
        string memory symbol_,
        bool atomic_pricing
    ) {

Missing: @param atomic_pricing


NToken.sol: L39-49

    /**
     * @dev Constructor.
     * @param pool The address of the Pool contract
     */
    constructor(IPool pool, bool atomic_pricing)
        MintableIncentivizedERC721(
            pool,
            "NTOKEN_IMPL",
            "NTOKEN_IMPL",
            atomic_pricing
        )

Missing: @param atomic_pricing


NTokenApeStaking.sol: L28-34

    /**
     * @dev Constructor.
     * @param pool The address of the Pool contract
     */
    constructor(IPool pool, address apeCoinStaking) NToken(pool, false) {
        _apeCoinStaking = ApeCoinStaking(apeCoinStaking);
    }

Missing: @param apeCoinStaking


NTokenBAYC.sol: L10-17

/**
 * @title BAYC NToken
 *
 * @notice Implementation of the NToken for the ParaSpace protocol
 */
contract NTokenBAYC is NTokenApeStaking {
    constructor(IPool pool, address apeCoinStaking)
        NTokenApeStaking(pool, apeCoinStaking)

Missing: @param statements


NTokenMAYC.sol: L10-17

/**
 * @title MAYC NToken
 *
 * @notice Implementation of the NToken for the ParaSpace protocol
 */
contract NTokenMAYC is NTokenApeStaking {
    constructor(IPool pool, address apeCoinStaking)
        NTokenApeStaking(pool, apeCoinStaking)

Missing: @param statements



No.Description
4Missing NatSpec for event
Explanations (often consisting of an @notice or an @dev statement) are provided for almost all of the events in the contest. Below are two instances of events without such NatSpec:

PoolApeStaking.sol: L37-45

    event ReserveUsedAsCollateralEnabled(
        address indexed reserve,
        address indexed user
    );

    event ReserveUsedAsCollateralDisabled(
        address indexed reserve,
        address indexed user
    );


No.Description
5Partially missing NatSpec for some functions
NatSpec is partially missing for the following external or public functions. Note that, while it may be informative, NatSpec is not required for internal or private functions. I'm also ignoring functions preceded by references such as @inheritdoc IPriceOracleGetter

NFTFloorOracle.sol: L174-177

    /// @notice Allows owner to update oracle configs
    function setConfig(uint128 expirationPeriod, uint128 maxPriceDeviation)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)

Missing: @param expirationPeriod and @param maxPriceDeviation


NFTFloorOracle.sol: L182-185

    /// @notice Allows owner to pause asset
    function setPause(address _asset, bool _flag)
        external
        onlyRole(DEFAULT_ADMIN_ROLE)

Missing: @param _asset and @param _flag


NFTFloorOracle.sol: L218-224

    /// @notice Allows owner to set new price on PriceInformation and updates the
    /// internal Median cumulativePrice.
    /// @param _assets The nft contract to set a floor price for
    function setMultiplePrices(
        address[] calldata _assets,
        uint256[] calldata _twaps
    ) external onlyRole(UPDATER_ROLE) {

Missing: @param _twaps


UniswapV3OracleWrapper.sol: L48-54

    /**
     * @notice get onchain position data from uniswap for the specified tokenId.
     */
    function getOnchainPositionData(uint256 tokenId)
        public
        view
        returns (UinswapV3PositionData memory)

Missing: @param tokenId and @return


UniswapV3OracleWrapper.sol: L93-99

    /**
     * @notice get onchain liquidity amount for the specified tokenId.
     */
    function getLiquidityAmount(uint256 tokenId)
        external
        view
        returns (uint256 token0Amount, uint256 token1Amount)

Missing: @param tokenId and two @return statements


UniswapV3OracleWrapper.sol: L109-115

    /**
     * @notice calculate liquidity amount for the position data.
     * @param positionData The specified position data
     */
    function getLiquidityAmountFromPositionData(
        UinswapV3PositionData memory positionData
    ) public pure returns (uint256 token0Amount, uint256 token1Amount) {

Missing: Two @return statements


UniswapV3OracleWrapper.sol: L124-130

    /**
     * @notice get liquidity provider fee amount for the specified tokenId.
     */
    function getLpFeeAmount(uint256 tokenId)
        external
        view
        returns (uint256 token0Amount, uint256 token1Amount)

Missing: @param tokenId and two @return statements


UniswapV3OracleWrapper.sol: L140-146

    /**
     * @notice calculate liquidity provider fee amount for the position data.
     * @param positionData The specified position data
     */
    function getLpFeeAmountFromPositionData(
        UinswapV3PositionData memory positionData
    ) public view returns (uint256 token0Amount, uint256 token1Amount) {

Missing: Two @return statements


UniswapV3OracleWrapper.sol: L153-156

    /**
     * @notice Returns the price for the specified tokenId.
     */
    function getTokenPrice(uint256 tokenId) public view returns (uint256) {

Missing: @param tokenId and @return


UniswapV3OracleWrapper.sol: L183-189

    /**
     * @notice Returns the price for the specified tokenId array.
     */
    function getTokensPrices(uint256[] calldata tokenIds)
        external
        view
        returns (uint256[] memory)

Missing: @param tokenIds and @return


UniswapV3OracleWrapper.sol: L200-206

    /**
     * @notice Returns the total price for the specified tokenId array.
     */
    function getTokensPricesSum(uint256[] calldata tokenIds)
        external
        view
        returns (uint256)

Missing: @param tokenIds and @return


PoolLogic.sol: L155-180

    /**
     * @notice Returns the user account data across all the reserves
     * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed
     * @return totalDebtBase The total debt of the user in the base currency used by the price feed
     * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed
     * @return currentLiquidationThreshold The liquidation threshold of the user
     * @return ltv The loan to value of The user
     * @return healthFactor The current health factor of the user
     * @return erc721HealthFactor The current erc721 health factor of the user
     **/
    function executeGetUserAccountData(
        address user,
        DataTypes.PoolStorage storage ps,
        address oracle
    )
        external
        view
        returns (
            uint256 totalCollateralBase,
            uint256 totalDebtBase,
            uint256 availableBorrowsBase,
            uint256 currentLiquidationThreshold,
            uint256 ltv,
            uint256 healthFactor,
            uint256 erc721HealthFactor
        )

Missing: @param user, @param ps and @param oracle


NTokenApeStaking.sol: L61-65

    /**
     * @notice Returns the address of BAKC contract address.
     **/
    function getBAKC() public view returns (IERC721) {
        return _apeCoinStaking.nftContracts(ApeStakingLogic.BAKC_POOL_ID);

Missing: @return


NTokenApeStaking.sol: L68-72

    /**
     * @notice Returns the address of ApeCoinStaking contract address.
     **/
    function getApeStaking() external view returns (ApeCoinStaking) {
        return _apeCoinStaking;

Missing: @return


NTokenApeStaking.sol: L99-106

    /**
     * @notice Overrides the burn from NToken to withdraw all staked and pending rewards before burning the NToken on liquidation/withdraw
     */
    function burn(
        address from,
        address receiverOfUnderlying,
        uint256[] calldata tokenIds
    ) external virtual override onlyPool nonReentrant returns (uint64, uint64) {

Missing: @param from, @param receiverOfUnderlying, @param tokenIds and two @return statements


NTokenApeStaking.sol: L178-185

    /**
     * @notice get user total ape staking position
     * @param user user address
     */
    function getUserApeStakingAmount(address user)
        external
        view
        returns (uint256)

Missing: @return


NTokenMoonBirds.sol: L92-96

    /**
        @dev an additional function that is custom to MoonBirds reserve.
        This function allows NToken holders to toggle on/off the nesting the status for the underlying tokens
    */
    function toggleNesting(uint256[] calldata tokenIds) external {

Missing: @param tokenIds


NTokenMoonBirds.sol: L107-118

    /**
        @dev an additional function that is custom to MoonBirds reserve.
        This function allows NToken holders to get nesting the state for the underlying tokens
    */
    function nestingPeriod(uint256 tokenId)
        external
        view
        returns (
            bool nesting,
            uint256 current,
            uint256 total
        )

Missing: @param tokenId and three @return statements


NTokenMoonBirds.sol: L123-128

    /**
        @dev an additional function that is custom to MoonBirds reserve.
        This function check if nesting is open for the underlying tokens
    */
    function nestingOpen() external returns (bool) {
        return IMoonBird(_underlyingAsset).nestingOpen();

Missing: @return


ApeStakingLogic.sol: L196-210

    /**
     * @notice get user total ape staking position
     * @param userState The user state of nToken
     * @param ownedTokens The ownership mapping state of nNtoken
     * @param user User address
     * @param poolId identify whether BAYC pool or MAYC pool
     * @param _apeCoinStaking ApeCoinStaking contract address
     */
    function getUserTotalStakingAmount(
        mapping(address => UserState) storage userState,
        mapping(address => mapping(uint256 => uint256)) storage ownedTokens,
        address user,
        uint256 poolId,
        ApeCoinStaking _apeCoinStaking
    ) external view returns (uint256) {

Missing: @return


ApeStakingLogic.sol: L225-235

    /**
     * @notice get ape staking position for a tokenId
     * @param poolId identify whether BAYC pool or MAYC pool
     * @param _apeCoinStaking ApeCoinStaking contract address
     * @param tokenId specified the tokenId for the position
     */
    function getTokenIdStakingAmount(
        uint256 poolId,
        ApeCoinStaking _apeCoinStaking,
        uint256 tokenId
    ) public view returns (uint256) {

Missing: @return


WPunkGateway.sol: L119-135

    /**
     * @notice Implements the acceptBidWithCredit feature. AcceptBidWithCredit allows users to
     * accept a leveraged bid on ParaSpace NFT marketplace. Users can submit leveraged bid and pay
     * at most (1 - LTV) * $NFT
     * @dev The nft receiver just needs to do the downpayment
     * @param marketplaceId The marketplace identifier
     * @param payload The encoded parameters to be passed to marketplace contract (selector eliminated)
     * @param credit The credit that user would like to use for this purchase
     * @param referralCode The referral code used
     */
    function acceptBidWithCredit(
        bytes32 marketplaceId,
        bytes calldata payload,
        DataTypes.Credit calldata credit,
        uint256[] calldata punkIndexes,
        uint16 referralCode
    ) external nonReentrant {

Missing: @param punkIndexes


WPunkGateway.sol: L157-173

    /**
     * @notice Implements the batchAcceptBidWithCredit feature. AcceptBidWithCredit allows users to
     * accept a leveraged bid on ParaSpace NFT marketplace. Users can submit leveraged bid and pay
     * at most (1 - LTV) * $NFT
     * @dev The nft receiver just needs to do the downpayment
     * @param marketplaceIds The marketplace identifiers
     * @param payloads The encoded parameters to be passed to marketplace contract (selector eliminated)
     * @param credits The credits that the makers have approved to use for this purchase
     * @param referralCode The referral code used
     */
    function batchAcceptBidWithCredit(
        bytes32[] calldata marketplaceIds,
        bytes[] calldata payloads,
        DataTypes.Credit[] calldata credits,
        uint256[] calldata punkIndexes,
        uint16 referralCode
    ) external nonReentrant {

Missing: @param punkIndexes


WPunkGateway.sol: L225-228

    /**
     * @dev Get WPunk address used by WPunkGateway
     */
    function getWPunkAddress() external view returns (address) {

Missing: @return



No.Description
6Completely missing NatSpec for some functions
NatSpec is completely or almost completely missing for the following external or public functions:

Example:

LooksRareAdapter.sol: L67-69

    function getBidOrderInfo(
        bytes memory /*params*/
    ) external pure override returns (DataTypes.OrderInfo memory) {

Similarly for the following functions:

LooksRareAdapter.sol

L25-29, L73-77, L96-100

SeaportAdapter.sol

L25-29, L60-64, L93-97, L109-112

X2Y2Adapter.sol

L31-35, L79-83, L88-92, L104-108

NFTFloorOracle.sol

L261-262

ParaSpaceOracle.sol

L138-142, L155-159, L172-176

UniswapV3OracleWrapper.sol

L217-218

MarketplaceLogic.sol

L63-70, L160-167, L229-237, L267-275

PoolLogic.sol

L214-218

SupplyLogic.sol

L335-340, L396-401

DefaultReserveAuctionStrategy.sol

L66-67, L70-71, L74-75, L78-79

L82-83, L86-87, L90-93

PoolCore.sol

L237-244, L397-401, L792-798

PoolParameters.sol

L251-256

MintableIncentivizedERC721.sol

L96-97, L100-101, L104-109

NToken.sol

L52-59, L127-131, L136-140, L151-157

L168-171, L271-276, L280-286, L295-301

L323-324, L327-332

NTokenApeStaking.sol

L36-43, L136

NTokenBAYC.sol

L113

NTokenMAYC.sol L113

NTokenMoonBirds.sol

L36, L40-44, L63-68

NTokenUniswapV3.sol

L37

MintableERC721Logic.sol

L80-87, L135-142, L187-197, L260-270, L340-344

L357-362, L368-372, L386-390, L424-428

WPunkGateway.sol

L57, L232-237



No.Description
7Long single-line comments
In theory, comments over 80 characters should wrap using multi-line comment syntax. Even if longer comments (up to 120 characters) are becoming
acceptable and a scroll bar is provided, very long comments can interfere with readability. Most of the long comments in the contest do wrap.
However, below are instances of extra-long comments (over 120 characters) whose readability could be improved
by wrapping, as shown. Note that a small indentation is used in each continuation line (which flags for the reader
that the comment is ongoing), along with a period at the close (to signal the end of the comment). In addition,
when an extra-long line includes both code and comments, it makes sense to put the comment in the line above.

LiquidationLogic.sol: L603

     * @return The actual debt that is getting liquidated. If liquidation amount passed in by the liquidator is greater then the total user debt, then use the user total debt as the actual debt getting liquidated. If the user total debt is greater than the liquidation amount getting passed in by the liquidator, then use the liquidation amount the user is passing in.

Suggestion:

     * @return The actual debt that is getting liquidated. If liquidation amount passed in by the 
     *   liquidator is greater than the total user debt, then use the user total debt as the actual
     *   debt getting liquidated. If the user total debt is greater than the liquidation amount getting
     *   passed in by the liquidator, then use the liquidation amount the user is passing in.

ValidationLogic.sol: L1137

                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")

Suggestion:

                    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
                    0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f

AuctionLogic.sol: L94

     * @notice Function to end auction on an ERC721 of a position if its ERC721 Health Factor increases back to above `AUCTION_RECOVERY_HEALTH_FACTOR`.

Suggestion:

     * @notice Function to end auction on an ERC721 of a position if its ERC721
     *   Health Factor increases back to above `AUCTION_RECOVERY_HEALTH_FACTOR`.

NTokenApeStaking.sol: L100

     * @notice Overrides the burn from NToken to withdraw all staked and pending rewards before burning the NToken on liquidation/withdraw

Suggestion:

     * @notice Overrides the burn from NToken to withdraw all staked and 
     *   pending rewards before burning the NToken on liquidation/withdraw.

MintableERC721Logic.sol: L530

            erc721Data.ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token

Suggestion:

            // Move the last token to the slot of the to-delete token
            erc721Data.ownedTokens[from][tokenIndex] = lastTokenId;

Similarly for the following extra-long (over 120 characters) comments:

PoolAddressesProvider.sol: L259

PoolAddressesProvider.sol: L338

LiquidationLogic.sol: L655

WPunkGateway.sol: L71

BorrowLogic.sol: L157

NTokenBAYC.sol: L48

NTokenBAYC.sol: L93

NTokenBAYC.sol: L95

NTokenMAYC.sol: L48

NTokenMAYC.sol: L93

NTokenMAYC.sol: L95

SupplyLogic.sol: L260



No.Description
8Typos

ApeStakingLogic.sol: L59

     * @notice undate incentive percentage for unstakePositionAndRepay

Change undate to update if that was intended


AuctionLogic.sol: L34

     * @notice Function to tsatr auction on an ERC721 of a position if its ERC721 Health Factor drops below 1.

Change tsatr to start if that was intended


LiquidationLogic.sol: L603

     * @return The actual debt that is getting liquidated. If liquidation amount passed in by the liquidator is greater then the total user debt, then use the user total debt as the actual debt getting liquidated. If the user total debt is greater than the liquidation amount getting passed in by the liquidator, then use the liquidation amount the user is passing in.

Change the first then to than


MarketplaceLogic.sol: L195

            // eg. The following example image that the `taker` owns only ETH and wants to

Change image to imagines if that was intended


NFTFloorOracle.sol: L11

//we do not accept price lags behind to much

Change to to too


The same typo (aggeregate) occurs in all three lines referenced below:

NFTFloorOracle.sol: L53

NFTFloorOracle.sol: L54

NFTFloorOracle.sol: L412

/// aggeregate prices which are not expired from different feeders, if number of valid/unexpired prices

Change aggeregate to aggregate


PoolAddressesProvider.sol: L336

     * @notice Returns the the implementation contract of the proxy contract by its identifier.

Remove repeated word the



No.Description
9Unclear comment
Comments should communicate clearly, immediately and without ambiguity. The readability of the comment below could be improved:

AuctionLogic.sol: L34

     * @notice Function to tsatr auction on an ERC721 of a position if its ERC721 Health Factor drops below 1.

This @notice is challenging to understand even after correcting tsatr to start



No.Description
10Inconsistent comment // spacing
Some lines use //x (///x) whereas others use // x (/// x). While the choice of spacing is up to the developers, it should be made consistent (normally // x or /// x is used).

NFTFloorOracle.sol: L8-21

//we need to deploy 3 oracles at least
uint8 constant MIN_ORACLES_NUM = 3;
//expirationPeriod at least the interval of client to feed data(currently 6h=21600s/12=1800 in mainnet)
//we do not accept price lags behind to much
uint128 constant EXPIRATION_PERIOD = 1800;
//reject when price increase/decrease 1.5 times more than original value
uint128 constant MAX_DEVIATION_RATE = 150;

struct OracleConfig {
    // Expiration Period for each feed price
    uint128 expirationPeriod;
    // Maximum deviation allowed between two consecutive oracle prices
    uint128 maxPriceDeviation;
}


No.Description
11Solidity version should be upgraded
The current solidity version in contracts is 0.8.10, compared to the latest version of 0.8.17. Only the latest version receives security fixes.
On the other hand, the latest version often has bugs, so it's safer to use releases a few versions older at first, as has been done here.
Having said that, version 0.8.12 or later is needed for string.concat to be used instead of abi.encodePacked and 0.8.13 is required to get the ability to use using for with a list of free functions


No.Description
12TODOs and other open items
Comments that refer to open items should be addressed and modified or removed

UniswapV3OracleWrapper.sol: L238

        // TODO using bit shifting for the 2^96

MarketplaceLogic.sol: L442

        // TODO: support PToken

NToken.sol: L220-222

    {
        // Intentionally left blank
    }

NTokenMoonBirds.sol: L32-34

    constructor(IPool pool) NToken(pool, false) {
        // Intentionally left blank
    }


#0 - c4-judge

2023-01-25T16:39:07Z

dmvt marked the issue as grade-a

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