xylabs/sdk-react

View on GitHub
packages/crypto/src/wallets/lib/EIP1193Events.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { forget } from '@xylabs/forget'
import { BrowserProvider, Eip1193Provider, Listener } from 'ethers'

import { EIP1193EventNames, EIP1193EventsCompatible } from './EIP1193'
import { SupportedEventProposals } from './SupportedEvents'

/**
 * A zero dependency class that provides functionality for handling EIP1193 events
 *
 * See - https://eips.ethereum.org/EIPS/eip-1193
 */
export abstract class EIP1193Events implements EIP1193EventsCompatible {
  // helpful for crafting debug/error messages to know the injected wallet
  _providerName: string | undefined

  // list of EIP-1193 specific event names and listeners for easy cleanup
  private eip1193Listeners: [event: EIP1193EventNames, listener: Listener][] = []

  // opt-in to EIP-1193 events since not all wallets will support them
  private eventsEnabled = false

  private listeningProvider: BrowserProvider | undefined

  constructor(supportedEvents?: SupportedEventProposals[], provider?: Eip1193Provider, providerName?: string) {
    this._providerName = providerName
    this.eventsEnabled = !!supportedEvents?.includes('EIP-1193')
    if (window.ethereum === undefined && provider === undefined) {
      console.warn('attempting to subscribe to EIP1193 events but missing provider in arguments or at window.ethereum')
    }
    this.listeningProvider = (provider ?? window.ethereum) as BrowserProvider | undefined
  }

  onAccountsChanged(listener: Listener) {
    this.addListener('accountsChanged', listener)
  }

  onChainChanged(listener: Listener) {
    this.addListener('chainChanged', listener)
  }

  onConnect(listener: Listener) {
    this.addListener('connect', listener)
  }

  onDisconnect(listener: Listener) {
    this.addListener('disconnect', listener)
  }

  removeEIP11193Listener(event: EIP1193EventNames, listener: Listener) {
    this.enabled(() => {
      forget(this.listeningProvider?.removeListener(event, listener) ?? Promise.resolve())
      this.eip1193Listeners = this.eip1193Listeners.filter(([, savedListener]) => listener !== savedListener)
    })
  }

  removeEIP11193Listeners() {
    for (const [event, listener] of this.eip1193Listeners) forget(this.listeningProvider?.removeListener(event, listener) ?? Promise.resolve())
  }

  private addListener(event: EIP1193EventNames, listener: Listener) {
    this.enabled(() => {
      forget(this.listeningProvider?.on(event, listener) ?? Promise.resolve())
      this.eip1193Listeners.push([event, listener])
    })
  }

  private enabled(method?: () => void) {
    if (this.eventsEnabled) {
      method?.()
    }
  }
}