Skip to main content

Events

EIP-1193 providers emit events for account changes, chain changes, and connection status using the standard EventEmitter pattern.

Overview

Providers emit four standard events:
// Subscribe to account changes
provider.on('accountsChanged', (accounts) => {
  console.log('Active accounts:', accounts);
});

// Subscribe to chain changes
provider.on('chainChanged', (chainId) => {
  console.log('Chain ID:', chainId);
});

// Subscribe to connection
provider.on('connect', (connectInfo) => {
  console.log('Connected to chain:', connectInfo.chainId);
});

// Subscribe to disconnection
provider.on('disconnect', (error) => {
  console.log('Disconnected:', error);
});

Available Events

accountsChanged

Emitted when the active accounts change (e.g., user switches accounts in wallet). Event Data: string[] - Array of addresses
provider.on('accountsChanged', (accounts: string[]) => {
  if (accounts.length === 0) {
    console.log('No accounts available');
  } else {
    console.log('Active account:', accounts[0]);
  }
});
Use cases:
  • Update UI when user switches wallets
  • Re-fetch user-specific data
  • Prompt user to reconnect

chainChanged

Emitted when the chain changes (e.g., user switches from mainnet to testnet). Event Data: string - Chain ID (hex-encoded)
provider.on('chainChanged', (chainId: string) => {
  const chainIdNum = parseInt(chainId, 16);
  console.log('Chain ID:', chainIdNum);

  // Reload app to avoid stale state
  window.location.reload();
});
Use cases:
  • Reload application to avoid stale state
  • Update network-specific configurations
  • Display appropriate chain indicator
When chain changes, app state may be invalid. Most apps should reload: window.location.reload().

connect

Emitted when provider becomes connected to a chain. Event Data: {chainId: string} - Connection info
provider.on('connect', (connectInfo: { chainId: string }) => {
  const chainId = parseInt(connectInfo.chainId, 16);
  console.log('Connected to chain:', chainId);

  // Initialize app with chain-specific data
  initializeApp(chainId);
});
Use cases:
  • Initialize app after connection established
  • Fetch initial blockchain data
  • Enable blockchain-dependent features

disconnect

Emitted when provider disconnects from all chains. Event Data: {code: number, message: string} - Error info
provider.on('disconnect', (error: { code: number; message: string }) => {
  console.log('Disconnected:', error.message);

  // Clean up and show reconnection UI
  showReconnectDialog();
});
Use cases:
  • Clean up subscriptions and listeners
  • Show reconnection UI
  • Save user state before disconnect

Event Management

Adding Listeners

Use on() or addEventListener():
const handler = (accounts) => {
  console.log('Accounts:', accounts);
};

// Both work
provider.on('accountsChanged', handler);
provider.addEventListener('accountsChanged', handler);

Removing Listeners

Use removeListener() or removeEventListener():
const handler = (accounts) => {
  console.log('Accounts:', accounts);
};

provider.on('accountsChanged', handler);

// Later, remove listener
provider.removeListener('accountsChanged', handler);
// or
provider.removeEventListener('accountsChanged', handler);
Always use the same function reference when removing listeners. Arrow functions created inline can’t be removed.

One-Time Listeners

Use once() for single-use handlers:
provider.once('connect', (connectInfo) => {
  console.log('Initial connection:', connectInfo.chainId);
  // Handler automatically removed after firing
});

Usage Patterns

React Hook

import { useEffect, useState } from 'react';

function useAccounts(provider) {
  const [accounts, setAccounts] = useState<string[]>([]);

  useEffect(() => {
    const handler = (newAccounts: string[]) => {
      setAccounts(newAccounts);
    };

    provider.on('accountsChanged', handler);

    // Cleanup
    return () => {
      provider.removeListener('accountsChanged', handler);
    };
  }, [provider]);

  return accounts;
}

Chain Change Handler

function setupChainHandler(provider, allowedChains: number[]) {
  provider.on('chainChanged', (chainId: string) => {
    const chainIdNum = parseInt(chainId, 16);

    if (!allowedChains.includes(chainIdNum)) {
      alert(`Please switch to supported chain`);
      return;
    }

    // Chain is valid, reload
    window.location.reload();
  });
}

Connection Monitor

class ConnectionMonitor {
  private isConnected = false;

  constructor(private provider: any) {
    this.setupListeners();
  }

  private setupListeners() {
    this.provider.on('connect', (info: { chainId: string }) => {
      this.isConnected = true;
      console.log('Connected to chain:', info.chainId);
    });

    this.provider.on('disconnect', (error: any) => {
      this.isConnected = false;
      console.log('Disconnected:', error.message);
    });
  }

  async waitForConnection(): Promise<string> {
    if (this.isConnected) {
      const chainId = await this.provider.request({
        method: 'eth_chainId'
      });
      return chainId;
    }

    return new Promise((resolve) => {
      this.provider.once('connect', (info: { chainId: string }) => {
        resolve(info.chainId);
      });
    });
  }
}

Account Switcher

function createAccountSwitcher(provider) {
  let currentAccount: string | null = null;

  provider.on('accountsChanged', async (accounts: string[]) => {
    const newAccount = accounts[0] || null;

    if (newAccount !== currentAccount) {
      const oldAccount = currentAccount;
      currentAccount = newAccount;

      console.log(`Account changed from ${oldAccount} to ${newAccount}`);

      // Re-fetch account-specific data
      if (newAccount) {
        await loadAccountData(newAccount);
      }
    }
  });

  return {
    getCurrentAccount: () => currentAccount
  };
}

Best Practices

  1. Always remove listeners when component unmounts or is no longer needed
  2. Use same function reference for add/remove to work correctly
  3. Reload on chainChanged to avoid stale state issues
  4. Handle empty accounts array - user may disconnect wallet
  5. Test disconnection scenarios - network issues, manual disconnects

Error Handling

Events themselves don’t throw errors, but you can wrap handlers:
provider.on('accountsChanged', (accounts) => {
  try {
    updateAccountState(accounts);
  } catch (error) {
    console.error('Failed to handle account change:', error);
  }
});

Key Differences from Custom Event Systems

FeatureEIP-1193 EventsCustom Events
EventsaccountsChanged, chainChanged, connect, disconnectVaries by library
PatternStandard EventEmitterLibrary-specific
CompatibilityAll EIP-1193 providersProvider-specific
Use caseWallet/chain changesBlockchain data (blocks, logs)
For blockchain data subscriptions (blocks, logs, transactions), use JSON-RPC subscription methods like eth_subscribe.