Created and maintained with ❤️ by LoopStudio
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?
- Solidity 0.8.15
- Hardhat
- hardhat-deploy
- hardhat-ethers
- hardhat-etherscan
- Openzeppelin Contracts
- ERC721URIStorage
- Ownable
- Chainlink Contracts
- VRFConsumerBaseV2
- Typescript & Typechain
- Pinnata SDK (IPFS)
.
├── contracts/
├── deploy/
├── deployments
├── tasks
├── typechain-types
├── utils
├── test
├── hardhat.config.ts
├── helper-hardhat-config.ts
What does each folder represents?
- contracts: where the
LoopNFT.solis placed - deploy:
hardhat-deployscripts to deploy to localhost, using mock contracts, testnet and mainnet. The scripts also verifies contract source code toetherscanand publish the NFTmetadatatoIPFSusingpinata-sdk - deployment: information about old deploys and ABI, generated by
hardhat-deploy - tasks: hardhat tasks to interact with the
LoopNFT.solcontract 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
IPFSand verify sourcecode againstetherscan - test: unit and staging tests
- hardhat.config.ts: hardhat configuration file. Currenlty supports
ethereumandpolygonnetworks configuration. - helper-hardhat-config.ts: helper file containing information per network, like # of confirmations, Chainlink smart contract addresses and subscriptions.
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
mintfunction 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
minttransactions
We follow the following approach to bootstrap 70 unique items and ensure that the mint was randomic:
- The contract constructor receives
characterUris, an array of NFT metadata following the ERC721 Metadata Standards. Metadata was previously stored onIPFSusingpinata sdkto ensure its completely descentralized. - We used Chainlink VRF for random numbers generation, thus
LoopNFTinherits fromVRFConsumerBaseV2 - 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 - Since we didnt want to charge the minter with extra fees of random requesting, an
onlyOnwer initializeRandomsfunction 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. - Each time
mintis called we take the latest random number and we divide it by the modulus of thecharacterUrisaray size to get the current charactertokenUrito mint. We associate thattokenUriwith thetokenIdthat is being minted usingERC721URIStorage_setTokenUri(tokenId, tokenUri)method. Since both the random number and the tokenUri were used, we removed them from the contract storage.
- Copy
.env.exampleto.env - 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
npm installoryarn install- Deploy to localhost
yarn hardhat deploy - Deploy to ethereum testnet
yarn hardhat deploy --network goerli - Deploy to polygon testnet
yarn hardhat deploy --network muambai
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:
- After deployed, random numbers must be fulfilled:
yarn hardhat initializeRandoms --ca {contract_address} --network {goerli|localhost} - Check random values after 6 confirmations:
yarn hardhat getRandomValues --ca {contract_address} --id 0 --network {goerli|localhost} - Then, new items can be minted:
yarn hardhat mintLoopNFT --ca {contract_address} --network {goerli|localhost} - Check
tokenCounterby running;yarn hardhat getTokenCounter --ca {contract_address} --network {goerli|localhost}
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
After deployed and minted you should find your items on OpeanSea: