UNPKG

@colony/purser-metamask

Version:

A javascript library to interact with a Metamask based Ethereum wallet

155 lines (143 loc) 4.5 kB
/* @flow */ import Web3Instance from 'web3'; import { warning } from '@colony/purser-core/utils'; import MetamaskWallet from './class'; import { methodCaller, getInpageProvider, detect as detectHelper, setStateEventObserver, } from './helpers'; import { staticMethods as messages } from './messages'; import type { MetamaskInpageProviderType, MetamaskStateEventsObserverType, } from './flowtypes'; /** * Open the Metamask Wallet instance * * @method open * * @return {WalletType} The wallet object resulted by instantiating the class * (Object is wrapped in a promise). */ export const open = async (): Promise<MetamaskWallet> => { let addressAfterEnable: string; try { /* * We're on the Modern Metamask (after EIP-1102) * See: https://eips.ethereum.org/EIPS/eip-1102 */ if (global.ethereum) { /* * Inject the web3 provider */ global.web3 = new Web3Instance(global.ethereum); /* * Enable it */ [ addressAfterEnable ] = await global.ethereum.enable(); } /* * We're on the legacy version of Metamask */ if (!global.ethereum && global.web3) { /* * Warn the user about legacy mode * * @TODO Remove legacy metmask version messages * After an adequate amount of time has passed */ warning(messages.legacyMode); /* * Enable it * * @NOTE There's an argument to be made here that it's dangerous to use * the `getInpageProvider()` helper before using `detect()` */ const legacyProvider: MetamaskInpageProviderType = getInpageProvider(); legacyProvider.enable(); /* * Inject the web3 provider (overwrite the current one) */ global.web3 = new Web3Instance(legacyProvider); } /* * Everything functions the same since the Web3 instance is now in place * (Granted, it's now using the 1.x.x version) */ return methodCaller(() => { const { publicConfigStore: { _state: state }, }: MetamaskInpageProviderType = getInpageProvider(); return new MetamaskWallet({ /* * The EIP-1102 mode uses the address we got after enabling (and getting * the users's permission), while the legacy mode get the address from * the state */ address: addressAfterEnable || state.selectedAddress, }); }, messages.metamaskNotAvailable); } catch (caughtError) { /* * User did not authorize us to open his account. We cannot do anything else. * (By clicking the 'Reject' button on the API request popup) */ throw new Error(messages.didNotAuthorize); } }; /** * Check if Metamask's injected web3 proxy instance is available in the * global object. * * Makes use of the `detect()` helper, basically it's a wrapper * that exposes it from the module. * * @method detect * * @return {boolean} Only returns true if it's available, otherwise it will throw. */ export const detect = async (): Promise<boolean> => detectHelper(); /** * Hook into Metamask's state events observers array to be able to act on account * changes from the UI * * It's a wrapper around the `setStateEventObserver()` helper method * * @method accountChangeHook * * @param {Function} callback Function to add the state events update array * It receives the state object as an only argument * * @return {Promise<void>} Does not return noting */ export const accountChangeHook = async (callback: MetamaskStateEventsObserverType): Promise<void> => { /* * If detect fails, it will throw, with a message explaining the problem * (Most likely Metamask will be locked, so we won't be able to get to * the state observer via the in-page provider) */ detectHelper(); try { return setStateEventObserver(callback); } catch (error) { /* * If this throws/catches here it means something very weird is going on. * `detect()` should catch anything that're directly related to Metamask's functionality, * but if that passes and we have to catch it here, it means some underlying APIs * might have changed, and this will be very hard to debug */ throw new Error(messages.cannotAddHook); } }; /* * @NOTE There's an argument to be made here to expose the new version */ const metamaskWallet: Object = { open, detect, accountChangeHook, }; export default metamaskWallet;