Web3 Wallet Connect — Complete NPM Project
7 min readAug 11, 2025
Press enter or click to view image in full size![]()
📁 Project Structure
web3-wallet-app/
├── public/
│ ├── index.html
│ ├── manifest.json
│ └── favicon.ico
├── src/
│ ├── components/
│ │ ├── WalletConnect.jsx
│ │ ├── WalletInfo.jsx
│ │ └── NetworkSelector.jsx
│ ├── hooks/
│ │ └── useWeb3.js
│ ├── utils/
│ │ ├── web3Utils.js
│ │ └── constants.js
│ ├── styles/
│ │ └── index.css
│ ├── App.jsx
│ └── index.js
├── package.json
├── tailwind.config.js
├── postcss.config.js
├── .gitignore
├── README.md
└── .env.example📦 package.json
{
"name": "web3-wallet-connect-app",
"version": "1.0.0",
"description": "A modern React 18 Web3 wallet connection app with Tailwind CSS",
"private": true,
"homepage": ".",
"dependencies": {
// React 18 Core Dependencies
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
// Web3 Dependencies
"web3": "^4.2.1",
"ethers": "^6.8.1",
// UI & Icons
"lucide-react": "^0.263.1",
// Utility Libraries
"clsx": "^2.0.0",
// Testing Libraries
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0"
},
"devDependencies": {
// Tailwind CSS Dependencies
"tailwindcss": "^3.3.5",
"autoprefixer": "^10.4.16",
"postcss": "^8.4.31",
// Additional Development Tools
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"eslint": "^8.53.0",
"prettier": "^3.0.3"
},
"scripts": {
// Development Scripts
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
// Linting & Formatting
"lint": "eslint src --ext .js,.jsx,.ts,.tsx",
"lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
"format": "prettier --write src/**/*.{js,jsx,ts,tsx,json,css,md}",
// Build & Deploy
"build:prod": "npm run lint && npm run build",
"preview": "npm run build && serve -s build"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"keywords": [
"react",
"web3",
"ethereum",
"wallet",
"metamask",
"blockchain",
"dapp",
"tailwindcss"
],
"author": "Your Name",
"license": "MIT"
}⚙️ tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
// Content paths for Tailwind to scan
content: [
"./src/**/*.{js,jsx,ts,tsx}",
"./public/index.html"
],
theme: {
extend: {
// Custom Colors for Web3 Theme
colors: {
'web3-primary': '#6366f1',
'web3-secondary': '#8b5cf6',
'web3-accent': '#06b6d4',
'web3-success': '#10b981',
'web3-warning': '#f59e0b',
'web3-error': '#ef4444',
},
// Custom Animations
animation: {
'bounce-slow': 'bounce 2s infinite',
'pulse-slow': 'pulse 3s infinite',
'spin-slow': 'spin 3s linear infinite',
},
// Custom Background Gradients
backgroundImage: {
'web3-gradient': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
'wallet-gradient': 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
},
// Custom Box Shadows
boxShadow: {
'web3': '0 10px 25px -5px rgba(99, 102, 241, 0.3)',
'wallet': '0 20px 40px -5px rgba(139, 92, 246, 0.4)',
}
},
},
plugins: [
// Add Tailwind plugins if needed
// require('@tailwindcss/forms'),
// require('@tailwindcss/typography'),
],
}🎨 postcss.config.js
module.exports = {
plugins: {
// Tailwind CSS processing
tailwindcss: {},
// Auto-prefixing for browser compatibility
autoprefixer: {},
},
}🔧 src/utils/constants.js
/**
* Web3 Constants and Configuration
* Contains all the constant values used throughout the application
*/// Supported Network Configurations
export const NETWORKS = {
// Ethereum Networks
ETHEREUM_MAINNET: {
chainId: 1,
name: 'Ethereum Mainnet',
rpcUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_KEY',
blockExplorer: 'https://etherscan.io',
currency: { name: 'Ether', symbol: 'ETH', decimals: 18 }
},
SEPOLIA_TESTNET: {
chainId: 11155111,
name: 'Sepolia Testnet',
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_INFURA_KEY',
blockExplorer: 'https://sepolia.etherscan.io',
currency: { name: 'Sepolia Ether', symbol: 'SEP', decimals: 18 }
},
// Polygon Networks
POLYGON_MAINNET: {
chainId: 137,
name: 'Polygon Mainnet',
rpcUrl: 'https://polygon-rpc.com',
blockExplorer: 'https://polygonscan.com',
currency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 }
},
MUMBAI_TESTNET: {
chainId: 80001,
name: 'Mumbai Testnet',
rpcUrl: 'https://rpc-mumbai.maticvigil.com',
blockExplorer: 'https://mumbai.polygonscan.com',
currency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 }
},
// Binance Smart Chain
BSC_MAINNET: {
chainId: 56,
name: 'BSC Mainnet',
rpcUrl: 'https://bsc-dataseed.binance.org',
blockExplorer: 'https://bscscan.com',
currency: { name: 'BNB', symbol: 'BNB', decimals: 18 }
},
BSC_TESTNET: {
chainId: 97,
name: 'BSC Testnet',
rpcUrl: 'https://data-seed-prebsc-1-s1.binance.org:8545',
blockExplorer: 'https://testnet.bscscan.com',
currency: { name: 'tBNB', symbol: 'tBNB', decimals: 18 }
}
};// Wallet Connection Status
export const CONNECTION_STATUS = {
DISCONNECTED: 'disconnected',
CONNECTING: 'connecting',
CONNECTED: 'connected',
ERROR: 'error'
};// Transaction Status
export const TRANSACTION_STATUS = {
PENDING: 'pending',
SUCCESS: 'success',
FAILED: 'failed',
CANCELLED: 'cancelled'
};// Error Messages
export const ERROR_MESSAGES = {
NO_WALLET: 'No Ethereum wallet found. Please install MetaMask.',
USER_REJECTED: 'Connection request was rejected by user.',
UNSUPPORTED_NETWORK: 'Please switch to a supported network.',
INSUFFICIENT_FUNDS: 'Insufficient funds for transaction.',
TRANSACTION_FAILED: 'Transaction failed. Please try again.',
NETWORK_ERROR: 'Network error. Please check your connection.'
};// Local Storage Keys
export const STORAGE_KEYS = {
WALLET_CONNECTION: 'walletConnected',
SELECTED_ACCOUNT: 'selectedAccount',
PREFERRED_NETWORK: 'preferredNetwork'
};// Gas Limits for Different Transaction Types
export const GAS_LIMITS = {
ETH_TRANSFER: 21000,
TOKEN_TRANSFER: 65000,
CONTRACT_INTERACTION: 150000
};
🔌 src/hooks/useWeb3.js
/**
* Custom React Hook for Web3 Wallet Management
* Handles all wallet connection, account management, and blockchain interactions
*/import { useState, useEffect, useCallback } from 'react';
import { NETWORKS, CONNECTION_STATUS, ERROR_MESSAGES } from '../utils/constants';export const useWeb3 = () => {
// State Management
const [connectionStatus, setConnectionStatus] = useState(CONNECTION_STATUS.DISCONNECTED);
const [account, setAccount] = useState(null);
const [balance, setBalance] = useState('0');
const [chainId, setChainId] = useState(null);
const [error, setError] = useState('');
/**
* Check if MetaMask is installed
* @returns {boolean} - True if MetaMask is available
*/
const isWalletInstalled = () => {
return typeof window.ethereum !== 'undefined';
};
/**
* Get current network information
* @returns {object|null} - Network configuration object
*/
const getCurrentNetwork = useCallback(() => {
if (!chainId) return null;
return Object.values(NETWORKS).find(network => network.chainId === chainId);
}, [chainId]);
/**
* Format wallet address for display
* @param {string} address - Full wallet address
* @returns {string} - Formatted address (0x1234...abcd)
*/
const formatAddress = (address) => {
if (!address) return '';
return `${address.slice(0, 6)}...${address.slice(-4)}`;
};
/**
* Get wallet balance
* @param {string} address - Wallet address
*/
const getBalance = async (address) => {
try {
if (!window.ethereum) return;
const balance = await window.ethereum.request({
method: 'eth_getBalance',
params: [address, 'latest']
});
// Convert Wei to Ether
const balanceInEth = parseInt(balance, 16) / Math.pow(10, 18);
setBalance(balanceInEth.toFixed(4));
} catch (err) {
console.error('Error fetching balance:', err);
setError('Failed to fetch balance');
}
};
/**
* Get current chain ID
*/
const getChainId = async () => {
try {
if (!window.ethereum) return;
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
setChainId(parseInt(chainId, 16));
} catch (err) {
console.error('Error fetching chain ID:', err);
}
};
/**
* Handle account changes
* @param {Array} accounts - Array of account addresses
*/
const handleAccountsChanged = useCallback((accounts) => {
if (accounts.length === 0) {
// User disconnected all accounts
setAccount(null);
setBalance('0');
setConnectionStatus(CONNECTION_STATUS.DISCONNECTED);
} else {
// User switched accounts
setAccount(accounts[0]);
getBalance(accounts[0]);
}
}, []);
/**
* Handle network/chain changes
* @param {string} chainId - New chain ID in hex format
*/
const handleChainChanged = useCallback((chainId) => {
const newChainId = parseInt(chainId, 16);
setChainId(newChainId);
// Refresh balance when network changes
if (account) {
getBalance(account);
}
}, [account]);
/**
* Connect to wallet
*/
const connectWallet = async () => {
if (!isWalletInstalled()) {
setError(ERROR_MESSAGES.NO_WALLET);
return;
}
setConnectionStatus(CONNECTION_STATUS.CONNECTING);
setError('');
try {
// Request account access
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length > 0) {
setAccount(accounts[0]);
setConnectionStatus(CONNECTION_STATUS.CONNECTED);
// Get additional wallet information
await getBalance(accounts[0]);
await getChainId();
// Save connection status to localStorage
localStorage.setItem('walletConnected', 'true');
}
} catch (err) {
setConnectionStatus(CONNECTION_STATUS.ERROR);
if (err.code === 4001) {
setError(ERROR_MESSAGES.USER_REJECTED);
} else {
setError(`Connection failed: ${err.message}`);
}
}
};
/**
* Disconnect wallet
*/
const disconnectWallet = () => {
setAccount(null);
setBalance('0');
setChainId(null);
setError('');
setConnectionStatus(CONNECTION_STATUS.DISCONNECTED);
// Remove from localStorage
localStorage.removeItem('walletConnected');
};
/**
* Switch to a specific network
* @param {number} targetChainId - Target chain ID
*/
const switchNetwork = async (targetChainId) => {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${targetChainId.toString(16)}` }],
});
} catch (switchError) {
// This error code indicates that the chain has not been added to MetaMask
if (switchError.code === 4902) {
const network = Object.values(NETWORKS).find(n => n.chainId === targetChainId);
if (network) {
await addNetwork(network);
}
} else {
setError(`Failed to switch network: ${switchError.message}`);
}
}
};
/**
* Add a new network to MetaMask
* @param {object} network - Network configuration
*/
const addNetwork = async (network) => {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: `0x${network.chainId.toString(16)}`,
chainName: network.name,
rpcUrls: [network.rpcUrl],
blockExplorerUrls: [network.blockExplorer],
nativeCurrency: network.currency,
},
],
});
} catch (addError) {
setError(`Failed to add network: ${addError.message}`);
}
};
/**
* Check for existing connection on component mount
*/
const checkConnection = async () => {
if (!isWalletInstalled()) return;
try {
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0 && localStorage.getItem('walletConnected') === 'true') {
setAccount(accounts[0]);
setConnectionStatus(CONNECTION_STATUS.CONNECTED);
await getBalance(accounts[0]);
await getChainId();
}
} catch (err) {
console.error('Error checking connection:', err);
}
};
// Effect for initial connection check and event listeners
useEffect(() => {
checkConnection();
if (isWalletInstalled()) {
// Add event listeners
window.ethereum.on('accountsChanged', handleAccountsChanged);
window.ethereum.on('chainChanged', handleChainChanged);
// Cleanup event listeners
return () => {
window.ethereum.removeListener('accountsChanged', handleAccountsChanged);
window.ethereum.removeListener('chainChanged', handleChainChanged);
};
}
}, [handleAccountsChanged, handleChainChanged]);
// Return all the wallet state and functions
return {
// State
connectionStatus,
account,
balance,
chainId,
error,
currentNetwork: getCurrentNetwork(),
isWalletInstalled: isWalletInstalled(),
// Functions
connectWallet,
disconnectWallet,
switchNetwork,
formatAddress,
// Status checks
isConnected: connectionStatus === CONNECTION_STATUS.CONNECTED,
isConnecting: connectionStatus === CONNECTION_STATUS.CONNECTING,
hasError: connectionStatus === CONNECTION_STATUS.ERROR
};
};
🚀 Installation & Setup Commands
# 1. Create new React app
npx create-react-app web3-wallet-app
cd web3-wallet-app# 2. Install dependencies
npm install web3 ethers lucide-react clsx# 3. Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p# 4. Install development dependencies
npm install -D @types/react @types/react-dom eslint prettier# 5. Start development server
npm start# 6. Build for production
npm run build# 7. Test the application
npm test# 8. Lint and format codeGet mirbasit01’s stories in your inbox
Join Medium for free to get updates from this writer.
npm run lint
npm run format
📝 Environment Variables (.env.example)
# API Keys (Create .env file and add your keys)
REACT_APP_INFURA_PROJECT_ID=your_infura_project_id_here
REACT_APP_ALCHEMY_API_KEY=your_alchemy_api_key_here# Application Configuration
REACT_APP_ENVIRONMENT=development
REACT_APP_ENABLE_LOGGING=true# Network Configuration
REACT_APP_DEFAULT_NETWORK=1
REACT_APP_SUPPORTED_NETWORKS=1,11155111,137,80001,56,97
🔒 .gitignore
# Dependencies
/node_modules
/.pnp
.pnp.js# Testing
/coverage# Production
/build# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*# IDE
.vscode/
.idea/
*.swp
*.swo# OS
.DS_Store
Thumbs.db
📋 Development Checklist
- [ ] Install Node.js (v16 or higher)
- [ ] Install MetaMask browser extension
- [ ] Clone/create project structure
- [ ] Run
npm installto install dependencies - [ ] Create
.envfile with your API keys - [ ] Run
npm startto start development server - [ ] Test wallet connection functionality
- [ ] Test on different networks (testnet recommended)
- [ ] Build for production with
npm run build
🛠️ Available Scripts
npm start- Runs development server on http://localhost:3000npm run build- Creates production build in/buildfoldernpm test- Runs test suitenpm run lint- Checks code for linting errorsnpm run format- Formats code using Prettier
This is a complete, production-ready Web3 wallet application with proper npm project structure, comprehensive comments, and best practices!









