Forgeries contest details

  • Total Prize Pool: $36,500 USDC
    • HM awards: $25,500 USDC
    • QA report awards: $3,000 USDC
    • Gas report awards: $1,500 USDC
    • Judge + presort awards: $6,000 USDC
    • Scout awards: $500 USDC
  • Join C4 Discord to register
  • Submit findings using the C4 form
  • Read our guidelines for more details
  • Starts December 13, 2022 20:00 UTC
  • Ends December 16, 2022 20:00 UTC

C4udit / Publicly Known Issues

The C4audit output for the contest can be found here within an hour of contest opening.

Note for C4 wardens: Anything included in the C4udit output is considered a publicly known issue and is ineligible for awards.

We rely on @chainlink/contracts to supply VRF numbers and this contract clearly documents chain.link as a dependency for this project. Any issues directly related to the chainlink infastructure contracts other than incorrect configuration of their libraries within this project are not in scope for this audit.

Overview

We want to raffle away a single NFT (token) based off of another NFT collection (or drawingToken) in a fair and trustless manner.

For instance, we could raffle off a single high value NFT to any cryptopunk holder, the punk that wins can choose to claim the NFT. If they do not claim, a re-roll or redraw can be done to select a new holder that would be able to claim the NFT.

The contract follows the hyperstructure concept (https://jacob.energy/hyperstructures.html) except for the dependency on chain.link (https://chain.link/).

We are utilizing the chain.link Verifiable Random Function (VRF) contract tools to fairly raffle off the NFT. Their VRF docs can be found at: https://docs.chain.link/vrf/v2/introduction/.

The main functions are VRFNFTRandomDrawFactory.makeNewDraw() to create a new non-upgradeable minimal clones proxy draw contract with your desired configuration. Each contract is separate to allow for easier UX and more security with interactions. After the drawing is created, it needs to be started which will pull the NFT from the creator/owner's wallet up for raffle when they call VRFNFTRandomDraw.startDraw().

After the drawing is started, we will request a random entropy from chain.link using the internal _requestRoll() function. Once chain.link returns the data in the fulfillRandomWords() callback the raffle NFT will be chosen and saved. If the raffle NFT is burned or removed this will still complete and a redraw will need to happen to find an NFT that is active/accessible to draw the winning NFT. Most raffles will use a specific contract that users will have a high incentive to withdraw their winning NFT.

The winning user can determine if they have won by calling hasUserWon(address) that checks the owner of the winning NFT to return the winning user. They also can look at request().currentChosenTokenId to see the currently chosen winning NFT token id. Once they have won, they can call winnerClaimNFT() from the account that won to have the raffled NFT transferred to the winner.

If the winning user does not claim the winning NFT within a specific deadline, the owner can call redraw() to redraw the NFT raffle. This is an ownerOnly function that will call into chain.link.

If no users ultimately claim the NFT, the admin specifies a timelock period after which they can retrieve the raffled NFT.

Scope

Files in scope

File SLOC Description and Coverage Libraries
Contracts (4)
src/utils/Version.sol 10 100.00%
src/VRFNFTRandomDrawFactoryProxy.sol 15 Proxy Contract linking to the Factory,   - @openzeppelin/contracts-upgradeable
src/VRFNFTRandomDrawFactory.sol 41 Factory for VRF NFT Raffle, UUPS Upgradable by owner.,   100.00% @openzeppelin/contracts-upgradeable
src/VRFNFTRandomDraw.sol ♻️ 196 This contract is the main escrow and VRF-integrated raffle contract,   86.67% @openzeppelin/contracts-upgradeable @chainlink/contracts
Abstracts (1)
src/ownable/OwnableUpgradeable.sol 73 This contract is the main escrow and VRF-integrated raffle contract,   94.12% @openzeppelin/contracts-upgradeable
Interfaces (3)
src/interfaces/IVRFNFTRandomDrawFactory.sol 11 Interface to the main VRFNFTRandomDraw contract,   -
src/ownable/IOwnableUpgradeable.sol 15 The interface to an owner safe-transferrable upgradeable openzeppelin fork,   -
src/interfaces/IVRFNFTRandomDraw.sol 62 Interface to the factory VRFNFTRandomDrawFactory contract,   -
Total (over 8 files): 423 89.41%

External imports

Out of scope

  1. OpenZeppelin dependency contracts
  2. UUPS Proxy and OpenZeppelin implementation thereof
  3. Chainlink dependency architecture / contracts
  4. Issues / drawbacks of using specific EIP standards (EIP721 (NFT Token standard), EIP1167 (minimal proxies/clones))
  5. The NFT up for raffle and NFT that is used for the raffle are non-malicious contracts not attempting to compromise this raffle contract (within reason). Assume creators of raffles will do checks to ensure that the NFT itself is not compromised or unusual preventing the functioning of the raffle contract.

Additional Context

  1. We have no unique curve logic or mathematical models in this contract.
  2. This contract should prevent the owner from iterrupting the contest until the timelock unlocks at the end and preventing any users that do not win from withdrawing the NFT.

Scoping Details

- If you have a public code repo, please share it here:  https://github.com/0xigami/vrf-nft-raffle
- How many contracts are in scope?:   3
- Total SLoC for these contracts?:  320
- How many external imports are there?: 6 
- How many separate interfaces and struct definitions are there for the contracts within scope?:  2 structs, 2 interfaces
- Does most of your code generally use composition or inheritance?:   Code typically uses composition
- How many external calls?:   4
- What is the overall line coverage percentage provided by your tests?:  94
- Is there a need to understand a separate part of the codebase / get context in order to audit this part of the protocol?:   false
- Please describe required context:   
- Does it use an oracle?:  true
- Does the token conform to the ERC20 standard?:  N/A
- Are there any novel or unique curve logic or mathematical models?: N/A
- Does it use a timelock function?:  Yes
- Is it an NFT?: No (but NFTs get locked inside)
- Does it have an AMM?:   No
- Is it a fork of a popular project?:   false
- Does it use rollups?:   false
- Is it multi-chain?:  false
- Does it use a side-chain?: false 

Tests

Quickstart command

rm -Rf 2022-12-forgeries || true && git clone https://github.com/code-423n4/2022-12-forgeries.git -j8 && cd 2022-12-forgeries && yarn && foundryup && yarn test-gas

To run tests:

  1. Setup yarn: https://yarnpkg.com/getting-started/install
  2. Setup forge: https://book.getfoundry.sh/getting-started/installation
  3. Install dependencies: run yarn
  4. Run tests: run yarn test

Slither notes:

  1. This project is tested and works with Slither v 0.9.0
  2. Slither will have issues with the try/catch blocks upon first test
  3. All test files and files from chain.link have issues that are out of scope in the repo for Slither.