Loopstudio Collectibles

Loopcollectibles Created and maintained with ❤️ by LoopStudio

Description

Loopstudio Collectibles is an NFT collection crafted by Loopstudio developers and designers

Art was made by one of our incredible designers, Facundo Astete, and his drawings represents a different role at Loopstudio:

  • Project Manager
  • Developer
  • UX/UI Designer
  • QA
  • Office Manager

Each role, in conjuntion with different traits, define an unique NFT that represents a colaborator of Loopstudio:

  • Seniority
  • Role
  • Hobby
  • Does it drinks coffe or Mate?

QA

Watch them at OpenSea

Technical Details

Stack

  • Solidity 0.8.15
  • Hardhat
    • hardhat-deploy
    • hardhat-ethers
    • hardhat-etherscan
  • Openzeppelin Contracts
    • ERC721URIStorage
    • Ownable
  • Chainlink Contracts
    • VRFConsumerBaseV2
  • Typescript & Typechain
  • Pinnata SDK (IPFS)

Project Structure

.
├── contracts/
├── deploy/
├── deployments
├── tasks
├── typechain-types
├── utils
├── test
├── hardhat.config.ts
├── helper-hardhat-config.ts

What does each folder represents?

  • contracts: where the LoopNFT.sol is placed
  • deploy: hardhat-deploy scripts to deploy to localhost, using mock contracts, testnet and mainnet. The scripts also verifies contract source code to etherscan and publish the NFT metadata to IPFS using pinata-sdk
  • deployment: information about old deploys and ABI, generated by hardhat-deploy
  • tasks: hardhat tasks to interact with the LoopNFT.sol contract after its deployed to a network
  • typechain-types: types definitions for each contract generated by typechain
  • utils: images/assets, metadata about the NFT items and functions to upload information to IPFS and verify sourcecode against etherscan
  • test: unit and staging tests
  • hardhat.config.ts: hardhat configuration file. Currenlty supports ethereum and polygon networks configuration.
  • helper-hardhat-config.ts: helper file containing information per network, like # of confirmations, Chainlink smart contract addresses and subscriptions.

Use Case

We wanted to represent the following usecase:

  • The contract is deployed and a limited amount of unique items are available to be minted.
  • Initially, the collection is empty, this means, the owner of the contract has no control of any NFT.
  • Any EOA can retrieve one or more of the limited unique items by interacting with the mint function and paying the gas fees.
  • The contract obtains a random item based on a random number and assign it to the minter address
  • When there are no items left, the contract reverts the following mint transactions

How did we solve it?

We follow the following approach to bootstrap 70 unique items and ensure that the mint was randomic:

  1. The contract constructor receives characterUris, an array of NFT metadata following the ERC721 Metadata Standards. Metadata was previously stored on IPFS using pinata sdk to ensure its completely descentralized.
  2. We used Chainlink VRF for random numbers generation, thusLoopNFT inherits from VRFConsumerBaseV2
  3. Once the contract is deployed, it address is added as a VRF subscription consumer, this means that we needed to fund the contract address with $LINK
  4. Since we didnt want to charge the minter with extra fees of random requesting, an onlyOnwer initializeRandoms function is invoked by the deployer to fulfill an array of random numbers. The size of this array is exactly the same as the different unique items that the collection provides.
  5. Each time mint is called we take the latest random number and we divide it by the modulus of the characterUris aray size to get the current character tokenUri to mint. We associate that tokenUri with the tokenId that is being minted using ERC721URIStorage _setTokenUri(tokenId, tokenUri) method. Since both the random number and the tokenUri were used, we removed them from the contract storage.

Getting started

Config

  1. Copy .env.example to .env
  2. Fullfil the following properties:
GOERLI_URL=[INFURA OR ALCHEMY RPC URL]
MUAMBAI_URL=[INFURA OR ALCHEMY RPC URL] // Optional: for polygon deployment
PRIVATE_KEY=[DEPLOYER_PRIVATE_KEY]
PRIVATE_KEY_2=[ANOTHER_ACCOUNT_PRIVATE_KEY] // Optional
REPORT_GAS=true
ETHERSCAN_API_KEY=[YOUR_ETHERSCAN_API_KEY]
VERIFY_CONTRACT=false
COINMARKETCAP_API_KEY=[YOUR_COINMARKETCAP_API_KEY] // Optional: only needed by hardhat-gas-reporter.
PINATA_API_KEY=[YOUR_PINATA_API_KEY]
PINATA_API_SECRET=[YOUR_PINATA_API_SECRET]
UPLOAD_TO_PINATA=true

Deploy

  1. npm install or yarn install
  2. Deploy to localhost yarn hardhat deploy
  3. Deploy to ethereum testnet yarn hardhat deploy --network goerli
  4. Deploy to polygon testnet yarn hardhat deploy --network muambai

Interact

Since the contracts are verified by default using etherscan you can just interact with your contract on the etherscan UI, i.e https://etherscan.io/token/0x271682DEB8C4E0901D1a1550aD2e64D568E69909#readContract

Otherwise, we provide hardhat tasks to interact with LoopNFT:

  1. After deployed, random numbers must be fulfilled: yarn hardhat initializeRandoms --ca {contract_address} --network {goerli|localhost}
  2. Check random values after 6 confirmations: yarn hardhat getRandomValues --ca {contract_address} --id 0 --network {goerli|localhost}
  3. Then, new items can be minted: yarn hardhat mintLoopNFT --ca {contract_address} --network {goerli|localhost}
  4. Check tokenCounter by running; yarn hardhat getTokenCounter --ca {contract_address} --network {goerli|localhost}

Test

Tests are separated between unit and staging tests.

unit tests are for testing LoopNFT public API meanwhile staging tests are for running on testnet with:

  • an already deployed LoopNFT contract.
  • an already configured Chainlink VRF subscription

Commands:

  • Unit: npx hardhat test
  • Staging: npx hardhat test --network goerli
  • Coverage: npx hardhat coverage

OpenSea

After deployed and minted you should find your items on OpeanSea: