Skip to main content

Web3 EVM App Demo

·3 mins

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