Open Head NFTs and Raffles - Audit Report

Audit Summary

Open Head NFTs and Raffless Audit Report OpenHead is building a new NFT collection with a system of raffles in which NFT holders can participate and earn rewards.

For this audit, we reviewed the following contracts on the Ethereum Blockchain Mainnet:

Audit Findings

Please ensure trust in the team prior to investing as they have some control in the ecosystem.
Date: January 14th, 2022.
Updated: January 16th, 2022 to use a Merkle tree to maintain the whitelist in an effort to reduce gas consumption, and to include mainnet addresses.

Contracts Overview

OpenHeadToken Contract:
  • This contract is used to facilitate a presale and a public sale for Open Head NFTs.
  • The total supply of Open Head NFTs is 10,000.
  • Of the 10,000 NFTs, 9,750 are available to be minted by the public, and the remaining 250 are reserved for the owner for 'gifting' purposes.
  • The contract uses an off-chain generated Merkle tree provided by the owner to store and verify addresses that are whitelisted for minting during the presale.
  • While the presale is active, whitelisted users can each mint a maximum of 2 NFTs until the maximum public supply has been reached; the user must provide a proof that can be used to verify against the Merkle tree to ensure that the user is indeed whitlisted.
  • While the public sale is active, users can each mint a maximum of 5 NFTs until the maximum public supply has been reached.
  • Each NFT costs 0.07 ETH to mint; users should exercise caution and ensure that the correct amount of ETH is supplied, as the contract will not return any excess ETH to the user.
  • For each NFT minted, 0.010256410256410256 ETH is transferred to the InitialRaffle contract address, and the remaining ETH is transferred to a wallet address controlled by the team.
  • The owner can use the 'gift' functionality to mint up to 250 NFTs to any address at any time.
  • In the event that the address receiving an Open Head NFT is a contract, the contract must have implemented the onERC721Received() function in order to successfully receive the NFT.
  • The token URI associated with each NFT is determined using the NFT's token ID and the starting index value stored in the contract.
  • The starting index value is based on the starting index block number, which is set immediately after all the publicly available NFTs have been sold or after the owner has triggled the reveal() function.
  • The team's wallet address and the InitialRaffle contract address are set on deployment and cannot be changed.
  • The base URI value is set on deployment, but is not meant to be the final base URI. This is intended so that users will not know which Open Head NFT they have received until the true base URI is revealed.
  • The owner can change the base URI value at any time.
  • The owner can set the root of the Merkle tree at any time, effectively controlling the presale whitelist.
  • The owner can toggle the presale and the public sale at any time.
  • This contract complies with the ERC721 standard.
