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
- Always remove listeners when component unmounts or is no longer needed
- Use same function reference for add/remove to work correctly
- Reload on chainChanged to avoid stale state issues
- Handle empty accounts array - user may disconnect wallet
- 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
| Feature | EIP-1193 Events | Custom Events |
|---|
| Events | accountsChanged, chainChanged, connect, disconnect | Varies by library |
| Pattern | Standard EventEmitter | Library-specific |
| Compatibility | All EIP-1193 providers | Provider-specific |
| Use case | Wallet/chain changes | Blockchain data (blocks, logs) |
For blockchain data subscriptions (blocks, logs, transactions), use JSON-RPC subscription methods like eth_subscribe.