2016-07-07 05:32:36 +02:00
/*global Web3*/
2016-04-30 00:38:24 +02:00
cleanContextForImports ( )
2016-07-07 05:20:40 +02:00
require ( 'web3/dist/web3.min.js' )
2017-10-26 00:45:26 +02:00
const log = require ( 'loglevel' )
2016-08-11 23:04:20 +02:00
const LocalMessageDuplexStream = require ( 'post-message-stream' )
2018-06-15 00:15:23 +02:00
const setupDappAutoReload = require ( './lib/auto-reload.js' )
2018-08-21 00:39:03 +02:00
const MetamaskInpageProvider = require ( 'metamask-inpage-provider' )
2019-02-20 01:42:08 +01:00
const createStandardProvider = require ( './createStandardProvider' ) . default
2018-10-08 22:54:13 +02:00
2018-10-29 22:28:59 +01:00
let isEnabled = false
let warned = false
2018-10-30 00:09:21 +01:00
let providerHandle
let isApprovedHandle
let isUnlockedHandle
2018-10-29 22:28:59 +01:00
2016-04-30 00:38:24 +02:00
restoreContextAfterImports ( )
2016-04-15 21:12:04 +02:00
2018-04-12 23:06:59 +02:00
log . setDefaultLevel ( process . env . METAMASK _DEBUG ? 'debug' : 'warn' )
2015-10-10 08:14:18 +02:00
2018-11-07 17:18:21 +01:00
/ * *
* Adds a postMessage listener for a specific message type
*
* @ param { string } messageType - postMessage type to listen for
* @ param { Function } handler - event handler
* @ param { boolean } remove - removes this handler after being triggered
* /
2019-02-18 22:07:58 +01:00
function onMessage ( messageType , callback , remove ) {
const handler = function ( { data } ) {
2018-11-09 13:14:32 +01:00
if ( ! data || data . type !== messageType ) { return }
2018-11-07 17:18:21 +01:00
remove && window . removeEventListener ( 'message' , handler )
2019-02-18 22:07:58 +01:00
callback . apply ( window , arguments )
}
window . addEventListener ( 'message' , handler )
2018-11-07 17:11:08 +01:00
}
2016-02-15 06:53:54 +01:00
//
2015-12-19 07:05:16 +01:00
// setup plugin communication
2016-02-15 06:53:54 +01:00
//
2016-04-15 21:12:04 +02:00
// setup background connection
2019-02-12 15:02:24 +01:00
const metamaskStream = new LocalMessageDuplexStream ( {
2015-12-19 07:05:16 +01:00
name : 'inpage' ,
target : 'contentscript' ,
} )
2016-05-06 01:04:43 +02:00
2016-05-23 00:23:16 +02:00
// compose the inpage provider
2019-02-12 15:02:24 +01:00
const inpageProvider = new MetamaskInpageProvider ( metamaskStream )
2018-10-18 00:29:40 +02:00
2018-10-18 01:17:39 +02:00
// set a high max listener count to avoid unnecesary warnings
inpageProvider . setMaxListeners ( 100 )
2016-04-15 21:12:04 +02:00
2018-10-29 23:44:04 +01:00
// set up a listener for when MetaMask is locked
2018-11-07 17:31:21 +01:00
onMessage ( 'metamasksetlocked' , ( ) => { isEnabled = false } )
2018-10-29 23:44:04 +01:00
2018-11-10 02:40:32 +01:00
// set up a listener for privacy mode responses
onMessage ( 'ethereumproviderlegacy' , ( { data : { selectedAddress } } ) => {
isEnabled = true
2018-11-18 13:23:04 +01:00
setTimeout ( ( ) => {
inpageProvider . publicConfigStore . updateState ( { selectedAddress } )
} , 0 )
2018-11-10 02:40:32 +01:00
} , true )
2018-10-22 21:08:26 +02:00
// augment the provider with its enable method
2018-11-05 15:03:30 +01:00
inpageProvider . enable = function ( { force } = { } ) {
2018-09-15 01:26:03 +02:00
return new Promise ( ( resolve , reject ) => {
2018-11-10 02:40:32 +01:00
providerHandle = ( { data : { error , selectedAddress } } ) => {
2018-11-07 17:11:08 +01:00
if ( typeof error !== 'undefined' ) {
2019-02-20 01:42:08 +01:00
reject ( {
message : error ,
code : 4001 ,
} )
2018-09-27 20:19:09 +02:00
} else {
2018-11-07 17:11:08 +01:00
window . removeEventListener ( 'message' , providerHandle )
2018-11-18 13:23:04 +01:00
setTimeout ( ( ) => {
inpageProvider . publicConfigStore . updateState ( { selectedAddress } )
} , 0 )
2018-10-23 19:19:01 +02:00
2018-11-03 12:06:11 +01:00
// wait for the background to update with an account
2018-11-10 02:40:32 +01:00
inpageProvider . sendAsync ( { method : 'eth_accounts' , params : [ ] } , ( error , response ) => {
if ( error ) {
reject ( error )
} else {
2018-10-23 19:19:01 +02:00
isEnabled = true
2018-11-10 02:40:32 +01:00
resolve ( response . result )
}
} )
2018-09-27 20:19:09 +02:00
}
2018-10-30 00:09:21 +01:00
}
2018-11-07 17:18:21 +01:00
onMessage ( 'ethereumprovider' , providerHandle , true )
2018-11-05 15:03:30 +01:00
window . postMessage ( { type : 'ETHEREUM_ENABLE_PROVIDER' , force } , '*' )
2018-09-15 01:26:03 +02:00
} )
}
2018-10-22 21:08:26 +02:00
// add metamask-specific convenience methods
inpageProvider . _metamask = new Proxy ( {
/ * *
* Determines if this domain is currently enabled
*
* @ returns { boolean } - true if this domain is currently enabled
* /
isEnabled : function ( ) {
return isEnabled
} ,
/ * *
* Determines if this domain has been previously approved
*
* @ returns { Promise < boolean > } - Promise resolving to true if this domain has been previously approved
* /
2018-11-17 01:54:55 +01:00
isApproved : function ( ) {
2018-11-07 17:31:21 +01:00
return new Promise ( ( resolve ) => {
isApprovedHandle = ( { data : { caching , isApproved } } ) => {
if ( caching ) {
resolve ( ! ! isApproved )
2018-10-22 21:08:26 +02:00
} else {
2018-11-07 17:31:21 +01:00
resolve ( false )
2018-10-22 21:08:26 +02:00
}
2018-10-30 00:09:21 +01:00
}
2018-11-07 17:18:21 +01:00
onMessage ( 'ethereumisapproved' , isApprovedHandle , true )
2018-10-29 21:55:13 +01:00
window . postMessage ( { type : 'ETHEREUM_IS_APPROVED' } , '*' )
2018-10-04 17:05:32 +02:00
} )
2018-10-22 21:08:26 +02:00
} ,
/ * *
* Determines if MetaMask is unlocked by the user
*
* @ returns { Promise < boolean > } - Promise resolving to true if MetaMask is currently unlocked
* /
isUnlocked : function ( ) {
2018-11-07 17:31:21 +01:00
return new Promise ( ( resolve ) => {
isUnlockedHandle = ( { data : { isUnlocked } } ) => {
resolve ( ! ! isUnlocked )
2018-10-30 00:09:21 +01:00
}
2018-11-07 17:18:21 +01:00
onMessage ( 'metamaskisunlocked' , isUnlockedHandle , true )
2018-10-29 21:55:13 +01:00
window . postMessage ( { type : 'METAMASK_IS_UNLOCKED' } , '*' )
2018-10-18 00:38:31 +02:00
} )
2018-10-22 21:08:26 +02:00
} ,
} , {
2018-11-17 01:54:55 +01:00
get : function ( obj , prop ) {
2018-10-29 23:51:29 +01:00
! warned && console . warn ( 'Heads up! ethereum._metamask exposes methods that have ' +
2018-10-22 21:08:26 +02:00
'not been standardized yet. This means that these methods may not be implemented ' +
2018-10-29 23:51:29 +01:00
'in other dapp browsers and may be removed from MetaMask in the future.' )
2018-10-22 21:08:26 +02:00
warned = true
return obj [ prop ]
} ,
} )
2018-10-18 00:29:40 +02:00
2018-10-08 22:54:13 +02:00
// 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 ,
} )
2019-02-20 01:42:08 +01:00
window . ethereum = createStandardProvider ( proxiedInpageProvider )
2018-09-15 01:26:03 +02:00
2018-11-05 15:13:22 +01:00
// detect eth_requestAccounts and pipe to enable for now
2018-11-17 01:54:55 +01:00
function detectAccountRequest ( method ) {
2018-11-05 15:13:22 +01:00
const originalMethod = inpageProvider [ method ]
inpageProvider [ method ] = function ( { method } ) {
if ( method === 'eth_requestAccounts' ) {
return window . ethereum . enable ( )
}
return originalMethod . apply ( this , arguments )
}
}
detectAccountRequest ( 'send' )
detectAccountRequest ( 'sendAsync' )
2016-04-15 06:11:35 +02:00
//
2016-05-06 01:04:43 +02:00
// setup web3
2016-04-15 06:11:35 +02:00
//
2017-10-31 17:59:26 +01:00
if ( typeof window . web3 !== 'undefined' ) {
throw new Error ( ` MetaMask detected another web3.
MetaMask will not work reliably with another web3 extension .
This usually happens if you have two MetaMasks installed ,
or MetaMask and another web3 extension . Please remove one
and try again . ` )
}
2018-09-26 16:48:17 +02:00
2019-02-12 15:02:24 +01:00
const web3 = new Web3 ( proxiedInpageProvider )
2016-06-21 22:18:32 +02:00
web3 . setProvider = function ( ) {
2017-10-26 00:45:26 +02:00
log . debug ( 'MetaMask - overrode web3.setProvider' )
2016-04-15 06:11:35 +02:00
}
2017-10-26 00:45:26 +02:00
log . debug ( 'MetaMask - injected web3' )
2018-06-12 18:28:50 +02:00
2018-06-15 00:15:23 +02:00
setupDappAutoReload ( web3 , inpageProvider . publicConfigStore )
2018-06-12 18:28:50 +02:00
// export global web3, with usage-detection and deprecation warning
2018-06-15 00:15:23 +02:00
/ * T O D O : U n c o m m e n t t h i s a r e a o n c e a u t o - r e l o a d . j s h a s b e e n d e p r e c a t e d :
2018-06-12 20:04:37 +02:00
let hasBeenWarned = false
global . web3 = new Proxy ( web3 , {
get : ( _web3 , key ) => {
// show warning once on web3 access
if ( ! hasBeenWarned && key !== 'currentProvider' ) {
console . warn ( 'MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation' )
hasBeenWarned = true
}
// return value normally
return _web3 [ key ]
} ,
set : ( _web3 , key , value ) => {
// set value normally
_web3 [ key ] = value
} ,
} )
2018-06-15 00:15:23 +02:00
* /
2016-04-15 22:04:17 +02:00
2017-01-31 00:25:12 +01:00
// set web3 defaultAccount
2016-06-21 22:18:32 +02:00
inpageProvider . publicConfigStore . subscribe ( function ( state ) {
2017-01-31 00:38:32 +01:00
web3 . eth . defaultAccount = state . selectedAddress
2016-04-15 22:04:17 +02:00
} )
2016-04-30 00:38:24 +02:00
// 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...
2019-02-12 15:02:24 +01:00
let _ _define
2016-04-30 00:38:24 +02:00
2018-04-18 23:02:08 +02:00
/ * *
2018-04-19 03:30:13 +02:00
* Caches reference to global define object and deletes it to
* avoid conflicts with other global define objects , such as
* AMD ' s define function
2018-04-18 23:02:08 +02:00
* /
2016-06-21 22:18:32 +02:00
function cleanContextForImports ( ) {
2016-04-30 00:38:24 +02:00
_ _define = global . define
2016-07-19 02:57:23 +02:00
try {
2016-08-23 04:22:12 +02:00
global . define = undefined
2016-07-19 02:57:23 +02:00
} catch ( _ ) {
console . warn ( 'MetaMask - global.define could not be deleted.' )
}
2016-04-30 00:38:24 +02:00
}
2018-04-18 23:02:08 +02:00
/ * *
* Restores global define object from cached reference
* /
2016-06-21 22:18:32 +02:00
function restoreContextAfterImports ( ) {
2016-07-19 02:57:23 +02:00
try {
global . define = _ _define
2016-07-19 03:08:29 +02:00
} catch ( _ ) {
2016-07-19 02:57:23 +02:00
console . warn ( 'MetaMask - global.define could not be overwritten.' )
}
2016-05-06 01:04:43 +02:00
}