InitialRaffle Contract:
  • The contract is used to facilitate a single raffle among the owners of all Open Head NFTs for all the ETH in the contract.
  • Anyone can initiate the raffle only after the following conditions are met:
    • The token API address must be set by the team; this can only be set once and cannot be altered.
    • All Open Head NFTs available for public minting must have been minted, or the fallback date (90 days from the contract's deployment) must have passed.
  • Upon initiating the raffle, the deadline is set to the current time in the event that the fallback date has passed. Otherwise, the deadline is set to 7 days from initiation.
  • Once the raffle is initiated and the deadline has passed, anyone can advance the raffle in order to end the Funding period and generate the winning NFT ID number.
  • Users can alternatively end the Funding period by generating a random number which is evenly divisible by 7; in this case, users will need to wait 1 day before triggering the function to determine the winner.
  • The winner must claim the reward within 30 days, or anyone will be able to generate a new winner and the original winner will not be able to claim the reward.
  • Anyone can trigger the function to transfer the reward to the winner at any time.
  • Even if the Funding period is declared over, ETH can be supplied to this contract at any time.
WeeklyRaffle and WeeklyRaffleManager Contracts:
  • Anyone can send ETH to the WeeklyRaffleManager contract to generate and initialize 4 contracts representing 4 weekly raffles across the current month.
  • The raffles take place on the 1st, the 8th, the 15th, and the 22nd of each month.
  • In the event that a user sends ETH to the Manager contract during a month in which the raffles have already been initialized, the ETH is evenly distributed across each raffle; the raffles must be in the Funding state, or the transaction will revert.
  • Each raffle's funding period is over after 7 days from its respective start date.
  • Once the raffle's funding is over, anyone can advance the raffle in order to formally end the Funding period and generate the winning NFT ID number.
  • Users can alternatively end the Funding period by generating a random number which is evenly divisible by 7; in this case, users will need to wait 1 day before triggering the function to determine the winner.
  • The winner must claim the reward within 30 days, or anyone will be able to transfer the ETH back to the Manager contract and the original winner will not be able to claim the reward.
  • The ETH that is transferred back to the Manager contract is immediately distributed across the next month's weekly raffles.
MonthlyRaffle and MonthlyRaffleManager Contracts:
  • Anyone can send ETH to the MonthlyRaffleManager contract to generate and initialize a raffles for the next month.
  • Anyone can use either the Manager contract or the Raffle contract itself to contribute additional ETH to the pool after initialization, as long as the Raffle is in the Funding state.
  • The raffle takes place on the first day of the month and ends on the last day.
  • Once the raffle's funding is over, anyone can advance the raffle in order to end the Funding period and begin the Exclusion period.
  • Users can alternatively end the Funding period and begin the Exclusion period by generating a random number which is evenly divisible by 30.
  • During the Exclusion period, the owner can exclude up to 1000 NFT ID values from the raffle.
  • Users will need to wait 1 day before triggering the function to determine the winning NFT ID number.
  • The winner must claim the reward within 30 days, or anyone will be able to transfer the ETH back to the Manager contract and the original winner will not be able to claim the reward.
  • The ETH that is transferred back to the Manager contract is contributed to the following month's raffle.
OpenHeadTreasury Contract:
  • This contract is used to allocate and distribute any received ETH across the platform.
  • Any ETH received by the contract is distributed as follows:
    • A portion of at least 10% is transferred to the WeeklyRaffleManager.
    • A portion of at least 50% is transferred to the MonthlyRaffleManager.
    • Any remaining ETH is transferred to a wallet address controlled by the team.
  • The owner can set the total allocation percentage for the Weekly and Monthly raffle funds at any time, as long as the Weekly is at least 10%, the Monthly is at least 50%, and the combined percentages do not exceed 100%.
General Notes Across All Contracts:
  • Chainlink is used to generate random data securely from off-chain. This is the industry standard and is resistant to manipulation.
  • LINK tokens must be supplied to the contract in order to generate the random data.
  • As the contracts are implemented with Solidity 0.8.X, they are protected from any underflow/overflow attacks.

External Threat Results

Vulnerability CategoryNotesResult
Arbitrary Storage WriteN/APASS
Arbitrary JumpN/APASS
Centralization of ControlN/APASS
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
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

OpenHeadToken Contract

BEP20 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 #

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

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

 +  ERC165 (IERC165)
    - [Pub] supportsInterface

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

 +  OpenHeadToken (ERC721Enumerable, Ownable)
    - [Pub]  #
       - modifiers: ERC721
    - [Pub] allPublicMinted
    - [Ext] addToWhitelist #
       - modifiers: onlyOwner
    - [Ext] removeFromWhitelist #
       - modifiers: onlyOwner
    - [Ext] togglePresaleStatus #
       - modifiers: onlyOwner
    - [Ext] toggleSaleStatus #
       - modifiers: onlyOwner
    - [Ext] reveal #
       - modifiers: onlyOwner
    - [Pub] setStartingIndex #
    - [Pub] tokenURI
    - [Ext] mintHeads ($)
    - [Ext] mintPresaleHeads ($)
    - [Ext] gift #
       - modifiers: onlyOwner

InitialRaffle Contract

Bridge Contract 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 #

 + [Int] IOpenHeadToken 
    - [Ext] totalSupply
    - [Ext] ownerOf
    - [Ext] allPublicMinted

 + [Int] LinkTokenInterface 
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] balanceOf
    - [Ext] decimals
    - [Ext] decreaseApproval #
    - [Ext] increaseApproval #
    - [Ext] name
    - [Ext] symbol
    - [Ext] totalSupply
    - [Ext] transfer #
    - [Ext] transferAndCall #
    - [Ext] transferFrom #

 +  VRFRequestIDBase 
    - [Int] makeVRFInputSeed
    - [Int] makeRequestId

 +  VRFConsumerBase (VRFRequestIDBase)
    - [Int] fulfillRandomness #
    - [Int] requestRandomness #
    - [Pub]  #
    - [Ext] rawFulfillRandomness #

 +  InitialRaffle (Ownable, VRFConsumerBase)
    - [Pub]  #
       - modifiers: VRFConsumerBase
    - [Ext] setTokenAddr #
       - modifiers: onlyOwner
    - [Pub] initiateRaffle #
    - [Ext] doIRaffleToday #
    - [Ext] triggerCalculateWinner #
    - [Ext] findNewWinner #
    - [Ext] sendFundsToWinner #
    - [Int] fulfillRandomness #
    - [Ext]  ($)

