Merge pull request #337 from poanetwork/vb-eip-1193-final
Refactoring before EIP-1193
This commit is contained in:
commit
9ed5e6d6a3
|
@ -92,7 +92,7 @@ workflows:
|
|||
jobs:
|
||||
prep-deps-npm:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -108,7 +108,7 @@ jobs:
|
|||
|
||||
prep-build:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -127,7 +127,7 @@ jobs:
|
|||
|
||||
prep-docs:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -142,7 +142,7 @@ jobs:
|
|||
|
||||
prep-scss:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -161,7 +161,7 @@ jobs:
|
|||
|
||||
test-lint:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -172,7 +172,7 @@ jobs:
|
|||
|
||||
# test-deps:
|
||||
# docker:
|
||||
# - image: circleci/node:12.15.0-browsers
|
||||
# - image: circleci/node:10.19.0-browsers
|
||||
# steps:
|
||||
# - checkout
|
||||
# - restore_cache:
|
||||
|
@ -183,7 +183,7 @@ jobs:
|
|||
|
||||
test-e2e-chrome:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -208,7 +208,7 @@ jobs:
|
|||
|
||||
test-e2e-firefox:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -229,7 +229,7 @@ jobs:
|
|||
|
||||
test-e2e-beta-chrome:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -245,7 +245,7 @@ jobs:
|
|||
|
||||
test-e2e-beta-firefox:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -266,7 +266,7 @@ jobs:
|
|||
|
||||
job-screens:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -283,7 +283,7 @@ jobs:
|
|||
|
||||
job-publish-prerelease:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -310,7 +310,7 @@ jobs:
|
|||
|
||||
job-publish-release:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -323,7 +323,7 @@ jobs:
|
|||
|
||||
job-publish-postrelease:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -340,7 +340,7 @@ jobs:
|
|||
|
||||
test-unit:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -353,7 +353,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Firefox"]'
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -371,7 +371,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Chrome"]'
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -384,7 +384,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Firefox"]'
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -402,7 +402,7 @@ jobs:
|
|||
environment:
|
||||
browsers: '["Chrome"]'
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
@ -413,7 +413,7 @@ jobs:
|
|||
|
||||
all-tests-pass:
|
||||
docker:
|
||||
- image: circleci/node:12.15.0-browsers
|
||||
- image: circleci/node:10.19.0-browsers
|
||||
steps:
|
||||
- run:
|
||||
name: All Tests Passed
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## Building locally
|
||||
|
||||
- Install [Node.js](https://nodejs.org/en/) version 12.x.x and npm version 6.14.2
|
||||
- Install [Node.js](https://nodejs.org/en/) version 10.x.x and npm version 6.13.4
|
||||
- If you are using [nvm](https://github.com/creationix/nvm#installation) (recommended) running `nvm use` will automatically choose the right node version for you.
|
||||
- Select npm 6.9.0: ```npm install -g npm@6.9.0```
|
||||
- Install dependencies: ```npm install```
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
// this needs to run before anything else
|
||||
require('./lib/setupFetchDebugging')()
|
||||
|
||||
const urlUtil = require('url')
|
||||
const endOfStream = require('end-of-stream')
|
||||
const pump = require('pump')
|
||||
const debounce = require('debounce-stream')
|
||||
|
@ -29,7 +28,6 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
|
|||
const EdgeEncryptor = require('./edge-encryptor')
|
||||
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
|
||||
const getObjStructure = require('./lib/getObjStructure')
|
||||
const ipfsContent = require('./lib/ipfsContent.js')
|
||||
|
||||
const {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
|
@ -59,7 +57,6 @@ const isIE = !!document.documentMode
|
|||
// Edge 20+
|
||||
const isEdge = !isIE && !!window.StyleMedia
|
||||
|
||||
let ipfsHandle
|
||||
let popupIsOpen = false
|
||||
let notificationIsOpen = false
|
||||
const openMetamaskTabsIDs = {}
|
||||
|
@ -166,7 +163,6 @@ async function initialize () {
|
|||
const initLangCode = await getFirstPreferredLangCode()
|
||||
await setupController(initState, initLangCode)
|
||||
log.debug('Nifty Wallet initialization complete.')
|
||||
ipfsHandle = ipfsContent(initState.NetworkController.provider)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -271,11 +267,6 @@ function setupController (initState, initLangCode) {
|
|||
})
|
||||
global.metamaskController = controller
|
||||
|
||||
controller.networkController.on('networkDidChange', () => {
|
||||
ipfsHandle && ipfsHandle.remove()
|
||||
ipfsHandle = ipfsContent(controller.networkController.providerStore.getState())
|
||||
})
|
||||
|
||||
// report failed transactions to Sentry
|
||||
controller.txController.on(`tx:status-update`, (txId, status) => {
|
||||
if (status !== 'failed') return
|
||||
|
@ -295,7 +286,7 @@ function setupController (initState, initLangCode) {
|
|||
createStreamSink(persistData),
|
||||
(error) => {
|
||||
log.error('Nifty Wallet - Persistence pipeline failed', error)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
|
@ -397,9 +388,8 @@ function setupController (initState, initLangCode) {
|
|||
|
||||
// communication with page or other extension
|
||||
function connectExternal (remotePort) {
|
||||
const originDomain = urlUtil.parse(remotePort.sender.url).hostname
|
||||
const portStream = new PortStream(remotePort)
|
||||
controller.setupUntrustedCommunication(portStream, originDomain)
|
||||
controller.setupUntrustedCommunication(portStream, remotePort.sender)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -410,6 +400,8 @@ function setupController (initState, initLangCode) {
|
|||
controller.txController.on('update:badge', updateBadge)
|
||||
controller.messageManager.on('updateBadge', updateBadge)
|
||||
controller.personalMessageManager.on('updateBadge', updateBadge)
|
||||
controller.decryptMessageManager.on('updateBadge', updateBadge)
|
||||
controller.encryptionPublicKeyManager.on('updateBadge', updateBadge)
|
||||
controller.typedMessageManager.on('updateBadge', updateBadge)
|
||||
|
||||
/**
|
||||
|
@ -417,12 +409,15 @@ function setupController (initState, initLangCode) {
|
|||
* The number reflects the current number of pending transactions or message signatures needing user approval.
|
||||
*/
|
||||
function updateBadge () {
|
||||
var label = ''
|
||||
var unapprovedTxCount = controller.txController.getUnapprovedTxCount()
|
||||
var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
|
||||
var unapprovedPersonalMsgs = controller.personalMessageManager.unapprovedPersonalMsgCount
|
||||
var unapprovedTypedMsgs = controller.typedMessageManager.unapprovedTypedMessagesCount
|
||||
var count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgs + unapprovedTypedMsgs
|
||||
let label = ''
|
||||
const unapprovedTxCount = controller.txController.getUnapprovedTxCount()
|
||||
const unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
|
||||
const unapprovedPersonalMsgCount = controller.personalMessageManager.unapprovedPersonalMsgCount
|
||||
const unapprovedDecryptMsgCount = controller.decryptMessageManager.unapprovedDecryptMsgCount
|
||||
const unapprovedEncryptionPublicKeyMsgCount = controller.encryptionPublicKeyManager.unapprovedEncryptionPublicKeyMsgCount
|
||||
const unapprovedTypedMessagesCount = controller.typedMessageManager.unapprovedTypedMessagesCount
|
||||
const count = unapprovedTxCount + unapprovedMsgCount + unapprovedPersonalMsgCount + unapprovedDecryptMsgCount + unapprovedEncryptionPublicKeyMsgCount +
|
||||
unapprovedTypedMessagesCount
|
||||
if (count) {
|
||||
label = String(count)
|
||||
}
|
||||
|
@ -463,12 +458,12 @@ function showWatchAssetUi () {
|
|||
triggerUi()
|
||||
return new Promise(
|
||||
(resolve) => {
|
||||
var interval = setInterval(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (!notificationIsOpen) {
|
||||
clearInterval(interval)
|
||||
resolve()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ function setupStreams () {
|
|||
pageStream,
|
||||
pluginStream,
|
||||
pageStream,
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Contentscript Forwarding', err)
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Contentscript Forwarding', err),
|
||||
)
|
||||
|
||||
// setup local multistream channels
|
||||
|
@ -70,13 +70,13 @@ function setupStreams () {
|
|||
mux,
|
||||
pageStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Inpage', err)
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Inpage', err),
|
||||
)
|
||||
pump(
|
||||
mux,
|
||||
pluginStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Background', err)
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet Background', err),
|
||||
)
|
||||
|
||||
// connect ping stream
|
||||
|
@ -85,7 +85,7 @@ function setupStreams () {
|
|||
mux,
|
||||
pongStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet PingPongStream', err)
|
||||
(err) => logStreamDisconnectWarning('Nifty Wallet PingPongStream', err),
|
||||
)
|
||||
|
||||
// connect phishing warning stream
|
||||
|
|
|
@ -32,6 +32,7 @@ class PreferencesController {
|
|||
tokens: [],
|
||||
suggestedTokens: {},
|
||||
useBlockie: false,
|
||||
usePhishDetect: true,
|
||||
featureFlags: {},
|
||||
currentLocale: opts.initLangCode,
|
||||
identities: {},
|
||||
|
@ -77,6 +78,16 @@ class PreferencesController {
|
|||
this.store.updateState({ useBlockie: val })
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the `usePhishDetect` property
|
||||
*
|
||||
* @param {boolean} val - Whether or not the user prefers phishing domain protection
|
||||
*
|
||||
*/
|
||||
setUsePhishDetect (val) {
|
||||
this.store.updateState({ usePhishDetect: val })
|
||||
}
|
||||
|
||||
getSuggestedTokens () {
|
||||
return this.store.getState().suggestedTokens
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
const MessageManager = require('./lib/message-manager')
|
||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||
const TypedMessageManager = require('./lib/typed-message-manager')
|
||||
|
||||
class UserActionController {
|
||||
|
||||
constructor (opts = {}) {
|
||||
|
||||
this.messageManager = new MessageManager()
|
||||
this.personalMessageManager = new PersonalMessageManager()
|
||||
this.typedMessageManager = new TypedMessageManager()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = UserActionController
|
|
@ -1,10 +1,45 @@
|
|||
/*global Web3*/
|
||||
|
||||
// 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
|
||||
*/
|
||||
const cleanContextForImports = () => {
|
||||
__define = global.define
|
||||
try {
|
||||
global.define = undefined
|
||||
} catch (_) {
|
||||
console.warn('Nifty Wallet - global.define could not be deleted.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores global define object from cached reference
|
||||
*/
|
||||
const restoreContextAfterImports = () => {
|
||||
try {
|
||||
global.define = __define
|
||||
} catch (_) {
|
||||
console.warn('Nifty Wallet - global.define could not be overwritten.')
|
||||
}
|
||||
}
|
||||
|
||||
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('nifty-wallet-inpage-provider')
|
||||
|
||||
import log from 'loglevel'
|
||||
import LocalMessageDuplexStream from 'post-message-stream'
|
||||
import MetamaskInpageProvider from 'nifty-wallet-inpage-provider'
|
||||
|
||||
// TODO:deprecate:Q1-2020
|
||||
import 'web3/dist/web3.min.js'
|
||||
|
||||
import setupDappAutoReload from './lib/auto-reload.js'
|
||||
|
||||
restoreContextAfterImports()
|
||||
|
||||
|
@ -15,13 +50,14 @@ log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
|
|||
//
|
||||
|
||||
// setup background connection
|
||||
var metamaskStream = new LocalMessageDuplexStream({
|
||||
const metamaskStream = new LocalMessageDuplexStream({
|
||||
name: 'nifty-inpage',
|
||||
target: 'nifty-contentscript',
|
||||
})
|
||||
|
||||
// compose the inpage provider
|
||||
var inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
const inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
|
||||
// set a high max listener count to avoid unnecesary warnings
|
||||
inpageProvider.setMaxListeners(100)
|
||||
|
||||
|
@ -43,79 +79,35 @@ inpageProvider.enable = function (options = {}) {
|
|||
}
|
||||
|
||||
// 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
|
||||
// `sendAsync` method on the prototype, causing `this` reference issues
|
||||
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 = proxiedInpageProvider
|
||||
|
||||
//
|
||||
// TODO:deprecate:Q1-2020
|
||||
//
|
||||
|
||||
// setup web3
|
||||
//
|
||||
|
||||
var web3 = new Web3(inpageProvider)
|
||||
const web3 = new Web3(proxiedInpageProvider)
|
||||
web3.setProvider = function () {
|
||||
log.debug('Nifty Wallet - overrode web3.setProvider')
|
||||
}
|
||||
log.debug('Nifty Wallet - injected web3')
|
||||
|
||||
proxiedInpageProvider._web3Ref = web3.eth
|
||||
|
||||
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
|
||||
|
||||
// export global web3, with usage-detection and deprecation warning
|
||||
|
||||
/* TODO: Uncomment this area once auto-reload.js has been deprecated:
|
||||
let hasBeenWarned = false
|
||||
global.web3 = new Proxy(web3, {
|
||||
get: (_web3, key) => {
|
||||
// show warning once on web3 access
|
||||
if (!hasBeenWarned && key !== 'currentProvider') {
|
||||
console.warn('Nifty Wallet: 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
|
||||
},
|
||||
})
|
||||
*/
|
||||
|
||||
// set web3 defaultAccount
|
||||
inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
web3.eth.defaultAccount = state.selectedAddress
|
||||
})
|
||||
//
|
||||
// end deprecate:Q1-2020
|
||||
//
|
||||
|
||||
// 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...
|
||||
var __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('Nifty Wallet - global.define could not be deleted.')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores global define object from cached reference
|
||||
*/
|
||||
function restoreContextAfterImports () {
|
||||
try {
|
||||
global.define = __define
|
||||
} catch (_) {
|
||||
console.warn('Nifty Wallet - global.define could not be overwritten.')
|
||||
}
|
||||
}
|
||||
window.ethereum = proxiedInpageProvider
|
||||
|
|
|
@ -40,7 +40,9 @@ class ComposableObservableStore extends ObservableStore {
|
|||
getFlatState () {
|
||||
let flatState = {}
|
||||
for (const key in this.config) {
|
||||
flatState = { ...flatState, ...this.config[key].getState() }
|
||||
const controller = this.config[key]
|
||||
const state = controller.getState ? controller.getState() : controller.state
|
||||
flatState = { ...flatState, ...state }
|
||||
}
|
||||
return flatState
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
|
||||
// TODO:deprecate:Q1-2020
|
||||
|
||||
module.exports = setupDappAutoReload
|
||||
|
||||
function setupDappAutoReload (web3, observable) {
|
||||
|
@ -5,11 +8,17 @@ function setupDappAutoReload (web3, observable) {
|
|||
let reloadInProgress = false
|
||||
let lastTimeUsed
|
||||
let lastSeenNetwork
|
||||
let hasBeenWarned = false
|
||||
|
||||
global.web3 = new Proxy(web3, {
|
||||
get: (_web3, key) => {
|
||||
// get the time of use
|
||||
lastTimeUsed = Date.now()
|
||||
// show warning once on web3 access
|
||||
if (!hasBeenWarned && key !== 'currentProvider') {
|
||||
console.warn(`MetaMask: In Q1 2020, MetaMask will no longer inject web3. For more information, see: https://medium.com/metamask/no-longer-injecting-web3-js-4a899ad6e59e`)
|
||||
hasBeenWarned = true
|
||||
}
|
||||
// return value normally
|
||||
return _web3[key]
|
||||
},
|
||||
|
@ -20,8 +29,16 @@ function setupDappAutoReload (web3, observable) {
|
|||
})
|
||||
|
||||
observable.subscribe(function (state) {
|
||||
// if the auto refresh on network change is false do not
|
||||
// do anything
|
||||
if (!window.ethereum.autoRefreshOnNetworkChange) {
|
||||
return
|
||||
}
|
||||
|
||||
// if reload in progress, no need to check reload logic
|
||||
if (reloadInProgress) return
|
||||
if (reloadInProgress) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentNetwork = state.networkVersion
|
||||
|
||||
|
@ -32,10 +49,14 @@ function setupDappAutoReload (web3, observable) {
|
|||
}
|
||||
|
||||
// skip reload logic if web3 not used
|
||||
if (!lastTimeUsed) return
|
||||
if (!lastTimeUsed) {
|
||||
return
|
||||
}
|
||||
|
||||
// if network did not change, exit
|
||||
if (currentNetwork === lastSeenNetwork) return
|
||||
if (currentNetwork === lastSeenNetwork) {
|
||||
return
|
||||
}
|
||||
|
||||
// initiate page reload
|
||||
reloadInProgress = true
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
module.exports = createTabIdMiddleware
|
||||
|
||||
/**
|
||||
* Returns a middleware that appends the DApp TabId to the request
|
||||
* @param {{ tabId: number }} opts - The middleware options
|
||||
* @returns {Function}
|
||||
*/
|
||||
function createTabIdMiddleware (opts) {
|
||||
return function tabIdMiddleware (/** @type {any} */ req, /** @type {any} */ _, /** @type {Function} */ next) {
|
||||
req.tabId = opts.tabId
|
||||
next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
import EventEmitter from 'events'
|
||||
import ObservableStore from 'obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import createId from './random-id'
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/g
|
||||
import log from 'loglevel'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_decrypt' type decryption request. These are created when a
|
||||
* decryption for an eth_decrypt call is requested.
|
||||
*
|
||||
* @typedef {Object} DecryptMessage
|
||||
* @property {number} id An id to track and identify the message object
|
||||
* @property {Object} msgParams The parameters to pass to the decryptMessage method once the decryption request is
|
||||
* approved.
|
||||
* @property {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the decryption request
|
||||
* @property {number} time The epoch time at which the this message was created
|
||||
* @property {string} status Indicates whether the decryption request is 'unapproved', 'approved', 'decrypted' or 'rejected'
|
||||
* @property {string} type The json-prc decryption method for which a decryption request has been made. A 'Message' will
|
||||
* always have a 'eth_decrypt' type.
|
||||
*
|
||||
*/
|
||||
|
||||
export default class DecryptMessageManager extends EventEmitter {
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - DecryptMessage.
|
||||
*
|
||||
* @typedef {Object} DecryptMessageManager
|
||||
* @property {Object} memStore The observable store where DecryptMessage are saved with persistance.
|
||||
* @property {Object} memStore.unapprovedDecryptMsgs A collection of all DecryptMessages in the 'unapproved' state
|
||||
* @property {number} memStore.unapprovedDecryptMsgCount The count of all DecryptMessages in this.memStore.unapprobedMsgs
|
||||
* @property {array} messages Holds all messages that have been created by this DecryptMessageManager
|
||||
*
|
||||
*/
|
||||
constructor () {
|
||||
super()
|
||||
this.memStore = new ObservableStore({
|
||||
unapprovedDecryptMsgs: {},
|
||||
unapprovedDecryptMsgCount: 0,
|
||||
})
|
||||
this.messages = []
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the number of 'unapproved' DecryptMessages in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' DecryptMessages in this.messages
|
||||
*
|
||||
*/
|
||||
get unapprovedDecryptMsgCount () {
|
||||
return Object.keys(this.getUnapprovedMsgs()).length
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the 'unapproved' DecryptMessages in this.messages
|
||||
*
|
||||
* @returns {Object} An index of DecryptMessage ids to DecryptMessages, for all 'unapproved' DecryptMessages in
|
||||
* this.messages
|
||||
*
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg; return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DecryptMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new DecryptMessage to this.messages, and to save the unapproved DecryptMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_decrypt call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {Promise<Buffer>} The raw decrypted message contents
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessageAsync (msgParams, req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!msgParams.from) {
|
||||
reject(new Error('MetaMask Message for Decryption: from field is required.'))
|
||||
}
|
||||
const msgId = this.addUnapprovedMessage(msgParams, req)
|
||||
this.once(`${msgId}:finished`, (data) => {
|
||||
switch (data.status) {
|
||||
case 'decrypted':
|
||||
return resolve(data.rawData)
|
||||
case 'rejected':
|
||||
return reject(ethErrors.provider.userRejectedRequest('MetaMask Message for Decryption: User denied message decryption.'))
|
||||
case 'errored':
|
||||
return reject(new Error('This message cannot be decrypted'))
|
||||
default:
|
||||
return reject(new Error(`MetaMask Message for Decryption: Unknown problem: ${JSON.stringify(msgParams)}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DecryptMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new DecryptMessage to this.messages, and to save the unapproved DecryptMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_decryptMsg call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {number} The id of the newly created DecryptMessage.
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessage (msgParams, req) {
|
||||
log.debug(`DecryptMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
||||
// add origin from request
|
||||
if (req) {
|
||||
msgParams.origin = req.origin
|
||||
}
|
||||
msgParams.data = this.normalizeMsgData(msgParams.data)
|
||||
// create txData obj with parameters and meta data
|
||||
const time = (new Date()).getTime()
|
||||
const msgId = createId()
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams: msgParams,
|
||||
time: time,
|
||||
status: 'unapproved',
|
||||
type: 'eth_decrypt',
|
||||
}
|
||||
this.addMsg(msgData)
|
||||
|
||||
// signal update
|
||||
this.emit('update')
|
||||
return msgId
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a passed DecryptMessage to this.messages, and calls this._saveMsgList() to save the unapproved DecryptMessages from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg The DecryptMessage to add to this.messages
|
||||
*
|
||||
*/
|
||||
addMsg (msg) {
|
||||
this.messages.push(msg)
|
||||
this._saveMsgList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specified DecryptMessage.
|
||||
*
|
||||
* @param {number} msgId The id of the DecryptMessage to get
|
||||
* @returns {DecryptMessage|undefined} The DecryptMessage with the id that matches the passed msgId, or undefined
|
||||
* if no DecryptMessage has that id.
|
||||
*
|
||||
*/
|
||||
getMsg (msgId) {
|
||||
return this.messages.find((msg) => msg.id === msgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a DecryptMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
||||
* with the message params modified for proper decryption.
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to be used when eth_decryptMsg is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
*
|
||||
*/
|
||||
approveMessage (msgParams) {
|
||||
this.setMsgStatusApproved(msgParams.metamaskId)
|
||||
return this.prepMsgForDecryption(msgParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the DecryptMessage to approve.
|
||||
*
|
||||
*/
|
||||
setMsgStatusApproved (msgId) {
|
||||
this._setMsgStatus(msgId, 'approved')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'decrypted' via a call to this._setMsgStatus and updates that DecryptMessage in
|
||||
* this.messages by adding the raw decryption data of the decryption request to the DecryptMessage
|
||||
*
|
||||
* @param {number} msgId The id of the DecryptMessage to decrypt.
|
||||
* @param {buffer} rawData The raw data of the message request
|
||||
*
|
||||
*/
|
||||
setMsgStatusDecrypted (msgId, rawData) {
|
||||
const msg = this.getMsg(msgId)
|
||||
msg.rawData = rawData
|
||||
this._updateMsg(msg)
|
||||
this._setMsgStatus(msgId, 'decrypted')
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
*
|
||||
*/
|
||||
prepMsgForDecryption (msgParams) {
|
||||
delete msgParams.metamaskId
|
||||
return Promise.resolve(msgParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the DecryptMessage to reject.
|
||||
*
|
||||
*/
|
||||
rejectMsg (msgId) {
|
||||
this._setMsgStatus(msgId, 'rejected')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to error
|
||||
*
|
||||
*/
|
||||
errorMessage (msgId, error) {
|
||||
const msg = this.getMsg(msgId)
|
||||
msg.error = error
|
||||
this._updateMsg(msg)
|
||||
this._setMsgStatus(msgId, 'errored')
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status of a DecryptMessage in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId The id of the DecryptMessage to update.
|
||||
* @param {string} status The new status of the DecryptMessage.
|
||||
* @throws A 'DecryptMessageManager - DecryptMessage not found for id: "${msgId}".' if there is no DecryptMessage
|
||||
* in this.messages with an id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The DecryptMessage is also fired.
|
||||
* @fires If status is 'rejected' or 'decrypted', an event with a name equal to `${msgId}:finished` is fired along
|
||||
* with the DecryptMessage
|
||||
*
|
||||
*/
|
||||
_setMsgStatus (msgId, status) {
|
||||
const msg = this.getMsg(msgId)
|
||||
if (!msg) {
|
||||
throw new Error('DecryptMessageManager - Message not found for id: "${msgId}".')
|
||||
}
|
||||
msg.status = status
|
||||
this._updateMsg(msg)
|
||||
this.emit(`${msgId}:${status}`, msg)
|
||||
if (status === 'rejected' || status === 'decrypted' || status === 'errored') {
|
||||
this.emit(`${msgId}:finished`, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage in this.messages to the passed DecryptMessage if the ids are equal. Then saves the
|
||||
* unapprovedDecryptMsgs index to storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {msg} DecryptMessage A DecryptMessage that will replace an existing DecryptMessage (with the same
|
||||
* id) in this.messages
|
||||
*
|
||||
*/
|
||||
_updateMsg (msg) {
|
||||
const index = this.messages.findIndex((message) => message.id === msg.id)
|
||||
if (index !== -1) {
|
||||
this.messages[index] = msg
|
||||
}
|
||||
this._saveMsgList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the unapproved DecryptMessages, and their count, to this.memStore
|
||||
*
|
||||
* @private
|
||||
* @fires 'updateBadge'
|
||||
*
|
||||
*/
|
||||
_saveMsgList () {
|
||||
const unapprovedDecryptMsgs = this.getUnapprovedMsgs()
|
||||
const unapprovedDecryptMsgCount = Object.keys(unapprovedDecryptMsgs).length
|
||||
this.memStore.updateState({ unapprovedDecryptMsgs, unapprovedDecryptMsgCount })
|
||||
this.emit('updateBadge')
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
|
||||
*
|
||||
* @param {any} data The buffer data to convert to a hex
|
||||
* @returns {string} A hex string conversion of the buffer data
|
||||
*
|
||||
*/
|
||||
normalizeMsgData (data) {
|
||||
try {
|
||||
const stripped = ethUtil.stripHexPrefix(data)
|
||||
if (stripped.match(hexRe)) {
|
||||
return ethUtil.addHexPrefix(stripped)
|
||||
}
|
||||
} catch (e) {
|
||||
log.debug(`Message was not hex encoded, interpreting as utf8.`)
|
||||
}
|
||||
|
||||
return ethUtil.bufferToHex(Buffer.from(data, 'utf8'))
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,286 @@
|
|||
import EventEmitter from 'events'
|
||||
import ObservableStore from 'obs-store'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import createId from './random-id'
|
||||
import log from 'loglevel'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_getEncryptionPublicKey' type request. These are created when
|
||||
* an eth_getEncryptionPublicKey call is requested.
|
||||
*
|
||||
* @typedef {Object} EncryptionPublicKey
|
||||
* @property {number} id An id to track and identify the message object
|
||||
* @property {Object} msgParams The parameters to pass to the encryptionPublicKey method once the request is
|
||||
* approved.
|
||||
* @property {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the request
|
||||
* @property {number} time The epoch time at which the this message was created
|
||||
* @property {string} status Indicates whether the request is 'unapproved', 'approved', 'received' or 'rejected'
|
||||
* @property {string} type The json-prc method for which a request has been made. A 'Message' will
|
||||
* always have a 'eth_getEncryptionPublicKey' type.
|
||||
*
|
||||
*/
|
||||
|
||||
export default class EncryptionPublicKeyManager extends EventEmitter {
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - EncryptionPublicKey.
|
||||
*
|
||||
* @typedef {Object} EncryptionPublicKeyManager
|
||||
* @property {Object} memStore The observable store where EncryptionPublicKey are saved with persistance.
|
||||
* @property {Object} memStore.unapprovedEncryptionPublicKeyMsgs A collection of all EncryptionPublicKeys in the 'unapproved' state
|
||||
* @property {number} memStore.unapprovedEncryptionPublicKeyMsgCount The count of all EncryptionPublicKeys in this.memStore.unapprobedMsgs
|
||||
* @property {array} messages Holds all messages that have been created by this EncryptionPublicKeyManager
|
||||
*
|
||||
*/
|
||||
constructor () {
|
||||
super()
|
||||
this.memStore = new ObservableStore({
|
||||
unapprovedEncryptionPublicKeyMsgs: {},
|
||||
unapprovedEncryptionPublicKeyMsgCount: 0,
|
||||
})
|
||||
this.messages = []
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the number of 'unapproved' EncryptionPublicKeys in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' EncryptionPublicKeys in this.messages
|
||||
*
|
||||
*/
|
||||
get unapprovedEncryptionPublicKeyMsgCount () {
|
||||
return Object.keys(this.getUnapprovedMsgs()).length
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the 'unapproved' EncryptionPublicKeys in this.messages
|
||||
*
|
||||
* @returns {Object} An index of EncryptionPublicKey ids to EncryptionPublicKeys, for all 'unapproved' EncryptionPublicKeys in
|
||||
* this.messages
|
||||
*
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg; return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EncryptionPublicKey with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new EncryptionPublicKey to this.messages, and to save the unapproved EncryptionPublicKeys from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} address The param for the eth_getEncryptionPublicKey call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {Promise<Buffer>} The raw public key contents
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessageAsync (address, req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!address) {
|
||||
reject(new Error('MetaMask Message for EncryptionPublicKey: address field is required.'))
|
||||
}
|
||||
const msgId = this.addUnapprovedMessage(address, req)
|
||||
this.once(`${msgId}:finished`, (data) => {
|
||||
switch (data.status) {
|
||||
case 'received':
|
||||
return resolve(data.rawData)
|
||||
case 'rejected':
|
||||
return reject(ethErrors.provider.userRejectedRequest('MetaMask Message for EncryptionPublicKey: User denied message EncryptionPublicKey.'))
|
||||
default:
|
||||
return reject(new Error(`MetaMask Message for EncryptionPublicKey: Unknown problem: ${JSON.stringify(address)}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new EncryptionPublicKey with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new EncryptionPublicKey to this.messages, and to save the unapproved EncryptionPublicKeys from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} address The param for the eth_getEncryptionPublicKey call to be made after the message is approved.
|
||||
* @param {Object} _req (optional) The original request object possibly containing the origin
|
||||
* @returns {number} The id of the newly created EncryptionPublicKey.
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessage (address, req) {
|
||||
log.debug(`EncryptionPublicKeyManager addUnapprovedMessage: address`)
|
||||
// create txData obj with parameters and meta data
|
||||
const time = (new Date()).getTime()
|
||||
const msgId = createId()
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams: address,
|
||||
time: time,
|
||||
status: 'unapproved',
|
||||
type: 'eth_getEncryptionPublicKey',
|
||||
}
|
||||
|
||||
if (req) {
|
||||
msgData.origin = req.origin
|
||||
}
|
||||
|
||||
this.addMsg(msgData)
|
||||
|
||||
// signal update
|
||||
this.emit('update')
|
||||
return msgId
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a passed EncryptionPublicKey to this.messages, and calls this._saveMsgList() to save the unapproved EncryptionPublicKeys from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg The EncryptionPublicKey to add to this.messages
|
||||
*
|
||||
*/
|
||||
addMsg (msg) {
|
||||
this.messages.push(msg)
|
||||
this._saveMsgList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specified EncryptionPublicKey.
|
||||
*
|
||||
* @param {number} msgId The id of the EncryptionPublicKey to get
|
||||
* @returns {EncryptionPublicKey|undefined} The EncryptionPublicKey with the id that matches the passed msgId, or undefined
|
||||
* if no EncryptionPublicKey has that id.
|
||||
*
|
||||
*/
|
||||
getMsg (msgId) {
|
||||
return this.messages.find((msg) => msg.id === msgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a EncryptionPublicKey. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
||||
* with any the message params modified for proper providing.
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to be used when eth_getEncryptionPublicKey is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
*
|
||||
*/
|
||||
approveMessage (msgParams) {
|
||||
this.setMsgStatusApproved(msgParams.metamaskId)
|
||||
return this.prepMsgForEncryptionPublicKey(msgParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a EncryptionPublicKey status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the EncryptionPublicKey to approve.
|
||||
*
|
||||
*/
|
||||
setMsgStatusApproved (msgId) {
|
||||
this._setMsgStatus(msgId, 'approved')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a EncryptionPublicKey status to 'received' via a call to this._setMsgStatus and updates that EncryptionPublicKey in
|
||||
* this.messages by adding the raw data of request to the EncryptionPublicKey
|
||||
*
|
||||
* @param {number} msgId The id of the EncryptionPublicKey.
|
||||
* @param {buffer} rawData The raw data of the message request
|
||||
*
|
||||
*/
|
||||
setMsgStatusReceived (msgId, rawData) {
|
||||
const msg = this.getMsg(msgId)
|
||||
msg.rawData = rawData
|
||||
this._updateMsg(msg)
|
||||
this._setMsgStatus(msgId, 'received')
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
*
|
||||
*/
|
||||
prepMsgForEncryptionPublicKey (msgParams) {
|
||||
delete msgParams.metamaskId
|
||||
return Promise.resolve(msgParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a EncryptionPublicKey status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the EncryptionPublicKey to reject.
|
||||
*
|
||||
*/
|
||||
rejectMsg (msgId) {
|
||||
this._setMsgStatus(msgId, 'rejected')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to error
|
||||
*
|
||||
*/
|
||||
errorMessage (msgId, error) {
|
||||
const msg = this.getMsg(msgId)
|
||||
msg.error = error
|
||||
this._updateMsg(msg)
|
||||
this._setMsgStatus(msgId, 'errored')
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status of a EncryptionPublicKey in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId The id of the EncryptionPublicKey to update.
|
||||
* @param {string} status The new status of the EncryptionPublicKey.
|
||||
* @throws A 'EncryptionPublicKeyManager - EncryptionPublicKey not found for id: "${msgId}".' if there is no EncryptionPublicKey
|
||||
* in this.messages with an id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The EncryptionPublicKey is also fired.
|
||||
* @fires If status is 'rejected' or 'received', an event with a name equal to `${msgId}:finished` is fired along
|
||||
* with the EncryptionPublicKey
|
||||
*
|
||||
*/
|
||||
_setMsgStatus (msgId, status) {
|
||||
const msg = this.getMsg(msgId)
|
||||
if (!msg) {
|
||||
throw new Error('EncryptionPublicKeyManager - Message not found for id: "${msgId}".')
|
||||
}
|
||||
msg.status = status
|
||||
this._updateMsg(msg)
|
||||
this.emit(`${msgId}:${status}`, msg)
|
||||
if (status === 'rejected' || status === 'received') {
|
||||
this.emit(`${msgId}:finished`, msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a EncryptionPublicKey in this.messages to the passed EncryptionPublicKey if the ids are equal. Then saves the
|
||||
* unapprovedEncryptionPublicKeyMsgs index to storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {msg} EncryptionPublicKey A EncryptionPublicKey that will replace an existing EncryptionPublicKey (with the same
|
||||
* id) in this.messages
|
||||
*
|
||||
*/
|
||||
_updateMsg (msg) {
|
||||
const index = this.messages.findIndex((message) => message.id === msg.id)
|
||||
if (index !== -1) {
|
||||
this.messages[index] = msg
|
||||
}
|
||||
this._saveMsgList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the unapproved EncryptionPublicKeys, and their count, to this.memStore
|
||||
*
|
||||
* @private
|
||||
* @fires 'updateBadge'
|
||||
*
|
||||
*/
|
||||
_saveMsgList () {
|
||||
const unapprovedEncryptionPublicKeyMsgs = this.getUnapprovedMsgs()
|
||||
const unapprovedEncryptionPublicKeyMsgCount = Object.keys(unapprovedEncryptionPublicKeyMsgs).length
|
||||
this.memStore.updateState({ unapprovedEncryptionPublicKeyMsgs, unapprovedEncryptionPublicKeyMsgCount })
|
||||
this.emit('updateBadge')
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
const ENVIRONMENT_TYPE_POPUP = 'popup'
|
||||
const ENVIRONMENT_TYPE_NOTIFICATION = 'notification'
|
||||
const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen'
|
||||
const ENVIRONMENT_TYPE_BACKGROUND = 'background'
|
||||
|
||||
const PLATFORM_BRAVE = 'Brave'
|
||||
const PLATFORM_CHROME = 'Chrome'
|
||||
|
@ -8,10 +9,11 @@ const PLATFORM_EDGE = 'Edge'
|
|||
const PLATFORM_FIREFOX = 'Firefox'
|
||||
const PLATFORM_OPERA = 'Opera'
|
||||
|
||||
module.exports = {
|
||||
export {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_BACKGROUND,
|
||||
PLATFORM_BRAVE,
|
||||
PLATFORM_CHROME,
|
||||
PLATFORM_EDGE,
|
||||
|
|
|
@ -4,7 +4,7 @@ const allLocales = require('../../_locales/index.json')
|
|||
|
||||
const getPreferredLocales = extension.i18n ? promisify(
|
||||
extension.i18n.getAcceptLanguages,
|
||||
{ errorFirst: false }
|
||||
{ errorFirst: false },
|
||||
) : async () => []
|
||||
|
||||
const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-'))
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const EventEmitter = require('events')
|
||||
const ObservableStore = require('obs-store')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const createId = require('./random-id')
|
||||
import EventEmitter from 'events'
|
||||
import ObservableStore from 'obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import createId from './random-id'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for
|
||||
|
@ -21,7 +22,7 @@ const createId = require('./random-id')
|
|||
*
|
||||
*/
|
||||
|
||||
module.exports = class MessageManager extends EventEmitter {
|
||||
export default class MessageManager extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - Messages.
|
||||
|
@ -34,7 +35,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* @property {array} messages Holds all messages that have been created by this MessageManager
|
||||
*
|
||||
*/
|
||||
constructor (opts) {
|
||||
constructor () {
|
||||
super()
|
||||
this.memStore = new ObservableStore({
|
||||
unapprovedMsgs: {},
|
||||
|
@ -46,7 +47,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the number of 'unapproved' Messages in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' Messages in this.messages
|
||||
* @returns {number} - The number of 'unapproved' Messages in this.messages
|
||||
*
|
||||
*/
|
||||
get unapprovedMsgCount () {
|
||||
|
@ -56,21 +57,23 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the 'unapproved' Messages in this.messages
|
||||
*
|
||||
* @returns {Object} An index of Message ids to Messages, for all 'unapproved' Messages in this.messages
|
||||
* @returns {Object} - An index of Message ids to Messages, for all 'unapproved' Messages in this.messages
|
||||
*
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter(msg => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
return this.messages.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg; return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Message with an 'unapproved' status using the passed msgParams. this.addMsg is called to add the
|
||||
* new Message to this.messages, and to save the unapproved Messages from that list to this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {promise} after signature has been
|
||||
* @returns {promise} - after signature has been
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessageAsync (msgParams, req) {
|
||||
|
@ -82,7 +85,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
case 'signed':
|
||||
return resolve(data.rawSig)
|
||||
case 'rejected':
|
||||
return reject(new Error('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
return reject(ethErrors.provider.userRejectedRequest('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
default:
|
||||
return reject(new Error(`Nifty Wallet Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
|
||||
}
|
||||
|
@ -94,19 +97,21 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* Creates a new Message with an 'unapproved' status using the passed msgParams. this.addMsg is called to add the
|
||||
* new Message to this.messages, and to save the unapproved Messages from that list to this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object where the origin may be specificied
|
||||
* @returns {number} The id of the newly created message.
|
||||
* @returns {number} - The id of the newly created message.
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessage (msgParams, req) {
|
||||
// add origin from request
|
||||
if (req) msgParams.origin = req.origin
|
||||
if (req) {
|
||||
msgParams.origin = req.origin
|
||||
}
|
||||
msgParams.data = normalizeMsgData(msgParams.data)
|
||||
// create txData obj with parameters and meta data
|
||||
var time = (new Date()).getTime()
|
||||
var msgId = createId()
|
||||
var msgData = {
|
||||
const time = (new Date()).getTime()
|
||||
const msgId = createId()
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams: msgParams,
|
||||
time: time,
|
||||
|
@ -124,7 +129,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* Adds a passed Message to this.messages, and calls this._saveMsgList() to save the unapproved Messages from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg The Message to add to this.messages
|
||||
* @param {Message} msg - The Message to add to this.messages
|
||||
*
|
||||
*/
|
||||
addMsg (msg) {
|
||||
|
@ -135,21 +140,21 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* Returns a specified Message.
|
||||
*
|
||||
* @param {number} msgId The id of the Message to get
|
||||
* @returns {Message|undefined} The Message with the id that matches the passed msgId, or undefined if no Message has that id.
|
||||
* @param {number} msgId - The id of the Message to get
|
||||
* @returns {Message|undefined} - The Message with the id that matches the passed msgId, or undefined if no Message has that id.
|
||||
*
|
||||
*/
|
||||
getMsg (msgId) {
|
||||
return this.messages.find(msg => msg.id === msgId)
|
||||
return this.messages.find((msg) => msg.id === msgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a Message. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise with
|
||||
* any the message params modified for proper signing.
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams - The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
* @returns {Promise<object>} - Promises the msgParams object with metamaskId removed.
|
||||
*
|
||||
*/
|
||||
approveMessage (msgParams) {
|
||||
|
@ -160,7 +165,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a Message status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the Message to approve.
|
||||
* @param {number} msgId - The id of the Message to approve.
|
||||
*
|
||||
*/
|
||||
setMsgStatusApproved (msgId) {
|
||||
|
@ -171,8 +176,8 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* Sets a Message status to 'signed' via a call to this._setMsgStatus and updates that Message in this.messages by
|
||||
* adding the raw signature data of the signature request to the Message
|
||||
*
|
||||
* @param {number} msgId The id of the Message to sign.
|
||||
* @param {buffer} rawSig The raw data of the signature request
|
||||
* @param {number} msgId - The id of the Message to sign.
|
||||
* @param {buffer} rawSig - The raw data of the signature request
|
||||
*
|
||||
*/
|
||||
setMsgStatusSigned (msgId, rawSig) {
|
||||
|
@ -185,8 +190,8 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
* @param {Object} msgParams - The msgParams to modify
|
||||
* @returns {Promise<object>} - Promises the msgParams with the metamaskId property removed
|
||||
*
|
||||
*/
|
||||
prepMsgForSigning (msgParams) {
|
||||
|
@ -197,7 +202,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a Message status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the Message to reject.
|
||||
* @param {number} msgId - The id of the Message to reject.
|
||||
*
|
||||
*/
|
||||
rejectMsg (msgId) {
|
||||
|
@ -208,8 +213,8 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* Updates the status of a Message in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId The id of the Message to update.
|
||||
* @param {string} status The new status of the Message.
|
||||
* @param {number} msgId - The id of the Message to update.
|
||||
* @param {string} status - The new status of the Message.
|
||||
* @throws A 'MessageManager - Message not found for id: "${msgId}".' if there is no Message in this.messages with an
|
||||
* id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The Message is also fired.
|
||||
|
@ -218,7 +223,9 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
*/
|
||||
_setMsgStatus (msgId, status) {
|
||||
const msg = this.getMsg(msgId)
|
||||
if (!msg) throw new Error('MessageManager - Message not found for id: "${msgId}".')
|
||||
if (!msg) {
|
||||
throw new Error('MessageManager - Message not found for id: "${msgId}".')
|
||||
}
|
||||
msg.status = status
|
||||
this._updateMsg(msg)
|
||||
this.emit(`${msgId}:${status}`, msg)
|
||||
|
@ -232,7 +239,7 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
* storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {msg} Message A Message that will replace an existing Message (with the same id) in this.messages
|
||||
* @param {msg} Message - A Message that will replace an existing Message (with the same id) in this.messages
|
||||
*
|
||||
*/
|
||||
_updateMsg (msg) {
|
||||
|
@ -262,8 +269,8 @@ module.exports = class MessageManager extends EventEmitter {
|
|||
/**
|
||||
* A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
|
||||
*
|
||||
* @param {any} data The buffer data to convert to a hex
|
||||
* @returns {string} A hex string conversion of the buffer data
|
||||
* @param {any} data - The buffer data to convert to a hex
|
||||
* @returns {string} - A hex string conversion of the buffer data
|
||||
*
|
||||
*/
|
||||
function normalizeMsgData (data) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
const EventEmitter = require('events')
|
||||
const ObservableStore = require('obs-store')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const createId = require('./random-id')
|
||||
import EventEmitter from 'events'
|
||||
import ObservableStore from 'obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import createId from './random-id'
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/g
|
||||
const log = require('loglevel')
|
||||
import log from 'loglevel'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'personal_sign' type signature request. These are created when a
|
||||
|
@ -24,7 +26,7 @@ const log = require('loglevel')
|
|||
*
|
||||
*/
|
||||
|
||||
module.exports = class PersonalMessageManager extends EventEmitter {
|
||||
export default class PersonalMessageManager extends EventEmitter {
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - PersonalMessage.
|
||||
*
|
||||
|
@ -36,7 +38,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* @property {array} messages Holds all messages that have been created by this PersonalMessageManager
|
||||
*
|
||||
*/
|
||||
constructor (opts) {
|
||||
constructor () {
|
||||
super()
|
||||
this.memStore = new ObservableStore({
|
||||
unapprovedPersonalMsgs: {},
|
||||
|
@ -48,7 +50,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the number of 'unapproved' PersonalMessages in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' PersonalMessages in this.messages
|
||||
* @returns {number} - The number of 'unapproved' PersonalMessages in this.messages
|
||||
*
|
||||
*/
|
||||
get unapprovedPersonalMsgCount () {
|
||||
|
@ -58,13 +60,15 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the 'unapproved' PersonalMessages in this.messages
|
||||
*
|
||||
* @returns {Object} An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in
|
||||
* @returns {Object} - An index of PersonalMessage ids to PersonalMessages, for all 'unapproved' PersonalMessages in
|
||||
* this.messages
|
||||
*
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter(msg => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
return this.messages.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg; return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,9 +76,9 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* the new PersonalMessage to this.messages, and to save the unapproved PersonalMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {promise} When the message has been signed or rejected
|
||||
* @returns {promise} - When the message has been signed or rejected
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessageAsync (msgParams, req) {
|
||||
|
@ -88,7 +92,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
case 'signed':
|
||||
return resolve(data.rawSig)
|
||||
case 'rejected':
|
||||
return reject(new Error('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
return reject(ethErrors.provider.userRejectedRequest('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
default:
|
||||
return reject(new Error(`Nifty Wallet Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
|
||||
}
|
||||
|
@ -101,20 +105,22 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* the new PersonalMessage to this.messages, and to save the unapproved PersonalMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {number} The id of the newly created PersonalMessage.
|
||||
* @returns {number} - The id of the newly created PersonalMessage.
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessage (msgParams, req) {
|
||||
log.debug(`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
||||
// add origin from request
|
||||
if (req) msgParams.origin = req.origin
|
||||
if (req) {
|
||||
msgParams.origin = req.origin
|
||||
}
|
||||
msgParams.data = this.normalizeMsgData(msgParams.data)
|
||||
// create txData obj with parameters and meta data
|
||||
var time = (new Date()).getTime()
|
||||
var msgId = createId()
|
||||
var msgData = {
|
||||
const time = (new Date()).getTime()
|
||||
const msgId = createId()
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams: msgParams,
|
||||
time: time,
|
||||
|
@ -132,7 +138,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* Adds a passed PersonalMessage to this.messages, and calls this._saveMsgList() to save the unapproved PersonalMessages from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg The PersonalMessage to add to this.messages
|
||||
* @param {Message} msg - The PersonalMessage to add to this.messages
|
||||
*
|
||||
*/
|
||||
addMsg (msg) {
|
||||
|
@ -143,22 +149,22 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Returns a specified PersonalMessage.
|
||||
*
|
||||
* @param {number} msgId The id of the PersonalMessage to get
|
||||
* @returns {PersonalMessage|undefined} The PersonalMessage with the id that matches the passed msgId, or undefined
|
||||
* @param {number} msgId - The id of the PersonalMessage to get
|
||||
* @returns {PersonalMessage|undefined} - The PersonalMessage with the id that matches the passed msgId, or undefined
|
||||
* if no PersonalMessage has that id.
|
||||
*
|
||||
*/
|
||||
getMsg (msgId) {
|
||||
return this.messages.find(msg => msg.id === msgId)
|
||||
return this.messages.find((msg) => msg.id === msgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a PersonalMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
||||
* with any the message params modified for proper signing.
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams - The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
* @returns {Promise<object>} - Promises the msgParams object with metamaskId removed.
|
||||
*
|
||||
*/
|
||||
approveMessage (msgParams) {
|
||||
|
@ -169,7 +175,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a PersonalMessage status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the PersonalMessage to approve.
|
||||
* @param {number} msgId - The id of the PersonalMessage to approve.
|
||||
*
|
||||
*/
|
||||
setMsgStatusApproved (msgId) {
|
||||
|
@ -180,8 +186,8 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* Sets a PersonalMessage status to 'signed' via a call to this._setMsgStatus and updates that PersonalMessage in
|
||||
* this.messages by adding the raw signature data of the signature request to the PersonalMessage
|
||||
*
|
||||
* @param {number} msgId The id of the PersonalMessage to sign.
|
||||
* @param {buffer} rawSig The raw data of the signature request
|
||||
* @param {number} msgId - The id of the PersonalMessage to sign.
|
||||
* @param {buffer} rawSig - The raw data of the signature request
|
||||
*
|
||||
*/
|
||||
setMsgStatusSigned (msgId, rawSig) {
|
||||
|
@ -194,8 +200,8 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
* @param {Object} msgParams - The msgParams to modify
|
||||
* @returns {Promise<object>} - Promises the msgParams with the metamaskId property removed
|
||||
*
|
||||
*/
|
||||
prepMsgForSigning (msgParams) {
|
||||
|
@ -206,7 +212,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a PersonalMessage status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the PersonalMessage to reject.
|
||||
* @param {number} msgId - The id of the PersonalMessage to reject.
|
||||
*
|
||||
*/
|
||||
rejectMsg (msgId) {
|
||||
|
@ -217,8 +223,8 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* Updates the status of a PersonalMessage in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId The id of the PersonalMessage to update.
|
||||
* @param {string} status The new status of the PersonalMessage.
|
||||
* @param {number} msgId - The id of the PersonalMessage to update.
|
||||
* @param {string} status - The new status of the PersonalMessage.
|
||||
* @throws A 'PersonalMessageManager - PersonalMessage not found for id: "${msgId}".' if there is no PersonalMessage
|
||||
* in this.messages with an id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The PersonalMessage is also fired.
|
||||
|
@ -228,7 +234,9 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
*/
|
||||
_setMsgStatus (msgId, status) {
|
||||
const msg = this.getMsg(msgId)
|
||||
if (!msg) throw new Error('PersonalMessageManager - Message not found for id: "${msgId}".')
|
||||
if (!msg) {
|
||||
throw new Error(`PersonalMessageManager - Message not found for id: "${msgId}".`)
|
||||
}
|
||||
msg.status = status
|
||||
this._updateMsg(msg)
|
||||
this.emit(`${msgId}:${status}`, msg)
|
||||
|
@ -242,7 +250,7 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
* unapprovedPersonalMsgs index to storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {msg} PersonalMessage A PersonalMessage that will replace an existing PersonalMessage (with the same
|
||||
* @param {msg} PersonalMessage - A PersonalMessage that will replace an existing PersonalMessage (with the same
|
||||
* id) in this.messages
|
||||
*
|
||||
*/
|
||||
|
@ -271,8 +279,8 @@ module.exports = class PersonalMessageManager extends EventEmitter {
|
|||
/**
|
||||
* A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
|
||||
*
|
||||
* @param {any} data The buffer data to convert to a hex
|
||||
* @returns {string} A hex string conversion of the buffer data
|
||||
* @param {any} data - The buffer data to convert to a hex
|
||||
* @returns {string} - A hex string conversion of the buffer data
|
||||
*
|
||||
*/
|
||||
normalizeMsgData (data) {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import {
|
||||
MAINNET_CHAIN_ID,
|
||||
ROPSTEN_CHAIN_ID,
|
||||
RINKEBY_CHAIN_ID,
|
||||
KOVAN_CHAIN_ID,
|
||||
GOERLI_CHAIN_ID,
|
||||
} from './enums'
|
||||
|
||||
const standardNetworkId = {
|
||||
'1': MAINNET_CHAIN_ID,
|
||||
'3': ROPSTEN_CHAIN_ID,
|
||||
'4': RINKEBY_CHAIN_ID,
|
||||
'42': KOVAN_CHAIN_ID,
|
||||
'5': GOERLI_CHAIN_ID,
|
||||
}
|
||||
|
||||
function selectChainId (metamaskState) {
|
||||
const { network, provider: { chainId } } = metamaskState
|
||||
return standardNetworkId[network] || `0x${parseInt(chainId, 10).toString(16)}`
|
||||
}
|
||||
|
||||
export default selectChainId
|
|
@ -1,36 +1,10 @@
|
|||
const Through = require('through2')
|
||||
const ObjectMultiplex = require('obj-multiplex')
|
||||
const pump = require('pump')
|
||||
import ObjectMultiplex from 'obj-multiplex'
|
||||
import pump from 'pump'
|
||||
|
||||
module.exports = {
|
||||
jsonParseStream: jsonParseStream,
|
||||
jsonStringifyStream: jsonStringifyStream,
|
||||
setupMultiplex: setupMultiplex,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream transform that parses JSON strings passing through
|
||||
* @return {stream.Transform}
|
||||
*/
|
||||
function jsonParseStream () {
|
||||
return Through.obj(function (serialized, _, cb) {
|
||||
this.push(JSON.parse(serialized))
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream transform that calls {@code JSON.stringify}
|
||||
* on objects passing through
|
||||
* @return {stream.Transform} the stream transform
|
||||
*/
|
||||
function jsonStringifyStream () {
|
||||
return Through.obj(function (obj, _, cb) {
|
||||
this.push(JSON.stringify(obj))
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up stream multiplexing for the given stream
|
||||
* @param {any} connectionStream - the stream to mux
|
||||
|
@ -43,8 +17,10 @@ function setupMultiplex (connectionStream) {
|
|||
mux,
|
||||
connectionStream,
|
||||
(err) => {
|
||||
if (err) console.error(err)
|
||||
if (err) {
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
)
|
||||
return mux
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const EventEmitter = require('events')
|
||||
const ObservableStore = require('obs-store')
|
||||
const createId = require('./random-id')
|
||||
const assert = require('assert')
|
||||
const sigUtil = require('eth-sig-util')
|
||||
const log = require('loglevel')
|
||||
const jsonschema = require('jsonschema')
|
||||
import EventEmitter from 'events'
|
||||
import ObservableStore from 'obs-store'
|
||||
import createId from './random-id'
|
||||
import assert from 'assert'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import sigUtil from 'eth-sig-util'
|
||||
import log from 'loglevel'
|
||||
import jsonschema from 'jsonschema'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
|
||||
|
@ -24,7 +25,7 @@ const jsonschema = require('jsonschema')
|
|||
*
|
||||
*/
|
||||
|
||||
module.exports = class TypedMessageManager extends EventEmitter {
|
||||
export default class TypedMessageManager extends EventEmitter {
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - TypedMessage.
|
||||
*/
|
||||
|
@ -41,7 +42,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the number of 'unapproved' TypedMessages in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' TypedMessages in this.messages
|
||||
* @returns {number} - The number of 'unapproved' TypedMessages in this.messages
|
||||
*
|
||||
*/
|
||||
get unapprovedTypedMessagesCount () {
|
||||
|
@ -51,13 +52,15 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* A getter for the 'unapproved' TypedMessages in this.messages
|
||||
*
|
||||
* @returns {Object} An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in
|
||||
* @returns {Object} - An index of TypedMessage ids to TypedMessages, for all 'unapproved' TypedMessages in
|
||||
* this.messages
|
||||
*
|
||||
*/
|
||||
getUnapprovedMsgs () {
|
||||
return this.messages.filter(msg => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => { result[msg.id] = msg; return result }, {})
|
||||
return this.messages.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg; return result
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,9 +68,9 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* the new TypedMessage to this.messages, and to save the unapproved TypedMessages from that list to
|
||||
* this.memStore. Before any of this is done, msgParams are validated
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {promise} When the message has been signed or rejected
|
||||
* @returns {promise} - When the message has been signed or rejected
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessageAsync (msgParams, req, version) {
|
||||
|
@ -78,7 +81,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
case 'signed':
|
||||
return resolve(data.rawSig)
|
||||
case 'rejected':
|
||||
return reject(new Error('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
return reject(ethErrors.provider.userRejectedRequest('Nifty Wallet Message Signature: User denied message signature.'))
|
||||
case 'errored':
|
||||
return reject(new Error(`Nifty Wallet Message Signature: ${data.error}`))
|
||||
default:
|
||||
|
@ -93,22 +96,24 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* the new TypedMessage to this.messages, and to save the unapproved TypedMessages from that list to
|
||||
* this.memStore. Before any of this is done, msgParams are validated
|
||||
*
|
||||
* @param {Object} msgParams The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} msgParams - The params for the eth_sign call to be made after the message is approved.
|
||||
* @param {Object} req (optional) The original request object possibly containing the origin
|
||||
* @returns {number} The id of the newly created TypedMessage.
|
||||
* @returns {number} - The id of the newly created TypedMessage.
|
||||
*
|
||||
*/
|
||||
addUnapprovedMessage (msgParams, req, version) {
|
||||
msgParams.version = version
|
||||
this.validateParams(msgParams)
|
||||
// add origin from request
|
||||
if (req) msgParams.origin = req.origin
|
||||
if (req) {
|
||||
msgParams.origin = req.origin
|
||||
}
|
||||
|
||||
log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
||||
// create txData obj with parameters and meta data
|
||||
var time = (new Date()).getTime()
|
||||
var msgId = createId()
|
||||
var msgData = {
|
||||
const time = (new Date()).getTime()
|
||||
const msgId = createId()
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams: msgParams,
|
||||
time: time,
|
||||
|
@ -125,7 +130,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Helper method for this.addUnapprovedMessage. Validates that the passed params have the required properties.
|
||||
*
|
||||
* @param {Object} params The params to validate
|
||||
* @param {Object} params - The params to validate
|
||||
*
|
||||
*/
|
||||
validateParams (params) {
|
||||
|
@ -141,13 +146,16 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
}, 'Expected EIP712 typed data')
|
||||
break
|
||||
case 'V3':
|
||||
case 'V4':
|
||||
let data
|
||||
assert.equal(typeof params, 'object', 'Params should be an object.')
|
||||
assert.ok('data' in params, 'Params must include a data field.')
|
||||
assert.ok('from' in params, 'Params must include a from field.')
|
||||
assert.equal(typeof params.from, 'string', 'From field must be a string.')
|
||||
assert.equal(typeof params.data, 'string', 'Data must be passed as a valid JSON string.')
|
||||
assert.doesNotThrow(() => { data = JSON.parse(params.data) }, 'Data must be passed as a valid JSON string.')
|
||||
assert.doesNotThrow(() => {
|
||||
data = JSON.parse(params.data)
|
||||
}, 'Data must be passed as a valid JSON string.')
|
||||
const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
|
||||
assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
|
||||
assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.')
|
||||
|
@ -155,6 +163,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
const activeChainId = parseInt(this.networkController.getNetworkState())
|
||||
chainId && assert.equal(chainId, activeChainId, `Provided chainId (${chainId}) must match the active chainId (${activeChainId})`)
|
||||
break
|
||||
default:
|
||||
assert.fail(`Unknown params.version ${params.version}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +172,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* Adds a passed TypedMessage to this.messages, and calls this._saveMsgList() to save the unapproved TypedMessages from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg The TypedMessage to add to this.messages
|
||||
* @param {Message} msg - The TypedMessage to add to this.messages
|
||||
*
|
||||
*/
|
||||
addMsg (msg) {
|
||||
|
@ -173,22 +183,22 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Returns a specified TypedMessage.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to get
|
||||
* @returns {TypedMessage|undefined} The TypedMessage with the id that matches the passed msgId, or undefined
|
||||
* @param {number} msgId - The id of the TypedMessage to get
|
||||
* @returns {TypedMessage|undefined} - The TypedMessage with the id that matches the passed msgId, or undefined
|
||||
* if no TypedMessage has that id.
|
||||
*
|
||||
*/
|
||||
getMsg (msgId) {
|
||||
return this.messages.find(msg => msg.id === msgId)
|
||||
return this.messages.find((msg) => msg.id === msgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a TypedMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
||||
* with any the message params modified for proper signing.
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams - The msgParams to be used when eth_sign is called, plus data added by MetaMask.
|
||||
* @param {Object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
* @returns {Promise<object>} - Promises the msgParams object with metamaskId removed.
|
||||
*
|
||||
*/
|
||||
approveMessage (msgParams) {
|
||||
|
@ -199,7 +209,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a TypedMessage status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to approve.
|
||||
* @param {number} msgId - The id of the TypedMessage to approve.
|
||||
*
|
||||
*/
|
||||
setMsgStatusApproved (msgId) {
|
||||
|
@ -210,8 +220,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* Sets a TypedMessage status to 'signed' via a call to this._setMsgStatus and updates that TypedMessage in
|
||||
* this.messages by adding the raw signature data of the signature request to the TypedMessage
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to sign.
|
||||
* @param {buffer} rawSig The raw data of the signature request
|
||||
* @param {number} msgId - The id of the TypedMessage to sign.
|
||||
* @param {buffer} rawSig - The raw data of the signature request
|
||||
*
|
||||
*/
|
||||
setMsgStatusSigned (msgId, rawSig) {
|
||||
|
@ -224,8 +234,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {Object} msgParams The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
* @param {Object} msgParams - The msgParams to modify
|
||||
* @returns {Promise<object>} - Promises the msgParams with the metamaskId property removed
|
||||
*
|
||||
*/
|
||||
prepMsgForSigning (msgParams) {
|
||||
|
@ -237,7 +247,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a TypedMessage status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to reject.
|
||||
* @param {number} msgId - The id of the TypedMessage to reject.
|
||||
*
|
||||
*/
|
||||
rejectMsg (msgId) {
|
||||
|
@ -247,7 +257,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
/**
|
||||
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId The id of the TypedMessage to error
|
||||
* @param {number} msgId - The id of the TypedMessage to error
|
||||
*
|
||||
*/
|
||||
errorMessage (msgId, error) {
|
||||
|
@ -265,8 +275,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* Updates the status of a TypedMessage in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId The id of the TypedMessage to update.
|
||||
* @param {string} status The new status of the TypedMessage.
|
||||
* @param {number} msgId - The id of the TypedMessage to update.
|
||||
* @param {string} status - The new status of the TypedMessage.
|
||||
* @throws A 'TypedMessageManager - TypedMessage not found for id: "${msgId}".' if there is no TypedMessage
|
||||
* in this.messages with an id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The TypedMessage is also fired.
|
||||
|
@ -276,7 +286,9 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
*/
|
||||
_setMsgStatus (msgId, status) {
|
||||
const msg = this.getMsg(msgId)
|
||||
if (!msg) throw new Error('TypedMessageManager - Message not found for id: "${msgId}".')
|
||||
if (!msg) {
|
||||
throw new Error('TypedMessageManager - Message not found for id: "${msgId}".')
|
||||
}
|
||||
msg.status = status
|
||||
this._updateMsg(msg)
|
||||
this.emit(`${msgId}:${status}`, msg)
|
||||
|
@ -290,7 +302,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
|
|||
* unapprovedTypedMsgs index to storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {msg} TypedMessage A TypedMessage that will replace an existing TypedMessage (with the same
|
||||
* @param {msg} TypedMessage - A TypedMessage that will replace an existing TypedMessage (with the same
|
||||
* id) in this.messages
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const ethUtil = require('ethereumjs-util')
|
||||
const assert = require('assert')
|
||||
const BN = require('bn.js')
|
||||
import extension from 'extensionizer'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import assert from 'assert'
|
||||
import BN from 'bn.js'
|
||||
const {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
|
@ -152,6 +153,24 @@ function capitalizeFirstLetter (msg) {
|
|||
return msg.charAt(0).toUpperCase() + msg.slice(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Error if extension.runtime.lastError is present
|
||||
* this is a workaround for the non-standard error object thats used
|
||||
* @returns {Error}
|
||||
*/
|
||||
function checkForError () {
|
||||
const lastError = extension.runtime.lastError
|
||||
if (!lastError) {
|
||||
return
|
||||
}
|
||||
// if it quacks like an Error, its an Error
|
||||
if (lastError.stack && lastError.message) {
|
||||
return lastError
|
||||
}
|
||||
// repair incomplete error object (eg chromium v77)
|
||||
return new Error(lastError.message)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
removeListeners,
|
||||
applyListeners,
|
||||
|
@ -163,4 +182,5 @@ module.exports = {
|
|||
bnToHex,
|
||||
BnMultiplyByFraction,
|
||||
capitalizeFirstLetter,
|
||||
checkForError,
|
||||
}
|
||||
|
|
|
@ -4,21 +4,24 @@
|
|||
* @license MIT
|
||||
*/
|
||||
|
||||
const EventEmitter = require('events')
|
||||
const pump = require('pump')
|
||||
const Dnode = require('dnode')
|
||||
const ObservableStore = require('obs-store')
|
||||
import EventEmitter from 'events'
|
||||
|
||||
import pump from 'pump'
|
||||
import Dnode from 'dnode'
|
||||
import extension from 'extensionizer'
|
||||
import ObservableStore from 'obs-store'
|
||||
const ComposableObservableStore = require('./lib/ComposableObservableStore')
|
||||
const asStream = require('obs-store/lib/asStream')
|
||||
import asStream from 'obs-store/lib/asStream'
|
||||
const AccountTracker = require('./lib/account-tracker')
|
||||
const RpcEngine = require('json-rpc-engine')
|
||||
const debounce = require('debounce')
|
||||
import RpcEngine from 'json-rpc-engine'
|
||||
import { debounce } from 'lodash'
|
||||
const createEngineStream = require('json-rpc-middleware-stream/engineStream')
|
||||
const createFilterMiddleware = require('eth-json-rpc-filters')
|
||||
const createSubscriptionManager = require('eth-json-rpc-filters/subscriptionManager')
|
||||
const createOriginMiddleware = require('./lib/createOriginMiddleware')
|
||||
const createLoggerMiddleware = require('./lib/createLoggerMiddleware')
|
||||
const createProviderMiddleware = require('./lib/createProviderMiddleware')
|
||||
import createTabIdMiddleware from './lib/createTabIdMiddleware'
|
||||
import providerAsMiddleware from 'eth-json-rpc-middleware/providerAsMiddleware'
|
||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
const KeyringController = require('eth-keychain-controller')
|
||||
const NetworkController = require('./controllers/network')
|
||||
|
@ -28,34 +31,40 @@ const NoticeController = require('./notice-controller')
|
|||
const ShapeShiftController = require('./controllers/shapeshift')
|
||||
const AddressBookController = require('./controllers/address-book')
|
||||
const InfuraController = require('./controllers/infura')
|
||||
const BlacklistController = require('./controllers/blacklist')
|
||||
const CachedBalancesController = require('./controllers/cached-balances')
|
||||
const RecentBlocksController = require('./controllers/recent-blocks')
|
||||
const MessageManager = require('./lib/message-manager')
|
||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||
const TypedMessageManager = require('./lib/typed-message-manager')
|
||||
import MessageManager from './lib/message-manager'
|
||||
import DecryptMessageManager from './lib/decrypt-message-manager'
|
||||
import EncryptionPublicKeyManager from './lib/encryption-public-key-manager'
|
||||
import PersonalMessageManager from './lib/personal-message-manager'
|
||||
import TypedMessageManager from './lib/typed-message-manager'
|
||||
const TransactionController = require('./controllers/transactions')
|
||||
const BalancesController = require('./controllers/computed-balances')
|
||||
const TokenRatesController = require('./controllers/token-rates')
|
||||
const DetectTokensController = require('./controllers/detect-tokens')
|
||||
const nodeify = require('./lib/nodeify')
|
||||
const accountImporter = require('./account-import-strategies')
|
||||
const Mutex = require('await-semaphore').Mutex
|
||||
import { Mutex } from 'await-semaphore'
|
||||
import selectChainId from './lib/select-chain-id'
|
||||
const version = require('../manifest.json').version
|
||||
const BN = require('ethereumjs-util').BN
|
||||
import ethUtil, { BN } from 'ethereumjs-util'
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
const percentile = require('percentile')
|
||||
import percentile from 'percentile'
|
||||
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
|
||||
const log = require('loglevel')
|
||||
import log from 'loglevel'
|
||||
const TrezorKeyring = require('eth-trezor-keyring')
|
||||
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
|
||||
const EthQuery = require('eth-query')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
import EthQuery from 'eth-query'
|
||||
const sigUtil = require('eth-sig-util')
|
||||
import nanoid from 'nanoid'
|
||||
const { importTypes } = require('../../old-ui/app/accounts/import/enums')
|
||||
const { LEDGER, TREZOR } = require('../../old-ui/app/components/connect-hardware/enum')
|
||||
const { ifPOA, ifRSK, getNetworkID, getDPath, setDPath } = require('../../old-ui/app/util')
|
||||
|
||||
import {
|
||||
PhishingController,
|
||||
} from 'gaba'
|
||||
|
||||
const {
|
||||
CLASSIC_CODE,
|
||||
MAINNET_CODE } = require('./controllers/network/enums')
|
||||
|
@ -87,6 +96,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
// observable state store
|
||||
this.store = new ComposableObservableStore(initState)
|
||||
|
||||
// external connections by origin
|
||||
// Do not modify directly. Use the associated methods.
|
||||
this.connections = {}
|
||||
|
||||
// lock to ensure only one vault created at once
|
||||
this.createVaultMutex = new Mutex()
|
||||
|
||||
|
@ -114,8 +127,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
})
|
||||
this.infuraController.scheduleInfuraNetworkCheck()
|
||||
|
||||
this.blacklistController = new BlacklistController()
|
||||
this.blacklistController.scheduleUpdates()
|
||||
this.phishingController = new PhishingController()
|
||||
|
||||
// rpc provider
|
||||
this.initializeProvider()
|
||||
|
@ -137,6 +149,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
this.accountTracker = new AccountTracker({
|
||||
provider: this.provider,
|
||||
blockTracker: this.blockTracker,
|
||||
network: this.networkController,
|
||||
})
|
||||
|
||||
// start and stop polling for balances based on activeControllerConnections
|
||||
|
@ -191,6 +204,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
})
|
||||
|
||||
this.keyringController.memStore.subscribe((s) => this._onKeyringControllerUpdate(s))
|
||||
this.keyringController.on('unlock', () => this.emit('unlock'))
|
||||
|
||||
// detect tokens controller
|
||||
this.detectTokensController = new DetectTokensController({
|
||||
|
@ -251,8 +265,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
this.networkController.lookupNetwork()
|
||||
this.messageManager = new MessageManager()
|
||||
this.personalMessageManager = new PersonalMessageManager()
|
||||
this.decryptMessageManager = new DecryptMessageManager()
|
||||
this.encryptionPublicKeyManager = new EncryptionPublicKeyManager()
|
||||
this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController })
|
||||
this.publicConfigStore = this.initPublicConfigStore()
|
||||
|
||||
// ensure isClientOpenAndUnlocked is updated when memState updates
|
||||
this.on('update', (memState) => {
|
||||
this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
|
||||
})
|
||||
|
||||
this.store.updateStructure({
|
||||
TransactionController: this.txController.store,
|
||||
|
@ -276,6 +296,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
TokenRatesController: this.tokenRatesController.store,
|
||||
MessageManager: this.messageManager.memStore,
|
||||
PersonalMessageManager: this.personalMessageManager.memStore,
|
||||
DecryptMessageManager: this.decryptMessageManager.memStore,
|
||||
EncryptionPublicKeyManager: this.encryptionPublicKeyManager.memStore,
|
||||
TypesMessageManager: this.typedMessageManager.memStore,
|
||||
KeyringController: this.keyringController.memStore,
|
||||
PreferencesController: this.preferencesController.store,
|
||||
|
@ -301,20 +323,23 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
version,
|
||||
// account mgmt
|
||||
getAccounts: async () => {
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
const selectedAddress = this.preferencesController.getSelectedAddress()
|
||||
// only show address if account is unlocked
|
||||
if (isUnlocked && selectedAddress) {
|
||||
if (this.isUnlocked && selectedAddress) {
|
||||
return [selectedAddress]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
return [] // changing this is a breaking change
|
||||
},
|
||||
// tx signing
|
||||
processTransaction: this.newUnapprovedTransaction.bind(this),
|
||||
// msg signing
|
||||
processEthSignMessage: this.newUnsignedMessage.bind(this),
|
||||
processTypedMessage: this.newUnsignedTypedMessage.bind(this),
|
||||
processTypedMessageV3: this.newUnsignedTypedMessage.bind(this),
|
||||
processTypedMessageV4: this.newUnsignedTypedMessage.bind(this),
|
||||
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
|
||||
processDecryptMessage: this.newRequestDecryptMessage.bind(this),
|
||||
processEncryptionPublicKey: this.newRequestEncryptionPublicKey.bind(this),
|
||||
getPendingNonce: this.getPendingNonce.bind(this),
|
||||
}
|
||||
const providerProxy = this.networkController.initializeProvider(providerOpts)
|
||||
|
@ -325,25 +350,30 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* Constructor helper: initialize a public config store.
|
||||
* This store is used to make some config info available to Dapps synchronously.
|
||||
*/
|
||||
initPublicConfigStore () {
|
||||
// get init state
|
||||
createPublicConfigStore () {
|
||||
// subset of state for metamask inpage provider
|
||||
const publicConfigStore = new ObservableStore()
|
||||
|
||||
// memStore -> transform -> publicConfigStore
|
||||
this.on('update', (memState) => {
|
||||
this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
|
||||
const publicState = selectPublicState(memState)
|
||||
publicConfigStore.putState(publicState)
|
||||
})
|
||||
// setup memStore subscription hooks
|
||||
this.on('update', updatePublicConfigStore)
|
||||
updatePublicConfigStore(this.getState())
|
||||
|
||||
function selectPublicState (memState) {
|
||||
const result = {
|
||||
selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
|
||||
networkVersion: memState.network,
|
||||
}
|
||||
return result
|
||||
publicConfigStore.destroy = () => {
|
||||
this.removeEventListener && this.removeEventListener('update', updatePublicConfigStore)
|
||||
}
|
||||
|
||||
function updatePublicConfigStore (memState) {
|
||||
publicConfigStore.putState(selectPublicState(memState))
|
||||
}
|
||||
|
||||
function selectPublicState ({ isUnlocked, network, provider, selectedAddress }) {
|
||||
return {
|
||||
isUnlocked,
|
||||
selectedAddress: isUnlocked ? selectedAddress : undefined,
|
||||
networkVersion: network,
|
||||
chainId: selectChainId({ network, provider }),
|
||||
}
|
||||
}
|
||||
return publicConfigStore
|
||||
}
|
||||
|
||||
|
@ -354,7 +384,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
/**
|
||||
* The metamask-state of the various controllers, made available to the UI
|
||||
*
|
||||
* @returns {Object} status
|
||||
* @returns {Object} - status
|
||||
*/
|
||||
getState () {
|
||||
const vault = this.keyringController.store.getState().vault
|
||||
|
@ -379,11 +409,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
*/
|
||||
getApi () {
|
||||
const keyringController = this.keyringController
|
||||
const networkController = this.networkController
|
||||
const preferencesController = this.preferencesController
|
||||
const txController = this.txController
|
||||
const noticeController = this.noticeController
|
||||
const addressBookController = this.addressBookController
|
||||
const networkController = this.networkController
|
||||
|
||||
return {
|
||||
// etc
|
||||
|
@ -391,6 +421,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
setCurrentCurrency: this.setCurrentCurrency.bind(this),
|
||||
setCurrentCoin: this.setCurrentCoin.bind(this),
|
||||
setUseBlockie: this.setUseBlockie.bind(this),
|
||||
setUsePhishDetect: this.setUsePhishDetect.bind(this),
|
||||
setCurrentLocale: this.setCurrentLocale.bind(this),
|
||||
setDProvider: this.setDProvider.bind(this),
|
||||
markAccountsFound: this.markAccountsFound.bind(this),
|
||||
|
@ -471,10 +502,15 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
signPersonalMessage: nodeify(this.signPersonalMessage, this),
|
||||
cancelPersonalMessage: this.cancelPersonalMessage.bind(this),
|
||||
|
||||
// personalMessageManager
|
||||
// typedMessageManager
|
||||
signTypedMessage: nodeify(this.signTypedMessage, this),
|
||||
cancelTypedMessage: this.cancelTypedMessage.bind(this),
|
||||
|
||||
// decryptMessageManager
|
||||
decryptMessage: nodeify(this.decryptMessage, this),
|
||||
decryptMessageInline: nodeify(this.decryptMessageInline, this),
|
||||
cancelDecryptMessage: this.cancelDecryptMessage.bind(this),
|
||||
|
||||
// notices
|
||||
checkNotices: noticeController.updateNoticesList.bind(noticeController),
|
||||
markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
|
||||
|
@ -1169,6 +1205,147 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// eth_decrypt methods
|
||||
|
||||
/**
|
||||
* Called when a dapp uses the eth_decrypt method.
|
||||
*
|
||||
* @param {Object} msgParams - The params of the message to sign & return to the Dapp.
|
||||
* @param {Object} req - (optional) the original request, containing the origin
|
||||
* Passed back to the requesting Dapp.
|
||||
*/
|
||||
async newRequestDecryptMessage (msgParams, req) {
|
||||
const promise = this.decryptMessageManager.addUnapprovedMessageAsync(msgParams, req)
|
||||
this.sendUpdate()
|
||||
this.opts.showUnconfirmedMessage()
|
||||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
* Only decypt message and don't touch transaction state
|
||||
*
|
||||
* @param {Object} msgParams - The params of the message to decrypt.
|
||||
* @returns {Promise<Object>} - A full state update.
|
||||
*/
|
||||
async decryptMessageInline (msgParams) {
|
||||
log.info('MetaMaskController - decryptMessageInline')
|
||||
// decrypt the message inline
|
||||
const msgId = msgParams.metamaskId
|
||||
const msg = this.decryptMessageManager.getMsg(msgId)
|
||||
try {
|
||||
const stripped = ethUtil.stripHexPrefix(msgParams.data)
|
||||
const buff = Buffer.from(stripped, 'hex')
|
||||
msgParams.data = JSON.parse(buff.toString('utf8'))
|
||||
|
||||
msg.rawData = await this.keyringController.decryptMessage(msgParams)
|
||||
} catch (e) {
|
||||
msg.error = e.message
|
||||
}
|
||||
this.decryptMessageManager._updateMsg(msg)
|
||||
|
||||
return this.getState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Signifies a user's approval to decrypt a message in queue.
|
||||
* Triggers decrypt, and the callback function from newUnsignedDecryptMessage.
|
||||
*
|
||||
* @param {Object} msgParams - The params of the message to decrypt & return to the Dapp.
|
||||
* @returns {Promise<Object>} - A full state update.
|
||||
*/
|
||||
async decryptMessage (msgParams) {
|
||||
log.info('MetaMaskController - decryptMessage')
|
||||
const msgId = msgParams.metamaskId
|
||||
// sets the status op the message to 'approved'
|
||||
// and removes the metamaskId for decryption
|
||||
try {
|
||||
const cleanMsgParams = await this.decryptMessageManager.approveMessage(msgParams)
|
||||
|
||||
const stripped = ethUtil.stripHexPrefix(cleanMsgParams.data)
|
||||
const buff = Buffer.from(stripped, 'hex')
|
||||
cleanMsgParams.data = JSON.parse(buff.toString('utf8'))
|
||||
|
||||
// decrypt the message
|
||||
const rawMess = await this.keyringController.decryptMessage(cleanMsgParams)
|
||||
// tells the listener that the message has been decrypted and can be returned to the dapp
|
||||
this.decryptMessageManager.setMsgStatusDecrypted(msgId, rawMess)
|
||||
} catch (error) {
|
||||
log.info('MetaMaskController - eth_decrypt failed.', error)
|
||||
this.decryptMessageManager.errorMessage(msgId, error)
|
||||
}
|
||||
return this.getState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to cancel a eth_decrypt type message.
|
||||
* @param {string} msgId - The ID of the message to cancel.
|
||||
* @param {Function} cb - The callback function called with a full state update.
|
||||
*/
|
||||
cancelDecryptMessage (msgId, cb) {
|
||||
const messageManager = this.decryptMessageManager
|
||||
messageManager.rejectMsg(msgId)
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb(null, this.getState())
|
||||
}
|
||||
}
|
||||
|
||||
// eth_getEncryptionPublicKey methods
|
||||
|
||||
/**
|
||||
* Called when a dapp uses the eth_getEncryptionPublicKey method.
|
||||
*
|
||||
* @param {Object} msgParams - The params of the message to sign & return to the Dapp.
|
||||
* @param {Object} req - (optional) the original request, containing the origin
|
||||
* Passed back to the requesting Dapp.
|
||||
*/
|
||||
async newRequestEncryptionPublicKey (msgParams, req) {
|
||||
const promise = this.encryptionPublicKeyManager.addUnapprovedMessageAsync(msgParams, req)
|
||||
this.sendUpdate()
|
||||
this.opts.showUnconfirmedMessage()
|
||||
return promise
|
||||
}
|
||||
|
||||
/**
|
||||
* Signifies a user's approval to receiving encryption public key in queue.
|
||||
* Triggers receiving, and the callback function from newUnsignedEncryptionPublicKey.
|
||||
*
|
||||
* @param {Object} msgParams - The params of the message to receive & return to the Dapp.
|
||||
* @returns {Promise<Object>} - A full state update.
|
||||
*/
|
||||
async encryptionPublicKey (msgParams) {
|
||||
log.info('MetaMaskController - encryptionPublicKey')
|
||||
const msgId = msgParams.metamaskId
|
||||
// sets the status op the message to 'approved'
|
||||
// and removes the metamaskId for decryption
|
||||
try {
|
||||
const params = await this.encryptionPublicKeyManager.approveMessage(msgParams)
|
||||
|
||||
// EncryptionPublicKey message
|
||||
const publicKey = await this.keyringController.getEncryptionPublicKey(params.data)
|
||||
|
||||
// tells the listener that the message has been processed
|
||||
// and can be returned to the dapp
|
||||
this.encryptionPublicKeyManager.setMsgStatusReceived(msgId, publicKey)
|
||||
} catch (error) {
|
||||
log.info('MetaMaskController - eth_getEncryptionPublicKey failed.', error)
|
||||
this.encryptionPublicKeyManager.errorMessage(msgId, error)
|
||||
}
|
||||
return this.getState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to cancel a eth_getEncryptionPublicKey type message.
|
||||
* @param {string} msgId - The ID of the message to cancel.
|
||||
* @param {Function} cb - The callback function called with a full state update.
|
||||
*/
|
||||
cancelEncryptionPublicKey (msgId, cb) {
|
||||
const messageManager = this.encryptionPublicKeyManager
|
||||
messageManager.rejectMsg(msgId)
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb(null, this.getState())
|
||||
}
|
||||
}
|
||||
|
||||
// eth_signTypedData methods
|
||||
|
||||
/**
|
||||
|
@ -1356,29 +1533,37 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
cb()
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// SETUP
|
||||
//=============================================================================
|
||||
//=============================================================================
|
||||
// SETUP
|
||||
//=============================================================================
|
||||
|
||||
/**
|
||||
* A runtime.MessageSender object, as provided by the browser:
|
||||
* @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender
|
||||
* @typedef {Object} MessageSender
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to create a multiplexed stream for connecting to an untrusted context
|
||||
* like a Dapp or other extension.
|
||||
* @param {*} connectionStream - The Duplex stream to connect to.
|
||||
* @param {string} originDomain - The domain requesting the stream, which
|
||||
* may trigger a blacklist reload.
|
||||
* @param {MessageSender} sender - The sender of the messages on this stream
|
||||
*/
|
||||
setupUntrustedCommunication (connectionStream, originDomain) {
|
||||
// Check if new connection is blacklisted
|
||||
if (this.blacklistController.checkForPhishing(originDomain)) {
|
||||
log.debug('Nifty Wallet - sending phishing warning for', originDomain)
|
||||
this.sendPhishingWarning(connectionStream, originDomain)
|
||||
setupUntrustedCommunication (connectionStream, sender) {
|
||||
const { usePhishDetect } = this.preferencesController.store.getState()
|
||||
const hostname = (new URL(sender.url)).hostname
|
||||
// Check if new connection is blacklisted if phishing detection is on
|
||||
if (usePhishDetect && this.phishingController.test(hostname)) {
|
||||
log.debug('Nifty Wallet - sending phishing warning for', hostname)
|
||||
this.sendPhishingWarning(connectionStream, hostname)
|
||||
return
|
||||
}
|
||||
|
||||
// setup multiplexing
|
||||
const mux = setupMultiplex(connectionStream)
|
||||
// connect features
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain)
|
||||
|
||||
// messages between inpage and background
|
||||
this.setupProviderConnection(mux.createStream('provider'), sender)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'))
|
||||
}
|
||||
|
||||
|
@ -1389,15 +1574,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* functions, like the ability to approve transactions or sign messages.
|
||||
*
|
||||
* @param {*} connectionStream - The duplex stream to connect to.
|
||||
* @param {string} originDomain - The domain requesting the connection,
|
||||
* used in logging and error reporting.
|
||||
* @param {MessageSender} sender - The sender of the messages on this stream
|
||||
*/
|
||||
setupTrustedCommunication (connectionStream, originDomain) {
|
||||
setupTrustedCommunication (connectionStream, sender) {
|
||||
// setup multiplexing
|
||||
const mux = setupMultiplex(connectionStream)
|
||||
// connect features
|
||||
this.setupControllerConnection(mux.createStream('controller'))
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain)
|
||||
this.setupProviderConnection(mux.createStream('provider'), sender, true)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1421,9 +1605,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
*/
|
||||
setupControllerConnection (outStream) {
|
||||
const api = this.getApi()
|
||||
const dnode = Dnode(api, {
|
||||
weak: false,
|
||||
})
|
||||
const dnode = Dnode(api)
|
||||
// report new active controller connection
|
||||
this.activeControllerConnections++
|
||||
this.emit('controllerConnectionChanged', this.activeControllerConnections)
|
||||
|
@ -1437,8 +1619,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
this.activeControllerConnections--
|
||||
this.emit('controllerConnectionChanged', this.activeControllerConnections)
|
||||
// report any error
|
||||
if (err) log.error(err)
|
||||
if (err) {
|
||||
log.error(err)
|
||||
}
|
||||
},
|
||||
)
|
||||
dnode.on('remote', (remote) => {
|
||||
// push updates to popup
|
||||
|
@ -1452,9 +1636,57 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
/**
|
||||
* A method for serving our ethereum provider over a given stream.
|
||||
* @param {*} outStream - The stream to provide over.
|
||||
* @param {string} origin - The URI of the requesting resource.
|
||||
* @param {MessageSender} sender - The sender of the messages on this stream
|
||||
* @param {boolean} isInternal - True if this is a connection with an internal process
|
||||
*/
|
||||
setupProviderConnection (outStream, origin) {
|
||||
setupProviderConnection (outStream, sender, isInternal) {
|
||||
const origin = isInternal
|
||||
? 'metamask'
|
||||
: (new URL(sender.url)).hostname
|
||||
let extensionId
|
||||
if (sender.id !== extension.runtime.id) {
|
||||
extensionId = sender.id
|
||||
}
|
||||
let tabId
|
||||
if (sender.tab && sender.tab.id) {
|
||||
tabId = sender.tab.id
|
||||
}
|
||||
|
||||
const engine = this.setupProviderEngine({ origin, location: sender.url, extensionId, tabId })
|
||||
|
||||
// setup connection
|
||||
const providerStream = createEngineStream({ engine })
|
||||
|
||||
const connectionId = this.addConnection(origin, { engine })
|
||||
|
||||
pump(
|
||||
outStream,
|
||||
providerStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
// handle any middleware cleanup
|
||||
engine._middleware.forEach((mid) => {
|
||||
if (mid.destroy && typeof mid.destroy === 'function') {
|
||||
mid.destroy()
|
||||
}
|
||||
})
|
||||
connectionId && this.removeConnection(origin, connectionId)
|
||||
if (err) {
|
||||
log.error(err)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for creating a provider that is safely restricted for the requesting domain.
|
||||
* @param {Object} options - Provider engine options
|
||||
* @param {string} options.origin - The hostname of the sender
|
||||
* @param {string} options.location - The full URL of the sender
|
||||
* @param {extensionId} [options.extensionId] - The extension ID of the sender, if the sender is an external extension
|
||||
* @param {tabId} [options.tabId] - The tab ID of the sender - if the sender is within a tab
|
||||
**/
|
||||
setupProviderEngine ({ origin, location, extensionId, tabId }) {
|
||||
// setup json rpc engine stack
|
||||
const engine = new RpcEngine()
|
||||
const provider = this.provider
|
||||
|
@ -1462,12 +1694,18 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
|
||||
// create filter polyfill middleware
|
||||
const filterMiddleware = createFilterMiddleware({ provider, blockTracker })
|
||||
|
||||
// create subscription polyfill middleware
|
||||
const subscriptionManager = createSubscriptionManager({ provider, blockTracker })
|
||||
subscriptionManager.events.on('notification', (message) => engine.emit('notification', message))
|
||||
|
||||
// metadata
|
||||
// append origin to each request
|
||||
engine.push(createOriginMiddleware({ origin }))
|
||||
// append tabId to each request if it exists
|
||||
if (tabId) {
|
||||
engine.push(createTabIdMiddleware({ tabId }))
|
||||
}
|
||||
// logging
|
||||
engine.push(createLoggerMiddleware({ origin }))
|
||||
// filter and subscription polyfills
|
||||
engine.push(filterMiddleware)
|
||||
|
@ -1479,21 +1717,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this))
|
||||
engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3', true).bind(this))
|
||||
// forward to metamask primary provider
|
||||
engine.push(createProviderMiddleware({ provider }))
|
||||
|
||||
// setup connection
|
||||
const providerStream = createEngineStream({ engine })
|
||||
|
||||
pump(
|
||||
outStream,
|
||||
providerStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
// cleanup filter polyfill middleware
|
||||
filterMiddleware.destroy()
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
engine.push(providerAsMiddleware(provider))
|
||||
return engine
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1507,17 +1732,113 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @param {*} outStream - The stream to provide public config over.
|
||||
*/
|
||||
setupPublicConfig (outStream) {
|
||||
const configStream = asStream(this.publicConfigStore)
|
||||
const configStore = this.createPublicConfigStore()
|
||||
const configStream = asStream(configStore)
|
||||
|
||||
pump(
|
||||
configStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
configStore.destroy()
|
||||
configStream.destroy()
|
||||
if (err) log.error(err)
|
||||
if (err) {
|
||||
log.error(err)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a reference to a connection by origin. Ignores the 'metamask' origin.
|
||||
* Caller must ensure that the returned id is stored such that the reference
|
||||
* can be deleted later.
|
||||
*
|
||||
* @param {string} origin - The connection's origin string.
|
||||
* @param {Object} options - Data associated with the connection
|
||||
* @param {Object} options.engine - The connection's JSON Rpc Engine
|
||||
* @returns {string} - The connection's id (so that it can be deleted later)
|
||||
*/
|
||||
addConnection (origin, { engine }) {
|
||||
|
||||
if (origin === 'metamask') {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!this.connections[origin]) {
|
||||
this.connections[origin] = {}
|
||||
}
|
||||
|
||||
const id = nanoid()
|
||||
this.connections[origin][id] = {
|
||||
engine,
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a reference to a connection, by origin and id.
|
||||
* Ignores unknown origins.
|
||||
*
|
||||
* @param {string} origin - The connection's origin string.
|
||||
* @param {string} id - The connection's id, as returned from addConnection.
|
||||
*/
|
||||
removeConnection (origin, id) {
|
||||
|
||||
const connections = this.connections[origin]
|
||||
if (!connections) {
|
||||
return
|
||||
}
|
||||
|
||||
delete connections[id]
|
||||
|
||||
if (Object.keys(connections.length === 0)) {
|
||||
delete this.connections[origin]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the RPC engines associated with the connections to the given origin
|
||||
* to emit a notification event with the given payload.
|
||||
* Does nothing if the extension is locked or the origin is unknown.
|
||||
*
|
||||
* @param {string} origin - The connection's origin string.
|
||||
* @param {any} payload - The event payload.
|
||||
*/
|
||||
notifyConnections (origin, payload) {
|
||||
|
||||
const { isUnlocked } = this.getState()
|
||||
const connections = this.connections[origin]
|
||||
if (!isUnlocked || !connections) {
|
||||
return
|
||||
}
|
||||
|
||||
Object.values(connections).forEach((conn) => {
|
||||
conn.engine && conn.engine.emit('notification', payload)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the RPC engines associated with all connections to emit a
|
||||
* notification event with the given payload.
|
||||
* Does nothing if the extension is locked.
|
||||
*
|
||||
* @param {any} payload - The event payload.
|
||||
*/
|
||||
notifyAllConnections (payload) {
|
||||
|
||||
const { isUnlocked } = this.getState()
|
||||
if (!isUnlocked) {
|
||||
return
|
||||
}
|
||||
|
||||
Object.values(this.connections).forEach((origin) => {
|
||||
Object.values(origin).forEach((conn) => {
|
||||
conn.engine && conn.engine.emit('notification', payload)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a KeyringController update
|
||||
* @param {object} state the KC state
|
||||
|
@ -1554,6 +1875,13 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
this.emit('update', this.getState())
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} Whether the extension is unlocked.
|
||||
*/
|
||||
isUnlocked () {
|
||||
return this.keyringController.memStore.getState().isUnlocked
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for estimating a good gas price
|
||||
* For ETH, ETC: from gas price oracles
|
||||
|
@ -1787,6 +2115,20 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not to use phishing detection.
|
||||
* @param {boolean} val
|
||||
* @param {Function} cb
|
||||
*/
|
||||
setUsePhishDetect (val, cb) {
|
||||
try {
|
||||
this.preferencesController.setUsePhishDetect(val)
|
||||
cb(null)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for setting a user's current locale, affecting the language rendered.
|
||||
* @param {string} key - Locale identifier.
|
||||
|
@ -1837,7 +2179,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
*/
|
||||
set isClientOpen (open) {
|
||||
this._isClientOpen = open
|
||||
this.isClientOpenAndUnlocked = this.getState().isUnlocked && open
|
||||
this.isClientOpenAndUnlocked = this.isUnlocked() && open
|
||||
this.detectTokensController.isOpen = open
|
||||
}
|
||||
|
||||
|
@ -1882,10 +2224,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a domain to the {@link BlacklistController} whitelist
|
||||
* @param {string} hostname the domain to whitelist
|
||||
* Adds a domain to the PhishingController whitelist
|
||||
* @param {string} hostname - the domain to whitelist
|
||||
*/
|
||||
whitelistPhishingDomain (hostname) {
|
||||
return this.blacklistController.whitelistDomain(hostname)
|
||||
return this.phishingController.bypass(hostname)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const extension = require('extensionizer')
|
||||
import extension from 'extensionizer'
|
||||
const explorerLinks = require('eth-net-props').explorerLinks
|
||||
const { capitalizeFirstLetter } = require('../lib/util')
|
||||
const { capitalizeFirstLetter, getEnvironmentType, checkForError } = require('../lib/util')
|
||||
const { ENVIRONMENT_TYPE_BACKGROUND } = require('../lib/enums')
|
||||
|
||||
class ExtensionPlatform {
|
||||
|
||||
|
@ -57,6 +58,9 @@ class ExtensionPlatform {
|
|||
extensionURL += `#${route}`
|
||||
}
|
||||
this.openWindow({ url: extensionURL })
|
||||
if (getEnvironmentType() !== ENVIRONMENT_TYPE_BACKGROUND) {
|
||||
window.close()
|
||||
}
|
||||
}
|
||||
|
||||
getPlatformInfo (cb) {
|
||||
|
@ -70,15 +74,57 @@ class ExtensionPlatform {
|
|||
}
|
||||
|
||||
showTransactionNotification (txMeta) {
|
||||
const { status, txReceipt: { status: receiptStatus } = {} } = txMeta
|
||||
|
||||
const status = txMeta.status
|
||||
if (status === 'confirmed') {
|
||||
this._showConfirmedTransaction(txMeta)
|
||||
// There was an on-chain failure
|
||||
receiptStatus === '0x0'
|
||||
? this._showFailedTransaction(txMeta, 'Transaction encountered an error.')
|
||||
: this._showConfirmedTransaction(txMeta)
|
||||
} else if (status === 'failed') {
|
||||
this._showFailedTransaction(txMeta)
|
||||
}
|
||||
}
|
||||
|
||||
currentTab () {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.getCurrent((tab) => {
|
||||
const err = checkForError()
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(tab)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
switchToTab (tabId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.update(tabId, { highlighted: true }, (tab) => {
|
||||
const err = checkForError()
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(tab)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
closeTab (tabId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.remove(tabId, () => {
|
||||
const err = checkForError()
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_showConfirmedTransaction (txMeta) {
|
||||
|
||||
this._subscribeToNotificationClicked()
|
||||
|
@ -91,11 +137,11 @@ class ExtensionPlatform {
|
|||
this._showNotification(title, message, url)
|
||||
}
|
||||
|
||||
_showFailedTransaction (txMeta) {
|
||||
_showFailedTransaction (txMeta, errorMessage) {
|
||||
|
||||
const nonce = parseInt(txMeta.txParams.nonce, 16)
|
||||
const title = 'Failed transaction'
|
||||
const message = `Transaction ${nonce} failed! ${capitalizeFirstLetter(txMeta.err.message)}`
|
||||
const message = `Transaction ${nonce} failed! ${errorMessage || capitalizeFirstLetter(txMeta.err.message)}`
|
||||
this._showNotification(title, message)
|
||||
}
|
||||
|
||||
|
@ -131,7 +177,6 @@ class ExtensionPlatform {
|
|||
url: explorerLinks.getExplorerTxLinkFor(hash, networkId),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ExtensionPlatform
|
||||
|
|
|
@ -151,6 +151,6 @@ function startApp () {
|
|||
}),
|
||||
]),
|
||||
|
||||
]
|
||||
],
|
||||
), container)
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ function startApp () {
|
|||
}),
|
||||
]),
|
||||
|
||||
]
|
||||
],
|
||||
), container)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ const loggerMiddleware = createLogger()
|
|||
|
||||
const createStoreWithMiddleware = applyMiddleware(
|
||||
thunkMiddleware,
|
||||
loggerMiddleware
|
||||
loggerMiddleware,
|
||||
)(createStore)
|
||||
|
||||
function configureStore (initialState) {
|
||||
|
|
|
@ -121,10 +121,10 @@ async function startContainer (fileRegEx, testGenerator) {
|
|||
await promisify(fs.writeFile)(
|
||||
`${__dirname}/${sRootPath}tests/${testFilePath}`,
|
||||
containerTest,
|
||||
'utf8'
|
||||
'utf8',
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}, (err) => {
|
||||
console.log('123', err)
|
||||
|
|
48
gulpfile.js
48
gulpfile.js
|
@ -200,16 +200,16 @@ gulp.task('copy',
|
|||
gulp.parallel(...copyTaskNames),
|
||||
'manifest:production',
|
||||
'manifest:chrome',
|
||||
'manifest:opera'
|
||||
)
|
||||
'manifest:opera',
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('dev:copy',
|
||||
gulp.series(
|
||||
gulp.parallel(...copyDevTaskNames),
|
||||
'manifest:chrome',
|
||||
'manifest:opera'
|
||||
)
|
||||
'manifest:opera',
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('lint-scss', function () {
|
||||
|
@ -328,9 +328,9 @@ gulp.task('dev',
|
|||
'dev:extension:js',
|
||||
'dev:mascara:js',
|
||||
'dev:copy',
|
||||
'dev:reload'
|
||||
)
|
||||
)
|
||||
'dev:reload',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('dev:extension',
|
||||
|
@ -339,9 +339,9 @@ gulp.task('dev:extension',
|
|||
gulp.parallel(
|
||||
'dev:extension:js',
|
||||
'dev:copy',
|
||||
'dev:reload'
|
||||
)
|
||||
)
|
||||
'dev:reload',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('dev:mascara',
|
||||
|
@ -350,9 +350,9 @@ gulp.task('dev:mascara',
|
|||
gulp.parallel(
|
||||
'dev:mascara:js',
|
||||
'dev:copy',
|
||||
'dev:reload'
|
||||
)
|
||||
)
|
||||
'dev:reload',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('build',
|
||||
|
@ -361,9 +361,9 @@ gulp.task('build',
|
|||
gulpParallel(
|
||||
'build:extension:js',
|
||||
'build:mascara:js',
|
||||
'copy'
|
||||
)
|
||||
)
|
||||
'copy',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('build:extension',
|
||||
|
@ -371,9 +371,9 @@ gulp.task('build:extension',
|
|||
'clean',
|
||||
gulp.parallel(
|
||||
'build:extension:js',
|
||||
'copy'
|
||||
)
|
||||
)
|
||||
'copy',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('build:mascara',
|
||||
|
@ -381,16 +381,16 @@ gulp.task('build:mascara',
|
|||
'clean',
|
||||
gulp.parallel(
|
||||
'build:mascara:js',
|
||||
'copy'
|
||||
)
|
||||
)
|
||||
'copy',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
gulp.task('dist',
|
||||
gulp.series(
|
||||
'build',
|
||||
'zip'
|
||||
)
|
||||
'zip',
|
||||
),
|
||||
)
|
||||
|
||||
// task generators
|
||||
|
|
|
@ -194,5 +194,5 @@ export default connect(
|
|||
dispatch => ({
|
||||
goToCoinbase: address => dispatch(buyEth({ network: '1', address, amount: 0 })),
|
||||
showAccountDetail: address => dispatch(showAccountDetail(address)),
|
||||
})
|
||||
}),
|
||||
)(BuyEtherWidget)
|
||||
|
|
|
@ -196,5 +196,5 @@ export default connect(
|
|||
dispatch => ({
|
||||
goToCoinbase: address => dispatch(buyEth({ network: '1', address, amount: 0 })),
|
||||
showAccountDetail: address => dispatch(showAccountDetail(address)),
|
||||
})
|
||||
}),
|
||||
)(BuyEtherScreen)
|
||||
|
|
|
@ -157,6 +157,6 @@ export default compose(
|
|||
dispatch => ({
|
||||
confirmSeedWords: () => dispatch(confirmSeedWords()),
|
||||
openBuyEtherModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER'})),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(ConfirmSeedScreen)
|
||||
|
|
|
@ -210,6 +210,6 @@ export default compose(
|
|||
mapStateToProps,
|
||||
dispatch => ({
|
||||
createAccount: password => dispatch(createNewVaultAndKeychain(password)),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(CreatePasswordScreen)
|
||||
|
|
|
@ -210,5 +210,5 @@ export default connect(
|
|||
dispatch => ({
|
||||
importNewAccount: (strategy, args) => dispatch(importNewAccount(strategy, args)),
|
||||
hideWarning: () => dispatch(hideWarning()),
|
||||
})
|
||||
}),
|
||||
)(ImportAccountScreen)
|
||||
|
|
|
@ -186,5 +186,5 @@ export default connect(
|
|||
dispatch(unMarkPasswordForgotten())
|
||||
},
|
||||
createNewVaultAndRestore: (pw, seed) => dispatch(createNewVaultAndRestore(pw, seed)),
|
||||
})
|
||||
}),
|
||||
)(ImportSeedPhraseScreen)
|
||||
|
|
|
@ -95,5 +95,5 @@ const mapStateToProps = ({ metamask }) => {
|
|||
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps)
|
||||
connect(mapStateToProps),
|
||||
)(FirstTimeFlow)
|
||||
|
|
|
@ -130,6 +130,6 @@ export default compose(
|
|||
mapStateToProps,
|
||||
dispatch => ({
|
||||
markNoticeRead: notice => dispatch(markNoticeRead(notice)),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(NoticeScreen)
|
||||
|
|
|
@ -171,6 +171,6 @@ export default compose(
|
|||
seedWords,
|
||||
isLoading,
|
||||
address: selectedAddress,
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(BackupPhraseScreen)
|
||||
|
|
|
@ -45,6 +45,6 @@ export default compose(
|
|||
connect(
|
||||
({ metamask: { selectedAddress } }) => ({
|
||||
address: selectedAddress,
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(UniqueImageScreen)
|
||||
|
|
|
@ -214,5 +214,5 @@ export default connect(
|
|||
shapeShiftSubview: () => dispatch(shapeShiftSubview()),
|
||||
pairUpdate: coin => dispatch(pairUpdate(coin)),
|
||||
buyWithShapeShift: data => dispatch(buyWithShapeShift(data)),
|
||||
})
|
||||
}),
|
||||
)(ShapeShiftForm)
|
||||
|
|
|
@ -144,7 +144,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
}, [
|
||||
identity && identity.name,
|
||||
]),
|
||||
]
|
||||
],
|
||||
),
|
||||
h(
|
||||
AccountDropdowns,
|
||||
|
@ -161,7 +161,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
enableAccountOptions: true,
|
||||
},
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
]),
|
||||
h('.flex-row', {
|
||||
|
|
|
@ -50,7 +50,7 @@ const { getNetworkID } = require('./util')
|
|||
|
||||
module.exports = compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps)
|
||||
connect(mapStateToProps),
|
||||
)(App)
|
||||
|
||||
inherits(App, Component)
|
||||
|
|
|
@ -92,7 +92,7 @@ ExportAccountView.prototype.render = function () {
|
|||
}},
|
||||
[
|
||||
h('div.error', this.props.warning.split('-')),
|
||||
]
|
||||
],
|
||||
)
|
||||
),
|
||||
])
|
||||
|
@ -134,7 +134,7 @@ ExportAccountView.prototype.render = function () {
|
|||
},
|
||||
}, h(CopyButton, {
|
||||
value: accountDetail.privateKey,
|
||||
})
|
||||
}),
|
||||
),
|
||||
]),
|
||||
h('div', {
|
||||
|
|
|
@ -12,5 +12,5 @@ const mapStateToProps = state => {
|
|||
|
||||
export default compose(
|
||||
connect(mapStateToProps),
|
||||
withTokenTracker
|
||||
withTokenTracker,
|
||||
)(TokenBalance)
|
||||
|
|
|
@ -96,7 +96,7 @@ class AccountList extends Component {
|
|||
>{`${a.balance}`}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ RadioList.prototype.render = function () {
|
|||
props.onClick(event)
|
||||
},
|
||||
})
|
||||
})
|
||||
}),
|
||||
),
|
||||
h('.text', {},
|
||||
labels.map((label) => {
|
||||
|
@ -52,7 +52,7 @@ RadioList.prototype.render = function () {
|
|||
} else {
|
||||
return h('.radio-titles.font-pre-medium', label)
|
||||
}
|
||||
})
|
||||
}),
|
||||
),
|
||||
])
|
||||
)
|
||||
|
|
|
@ -218,7 +218,7 @@ PendingTx.prototype.render = function () {
|
|||
},
|
||||
onClick: () => props.dispatch(actions.nextTx()),
|
||||
}),
|
||||
])]
|
||||
])],
|
||||
),
|
||||
|
||||
h(MiniAccountPanel, {
|
||||
|
|
|
@ -267,7 +267,7 @@ class SendTransactionScreen extends PersistentForm {
|
|||
const abi = require('ethereumjs-abi')
|
||||
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
||||
abi.rawEncode(['address', 'uint256'], [toAddress, ethUtil.addHexPrefix(amount)]),
|
||||
x => ('00' + x.toString(16)).slice(-2)
|
||||
x => ('00' + x.toString(16)).slice(-2),
|
||||
).join('')
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ const mapDispatchToProps = dispatch => {
|
|||
toAddress,
|
||||
tokensValueWithDec,
|
||||
txParams,
|
||||
confTxScreenParams
|
||||
confTxScreenParams,
|
||||
) => dispatch(actions.signTokenTx(tokenAddress, toAddress, tokensValueWithDec, txParams, confTxScreenParams)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ TokenCell.prototype.render = function () {
|
|||
})
|
||||
},
|
||||
},
|
||||
this.renderTokenOptions(menuToTop, ind)
|
||||
this.renderTokenOptions(menuToTop, ind),
|
||||
),
|
||||
|
||||
/*
|
||||
|
@ -147,7 +147,7 @@ TokenCell.prototype.renderTokenOptions = function (menuToTop, ind) {
|
|||
},
|
||||
'Remove',
|
||||
),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ class ConfigScreen extends Component {
|
|||
defaultValue: currentCurrency,
|
||||
}, infuraCurrencies.map((currency) => {
|
||||
return h('option', {key: currency.quote.code, value: currency.quote.code}, `${currency.quote.code.toUpperCase()} - ${currency.quote.name}`)
|
||||
})
|
||||
}),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -111,15 +111,17 @@
|
|||
"eth-block-tracker": "^4.4.2",
|
||||
"eth-contract-metadata": "^1.12.1",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"eth-json-rpc-errors": "^2.0.2",
|
||||
"eth-json-rpc-filters": "github:poanetwork/eth-json-rpc-filters#3.0.2",
|
||||
"eth-json-rpc-infura": "^4.0.2",
|
||||
"eth-json-rpc-middleware": "^4.4.1",
|
||||
"eth-keychain-controller": "github:vbaranov/KeyringController#5.1.0",
|
||||
"eth-ledger-bridge-keyring": "github:vbaranov/eth-ledger-bridge-keyring#0.1.0-clear-accounts-flag",
|
||||
"eth-method-registry": "^1.0.0",
|
||||
"eth-net-props": "^1.0.33",
|
||||
"eth-phishing-detect": "^1.1.4",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^2.2.0",
|
||||
"eth-sig-util": "^2.3.0",
|
||||
"eth-token-watcher": "^1.1.7",
|
||||
"eth-trezor-keyring": "github:vbaranov/eth-trezor-keyring#0.4.0",
|
||||
"ethereumjs-abi": "^0.6.7",
|
||||
|
@ -137,6 +139,7 @@
|
|||
"fast-json-patch": "^2.0.4",
|
||||
"fast-levenshtein": "^2.0.6",
|
||||
"fuse.js": "^3.2.0",
|
||||
"gaba": "^1.9.3",
|
||||
"human-standard-token-abi": "^2.0.0",
|
||||
"idb-global": "^2.1.0",
|
||||
"iframe-stream": "^3.0.0",
|
||||
|
@ -152,7 +155,8 @@
|
|||
"metamascara": "^2.0.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"multihashes": "^0.4.12",
|
||||
"nifty-wallet-inpage-provider": "git+ssh://git@github.com/poanetwork/nifty-wallet-inpage-provider.git#1.2.3",
|
||||
"nanoid": "^2.1.6",
|
||||
"nifty-wallet-inpage-provider": "github:poanetwork/nifty-wallet-inpage-provider#1.2.3",
|
||||
"number-to-bn": "^1.7.0",
|
||||
"obj-multiplex": "^1.0.0",
|
||||
"obs-store": "^4.0.3",
|
||||
|
@ -192,6 +196,7 @@
|
|||
"request-promise": "^4.2.1",
|
||||
"reselect": "^3.0.1",
|
||||
"rockicon": "^1.0.0",
|
||||
"rpc-cap": "^2.0.0",
|
||||
"sandwich-expando": "^1.1.3",
|
||||
"semaphore": "^1.0.5",
|
||||
"semver": "^5.4.1",
|
||||
|
@ -244,7 +249,6 @@
|
|||
"eslint-plugin-json": "^1.2.0",
|
||||
"eslint-plugin-mocha": "^6.2.2",
|
||||
"eslint-plugin-react": "^7.18.3",
|
||||
"eth-json-rpc-middleware": "^3.1.3",
|
||||
"expect": "^25.0.0",
|
||||
"fetch-mock": "^6.5.2",
|
||||
"file-loader": "^1.1.11",
|
||||
|
|
|
@ -61,7 +61,7 @@ class Driver {
|
|||
this.driver.wait(until.elementIsEnabled(element), this.timeout),
|
||||
)
|
||||
return acc
|
||||
}, [])
|
||||
}, []),
|
||||
)
|
||||
return elements
|
||||
}
|
||||
|
|
|
@ -22,5 +22,5 @@ pump(
|
|||
if (err) throw err
|
||||
console.log(`Integration test build completed: "${bundlePath}"`)
|
||||
process.exit(0)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -25,7 +25,7 @@ async function runAddTokenFlowTest (assert, done) {
|
|||
|
||||
// Used to set values on TextField input component
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
window.HTMLInputElement.prototype, 'value',
|
||||
).set
|
||||
|
||||
// Check that no tokens have been added
|
||||
|
|
|
@ -12,7 +12,7 @@ async function runFirstTimeUsageTest (assert, done) {
|
|||
|
||||
// Used to set values on TextField input component
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
||||
window.HTMLInputElement.prototype, 'value'
|
||||
window.HTMLInputElement.prototype, 'value',
|
||||
).set
|
||||
|
||||
// const loader = (await findAsync($, '.loading-overlay'))[0]
|
||||
|
|
|
@ -42,13 +42,13 @@ async function customizeGas (assert, price, limit, ethFee, usdFee) {
|
|||
assert.equal(
|
||||
(await findAsync(sendGasField, '.currency-display-component'))[0].textContent,
|
||||
ethFee,
|
||||
'send gas field should show customized gas total'
|
||||
'send gas field should show customized gas total',
|
||||
)
|
||||
|
||||
assert.equal(
|
||||
(await findAsync(sendGasField, '.currency-display__converted-value'))[0].textContent,
|
||||
usdFee,
|
||||
'send gas field should show customized gas total converted to USD'
|
||||
'send gas field should show customized gas total converted to USD',
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ const loggerMiddleware = createLogger()
|
|||
|
||||
const createStoreWithMiddleware = applyMiddleware(
|
||||
thunkMiddleware,
|
||||
loggerMiddleware
|
||||
loggerMiddleware,
|
||||
)(createStore)
|
||||
|
||||
function configureStore (initialState) {
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
const assert = require('assert')
|
||||
const sinon = require('sinon')
|
||||
const clone = require('clone')
|
||||
const nock = require('nock')
|
||||
const createThoughStream = require('through2').obj
|
||||
import assert from 'assert'
|
||||
import sinon from 'sinon'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import nock from 'nock'
|
||||
import { obj as createThoughStream } from 'through2'
|
||||
const blacklistJSON = require('eth-phishing-detect/src/config')
|
||||
const MetaMaskController = require('../../../../app/scripts/metamask-controller')
|
||||
const firstTimeState = require('../../../unit/localhostState')
|
||||
const createTxMeta = require('../../../lib/createTxMeta')
|
||||
const EthQuery = require('eth-query')
|
||||
import EthQuery from 'eth-query'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
const ExtensionizerMock = {
|
||||
runtime: {
|
||||
id: 'fake-extension-id',
|
||||
},
|
||||
}
|
||||
|
||||
const MetaMaskController = proxyquire('../../../../app/scripts/metamask-controller', {
|
||||
'extensionizer': ExtensionizerMock,
|
||||
})
|
||||
|
||||
const currentNetworkId = 42
|
||||
const DEFAULT_LABEL = 'Account 1'
|
||||
|
@ -57,7 +67,7 @@ describe('MetaMaskController', function () {
|
|||
return Promise.resolve(this.object)
|
||||
},
|
||||
},
|
||||
initState: clone(firstTimeState),
|
||||
initState: cloneDeep(firstTimeState),
|
||||
})
|
||||
// disable diagnostics
|
||||
metamaskController.diagnostics = null
|
||||
|
@ -372,7 +382,7 @@ describe('MetaMaskController', function () {
|
|||
sinon.spy(metamaskController.keyringController, 'addNewKeyring')
|
||||
await metamaskController.connectHardware('trezor', 0).catch((e) => null)
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Trezor Hardware'
|
||||
'Trezor Hardware',
|
||||
)
|
||||
assert.equal(metamaskController.keyringController.addNewKeyring.getCall(0).args, 'Trezor Hardware')
|
||||
assert.equal(keyrings.length, 1)
|
||||
|
@ -382,7 +392,7 @@ describe('MetaMaskController', function () {
|
|||
sinon.spy(metamaskController.keyringController, 'addNewKeyring')
|
||||
await metamaskController.connectHardware('ledger', 0).catch((e) => null)
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Ledger Hardware'
|
||||
'Ledger Hardware',
|
||||
)
|
||||
assert.equal(metamaskController.keyringController.addNewKeyring.getCall(0).args, 'Ledger Hardware')
|
||||
assert.equal(keyrings.length, 1)
|
||||
|
@ -419,7 +429,7 @@ describe('MetaMaskController', function () {
|
|||
await metamaskController.connectHardware('trezor', 0).catch((e) => null)
|
||||
await metamaskController.forgetDevice('trezor')
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Trezor Hardware'
|
||||
'Trezor Hardware',
|
||||
)
|
||||
|
||||
assert.deepEqual(keyrings[0].accounts, [])
|
||||
|
@ -468,7 +478,7 @@ describe('MetaMaskController', function () {
|
|||
|
||||
it('should set unlockedAccount in the keyring', async function () {
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Trezor Hardware'
|
||||
'Trezor Hardware',
|
||||
)
|
||||
assert.equal(keyrings[0].unlockedAccount, accountToUnlock)
|
||||
})
|
||||
|
@ -821,47 +831,104 @@ describe('MetaMaskController', function () {
|
|||
})
|
||||
|
||||
describe('#setupUntrustedCommunication', function () {
|
||||
let streamTest
|
||||
it('sets up phishing stream for untrusted communication ', async function () {
|
||||
const phishingMessageSender = {
|
||||
url: 'http://myethereumwalletntw.com',
|
||||
tab: {},
|
||||
}
|
||||
await metamaskController.phishingController.updatePhishingLists()
|
||||
|
||||
const phishingUrl = 'myethereumwalletntw.com'
|
||||
|
||||
afterEach(function () {
|
||||
streamTest.end()
|
||||
})
|
||||
|
||||
it('sets up phishing stream for untrusted communication ', async () => {
|
||||
await metamaskController.blacklistController.updatePhishingList()
|
||||
console.log(blacklistJSON.blacklist.includes(phishingUrl))
|
||||
|
||||
const { promise, resolve } = deferredPromise()
|
||||
|
||||
streamTest = createThoughStream((chunk, enc, cb) => {
|
||||
if (chunk.name !== 'phishing') return cb()
|
||||
assert.equal(chunk.data.hostname, phishingUrl)
|
||||
const streamTest = createThoughStream((chunk, _, cb) => {
|
||||
if (chunk.name !== 'phishing') {
|
||||
return cb()
|
||||
}
|
||||
assert.equal(chunk.data.hostname, (new URL(phishingMessageSender.url)).hostname)
|
||||
resolve()
|
||||
cb()
|
||||
})
|
||||
metamaskController.setupUntrustedCommunication(streamTest, phishingUrl)
|
||||
|
||||
|
||||
metamaskController.setupUntrustedCommunication(streamTest, phishingMessageSender)
|
||||
await promise
|
||||
streamTest.end()
|
||||
})
|
||||
|
||||
it('adds a tabId and origin to requests', function (done) {
|
||||
const messageSender = {
|
||||
url: 'http://mycrypto.com',
|
||||
tab: { id: 456 },
|
||||
}
|
||||
const streamTest = createThoughStream((chunk, _, cb) => {
|
||||
if (chunk.data && chunk.data.method) {
|
||||
cb(null, chunk)
|
||||
} else {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
|
||||
metamaskController.setupUntrustedCommunication(streamTest, messageSender)
|
||||
|
||||
const message = {
|
||||
id: 1999133338649204,
|
||||
jsonrpc: '2.0',
|
||||
params: ['mock tx params'],
|
||||
method: 'eth_sendTransaction',
|
||||
}
|
||||
streamTest.write({
|
||||
name: 'provider',
|
||||
data: message,
|
||||
}, null, () => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should add only origin to request if tabId not provided', function (done) {
|
||||
const messageSender = {
|
||||
url: 'http://mycrypto.com',
|
||||
}
|
||||
const streamTest = createThoughStream((chunk, _, cb) => {
|
||||
if (chunk.data && chunk.data.method) {
|
||||
cb(null, chunk)
|
||||
} else {
|
||||
cb()
|
||||
}
|
||||
})
|
||||
|
||||
metamaskController.setupUntrustedCommunication(streamTest, messageSender)
|
||||
|
||||
const message = {
|
||||
id: 1999133338649204,
|
||||
jsonrpc: '2.0',
|
||||
params: ['mock tx params'],
|
||||
method: 'eth_sendTransaction',
|
||||
}
|
||||
streamTest.write({
|
||||
name: 'provider',
|
||||
data: message,
|
||||
}, null, () => {
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#setupTrustedCommunication', function () {
|
||||
let streamTest
|
||||
|
||||
afterEach(function () {
|
||||
streamTest.end()
|
||||
})
|
||||
|
||||
it('sets up controller dnode api for trusted communication', function (done) {
|
||||
streamTest = createThoughStream((chunk, enc, cb) => {
|
||||
it('sets up controller dnode api for trusted communication', async function () {
|
||||
const messageSender = {
|
||||
url: 'http://mycrypto.com',
|
||||
tab: {},
|
||||
}
|
||||
const { promise, resolve } = deferredPromise()
|
||||
const streamTest = createThoughStream((chunk, _, cb) => {
|
||||
assert.equal(chunk.name, 'controller')
|
||||
resolve()
|
||||
cb()
|
||||
done()
|
||||
})
|
||||
|
||||
metamaskController.setupTrustedCommunication(streamTest, 'mycrypto.com')
|
||||
metamaskController.setupTrustedCommunication(streamTest, messageSender)
|
||||
await promise
|
||||
streamTest.end()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -964,6 +1031,8 @@ describe('MetaMaskController', function () {
|
|||
|
||||
function deferredPromise () {
|
||||
let resolve
|
||||
const promise = new Promise(_resolve => { resolve = _resolve })
|
||||
const promise = new Promise((_resolve) => {
|
||||
resolve = _resolve
|
||||
})
|
||||
return { promise, resolve }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const assert = require('assert')
|
||||
const MessageManager = require('../../../app/scripts/lib/message-manager')
|
||||
import assert from 'assert'
|
||||
import MessageManager from '../../../app/scripts/lib/message-manager'
|
||||
|
||||
describe('Message Manager', function () {
|
||||
let messageManager
|
||||
|
@ -10,7 +10,7 @@ describe('Message Manager', function () {
|
|||
|
||||
describe('#getMsgList', function () {
|
||||
it('when new should return empty array', function () {
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 0)
|
||||
})
|
||||
|
@ -21,9 +21,9 @@ describe('Message Manager', function () {
|
|||
|
||||
describe('#addMsg', function () {
|
||||
it('adds a Msg returned in getMsgList', function () {
|
||||
var Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].id, 1)
|
||||
|
@ -32,10 +32,10 @@ describe('Message Manager', function () {
|
|||
|
||||
describe('#setMsgStatusApproved', function () {
|
||||
it('sets the Msg status to approved', function () {
|
||||
var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
messageManager.setMsgStatusApproved(1)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].status, 'approved')
|
||||
|
@ -44,10 +44,10 @@ describe('Message Manager', function () {
|
|||
|
||||
describe('#rejectMsg', function () {
|
||||
it('sets the Msg status to rejected', function () {
|
||||
var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
messageManager.rejectMsg(1)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].status, 'rejected')
|
||||
|
@ -59,7 +59,7 @@ describe('Message Manager', function () {
|
|||
messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
|
||||
messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
|
||||
messageManager._updateMsg({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
|
||||
var result = messageManager.getMsg('1')
|
||||
const result = messageManager.getMsg('1')
|
||||
assert.equal(result.hash, 'foo')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
const assert = require('assert')
|
||||
|
||||
const PersonalMessageManager = require('../../../app/scripts/lib/personal-message-manager')
|
||||
import assert from 'assert'
|
||||
import PersonalMessageManager from '../../../app/scripts/lib/personal-message-manager'
|
||||
|
||||
describe('Personal Message Manager', function () {
|
||||
let messageManager
|
||||
|
@ -11,7 +10,7 @@ describe('Personal Message Manager', function () {
|
|||
|
||||
describe('#getMsgList', function () {
|
||||
it('when new should return empty array', function () {
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 0)
|
||||
})
|
||||
|
@ -22,9 +21,9 @@ describe('Personal Message Manager', function () {
|
|||
|
||||
describe('#addMsg', function () {
|
||||
it('adds a Msg returned in getMsgList', function () {
|
||||
var Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].id, 1)
|
||||
|
@ -33,10 +32,10 @@ describe('Personal Message Manager', function () {
|
|||
|
||||
describe('#setMsgStatusApproved', function () {
|
||||
it('sets the Msg status to approved', function () {
|
||||
var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
messageManager.setMsgStatusApproved(1)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].status, 'approved')
|
||||
|
@ -45,10 +44,10 @@ describe('Personal Message Manager', function () {
|
|||
|
||||
describe('#rejectMsg', function () {
|
||||
it('sets the Msg status to rejected', function () {
|
||||
var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
const Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
|
||||
messageManager.addMsg(Msg)
|
||||
messageManager.rejectMsg(1)
|
||||
var result = messageManager.messages
|
||||
const result = messageManager.messages
|
||||
assert.ok(Array.isArray(result))
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0].status, 'rejected')
|
||||
|
@ -60,7 +59,7 @@ describe('Personal Message Manager', function () {
|
|||
messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
|
||||
messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
|
||||
messageManager._updateMsg({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
|
||||
var result = messageManager.getMsg('1')
|
||||
const result = messageManager.getMsg('1')
|
||||
assert.equal(result.hash, 'foo')
|
||||
})
|
||||
})
|
||||
|
@ -87,20 +86,20 @@ describe('Personal Message Manager', function () {
|
|||
|
||||
describe('#normalizeMsgData', function () {
|
||||
it('converts text to a utf8 hex string', function () {
|
||||
var input = 'hello'
|
||||
var output = messageManager.normalizeMsgData(input)
|
||||
const input = 'hello'
|
||||
const output = messageManager.normalizeMsgData(input)
|
||||
assert.equal(output, '0x68656c6c6f', 'predictably hex encoded')
|
||||
})
|
||||
|
||||
it('tolerates a hex prefix', function () {
|
||||
var input = '0x12'
|
||||
var output = messageManager.normalizeMsgData(input)
|
||||
const input = '0x12'
|
||||
const output = messageManager.normalizeMsgData(input)
|
||||
assert.equal(output, '0x12', 'un modified')
|
||||
})
|
||||
|
||||
it('tolerates normal hex', function () {
|
||||
var input = '12'
|
||||
var output = messageManager.normalizeMsgData(input)
|
||||
const input = '12'
|
||||
const output = messageManager.normalizeMsgData(input)
|
||||
assert.equal(output, '0x12', 'adds prefix')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('ErrorComponent', () => {
|
|||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ErrorComponent error="Error!"/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
it('shows error', () => {
|
||||
|
@ -35,7 +35,7 @@ describe('ErrorComponent', () => {
|
|||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ErrorComponent/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ describe('ChooseContractExecutor component', () => {
|
|||
inputValues={[]}
|
||||
txParams={{}}
|
||||
/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('ExecutorCell component', () => {
|
|||
}}
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('SendHeader component', () => {
|
|||
<SendHeader
|
||||
title={'Execute Method'}
|
||||
/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
it('renders correct title', () => {
|
||||
|
|
|
@ -33,7 +33,7 @@ describe('SendProfile component', () => {
|
|||
wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<SendProfile/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
it('shows identity name', () => {
|
||||
|
|
|
@ -55,7 +55,7 @@ describe('Dropdown components', function () {
|
|||
closeMenu,
|
||||
onClick,
|
||||
}, 'Item 2'),
|
||||
]
|
||||
],
|
||||
), store)
|
||||
dropdownComponent = component
|
||||
})
|
||||
|
|
|
@ -44,7 +44,7 @@ describe('Token Cell', () => {
|
|||
currentCurrency={'usd'}
|
||||
image={'./test-image'}
|
||||
/>
|
||||
</Provider>
|
||||
</Provider>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -323,6 +323,8 @@ var actions = {
|
|||
SET_DPROVIDER: 'SET_DPROVIDER',
|
||||
setDProvider,
|
||||
|
||||
setUsePhishDetect,
|
||||
|
||||
// locale
|
||||
SET_CURRENT_LOCALE: 'SET_CURRENT_LOCALE',
|
||||
SET_LOCALE_MESSAGES: 'SET_LOCALE_MESSAGES',
|
||||
|
@ -1863,7 +1865,7 @@ function addTokens (tokens) {
|
|||
.entries(tokens)
|
||||
.map(([_, { address, symbol, decimals, image, network }]) => (
|
||||
dispatch(addToken(address, symbol, decimals, image, network))
|
||||
))
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2634,6 +2636,19 @@ function setUseBlockie (val) {
|
|||
}
|
||||
}
|
||||
|
||||
function setUsePhishDetect (val) {
|
||||
return (dispatch) => {
|
||||
dispatch(showLoadingIndication())
|
||||
log.debug(`background.setUsePhishDetect`)
|
||||
background.setUsePhishDetect(val, (err) => {
|
||||
dispatch(hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(displayWarning(err.message))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updateCurrentLocale (key) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
|
|
@ -34,7 +34,7 @@ export default class Button extends Component {
|
|||
'button',
|
||||
typeHash[type],
|
||||
large && CLASSNAME_LARGE,
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{ ...buttonProps }
|
||||
>
|
||||
|
|
|
@ -11,7 +11,7 @@ storiesOf('Button', module)
|
|||
type="primary"
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
</Button>,
|
||||
)
|
||||
.add('secondary', () =>
|
||||
<Button
|
||||
|
@ -19,7 +19,7 @@ storiesOf('Button', module)
|
|||
type="secondary"
|
||||
>
|
||||
{text('text', 'Click me')}
|
||||
</Button>
|
||||
</Button>,
|
||||
)
|
||||
.add('default', () => (
|
||||
<Button
|
||||
|
|
|
@ -135,7 +135,7 @@ class AccountDropdowns extends Component {
|
|||
]),
|
||||
|
||||
]),
|
||||
]
|
||||
],
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ class AccountDropdowns extends Component {
|
|||
style: {
|
||||
marginLeft: '8px',
|
||||
},
|
||||
}
|
||||
},
|
||||
),
|
||||
h('span', {
|
||||
style: {
|
||||
|
@ -226,7 +226,7 @@ class AccountDropdowns extends Component {
|
|||
style: {
|
||||
marginLeft: '8px',
|
||||
},
|
||||
}
|
||||
},
|
||||
),
|
||||
h('span', {
|
||||
style: {
|
||||
|
@ -237,9 +237,9 @@ class AccountDropdowns extends Component {
|
|||
lineHeight: '23px',
|
||||
},
|
||||
}, this.context.t('importAccount')),
|
||||
]
|
||||
],
|
||||
),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ class AccountDropdowns extends Component {
|
|||
this.context.t('addToken'),
|
||||
),
|
||||
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -393,9 +393,9 @@ class AccountDropdowns extends Component {
|
|||
})
|
||||
},
|
||||
},
|
||||
this.renderAccountOptions()
|
||||
this.renderAccountOptions(),
|
||||
),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,10 @@ class Dropdown extends Component {
|
|||
border-radius: 4px;
|
||||
}
|
||||
li.dropdown-menu-item { color: rgb(185, 185, 185); }
|
||||
`
|
||||
`,
|
||||
),
|
||||
...children,
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class DropdownMenuItem extends Component {
|
|||
color: 'white',
|
||||
}, style),
|
||||
},
|
||||
children
|
||||
children,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ Item.prototype.render = function () {
|
|||
return children
|
||||
? h('div', { className: itemClassName, onClick }, children)
|
||||
: h('div.menu__item', { className: itemClassName, onClick }, [ iconComponent, textComponent ]
|
||||
.filter(d => Boolean(d))
|
||||
.filter(d => Boolean(d)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,6 @@ NetworkDropdownIcon.prototype.render = function () {
|
|||
height: `${diameter}px`,
|
||||
width: `${diameter}px`,
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ NetworkDropdown.contextTypes = {
|
|||
|
||||
module.exports = compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps, mapDispatchToProps)
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
)(NetworkDropdown)
|
||||
|
||||
|
||||
|
@ -110,7 +110,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
|
||||
h('div.network-dropdown-content',
|
||||
{},
|
||||
this.context.t('defaultNetwork')
|
||||
this.context.t('defaultNetwork'),
|
||||
),
|
||||
]),
|
||||
|
||||
|
@ -133,7 +133,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'mainnet' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('mainnet')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
h(
|
||||
|
@ -155,7 +155,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'ropsten' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('ropsten')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
h(
|
||||
|
@ -177,7 +177,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'kovan' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('kovan')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
h(
|
||||
|
@ -199,7 +199,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'rinkeby' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('rinkeby')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
h(
|
||||
|
@ -221,7 +221,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'poa' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('poa')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
h(
|
||||
|
@ -243,7 +243,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: providerType === 'localhost' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('localhost')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
this.renderCustomOption(props.provider),
|
||||
|
@ -267,7 +267,7 @@ NetworkDropdown.prototype.render = function () {
|
|||
color: activeNetwork === 'custom' ? '#ffffff' : '#9b9b9b',
|
||||
},
|
||||
}, this.context.t('customRPC')),
|
||||
]
|
||||
],
|
||||
),
|
||||
|
||||
])
|
||||
|
@ -334,7 +334,7 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) {
|
|||
props.delRpcTarget(rpc)
|
||||
},
|
||||
}),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -372,7 +372,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) {
|
|||
color: '#ffffff',
|
||||
},
|
||||
}, rpcTarget),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class SimpleDropdown extends Component {
|
|||
h('div.simple-dropdown__selected', this.getDisplayValue() || placeholder || 'Select'),
|
||||
h('i.fa.fa-caret-down.fa-lg.simple-dropdown__caret'),
|
||||
isOpen && this.renderOptions(),
|
||||
]
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ describe('', () => {
|
|||
style = {{test: 'style'}}
|
||||
closeMenu = {closeMenuSpy}
|
||||
>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuItem>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ describe('Dropdown Menu Components', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Menu className = {'Test Class'} isShowing = {true}/>
|
||||
<Menu className = {'Test Class'} isShowing = {true}/>,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -33,7 +33,7 @@ describe('Dropdown Menu Components', () => {
|
|||
text = {'test text'}
|
||||
className = {'test className'}
|
||||
onClick = {onClickSpy}
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('ModalContent Component', () => {
|
|||
const wrapper = shallow(
|
||||
<ModalContent
|
||||
title="Modal Title"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
assert.equal(wrapper.find('.modal-content__title').length, 1)
|
||||
|
@ -20,7 +20,7 @@ describe('ModalContent Component', () => {
|
|||
const wrapper = shallow(
|
||||
<ModalContent
|
||||
description="Modal Description"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
assert.equal(wrapper.find('.modal-content__title').length, 0)
|
||||
|
@ -33,7 +33,7 @@ describe('ModalContent Component', () => {
|
|||
<ModalContent
|
||||
title="Modal Title"
|
||||
description="Modal Description"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
assert.equal(wrapper.find('.modal-content__title').length, 1)
|
||||
|
|
|
@ -24,7 +24,7 @@ describe('Modal Component', () => {
|
|||
cancelText="Cancel"
|
||||
onSubmit={handleSubmit}
|
||||
submitText="Submit"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
const buttons = wrapper.find(Button)
|
||||
|
@ -54,7 +54,7 @@ describe('Modal Component', () => {
|
|||
onSubmit={() => {}}
|
||||
submitText="Submit"
|
||||
submitType="confirm"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
const buttons = wrapper.find(Button)
|
||||
|
@ -72,7 +72,7 @@ describe('Modal Component', () => {
|
|||
submitText="Submit"
|
||||
>
|
||||
<div className="test-child" />
|
||||
</Modal>
|
||||
</Modal>,
|
||||
)
|
||||
|
||||
assert.ok(wrapper.find('.test-class'))
|
||||
|
@ -89,7 +89,7 @@ describe('Modal Component', () => {
|
|||
submitText="Submit"
|
||||
headerText="My Header"
|
||||
onClose={handleCancel}
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
||||
assert.ok(wrapper.find('.modal-container__header'))
|
||||
|
|
|
@ -80,7 +80,7 @@ BuyOptions.prototype.render = function () {
|
|||
this.renderModalContentOption(
|
||||
this.context.t('directDeposit'),
|
||||
this.context.t('depositFromAccount'),
|
||||
() => this.goToAccountDetailsModal()
|
||||
() => this.goToAccountDetailsModal(),
|
||||
),
|
||||
|
||||
]),
|
||||
|
|
|
@ -78,7 +78,7 @@ ExportPrivateKeyModal.prototype.exportAccountAndGetPrivateKey = function (passwo
|
|||
ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) {
|
||||
return h('span.private-key-password-label', privateKey
|
||||
? this.context.t('copyPrivateKey')
|
||||
: this.context.t('typePassword')
|
||||
: this.context.t('typePassword'),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -296,7 +296,7 @@ function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', selecte
|
|||
if (!selectedToken) return
|
||||
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
||||
abi.rawEncode(['address', 'uint256'], [toAddress, ethUtil.addHexPrefix(amount)]),
|
||||
x => ('00' + x.toString(16)).slice(-2)
|
||||
x => ('00' + x.toString(16)).slice(-2),
|
||||
).join('')
|
||||
}
|
||||
|
||||
|
|
|
@ -7,41 +7,41 @@ storiesOf('TextField', module)
|
|||
<TextField
|
||||
label="Text"
|
||||
type="text"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('password', () =>
|
||||
<TextField
|
||||
label="Password"
|
||||
type="password"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('error', () =>
|
||||
<TextField
|
||||
type="text"
|
||||
label="Name"
|
||||
error="Invalid value"
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('Mascara text', () =>
|
||||
<TextField
|
||||
label="Text"
|
||||
type="text"
|
||||
largeLabel
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('Material text', () =>
|
||||
<TextField
|
||||
label="Text"
|
||||
type="text"
|
||||
material
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('Material password', () =>
|
||||
<TextField
|
||||
label="Password"
|
||||
type="password"
|
||||
material
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
.add('Material error', () =>
|
||||
<TextField
|
||||
|
@ -49,5 +49,5 @@ storiesOf('TextField', module)
|
|||
label="Name"
|
||||
error="Invalid value"
|
||||
material
|
||||
/>
|
||||
/>,
|
||||
)
|
||||
|
|
|
@ -65,7 +65,7 @@ TokenCell.prototype.render = function () {
|
|||
if (contractExchangeRates[address]) {
|
||||
currentTokenToFiatRate = multiplyCurrencies(
|
||||
contractExchangeRates[address],
|
||||
conversionRate
|
||||
conversionRate,
|
||||
)
|
||||
currentTokenInFiat = conversionUtil(string, {
|
||||
fromNumericBase: 'dec',
|
||||
|
|
|
@ -70,7 +70,7 @@ const baseChange = {
|
|||
const fromAndToCurrencyPropsNotEqual = R.compose(
|
||||
R.not,
|
||||
R.eqBy(R.__, 'fromCurrency', 'toCurrency'),
|
||||
R.flip(R.prop)
|
||||
R.flip(R.prop),
|
||||
)
|
||||
|
||||
// Lens
|
||||
|
@ -82,8 +82,8 @@ const whenPredSetCRWithPropAndSetter = (pred, prop, setter) => R.when(
|
|||
pred,
|
||||
R.converge(
|
||||
conversionRateLens,
|
||||
[R.pipe(R.prop(prop), setter), R.identity]
|
||||
)
|
||||
[R.pipe(R.prop(prop), setter), R.identity],
|
||||
),
|
||||
)
|
||||
|
||||
// conditional 'value' setting wrappers
|
||||
|
@ -91,13 +91,13 @@ const whenPredSetWithPropAndSetter = (pred, prop, setter) => R.when(
|
|||
pred,
|
||||
R.converge(
|
||||
valuePropertyLens,
|
||||
[R.pipe(R.prop(prop), setter), R.identity]
|
||||
)
|
||||
[R.pipe(R.prop(prop), setter), R.identity],
|
||||
),
|
||||
)
|
||||
const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter(
|
||||
R.prop(prop),
|
||||
prop,
|
||||
R.prop(R.__, setterMap)
|
||||
R.prop(R.__, setterMap),
|
||||
)
|
||||
|
||||
// Conversion utility function
|
||||
|
@ -111,7 +111,7 @@ const converter = R.pipe(
|
|||
whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round),
|
||||
whenPredSetWithPropAndSetter(R.prop('roundDown'), 'roundDown', roundDown),
|
||||
whenPropApplySetterMap('toNumericBase', baseChange),
|
||||
R.view(R.lensProp('value'))
|
||||
R.view(R.lensProp('value')),
|
||||
)
|
||||
|
||||
const conversionUtil = (value, {
|
||||
|
@ -209,7 +209,7 @@ const conversionMax = (
|
|||
) => {
|
||||
const firstIsGreater = conversionGreaterThan(
|
||||
{ ...firstProps },
|
||||
{ ...secondProps }
|
||||
{ ...secondProps },
|
||||
)
|
||||
|
||||
return firstIsGreater ? firstProps.value : secondProps.value
|
||||
|
|
|
@ -78,7 +78,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
it('should initialize state', () => {
|
||||
assert.deepEqual(
|
||||
ConfirmTransactionReducer({}),
|
||||
initialState
|
||||
initialState,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -106,7 +106,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
...mockState.confirmTransaction.txData,
|
||||
id: 2,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -118,7 +118,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
txData: {},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -136,7 +136,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
...mockState.confirmTransaction.tokenData,
|
||||
name: 'defToken',
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -148,7 +148,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
tokenData: {},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -166,7 +166,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
...mockState.confirmTransaction.methodData,
|
||||
name: 'transferFrom',
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -178,7 +178,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
methodData: {},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -197,7 +197,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
fiatTransactionAmount: '123.45',
|
||||
ethTransactionAmount: '.5',
|
||||
hexTransactionAmount: '0x1',
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -216,7 +216,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
fiatTransactionFee: '123.45',
|
||||
ethTransactionFee: '.5',
|
||||
hexTransactionFee: '0x1',
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -235,7 +235,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
fiatTransactionTotal: '123.45',
|
||||
ethTransactionTotal: '.5',
|
||||
hexTransactionTotal: '0x1',
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -254,7 +254,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
tokenSymbol: 'DEF',
|
||||
tokenDecimals: '1',
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -267,7 +267,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
nonce: '0x1',
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -280,7 +280,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
toSmartContract: true,
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -292,7 +292,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
{
|
||||
...mockState.confirmTransaction,
|
||||
fetchingData: true,
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -303,7 +303,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
}),
|
||||
{
|
||||
fetchingData: false,
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -314,7 +314,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
}),
|
||||
{
|
||||
...initialState,
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -329,7 +329,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTxData(txData),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -340,7 +340,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.clearTxData(),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -353,7 +353,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTokenData(tokenData),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -364,7 +364,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.clearTokenData(),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -377,7 +377,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateMethodData(methodData),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -388,7 +388,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.clearMethodData(),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -401,7 +401,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTransactionAmounts(transactionAmounts),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -414,7 +414,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTransactionFees(transactionFees),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -427,7 +427,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTransactionTotals(transactionTotals),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -443,7 +443,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateTokenProps(tokenProps),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -456,7 +456,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.updateNonce(nonce),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -467,7 +467,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.setFetchingData(true),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -478,7 +478,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.setFetchingData(false),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -489,7 +489,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
|
||||
assert.deepEqual(
|
||||
actions.clearConfirmTransaction(),
|
||||
expectedAction
|
||||
expectedAction,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -498,7 +498,7 @@ describe('Confirm Transaction Duck', () => {
|
|||
beforeEach(() => {
|
||||
global.eth = {
|
||||
getCode: sinon.stub().callsFake(
|
||||
address => Promise.resolve(address && address.match(/isContract/) ? 'not-0x' : '0x')
|
||||
address => Promise.resolve(address && address.match(/isContract/) ? 'not-0x' : '0x'),
|
||||
),
|
||||
}
|
||||
})
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('Send Duck', () => {
|
|||
it('should initialize state', () => {
|
||||
assert.deepEqual(
|
||||
SendReducer({}),
|
||||
initState
|
||||
initState,
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -40,7 +40,7 @@ describe('Send Duck', () => {
|
|||
type: 'someOtherAction',
|
||||
value: 'someValue',
|
||||
}),
|
||||
Object.assign({}, mockState.send)
|
||||
Object.assign({}, mockState.send),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -49,7 +49,7 @@ describe('Send Duck', () => {
|
|||
SendReducer(mockState, {
|
||||
type: OPEN_FROM_DROPDOWN,
|
||||
}),
|
||||
Object.assign({fromDropdownOpen: true}, mockState.send)
|
||||
Object.assign({fromDropdownOpen: true}, mockState.send),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -63,7 +63,7 @@ describe('Send Duck', () => {
|
|||
SendReducer(mockState, {
|
||||
type: CLOSE_FROM_DROPDOWN,
|
||||
}),
|
||||
Object.assign({fromDropdownOpen: false}, mockState.send)
|
||||
Object.assign({fromDropdownOpen: false}, mockState.send),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -72,7 +72,7 @@ describe('Send Duck', () => {
|
|||
SendReducer(mockState, {
|
||||
type: OPEN_TO_DROPDOWN,
|
||||
}),
|
||||
Object.assign({toDropdownOpen: true}, mockState.send)
|
||||
Object.assign({toDropdownOpen: true}, mockState.send),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -81,7 +81,7 @@ describe('Send Duck', () => {
|
|||
SendReducer(mockState, {
|
||||
type: CLOSE_TO_DROPDOWN,
|
||||
}),
|
||||
Object.assign({toDropdownOpen: false}, mockState.send)
|
||||
Object.assign({toDropdownOpen: false}, mockState.send),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -103,7 +103,7 @@ describe('Send Duck', () => {
|
|||
someError: false,
|
||||
someOtherError: true,
|
||||
},
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -112,7 +112,7 @@ describe('Send Duck', () => {
|
|||
SendReducer(mockState, {
|
||||
type: RESET_SEND_STATE,
|
||||
}),
|
||||
Object.assign({}, initState)
|
||||
Object.assign({}, initState),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -120,35 +120,35 @@ describe('Send Duck', () => {
|
|||
describe('openFromDropdown', () => {
|
||||
assert.deepEqual(
|
||||
openFromDropdown(),
|
||||
{ type: OPEN_FROM_DROPDOWN }
|
||||
{ type: OPEN_FROM_DROPDOWN },
|
||||
)
|
||||
})
|
||||
|
||||
describe('closeFromDropdown', () => {
|
||||
assert.deepEqual(
|
||||
closeFromDropdown(),
|
||||
{ type: CLOSE_FROM_DROPDOWN }
|
||||
{ type: CLOSE_FROM_DROPDOWN },
|
||||
)
|
||||
})
|
||||
|
||||
describe('openToDropdown', () => {
|
||||
assert.deepEqual(
|
||||
openToDropdown(),
|
||||
{ type: OPEN_TO_DROPDOWN }
|
||||
{ type: OPEN_TO_DROPDOWN },
|
||||
)
|
||||
})
|
||||
|
||||
describe('closeToDropdown', () => {
|
||||
assert.deepEqual(
|
||||
closeToDropdown(),
|
||||
{ type: CLOSE_TO_DROPDOWN }
|
||||
{ type: CLOSE_TO_DROPDOWN },
|
||||
)
|
||||
})
|
||||
|
||||
describe('updateSendErrors', () => {
|
||||
assert.deepEqual(
|
||||
updateSendErrors('mockErrorObject'),
|
||||
{ type: UPDATE_SEND_ERRORS, value: 'mockErrorObject' }
|
||||
{ type: UPDATE_SEND_ERRORS, value: 'mockErrorObject' },
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -18,28 +18,28 @@ describe('Confirm Transaction utils', () => {
|
|||
it('should return true if the first value is greater than the second value', () => {
|
||||
assert.equal(
|
||||
utils.hexGreaterThan('0xb', '0xa'),
|
||||
true
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
it('should return false if the first value is less than the second value', () => {
|
||||
assert.equal(
|
||||
utils.hexGreaterThan('0xa', '0xb'),
|
||||
false
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it('should return false if the first value is equal to the second value', () => {
|
||||
assert.equal(
|
||||
utils.hexGreaterThan('0xa', '0xa'),
|
||||
false
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it('should correctly compare prefixed and non-prefixed hex values', () => {
|
||||
assert.equal(
|
||||
utils.hexGreaterThan('0xb', 'a'),
|
||||
true
|
||||
true,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -48,14 +48,14 @@ describe('Confirm Transaction utils', () => {
|
|||
it('should multiply the hex gasLimit and hex gasPrice values together', () => {
|
||||
assert.equal(
|
||||
utils.getHexGasTotal({ gasLimit: '0x5208', gasPrice: '0x3b9aca00' }),
|
||||
'0x1319718a5000'
|
||||
'0x1319718a5000',
|
||||
)
|
||||
})
|
||||
|
||||
it('should prefix the result with 0x', () => {
|
||||
assert.equal(
|
||||
utils.getHexGasTotal({ gasLimit: '5208', gasPrice: '3b9aca00' }),
|
||||
'0x1319718a5000'
|
||||
'0x1319718a5000',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -64,14 +64,14 @@ describe('Confirm Transaction utils', () => {
|
|||
it('should add two values together rounding to 6 decimal places', () => {
|
||||
assert.equal(
|
||||
utils.addEth('0.12345678', '0'),
|
||||
'0.123457'
|
||||
'0.123457',
|
||||
)
|
||||
})
|
||||
|
||||
it('should add any number of values together rounding to 6 decimal places', () => {
|
||||
assert.equal(
|
||||
utils.addEth('0.1', '0.02', '0.003', '0.0004', '0.00005', '0.000006', '0.0000007'),
|
||||
'0.123457'
|
||||
'0.123457',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -80,14 +80,14 @@ describe('Confirm Transaction utils', () => {
|
|||
it('should add two values together rounding to 2 decimal places', () => {
|
||||
assert.equal(
|
||||
utils.addFiat('0.12345678', '0'),
|
||||
'0.12'
|
||||
'0.12',
|
||||
)
|
||||
})
|
||||
|
||||
it('should add any number of values together rounding to 2 decimal places', () => {
|
||||
assert.equal(
|
||||
utils.addFiat('0.1', '0.02', '0.003', '0.0004', '0.00005', '0.000006', '0.0000007'),
|
||||
'0.12'
|
||||
'0.12',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -48,6 +48,6 @@ const mapStateToProps = state => {
|
|||
}
|
||||
|
||||
module.exports = compose(
|
||||
connect(mapStateToProps)
|
||||
connect(mapStateToProps),
|
||||
)(I18nProvider)
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ const selectors = {
|
|||
getTotalUnapprovedCount,
|
||||
preferencesSelector,
|
||||
getMetaMaskAccounts,
|
||||
getUsePhishDetect,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
@ -69,6 +70,10 @@ function getMetaMaskAccounts (state) {
|
|||
return selectedAccounts
|
||||
}
|
||||
|
||||
function getUsePhishDetect (state) {
|
||||
return Boolean(state.metamask.usePhishDetect)
|
||||
}
|
||||
|
||||
function getSelectedAccount (state) {
|
||||
const accounts = getMetaMaskAccounts(state)
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
|
@ -166,7 +171,7 @@ function getSelectedTokenToFiatRate (state) {
|
|||
const tokenToFiatRate = multiplyCurrencies(
|
||||
conversionRate,
|
||||
selectedTokenExchangeRate,
|
||||
{ toNumericBase: 'dec' }
|
||||
{ toNumericBase: 'dec' },
|
||||
)
|
||||
|
||||
return tokenToFiatRate
|
||||
|
|
|
@ -20,14 +20,14 @@ export const unconfirmedTransactionsListSelector = createSelector(
|
|||
unapprovedMsgs = {},
|
||||
unapprovedPersonalMsgs = {},
|
||||
unapprovedTypedMessages = {},
|
||||
network
|
||||
network,
|
||||
) => txHelper(
|
||||
unapprovedTxs,
|
||||
unapprovedMsgs,
|
||||
unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages,
|
||||
network
|
||||
) || []
|
||||
network,
|
||||
) || [],
|
||||
)
|
||||
|
||||
export const unconfirmedTransactionsHashSelector = createSelector(
|
||||
|
@ -41,7 +41,7 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||
unapprovedMsgs = {},
|
||||
unapprovedPersonalMsgs = {},
|
||||
unapprovedTypedMessages = {},
|
||||
network
|
||||
network,
|
||||
) => {
|
||||
const filteredUnapprovedTxs = Object.keys(unapprovedTxs).reduce((acc, address) => {
|
||||
const { metamaskNetworkId } = unapprovedTxs[address]
|
||||
|
@ -60,7 +60,7 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||
...unapprovedPersonalMsgs,
|
||||
...unapprovedTypedMessages,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const unapprovedMsgCountSelector = state => state.metamask.unapprovedMsgCount
|
||||
|
@ -78,7 +78,7 @@ export const unconfirmedTransactionsCountSelector = createSelector(
|
|||
unapprovedMsgCount = 0,
|
||||
unapprovedPersonalMsgCount = 0,
|
||||
unapprovedTypedMessagesCount = 0,
|
||||
network
|
||||
network,
|
||||
) => {
|
||||
const filteredUnapprovedTxIds = Object.keys(unapprovedTxs).filter(txId => {
|
||||
const { metamaskNetworkId } = unapprovedTxs[txId]
|
||||
|
@ -87,7 +87,7 @@ export const unconfirmedTransactionsCountSelector = createSelector(
|
|||
|
||||
return filteredUnapprovedTxIds.length + unapprovedTypedMessagesCount + unapprovedMsgCount +
|
||||
unapprovedPersonalMsgCount
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
@ -102,22 +102,22 @@ const contractExchangeRatesSelector = state => state.metamask.contractExchangeRa
|
|||
|
||||
const tokenDecimalsSelector = createSelector(
|
||||
tokenPropsSelector,
|
||||
tokenProps => tokenProps && tokenProps.tokenDecimals
|
||||
tokenProps => tokenProps && tokenProps.tokenDecimals,
|
||||
)
|
||||
|
||||
const tokenDataParamsSelector = createSelector(
|
||||
tokenDataSelector,
|
||||
tokenData => tokenData && tokenData.params || []
|
||||
tokenData => tokenData && tokenData.params || [],
|
||||
)
|
||||
|
||||
const txParamsSelector = createSelector(
|
||||
txDataSelector,
|
||||
txData => txData && txData.txParams || {}
|
||||
txData => txData && txData.txParams || {},
|
||||
)
|
||||
|
||||
export const tokenAddressSelector = createSelector(
|
||||
txParamsSelector,
|
||||
txParams => txParams && txParams.to
|
||||
txParams => txParams && txParams.to,
|
||||
)
|
||||
|
||||
const TOKEN_PARAM_SPENDER = '_spender'
|
||||
|
@ -147,7 +147,7 @@ export const tokenAmountAndToAddressSelector = createSelector(
|
|||
toAddress,
|
||||
tokenAmount,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const approveTokenAmountAndToAddressSelector = createSelector(
|
||||
|
@ -172,7 +172,7 @@ export const approveTokenAmountAndToAddressSelector = createSelector(
|
|||
toAddress,
|
||||
tokenAmount,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const sendTokenTokenAmountAndToAddressSelector = createSelector(
|
||||
|
@ -197,11 +197,11 @@ export const sendTokenTokenAmountAndToAddressSelector = createSelector(
|
|||
toAddress,
|
||||
tokenAmount,
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const contractExchangeRateSelector = createSelector(
|
||||
contractExchangeRatesSelector,
|
||||
tokenAddressSelector,
|
||||
(contractExchangeRates, tokenAddress) => contractExchangeRates[tokenAddress]
|
||||
(contractExchangeRates, tokenAddress) => contractExchangeRates[tokenAddress],
|
||||
)
|
||||
|
|
|
@ -7,5 +7,5 @@ export const selectedTokenSelector = createSelector(
|
|||
selectedTokenAddressSelector,
|
||||
(tokens = [], selectedTokenAddress = '') => {
|
||||
return tokens.find(({ address }) => address === selectedTokenAddress)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
@ -33,26 +33,26 @@ export const transactionsSelector = createSelector(
|
|||
.sort((a, b) => b.time - a.time)
|
||||
: txsToRender
|
||||
.sort((a, b) => b.time - a.time)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const pendingTransactionsSelector = createSelector(
|
||||
transactionsSelector,
|
||||
(transactions = []) => (
|
||||
transactions.filter(transaction => transaction.status in pendingStatusHash).reverse()
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
export const submittedPendingTransactionsSelector = createSelector(
|
||||
transactionsSelector,
|
||||
(transactions = []) => (
|
||||
transactions.filter(transaction => transaction.status === SUBMITTED_STATUS)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
export const completedTransactionsSelector = createSelector(
|
||||
transactionsSelector,
|
||||
(transactions = []) => (
|
||||
transactions.filter(transaction => !(transaction.status in pendingStatusHash))
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -71,6 +71,6 @@ export default compose(
|
|||
mapStateToProps,
|
||||
dispatch => ({
|
||||
closeWelcomeScreen: () => dispatch(closeWelcomeScreen()),
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)(WelcomeScreen)
|
||||
|
|
|
@ -79,7 +79,7 @@ async function startApp (metamaskState, accountManager, opts) {
|
|||
h(Root, {
|
||||
// inject initial state
|
||||
store: store,
|
||||
}
|
||||
},
|
||||
), opts.container)
|
||||
|
||||
return store
|
||||
|
|
Loading…
Reference in New Issue