A personal, production-ready template for building EVM-compatible decentralized applications (dApps) and protocols. Designed to be cloned, configured, and deployed quickly for hackathons, competitions, or personal projects.
Supports Arbitrum, Base, and Monad out of the box, with easy extensibility to any EVM-compatible chain.
- What Is This?
- Tech Stack
- Project Structure
- Prerequisites
- Getting Started
- Backend Guide
- Frontend Guide
- Environment Variables
- Available Scripts
- Deploying a Smart Contract
- Verifying a Smart Contract
- Running Tests
- Deploying the Frontend to Vercel
- Using This Template for a New Project
- Supported Networks
- Glossary
template-evm is a full-stack monorepo template built to accelerate the development of Ethereum-compatible dApps. It comes pre-configured with everything you need:
- A Hardhat environment for writing, compiling, testing, and deploying Solidity smart contracts.
- A Next.js frontend with wallet connection powered by Reown AppKit and Wagmi.
- Multi-chain support with a centralized network configuration.
- Automated deploy and verification scripts.
- Code quality tools (ESLint, Prettier, solhint) already configured.
The goal is simple: clone this repo, replace the example contract and frontend with your own, fill in your environment variables, and you are ready to ship.
| Tool | Purpose |
|---|---|
| Hardhat v2 | Ethereum development environment |
| Solidity ^0.8.28 | Smart contract language |
| OpenZeppelin Contracts | Secure, audited contract libraries |
| hardhat-deploy | Deterministic deployment system |
| hardhat-toolbox | Bundled Hardhat plugins |
| hardhat-network-helpers | Test utilities (time manipulation, snapshots) |
| hardhat-gas-reporter | Gas usage reports during tests |
| solidity-coverage | Code coverage for Solidity |
| TypeChain | TypeScript bindings for contracts |
| Mocha + Chai | Testing framework and assertions |
| solhint | Solidity linter |
| Prettier + prettier-plugin-solidity | Code formatter |
| dotenv | Environment variable management |
| ethers v6 | Ethereum library |
| Tool | Purpose |
|---|---|
| Next.js 15 | React framework with SSR and Vercel-native deployment |
| React 19 | UI library |
| TypeScript | Typed JavaScript |
| Wagmi | React hooks for Ethereum |
| Viem | TypeScript Ethereum client |
| Reown AppKit | Wallet connection modal and session management |
| TanStack Query | Async state management |
| Tailwind CSS v4 | Utility-first CSS framework |
| shadcn/ui | Accessible, composable UI components |
| ESLint | JavaScript/TypeScript linter |
| Prettier | Code formatter |
template-evm/
├── .gitignore # Global Git ignore rules
├── backend/
│ ├── contracts/
│ │ └── Counter.sol # Example contract (replace with yours)
│ ├── deploy/
│ │ └── 01_deploy_counter.ts # Deploy script (one file per contract)
│ ├── scripts/
│ │ └── deploy-and-verify.ts # Automated deploy + verify in one command
│ ├── test/
│ │ └── Counter.test.ts # Contract tests
│ ├── typechain-types/ # Auto-generated TypeScript typings (do not edit)
│ ├── .env # Your private environment variables (never commit)
│ ├── .prettierrc # Prettier config for Solidity
│ ├── .solhint.json # Solidity linting rules
│ ├── hardhat.config.ts # Hardhat + network + plugin configuration
│ ├── package.json
│ └── tsconfig.json
└── frontend/
├── app/
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout with AppKitProvider
│ └── page.tsx # Home page
├── components/ui/ # shadcn/ui components (add via CLI)
├── config/
│ ├── appkit.ts # Reown AppKit + Wagmi adapter config
│ └── chains.ts # Centralized network definitions
├── context/
│ └── AppKitProvider.tsx # Global wallet provider
├── hooks/ # Custom React hooks (add yours here)
├── lib/
│ └── utils.ts # shadcn/ui utility functions
├── public/ # Static assets
├── .env.local # Your private environment variables (never commit)
├── .prettierrc # Prettier config for TypeScript/React
├── components.json # shadcn/ui configuration
├── eslint.config.mjs # ESLint configuration
├── next.config.ts # Next.js configuration
├── package.json
└── tsconfig.json
Before using this template, make sure you have the following installed on your machine:
| Requirement | Minimum Version | Check |
|---|---|---|
| Node.js | v22 or higher | node --version |
| npm | v10 or higher | npm --version |
| Git | Any recent version | git --version |
You will also need:
- A crypto wallet (e.g., MetaMask) to interact with the dApp.
- A Reown Project ID (free) from cloud.reown.com for wallet connection.
- RPC URLs for the networks you want to deploy to (e.g., from Alchemy or Infura).
- Explorer API keys for contract verification (e.g., Arbiscan, Basescan).
git clone <your-repo-url>
cd template-evmcd backend
npm installcd ../frontend
npm installSee the Environment Variables section below.
The backend is a Hardhat project located in the backend/ folder.
cd backend
npm run compileThis compiles all .sol files in contracts/ and auto-generates TypeScript typings in typechain-types/.
npm run testnpm run coveragenpm run nodeThis starts a local Hardhat node at http://127.0.0.1:8545 with pre-funded test accounts. Keep this terminal open while developing locally.
npm run cleanThe frontend is a Next.js project located in the frontend/ folder.
cd frontend
npm run devOpen http://localhost:3000 in your browser.
npm run buildnpm run startnpx shadcn@latest add buttonReplace button with any component from the shadcn/ui catalog.
# Your wallet private key (without 0x prefix)
PRIVATE_KEY=
# RPC URLs — get these from Alchemy, Infura, or your provider
ARBITRUM_RPC_URL=
ARBITRUM_SEPOLIA_RPC_URL=
BASE_RPC_URL=
BASE_SEPOLIA_RPC_URL=
MONAD_TESTNET_RPC_URL=
# Explorer API keys for contract verification
ARBISCAN_API_KEY=
BASESCAN_API_KEY=
# Set to "true" to enable gas reporting during tests
REPORT_GAS=false
⚠️ Never commit your.envfile. It is already protected by.gitignore.
# Get your Project ID at https://cloud.reown.com
NEXT_PUBLIC_REOWN_PROJECT_ID=
⚠️ Never commit your.env.localfile. It is already protected by.gitignore.
| Command | Description |
|---|---|
npm run compile |
Compile all Solidity contracts |
npm run test |
Run all tests |
npm run coverage |
Run tests with coverage report |
npm run node |
Start local Hardhat node |
npm run clean |
Remove build artifacts |
npm run deploy:local |
Deploy to local Hardhat node |
npm run deploy:arbitrum |
Deploy to Arbitrum Mainnet |
npm run deploy:arbitrumSepolia |
Deploy to Arbitrum Sepolia testnet |
npm run deploy:base |
Deploy to Base Mainnet |
npm run deploy:baseSepolia |
Deploy to Base Sepolia testnet |
npm run deploy:monadTestnet |
Deploy to Monad Testnet |
npm run verify:arbitrum |
Verify contract on Arbitrum Mainnet |
npm run verify:arbitrumSepolia |
Verify contract on Arbitrum Sepolia |
npm run verify:base |
Verify contract on Base Mainnet |
npm run verify:baseSepolia |
Verify contract on Base Sepolia |
| Command | Description |
|---|---|
npm run dev |
Start development server at localhost:3000 |
npm run build |
Build for production |
npm run start |
Start production server |
npm run lint |
Run ESLint |
Open two terminals:
Terminal 1 — start the node:
cd backend
npm run nodeTerminal 2 — deploy:
cd backend
npm run deploy:localMake sure your backend/.env has the correct PRIVATE_KEY and RPC URL for your target network, then run:
npm run deploy:arbitrumSepoliaReplace arbitrumSepolia with your target network. The deploy script will automatically wait for enough confirmations before proceeding.
npx hardhat run scripts/deploy-and-verify.ts --network arbitrumSepoliaThis script deploys the contract, waits 30 seconds for the explorer to index it, and then verifies it automatically.
Contract verification makes your source code publicly readable on block explorers (Arbiscan, Basescan, etc.).
npm run verify:arbitrumSepoliaYou can also verify manually with a specific address:
npx hardhat verify --network arbitrumSepolia <CONTRACT_ADDRESS>If your constructor takes arguments:
npx hardhat verify --network arbitrumSepolia <CONTRACT_ADDRESS> <ARG1> <ARG2>Make sure your explorer API key is set in
backend/.envbefore verifying.
cd backend
npm run testTo run a specific test file:
npx hardhat test test/Counter.test.tsTo run with gas reporting enabled:
REPORT_GAS=true npm run testTo run with coverage:
npm run coverageCoverage results will appear in backend/coverage/index.html. Open it in a browser for a detailed line-by-line report.
- Push your repository to GitHub.
- Go to vercel.com and click Add New Project.
- Import your GitHub repository.
- Set the Root Directory to
frontend. - Add your environment variable:
NEXT_PUBLIC_REOWN_PROJECT_ID. - Click Deploy.
Vercel will automatically redeploy every time you push to your main branch.
- Clone the repo and set up environment variables as described above.
- Replace
backend/contracts/Counter.solwith your own contract. - Update
backend/deploy/01_deploy_counter.tswith your contract name and constructor arguments. - Update
backend/test/Counter.test.tswith tests for your contract. - Compile and test:
npm run compile && npm run test. - Update
frontend/app/page.tsxwith your dApp UI. - Update
frontend/config/appkit.tswith your app name, description, and URL. - Deploy your contract to the target network.
- Connect your frontend to the deployed contract using the generated TypeChain types from
backend/typechain-types/. - Deploy the frontend to Vercel.
| Network | Type | Chain ID |
|---|---|---|
| Hardhat (local) | Local | 31337 |
| Localhost | Local | 31337 |
| Arbitrum One | Mainnet | 42161 |
| Arbitrum Sepolia | Testnet | 421614 |
| Base | Mainnet | 8453 |
| Base Sepolia | Testnet | 84532 |
| Monad Testnet | Testnet | 10143 |
Backend — backend/hardhat.config.ts:
newNetwork: {
chainId: 12345,
url: process.env.NEW_NETWORK_RPC_URL || "",
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
},Frontend — frontend/config/chains.ts:
export const newNetwork = {
id: 12345,
name: "New Network",
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
rpcUrls: { default: { http: ["https://rpc.newnetwork.xyz"] } },
blockExplorers: { default: { name: "Explorer", url: "https://explorer.newnetwork.xyz" } },
} as const satisfies AppKitNetwork;Then add it to the networks array in the same file.
dApp — Decentralized application. An app whose backend logic runs on a blockchain via smart contracts.
EVM — Ethereum Virtual Machine. The runtime environment for smart contracts on Ethereum and compatible chains (Arbitrum, Base, Monad, etc.).
Smart Contract — A self-executing program deployed on a blockchain. Once deployed, its code cannot be changed.
ABI — Application Binary Interface. Describes the functions and events of a smart contract so frontends can interact with it.
RPC URL — Remote Procedure Call URL. The endpoint your app uses to communicate with a blockchain node.
Testnet — A public blockchain network used for testing. Tokens have no real value. Use testnets before deploying to mainnet.
Mainnet — The live, production blockchain where real assets are at stake.
Gas — The fee paid to execute transactions and smart contract functions on an EVM chain.
Wallet — Software (e.g., MetaMask) that manages your private keys and lets you sign transactions.
Private Key — A secret cryptographic key that proves ownership of a wallet. Never share it or commit it to Git.
Contract Verification — The process of uploading your contract source code to a block explorer so anyone can read and audit it.
TypeChain — A tool that generates TypeScript type definitions from your compiled contracts, enabling type-safe contract interactions.
hardhat-deploy — A Hardhat plugin that manages deterministic deployments and keeps track of deployed contract addresses across networks.
Reown AppKit — A wallet connection library (formerly WalletConnect AppKit) that provides a customizable modal for connecting Web3 wallets.
Wagmi — A collection of React hooks for interacting with Ethereum, built on top of Viem.
Viem — A lightweight, type-safe TypeScript library for Ethereum interactions.