Tropical Wonders Marketplace

Smart Contract Audit Report

Audit Summary

Tropical Wonders Marketplace Audit Report Tropical Finance is building an NFT marketplace where users can list and purchase NFTs.

For this audit, we reviewed the WondersMarketplace and WondersCollection contracts provided to us by the project team.

Audit Findings

Please ensure trust in the team prior to investing as they have some control in the ecosystem.
Date: March 10th, 2022.
Updated: March 11th, 2022 to reflect updates made to the WondersMarketplace contract that resolve Findings #1 - #4.

Finding #1 - WondersMarketplace - High (Resolved)

Description: In the purchase() function, when a new user outbids the previous user, the previous user's tokens are not properly refunded as the function is attempting to transfer the tokens from the previous user to the contract address once again.
if(lastTx.txType == 1) {
	IBEP20(_wonderForSale.currencyAddress).transferFrom(address(lastTx.sender), address(this), lastTx.price);
Risk/Impact: The transaction will revert if the bidder has not granted the contract address an approval. All subsequent bid transactions will revert and the NFT will always be purchased by the first bidder once the expire time runs out.
Recommendation: The previous user's full bid amount should be transferred from the contract address to their wallet address in order for the previous user to receive their refund.
Resolution: The team has implemented the above recommendation.

Finding #2 - WondersMarketplace - Informational (Resolved)

Description: The safeCheck() modifier is utilized twice in the removeFromSale() function.
Recommendation: One of these modifiers should be removed for additional gas savings on each call.
Resolution: The team has removed one of the modifiers from the removeFromSale() function.

Finding #3 - WondersMarketplace - Informational (Resolved)

Description: Several functions are declared public, but are never called internally.
setCurrency, setCurrencyCashback, removeCollection, nextBidMinAmount, secondsAddedOnLastBid, secondsLeftToAdd, maxSaleDurationInSecondsAuction, maxSaleDurationInSecondsFixedPrice, minSaleDurationInSeconds, setIncentivesEnabled, setIncentiveCurrency, setIncentivesMinHoldings, setIncentivesAmounts, collectionsLength, getCurrentPrice, claim, setForSale, removeFromSale
Recommendation: These functions should be declared external for additional gas savings on each call.
Resolution: The team has declared the above functions external.

Finding #4 - WondersMarketplace - Informational (Resolved)

Description: The first require statement in the eligible() modifier contains a tautology as the value of the sellType variable will always be at least 0.
sellType >= 0 && sellType <= 1
Recommendation: The require statement can be simplified to only check that the sellType is less than or equal to 1 for for additional gas savings on each call.
Resolution: The team has implemented the above recommendation.

Finding #5 - WondersMarketplace - Informational

Description: Although the SafeMath library is utilized, the contract is implemented with Solidity v0.8.x which has built-in overflow checks.
Recommendation: SafeMath could be safely removed to reduce contract size and deployment costs.

Contracts Overview

  • As the contracts are implemented with Solidity v0.8.x, they are safe from any possible overflows/underflows.
  • The team must exercise caution when assigning the currency token to avoid using a fee-on-transfer or ERC-777 compliant token. If a fee on transfer token is used then the WondersMarketplace contract should be excluded from fees.
WondersCollection Contract:
  • This contract is used to represent an ERC-721 NFT collection on the WondersMarketplace platform.
  • The owner can mint any number of Wonders NFTs to any address at any time.
  • This contract complies with the ERC-721 token standard.
WondersMarketplace Contract:
  • The contract allows users to list NFTs for sale and purchase/bid on listed NFTs on the marketplace.
  • Upon listing an NFT, users will specify the collectionID, price, currency address it can be purchased with, the Sale type, and the expire time.
  • The collectionID and currency token address must have been pre-approved by the owner in order for a listing to be successful.
  • The team must exercise caution when assigning the currency token to avoid using a fee-on-transfer token.
  • The Sale type can either be a fixed-price sale or an auction sale.
  • The expire time dictates how long their NFT will be listed for (in seconds). The expire time must be greater than the minimum sale duration (determined by the owner) for both Sale types.
  • The user attempting to list an NFT must be the owner of that NFT.
  • Users must grant the contract address an approval for the NFT they are attempting to list.
  • Users can unlist their NFTs at any time if they currently do not have any pending bids. The NFT will be returned to the user's wallet address.

  • Users can purchase fixed-price NFTs by entering in the collectionID of the NFT and the listed price.
  • Users must own a sufficient number of currency tokens in order for a purchase to be successful.
  • Users must grant the contract address an approval for the number of currency tokens it will cost to purchase the NFT.
  • The NFT will be returned to the lister if the fixed-price expire time passes without a purchase.
  • Users can bid on any existing auctioned NFT at any time; auction owners cannot bid on their own NFTs.
  • The bid price cannot be less than the listed price on the first bid.
  • The bid price must be at least 3% greater than a previous user's bid on all subsequent bids.
  • Once the auction expire time passes, the NFT will be purchased by the last bidder.
  • The NFT will be returned to the lister if the auction expire time passes without any bids.
  • All purchased or returned NFTs must be manually claimed by users.

  • A fee is charged on all purchases that is allocated to the team's Fee wallet.
  • The contract features a Currency Cashback mechanism that can be set by the owner for each approved currency.
  • If set, a percentage (determined by the owner) will be deducted from the fee amount as a discount and sent to both the buyer and seller of the NFT.
  • If the NFT seller is holding the minimum number of the currency token, another percentage (determined by the owner) will be deducted from the fee amount and sent to the seller as a fee discount.
  • If the NFT buyer is holding the minimum number of the currency token, another percentage (determined by the owner) will be deducted from the fee amount and sent to the buyer as a fee discount.

  • The owner can add/remove NFT collections from the eligible collection list at any time.
  • The owner can add/remove any token address as an approved currency at any time.
  • The owner can set the fee associated with each approved currency to any percentage at any time.
  • The owner can set the Currency Cashback percentages that are allocated to the buyer and seller on NFT purchases to any percentages at any time.
  • The owner can update the percentage increase that a subsequent bid must be over a previous bid to any percentage at any time.
  • The owner can update the maximum and minimum sale durations for both fixed-price sales and auction sales to any values at any time.
  • The owner can update the minimum number of currency tokens that a buyer/seller must hold in order to be eligible for a fee discount.
  • The owner can update the fee discount for both the buyer/seller to any percentages at any time.
  • The owner can enable/disable the fee discount mechanism at any time.
  • The owner can update the number of seconds between the last bid and the expire time that will trigger extra time to be added onto the auction to any value at any time.
  • The owner can update the extra time that will be added on to any value at any time.
  • The owner can set the team's Fee wallet to any address at any time.

Audit Results

Vulnerability CategoryNotesResult
Arbitrary Jump/Storage WriteN/APASS
Centralization of ControlThe owner can set the fee associated with each approved currency up to 15%PASS
Compiler IssuesN/APASS
Delegate Call to Untrusted ContractN/APASS
Dependence on Predictable VariablesN/APASS
Ether/Token TheftN/APASS
Flash LoansN/APASS
Front RunningN/APASS
Improper EventsN/APASS
Improper Authorization SchemeN/APASS
Integer Over/UnderflowN/APASS
Logical IssuesN/APASS
Oracle IssuesN/APASS
Outdated Compiler VersionN/APASS
Race ConditionsN/APASS
ReentrancyN/APASS
Signature IssuesN/APASS
Unbounded LoopsN/APASS
Unused CodeN/APASS
Overall Contract Safety PASS

Inheritance Chart

Smart Contract Audit - Inheritance

Function Graph

Smart Contract Audit - Graph

Functions Overview


 ($) = payable function
 # = non-constant function
 
 Int = Internal
 Ext = External
 Pub = Public

 + [Int] IERC165 
    - [Ext] supportsInterface

 +  ERC165 (IERC165)
    - [Pub] supportsInterface

 + [Int] IERC721 (IERC165)
    - [Ext] balanceOf
    - [Ext] ownerOf
    - [Ext] safeTransferFrom #
    - [Ext] transferFrom #
    - [Ext] approve #
    - [Ext] getApproved
    - [Ext] setApprovalForAll #
    - [Ext] isApprovedForAll
    - [Ext] safeTransferFrom #

 + [Int] IERC721Enumerable (IERC721)
    - [Ext] totalSupply
    - [Ext] tokenOfOwnerByIndex
    - [Ext] tokenByIndex

 + [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

 +  Context 
    - [Int] _msgSender
    - [Int] _msgData

 + [Lib] Strings 
    - [Int] toString
    - [Int] toHexString
    - [Int] toHexString

 +  ERC721 (Context, ERC165, IERC721, IERC721Metadata)
    - [Pub]  #
    - [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] _afterTokenTransfer #

 +  ERC721Enumerable (ERC721, IERC721Enumerable)
    - [Pub] supportsInterface
    - [Pub] tokenOfOwnerByIndex
    - [Pub] totalSupply
    - [Pub] tokenByIndex
    - [Int] _beforeTokenTransfer #
    - [Prv] _addTokenToOwnerEnumeration #
    - [Prv] _addTokenToAllTokensEnumeration #
    - [Prv] _removeTokenFromOwnerEnumeration #
    - [Prv] _removeTokenFromAllTokensEnumeration #

 + [Lib] Counters 
    - [Int] current
    - [Int] increment #
    - [Int] decrement #
    - [Int] reset #

 +  Ownable (Context)
    - [Pub]  #
    - [Pub] owner
    - [Pub] renounceOwnership #
       - modifiers: onlyOwner
    - [Pub] transferOwnership #
       - modifiers: onlyOwner
    - [Int] _transferOwnership #

 +  WondersCollection (ERC721Enumerable, Ownable)
    - [Pub]  #
       - modifiers: ERC721
    - [Int] _baseURI
    - [Pub] tokenURI
    - [Ext] mintX #
       - modifiers: onlyOwner
    - [Pub] mint #
       - modifiers: onlyOwner

 + [Int] IBEP20 
    - [Ext] totalSupply
    - [Ext] balanceOf
    - [Ext] transfer #
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] transferFrom #

 + [Lib] SafeMath 
    - [Int] tryAdd
    - [Int] trySub
    - [Int] tryMul
    - [Int] tryDiv
    - [Int] tryMod
    - [Int] add
    - [Int] sub
    - [Int] mul
    - [Int] div
    - [Int] mod
    - [Int] sub
    - [Int] div
    - [Int] mod

 +  WondersMarketplace (Ownable)
    - [Pub]  #
    - [Pub] setFeeAddress #
       - modifiers: onlyOwner
    - [Pub] setCurrency #
       - modifiers: onlyOwner
    - [Int] setCurrencyFee #
    - [Pub] setCurrencyCashback #
       - modifiers: onlyOwner
    - [Int] updateList #
    - [Int] setWonderOwner #
    - [Int] updateWonderOwner #
    - [Int] updateUserBid #
    - [Int] _wondersInCollection
    - [Pub] addCollection #
       - modifiers: onlyOwner
    - [Pub] removeCollection #
       - modifiers: onlyOwner
    - [Pub] nextBidMinAmount #
       - modifiers: onlyOwner
    - [Pub] secondsAddedOnLastBid #
       - modifiers: onlyOwner
    - [Pub] secondsLeftToAdd #
       - modifiers: onlyOwner
    - [Pub] maxSaleDurationInSecondsAuction #
       - modifiers: onlyOwner
    - [Pub] maxSaleDurationInSecondsFixedPrice #
       - modifiers: onlyOwner
    - [Pub] minSaleDurationInSeconds #
       - modifiers: onlyOwner
    - [Pub] setIncentivesEnabled #
       - modifiers: onlyOwner
    - [Pub] setIncentiveCurrency #
       - modifiers: onlyOwner
    - [Pub] setIncentivesMinHoldings #
       - modifiers: onlyOwner
    - [Pub] setIncentivesAmounts #
       - modifiers: onlyOwner
    - [Pub] collectionsLength
    - [Pub] wondersInCollectionLength
    - [Pub] wonderTxsLength
    - [Pub] getCollectionAddressByID
    - [Pub] isExpired
    - [Pub] isForSale
    - [Pub] getFeeAmount
    - [Pub] getCurrentPrice
    - [Pub] getTokensByOwnerLength
    - [Pub] getUserBidsLength
    - [Pub] claim #
       - modifiers: safeCheck
    - [Pub] setForSale #
       - modifiers: eligible
    - [Pub] purchase #
       - modifiers: safeCheck
    - [Pub] removeFromSale #
       - modifiers: safeCheck,safeCheck

About SourceHat

SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

Contact us today to get a free quote for a smart contract audit of your project!

What is a SourceHat Audit?

Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

How Do I Interpret the Findings?

Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

  • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
  • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
  • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
  • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.