WeeklyRaffleManager and WeeklyRaffle Contracts

Bridge Contract Graph

Multi-file Token

												
($) = payable function
 # = non-constant function

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

 + [Lib] Clones 
    - [Int] clone #
    - [Int] cloneDeterministic #
    - [Int] predictDeterministicAddress
    - [Int] predictDeterministicAddress

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

 + [Int] DateTimeAPI 
    - [Ext] getYear
    - [Ext] getMonth
    - [Ext] getDaysInMonth
    - [Ext] toTimestamp

 + [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] IERC721Enumerable (IERC721)
    - [Ext] totalSupply
    - [Ext] tokenOfOwnerByIndex
    - [Ext] tokenByIndex

 + [Int] LinkTokenInterface 
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] balanceOf
    - [Ext] decimals
    - [Ext] decreaseApproval #
    - [Ext] increaseApproval #
    - [Ext] name
    - [Ext] symbol
    - [Ext] totalSupply
    - [Ext] transfer #
    - [Ext] transferAndCall #
    - [Ext] transferFrom #

 +  VRFRequestIDBase 
    - [Int] makeVRFInputSeed
    - [Int] makeRequestId

 +  VRFConsumerBase (VRFRequestIDBase)
    - [Int] fulfillRandomness #
    - [Int] requestRandomness #
    - [Pub]  #
    - [Int] initialize #
    - [Ext] rawFulfillRandomness #

 +  WeeklyRaffle (VRFConsumerBase)
    - [Pub]  #
    - [Ext] initialize #
    - [Prv] requestRandom #
    - [Ext] doIRaffleToday #
    - [Ext] triggerCalculateWinner #
    - [Ext] sendFundsBackToManager #
    - [Ext] sendFundsToWinner #
    - [Int] fulfillRandomness #
    - [Ext]  ($)

 +  WeeklyRaffleManager 
    - [Pub]  #
    - [Ext]  ($)

MonthlyRaffleManager and MonthlyRaffle Contracts

Bridge Contract Graph

Multi-file Token

												
($) = payable function
 # = non-constant function

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

 + [Lib] Clones 
    - [Int] clone #
    - [Int] cloneDeterministic #
    - [Int] predictDeterministicAddress
    - [Int] predictDeterministicAddress

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

 + [Int] DateTimeAPI 
    - [Ext] getYear
    - [Ext] getMonth
    - [Ext] getDaysInMonth
    - [Ext] toTimestamp

 + [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] IERC721Enumerable (IERC721)
    - [Ext] totalSupply
    - [Ext] tokenOfOwnerByIndex
    - [Ext] tokenByIndex

 + [Int] LinkTokenInterface 
    - [Ext] allowance
    - [Ext] approve #
    - [Ext] balanceOf
    - [Ext] decimals
    - [Ext] decreaseApproval #
    - [Ext] increaseApproval #
    - [Ext] name
    - [Ext] symbol
    - [Ext] totalSupply
    - [Ext] transfer #
    - [Ext] transferAndCall #
    - [Ext] transferFrom #

 +  VRFRequestIDBase 
    - [Int] makeVRFInputSeed
    - [Int] makeRequestId

 +  VRFConsumerBase (VRFRequestIDBase)
    - [Int] fulfillRandomness #
    - [Int] requestRandomness #
    - [Pub]  #
    - [Int] initialize #
    - [Ext] rawFulfillRandomness #

 +  MonthlyRaffle (VRFConsumerBase)
    - [Pub]  #
    - [Ext] initialize #
    - [Prv] requestRandom #
    - [Ext] doIRaffleToday #
    - [Ext] triggerCalculateWinner #
    - [Ext] addExcluded #
       - modifiers: onlyOwner
    - [Ext] sendFundsBackToManager #
    - [Ext] sendFundsToWinner #
    - [Int] fulfillRandomness #
    - [Ext]  ($)

 +  MonthlyRaffleManager 
    - [Pub]  #
    - [Ext]  ($)

OpenHeadTreasury Contract

Bridge Contract 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 #

 +  OpenHeadTreasury (Ownable)
    - [Pub]  #
    - [Ext] setWeeklyShare #
       - modifiers: onlyOwner
    - [Ext] setMonthlyShare #
       - modifiers: onlyOwner
    - [Ext] setTeamAddress #
       - modifiers: onlyOwner
    - [Ext]  ($)