{"id":21763370,"url":"https://github.com/complexlity/immutable-game","last_synced_at":"2025-03-21T04:44:18.504Z","repository":{"id":204771118,"uuid":"711283743","full_name":"Complexlity/immutable-game","owner":"Complexlity","description":"Memory test game built on Immutable passport ","archived":false,"fork":false,"pushed_at":"2024-02-13T22:41:53.000Z","size":3744,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-26T01:28:38.503Z","etag":null,"topics":["immutable","nextjs","nextui","redis"],"latest_commit_sha":null,"homepage":"https://immutable-game.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Complexlity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-10-28T19:04:00.000Z","updated_at":"2024-01-19T13:04:46.000Z","dependencies_parsed_at":"2023-11-24T18:28:44.294Z","dependency_job_id":"82673c24-5e10-4238-aa26-93052620d382","html_url":"https://github.com/Complexlity/immutable-game","commit_stats":null,"previous_names":["complexlity/immutable-game"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Complexlity%2Fimmutable-game","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Complexlity%2Fimmutable-game/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Complexlity%2Fimmutable-game/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Complexlity%2Fimmutable-game/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Complexlity","download_url":"https://codeload.github.com/Complexlity/immutable-game/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244739939,"owners_count":20501990,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["immutable","nextjs","nextui","redis"],"created_at":"2024-11-26T12:15:06.342Z","updated_at":"2025-03-21T04:44:18.478Z","avatar_url":"https://github.com/Complexlity.png","language":"JavaScript","readme":"# Immutable Game\n\n\n## Technologies Used\n\n- [Immutable Passport]('https://docs.immutable.com/docs/zkevm/products/passport/')\n- [Nextjs](https://nextjs.org)\n\n## Rules/Features of the Game\n\n### Features\n\n- User Authetication using immutable passport\n- NFT minting\n- Get all NFTs minted to your address\n\n### Rules\n\n- You should try to click on each card only once\n- For each time you click, the cards shuffles so it gets harder to know what was clicked\n- If you click twice, it resets your counter to zero and saves your score\n- As there are twelve(12) cards, the maximum score you can get is 12\n- If you attain this score and you are authenticated using immutable passport, you will have a free nft minted for you.\n- You can view all nfts minted to you as well\n\n\n\n## Steps Taken\n\nHere are the steps I took to build the application\n\n1. Uploading NFT images to pinata.\n\nThe first step is taking 11 nfts images and putting them on [pinata]('https://www.pinata.cloud/') and retrieving their CIDs.\n\nPinata is a decentralize storage platform which facilitates data sharing on web3.\n\n10 nfts are the actual nft images and 1 more represents the image for the collection.\n\n2. Creating **nft-metadata** folder and **contract.json** file.\n\nThese are the two files need to create the contract.\n\nAn nft-metadata file contains information about the nft including the name, image url, description, id, etc.\n\n\u003cdetails\u003e\n\u003csummary\u003enft metadate file example\u003c/summary\u003e\n\u003cpre\u003e\n{\n      \"id\": 1,\n      \"image\": \"https://aquamarine-private-asp-292.mypinata.cloud/ipfs/QmThUbV2EkLdh1LX1tkyaNJSRFFfRSF6MEn29McCXchugo\",\n      \"token_id\": 1,\n      \"background_color\": null,\n      \"animation_url\": null,\n      \"youtube_url\": null,\n      \"name\": \"Memory Game NFT\",\n      \"description\": \"This is the nft for the winner of the memory game\",\n      \"external_url\": null,\n      \"attributes\": [\n        {\n          \"trait_type\": \"Rarity\",\n          \"value\": \"Unique\"\n        }\n      ]\n    }\n\u003c/pre\u003e\n\u003c/details\u003e\n\nFor this contract, I needed to mint 100 different NFTs.\n\nThis is because, the ERC721 specification by immutable forbids creating more that one NFT using the same token id (in layman terms: I can only use a metadata file once).\nSo I need to create as much as possible.\n\nGet in [generateMetadata.js](src/utils/generateMetadata.js) function ( found in `src/utils/generateMetadata.js`).\n\nI made this function which takes all 10 image CIDs gotten from Pinata and also takes the metadata template file and creates 100 different NFTs at random.\n\nIt then puts them in a folder called `nft-metadata`. This ensure less repetition when user's mint\n\n**Point To Note**: A metadata file has only numerical names with no file extension. i.e 1, 2, 3 not 1.json, 2.json, ...\n\nThe *contract.json* file will contain the CID of the nft collection image and some other information about the collection\n\n\u003cdetails\u003e\n\u003csummary\u003e contract.json file example\n\u003cpre\u003e\n{\n  \"name\": \"My NFT Collection\",\n  \"description\": \"NFT Collection on Immutable zkEVM\",\n  \"image\": \"https://aquamarine-private-asp-292.mypinata.cloud/ipfs/QmUQHH944BeXaM5DN9mJnEcRuPg6xgCW4sACCRWeWXp6aP\",\n  \"external_link\": \"https://some-url\"\n}\n\u003c/pre\u003e\n\u003c/details\u003e\n\n3. After creating (or generating :) ) these files, I uploaded them back to `pinata`. The nft-metadata folder is uploaded as `folder` while the `contract.json` as a file and obtained their CID values after successful upload.\n\n4. I then created need an ethereum testnet account to deploy this contract. Here are steps to do so.\n\n- [Download Metamask Chrome Extension](https://metamask.io/download/) and Create an account.\n- Copy our wallet address and get some testnet tokens from [Sepolia faucet](https://sepoliafaucet.com/).\n- On success, Copy our metamask private key.([How to](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key#:~:text=On%20the%20'Account%20details'%20page,private%20key%20to%20your%20clipboard.)).\n\n5. Next, I used [zkEVM boilerplate repository](https://github.com/immutable/zkevm-boilerplate) provided by the immutable team to quickly deploy the smart contract. Here's a summary in steps to do the same\n\n - Clone the repository on you local machine and install all dependencies.\n\n  ```bash\n  git clone https://github.com/immutable/zkevm-boilerplate.git\n  npm install\n  ```\n- Rename `.env.example` to `env` and put your metamask private key in the `PRIVATE_KEY` variable\n- Head to `scripts/deploy.ts` and ensure to replace the values with that of your new NFT collection. See the code snippet for reference\n\n```\nconst contract: MyERC721 = await factory.connect(deployer).deploy(\n    deployer.address, // owner\n    \"\u003cyour nft name\u003e\", // name\n    \"MG\", // symbol\n    \"\u003cyour-pinata-gateway\u003e.mypinata.cloud/ipfs/\u003cyour-nft-metadata-CID\u003e/\", // baseURI\n    \"\u003cyour-pinata-gateway\u003e.mypinata.cloud/ipfs/\u003cyour-contract.json-uri\u003e\", // contractURI\n    operatorAllowlist, // operator allowlist\n    deployer.address, // royalty recipient\n    ethers.BigNumber.from(\"2000\") // fee numerator\n  );\n  ```\n\n- Compile and Deploy\n\n```npm\nnpx hardhat compile\nnpx hardhat run --network immutableZkevmTestnet scripts/deploy.ts\n```\n\n6. After deploying the contract, copy the contract's hash (shown on the console) as we would need it to be able to mint and get the NFTs on the client.\n\n7. Next, we make our testnet address a minter.\nUsing the [grantMinterRole](src/utils/grantMinterRole.js) function and supplying the **private key** obtained from metamask and the just deployed **contract address**. I am able to give my address the ability to generate new assets.\n\n8. I then created some utility functions needed in the client application. Here, there's three of them.\n\n- Mint Nft - Used to create a new NFT items. Found in [src/utils/mintNft.js](src/utils/mintNft.js).\n- Get Nft - Used to retrieve information about the NFT by tokenId. This is used to quickly return the newly minted NFT on the client once minted. Found in [src/utils/getNft.js](src/utils/getNft.js).\n\n**Bug?**: I noticed that, if I mint a new item using `mintNft()` function and immediatly after, try to retrieve the newly  minted NFT using `getNft()` it returns not found (while it shouldn't be so as this item was just minted). My guess is the way the rpc works, it takes sometime to get updated transactions.\n**Work Around**: Instead of fetching the recently minted NFT, I fetch the previously minted one (to avoid errors). But when the user goes to their collections, they would see the correct NFT there\n\n- Get User's Nfts. Found in [src/utils/getNftByAddress.js](src/utils/getNftByAddress.js).  It is used to retrieve all NFTs minted to the logged in user's address. This is used to fetch the collection owned by a particular user.\n\n9. The client side of the project was bootstrapped using [Nextjs]('https://nextjs.org').\n\nIn summary, there are 12 static images used to make 12 cards. Each card have a unique id which I used to track the `clicked` state of each.\n\nThis an example psuedocode of what happens:\n\n```pseudocode\ncard clicked\nif card was not clicked before\nincrement the score\nelse (card was clicked before)\nreset the whole system. While doing so, check if the current score is higher than the user's max score and update best score accordingly\nIf you have obtained the max score (in this case 12)\nreset the whole systems and save the highest score\n```\n\n10.  I then added authentication using Immutable Passport as required. Here's a detailed guide I wrote about doing so - [Detailed Guide](https://github.com/Complexlity/immutable-planner-app).\n\n11. After doing this, I was ready to add the utility functions to the application. I took the Modal Element(https://nextui.org/docs/components/modal) from [NextUI]('https://nextui.org').I then made this component to render whenever an authenticated user reached the `end of game` (i.e complete the challenge clicking each card once).\n\nUsing React's [useEffect Hook](https://react.dev/reference/react/useEffect),\nI made this component to trigger the minting and fetching whenever it renders. It calls the `mintNft()` function and then the `getNft()` function both of which are built on the immutable's sdk. This also takes some time so I have removed all possibities of closing midway to avoid causing unwanted behaviours\n\n12. When the items is fully minted and fetched, the user would see two buttons:\n\n- `View Nfts`: For the user to see all the items they have collected so far.\n- `Close`: To close the modal now that we have completed all functions.\n\n13. The view NFT page which then uses the `getNftsByAddress` function to show all the NFTs by the user.\n\n### The Unique Token Id Problem\n\nSince immutable does not allow creating multiple NFTs with the same token Id, I have to be able to track the minted NFTs i.e if I create an item with say `tokenId = 1` and try to create another with the same tokenId of 1, it fails. So I have to find a way to always increment this value each time any user mints a new item.\nI created an [api route](src/pages/api/token.js) which just statically fetches the id and updates it each time. This was done using [Redis](https://redis.io/) for [speed](https://levelup.gitconnected.com/redis-vs-other-databases-an-in-depth-comparison-of-sql-and-nosql-solutions-7c4a9ca9183).\n\n## Conclusion\n\nI learnt quite a lot of things carrying out this bounty. I can also affirm the ease of adding authentication and interacting with the blockchain using immutable passport especially from a web2 background. And I would definitely build with it in future projects.\n\n\n## Resources\n\n- [Immutable Passport Documentation](https://docs.immutable.com/docs/zkEVM/products/passport).\n- [Immutable Campaign Quest 4](https://app.stackup.dev/quest_page/quest-4---mint-your-first-nft-on-immutable-zkevm).","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomplexlity%2Fimmutable-game","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomplexlity%2Fimmutable-game","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomplexlity%2Fimmutable-game/lists"}