Web3 EVM App Demo

Overview #
A Web3 application demonstrating wallet connection and cryptographic message signing on EVM-compatible blockchains.
Link: Live Demo
Context #
Requirement:
“Add a ‘wallet connect’ button (MetaMask or any test wallet). After connecting, sign: ‘This is a test’ and show the signed hash in the console. Then hide the loading screen and show the home page.”
The implementation goes beyond the basic requirements with production-grade error handling, responsive UI, and proper Web3 integration patterns.
Key Features #
Wallet Integration #
- MetaMask Detection: Checks for MetaMask availability before connection
- Connection Flow: Handles user approval/rejection gracefully
- State Management: React hooks for wallet state and loading indicators
- Error Handling: User-friendly error messages for common issues
Message Signing #
- EIP-191 Compliance: Uses Ethers.js for standard personal message signing
- Signature Display: Shows full signature hash in both console and UI
User Experience #
- Responsive Design: Works on mobile and desktop
- Loading States: Clear visual feedback during async operations
- Auto-connect: Seamless wallet connection before signing if needed
Core Implementation #
1. Wallet Connection Logic #
The useWallet hook handles MetaMask connection with proper error handling:
const connectWallet = async () => {
try {
setIsConnecting(true);
setError(null);
// Check if MetaMask is installed
if (!window.ethereum) {
throw new Error('MetaMask is not installed. Please install MetaMask to continue.');
}
// Request account access
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts',
});
const address = accounts[0];
setWalletAddress(address);
console.log('✅ Wallet connected:', address);
return address;
} catch (err) {
const errorMessage = err.message || 'Failed to connect wallet';
setError(errorMessage);
console.error('❌ Wallet connection error:', errorMessage);
throw err;
} finally {
setIsConnecting(false);
}
};
2. Message Signing Implementation #
Uses Ethers.js v6 BrowserProvider for cryptographic signing:
const signMessage = async (message) => {
try {
if (!window.ethereum) {
throw new Error('MetaMask is not installed');
}
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
console.log('📝 Signing message:', message);
const signature = await signer.signMessage(message);
console.log('✅ Message signed successfully!');
console.log('📋 Signature:', signature);
return signature;
} catch (err) {
const errorMessage = err.message || 'Failed to sign message';
console.error('❌ Signing error:', errorMessage);
throw err;
}
};
3. UI Integration #
React component handling the sign flow with auto-connect:
const handleSignMessage = async () => {
try {
setIsSigning(true);
// If not connected, connect first
if (!currentWalletAddress) {
console.log('🔗 Wallet not connected, connecting...');
await connectWallet();
}
const message = 'This is a test';
const sig = await signMessage(message);
console.log('✍️ Signed message:', message);
console.log('📋 Signature hash:', sig);
setSignature(sig);
} catch (err) {
console.error('Error signing message:', err);
} finally {
setIsSigning(false);
}
};
4. Header Component with Connect/Disconnect #
Dynamic header button that adapts based on wallet state:
const handleHeaderButtonClick = async () => {
if (currentWalletAddress) {
// Disconnect - reload page to reset state
window.location.reload();
} else {
// Connect wallet
try {
await connectWallet();
} catch (err) {
console.error('Failed to connect wallet:', err);
}
}
};
const getHeaderButtonText = () => {
if (isConnecting) return 'Connecting...';
if (currentWalletAddress) return `Disconnect ${formatAddress(currentWalletAddress)}`;
return 'Connect Wallet';
};
Tech Stack #
- Frontend: React 19
- Styling: Tailwind CSS
- Web3: Ethers.js v6, MetaMask integration
- Infrastructure: Kubernetes, full CI/CD pipeline
Project Structure #
src/
├── components/
│ ├── Header.jsx # Wallet connect/disconnect button
│ ├── HomePage.jsx # Main page with sign flow
│ ├── Tile.jsx # Reusable card component
│ └── LoadingScreen.jsx # Initial loading state
├── hooks/
│ └── useWallet.js # Wallet connection & signing logic
└── App.jsx # Router configuration
Supported Networks #
EVM-compatible blockchains:
- Ethereum (Mainnet, Sepolia, Goerli)
- Polygon
- Avalanche
- etc
Future Enhancements #
- Multi-wallet support (WalletConnect, Coinbase Wallet)
- Transaction signing and broadcasting
- Smart contract interaction examples
- Network switching UI
- ENS name resolution