Eden Gallery - Smart Contract Audit Report

Audit Summary

Eden Gallery is creating a new NFT collection which users generate rewards in the form of a yield token.

For this audit, we reviewed Eden Gallery's YieldToken, MintablePresale, MintableERC721, Nameable, Eagles, and FounderKeys contracts using code provided by the project team.

Audit Findings

Please ensure trust in the team prior to investing as they have substantial control in the ecosystem. Off-chain logic is used to authorize users to participate in the presale via a Merkle tree.
Date: January 21st, 2022.

Finding #1- Eagles - Low

Description: When "ascending" two tokens to create a token of a higher level, there is no check to ensure that the new token ID is within the range of token IDs for the desired token level.
Risk/Impact: It is possible that a user will receive a token ID that is a level higher than intended and break the counter for the next level. The risk of this occurring is low, as the new token ID in this scenario would likely already be taken, causing the function to fail.
Example Scenario:
  1. Given no level 3 Eagles have been minted yet and 6,000 level 2 Eagles have been minted, levelMinted[2] will be 6,000.
  2. A user attempts to ascend two level 1 Eagles in order to create a level 2 Eagle.
  3. The following lines in the _mintAscended function will cause the new ID to be 18,001, which corresponds to a level 3 Eagle:
    			
    levelMinted[_levelToMint]++;
    uint256 newId = _getLevelCap(_levelToMint - 1) + levelMinted[_levelToMint];
    
    Since no level 3 Eagles have been minted yet, this ID is not taken and the user will be successfully minted a level 3 Eagle. This will continue any time users ascend two level 1 tokens. In addition, ascending two level 2 tokens will no longer work as the token ID will be used to mint will already be taken.
Recommendation: Check that the new ID is less than or equal to the ID cap of the desired token level, or ensure that levelMinted[_levelToMint] is less than or equal to desired LEVEL_MAX_COUNTS[_levelToMint].

Finding #2 - Eagles - Informational

Description: The following functions are declared public, but are never called internally:
			
getTokenName(), getTokenBio(), tokenNameByIndex(), mintLegendary, walletOfOwner().

Recommendation: These functions can be declared external for additional gas savings on each call.

Finding #3 - MintablePresale - Informational

Description: The following functions are declared public, but are never called internally:
			
buy(), buyTo(), withdraw().

Recommendation: These functions can be declared external for additional gas savings on each call.

Contracts Overview

YieldToken Contract:
  • Wings ($WNGS) is an ERC20 token that interacts with the team's Eagles NFT contract.
  • No tokens are minted upon deployment; tokens are only minted when rewards are claimed.
  • The Eagles Contract Role, which is intended to be granted to the Eagles contract, has the ability to distribute rewards and burn any user's tokens at any time.
  • The Eagles Contract Role also has the ability to burn any user's $WNGS at any time.
  • Users can reduce the circulating supply by sending tokens to the 0x..dead address if desired.
  • Holders of Eagles NFTs earn rewards over time based on the number and levels of the NFTs that they own.
  • Users will earn 10 tokens per day for each level one Eagle held, 20 tokens for each level two, 40 tokens for each level three, and 80 tokens for each level four.
  • Rewards will stop being generated on December 29th, 2031.
  • The owner can grant the Eagles Contract Role to any address at any time.
MintablePresale Contract:
  • This contract allows authorized users to use ETH to purchase a specified supported ERC-721 token.
  • When purchasing, the user must specify a price, start time, end time, number of tokens to purchase, and a Merkle proof.
  • The user address, price, start time, and end time are hashed and verified with the passed Merkle proof against the contract's Merkle root.
  • The time of purchase must be between within the verified start and end times.
  • Users can purchase one or more tokens in a single call.
  • When buying in batches, the user's amount to purchase cannot exceed the contract's "batch limit".
  • In addition, a user's total number of purchases cannot exceed the contract's mint limit.
  • Any excess ETH sent will be returned to the purchaser.
  • The owner can update the Merkle root, mint limit, batch limit, next token ID to be minted, and the last ID that can be minted at any time.
  • The owner can withdraw any ETH from the contract at any time.
