BitHotelNFT - Smart Contract Audit Report

Audit Summary

BitHotel Audit Report BitHotel is a new NFT collection representing rooms with dimensions.

For this audit, we reviewed the BitHotelCollection, ERC2981PerTokenRoyalties, and PreSaleBitHotelRoomCollection at commit ffbc87b927cd42bb1f894dfc56ae4ae60af7aa33 on the team's GitHub

We previously reviewed the team's NFT collection contract here.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem.
Date: January 17th, 2022.
Updated: January 21st, 2022 to support changes from commit 3abf4c9057cd71a2600ee1150cded2c67327ce80 to commit ffbc87b927cd42bb1f894dfc56ae4ae60af7aa33.

PresaleBitHotelRoomCollectionBTH - Finding #1 - High (Resolved)

Description: When removing an address from the whitelist in the removeWhitelist() function and the discount list in the removeDiscountedFunction(), the mapping from a user's address to their index within the list is not updated.
Risk/Impact: After a user is removed, all addresses that occur later in the list than the removed address will have the wrong index in the mapping. This will cause the wrong address to be removed when attemping to remove one of the affected addresses.
Recommendation: Update the mapping for each user when their index in the list is changed.
Resolution: The team is now updating the mapping correctly during removal.

PresaleBitHotelRoomCollectionBTH - Finding #2 - Informational (Resolved)

Description: Expensive looping is used when removing users from the whitelist in the removeWhitelist() function and the discount list in the removeDiscounted() function.
Risk/Impact: As more users are added to either list, the cost of iterating over the list will become increasingly expensive.
Recommendation: It is possible to remove a user from the list with no iterations. We have provided a pseudocode implementation of such a method:
if(index != list.length-1) {
  list[list.length-1] = index;
  }
list.pop();
Resolution: The team has implemented the method of removal described above.

PresaleBitHotelRoomCollectionBTH - Finding #3 - Informational (Resolved)

Description: There is unneccessary logic present in the buy() function.
Risk/Impact: When incrementing the amount of NFTs a user has bought, there is no need to set the value to 1 the first time.
Recommendation: Instead of manually setting the amount of NFTs a user has bought to 1 after their first purchase, incrementing the value will have the same result. Removing the if statement and instead incrementing the value in all cases will have the same behavior with less logic required, saving users gas on each buy transaction.
Resolution: The team has removed the unnecessary logic.

Contracts Overview

General Notes Across Contracts:
  • Both contracts are implemented using the Authorizable permission scheme.
  • This allows the contract owner to set various "roles" within the contract with each role having potentially different permissions.
  • The deployer is given the Default Admin role upon deployment.
  • The Default Admin may assign any role to any address at any time.
  • As the contracts are implemented in Solidity version 0.8.X, they are protected from overflow.
BitHotelRoomCollection Contract:
  • A Controller address and maximum number of NFTs is declared upon deployment.
  • The Controller may mint BitHotelRoomCollection NFTs on the behalf of any user at any time.
  • An additional royalty value consisting of the token, recipient, and amount of token to be paid can also be specified when minting.
  • NFT metadata that contains information about the NFTs is stored using an off-chain URI endpoint.
  • The Controller may lock and unlock an NFT at any time; a locked NFT may not be transferred.
  • The Controller may update an NFT's attributes at any time.
  • The Controller may update the base URI as well as an individual NFT's URI at any time.
  • The Controller may update its own address at any time.
