This project demonstrates my building of an NFT game tracking real-life basketball tournament scores and allowing players to place their bets in the form of tournament brackets. It implements The Diamond Standard (EIP-2535) as a structural pattern, allowing upgradeability and deployment of larger than 24KB size contracts. It also utilizes a range of additional concepts such as TheGraph for indexing, OpenZeppelin MetaTransactions for gasless transactions, https://delegate.cash/ for token-gated access, and many more.
npm install
cp .env.example .env
and populate the missing variablesnpx hardhat compile
compile the contracts
- Make sure
subgraph/subgraph.yaml
has the proper config (networks, addresses, startBlocks)
yarn graph-deploy-production
yarn graph-deploy-dev
NOTE:
If there's an update in the contracts, the ABI in nfb-diamond-graph/abis
should be updated as well. Also, make sure the network in subgraph.yaml is local
and not mumbai
or mainnet
- Step 1: Install Graph Dependencies:
yarn graph-install
- Step 2: Generate graph code:
yarn graph-codegen
- Step 3: Run a local hardhat node:
yarn hardhat-local
- Step 4: In another terminal tab, run a local graph instance:
yarn graph-local
- Step 5: In a third tab, deploy the contracts locally:
yarn contracts:migrate:local
-
Step 6: Copy the local address of the locally deployed diamond contract (all addresses are in
./contracts.json
) under source/address ofnfb-diamond-graph/subgraph.yaml
-
Step 7: Create the local subgraph:
yarn create-local
- Step 8: Lastly, Deploy the local subgraph:
yarn deploy-local
Query the graph at
http://127.0.0.1:8000/subgraphs/name/nfb-diamond-graph
- To clean the graph:
yarn graph-local-clean
- The following tasks are exposed and adjusted to work with the testnet contracts:
yarn setupTournament
- Adds the following config to the diamond:
- Tournament Format: PlayoffBrackets (type 0)
- Sports League: NFL (number 0)
- NOTE: This config can be reused every time when creating a new tournament (no need to be added again)
- Adds the following config to the diamond:
yarn addTournament
- Adds a tournament with the following the args:
- League id: 1 (the one added in the last step)
- Format id: 1 (added in the last step)
- Name: MarchMadness
- Season: 2023
- Adds a tournament with the following the args:
yarn initializeTournament
- Uses default args:
- TournamentId: 1
- RoundsCount: 6
- WinnersPerRound: [32, 16, 8, 4, 2, 1]
- Start of 1st round: now() + 4 hours ahead
- End of 1st round: startOfFirstRound + 4 hours ahead
- Bracket Length: 63
- Reward Distribution Model: Top 5
- Reward Distribution Percentage: [3700, 2500, 1500, 1200, 1100, 0]
- Reward Distribution Ranges: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
- Round Indices: [0, 32, 48, 56, 60, 62]
- Tournament Stage: 32
- To have control over the main arguments, execute:
npx hardhat initializeTournament --diamond <diamondAddress> --id <tournamentId> --rounds <RoundsCount> --bracketlen <LenBracket> --stage <tournamentStage>
- Uses default args:
yarn signAddPool
- Signs an AddPool transaction on behalf of a user's private key (EXAMPLE_USER_PRIVATE_KEY variable in .env) and executes the autotask directly.
yarn signEnterPool
- Signs an EnterPool transaction on behalf of a user's private key (EXAMPLE_USER_PRIVATE_KEY variable in .env)and executes the autotask directly.
- Uses default args:
- poolId: 1
- tokenId: 0 (this means new token will be minted when entering the pool)
- uses a predefined bracket from constants.ts
- bracket is editable by default
- To have control over the args
npx hardhat signEnterPool --diamond <diamondAddress> --forwarder <forwarderAddress> --poolid <poolId> --tokenid <tokenId> --iseditable <true|false>
yarn signUpdateBracket
- Signs an UpdateBracket transaction on behalf of a user's private key (EXAMPLE_USER_PRIVATE_KEY variable in .env) and executes the autotask directly.
- Uses default args:
- poolId: 1
- tokenid: 1
- uses predefined newBracket from constants.ts that is to be updated
- To see what arguments are passed to each contract call, see how tasks are triggered in
package.json
. We currently have set-up, created and initialized a tournament, so calling some functions with the same arguments may fail.
yarn setupTournament
yarn addTournament
yarn initializeTournament
-
yarn signAddPool
-
yarn signEnterPool
-
yarn signUpdateBracket
yarn contracts:migrate:mumbai
yarn contracts:migrate:local
https://lucid.app/lucidchart/538311fb-34e4-403d-ba0a-e484b42b2a7d/view