Eagles Contract:
  • This contract allows users to upgrade their Eagles NFTs and earn $WNGS rewards based on their holdings.
  • The owner of this contract is given the Default Admin Role, Minter Role, Burner Role, URI Manager Role, Yield Token Manager Role, and Legendary Minter Role upon deployment.
  • The owner can grant or revoke any role to any address at any time.
  • Any URI Manager can update the base URI at any time.
  • Any Burner can burn any user's Eagles at any time.
  • Any Minter can mint Eagles to any address at any time.
  • Any Yield Token Manager can update the Wings token address at any time.
  • Any Legendary Minter has the ability to mint level 4 Eagles to any address at any time.
  • Users can "ascend" two Eagles of the same level to have an Eagle minted to them that is one level higher; the two ascended Eagles are burned in the process.
  • A higher level Eagle will earn a greater reward amount.
  • In addition to levels, Eagles also have names and bios associated with them. Names must be unique and have certain limitations, but bios can be set to any string.
  • Users can also spend 100 $WNGS to update their Eagles bio, or 300 $WNGS to update its name; $WNGS spent on these purchases are burned.
  • Users can also claim their accrued $WNGS rewards through this contract; where $WNGS will be minted to them.
  • Only 12,000 level 1 Eagles, 6,000 level 2 Eagles, and 3,000 level 3 Eagles can be minted.
  • An unlimited number of level 4 (Legendary) Eagles can be minted.
  • Users cannot purchase Eagles through this contract.
  • Users' reward rates are updated any time they claim, ascend, participate in a transfer, or have Eagles minted to them.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of Control
  • The owner and defined roles have the permissions mentioned above.
  • Any Burner can burn any user's Eagles at any time.
  • Any Eagles Contract Role can burn users' $WNGS at any time.
  • A Minter can mint Eagles to any address at any time.
  • A Legendary Minter can mint level 4 Eagles to any address at any time.
  • Off-chain logic is used to keep track of users authorized to participate in the presale by use of a Merkle tree.
  • WARNING
    Delegate Call to Untrusted ContractN/APASS
    Dependence on Predictable VariablesN/APASS
    Deprecated OpcodesN/APASS
    Ether ThiefN/APASS
    ExceptionsN/APASS
    External CallsN/APASS
    Flash LoansN/APASS
    Integer Over/UnderflowN/APASS
    Logical IssuesIt is possible for ascends to generate a token of an incorrect level, resulting in mints of tokens in this new level to stop functioning.WARNING
    Multiple SendsN/APASS
    OraclesN/APASS
    SuicideN/APASS
    State Change External CallsN/APASS
    Unbounded LoopN/APASS
    Unchecked RetvalN/APASS
    User Supplied AssertionN/APASS
    Critical Solidity CompilerN/APASS
    Overall Contract Safety PASS

    Details: YieldToken Contract

    ERC20 Token Graph

    Multi-file Token

    												
    ($) = 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 #
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     + [Int] IERC20 
        - [Ext] totalSupply
        - [Ext] balanceOf
        - [Ext] transfer #
        - [Ext] allowance
        - [Ext] approve #
        - [Ext] transferFrom #
    
     + [Int] IERC20Metadata (IERC20)
        - [Ext] name
        - [Ext] symbol
        - [Ext] decimals
    
     +  ERC20 (Context, IERC20, IERC20Metadata)
        - [Pub]  #
        - [Pub] name
        - [Pub] symbol
        - [Pub] decimals
        - [Pub] totalSupply
        - [Pub] balanceOf
        - [Pub] transfer #
        - [Pub] allowance
        - [Pub] approve #
        - [Pub] transferFrom #
        - [Pub] increaseAllowance #
        - [Pub] decreaseAllowance #
        - [Int] _transfer #
        - [Int] _mint #
        - [Int] _burn #
        - [Int] _approve #
        - [Int] _beforeTokenTransfer #
        - [Int] _afterTokenTransfer #
    
     +  YieldToken (ERC20, AccessControl, Ownable)
        - [Pub]  #
        - [Int] min
        - [Ext] setEaglesContract #
           - modifiers: onlyOwner
        - [Ext] updateReward #
           - modifiers: onlyRole
        - [Ext] getReward #
           - modifiers: onlyRole
        - [Ext] burn #
           - modifiers: onlyRole
        - [Ext] getTotalClaimable
        - [Int] _getTotalPendingRewardForUser
        - [Int] _updateRewardsAndLastUpdate #
    
     + [Int] IEagles 
        - [Ext] getLevelForToken
        - [Ext] getAddressBalanceForLevel 
     

    Details: MintablePresale Contract

    ERC20 Token Graph

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
      
      +  Context 
        - [Int] _msgSender
        - [Int] _msgData
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     + [Lib] MerkleProof 
        - [Int] verify
        - [Int] processProof
    
     + [Int] IERC165 
        - [Ext] supportsInterface
    
     + [Int] IERC721 (IERC165)
        - [Ext] balanceOf
        - [Ext] ownerOf
        - [Ext] safeTransferFrom #
        - [Ext] transferFrom #
        - [Ext] approve #
        - [Ext] getApproved
        - [Ext] setApprovalForAll #
        - [Ext] isApprovedForAll
        - [Ext] safeTransferFrom #
    
     + [Int] IMintableERC721 
        - [Ext] exists
        - [Ext] mint #
        - [Ext] mintBatch #
        - [Ext] safeMint #
        - [Ext] safeMint #
        - [Ext] safeMintBatch #
        - [Ext] safeMintBatch #
    
     +  MintablePresale (Ownable)
        - [Pub]  #
        - [Pub] itemsOnSale
        - [Pub] itemsAvailable
        - [Pub] isActive
        - [Pub] initialize #
           - modifiers: onlyOwner
        - [Pub] buy ($)
        - [Pub] buyTo ($)
        - [Pub] buySingle ($)
        - [Pub] buySingleTo ($)
        - [Pub] withdraw #
        - [Pub] withdrawTo #
           - modifiers: onlyOwner
    
     

    Details: Eagles Contract

    ERC20 Token Graph

    Multi-file Token

    												
    ($) = 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 #
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     + [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]  #
        - [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 #
    
     + [Int] IMintableERC721 
        - [Ext] exists
        - [Ext] mint #
        - [Ext] mintBatch #
        - [Ext] safeMint #
        - [Ext] safeMint #
        - [Ext] safeMintBatch #
        - [Ext] safeMintBatch #
    
     +  MintableERC721 (ERC721, ERC721Enumerable, IMintableERC721, AccessControl, Ownable)
        - [Pub]  #
           - modifiers: ERC721
        - [Int] _baseURI
        - [Pub] tokenURI
        - [Ext] setBaseURI #
           - modifiers: onlyRole
        - [Ext] exists
        - [Pub] burn #
           - modifiers: onlyRole
        - [Pub] mint #
           - modifiers: onlyRole
        - [Pub] mintBatch #
           - modifiers: onlyRole
        - [Pub] safeMint #
           - modifiers: onlyRole
        - [Pub] safeMint #
        - [Pub] safeMintBatch #
        - [Ext] safeMintBatch #
        - [Pub] supportsInterface
        - [Int] _beforeTokenTransfer #
    
     +  Nameable 
        - [Pub] getTokenName
        - [Pub] getTokenBio
        - [Pub] changeBio #
        - [Pub] changeName #
        - [Int] toggleReserveName #
        - [Pub] tokenNameByIndex
        - [Pub] isNameReserved
        - [Pub] validateName
        - [Pub] toLower
    
     +  Eagles (MintableERC721, Nameable)
        - [Pub]  #
           - modifiers: MintableERC721
        - [Ext] setYieldToken #
           - modifiers: onlyRole
        - [Ext] ascend #
           - modifiers: isValidAscent
        - [Ext] getReward #
        - [Ext] getTokenLevel
        - [Ext] getAddressBalanceForLevel
        - [Pub] transferFrom #
        - [Pub] safeTransferFrom #
        - [Pub] mint #
           - modifiers: onlyRole,mintCheck
        - [Pub] mintLegendary #
           - modifiers: onlyRole
        - [Pub] mintBatch #
           - modifiers: onlyRole,mintCheck
        - [Pub] changeBio #
        - [Pub] changeName #
        - [Pub] walletOfOwner
        - [Int] _mintToken #
        - [Int] _mintAscended #
        - [Int] _burnAscent #
        - [Int] _getLevelCap
        - [Int] _adjustLevelBalances #
    
     + [Int] IWings 
        - [Ext] burn #
        - [Ext] updateReward #
        - [Ext] getReward #
    
     
     

    Details: FounderKeys Contract

    ERC20 Token Graph

    Multi-file Token

    												
    ($) = payable function
     # = non-constant function
    
      + [Int] IERC165 
        - [Ext] supportsInterface
    
     + [Int] IERC1155 (IERC165)
        - [Ext] balanceOf
        - [Ext] balanceOfBatch
        - [Ext] setApprovalForAll #
        - [Ext] isApprovedForAll
        - [Ext] safeTransferFrom #
        - [Ext] safeBatchTransferFrom #
    
     + [Int] IERC1155Receiver (IERC165)
        - [Ext] onERC1155Received #
        - [Ext] onERC1155BatchReceived #
    
     + [Int] IERC1155MetadataURI (IERC1155)
        - [Ext] uri
    
     + [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
    
     +  ERC165 (IERC165)
        - [Pub] supportsInterface
    
     +  ERC1155 (Context, ERC165, IERC1155, IERC1155MetadataURI)
        - [Pub]  #
        - [Pub] supportsInterface
        - [Pub] uri
        - [Pub] balanceOf
        - [Pub] balanceOfBatch
        - [Pub] setApprovalForAll #
        - [Pub] isApprovedForAll
        - [Pub] safeTransferFrom #
        - [Pub] safeBatchTransferFrom #
        - [Int] _safeTransferFrom #
        - [Int] _safeBatchTransferFrom #
        - [Int] _setURI #
        - [Int] _mint #
        - [Int] _mintBatch #
        - [Int] _burn #
        - [Int] _burnBatch #
        - [Int] _setApprovalForAll #
        - [Int] _beforeTokenTransfer #
        - [Prv] _doSafeTransferAcceptanceCheck #
        - [Prv] _doSafeBatchTransferAcceptanceCheck #
        - [Prv] _asSingletonArray
    
     +  Ownable (Context)
        - [Pub]  #
        - [Pub] owner
        - [Pub] renounceOwnership #
           - modifiers: onlyOwner
        - [Pub] transferOwnership #
           - modifiers: onlyOwner
        - [Int] _transferOwnership #
    
     +  FounderKeys (ERC1155, Ownable)
        - [Pub]  #
           - modifiers: ERC1155
        - [Pub] uri
        - [Ext] setURI #
           - modifiers: onlyOwner