EventEmitter-based system for handling wallet connection events, account changes, chain switches, and connection state management.
Event types and their payload structures for wallet state changes.
type ProviderEventMap = {
/** Emitted when wallet connection is established */
connect: ProviderConnectInfo;
/** Emitted when wallet is disconnected */
disconnect: ProviderRpcError;
/** Emitted when the active chain is changed */
chainChanged: string; // hex string
/** Emitted when connected accounts change */
accountsChanged: string[];
};
interface ProviderConnectInfo {
/** Chain ID of the connected network (hex string) */
readonly chainId: string;
}
interface ProviderRpcError extends Error {
/** Error message */
message: string;
/** Error code following EIP-1193 standards */
code: number;
/** Additional error data (optional) */
data?: unknown;
}Base event emitter class for provider event handling.
class ProviderEventEmitter extends EventEmitter<keyof ProviderEventMap> {
/** Emit an event with typed payload */
emit<K extends keyof ProviderEventMap>(
event: K,
...args: [ProviderEventMap[K]]
): boolean;
/** Listen for events with typed callback */
on<K extends keyof ProviderEventMap>(
event: K,
listener: (_: ProviderEventMap[K]) => void
): this;
/** Listen for events once with typed callback */
once<K extends keyof ProviderEventMap>(
event: K,
listener: (_: ProviderEventMap[K]) => void
): this;
/** Remove event listener */
off<K extends keyof ProviderEventMap>(
event: K,
listener: (_: ProviderEventMap[K]) => void
): this;
}Events related to wallet connection establishment and termination.
/**
* Connect event - emitted when wallet connection is established
* @param info - Connection information including chain ID
*/
provider.on('connect', (info: ProviderConnectInfo) => void);
/**
* Disconnect event - emitted when wallet connection is lost
* @param error - Error information about the disconnection
*/
provider.on('disconnect', (error: ProviderRpcError) => void);Usage Examples:
import { createCoinbaseWalletSDK } from "@coinbase/wallet-sdk";
const sdk = createCoinbaseWalletSDK({
appName: "Event Handling Example"
});
const provider = sdk.getProvider();
// Listen for connection
provider.on('connect', (connectInfo) => {
console.log('Wallet connected to chain:', connectInfo.chainId);
const chainId = parseInt(connectInfo.chainId, 16);
if (chainId === 1) {
console.log('Connected to Ethereum mainnet');
} else if (chainId === 8453) {
console.log('Connected to Base');
}
});
// Listen for disconnection
provider.on('disconnect', (error) => {
console.log('Wallet disconnected:', error.message);
console.log('Error code:', error.code);
// Handle different disconnect scenarios
if (error.code === 1013) {
console.log('User disconnected');
} else {
console.log('Unexpected disconnection');
}
});
// Connect to trigger events
try {
await provider.request({ method: 'eth_requestAccounts' });
} catch (error) {
console.error('Connection failed:', error);
}Events for monitoring changes to connected wallet accounts.
/**
* AccountsChanged event - emitted when connected accounts change
* @param accounts - Array of connected account addresses
*/
provider.on('accountsChanged', (accounts: string[]) => void);Usage Examples:
// Monitor account changes
provider.on('accountsChanged', (accounts) => {
console.log('Accounts changed:', accounts);
if (accounts.length === 0) {
console.log('No accounts connected');
// Handle disconnected state
updateUIForDisconnectedState();
} else {
console.log('Active account:', accounts[0]);
// Handle account switch
updateUIForNewAccount(accounts[0]);
}
});
// Initial connection
const accounts = await provider.request({
method: 'eth_requestAccounts'
});
console.log('Initially connected accounts:', accounts);
function updateUIForDisconnectedState() {
// Update UI to show disconnected state
document.getElementById('account')?.textContent = 'Not connected';
}
function updateUIForNewAccount(account: string) {
// Update UI with new account
document.getElementById('account')?.textContent =
`${account.slice(0, 6)}...${account.slice(-4)}`;
}Events for monitoring blockchain network changes.
/**
* ChainChanged event - emitted when the active chain changes
* @param chainId - New chain ID as hex string
*/
provider.on('chainChanged', (chainId: string) => void);Usage Examples:
// Monitor chain changes
provider.on('chainChanged', (chainId) => {
const chainIdNumber = parseInt(chainId, 16);
console.log('Chain changed to:', chainIdNumber);
// Handle different chains
switch (chainIdNumber) {
case 1:
console.log('Switched to Ethereum mainnet');
updateUIForChain('Ethereum', '#627EEA');
break;
case 8453:
console.log('Switched to Base');
updateUIForChain('Base', '#0052FF');
break;
case 137:
console.log('Switched to Polygon');
updateUIForChain('Polygon', '#8247E5');
break;
default:
console.log('Switched to unknown chain:', chainIdNumber);
updateUIForChain(`Chain ${chainIdNumber}`, '#666666');
}
// Reload application data for new chain
await loadDataForChain(chainIdNumber);
});
function updateUIForChain(name: string, color: string) {
const chainElement = document.getElementById('chain');
if (chainElement) {
chainElement.textContent = name;
chainElement.style.color = color;
}
}
async function loadDataForChain(chainId: number) {
// Reload balances, transactions, etc. for new chain
console.log(`Loading data for chain ${chainId}`);
}Methods for managing event listeners lifecycle.
/**
* Add event listener
* @param event - Event name
* @param listener - Event handler function
*/
provider.on<K extends keyof ProviderEventMap>(
event: K,
listener: (payload: ProviderEventMap[K]) => void
): this;
/**
* Add one-time event listener
* @param event - Event name
* @param listener - Event handler function (called once)
*/
provider.once<K extends keyof ProviderEventMap>(
event: K,
listener: (payload: ProviderEventMap[K]) => void
): this;
/**
* Remove event listener
* @param event - Event name
* @param listener - Event handler function to remove
*/
provider.off<K extends keyof ProviderEventMap>(
event: K,
listener: (payload: ProviderEventMap[K]) => void
): this;Usage Examples:
// Create event handlers
const handleAccountsChanged = (accounts: string[]) => {
console.log('Accounts updated:', accounts);
};
const handleChainChanged = (chainId: string) => {
console.log('Chain updated:', chainId);
};
// Add listeners
provider.on('accountsChanged', handleAccountsChanged);
provider.on('chainChanged', handleChainChanged);
// One-time listener for initial connection
provider.once('connect', (connectInfo) => {
console.log('Initial connection established:', connectInfo.chainId);
});
// Remove specific listener
provider.off('accountsChanged', handleAccountsChanged);
// Component cleanup (remove all listeners)
function cleanup() {
provider.off('accountsChanged', handleAccountsChanged);
provider.off('chainChanged', handleChainChanged);
// Or remove all listeners for an event
provider.removeAllListeners('connect');
}Handle provider-specific errors through disconnect events and request rejections.
// Global error handling
provider.on('disconnect', (error) => {
switch (error.code) {
case 1013: // User disconnected
console.log('User intentionally disconnected');
break;
case 1001: // Network error
console.log('Network connection lost');
break;
default:
console.error('Unexpected disconnect:', error);
}
});
// Request-specific error handling
try {
await provider.request({ method: 'eth_sendTransaction', params: [...] });
} catch (error) {
if (error.code === 4001) {
console.log('User rejected transaction');
} else {
console.error('Transaction failed:', error);
}
}Implement reactive application patterns using provider events:
class WalletManager {
private provider: ProviderInterface;
private isConnected = false;
private currentAccount: string | null = null;
private currentChain: number | null = null;
constructor(provider: ProviderInterface) {
this.provider = provider;
this.setupEventListeners();
}
private setupEventListeners() {
this.provider.on('connect', this.handleConnect.bind(this));
this.provider.on('disconnect', this.handleDisconnect.bind(this));
this.provider.on('accountsChanged', this.handleAccountsChanged.bind(this));
this.provider.on('chainChanged', this.handleChainChanged.bind(this));
}
private handleConnect(connectInfo: ProviderConnectInfo) {
this.isConnected = true;
this.currentChain = parseInt(connectInfo.chainId, 16);
console.log('Connected to chain:', this.currentChain);
}
private handleDisconnect(error: ProviderRpcError) {
this.isConnected = false;
this.currentAccount = null;
this.currentChain = null;
console.log('Disconnected:', error.message);
}
private handleAccountsChanged(accounts: string[]) {
this.currentAccount = accounts.length > 0 ? accounts[0] : null;
console.log('Current account:', this.currentAccount);
}
private handleChainChanged(chainId: string) {
this.currentChain = parseInt(chainId, 16);
console.log('Current chain:', this.currentChain);
}
public getState() {
return {
isConnected: this.isConnected,
account: this.currentAccount,
chainId: this.currentChain
};
}
}
// Usage
const walletManager = new WalletManager(provider);