PresaleBitHotelRoomCollectionBTH Contract:
  • This contract acts as a valid Controller for the BitHotelRoomCollection contract.
  • Any user may use BitHotel tokens to purchase an NFT from any specified BitHotelRoomCollection when the whitelist is not enabled. If the whitelist is enabled, a user must be whitelisted to purchase an NFT.
  • Users may purchase a total of 2 NFTs across all collections.
  • Users may also be added to a discount list, allowing them to purchase NFTs at a lower price.
  • Each collection of NFTs has its own maximum supply and price, which may vary between collections.
  • The Default Admin may add any valid BitHotelRoomCollection to be purchased at any time.
  • The Default Admin may add and remove addresses from the whitelist and discount list at any time.
  • The Default Admin may update the information for any NFT at any time.
  • The Default Admin may lock and unlock any NFT at any time.
  • The Default Admin may update the Controller of any BitHotelRoomCollection contract at anytime.
  • The Default Admin may update the BitHotel token address at any time.
  • The Default Admin may update the discounted price for any NFT at any time.
  • The Default Admin may enable and disable the whitelist at any time.
  • The Default Admin may pause the contract, disabling the ability to buy NFTs, at any time.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of Control
  • The team retains ownership functionality described above.
  • Royalties may be set up to 100% in the ERC2981PerTokenRoyalties contract.
  • The may update any NFT's characteristics at any time.
  • WARNING
    Delegate Call to Untrusted ContractN/APASS
    Dependence on Predictable VariablesN/APASS
    Deprecated OpcodesN/APASS
    Ether ThiefN/APASS
    ExceptionsN/APASS
    External CallsN/APASS
    Integer Over/UnderflowN/APASS
    Logical IssuesN/APASS
    Multiple SendsN/APASS
    SuicideN/APASS
    State Change External CallsN/APASS
    Unchecked RetvalN/APASS
    User Supplied AssertionN/APASS
    Critical Solidity CompilerN/APASS
    Overall Contract Safety PASS

    PresaleBitHotelRoomCollection Contract

    smart_contract_audit_company

    token_audit

    
     ($) = payable function
     # = non-constant function
    
     + [Int] IAccessControl 
        - [Ext] hasRole
        - [Ext] getRoleAdmin
        - [Ext] grantRole #
        - [Ext] revokeRole #
        - [Ext] renounceRole #
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     + [Lib] Strings 
        - [Int] toString
        - [Int] toHexString
        - [Int] toHexString
    
     + [Int] IERC165 
        - [Ext] supportsInterface
    
     +  ERC165 (IERC165)
        - [Pub] supportsInterface
    
     +  AccessControl (Context, IAccessControl, ERC165)
        - [Pub] supportsInterface
        - [Pub] hasRole
        - [Int] _checkRole
        - [Pub] getRoleAdmin
        - [Pub] grantRole #
           - modifiers: onlyRole
        - [Pub] revokeRole #
           - modifiers: onlyRole
        - [Pub] renounceRole #
        - [Int] _setupRole #
        - [Int] _setRoleAdmin #
        - [Int] _grantRole #
        - [Int] _revokeRole #
    
     + [Int] IERC721 (IERC165)
        - [Ext] balanceOf
        - [Ext] ownerOf
        - [Ext] safeTransferFrom #
        - [Ext] transferFrom #
        - [Ext] approve #
        - [Ext] getApproved
        - [Ext] setApprovalForAll #
        - [Ext] isApprovedForAll
        - [Ext] safeTransferFrom #
    
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     + [Int] IERC721Receiver 
        - [Ext] onERC721Received #
    
     +  ERC721Holder (IERC721Receiver)
        - [Pub] onERC721Received #
    
     + [Lib] EnumerableSet 
        - [Prv] _add #
        - [Prv] _remove #
        - [Prv] _contains
        - [Prv] _length
        - [Prv] _at
        - [Prv] _values
        - [Int] add #
        - [Int] remove #
        - [Int] contains
        - [Int] length
        - [Int] at
        - [Int] values
        - [Int] add #
        - [Int] remove #
        - [Int] contains
        - [Int] length
        - [Int] at
        - [Int] values
        - [Int] add #
        - [Int] remove #
        - [Int] contains
        - [Int] length
        - [Int] at
        - [Int] values
    
     +  Pausable (Context)
        - [Pub] Constructor #
        - [Pub] paused
        - [Int] _pause #
           - modifiers: whenNotPaused
        - [Int] _unpause #
           - modifiers: whenPaused
    
     +  ReentrancyGuard 
        - [Pub] Constructor #
    
     + [Lib] Address 
        - [Int] isContract
        - [Int] sendValue #
        - [Int] functionCall #
        - [Int] functionCall #
        - [Int] functionCallWithValue #
        - [Int] functionCallWithValue #
        - [Int] functionStaticCall
        - [Int] functionStaticCall
        - [Int] functionDelegateCall #
        - [Int] functionDelegateCall #
        - [Int] verifyCallResult
    
     + [Lib] SafeERC20 
        - [Int] safeTransfer #
        - [Int] safeTransferFrom #
        - [Int] safeApprove #
        - [Int] safeIncreaseAllowance #
        - [Int] safeDecreaseAllowance #
        - [Prv] _callOptionalReturn #
    
     + [Int] IUniswapV2Router01 
        - [Ext] factory
        - [Ext] WETH
        - [Ext] addLiquidity #
        - [Ext] addLiquidityETH ($)
        - [Ext] removeLiquidity #
        - [Ext] removeLiquidityETH #
        - [Ext] removeLiquidityWithPermit #
        - [Ext] removeLiquidityETHWithPermit #
        - [Ext] swapExactTokensForTokens #
        - [Ext] swapTokensForExactTokens #
        - [Ext] swapExactETHForTokens ($)
        - [Ext] swapTokensForExactETH #
        - [Ext] swapExactTokensForETH #
        - [Ext] swapETHForExactTokens ($)
        - [Ext] quote
        - [Ext] getAmountOut
        - [Ext] getAmountIn
        - [Ext] getAmountsOut
        - [Ext] getAmountsIn
    
     + [Int] IUniswapV2Router02 (IUniswapV2Router01)
        - [Ext] removeLiquidityETHSupportingFeeOnTransferTokens #
        - [Ext] removeLiquidityETHWithPermitSupportingFeeOnTransferTokens #
        - [Ext] swapExactTokensForTokensSupportingFeeOnTransferTokens #
        - [Ext] swapExactETHForTokensSupportingFeeOnTransferTokens ($)
        - [Ext] swapExactTokensForETHSupportingFeeOnTransferTokens #
    
     + [Int] IERC721Metadata (IERC721)
        - [Ext] name
        - [Ext] symbol
        - [Ext] tokenURI
    
     +  ERC721 (Context, ERC165, IERC721, IERC721Metadata)
        - [Pub] Constructor #
        - [Pub] supportsInterface
        - [Pub] balanceOf
        - [Pub] ownerOf
        - [Pub] name
        - [Pub] symbol
        - [Pub] tokenURI
        - [Int] _baseURI
        - [Pub] approve #
        - [Pub] getApproved
        - [Pub] setApprovalForAll #
        - [Pub] isApprovedForAll
        - [Pub] transferFrom #
        - [Pub] safeTransferFrom #
        - [Pub] safeTransferFrom #
        - [Int] _safeTransfer #
        - [Int] _exists
        - [Int] _isApprovedOrOwner
        - [Int] _safeMint #
        - [Int] _safeMint #
        - [Int] _mint #
        - [Int] _burn #
        - [Int] _transfer #
        - [Int] _approve #
        - [Int] _setApprovalForAll #
        - [Prv] _checkOnERC721Received #
        - [Int] _beforeTokenTransfer #
    
     + [Int] IERC721Enumerable (IERC721)
        - [Ext] totalSupply
        - [Ext] tokenOfOwnerByIndex
        - [Ext] tokenByIndex
    
     +  ERC721Enumerable (ERC721, IERC721Enumerable)
        - [Pub] supportsInterface
        - [Pub] tokenOfOwnerByIndex
        - [Pub] totalSupply
        - [Pub] tokenByIndex
        - [Int] _beforeTokenTransfer #
        - [Prv] _addTokenToOwnerEnumeration #
        - [Prv] _addTokenToAllTokensEnumeration #
        - [Prv] _removeTokenFromOwnerEnumeration #
        - [Prv] _removeTokenFromAllTokensEnumeration #
    
     +  ERC721URIStorage (ERC721)
        - [Pub] tokenURI
        - [Int] _setTokenURI #
        - [Int] _burn #
    
     + [Lib] Counters 
        - [Int] current
        - [Int] increment #
        - [Int] decrement #
        - [Int] reset #
    
     + [Int] IBitHotelRoomCollection (IERC721)
        - [Ext] tokenIds
        - [Ext] ownersHistory
        - [Ext] getRoomInfos
        - [Ext] getRoomDimensions
        - [Ext] locked
        - [Ext] controller
        - [Ext] replicas
        - [Ext] baseURI
        - [Ext] exists
        - [Ext] setRoomInfos #
        - [Ext] setController #
        - [Ext] lockTokenId #
        - [Ext] releaseLockedTokenId #
        - [Ext] setBaseURI #
        - [Ext] setTokenURI #
        - [Ext] mint #
        - [Ext] safeMint #
    
     + [Int] IERC2981Royalties 
        - [Ext] royaltyInfo
    
     +  ERC2981PerTokenRoyalties (ERC165, IERC2981Royalties)
        - [Int] _setTokenRoyalty #
        - [Ext] royaltyInfo
    
     +  BitHotelRoomCollection (IBitHotelRoomCollection, AccessControl, ERC721Enumerable, ERC721URIStorage, ERC2981PerTokenRoyalties)
        - [Pub] Constructor #
           - modifiers: ERC721
        - [Ext] tokenIds
        - [Ext] ownersHistory
        - [Ext] getRoomInfos
        - [Ext] getRoomDimensions
        - [Ext] locked
        - [Ext] setRoomInfos #
           - modifiers: onlyController
        - [Ext] setController #
           - modifiers: onlyController
        - [Ext] lockTokenId #
           - modifiers: onlyController
        - [Ext] releaseLockedTokenId #
           - modifiers: onlyController
        - [Pub] controller
        - [Pub] replicas
        - [Pub] baseURI
        - [Pub] exists
        - [Pub] tokenURI
        - [Pub] supportsInterface
        - [Pub] setBaseURI #
           - modifiers: onlyRole
        - [Pub] setTokenURI #
           - modifiers: onlyRole
        - [Pub] mint #
           - modifiers: onlyController
        - [Pub] safeMint #
           - modifiers: onlyController
        - [Int] _beforeTokenTransfer #
           - modifiers: notLocked
        - [Int] _baseURI
        - [Int] _burn #
           - modifiers: onlyRole
    
     +  PresaleBitHotelRoomCollectionBTH (AccessControl, ERC721Holder, Pausable, ReentrancyGuard)
        - [Pub] Constructor #
        - [Ext] initializer #
           - modifiers: onlyRole
        - [Ext] bulkRemoveWhitelist #
           - modifiers: onlyRole
        - [Ext] bulkRemoveDiscountedAddresses #
           - modifiers: onlyRole
        - [Ext] bulkAddGameNFTs #
           - modifiers: onlyRole
        - [Ext] setRoomInfos #
           - modifiers: onlyRole
        - [Ext] whitelisted
        - [Ext] discounted
        - [Ext] tokenPath
        - [Ext] pause #
           - modifiers: onlyRole
        - [Ext] unPause #
           - modifiers: onlyRole
        - [Ext] setDiscountRate #
           - modifiers: onlyRole
        - [Ext] setWhitelistEnabled #
           - modifiers: onlyRole
        - [Ext] lockTokenId #
           - modifiers: onlyRole
        - [Ext] releaseLockedTokenId #
           - modifiers: onlyRole
        - [Ext] bulkWhitelist #
           - modifiers: onlyRole
        - [Ext] bulkDiscounted #
           - modifiers: onlyRole
        - [Ext] setController #
           - modifiers: onlyRole
        - [Ext] setToken #
           - modifiers: onlyRole
        - [Pub] token
        - [Pub] bth
        - [Pub] busd
        - [Pub] router
        - [Pub] discountRate
        - [Pub] wallet
        - [Pub] globalRoyaltyValue
        - [Pub] weiRaised
        - [Pub] totalWhitelisted
        - [Pub] totalDiscounted
        - [Pub] whitelistIndex
        - [Pub] discountedIndex
        - [Pub] bought
        - [Pub] exists
        - [Pub] getAmountsOut
        - [Pub] gameNFTbyTokenId
        - [Pub] getDiscountPriceByTokenID
        - [Pub] isWhitelistEnabled
        - [Pub] setWhitelist #
           - modifiers: onlyRole
        - [Pub] setDiscountAddress #
           - modifiers: onlyRole
        - [Pub] addGameNft #
           - modifiers: onlyRole
        - [Pub] removeWhitelist #
           - modifiers: onlyRole
        - [Pub] removeDiscounted #
           - modifiers: onlyRole
        - [Ext] buy #
           - modifiers: nonReentrant,whenNotPaused
        - [Int] _preValidateBuy
        - [Int] _preValidateMint
        - [Int] _receiveTokens #
        - [Int] _processMintNft #
        - [Int] _safeMint #
    
    							

    BitHotelRoomCollection Contract

    smart_contract_audit_company

    token_audit

    
     ($) = payable function
     # = non-constant function
    
     + [Int] IAccessControl 
        - [Ext] hasRole
        - [Ext] getRoleAdmin
        - [Ext] grantRole #
        - [Ext] revokeRole #
        - [Ext] renounceRole #
    
     +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     + [Lib] Strings 
        - [Int] toString
        - [Int] toHexString
        - [Int] toHexString
    
     + [Int] IERC165 
        - [Ext] supportsInterface
    
     +  ERC165 (IERC165)
        - [Pub] supportsInterface
    
     +  AccessControl (Context, IAccessControl, ERC165)
        - [Pub] supportsInterface
        - [Pub] hasRole
        - [Int] _checkRole
        - [Pub] getRoleAdmin
        - [Pub] grantRole #
           - modifiers: onlyRole
        - [Pub] revokeRole #
           - modifiers: onlyRole
        - [Pub] renounceRole #
        - [Int] _setupRole #
        - [Int] _setRoleAdmin #
        - [Int] _grantRole #
        - [Int] _revokeRole #
    
     + [Int] IERC721 (IERC165)
        - [Ext] balanceOf
        - [Ext] ownerOf
        - [Ext] safeTransferFrom #
        - [Ext] transferFrom #
        - [Ext] approve #
        - [Ext] getApproved
        - [Ext] setApprovalForAll #
        - [Ext] isApprovedForAll
        - [Ext] safeTransferFrom #
    
     + [Int] IERC721Receiver 
        - [Ext] onERC721Received #
    
     + [Int] IERC721Metadata (IERC721)
        - [Ext] name
        - [Ext] symbol
        - [Ext] tokenURI
    
     + [Lib] Address 
        - [Int] isContract
        - [Int] sendValue #
        - [Int] functionCall #
        - [Int] functionCall #
        - [Int] functionCallWithValue #
        - [Int] functionCallWithValue #
        - [Int] functionStaticCall
        - [Int] functionStaticCall
        - [Int] functionDelegateCall #
        - [Int] functionDelegateCall #
        - [Int] verifyCallResult
    
     +  ERC721 (Context, ERC165, IERC721, IERC721Metadata)
        - [Pub] Constructor #
        - [Pub] supportsInterface
        - [Pub] balanceOf
        - [Pub] ownerOf
        - [Pub] name
        - [Pub] symbol
        - [Pub] tokenURI
        - [Int] _baseURI
        - [Pub] approve #
        - [Pub] getApproved
        - [Pub] setApprovalForAll #
        - [Pub] isApprovedForAll
        - [Pub] transferFrom #
        - [Pub] safeTransferFrom #
        - [Pub] safeTransferFrom #
        - [Int] _safeTransfer #
        - [Int] _exists
        - [Int] _isApprovedOrOwner
        - [Int] _safeMint #
        - [Int] _safeMint #
        - [Int] _mint #
        - [Int] _burn #
        - [Int] _transfer #
        - [Int] _approve #
        - [Int] _setApprovalForAll #
        - [Prv] _checkOnERC721Received #
        - [Int] _beforeTokenTransfer #
    
     + [Int] IERC721Enumerable (IERC721)
        - [Ext] totalSupply
        - [Ext] tokenOfOwnerByIndex
        - [Ext] tokenByIndex
    
     +  ERC721Enumerable (ERC721, IERC721Enumerable)
        - [Pub] supportsInterface
        - [Pub] tokenOfOwnerByIndex
        - [Pub] totalSupply
        - [Pub] tokenByIndex
        - [Int] _beforeTokenTransfer #
        - [Prv] _addTokenToOwnerEnumeration #
        - [Prv] _addTokenToAllTokensEnumeration #
        - [Prv] _removeTokenFromOwnerEnumeration #
        - [Prv] _removeTokenFromAllTokensEnumeration #
    
     +  ERC721URIStorage (ERC721)
        - [Pub] tokenURI
        - [Int] _setTokenURI #
        - [Int] _burn #
    
     + [Lib] Counters 
        - [Int] current
        - [Int] increment #
        - [Int] decrement #
        - [Int] reset #
    
     + [Int] IBitHotelRoomCollection (IERC721)
        - [Ext] tokenIds
        - [Ext] ownersHistory
        - [Ext] getRoomInfos
        - [Ext] getRoomDimensions
        - [Ext] locked
        - [Ext] controller
        - [Ext] replicas
        - [Ext] baseURI
        - [Ext] exists
        - [Ext] setRoomInfos #
        - [Ext] setController #
        - [Ext] lockTokenId #
        - [Ext] releaseLockedTokenId #
        - [Ext] setBaseURI #
        - [Ext] setTokenURI #
        - [Ext] mint #
        - [Ext] safeMint #
    
     + [Int] IERC2981Royalties 
        - [Ext] royaltyInfo
    
     +  ERC2981PerTokenRoyalties (ERC165, IERC2981Royalties)
        - [Int] _setTokenRoyalty #
        - [Ext] royaltyInfo
    
     +  BitHotelRoomCollection (IBitHotelRoomCollection, AccessControl, ERC721Enumerable, ERC721URIStorage, ERC2981PerTokenRoyalties)
        - [Pub] Constructor #
           - modifiers: ERC721
        - [Ext] tokenIds
        - [Ext] ownersHistory
        - [Ext] getRoomInfos
        - [Ext] getRoomDimensions
        - [Ext] locked
        - [Ext] setRoomInfos #
           - modifiers: onlyController
        - [Ext] setController #
           - modifiers: onlyController
        - [Ext] lockTokenId #
           - modifiers: onlyController
        - [Ext] releaseLockedTokenId #
           - modifiers: onlyController
        - [Pub] controller
        - [Pub] replicas
        - [Pub] baseURI
        - [Pub] exists
        - [Pub] tokenURI
        - [Pub] supportsInterface
        - [Pub] setBaseURI #
           - modifiers: onlyRole
        - [Pub] setTokenURI #
           - modifiers: onlyRole
        - [Pub] mint #
           - modifiers: onlyController
        - [Pub] safeMint #
           - modifiers: onlyController
        - [Int] _beforeTokenTransfer #
           - modifiers: notLocked
        - [Int] _baseURI
        - [Int] _burn #
           - modifiers: onlyRole
    
    							

    ERC2981PerTokenRoyalties Contract

    smart_contract_audit_company

    token_audit

    
     ($) = payable function
     # = non-constant function
    
     + [Int] IERC165 
        - [Ext] supportsInterface
    
     +  ERC165 (IERC165)
        - [Pub] supportsInterface
    
     + [Int] IERC2981Royalties 
        - [Ext] royaltyInfo
    
     +  ERC2981PerTokenRoyalties (ERC165, IERC2981Royalties)
        - [Int] _setTokenRoyalty #
        - [Ext] royaltyInfo
    
    							


    © Solidity Finance LLC. | All rights reserved.
    Please note we are not associated with the Solidity programming language or the core team which develops the language.