├── .env.dist ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .solhint.json ├── .solhintignore ├── LICENSE ├── README.md ├── assets.dist └── contract │ ├── image.png │ └── placeholder.png ├── collection-public-sale.png ├── collection.config.js.dist ├── contracts └── ERC721Collection.sol ├── deploy ├── 01_upload_collection_metadata.js ├── 02_upload_placeholder_metadata.js └── 03_deploy_collection_contract.js ├── hardhat.config.js ├── package-lock.json ├── package.json └── staking-screenshot.png /.env.dist: -------------------------------------------------------------------------------- 1 | # To deploy on Ethereum using Infura 2 | INFURA_PROJECT_ID=xxxxxxxxxxxxxxxxxxxxx 3 | 4 | # Gas price to accept when sending transactions (in wei) 5 | GAS_PRICE=20000000000 6 | 7 | # Initial owner of smart contracts 8 | DEPLOYER_PRIVATE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9 | 10 | # To verify contracts on EtherScan 11 | ETHERSCAN_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxx 12 | 13 | # To calculate gas prices 14 | COIN_MARKET_CAP_API_KEY=xxxxxx-xxxxxx-xxxxx-xxxx 15 | 16 | # Pinata API and Secret Key to upload metadata and images 17 | PINATA_API_KEY=xxxxxxxxxxxxxxxxxxxx 18 | PINATA_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 19 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | .terraform 5 | terraform.tfstate 6 | terraform.tfstate* 7 | allure-results 8 | allure-report 9 | _ignore.* 10 | test/integration/* 11 | test/utils/* 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 2019 4 | }, 5 | "plugins": [ 6 | "simple-import-sort", 7 | "import" 8 | ], 9 | "extends": [ 10 | "prettier", 11 | "plugin:prettier/recommended" 12 | ], 13 | "root": true, 14 | "env": { 15 | "browser": false, 16 | "es2021": true, 17 | "mocha": true, 18 | "node": true 19 | }, 20 | "rules": { 21 | "simple-import-sort/imports": "error", 22 | "import/first": "error", 23 | "import/newline-after-import": "error", 24 | "import/no-duplicates": "error" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Scaffold files 4 | .env 5 | collection.config.js 6 | blueprint.config.json 7 | dist 8 | assets 9 | contracts\/collections\/* 10 | !contracts\/collections\/SampleCreatures.sol 11 | 12 | # Hardhat files 13 | cache 14 | artifacts 15 | deployments 16 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | artifacts 3 | cache 4 | coverage* 5 | gasReporterOutput.json 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": ["error", "^0.8.3"], 5 | "func-visibility": ["warn", { "ignoreConstructors": true }] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Flair 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFT Collection Scaffold 2 | [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Production-ready%20%23NFT%20collection%20scaffold%20that%20will%20save%20you%20weeks%20of%20development%20work%20if%20launching%20an%20NFT%20project](https://github.com/0xflair/nft-collection-scaffold)) 3 | 4 | Production-ready code for a rarity-based PFP (a.k.a 10k avatar) collection on Ethereum, Polygon (Matic), Binance Chain and any other EVM-compatible chain. 5 | 6 | This repository is uses [OpenZeppelin](https://www.openzeppelin.com) and [Flair](https://flair.finance/) contracts. 7 | 8 | #### Current Features 9 | * Ability to **reveal NFT metadata** after the mint, by uploading to IPFS. 10 | * Deploy **ready-made smart contracts** to mainnet with a simple command. 11 | * Ability to run pre-sales by allow-listing addresses. 12 | * Controllable pre-sale, public sale and direct purchase toggles. 13 | * Customizable maximum mints per transaction. 14 | * Customizable maximum pre-sale mints per address. 15 | * No gas fee for when collectors want to "list" NFTs from this collection. 16 | * Supports royalty for secondary sales on OpenSea, LooksRare, Rarible, and EIP2981 etc. 17 | 18 | #### Stack 19 | * Solidity 0.8.x ([OpenZeppelin](https://docs.openzeppelin.com/contracts/4.x/)) 20 | * Hardhat 21 | * TypeScript 22 | 23 | # :fire: Quick Start 24 | 25 | If you want to create a new NFT Collection under 5 minutes, you can use Flair dashboard, which uses open-source smart contracts to deploy the contract for you, with your full ownership. 26 | 27 | * Tutorial: [https://docs.flair.finance/tutorials/nft-sale](https://docs.flair.finance/tutorials/nft-sale) 28 | * Dashboard: [https://app.flair.finance](https://app.flair.finance) 29 | 30 | [![](./collection-public-sale.png)](https://flair.finance) 31 | 32 | # 🧗 Deploying the Hard Way 33 | 34 | #### 1. Clone this repo 35 | 36 | ```sh 37 | git clone https://github.com/0xflair/nft-collection-scaffold.git 38 | ``` 39 | 40 | #### 2. Create env variables 41 | 42 | Copy the `.env.dist` file to a file named `.env`, and then edit it to fill in the details. Enter your Etherscan API key, your Infura Access Key, and the private key of the account which will send the deployment transaction. 43 | 44 | #### 3. Create collection configuration 45 | 46 | Copy `collection.config.js.dist` to a file named `collection.config.js`, then update the details based on your collection info, such as name, description, pre-reveal metadata and image. 47 | 48 | #### 4. Deploy the contract using Hardhat 49 | 50 | ```sh 51 | # On testnet 52 | npm run deploy:rinkeby 53 | 54 | # Or mainnet 55 | npm run deploy:mainnet 56 | ``` 57 | 58 | ## 😍 Are you looking for Staking? 59 | 60 | Flair has a tutorial to create a staking pool under 5 minutes, [check it out here](https://docs.flair.finance/tutorials/nft-staking)! 61 | 62 | [![](./staking-screenshot.png)](https://docs.flair.finance/tutorials/nft-staking) 63 | 64 | ## :rocket: WAGMI 65 | -------------------------------------------------------------------------------- /assets.dist/contract/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xflair/nft-collection-scaffold/bd0b6be54d37f73795403b598fa05d80d3316452/assets.dist/contract/image.png -------------------------------------------------------------------------------- /assets.dist/contract/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xflair/nft-collection-scaffold/bd0b6be54d37f73795403b598fa05d80d3316452/assets.dist/contract/placeholder.png -------------------------------------------------------------------------------- /collection-public-sale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xflair/nft-collection-scaffold/bd0b6be54d37f73795403b598fa05d80d3316452/collection-public-sale.png -------------------------------------------------------------------------------- /collection.config.js.dist: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const web3 = require('web3'); 4 | const hre = require('hardhat'); 5 | const { loadContract } = require('@0xflair/contracts-registry'); 6 | const { FLAIR_OPENSEA_ADDRESSES } = require('@0xflair/react-nft-collections'); 7 | 8 | const forwarderArtifact = loadContract( 9 | 'common/meta-transactions/UnorderedForwarder', 10 | LATEST_VERSION, 11 | ); 12 | 13 | module.exports = { 14 | // Name of the collection shown in EtherScan and OpenSea 15 | name: 'Flair Angels', 16 | 17 | // Description of collection stored in contract-level metadata shown in OpenSea 18 | description: 19 | 'Angels will help bring Flair.Finance to life and make our decentralized world a safer place.', 20 | 21 | // Token tracker symbol shown in EtherScan 22 | symbol: 'ANGEL', 23 | 24 | // Price of minting 25 | preSalePrice: web3.utils.toWei('0.06'), // 0.06 ETH 26 | publicSalePrice: web3.utils.toWei('0.08'), // 0.08 ETH 27 | 28 | // Total number of tokens in your collection 29 | maxSupply: 8000, 30 | 31 | // Number of pre-sale mints possible for each allow-listed address 32 | preSaleMaxMintPerWallet: 2, 33 | 34 | // Number of mints possible on each transaction 35 | publicSaleMaxMintPerTx: 10, 36 | 37 | // Primary image of collection stored in contract-level metadata shown in OpenSea 38 | collectionImage: 'assets/contract/image.png', 39 | 40 | // Image (or gif) for NFT placeholders before the reveal 41 | unrevealedPlaceholder: 'assets/contract/placeholder.png', 42 | 43 | // Website of collection stored in contract-level metadata shown in OpenSea 44 | externalLink: 'https://flair.finance', 45 | 46 | // Royalty fee for secondary sales (on Rarible, OpenSea, LooksRare) 47 | defaultRoyaltyBps: 500, // Indicates a 5% seller fee. 48 | 49 | // Receiver of the royalty fees 50 | defaultRoyaltyAddress: '0xc997c206650cc62248a8514e2f60cbe29a24800f', 51 | 52 | // OpenSea registry address to avoid additional approval when collectors want to list on OS 53 | openSeaProxyRegistryAddress: FLAIR_OPENSEA_ADDRESSES[ 54 | hre.network.config.chainId 55 | ] 56 | ? FLAIR_OPENSEA_ADDRESSES[hre.network.config.chainId].registryAddress 57 | : '0x0', 58 | openSeaExchangeAddress: FLAIR_OPENSEA_ADDRESSES[hre.network.config.chainId] 59 | ? FLAIR_OPENSEA_ADDRESSES[hre.network.config.chainId].exchangeAddress 60 | : '0x0', 61 | 62 | // Address of the trusted forwarder contract when using meta-transactions (i.e. trustless minting from a backend API) 63 | trustedForwarder: forwarderArtifact.address[hre.network.config.chainId] 64 | ? forwarderArtifact.address[hre.network.config.chainId] 65 | : '0x0', 66 | 67 | // Pinata API and Secret Keys to upload and pin metadata for NFTs and contract (you must edit .env file) 68 | pinataApiKey: process.env.PINATA_API_KEY, 69 | pinataSecretKey: process.env.PINATA_SECRET_KEY, 70 | }; 71 | -------------------------------------------------------------------------------- /contracts/ERC721Collection.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.9; 4 | 5 | import "@0xflair/evm-contracts/collections/ERC721/presets/ERC721FullFeaturedCollection.sol"; 6 | 7 | contract ERC721Collection is ERC721FullFeaturedCollection { 8 | constructor(Config memory config) ERC721FullFeaturedCollection(config) {} 9 | } 10 | -------------------------------------------------------------------------------- /deploy/01_upload_collection_metadata.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const pinataSDK = require('@pinata/sdk'); 4 | 5 | const collectionConfig = require('../collection.config'); 6 | 7 | const distDirectory = path.resolve(__dirname, '../dist'); 8 | 9 | fs.mkdirSync(distDirectory, { recursive: true }); 10 | 11 | const pinata = pinataSDK( 12 | collectionConfig.pinataApiKey, 13 | collectionConfig.pinataSecretKey, 14 | ); 15 | 16 | module.exports = async ({ getNamedAccounts, deployments }) => { 17 | console.log(' - Uploading collection image to IPFS...'); 18 | const { IpfsHash: imageHash } = await pinata.pinFromFS( 19 | path.resolve(collectionConfig.collectionImage), 20 | ); 21 | console.log(` - IPFS Hash: ${imageHash}`); 22 | 23 | const contractMetadata = { 24 | name: collectionConfig.name, 25 | description: collectionConfig.description, 26 | image: `ipfs://${imageHash}`, 27 | external_link: collectionConfig.externalLink, 28 | seller_fee_basis_points: collectionConfig.royaltyFee, 29 | fee_recipient: collectionConfig.royaltyFeeRecipient, 30 | }; 31 | 32 | const { IpfsHash: metadataHash } = await pinata.pinJSONToIPFS( 33 | contractMetadata, 34 | ); 35 | 36 | fs.writeFileSync(`${distDirectory}/.contractURI`, `ipfs://${metadataHash}`); 37 | }; 38 | 39 | module.exports.tags = ['metadata']; 40 | -------------------------------------------------------------------------------- /deploy/02_upload_placeholder_metadata.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const pinataSDK = require('@pinata/sdk'); 4 | 5 | const collectionConfig = require('../collection.config'); 6 | 7 | const distDirectory = path.resolve(__dirname, '../dist'); 8 | 9 | const pinata = pinataSDK( 10 | collectionConfig.pinataApiKey, 11 | collectionConfig.pinataSecretKey, 12 | ); 13 | 14 | module.exports = async ({ getNamedAccounts, deployments }) => { 15 | console.log(' - Uploading placeholder image to IPFS...'); 16 | const { IpfsHash: imageHash } = await pinata.pinFromFS( 17 | path.resolve(collectionConfig.unrevealedPlaceholder), 18 | ); 19 | console.log(` - IPFS Hash: ${imageHash}`); 20 | 21 | const contractMetadata = { 22 | name: collectionConfig.name, 23 | image: `ipfs://${imageHash}`, 24 | }; 25 | 26 | const { IpfsHash: metadataHash } = await pinata.pinJSONToIPFS( 27 | contractMetadata, 28 | ); 29 | 30 | fs.writeFileSync( 31 | `${distDirectory}/.placeholderURI`, 32 | `ipfs://${metadataHash}`, 33 | ); 34 | }; 35 | 36 | module.exports.tags = ['placeholder']; 37 | -------------------------------------------------------------------------------- /deploy/03_deploy_collection_contract.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const collectionConfig = require('../collection.config'); 5 | const { utils } = require('ethers'); 6 | 7 | const distDirectory = path.resolve(__dirname, '../dist'); 8 | 9 | module.exports = async ({ getNamedAccounts, deployments }) => { 10 | const { deployer } = await getNamedAccounts(); 11 | 12 | const contractURI = fs 13 | .readFileSync(`${distDirectory}/.contractURI`) 14 | .toString(); 15 | const placeholderURI = fs 16 | .readFileSync(`${distDirectory}/.placeholderURI`) 17 | .toString(); 18 | 19 | await deployments.deploy('ERC721Collection', { 20 | from: deployer, 21 | args: [ 22 | { 23 | name: collectionConfig.name, 24 | symbol: collectionConfig.symbol, 25 | maxSupply: collectionConfig.maxSupply, 26 | 27 | contractURI: contractURI, 28 | placeholderURI: placeholderURI, 29 | 30 | preSalePrice: collectionConfig.preSalePrice, 31 | preSaleMaxMintPerWallet: collectionConfig.preSaleMaxMintPerWallet, 32 | 33 | publicSalePrice: collectionConfig.publicSalePrice, 34 | publicSaleMaxMintPerTx: collectionConfig.publicSaleMaxMintPerTx, 35 | 36 | defaultRoyaltyAddress: collectionConfig.defaultRoyaltyAddress, 37 | defaultRoyaltyBps: collectionConfig.defaultRoyaltyBps, 38 | 39 | openSeaProxyRegistryAddress: 40 | collectionConfig.openSeaProxyRegistryAddress, 41 | openSeaExchangeAddress: collectionConfig.openSeaExchangeAddress, 42 | trustedForwarder: collectionConfig.trustedForwarder, 43 | }, 44 | ], 45 | log: true, 46 | estimateGasExtra: 1000000, 47 | }); 48 | }; 49 | 50 | module.exports.tags = ['collection']; 51 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | require('hardhat-deploy'); 4 | require('@nomiclabs/hardhat-waffle'); 5 | require('@nomiclabs/hardhat-ethers'); 6 | require('@nomiclabs/hardhat-etherscan'); 7 | require('solidity-coverage'); 8 | require('hardhat-contract-sizer'); 9 | require('hardhat-gas-reporter'); 10 | 11 | // You need to export an object to set up your config 12 | // Go to https://hardhat.org/config/ to learn more 13 | 14 | const { 15 | INFURA_PROJECT_ID, 16 | DEPLOYER_PRIVATE_KEY, 17 | ETHERSCAN_API_KEY, 18 | GAS_PRICE, 19 | COIN_MARKET_CAP_API_KEY, 20 | } = process.env; 21 | 22 | /** 23 | * @type import('hardhat/config').HardhatUserConfig 24 | */ 25 | module.exports = { 26 | solidity: { 27 | version: '0.8.9', 28 | settings: { 29 | optimizer: { 30 | enabled: true, 31 | runs: 10, 32 | }, 33 | }, 34 | }, 35 | defaultNetwork: 'hardhat', 36 | networks: { 37 | hardhat: { 38 | gas: 12000000, 39 | blockGasLimit: 0x1fffffffffffff, 40 | allowUnlimitedContractSize: true, 41 | timeout: 1800000, 42 | }, 43 | localhost: { 44 | url: `http://127.0.0.1:8545`, 45 | network_id: '*', 46 | gas: 12000000, 47 | blockGasLimit: 0x1fffffffffffff, 48 | allowUnlimitedContractSize: true, 49 | timeout: 1800000, 50 | }, 51 | rinkeby: { 52 | chainId: 4, 53 | url: `https://rinkeby.infura.io/v3/${INFURA_PROJECT_ID}`, 54 | network_id: '*', 55 | gasPrice: GAS_PRICE && parseInt(GAS_PRICE), 56 | ...(DEPLOYER_PRIVATE_KEY 57 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 58 | : {}), 59 | }, 60 | bscTestnet: { 61 | chainId: 97, 62 | url: 'https://data-seed-prebsc-1-s1.binance.org:8545', 63 | gasPrice: GAS_PRICE && parseInt(GAS_PRICE), 64 | ...(DEPLOYER_PRIVATE_KEY 65 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 66 | : {}), 67 | }, 68 | mainnet: { 69 | chainId: 1, 70 | url: `https://mainnet.infura.io/v3/${INFURA_PROJECT_ID}`, 71 | network_id: '*', 72 | gasPrice: GAS_PRICE && parseInt(GAS_PRICE), 73 | ...(DEPLOYER_PRIVATE_KEY 74 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 75 | : {}), 76 | }, 77 | matic: { 78 | chainId: 137, 79 | url: `https://polygon-mainnet.infura.io/v3/${INFURA_PROJECT_ID}`, 80 | ...(DEPLOYER_PRIVATE_KEY 81 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 82 | : {}), 83 | }, 84 | mumbai: { 85 | chainId: 80001, 86 | // url: `https://polygon-mumbai.infura.io/v3/${INFURA_PROJECT_ID}`, 87 | url: `https://matic-mumbai.chainstacklabs.com/`, 88 | ...(DEPLOYER_PRIVATE_KEY 89 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 90 | : {}), 91 | }, 92 | arbitrumOne: { 93 | chainId: 42161, 94 | url: 'https://arb1.arbitrum.io/rpc', 95 | ...(DEPLOYER_PRIVATE_KEY 96 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 97 | : {}), 98 | }, 99 | arbitrumTestnet: { 100 | chainId: 421611, 101 | url: 'https://rinkeby.arbitrum.io/rpc', 102 | ...(DEPLOYER_PRIVATE_KEY 103 | ? { accounts: [`0x${DEPLOYER_PRIVATE_KEY}`] } 104 | : {}), 105 | }, 106 | }, 107 | etherscan: { 108 | // Your API key for Etherscan 109 | // Obtain one at https://etherscan.io/ 110 | apiKey: ETHERSCAN_API_KEY, 111 | }, 112 | contractSizer: { 113 | alphaSort: false, 114 | runOnCompile: false, 115 | disambiguatePaths: false, 116 | }, 117 | namedAccounts: { 118 | deployer: { 119 | default: 0, 120 | }, 121 | }, 122 | gasReporter: { 123 | coinmarketcap: COIN_MARKET_CAP_API_KEY, 124 | currency: 'USD', 125 | enabled: true, 126 | }, 127 | }; 128 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-collection-scaffold", 3 | "version": "1.0.0", 4 | "description": "Production-ready boilerplate code for a rarity-based NFT collection on Ethereum, Polygon, Optimism, Arbitrum and Binance Smart Chain", 5 | "main": "index.js", 6 | "repository": "git@github.com:0xflair/nft-collection-scaffold.git", 7 | "author": "Flair.Finance", 8 | "license": "MIT", 9 | "private": false, 10 | "scripts": { 11 | "lint": "eslint \"**/*.{js,ts}\" && solhint src/**/*.sol", 12 | "lint:fix": "eslint --fix \"**/*.{js,ts}\" && solhint --fix src/**/*.sol", 13 | "format": "prettier --check \"**/*.{ts,js,sol}\"", 14 | "format:fix": "prettier --write \"**/*.{ts,js,sol}\"", 15 | "test": "npx hardhat test --network hardhat", 16 | "build": "npm run clean && npm run compile", 17 | "deploy:mainnet": "npx hardhat deploy --network mainnet && npx hardhat etherscan-verify --network mainnet", 18 | "deploy:rinkeby": "npx hardhat deploy --network rinkeby && npx hardhat etherscan-verify --network rinkeby", 19 | "deploy:mumbai": "npx hardhat deploy --network mumbai && npx hardhat etherscan-verify --network mumbai", 20 | "deploy:matic": "npx hardhat deploy --network matic && npx hardhat etherscan-verify --network matic", 21 | "clean": "npx hardhat clean", 22 | "compile": "npx hardhat compile", 23 | "coverage": "npm run build && npx hardhat coverage --temp artifacts --network hardhat" 24 | }, 25 | "devDependencies": { 26 | "@0xflair/contracts-registry": "^0.56.3", 27 | "@0xflair/evm-contracts": "^1.8.12", 28 | "@0xflair/meta-transactions": "^0.56.3", 29 | "@0xflair/react-nft-collections": "^0.56.3", 30 | "@manifoldxyz/royalty-registry-solidity": "^1.0.9", 31 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13", 32 | "@nomiclabs/hardhat-etherscan": "^2.1.8", 33 | "@nomiclabs/hardhat-waffle": "^2.0.0", 34 | "@openzeppelin/contracts": "^4.3.2", 35 | "@pinata/sdk": "^1.1.23", 36 | "@rarible/royalties": "^0.7.2", 37 | "@types/chai": "^4.2.21", 38 | "@types/mocha": "^9.0.0", 39 | "@types/node": "^16.9.1", 40 | "canvas": "^2.8.0", 41 | "chai": "^4.2.0", 42 | "csv-parse": "^4.16.3", 43 | "dotenv": "^10.0.0", 44 | "eslint": "^7.29.0", 45 | "eslint-config-prettier": "^8.3.0", 46 | "eslint-config-standard": "^16.0.3", 47 | "eslint-plugin-import": "^2.23.4", 48 | "eslint-plugin-jest": "^23.1.1", 49 | "eslint-plugin-node": "^11.1.0", 50 | "eslint-plugin-prettier": "^3.4.0", 51 | "eslint-plugin-promise": "^5.1.0", 52 | "eslint-plugin-simple-import-sort": "^7.0.0", 53 | "ethereum-waffle": "^3.0.0", 54 | "ethers": "^5.6.8", 55 | "hardhat": "^2.6.4", 56 | "hardhat-contract-sizer": "^2.1.0", 57 | "hardhat-deploy": "^0.9.1", 58 | "hardhat-gas-reporter": "^1.0.4", 59 | "lodash": "^4.17.21", 60 | "merge-images": "^2.0.0", 61 | "merge-img": "^2.1.3", 62 | "prettier": "^2.3.2", 63 | "prettier-plugin-solidity": "^1.0.0-beta.13", 64 | "seedrandom": "^3.0.5", 65 | "solhint": "^3.3.6", 66 | "solidity-coverage": "^0.7.16" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /staking-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xflair/nft-collection-scaffold/bd0b6be54d37f73795403b598fa05d80d3316452/staking-screenshot.png --------------------------------------------------------------------------------