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: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].
- Given no level 3 Eagles have been minted yet and 6,000 level 2 Eagles have been minted, levelMinted[2] will be 6,000.
- A user attempts to ascend two level 1 Eagles in order to create a level 2 Eagle.
- The following lines in the _mintAscended function will cause the new ID to be 18,001, which corresponds to a level 3 Eagle:
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.levelMinted[_levelToMint]++; uint256 newId = _getLevelCap(_levelToMint - 1) + levelMinted[_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:MintablePresale 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.
Eagles 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.
- 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 Category | Notes | Result |
---|---|---|
Arbitrary Storage Write | N/A | PASS |
Arbitrary Jump | N/A | PASS |
Centralization of Control | WARNING | |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Deprecated Opcodes | N/A | PASS |
Ether Thief | N/A | PASS |
Exceptions | N/A | PASS |
External Calls | N/A | PASS |
Flash Loans | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | It 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 Sends | N/A | PASS |
Oracles | N/A | PASS |
Suicide | N/A | PASS |
State Change External Calls | N/A | PASS |
Unbounded Loop | N/A | PASS |
Unchecked Retval | N/A | PASS |
User Supplied Assertion | N/A | PASS |
Critical Solidity Compiler | N/A | PASS |
Overall Contract Safety | PASS |
Details: YieldToken Contract
($) = 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
($) = 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
($) = 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
($) = 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