UNPKG

fugitut

Version:

A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps

223 lines (187 loc) 6.93 kB
import { AbstractConnectorArguments, ConnectorUpdate } from '@web3-react-fork/types' import { AbstractConnector } from '@web3-react-fork/abstract-connector' import warning from 'tiny-warning' import { SendReturnResult, SendReturn, SendOld, SendAsync } from './types' function parseSendReturn(sendReturn: SendReturnResult | SendReturn): any { return sendReturn.hasOwnProperty('result') ? sendReturn.result : sendReturn } export class NoEthereumProviderError extends Error { public constructor() { super() this.name = this.constructor.name this.message = 'No Ethereum provider was found on window.ethereum.' } } export class UserRejectedRequestError extends Error { public constructor() { super() this.name = this.constructor.name this.message = 'The user rejected the request.' } } export class InjectedConnector extends AbstractConnector { constructor(kwargs: AbstractConnectorArguments) { super(kwargs) this.handleChainChanged = this.handleChainChanged.bind(this) this.handleAccountsChanged = this.handleAccountsChanged.bind(this) this.handleDisconnect = this.handleDisconnect.bind(this) } private handleChainChanged(chainId: string | number): void { if (__DEV__) { console.log("Handling 'chainChanged' event with payload", chainId) } this.emitUpdate({ chainId, provider: window.ethereum }) } private handleAccountsChanged(accounts: string[]): void { if (__DEV__) { console.log("Handling 'accountsChanged' event with payload", accounts) } if (accounts.length === 0) { this.emitDeactivate() } else { this.emitUpdate({ account: accounts[0] }) } } private handleDisconnect(code: number, reason: string): void { if (__DEV__) { console.log("Handling 'close' event with payload", code, reason) } this.emitDeactivate() } public async activate(): Promise<ConnectorUpdate> { if (!window.ethereum) { throw new NoEthereumProviderError() } if (window.ethereum.on) { window.ethereum.on('chainChanged', this.handleChainChanged) window.ethereum.on('accountsChanged', this.handleAccountsChanged) window.ethereum.on('disconnect', this.handleDisconnect) } if ((window.ethereum as any).isMetaMask) { ;(window.ethereum as any).autoRefreshOnNetworkChange = false } // try to activate + get account via const result: ConnectorUpdate = await new Promise(resolve => { ;(window.ethereum!.sendAsync as SendAsync)({ method: 'eth_requestAccounts' }, async (err, sendReturn) => { if (err) { if ((err as any).code === 4001) { throw new UserRejectedRequestError() } warning(false, 'eth_requestAccounts was unsuccessful, falling back to enable') const account = await window .ethereum!.enable() .then(sendReturn => sendReturn && parseSendReturn(sendReturn)[0]) resolve({ provider: window.ethereum, ...(account ? { account } : {}) }) } const account = parseSendReturn(sendReturn)[0] resolve({ provider: window.ethereum, ...(account ? { account } : {}) }) }) }) return result } public async getProvider(): Promise<any> { return window.ethereum } public async getChainId(): Promise<number | string> { if (!window.ethereum) { throw new NoEthereumProviderError() } let chainId: string | number chainId = await new Promise(resolve => { ;(window.ethereum!.sendAsync as SendAsync)({ method: 'eth_chainId' }, async (err, sendReturn) => { if (err) { warning(false, 'eth_chainId was unsuccessful, falling back to net_version') resolve(0) } const result = parseSendReturn(sendReturn) resolve(result) }) }) if (!chainId) { chainId = await new Promise(resolve => { ;(window.ethereum!.sendAsync as SendAsync)({ method: 'net_version' }, async (err, sendReturn) => { if (err) { warning(false, 'net_version was unsuccessful, falling back to net version v2') resolve(0) } const result = parseSendReturn(sendReturn) resolve(result) }) }) } if (!chainId) { try { chainId = parseSendReturn((window.ethereum.send as SendOld)({ method: 'net_version' })) } catch { warning(false, 'net_version v2 was unsuccessful, falling back to manual matches and static properties') } } if (!chainId) { if ((window.ethereum as any).isDapper) { chainId = parseSendReturn((window.ethereum as any).cachedResults.net_version) } else { chainId = (window.ethereum as any).chainId || (window.ethereum as any).netVersion || (window.ethereum as any).networkVersion || (window.ethereum as any)._chainId } } return chainId } public async getAccount(): Promise<null | string> { if (!window.ethereum) { throw new NoEthereumProviderError() } let account: string account = await new Promise(resolve => { ;(window.ethereum!.sendAsync as SendAsync)({ method: 'eth_accounts' }, async (err, sendReturn) => { if (err) { warning(false, 'eth_accounts was unsuccessful, falling back to enable') resolve('') } const result = parseSendReturn(sendReturn)[0] resolve(result) }) }) if (!account) { try { account = await window.ethereum.enable().then(sendReturn => parseSendReturn(sendReturn)[0]) } catch { warning(false, 'enable was unsuccessful, falling back to eth_accounts v2') } } if (!account) { account = parseSendReturn((window.ethereum.send as SendOld)({ method: 'eth_accounts' }))[0] } return account } public deactivate() { if (window.ethereum && window.ethereum.removeListener) { window.ethereum.removeListener('chainChanged', this.handleChainChanged) window.ethereum.removeListener('accountsChanged', this.handleAccountsChanged) window.ethereum.removeListener('disconnect', this.handleDisconnect) } } public async isAuthorized(): Promise<boolean> { if (!window.ethereum) { return false } let toRet: boolean = false toRet = await new Promise(resolve => { ;(window.ethereum!.sendAsync as SendAsync)({ method: 'eth_accounts' }, async (err, sendReturn) => { if (err) { warning(false, 'eth_accounts was unsuccessful, falling back to eth_accounts v2') const account = parseSendReturn((window.ethereum!.send as SendOld)({ method: 'eth_accounts' }))[0] resolve(!!account) } const result = parseSendReturn(sendReturn) if (result.length > 0) { resolve(true) } resolve(false) }) }) return toRet } }