├── .env.example ├── .eslintrc.json ├── .github ├── renovate.json └── workflows │ └── ci.yml ├── .gitignore ├── .gitpod.yml ├── .husky └── pre-commit ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── contracts └── Greeter.sol ├── hardhat.config.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── playwright.config.ts ├── postcss.config.js ├── public ├── favicon.ico └── vercel.svg ├── scripts └── sample-script.js ├── src ├── components │ └── Header.tsx ├── pages │ ├── _app.tsx │ ├── api │ │ └── hello.ts │ └── index.tsx └── utils │ └── helpers │ └── shortenAddress.ts ├── styles └── globals.css ├── tailwind.config.js ├── test ├── pages │ └── index.spec.ts └── sample-test.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY="" 2 | REPORT_GAS=true 3 | TS_NODE_FILES=true 4 | POLYGON_RPC_URL="" 5 | ETHERSCAN_API_KEY="" -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 2021, 5 | "sourceType": "module" 6 | }, 7 | "plugins": [ 8 | "@typescript-eslint", 9 | "unused-imports", 10 | "simple-import-sort", 11 | "prettier" 12 | ], 13 | "extends": ["next", "next/core-web-vitals"], 14 | "rules": { 15 | "prettier/prettier": ["error"], 16 | "react/prop-types": "off", 17 | "react/no-unescaped-entities": "off", 18 | "no-unused-vars": "warn", 19 | "unused-imports/no-unused-imports": "error", 20 | "@next/next/no-html-link-for-pages": ["off", "/api/"], 21 | "@typescript-eslint/ban-ts-comment": "warn", 22 | "@typescript-eslint/explicit-module-boundary-types": "off", 23 | "@next/next/no-img-element": "off", 24 | "simple-import-sort/imports": "error", 25 | "simple-import-sort/exports": "error", 26 | "jsx-a11y/role-supports-aria-props": "off" 27 | } 28 | } -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "timezone": "Asia/Calcutta", 4 | "schedule": [ 5 | "after 10pm every weekday", 6 | "before 5am every weekday", 7 | "every weekend" 8 | ], 9 | "packageRules": [ 10 | { 11 | "matchUpdateTypes": ["major", "minor", "patch", "pin", "digest"], 12 | "automerge": true 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | node-version: [14.x, 16.x] 12 | 13 | steps: 14 | - name: Use Node.js ${{ matrix.node-version }} 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: ${{ matrix.node-version }} 18 | 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | 22 | - name: Get yarn cache directory path 23 | id: yarn-cache-dir 24 | run: echo "::set-output name=dir::$(yarn cache dir)" 25 | 26 | # - name: Restore yarn cache 27 | # uses: actions/cache@v3 28 | # id: yarn-cache 29 | # with: 30 | # path: ${{ steps.yarn-cache-dir.outputs.dir }} 31 | # key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 32 | # restore-keys: | 33 | # ${{ runner.os }}-yarn- 34 | 35 | - name: Install Yarn Dependencies 36 | run: yarn install --frozen-lockfile --silent 37 | 38 | - name: Lint 39 | run: yarn lint 40 | 41 | - name: Prettier 42 | run: yarn prettier:check 43 | 44 | - name: Typecheck 45 | run: yarn typecheck 46 | 47 | - name: Double check 48 | run: npx playwright install && yarn test 49 | 50 | - name: Build 51 | env: 52 | COOKIE_SECRET: ${{ secrets.COOKIE_SECRET }} 53 | DATABASE_URL: ${{ secrets.DATABASE_URL }} 54 | run: yarn build 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | 39 | node_modules 40 | .env 41 | coverage 42 | coverage.json 43 | typechain 44 | 45 | #Hardhat files 46 | cache 47 | artifacts 48 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | tasks: 6 | - init: yarn install 7 | command: yarn run dev 8 | 9 | 10 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn typecheck -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "semi": false, 4 | "useTabs": false, 5 | "trailingComma": "none", 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "git.autofetch": true, 5 | "git.confirmSync": false, 6 | "git.enableSmartCommit": true, 7 | "editor.codeActionsOnSave": { 8 | "source.fixAll": true 9 | }, 10 | "[typescript]": { 11 | "editor.defaultFormatter": "esbenp.prettier-vscode" 12 | } 13 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![](https://cloudflare-ipfs.com/ipfs/QmX6BtuiAQmjLpbqzyBWxRieR4PxSquCzjuFd3vsr1fAGj) 3 | ###### image source([ethereum](https://ethereum.org/)) 4 | 5 | # Full stack DApp starter 6 | 7 | ## Stack 8 | - NextJs with Typescript 9 | - TailwindCSS 10 | - Wagmi & RainbowKit 🌈 11 | - Ethers 12 | - Hardhat 13 | - Playwright Test Suite 14 | - Github workflow and renovate bot 15 | - ESlint and Prettier 16 | - Path aliases 17 | - Notification Toaster and Helpers 18 | - Gas Reporter 19 | - and more 20 | 21 | ![image](https://user-images.githubusercontent.com/29498872/167078019-67528538-b39c-42f4-a326-871deb691f39.png) 22 | 23 | ## Use the template 24 | 25 | You can either clone the repository or use this template to kick-start your project. 26 | ``` 27 | npx create-next-app -e https://github.com/sasicodes/next-eth 28 | ``` 29 | 30 | Install all dependencies, 31 | 32 | ``` 33 | yarn install 34 | ``` 35 | 36 | and run the application using, 37 | 38 | ``` 39 | yarn dev 40 | ``` 41 | 42 | Also try running some of the following tasks: 43 | 44 | ```shell 45 | npx hardhat accounts 46 | npx hardhat compile 47 | npx hardhat clean 48 | npx hardhat test 49 | npx hardhat node 50 | node scripts/sample-script.js 51 | npx hardhat help 52 | ``` 53 | -------------------------------------------------------------------------------- /contracts/Greeter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | contract Greeter { 7 | string private greeting; 8 | 9 | constructor(string memory _greeting) { 10 | console.log("Deploying a Greeter with greeting:", _greeting); 11 | greeting = _greeting; 12 | } 13 | 14 | function greet() public view returns (string memory) { 15 | return greeting; 16 | } 17 | 18 | function setGreeting(string memory _greeting) public { 19 | console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); 20 | greeting = _greeting; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import '@nomiclabs/hardhat-waffle' 2 | import 'hardhat-gas-reporter' 3 | import '@nomiclabs/hardhat-etherscan' 4 | import '@nomiclabs/hardhat-waffle' 5 | 6 | import * as dotenv from 'dotenv' 7 | import { HardhatUserConfig, task } from 'hardhat/config' 8 | dotenv.config() 9 | 10 | task('accounts', 'Prints the list of accounts', async (taskArgs, hre) => { 11 | const accounts = await hre.ethers.getSigners() 12 | 13 | for (const account of accounts) { 14 | console.log(account.address) 15 | } 16 | }) 17 | 18 | /** 19 | * @type import('hardhat/config').HardhatUserConfig 20 | */ 21 | const config: HardhatUserConfig = { 22 | solidity: '0.8.9', 23 | networks: { 24 | polygon: { 25 | url: process.env.POLYGON_RPC_URL || '', 26 | accounts: 27 | process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [] 28 | } 29 | }, 30 | gasReporter: { 31 | enabled: process.env.REPORT_GAS !== undefined, 32 | currency: 'USD' 33 | }, 34 | etherscan: { 35 | apiKey: process.env.ETHERSCAN_API_KEY 36 | } 37 | } 38 | 39 | export default config 40 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-eth", 3 | "description": "NextJs and Hardhat setup to kickstart your decentralized application.", 4 | "version": "1.0.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start", 10 | "test": "playwright test", 11 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx", 12 | "typecheck": "tsc --noEmit", 13 | "prepare": "husky install", 14 | "prettier:check": "prettier --check \"./src/**/*.{ts,tsx}\"" 15 | }, 16 | "dependencies": { 17 | "@rainbow-me/rainbowkit": "0.10.0", 18 | "ethers": "5.7.2", 19 | "next": "13.1.6", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0", 22 | "react-hot-toast": "2.4.0", 23 | "wagmi": "0.11.5" 24 | }, 25 | "devDependencies": { 26 | "@nomiclabs/hardhat-ethers": "2.2.2", 27 | "@nomiclabs/hardhat-etherscan": "3.1.6", 28 | "@nomiclabs/hardhat-waffle": "2.0.4", 29 | "@playwright/test": "1.30.0", 30 | "@types/mocha": "10.0.1", 31 | "@types/node": "18.13.0", 32 | "@types/react": "18.0.28", 33 | "@typescript-eslint/eslint-plugin": "5.52.0", 34 | "autoprefixer": "10.4.13", 35 | "dotenv": "16.0.3", 36 | "eslint": "8.34.0", 37 | "eslint-config-next": "13.1.6", 38 | "eslint-plugin-prettier": "4.2.1", 39 | "eslint-plugin-simple-import-sort": "10.0.0", 40 | "eslint-plugin-unused-imports": "2.0.0", 41 | "ethereum-waffle": "4.0.10", 42 | "hardhat": "2.12.7", 43 | "hardhat-gas-reporter": "1.0.9", 44 | "husky": "8.0.3", 45 | "postcss": "8.4.21", 46 | "tailwindcss": "3.2.6", 47 | "ts-node": "10.9.1", 48 | "typescript": "4.9.5" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { PlaywrightTestConfig } from '@playwright/test' 2 | 3 | const config: PlaywrightTestConfig = { 4 | testDir: 'test', 5 | webServer: { 6 | command: 'yarn dev', 7 | port: 3000, 8 | timeout: 500 * 1000, 9 | reuseExistingServer: !process.env.CI 10 | }, 11 | use: { baseURL: 'http://localhost:3000' } 12 | } 13 | 14 | export default config 15 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sasicodes/next-eth/fc8b56bbf2a15358a1f149bbee3a69e688292c4b/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/sample-script.js: -------------------------------------------------------------------------------- 1 | const hre = require('hardhat') 2 | 3 | async function main() { 4 | const Greeter = await hre.ethers.getContractFactory('Greeter') 5 | const greeter = await Greeter.deploy('Hello, Hardhat!') 6 | 7 | await greeter.deployed() 8 | 9 | console.log('Greeter deployed to:', greeter.address) 10 | } 11 | 12 | main() 13 | .then(() => process.exit(0)) 14 | .catch((error) => { 15 | console.error(error) 16 | process.exit(1) 17 | }) 18 | -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { ConnectButton } from '@rainbow-me/rainbowkit' 2 | import Link from 'next/link' 3 | 4 | const Header: React.FC = () => { 5 | return ( 6 |
7 |
8 | 9 | 10 | Your Metaverse 11 | 12 | 13 |
14 |
15 | 16 |
17 |
18 | ) 19 | } 20 | 21 | export default Header 22 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../../styles/globals.css' 2 | import '@rainbow-me/rainbowkit/styles.css' 3 | 4 | import { getDefaultWallets, RainbowKitProvider } from '@rainbow-me/rainbowkit' 5 | import type { AppProps } from 'next/app' 6 | import Head from 'next/head' 7 | import { Toaster } from 'react-hot-toast' 8 | import { configureChains, createClient, WagmiConfig } from 'wagmi' 9 | import { polygon } from 'wagmi/chains' 10 | import { publicProvider } from 'wagmi/providers/public' 11 | 12 | const { chains, provider } = configureChains([polygon], [publicProvider()]) 13 | 14 | const { connectors } = getDefaultWallets({ 15 | appName: 'My Web3 App', 16 | chains 17 | }) 18 | 19 | const wagmiClient = createClient({ 20 | autoConnect: true, 21 | connectors, 22 | provider 23 | }) 24 | 25 | function MyApp({ Component, pageProps }: AppProps) { 26 | return ( 27 | 28 | 29 | 30 | Welcome to Web3! 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default MyApp 40 | -------------------------------------------------------------------------------- /src/pages/api/hello.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next' 2 | 3 | type Data = { 4 | name: string 5 | } 6 | 7 | export default function handler( 8 | req: NextApiRequest, 9 | res: NextApiResponse 10 | ) { 11 | res.status(200).json({ name: 'Welcome to Web3!' }) 12 | } 13 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from '@components/Header' 2 | 3 | export default function Home() { 4 | return ( 5 |
6 |
7 |
8 |
9 | Open `src/pages/index.tsx`{' '} 10 | and start building 11 |
12 |
13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/helpers/shortenAddress.ts: -------------------------------------------------------------------------------- 1 | const shortenAddress = (address: string, chars = 4) => { 2 | return `${address.substring(0, chars + 2)}...${address.substring(42 - chars)}` 3 | } 4 | export default shortenAddress 5 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | './src/pages/**/*.{js,ts,jsx,tsx}', 4 | './src/components/**/*.{js,ts,jsx,tsx}' 5 | ], 6 | theme: { 7 | extend: {} 8 | }, 9 | plugins: [] 10 | } 11 | -------------------------------------------------------------------------------- /test/pages/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from '@playwright/test' 2 | 3 | test('basic test', async ({ page }) => { 4 | await page.goto('/') 5 | await page.content() 6 | }) 7 | -------------------------------------------------------------------------------- /test/sample-test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from '@playwright/test' 2 | import { ethers } from 'hardhat' 3 | test('Greeter', async () => { 4 | const Greeter = await ethers.getContractFactory('Greeter') 5 | const greeter = await Greeter.deploy('Hello, world!') 6 | await greeter.deployed() 7 | 8 | expect(await greeter.greet()).toBe('Hello, world!') 9 | 10 | const setGreetingTx = await greeter.setGreeting('Hola, mundo!') 11 | 12 | // wait until the transaction is mined 13 | await setGreetingTx.wait() 14 | 15 | expect(await greeter.greet()).toBe('Hola, mundo!') 16 | }) 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noImplicitReturns": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "incremental": true, 12 | "esModuleInterop": true, 13 | "module": "commonjs", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "baseUrl": ".", 19 | "paths": { 20 | "@utils*": ["src/utils*"], 21 | "@components*": ["src/components*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "./scripts", "./test"], 25 | "exclude": ["node_modules", "dist", ".next", "next.config.js"], 26 | "files": ["./hardhat.config.ts"] 27 | } 28 | --------------------------------------------------------------------------------