/*global Web3*/ cleanContextForImports() require('web3/dist/web3.min.js') const log = require('loglevel') const LocalMessageDuplexStream = require('post-message-stream') const setupDappAutoReload = require('./lib/auto-reload.js') const MetamaskInpageProvider = require('metamask-inpage-provider') const createStandardProvider = require('./createStandardProvider').default let warned = false restoreContextAfterImports() log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn') // // setup plugin communication // // setup background connection const metamaskStream = new LocalMessageDuplexStream({ name: 'inpage_tangerine', target: 'contentscript_tangerine', }) // compose the inpage provider const inpageProvider = new MetamaskInpageProvider(metamaskStream) // set a high max listener count to avoid unnecesary warnings inpageProvider.setMaxListeners(100) // augment the provider with its enable method inpageProvider.enable = function ({ force } = {}) { return new Promise((resolve, reject) => { inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => { if (error || response.error) { reject(error || response.error) } else { resolve(response.result) } }) }) } // give the dapps control of a refresh they can toggle this off on the window.ethereum // this will be default true so it does not break any old apps. inpageProvider.autoRefreshOnNetworkChange = true // add metamask-specific convenience methods inpageProvider._metamask = new Proxy({ /** * Synchronously determines if this domain is currently enabled, with a potential false negative if called to soon * * @returns {boolean} - returns true if this domain is currently enabled */ isEnabled: function () { const { isEnabled } = inpageProvider.publicConfigStore.getState() return Boolean(isEnabled) }, /** * Asynchronously determines if this domain is currently enabled * * @returns {Promise} - Promise resolving to true if this domain is currently enabled */ isApproved: async function () { const { isEnabled } = await getPublicConfigWhenReady() return Boolean(isEnabled) }, /** * Determines if MetaMask is unlocked by the user * * @returns {Promise} - Promise resolving to true if MetaMask is currently unlocked */ isUnlocked: async function () { const { isUnlocked } = await getPublicConfigWhenReady() return Boolean(isUnlocked) }, }, { get: function (obj, prop) { !warned && console.warn('Heads up! ethereum._metamask exposes methods that have ' + 'not been standardized yet. This means that these methods may not be implemented ' + 'in other dapp browsers and may be removed from MetaMask in the future.') warned = true return obj[prop] }, }) // publicConfig isn't populated until we get a message from background. // Using this getter will ensure the state is available async function getPublicConfigWhenReady () { const store = inpageProvider.publicConfigStore let state = store.getState() // if state is missing, wait for first update if (!state.networkVersion) { state = await new Promise(resolve => store.once('update', resolve)) console.log('new state', state) } return state } // Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound // `sendAsync` method on the prototype, causing `this` reference issues with drizzle const proxiedInpageProvider = new Proxy(inpageProvider, { // straight up lie that we deleted the property so that it doesnt // throw an error in strict mode deleteProperty: () => true, }) // window.ethereum = createStandardProvider(proxiedInpageProvider) window.tangerine = createStandardProvider(proxiedInpageProvider) // // setup web3 // const web3 = new Web3(proxiedInpageProvider) web3.setProvider = function () { log.debug('MetaMask - overrode web3.setProvider') } log.debug('MetaMask - injected web3') setupDappAutoReload(web3, inpageProvider.publicConfigStore) // set web3 defaultAccount inpageProvider.publicConfigStore.subscribe(function (state) { web3.eth.defaultAccount = state.selectedAddress }) inpageProvider.publicConfigStore.subscribe(function (state) { if (state.onboardingcomplete) { window.postMessage('onboardingcomplete', '*') } }) // need to make sure we aren't affected by overlapping namespaces // and that we dont affect the app with our namespace // mostly a fix for web3's BigNumber if AMD's "define" is defined... let __define /** * Caches reference to global define object and deletes it to * avoid conflicts with other global define objects, such as * AMD's define function */ function cleanContextForImports () { __define = global.define try { global.define = undefined } catch (_) { console.warn('MetaMask - global.define could not be deleted.') } } /** * Restores global define object from cached reference */ function restoreContextAfterImports () { try { global.define = __define } catch (_) { console.warn('MetaMask - global.define could not be overwritten.') } }