mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #6599 from MetaMask/develop
Master Version Bump (v6.5.0)
This commit is contained in:
commit
bf5b7f8e77
@ -133,7 +133,7 @@
|
||||
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
|
||||
"no-unreachable": 2,
|
||||
"no-unsafe-finally": 2,
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
|
||||
"no-unused-vars": [2, { "vars": "all", "args": "all", "argsIgnorePattern": "[_]+" }],
|
||||
"no-useless-call": 2,
|
||||
"no-useless-computed-key": 2,
|
||||
"no-useless-constructor": 2,
|
||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -2,13 +2,29 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 6.5.1 Tue May 14 2019
|
||||
|
||||
- Fix bug where approve method would show a warning. #6602
|
||||
- [#6593](https://github.com/MetaMask/metamask-extension/pull/6593): Fix wording of autoLogoutTimeLimitDescription
|
||||
|
||||
## 6.5.0 Fri May 10 2019
|
||||
|
||||
- [#6568](https://github.com/MetaMask/metamask-extension/pull/6568): feature: integrate gaba/PhishingController
|
||||
- [#6490](https://github.com/MetaMask/metamask-extension/pull/6490): Redesign custom RPC form
|
||||
- [#6558](https://github.com/MetaMask/metamask-extension/pull/6558): Adds auto logout with customizable time frame
|
||||
- [#6578](https://github.com/MetaMask/metamask-extension/pull/6578): Fixes ability to send to token contract addresses
|
||||
- [#6557](https://github.com/MetaMask/metamask-extension/pull/6557): Adds drag and drop functionality to seed phrase entry.
|
||||
- [#6526](https://github.com/MetaMask/metamask-extension/pull/6526): Include token checksum address in prices lookup for token rates
|
||||
- [#6502](https://github.com/MetaMask/metamask-extension/pull/6502): Add subheader to all settings subviews
|
||||
- [#6501](https://github.com/MetaMask/metamask-extension/pull/6501): Improve confirm screen loading performance by fixing home screen rendering bug
|
||||
|
||||
## 6.4.1 Fri Apr 26 2019
|
||||
|
||||
- [#6521](https://github.com/MetaMask/metamask-extension/pull/6521): Revert "Adds 4byte registry fallback to getMethodData()" to fix stalling bug.
|
||||
|
||||
## 6.4.0 Wed Apr 17 2019
|
||||
|
||||
- [#6445](https://github.com/MetaMask/metamask-extension/pull/6445): * Move send to pages/
|
||||
- [#6445](https://github.com/MetaMask/metamask-extension/pull/6445): * Move send to pages/
|
||||
- [#6470](https://github.com/MetaMask/metamask-extension/pull/6470): update publishing.md with dev diagram
|
||||
- [#6403](https://github.com/MetaMask/metamask-extension/pull/6403): Update to eth-method-registry@1.2.0
|
||||
- [#6468](https://github.com/MetaMask/metamask-extension/pull/6468): Fix switcher height when Custom RPC is selected or loading
|
||||
|
@ -83,6 +83,9 @@
|
||||
"address": {
|
||||
"message": "Address"
|
||||
},
|
||||
"addNetwork": {
|
||||
"message": "Add Network"
|
||||
},
|
||||
"advanced": {
|
||||
"message": "Advanced"
|
||||
},
|
||||
@ -154,6 +157,12 @@
|
||||
"attributions": {
|
||||
"message": "Attributions"
|
||||
},
|
||||
"autoLogoutTimeLimit": {
|
||||
"message": "Auto-Logout Timer (minutes)"
|
||||
},
|
||||
"autoLogoutTimeLimitDescription": {
|
||||
"message": "Set the idle time in minutes before MetaMask will automatically log out"
|
||||
},
|
||||
"available": {
|
||||
"message": "Available"
|
||||
},
|
||||
@ -185,6 +194,13 @@
|
||||
"message": "must be greater than or equal to $1 and less than or equal to $2.",
|
||||
"description": "helper for inputting hex as decimal input"
|
||||
},
|
||||
"blockExplorerUrl": {
|
||||
"message": "Block Explorer"
|
||||
},
|
||||
"blockExplorerView": {
|
||||
"message": "View account at $1",
|
||||
"description": "$1 replaced by URL for custom block explorer"
|
||||
},
|
||||
"blockiesIdenticon": {
|
||||
"message": "Use Blockies Identicon"
|
||||
},
|
||||
@ -224,6 +240,9 @@
|
||||
"ok": {
|
||||
"message": "Ok"
|
||||
},
|
||||
"optionalBlockExplorerUrl": {
|
||||
"message": "Block Explorer URL (optional)"
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancel"
|
||||
},
|
||||
@ -239,6 +258,9 @@
|
||||
"cancelN": {
|
||||
"message": "Cancel all $1 transactions"
|
||||
},
|
||||
"chainId": {
|
||||
"message": "Chain ID"
|
||||
},
|
||||
"classicInterface": {
|
||||
"message": "Use classic interface"
|
||||
},
|
||||
@ -496,6 +518,9 @@
|
||||
"edit": {
|
||||
"message": "Edit"
|
||||
},
|
||||
"editNetwork": {
|
||||
"message": "Edit Network"
|
||||
},
|
||||
"editAccountName": {
|
||||
"message": "Edit Account Name"
|
||||
},
|
||||
@ -928,9 +953,15 @@
|
||||
"negativeETH": {
|
||||
"message": "Can not send negative amounts of ETH."
|
||||
},
|
||||
"networkName": {
|
||||
"message": "Network Name"
|
||||
},
|
||||
"networks": {
|
||||
"message": "Networks"
|
||||
},
|
||||
"networkSettingsDescription": {
|
||||
"message": "Add and edit custom RPC networks"
|
||||
},
|
||||
"nevermind": {
|
||||
"message": "Nevermind"
|
||||
},
|
||||
@ -971,7 +1002,7 @@
|
||||
"protectYourKeysMessage2": {
|
||||
"message": "Keep your phrase safe. If you see something fishy, or you’re uncertain about a website, email support@metamask.io"
|
||||
},
|
||||
"rpcURL": {
|
||||
"rpcUrl": {
|
||||
"message": "New RPC URL"
|
||||
},
|
||||
"showAdvancedOptions": {
|
||||
@ -1486,6 +1517,9 @@
|
||||
"supportCenter": {
|
||||
"message": "Visit our Support Center"
|
||||
},
|
||||
"symbol": {
|
||||
"message": "Symbol"
|
||||
},
|
||||
"symbolBetweenZeroTwelve": {
|
||||
"message": "Symbol must be between 0 and 12 characters."
|
||||
},
|
||||
@ -1708,9 +1742,15 @@
|
||||
"viewAccount": {
|
||||
"message": "View Account"
|
||||
},
|
||||
"viewOnCustomBlockExplorer": {
|
||||
"message": "View at $1"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "View on Etherscan"
|
||||
},
|
||||
"viewNetworkInfo": {
|
||||
"message": "View Network Info"
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visit our web site"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "6.4.1",
|
||||
"version": "6.5.1",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
@ -1,18 +1,17 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const pump = require('pump')
|
||||
const log = require('loglevel')
|
||||
const Dnode = require('dnode')
|
||||
const querystring = require('querystring')
|
||||
const LocalMessageDuplexStream = require('post-message-stream')
|
||||
const PongStream = require('ping-pong-stream/pong')
|
||||
const ObjectMultiplex = require('obj-multiplex')
|
||||
const extension = require('extensionizer')
|
||||
const PortStream = require('extension-port-stream')
|
||||
const {Transform: TransformStream} = require('stream')
|
||||
|
||||
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
|
||||
const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
|
||||
const inpageBundle = inpageContent + inpageSuffix
|
||||
let isEnabled = false
|
||||
|
||||
// Eventually this streaming injection could be replaced with:
|
||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction
|
||||
@ -23,9 +22,7 @@ let isEnabled = false
|
||||
|
||||
if (shouldInjectWeb3()) {
|
||||
injectScript(inpageBundle)
|
||||
setupStreams()
|
||||
listenForProviderRequest()
|
||||
checkPrivacyMode()
|
||||
start()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,148 +44,107 @@ function injectScript (content) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up two-way communication streams between the
|
||||
* browser extension and local per-page browser context
|
||||
* Sets up the stream communication and submits site metadata
|
||||
*
|
||||
*/
|
||||
function setupStreams () {
|
||||
// setup communication to page and plugin
|
||||
async function start () {
|
||||
await setupStreams()
|
||||
await domIsReady()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up two-way communication streams between the
|
||||
* browser extension and local per-page browser context.
|
||||
*
|
||||
*/
|
||||
async function setupStreams () {
|
||||
// the transport-specific streams for communication between inpage and background
|
||||
const pageStream = new LocalMessageDuplexStream({
|
||||
name: 'contentscript',
|
||||
target: 'inpage',
|
||||
})
|
||||
const pluginPort = extension.runtime.connect({ name: 'contentscript' })
|
||||
const pluginStream = new PortStream(pluginPort)
|
||||
const extensionPort = extension.runtime.connect({ name: 'contentscript' })
|
||||
const extensionStream = new PortStream(extensionPort)
|
||||
|
||||
// Filter out selectedAddress until this origin is enabled
|
||||
const approvalTransform = new TransformStream({
|
||||
objectMode: true,
|
||||
transform: (data, _, done) => {
|
||||
if (typeof data === 'object' && data.name && data.name === 'publicConfig' && !isEnabled) {
|
||||
data.data.selectedAddress = undefined
|
||||
}
|
||||
done(null, { ...data })
|
||||
},
|
||||
})
|
||||
// create and connect channel muxers
|
||||
// so we can handle the channels individually
|
||||
const pageMux = new ObjectMultiplex()
|
||||
pageMux.setMaxListeners(25)
|
||||
const extensionMux = new ObjectMultiplex()
|
||||
extensionMux.setMaxListeners(25)
|
||||
|
||||
// forward communication plugin->inpage
|
||||
pump(
|
||||
pageMux,
|
||||
pageStream,
|
||||
pluginStream,
|
||||
approvalTransform,
|
||||
pageStream,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Contentscript Forwarding', err)
|
||||
)
|
||||
|
||||
// setup local multistream channels
|
||||
const mux = new ObjectMultiplex()
|
||||
mux.setMaxListeners(25)
|
||||
|
||||
pump(
|
||||
mux,
|
||||
pageStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Inpage', err)
|
||||
pageMux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Inpage Multiplex', err)
|
||||
)
|
||||
pump(
|
||||
mux,
|
||||
pluginStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Background', err)
|
||||
extensionMux,
|
||||
extensionStream,
|
||||
extensionMux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask Background Multiplex', err)
|
||||
)
|
||||
|
||||
// connect ping stream
|
||||
const pongStream = new PongStream({ objectMode: true })
|
||||
pump(
|
||||
mux,
|
||||
pongStream,
|
||||
mux,
|
||||
(err) => logStreamDisconnectWarning('MetaMask PingPongStream', err)
|
||||
)
|
||||
// forward communication across inpage-background for these channels only
|
||||
forwardTrafficBetweenMuxers('provider', pageMux, extensionMux)
|
||||
forwardTrafficBetweenMuxers('publicConfig', pageMux, extensionMux)
|
||||
|
||||
// connect phishing warning stream
|
||||
const phishingStream = mux.createStream('phishing')
|
||||
// connect "phishing" channel to warning system
|
||||
const phishingStream = extensionMux.createStream('phishing')
|
||||
phishingStream.once('data', redirectToPhishingWarning)
|
||||
|
||||
// ignore unused channels (handled by background, inpage)
|
||||
mux.ignoreStream('provider')
|
||||
mux.ignoreStream('publicConfig')
|
||||
// connect "publicApi" channel to submit page metadata
|
||||
const publicApiStream = extensionMux.createStream('publicApi')
|
||||
const background = await setupPublicApi(publicApiStream)
|
||||
|
||||
return { background }
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes listeners for requests to fully-enable the provider from the dapp context
|
||||
* and for full-provider approvals and rejections from the background script context. Dapps
|
||||
* should not post messages directly and should instead call provider.enable(), which
|
||||
* handles posting these messages internally.
|
||||
*/
|
||||
function listenForProviderRequest () {
|
||||
window.addEventListener('message', ({ source, data }) => {
|
||||
if (source !== window || !data || !data.type) { return }
|
||||
switch (data.type) {
|
||||
case 'ETHEREUM_ENABLE_PROVIDER':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-provider-request',
|
||||
force: data.force,
|
||||
origin: source.location.hostname,
|
||||
siteImage: getSiteIcon(source),
|
||||
siteTitle: getSiteName(source),
|
||||
})
|
||||
break
|
||||
case 'ETHEREUM_IS_APPROVED':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-is-approved',
|
||||
origin: source.location.hostname,
|
||||
})
|
||||
break
|
||||
case 'METAMASK_IS_UNLOCKED':
|
||||
extension.runtime.sendMessage({
|
||||
action: 'init-is-unlocked',
|
||||
})
|
||||
break
|
||||
function forwardTrafficBetweenMuxers (channelName, muxA, muxB) {
|
||||
const channelA = muxA.createStream(channelName)
|
||||
const channelB = muxB.createStream(channelName)
|
||||
pump(
|
||||
channelA,
|
||||
channelB,
|
||||
channelA,
|
||||
(err) => logStreamDisconnectWarning(`MetaMask muxed traffic for channel "${channelName}" failed.`, err)
|
||||
)
|
||||
}
|
||||
|
||||
async function setupPublicApi (outStream) {
|
||||
const api = {
|
||||
getSiteMetadata: (cb) => cb(null, getSiteMetadata()),
|
||||
}
|
||||
const dnode = Dnode(api)
|
||||
pump(
|
||||
outStream,
|
||||
dnode,
|
||||
outStream,
|
||||
(err) => {
|
||||
// report any error
|
||||
if (err) log.error(err)
|
||||
}
|
||||
})
|
||||
|
||||
extension.runtime.onMessage.addListener(({ action = '', isApproved, caching, isUnlocked, selectedAddress }) => {
|
||||
switch (action) {
|
||||
case 'approve-provider-request':
|
||||
isEnabled = true
|
||||
window.postMessage({ type: 'ethereumprovider', selectedAddress }, '*')
|
||||
break
|
||||
case 'approve-legacy-provider-request':
|
||||
isEnabled = true
|
||||
window.postMessage({ type: 'ethereumproviderlegacy', selectedAddress }, '*')
|
||||
break
|
||||
case 'reject-provider-request':
|
||||
window.postMessage({ type: 'ethereumprovider', error: 'User denied account authorization' }, '*')
|
||||
break
|
||||
case 'answer-is-approved':
|
||||
window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
|
||||
break
|
||||
case 'answer-is-unlocked':
|
||||
window.postMessage({ type: 'metamaskisunlocked', isUnlocked }, '*')
|
||||
break
|
||||
case 'metamask-set-locked':
|
||||
isEnabled = false
|
||||
window.postMessage({ type: 'metamasksetlocked' }, '*')
|
||||
break
|
||||
case 'ethereum-ping-success':
|
||||
window.postMessage({ type: 'ethereumpingsuccess' }, '*')
|
||||
break
|
||||
case 'ethereum-ping-error':
|
||||
window.postMessage({ type: 'ethereumpingerror' }, '*')
|
||||
}
|
||||
})
|
||||
)
|
||||
const background = await new Promise(resolve => dnode.once('remote', resolve))
|
||||
return background
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if MetaMask is currently operating in "privacy mode", meaning
|
||||
* dapps must call ethereum.enable in order to access user accounts
|
||||
* Gets site metadata and returns it
|
||||
*
|
||||
*/
|
||||
function checkPrivacyMode () {
|
||||
extension.runtime.sendMessage({ action: 'init-privacy-request' })
|
||||
function getSiteMetadata () {
|
||||
// get metadata
|
||||
const metadata = {
|
||||
name: getSiteName(window),
|
||||
icon: getSiteIcon(window),
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler for page to plugin stream disconnections
|
||||
* Error handler for page to extension stream disconnections
|
||||
*
|
||||
* @param {string} remoteLabel Remote stream name
|
||||
* @param {Error} err Stream connection error
|
||||
@ -301,6 +257,10 @@ function redirectToPhishingWarning () {
|
||||
})}`
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts a name for the site from the DOM
|
||||
*/
|
||||
function getSiteName (window) {
|
||||
const document = window.document
|
||||
const siteName = document.querySelector('head > meta[property="og:site_name"]')
|
||||
@ -316,6 +276,9 @@ function getSiteName (window) {
|
||||
return document.title
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an icon for the site from the DOM
|
||||
*/
|
||||
function getSiteIcon (window) {
|
||||
const document = window.document
|
||||
|
||||
@ -333,3 +296,13 @@ function getSiteIcon (window) {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves when the DOM is loaded (does not wait for images to load)
|
||||
*/
|
||||
async function domIsReady () {
|
||||
// already loaded
|
||||
if (['interactive', 'complete'].includes(document.readyState)) return
|
||||
// wait for load
|
||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve, { once: true }))
|
||||
}
|
||||
|
73
app/scripts/controllers/app-state.js
Normal file
73
app/scripts/controllers/app-state.js
Normal file
@ -0,0 +1,73 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const extend = require('xtend')
|
||||
|
||||
class AppStateController {
|
||||
/**
|
||||
* @constructor
|
||||
* @param opts
|
||||
*/
|
||||
constructor (opts = {}) {
|
||||
const {initState, onInactiveTimeout, preferencesStore} = opts
|
||||
const {preferences} = preferencesStore.getState()
|
||||
|
||||
this.onInactiveTimeout = onInactiveTimeout || (() => {})
|
||||
this.store = new ObservableStore(extend({
|
||||
timeoutMinutes: 0,
|
||||
}, initState))
|
||||
this.timer = null
|
||||
|
||||
preferencesStore.subscribe(state => {
|
||||
this._setInactiveTimeout(state.preferences.autoLogoutTimeLimit)
|
||||
})
|
||||
|
||||
this._setInactiveTimeout(preferences.autoLogoutTimeLimit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last active time to the current time
|
||||
* @return {void}
|
||||
*/
|
||||
setLastActiveTime () {
|
||||
this._resetTimer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the inactive timeout for the app
|
||||
* @param {number} timeoutMinutes the inactive timeout in minutes
|
||||
* @return {void}
|
||||
* @private
|
||||
*/
|
||||
_setInactiveTimeout (timeoutMinutes) {
|
||||
this.store.putState({
|
||||
timeoutMinutes,
|
||||
})
|
||||
|
||||
this._resetTimer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal inactive timer
|
||||
*
|
||||
* If the {@code timeoutMinutes} state is falsy (i.e., zero) then a new
|
||||
* timer will not be created.
|
||||
*
|
||||
* @return {void}
|
||||
* @private
|
||||
*/
|
||||
_resetTimer () {
|
||||
const {timeoutMinutes} = this.store.getState()
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer)
|
||||
}
|
||||
|
||||
if (!timeoutMinutes) {
|
||||
return
|
||||
}
|
||||
|
||||
this.timer = setTimeout(() => this.onInactiveTimeout(), timeoutMinutes * 60 * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppStateController
|
||||
|
@ -68,7 +68,7 @@ class BalanceController {
|
||||
_registerUpdates () {
|
||||
const update = this.updateBalance.bind(this)
|
||||
|
||||
this.txController.on('tx:status-update', (txId, status) => {
|
||||
this.txController.on('tx:status-update', (_, status) => {
|
||||
switch (status) {
|
||||
case 'submitted':
|
||||
case 'confirmed':
|
||||
|
@ -1,136 +0,0 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const extend = require('xtend')
|
||||
const PhishingDetector = require('eth-phishing-detect/src/detector')
|
||||
const log = require('loglevel')
|
||||
|
||||
// compute phishing lists
|
||||
const PHISHING_DETECTION_CONFIG = require('eth-phishing-detect/src/config.json')
|
||||
// every four minutes
|
||||
const POLLING_INTERVAL = 4 * 60 * 1000
|
||||
|
||||
class BlacklistController {
|
||||
|
||||
/**
|
||||
* Responsible for polling for and storing an up to date 'eth-phishing-detect' config.json file, while
|
||||
* exposing a method that can check whether a given url is a phishing attempt. The 'eth-phishing-detect'
|
||||
* config.json file contains a fuzzylist, whitelist and blacklist.
|
||||
*
|
||||
*
|
||||
* @typedef {Object} BlacklistController
|
||||
* @param {object} opts Overrides the defaults for the initial state of this.store
|
||||
* @property {object} store The the store of the current phishing config
|
||||
* @property {object} store.phishing Contains fuzzylist, whitelist and blacklist arrays. @see
|
||||
* {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
|
||||
* @property {object} _phishingDetector The PhishingDetector instantiated by passing store.phishing to
|
||||
* PhishingDetector.
|
||||
* @property {object} _phishingUpdateIntervalRef Id of the interval created to periodically update the blacklist
|
||||
*
|
||||
*/
|
||||
constructor (opts = {}) {
|
||||
const initState = extend({
|
||||
phishing: PHISHING_DETECTION_CONFIG,
|
||||
whitelist: [],
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
// phishing detector
|
||||
this._phishingDetector = null
|
||||
this._setupPhishingDetector(initState.phishing)
|
||||
// polling references
|
||||
this._phishingUpdateIntervalRef = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given hostname to the runtime whitelist
|
||||
* @param {string} hostname the hostname to whitelist
|
||||
*/
|
||||
whitelistDomain (hostname) {
|
||||
if (!hostname) {
|
||||
return
|
||||
}
|
||||
|
||||
const { whitelist } = this.store.getState()
|
||||
this.store.updateState({
|
||||
whitelist: [...new Set([hostname, ...whitelist])],
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a url, returns the result of checking if that url is in the store.phishing blacklist
|
||||
*
|
||||
* @param {string} hostname The hostname portion of a url; the one that will be checked against the white and
|
||||
* blacklists of store.phishing
|
||||
* @returns {boolean} Whether or not the passed hostname is on our phishing blacklist
|
||||
*
|
||||
*/
|
||||
checkForPhishing (hostname) {
|
||||
if (!hostname) return false
|
||||
|
||||
const { whitelist } = this.store.getState()
|
||||
if (whitelist.some((e) => e === hostname)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { result } = this._phishingDetector.check(hostname)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries `https://api.infura.io/v2/blacklist` for an updated blacklist config. This is passed to this._phishingDetector
|
||||
* to update our phishing detector instance, and is updated in the store. The new phishing config is returned
|
||||
*
|
||||
*
|
||||
* @returns {Promise<object>} Promises the updated blacklist config for the phishingDetector
|
||||
*
|
||||
*/
|
||||
async updatePhishingList () {
|
||||
// make request
|
||||
let response
|
||||
try {
|
||||
response = await fetch('https://api.infura.io/v2/blacklist')
|
||||
} catch (err) {
|
||||
log.error(new Error(`BlacklistController - failed to fetch blacklist:\n${err.stack}`))
|
||||
return
|
||||
}
|
||||
// parse response
|
||||
let rawResponse
|
||||
let phishing
|
||||
try {
|
||||
const rawResponse = await response.text()
|
||||
phishing = JSON.parse(rawResponse)
|
||||
} catch (err) {
|
||||
log.error(new Error(`BlacklistController - failed to parse blacklist:\n${rawResponse}`))
|
||||
return
|
||||
}
|
||||
// update current blacklist
|
||||
this.store.updateState({ phishing })
|
||||
this._setupPhishingDetector(phishing)
|
||||
return phishing
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the updating of the local blacklist at a set interval. The update is done via this.updatePhishingList().
|
||||
* Also, this method store a reference to that interval at this._phishingUpdateIntervalRef
|
||||
*
|
||||
*/
|
||||
scheduleUpdates () {
|
||||
if (this._phishingUpdateIntervalRef) return
|
||||
this.updatePhishingList()
|
||||
this._phishingUpdateIntervalRef = setInterval(() => {
|
||||
this.updatePhishingList()
|
||||
}, POLLING_INTERVAL)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this._phishingDetector to a new PhishingDetector instance.
|
||||
* @see {@link https://github.com/MetaMask/eth-phishing-detect}
|
||||
*
|
||||
* @private
|
||||
* @param {object} config A config object like that found at {@link https://github.com/MetaMask/eth-phishing-detect/blob/master/src/config.json}
|
||||
*
|
||||
*/
|
||||
_setupPhishingDetector (config) {
|
||||
this._phishingDetector = new PhishingDetector(config)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlacklistController
|
@ -1,19 +0,0 @@
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
/**
|
||||
* Creates a block tracker that sends platform events on success and failure
|
||||
*/
|
||||
module.exports = function createBlockTracker (args, platform) {
|
||||
const blockTracker = new BlockTracker(args)
|
||||
blockTracker.on('latest', () => {
|
||||
if (platform && platform.sendMessage) {
|
||||
platform.sendMessage({ action: 'ethereum-ping-success' })
|
||||
}
|
||||
})
|
||||
blockTracker.on('error', () => {
|
||||
if (platform && platform.sendMessage) {
|
||||
platform.sendMessage({ action: 'ethereum-ping-error' })
|
||||
}
|
||||
})
|
||||
return blockTracker
|
||||
}
|
@ -7,14 +7,14 @@ const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createInfuraMiddleware = require('eth-json-rpc-infura')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createInfuraClient
|
||||
|
||||
function createInfuraClient ({ network, platform }) {
|
||||
function createInfuraClient ({ network }) {
|
||||
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
|
||||
const infuraProvider = providerFromMiddleware(infuraMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: infuraProvider }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: infuraProvider })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createNetworkAndChainIdMiddleware({ network }),
|
||||
|
@ -5,14 +5,14 @@ const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache'
|
||||
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createJsonRpcClient
|
||||
|
||||
function createJsonRpcClient ({ rpcUrl, platform }) {
|
||||
function createJsonRpcClient ({ rpcUrl }) {
|
||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
|
||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: blockProvider }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: blockProvider })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
||||
|
@ -3,14 +3,14 @@ const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
|
||||
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
|
||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||
const createBlockTracker = require('./createBlockTracker')
|
||||
const BlockTracker = require('eth-block-tracker')
|
||||
|
||||
module.exports = createLocalhostClient
|
||||
|
||||
function createLocalhostClient ({ platform }) {
|
||||
function createLocalhostClient () {
|
||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
|
||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||
const blockTracker = createBlockTracker({ provider: blockProvider, pollingInterval: 1000 }, platform)
|
||||
const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
|
||||
|
||||
const networkMiddleware = mergeMiddleware([
|
||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
||||
|
@ -46,9 +46,8 @@ const defaultNetworkConfig = {
|
||||
|
||||
module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
constructor (opts = {}, platform) {
|
||||
constructor (opts = {}) {
|
||||
super()
|
||||
this.platform = platform
|
||||
|
||||
// parse options
|
||||
const providerConfig = opts.provider || defaultProviderConfig
|
||||
@ -130,13 +129,14 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
|
||||
setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '', rpcPrefs) {
|
||||
const providerConfig = {
|
||||
type: 'rpc',
|
||||
rpcTarget,
|
||||
chainId,
|
||||
ticker,
|
||||
nickname,
|
||||
rpcPrefs,
|
||||
}
|
||||
this.providerConfig = providerConfig
|
||||
}
|
||||
@ -190,7 +190,7 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
_configureInfuraProvider ({ type }) {
|
||||
log.info('NetworkController - configureInfuraProvider', type)
|
||||
const networkClient = createInfuraClient({ network: type, platform: this.platform })
|
||||
const networkClient = createInfuraClient({ network: type })
|
||||
this._setNetworkClient(networkClient)
|
||||
// setup networkConfig
|
||||
var settings = {
|
||||
@ -201,13 +201,13 @@ module.exports = class NetworkController extends EventEmitter {
|
||||
|
||||
_configureLocalhostProvider () {
|
||||
log.info('NetworkController - configureLocalhostProvider')
|
||||
const networkClient = createLocalhostClient({ platform: this.platform })
|
||||
const networkClient = createLocalhostClient()
|
||||
this._setNetworkClient(networkClient)
|
||||
}
|
||||
|
||||
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
|
||||
log.info('NetworkController - configureStandardProvider', rpcUrl)
|
||||
const networkClient = createJsonRpcClient({ rpcUrl, platform: this.platform })
|
||||
const networkClient = createJsonRpcClient({ rpcUrl })
|
||||
// hack to add a 'rpc' network with chainId
|
||||
networks.networkList['rpc'] = {
|
||||
chainId: chainId,
|
||||
|
@ -117,6 +117,14 @@ class PreferencesController {
|
||||
return metaMetricsId
|
||||
}
|
||||
|
||||
getMetaMetricsId () {
|
||||
return this.store.getState().metaMetricsId
|
||||
}
|
||||
|
||||
getParticipateInMetaMetrics () {
|
||||
return this.store.getState().participateInMetaMetrics
|
||||
}
|
||||
|
||||
setMetaMetricsSendCount (val) {
|
||||
this.store.updateState({ metaMetricsSendCount: val })
|
||||
}
|
||||
@ -331,7 +339,7 @@ class PreferencesController {
|
||||
}
|
||||
|
||||
removeSuggestedTokens () {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
this.store.updateState({ suggestedTokens: {} })
|
||||
resolve({})
|
||||
})
|
||||
@ -388,7 +396,7 @@ class PreferencesController {
|
||||
const newEntry = { address, symbol, decimals }
|
||||
const tokens = this.store.getState().tokens
|
||||
const assetImages = this.getAssetImages()
|
||||
const previousEntry = tokens.find((token, index) => {
|
||||
const previousEntry = tokens.find((token) => {
|
||||
return token.address === address
|
||||
})
|
||||
const previousIndex = tokens.indexOf(previousEntry)
|
||||
@ -453,7 +461,7 @@ class PreferencesController {
|
||||
*
|
||||
*/
|
||||
setCurrentAccountTab (currentAccountTab) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
this.store.updateState({ currentAccountTab })
|
||||
resolve()
|
||||
})
|
||||
@ -480,8 +488,8 @@ class PreferencesController {
|
||||
rpcList[index] = updatedRpc
|
||||
this.store.updateState({ frequentRpcListDetail: rpcList })
|
||||
} else {
|
||||
const { rpcUrl, chainId, ticker, nickname } = newRpcDetails
|
||||
return this.addToFrequentRpcList(rpcUrl, chainId, ticker, nickname)
|
||||
const { rpcUrl, chainId, ticker, nickname, rpcPrefs = {} } = newRpcDetails
|
||||
return this.addToFrequentRpcList(rpcUrl, chainId, ticker, nickname, rpcPrefs)
|
||||
}
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
@ -495,22 +503,22 @@ class PreferencesController {
|
||||
* @returns {Promise<array>} Promise resolving to updated frequentRpcList.
|
||||
*
|
||||
*/
|
||||
addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '') {
|
||||
const rpcList = this.getFrequentRpcListDetail()
|
||||
const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
if (url !== 'http://localhost:8545') {
|
||||
let checkedChainId
|
||||
if (!!chainId && !Number.isNaN(parseInt(chainId))) {
|
||||
checkedChainId = chainId
|
||||
addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
|
||||
const rpcList = this.getFrequentRpcListDetail()
|
||||
const index = rpcList.findIndex((element) => { return element.rpcUrl === url })
|
||||
if (index !== -1) {
|
||||
rpcList.splice(index, 1)
|
||||
}
|
||||
rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname })
|
||||
if (url !== 'http://localhost:8545') {
|
||||
let checkedChainId
|
||||
if (!!chainId && !Number.isNaN(parseInt(chainId))) {
|
||||
checkedChainId = chainId
|
||||
}
|
||||
rpcList.push({ rpcUrl: url, chainId: checkedChainId, ticker, nickname, rpcPrefs })
|
||||
}
|
||||
this.store.updateState({ frequentRpcListDetail: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
this.store.updateState({ frequentRpcListDetail: rpcList })
|
||||
return Promise.resolve(rpcList)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes custom RPC url from state.
|
||||
|
@ -1,9 +1,11 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const SafeEventEmitter = require('safe-event-emitter')
|
||||
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
|
||||
|
||||
/**
|
||||
* A controller that services user-approved requests for a full Ethereum provider API
|
||||
*/
|
||||
class ProviderApprovalController {
|
||||
class ProviderApprovalController extends SafeEventEmitter {
|
||||
/**
|
||||
* Determines if caching is enabled
|
||||
*/
|
||||
@ -14,38 +16,43 @@ class ProviderApprovalController {
|
||||
*
|
||||
* @param {Object} [config] - Options to configure controller
|
||||
*/
|
||||
constructor ({ closePopup, keyringController, openPopup, platform, preferencesController, publicConfigStore } = {}) {
|
||||
constructor ({ closePopup, keyringController, openPopup, preferencesController } = {}) {
|
||||
super()
|
||||
this.approvedOrigins = {}
|
||||
this.closePopup = closePopup
|
||||
this.keyringController = keyringController
|
||||
this.openPopup = openPopup
|
||||
this.platform = platform
|
||||
this.preferencesController = preferencesController
|
||||
this.publicConfigStore = publicConfigStore
|
||||
this.store = new ObservableStore({
|
||||
providerRequests: [],
|
||||
})
|
||||
}
|
||||
|
||||
if (platform && platform.addMessageListener) {
|
||||
platform.addMessageListener(({ action = '', force, origin, siteTitle, siteImage }, { tab }) => {
|
||||
if (tab && tab.id) {
|
||||
switch (action) {
|
||||
case 'init-provider-request':
|
||||
this._handleProviderRequest(origin, siteTitle, siteImage, force, tab.id)
|
||||
break
|
||||
case 'init-is-approved':
|
||||
this._handleIsApproved(origin, tab.id)
|
||||
break
|
||||
case 'init-is-unlocked':
|
||||
this._handleIsUnlocked(tab.id)
|
||||
break
|
||||
case 'init-privacy-request':
|
||||
this._handlePrivacyRequest(tab.id)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Called when a user approves access to a full Ethereum provider API
|
||||
*
|
||||
* @param {object} opts - opts for the middleware contains the origin for the middleware
|
||||
*/
|
||||
createMiddleware ({ origin, getSiteMetadata }) {
|
||||
return createAsyncMiddleware(async (req, res, next) => {
|
||||
// only handle requestAccounts
|
||||
if (req.method !== 'eth_requestAccounts') return next()
|
||||
// if already approved or privacy mode disabled, return early
|
||||
if (this.shouldExposeAccounts(origin)) {
|
||||
res.result = [this.preferencesController.getSelectedAddress()]
|
||||
return
|
||||
}
|
||||
// register the provider request
|
||||
const metadata = await getSiteMetadata(origin)
|
||||
this._handleProviderRequest(origin, metadata.name, metadata.icon, false, null)
|
||||
// wait for resolution of request
|
||||
const approved = await new Promise(resolve => this.once(`resolvedRequest:${origin}`, ({ approved }) => resolve(approved)))
|
||||
if (approved) {
|
||||
res.result = [this.preferencesController.getSelectedAddress()]
|
||||
} else {
|
||||
throw new Error('User denied account authorization')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,79 +66,37 @@ class ProviderApprovalController {
|
||||
this.store.updateState({ providerRequests: [{ origin, siteTitle, siteImage, tabID }] })
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
if (!force && this.approvedOrigins[origin] && this.caching && isUnlocked) {
|
||||
this.approveProviderRequest(tabID)
|
||||
return
|
||||
}
|
||||
this.openPopup && this.openPopup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a tab to determine if an origin has been approved in the past
|
||||
*
|
||||
* @param {string} origin - Origin of the window
|
||||
*/
|
||||
_handleIsApproved (origin, tabID) {
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'answer-is-approved',
|
||||
isApproved: this.approvedOrigins[origin] && this.caching,
|
||||
caching: this.caching,
|
||||
}, { id: tabID })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by a tab to determine if MetaMask is currently locked or unlocked
|
||||
*/
|
||||
_handleIsUnlocked (tabID) {
|
||||
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
|
||||
this.platform && this.platform.sendMessage({ action: 'answer-is-unlocked', isUnlocked }, { id: tabID })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to check privacy mode; if privacy mode is off, this will automatically enable the provider (legacy behavior)
|
||||
*/
|
||||
_handlePrivacyRequest (tabID) {
|
||||
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
|
||||
if (!privacyMode) {
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'approve-legacy-provider-request',
|
||||
selectedAddress: this.publicConfigStore.getState().selectedAddress,
|
||||
}, { id: tabID })
|
||||
this.publicConfigStore.emit('update', this.publicConfigStore.getState())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user approves access to a full Ethereum provider API
|
||||
*
|
||||
* @param {string} tabID - ID of the target window that approved provider access
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
approveProviderRequest (tabID) {
|
||||
approveProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const origin = requests.find(request => request.tabID === tabID).origin
|
||||
this.platform && this.platform.sendMessage({
|
||||
action: 'approve-provider-request',
|
||||
selectedAddress: this.publicConfigStore.getState().selectedAddress,
|
||||
}, { id: tabID })
|
||||
this.publicConfigStore.emit('update', this.publicConfigStore.getState())
|
||||
const providerRequests = requests.filter(request => request.tabID !== tabID)
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
this.approvedOrigins[origin] = true
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a tab rejects access to a full Ethereum provider API
|
||||
*
|
||||
* @param {string} tabID - ID of the target window that rejected provider access
|
||||
* @param {string} origin - origin of the domain that had provider access approved
|
||||
*/
|
||||
rejectProviderRequest (tabID) {
|
||||
rejectProviderRequestByOrigin (origin) {
|
||||
this.closePopup && this.closePopup()
|
||||
const requests = this.store.getState().providerRequests
|
||||
const origin = requests.find(request => request.tabID === tabID).origin
|
||||
this.platform && this.platform.sendMessage({ action: 'reject-provider-request' }, { id: tabID })
|
||||
const providerRequests = requests.filter(request => request.tabID !== tabID)
|
||||
const providerRequests = requests.filter(request => request.origin !== origin)
|
||||
this.store.updateState({ providerRequests })
|
||||
delete this.approvedOrigins[origin]
|
||||
this.emit(`resolvedRequest:${origin}`, { approved: false })
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,16 +114,10 @@ class ProviderApprovalController {
|
||||
*/
|
||||
shouldExposeAccounts (origin) {
|
||||
const privacyMode = this.preferencesController.getFeatureFlags().privacyMode
|
||||
return !privacyMode || this.approvedOrigins[origin]
|
||||
const result = !privacyMode || Boolean(this.approvedOrigins[origin])
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells all tabs that MetaMask is now locked. This is primarily used to set
|
||||
* internal flags in the contentscript and inpage script.
|
||||
*/
|
||||
setLocked () {
|
||||
this.platform.sendMessage({ action: 'metamask-set-locked' })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProviderApprovalController
|
||||
|
@ -1,180 +0,0 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const extend = require('xtend')
|
||||
const log = require('loglevel')
|
||||
|
||||
// every three seconds when an incomplete tx is waiting
|
||||
const POLLING_INTERVAL = 3000
|
||||
|
||||
class ShapeshiftController {
|
||||
|
||||
/**
|
||||
* Controller responsible for managing the list of shapeshift transactions. On construction, it initiates a poll
|
||||
* that queries a shapeshift.io API for updates to any pending shapeshift transactions
|
||||
*
|
||||
* @typedef {Object} ShapeshiftController
|
||||
* @param {object} opts Overrides the defaults for the initial state of this.store
|
||||
* @property {array} opts.initState initializes the the state of the ShapeshiftController. Can contain an
|
||||
* shapeShiftTxList array.
|
||||
* @property {array} shapeShiftTxList An array of ShapeShiftTx objects
|
||||
*
|
||||
*/
|
||||
constructor (opts = {}) {
|
||||
const initState = extend({
|
||||
shapeShiftTxList: [],
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
this.pollForUpdates()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, a single shapeshift transaction.
|
||||
* @typedef {Object} ShapeShiftTx
|
||||
* @property {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
|
||||
* user's Metamask account
|
||||
* @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
|
||||
* @property {string} key - The 'shapeshift' key differentiates this from other types of txs in Metamask
|
||||
* @property {number} time - The time at which the tx was created
|
||||
* @property {object} response - Initiated as an empty object, which will be replaced by a Response object. @see {@link
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Response}
|
||||
*/
|
||||
|
||||
//
|
||||
// PUBLIC METHODS
|
||||
//
|
||||
|
||||
/**
|
||||
* A getter for the shapeShiftTxList property
|
||||
*
|
||||
* @returns {array<ShapeShiftTx>}
|
||||
*
|
||||
*/
|
||||
getShapeShiftTxList () {
|
||||
const shapeShiftTxList = this.store.getState().shapeShiftTxList
|
||||
return shapeShiftTxList
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for all ShapeShiftTx in the shapeShiftTxList that have not successfully completed a deposit.
|
||||
*
|
||||
* @returns {array<ShapeShiftTx>} Only includes ShapeShiftTx which has a response property with a status !== complete
|
||||
*
|
||||
*/
|
||||
getPendingTxs () {
|
||||
const txs = this.getShapeShiftTxList()
|
||||
const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete')
|
||||
return pending
|
||||
}
|
||||
|
||||
/**
|
||||
* A poll that exists as long as there are pending transactions. Each call attempts to update the data of any
|
||||
* pendingTxs, and then calls itself again. If there are no pending txs, the recursive call is not made and
|
||||
* the polling stops.
|
||||
*
|
||||
* this.updateTx is used to attempt the update to the pendingTxs in the ShapeShiftTxList, and that updated data
|
||||
* is saved with saveTx.
|
||||
*
|
||||
*/
|
||||
pollForUpdates () {
|
||||
const pendingTxs = this.getPendingTxs()
|
||||
|
||||
if (pendingTxs.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
Promise.all(pendingTxs.map((tx) => {
|
||||
return this.updateTx(tx)
|
||||
}))
|
||||
.then((results) => {
|
||||
results.forEach(tx => this.saveTx(tx))
|
||||
this.timeout = setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to update a ShapeShiftTx with data from a shapeshift.io API. Both the response and time properties
|
||||
* can be updated. The response property is updated with every call, but the time property is only updated when
|
||||
* the response status updates to 'complete'. This will occur once the user makes a deposit as the ShapeShiftTx
|
||||
* depositAddress
|
||||
*
|
||||
* @param {ShapeShiftTx} tx The tx to update
|
||||
*
|
||||
*/
|
||||
async updateTx (tx) {
|
||||
try {
|
||||
const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
|
||||
const response = await fetch(url)
|
||||
const json = await response.json()
|
||||
tx.response = json
|
||||
if (tx.response.status === 'complete') {
|
||||
tx.time = new Date().getTime()
|
||||
}
|
||||
return tx
|
||||
} catch (err) {
|
||||
log.warn(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves an updated to a ShapeShiftTx in the shapeShiftTxList. If the passed ShapeShiftTx is not in the
|
||||
* shapeShiftTxList, nothing happens.
|
||||
*
|
||||
* @param {ShapeShiftTx} tx The updated tx to save, if it exists in the current shapeShiftTxList
|
||||
*
|
||||
*/
|
||||
saveTx (tx) {
|
||||
const { shapeShiftTxList } = this.store.getState()
|
||||
const index = shapeShiftTxList.indexOf(tx)
|
||||
if (index !== -1) {
|
||||
shapeShiftTxList[index] = tx
|
||||
this.store.updateState({ shapeShiftTxList })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a ShapeShiftTx from the shapeShiftTxList
|
||||
*
|
||||
* @param {ShapeShiftTx} tx The tx to remove
|
||||
*
|
||||
*/
|
||||
removeShapeShiftTx (tx) {
|
||||
const { shapeShiftTxList } = this.store.getState()
|
||||
const index = shapeShiftTxList.indexOf(index)
|
||||
if (index !== -1) {
|
||||
shapeShiftTxList.splice(index, 1)
|
||||
}
|
||||
this.updateState({ shapeShiftTxList })
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ShapeShiftTx, adds it to the shapeShiftTxList, and initiates a new poll for updates of pending txs
|
||||
*
|
||||
* @param {string} depositAddress - An address at which to send a crypto deposit, so that eth can be sent to the
|
||||
* user's Metamask account
|
||||
* @param {string} depositType - An abbreviation of the type of crypto currency to be deposited.
|
||||
*
|
||||
*/
|
||||
createShapeShiftTx (depositAddress, depositType) {
|
||||
const state = this.store.getState()
|
||||
let { shapeShiftTxList } = state
|
||||
|
||||
var shapeShiftTx = {
|
||||
depositAddress,
|
||||
depositType,
|
||||
key: 'shapeshift',
|
||||
time: new Date().getTime(),
|
||||
response: {},
|
||||
}
|
||||
|
||||
if (!shapeShiftTxList) {
|
||||
shapeShiftTxList = [shapeShiftTx]
|
||||
} else {
|
||||
shapeShiftTxList.push(shapeShiftTx)
|
||||
}
|
||||
|
||||
this.store.updateState({ shapeShiftTxList })
|
||||
this.pollForUpdates()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ShapeshiftController
|
@ -1,6 +1,8 @@
|
||||
const ObservableStore = require('obs-store')
|
||||
const log = require('loglevel')
|
||||
const normalizeAddress = require('eth-sig-util').normalize
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
|
||||
|
||||
// By default, poll every 3 minutes
|
||||
const DEFAULT_INTERVAL = 180 * 1000
|
||||
@ -36,7 +38,7 @@ class TokenRatesController {
|
||||
const response = await fetch(`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`)
|
||||
const prices = await response.json()
|
||||
this._tokens.forEach(token => {
|
||||
const price = prices[token.address.toLowerCase()]
|
||||
const price = prices[token.address.toLowerCase()] || prices[ethUtil.toChecksumAddress(token.address)]
|
||||
contractExchangeRates[normalizeAddress(token.address)] = price ? price[nativeCurrency] : 0
|
||||
})
|
||||
} catch (error) {
|
||||
|
@ -3,6 +3,17 @@ const ObservableStore = require('obs-store')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const Transaction = require('ethereumjs-tx')
|
||||
const EthQuery = require('ethjs-query')
|
||||
const abi = require('human-standard-token-abi')
|
||||
const abiDecoder = require('abi-decoder')
|
||||
abiDecoder.addABI(abi)
|
||||
const {
|
||||
TOKEN_METHOD_APPROVE,
|
||||
TOKEN_METHOD_TRANSFER,
|
||||
TOKEN_METHOD_TRANSFER_FROM,
|
||||
SEND_ETHER_ACTION_KEY,
|
||||
DEPLOY_CONTRACT_ACTION_KEY,
|
||||
CONTRACT_INTERACTION_KEY,
|
||||
} = require('../../../../ui/app/helpers/constants/transactions.js')
|
||||
const TransactionStateManager = require('./tx-state-manager')
|
||||
const TxGasUtil = require('./tx-gas-utils')
|
||||
const PendingTransactionTracker = require('./pending-tx-tracker')
|
||||
@ -180,9 +191,11 @@ class TransactionController extends EventEmitter {
|
||||
}
|
||||
txUtils.validateTxParams(normalizedTxParams)
|
||||
// construct txMeta
|
||||
const { transactionCategory, getCodeResponse } = await this._determineTransactionCategory(txParams)
|
||||
let txMeta = this.txStateManager.generateTxMeta({
|
||||
txParams: normalizedTxParams,
|
||||
type: TRANSACTION_TYPE_STANDARD,
|
||||
transactionCategory,
|
||||
})
|
||||
this.addTx(txMeta)
|
||||
this.emit('newUnapprovedTx', txMeta)
|
||||
@ -191,7 +204,7 @@ class TransactionController extends EventEmitter {
|
||||
// check whether recipient account is blacklisted
|
||||
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
|
||||
// add default tx params
|
||||
txMeta = await this.addTxGasDefaults(txMeta)
|
||||
txMeta = await this.addTxGasDefaults(txMeta, getCodeResponse)
|
||||
} catch (error) {
|
||||
log.warn(error)
|
||||
txMeta.loadingDefaults = false
|
||||
@ -211,7 +224,7 @@ class TransactionController extends EventEmitter {
|
||||
@param txMeta {Object} - the txMeta object
|
||||
@returns {Promise<object>} resolves with txMeta
|
||||
*/
|
||||
async addTxGasDefaults (txMeta) {
|
||||
async addTxGasDefaults (txMeta, getCodeResponse) {
|
||||
const txParams = txMeta.txParams
|
||||
// ensure value
|
||||
txParams.value = txParams.value ? ethUtil.addHexPrefix(txParams.value) : '0x0'
|
||||
@ -222,7 +235,7 @@ class TransactionController extends EventEmitter {
|
||||
}
|
||||
txParams.gasPrice = ethUtil.addHexPrefix(gasPrice.toString(16))
|
||||
// set gasLimit
|
||||
return await this.txGasUtil.analyzeGasUsage(txMeta)
|
||||
return await this.txGasUtil.analyzeGasUsage(txMeta, getCodeResponse)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -555,6 +568,43 @@ class TransactionController extends EventEmitter {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a "type" for a transaction out of the following list: simpleSend, tokenTransfer, tokenApprove,
|
||||
contractDeployment, contractMethodCall
|
||||
*/
|
||||
async _determineTransactionCategory (txParams) {
|
||||
const { data, to } = txParams
|
||||
const { name } = data && abiDecoder.decodeMethod(data) || {}
|
||||
const tokenMethodName = [
|
||||
TOKEN_METHOD_APPROVE,
|
||||
TOKEN_METHOD_TRANSFER,
|
||||
TOKEN_METHOD_TRANSFER_FROM,
|
||||
].find(tokenMethodName => tokenMethodName === name && name.toLowerCase())
|
||||
|
||||
let result
|
||||
let code
|
||||
if (!txParams.data) {
|
||||
result = SEND_ETHER_ACTION_KEY
|
||||
} else if (tokenMethodName) {
|
||||
result = tokenMethodName
|
||||
} else if (!to) {
|
||||
result = DEPLOY_CONTRACT_ACTION_KEY
|
||||
} else {
|
||||
try {
|
||||
code = await this.query.getCode(to)
|
||||
} catch (e) {
|
||||
code = null
|
||||
log.warn(e)
|
||||
}
|
||||
// For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0'
|
||||
const codeIsEmpty = !code || code === '0x' || code === '0x0'
|
||||
|
||||
result = codeIsEmpty ? SEND_ETHER_ACTION_KEY : CONTRACT_INTERACTION_KEY
|
||||
}
|
||||
|
||||
return { transactionCategory: result, getCodeResponse: code }
|
||||
}
|
||||
|
||||
/**
|
||||
Sets other txMeta statuses to dropped if the txMeta that has been confirmed has other transactions
|
||||
in the list have the same nonce
|
||||
|
@ -4,7 +4,9 @@ const {
|
||||
BnMultiplyByFraction,
|
||||
bnToHex,
|
||||
} = require('../../lib/util')
|
||||
const log = require('loglevel')
|
||||
const { addHexPrefix } = require('ethereumjs-util')
|
||||
const { SEND_ETHER_ACTION_KEY } = require('../../../../ui/app/helpers/constants/transactions.js')
|
||||
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
|
||||
|
||||
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/app/helpers/constants/error-keys'
|
||||
@ -26,12 +28,13 @@ class TxGasUtil {
|
||||
@param txMeta {Object} - the txMeta object
|
||||
@returns {object} the txMeta object with the gas written to the txParams
|
||||
*/
|
||||
async analyzeGasUsage (txMeta) {
|
||||
async analyzeGasUsage (txMeta, getCodeResponse) {
|
||||
const block = await this.query.getBlockByNumber('latest', false)
|
||||
let estimatedGasHex
|
||||
try {
|
||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit)
|
||||
estimatedGasHex = await this.estimateTxGas(txMeta, block.gasLimit, getCodeResponse)
|
||||
} catch (err) {
|
||||
log.warn(err)
|
||||
txMeta.simulationFails = {
|
||||
reason: err.message,
|
||||
errorKey: err.errorKey,
|
||||
@ -54,7 +57,7 @@ class TxGasUtil {
|
||||
@param blockGasLimitHex {string} - hex string of the block's gas limit
|
||||
@returns {string} the estimated gas limit as a hex string
|
||||
*/
|
||||
async estimateTxGas (txMeta, blockGasLimitHex) {
|
||||
async estimateTxGas (txMeta, blockGasLimitHex, getCodeResponse) {
|
||||
const txParams = txMeta.txParams
|
||||
|
||||
// check if gasLimit is already specified
|
||||
@ -70,11 +73,10 @@ class TxGasUtil {
|
||||
|
||||
// see if we can set the gas based on the recipient
|
||||
if (hasRecipient) {
|
||||
const code = await this.query.getCode(recipient)
|
||||
// For an address with no code, geth will return '0x', and ganache-core v2.2.1 will return '0x0'
|
||||
const codeIsEmpty = !code || code === '0x' || code === '0x0'
|
||||
const categorizedAsSimple = txMeta.transactionCategory === SEND_ETHER_ACTION_KEY
|
||||
|
||||
if (codeIsEmpty) {
|
||||
if (categorizedAsSimple) {
|
||||
// if there's data in the params, but there's no contract code, it's not a valid transaction
|
||||
if (txParams.data) {
|
||||
const err = new Error('TxGasUtil - Trying to call a function on a non-contract address')
|
||||
@ -82,7 +84,7 @@ class TxGasUtil {
|
||||
err.errorKey = TRANSACTION_NO_CONTRACT_ERROR_KEY
|
||||
|
||||
// set the response on the error so that we can see in logs what the actual response was
|
||||
err.getCodeResponse = code
|
||||
err.getCodeResponse = getCodeResponse
|
||||
throw err
|
||||
}
|
||||
|
||||
|
@ -126,10 +126,10 @@ class TransactionStateManager extends EventEmitter {
|
||||
@returns {object} the txMeta
|
||||
*/
|
||||
addTx (txMeta) {
|
||||
this.once(`${txMeta.id}:signed`, function (txId) {
|
||||
this.once(`${txMeta.id}:signed`, function () {
|
||||
this.removeAllListeners(`${txMeta.id}:rejected`)
|
||||
})
|
||||
this.once(`${txMeta.id}:rejected`, function (txId) {
|
||||
this.once(`${txMeta.id}:rejected`, function () {
|
||||
this.removeAllListeners(`${txMeta.id}:signed`)
|
||||
})
|
||||
// initialize history
|
||||
|
@ -4,18 +4,10 @@ class StandardProvider {
|
||||
|
||||
constructor (provider) {
|
||||
this._provider = provider
|
||||
this._onMessage('ethereumpingerror', this._onClose.bind(this))
|
||||
this._onMessage('ethereumpingsuccess', this._onConnect.bind(this))
|
||||
window.addEventListener('load', () => {
|
||||
this._subscribe()
|
||||
this._ping()
|
||||
})
|
||||
}
|
||||
|
||||
_onMessage (type, handler) {
|
||||
window.addEventListener('message', function ({ data }) {
|
||||
if (!data || data.type !== type) return
|
||||
handler.apply(this, arguments)
|
||||
this._subscribe()
|
||||
// indicate that we've connected, mostly just for standard compliance
|
||||
setTimeout(() => {
|
||||
this._onConnect()
|
||||
})
|
||||
}
|
||||
|
||||
@ -34,15 +26,6 @@ class StandardProvider {
|
||||
this._isConnected = true
|
||||
}
|
||||
|
||||
async _ping () {
|
||||
try {
|
||||
await this.send('net_version')
|
||||
window.postMessage({ type: 'ethereumpingsuccess' }, '*')
|
||||
} catch (error) {
|
||||
window.postMessage({ type: 'ethereumpingerror' }, '*')
|
||||
}
|
||||
}
|
||||
|
||||
_subscribe () {
|
||||
this._provider.on('data', (error, { method, params }) => {
|
||||
if (!error && method === 'eth_subscription') {
|
||||
@ -59,11 +42,9 @@ class StandardProvider {
|
||||
* @returns {Promise<*>} Promise resolving to the result if successful
|
||||
*/
|
||||
send (method, params = []) {
|
||||
if (method === 'eth_requestAccounts') return this._provider.enable()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
this._provider.sendAsync({ method, params, beta: true }, (error, response) => {
|
||||
this._provider.sendAsync({ id: 1, jsonrpc: '2.0', method, params }, (error, response) => {
|
||||
error = error || response.error
|
||||
error ? reject(error) : resolve(response)
|
||||
})
|
||||
|
@ -7,32 +7,12 @@ const setupDappAutoReload = require('./lib/auto-reload.js')
|
||||
const MetamaskInpageProvider = require('metamask-inpage-provider')
|
||||
const createStandardProvider = require('./createStandardProvider').default
|
||||
|
||||
let isEnabled = false
|
||||
let warned = false
|
||||
let providerHandle
|
||||
let isApprovedHandle
|
||||
let isUnlockedHandle
|
||||
|
||||
restoreContextAfterImports()
|
||||
|
||||
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
|
||||
|
||||
/**
|
||||
* Adds a postMessage listener for a specific message type
|
||||
*
|
||||
* @param {string} messageType - postMessage type to listen for
|
||||
* @param {Function} handler - event handler
|
||||
* @param {boolean} remove - removes this handler after being triggered
|
||||
*/
|
||||
function onMessage (messageType, callback, remove) {
|
||||
const handler = function ({ data }) {
|
||||
if (!data || data.type !== messageType) { return }
|
||||
remove && window.removeEventListener('message', handler)
|
||||
callback.apply(window, arguments)
|
||||
}
|
||||
window.addEventListener('message', handler)
|
||||
}
|
||||
|
||||
//
|
||||
// setup plugin communication
|
||||
//
|
||||
@ -49,45 +29,16 @@ const inpageProvider = new MetamaskInpageProvider(metamaskStream)
|
||||
// set a high max listener count to avoid unnecesary warnings
|
||||
inpageProvider.setMaxListeners(100)
|
||||
|
||||
// set up a listener for when MetaMask is locked
|
||||
onMessage('metamasksetlocked', () => { isEnabled = false })
|
||||
|
||||
// set up a listener for privacy mode responses
|
||||
onMessage('ethereumproviderlegacy', ({ data: { selectedAddress } }) => {
|
||||
isEnabled = true
|
||||
setTimeout(() => {
|
||||
inpageProvider.publicConfigStore.updateState({ selectedAddress })
|
||||
}, 0)
|
||||
}, true)
|
||||
|
||||
// augment the provider with its enable method
|
||||
inpageProvider.enable = function ({ force } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
providerHandle = ({ data: { error, selectedAddress } }) => {
|
||||
if (typeof error !== 'undefined') {
|
||||
reject({
|
||||
message: error,
|
||||
code: 4001,
|
||||
})
|
||||
inpageProvider.sendAsync({ method: 'eth_requestAccounts', params: [force] }, (error, response) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
window.removeEventListener('message', providerHandle)
|
||||
setTimeout(() => {
|
||||
inpageProvider.publicConfigStore.updateState({ selectedAddress })
|
||||
}, 0)
|
||||
|
||||
// wait for the background to update with an account
|
||||
inpageProvider.sendAsync({ method: 'eth_accounts', params: [] }, (error, response) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
isEnabled = true
|
||||
resolve(response.result)
|
||||
}
|
||||
})
|
||||
resolve(response.result)
|
||||
}
|
||||
}
|
||||
onMessage('ethereumprovider', providerHandle, true)
|
||||
window.postMessage({ type: 'ETHEREUM_ENABLE_PROVIDER', force }, '*')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -98,31 +49,23 @@ inpageProvider.autoRefreshOnNetworkChange = true
|
||||
// add metamask-specific convenience methods
|
||||
inpageProvider._metamask = new Proxy({
|
||||
/**
|
||||
* Determines if this domain is currently enabled
|
||||
* Synchronously determines if this domain is currently enabled, with a potential false negative if called to soon
|
||||
*
|
||||
* @returns {boolean} - true if this domain is currently enabled
|
||||
* @returns {boolean} - returns true if this domain is currently enabled
|
||||
*/
|
||||
isEnabled: function () {
|
||||
return isEnabled
|
||||
const { isEnabled } = inpageProvider.publicConfigStore.getState()
|
||||
return Boolean(isEnabled)
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if this domain has been previously approved
|
||||
* Asynchronously determines if this domain is currently enabled
|
||||
*
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if this domain has been previously approved
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if this domain is currently enabled
|
||||
*/
|
||||
isApproved: function () {
|
||||
return new Promise((resolve) => {
|
||||
isApprovedHandle = ({ data: { caching, isApproved } }) => {
|
||||
if (caching) {
|
||||
resolve(!!isApproved)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
}
|
||||
onMessage('ethereumisapproved', isApprovedHandle, true)
|
||||
window.postMessage({ type: 'ETHEREUM_IS_APPROVED' }, '*')
|
||||
})
|
||||
isApproved: async function () {
|
||||
const { isEnabled } = await getPublicConfigWhenReady()
|
||||
return Boolean(isEnabled)
|
||||
},
|
||||
|
||||
/**
|
||||
@ -130,14 +73,9 @@ inpageProvider._metamask = new Proxy({
|
||||
*
|
||||
* @returns {Promise<boolean>} - Promise resolving to true if MetaMask is currently unlocked
|
||||
*/
|
||||
isUnlocked: function () {
|
||||
return new Promise((resolve) => {
|
||||
isUnlockedHandle = ({ data: { isUnlocked } }) => {
|
||||
resolve(!!isUnlocked)
|
||||
}
|
||||
onMessage('metamaskisunlocked', isUnlockedHandle, true)
|
||||
window.postMessage({ type: 'METAMASK_IS_UNLOCKED' }, '*')
|
||||
})
|
||||
isUnlocked: async function () {
|
||||
const { isUnlocked } = await getPublicConfigWhenReady()
|
||||
return Boolean(isUnlocked)
|
||||
},
|
||||
}, {
|
||||
get: function (obj, prop) {
|
||||
@ -149,6 +87,19 @@ inpageProvider._metamask = new Proxy({
|
||||
},
|
||||
})
|
||||
|
||||
// publicConfig isn't populated until we get a message from background.
|
||||
// Using this getter will ensure the state is available
|
||||
async function getPublicConfigWhenReady () {
|
||||
const store = inpageProvider.publicConfigStore
|
||||
let state = store.getState()
|
||||
// if state is missing, wait for first update
|
||||
if (!state.networkVersion) {
|
||||
state = await new Promise(resolve => store.once('update', resolve))
|
||||
console.log('new state', state)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// Work around for web3@1.0 deleting the bound `sendAsync` but not the unbound
|
||||
// `sendAsync` method on the prototype, causing `this` reference issues with drizzle
|
||||
const proxiedInpageProvider = new Proxy(inpageProvider, {
|
||||
@ -159,19 +110,6 @@ const proxiedInpageProvider = new Proxy(inpageProvider, {
|
||||
|
||||
window.ethereum = createStandardProvider(proxiedInpageProvider)
|
||||
|
||||
// detect eth_requestAccounts and pipe to enable for now
|
||||
function detectAccountRequest (method) {
|
||||
const originalMethod = inpageProvider[method]
|
||||
inpageProvider[method] = function ({ method }) {
|
||||
if (method === 'eth_requestAccounts') {
|
||||
return window.ethereum.enable()
|
||||
}
|
||||
return originalMethod.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
detectAccountRequest('send')
|
||||
detectAccountRequest('sendAsync')
|
||||
|
||||
//
|
||||
// setup web3
|
||||
//
|
||||
|
26
app/scripts/lib/backend-metametrics.js
Normal file
26
app/scripts/lib/backend-metametrics.js
Normal file
@ -0,0 +1,26 @@
|
||||
const {
|
||||
getMetaMetricState,
|
||||
} = require('../../../ui/app/selectors/selectors')
|
||||
const {
|
||||
sendMetaMetricsEvent,
|
||||
} = require('../../../ui/app/helpers/utils/metametrics.util')
|
||||
|
||||
const inDevelopment = process.env.NODE_ENV === 'development'
|
||||
|
||||
const METAMETRICS_TRACKING_URL = inDevelopment
|
||||
? 'http://www.metamask.io/metametrics'
|
||||
: 'http://www.metamask.io/metametrics-prod'
|
||||
|
||||
function backEndMetaMetricsEvent (metaMaskState, eventData) {
|
||||
const stateEventData = getMetaMetricState({ metamask: metaMaskState })
|
||||
|
||||
if (stateEventData.participateInMetaMetrics) {
|
||||
sendMetaMetricsEvent({
|
||||
...stateEventData,
|
||||
...eventData,
|
||||
url: METAMETRICS_TRACKING_URL + '/backend',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = backEndMetaMetricsEvent
|
16
app/scripts/lib/createDnodeRemoteGetter.js
Normal file
16
app/scripts/lib/createDnodeRemoteGetter.js
Normal file
@ -0,0 +1,16 @@
|
||||
module.exports = createDnodeRemoteGetter
|
||||
|
||||
function createDnodeRemoteGetter (dnode) {
|
||||
let remote
|
||||
|
||||
dnode.once('remote', (_remote) => {
|
||||
remote = _remote
|
||||
})
|
||||
|
||||
async function getRemote () {
|
||||
if (remote) return remote
|
||||
return await new Promise(resolve => dnode.once('remote', resolve))
|
||||
}
|
||||
|
||||
return getRemote
|
||||
}
|
@ -34,7 +34,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: {},
|
||||
|
@ -36,7 +36,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: {},
|
||||
|
@ -7,8 +7,10 @@
|
||||
const EventEmitter = require('events')
|
||||
const pump = require('pump')
|
||||
const Dnode = require('dnode')
|
||||
const pify = require('pify')
|
||||
const ObservableStore = require('obs-store')
|
||||
const ComposableObservableStore = require('./lib/ComposableObservableStore')
|
||||
const createDnodeRemoteGetter = require('./lib/createDnodeRemoteGetter')
|
||||
const asStream = require('obs-store/lib/asStream')
|
||||
const AccountTracker = require('./lib/account-tracker')
|
||||
const RpcEngine = require('json-rpc-engine')
|
||||
@ -23,10 +25,9 @@ const {setupMultiplex} = require('./lib/stream-utils.js')
|
||||
const KeyringController = require('eth-keyring-controller')
|
||||
const NetworkController = require('./controllers/network')
|
||||
const PreferencesController = require('./controllers/preferences')
|
||||
const AppStateController = require('./controllers/app-state')
|
||||
const CurrencyController = require('./controllers/currency')
|
||||
const ShapeShiftController = require('./controllers/shapeshift')
|
||||
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')
|
||||
@ -53,7 +54,12 @@ const HW_WALLETS_KEYRINGS = [TrezorKeyring.type, LedgerBridgeKeyring.type]
|
||||
const EthQuery = require('eth-query')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const sigUtil = require('eth-sig-util')
|
||||
const { AddressBookController } = require('gaba')
|
||||
const {
|
||||
AddressBookController,
|
||||
ShapeShiftController,
|
||||
PhishingController,
|
||||
} = require('gaba')
|
||||
const backEndMetaMetricsEvent = require('./lib/backend-metametrics')
|
||||
|
||||
|
||||
module.exports = class MetamaskController extends EventEmitter {
|
||||
@ -86,7 +92,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
this.createVaultMutex = new Mutex()
|
||||
|
||||
// network store
|
||||
this.networkController = new NetworkController(initState.NetworkController, this.platform)
|
||||
this.networkController = new NetworkController(initState.NetworkController)
|
||||
|
||||
// preferences controller
|
||||
this.preferencesController = new PreferencesController({
|
||||
@ -96,6 +102,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
network: this.networkController,
|
||||
})
|
||||
|
||||
// app-state controller
|
||||
this.appStateController = new AppStateController({
|
||||
preferencesStore: this.preferencesController.store,
|
||||
onInactiveTimeout: () => this.setLocked(),
|
||||
})
|
||||
|
||||
// currency controller
|
||||
this.currencyController = new CurrencyController({
|
||||
initState: initState.CurrencyController,
|
||||
@ -109,8 +121,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()
|
||||
@ -190,10 +201,26 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
})
|
||||
this.txController.on('newUnapprovedTx', () => opts.showUnapprovedTx())
|
||||
|
||||
this.txController.on(`tx:status-update`, (txId, status) => {
|
||||
this.txController.on(`tx:status-update`, async (txId, status) => {
|
||||
if (status === 'confirmed' || status === 'failed') {
|
||||
const txMeta = this.txController.txStateManager.getTx(txId)
|
||||
this.platform.showTransactionNotification(txMeta)
|
||||
|
||||
const { txReceipt } = txMeta
|
||||
const participateInMetaMetrics = this.preferencesController.getParticipateInMetaMetrics()
|
||||
if (txReceipt && txReceipt.status === '0x0' && participateInMetaMetrics) {
|
||||
const metamaskState = await this.getState()
|
||||
backEndMetaMetricsEvent(metamaskState, {
|
||||
customVariables: {
|
||||
errorMessage: txMeta.simulationFails.reason,
|
||||
},
|
||||
eventOpts: {
|
||||
category: 'backend',
|
||||
action: 'Transactions',
|
||||
name: 'On Chain Failure',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -210,38 +237,40 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
})
|
||||
this.balancesController.updateAllBalances()
|
||||
|
||||
this.shapeshiftController = new ShapeShiftController({
|
||||
initState: initState.ShapeShiftController,
|
||||
})
|
||||
this.shapeshiftController = new ShapeShiftController(undefined, initState.ShapeShiftController)
|
||||
|
||||
this.networkController.lookupNetwork()
|
||||
this.messageManager = new MessageManager()
|
||||
this.personalMessageManager = new PersonalMessageManager()
|
||||
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.providerApprovalController = new ProviderApprovalController({
|
||||
closePopup: opts.closePopup,
|
||||
keyringController: this.keyringController,
|
||||
openPopup: opts.openPopup,
|
||||
platform: opts.platform,
|
||||
preferencesController: this.preferencesController,
|
||||
publicConfigStore: this.publicConfigStore,
|
||||
})
|
||||
|
||||
this.store.updateStructure({
|
||||
AppStateController: this.appStateController.store,
|
||||
TransactionController: this.txController.store,
|
||||
KeyringController: this.keyringController.store,
|
||||
PreferencesController: this.preferencesController.store,
|
||||
AddressBookController: this.addressBookController,
|
||||
CurrencyController: this.currencyController.store,
|
||||
ShapeShiftController: this.shapeshiftController.store,
|
||||
ShapeShiftController: this.shapeshiftController,
|
||||
NetworkController: this.networkController.store,
|
||||
InfuraController: this.infuraController.store,
|
||||
CachedBalancesController: this.cachedBalancesController.store,
|
||||
})
|
||||
|
||||
this.memStore = new ComposableObservableStore(null, {
|
||||
AppStateController: this.appStateController.store,
|
||||
NetworkController: this.networkController.store,
|
||||
AccountTracker: this.accountTracker.store,
|
||||
TxController: this.txController.memStore,
|
||||
@ -256,7 +285,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
RecentBlocksController: this.recentBlocksController.store,
|
||||
AddressBookController: this.addressBookController,
|
||||
CurrencyController: this.currencyController.store,
|
||||
ShapeshiftController: this.shapeshiftController.store,
|
||||
ShapeshiftController: this.shapeshiftController,
|
||||
InfuraController: this.infuraController.store,
|
||||
ProviderApprovalController: this.providerApprovalController.store,
|
||||
})
|
||||
@ -305,22 +334,32 @@ 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 ({ checkIsEnabled }) {
|
||||
// subset of state for metamask inpage provider
|
||||
const publicConfigStore = new ObservableStore()
|
||||
|
||||
// memStore -> transform -> publicConfigStore
|
||||
this.on('update', (memState) => {
|
||||
this.isClientOpenAndUnlocked = memState.isUnlocked && this._isClientOpen
|
||||
// setup memStore subscription hooks
|
||||
this.on('update', updatePublicConfigStore)
|
||||
updatePublicConfigStore(this.getState())
|
||||
|
||||
publicConfigStore.destroy = () => {
|
||||
this.removeEventListener('update', updatePublicConfigStore)
|
||||
}
|
||||
|
||||
function updatePublicConfigStore (memState) {
|
||||
const publicState = selectPublicState(memState)
|
||||
publicConfigStore.putState(publicState)
|
||||
})
|
||||
}
|
||||
|
||||
function selectPublicState (memState) {
|
||||
function selectPublicState ({ isUnlocked, selectedAddress, network, completedOnboarding }) {
|
||||
const isEnabled = checkIsEnabled()
|
||||
const isReady = isUnlocked && isEnabled
|
||||
const result = {
|
||||
selectedAddress: memState.isUnlocked ? memState.selectedAddress : undefined,
|
||||
networkVersion: memState.network,
|
||||
onboardingcomplete: memState.completedOnboarding,
|
||||
isUnlocked,
|
||||
isEnabled,
|
||||
selectedAddress: isReady ? selectedAddress : undefined,
|
||||
networkVersion: network,
|
||||
onboardingcomplete: completedOnboarding,
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -430,6 +469,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// AddressController
|
||||
setAddressBook: this.addressBookController.set.bind(this.addressBookController),
|
||||
|
||||
// AppStateController
|
||||
setLastActiveTime: nodeify(this.appStateController.setLastActiveTime, this.appStateController),
|
||||
|
||||
// KeyringController
|
||||
setLocked: nodeify(this.setLocked, this),
|
||||
createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this),
|
||||
@ -460,9 +502,10 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
signTypedMessage: nodeify(this.signTypedMessage, this),
|
||||
cancelTypedMessage: this.cancelTypedMessage.bind(this),
|
||||
|
||||
approveProviderRequest: providerApprovalController.approveProviderRequest.bind(providerApprovalController),
|
||||
// provider approval
|
||||
approveProviderRequestByOrigin: providerApprovalController.approveProviderRequestByOrigin.bind(providerApprovalController),
|
||||
rejectProviderRequestByOrigin: providerApprovalController.rejectProviderRequestByOrigin.bind(providerApprovalController),
|
||||
clearApprovedOrigins: providerApprovalController.clearApprovedOrigins.bind(providerApprovalController),
|
||||
rejectProviderRequest: providerApprovalController.rejectProviderRequest.bind(providerApprovalController),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1190,9 +1233,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* with higher gas.
|
||||
*
|
||||
* @param {string} txId - The ID of the transaction to speed up.
|
||||
* @param {Function} cb - The callback function called with a full state update.
|
||||
*/
|
||||
async retryTransaction (txId, gasPrice, cb) {
|
||||
async retryTransaction (txId, gasPrice) {
|
||||
await this.txController.retryTransaction(txId, gasPrice)
|
||||
const state = await this.getState()
|
||||
return state
|
||||
@ -1205,7 +1247,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {string=} customGasPrice - the hex value to use for the cancel transaction
|
||||
* @returns {object} MetaMask state
|
||||
*/
|
||||
async createCancelTransaction (originalTxId, customGasPrice, cb) {
|
||||
async createCancelTransaction (originalTxId, customGasPrice) {
|
||||
try {
|
||||
await this.txController.createCancelTransaction(originalTxId, customGasPrice)
|
||||
const state = await this.getState()
|
||||
@ -1215,7 +1257,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
async createSpeedUpTransaction (originalTxId, customGasPrice, cb) {
|
||||
async createSpeedUpTransaction (originalTxId, customGasPrice) {
|
||||
await this.txController.createSpeedUpTransaction(originalTxId, customGasPrice)
|
||||
const state = await this.getState()
|
||||
return state
|
||||
@ -1270,7 +1312,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
*/
|
||||
setupUntrustedCommunication (connectionStream, originDomain) {
|
||||
// Check if new connection is blacklisted
|
||||
if (this.blacklistController.checkForPhishing(originDomain)) {
|
||||
if (this.phishingController.test(originDomain)) {
|
||||
log.debug('MetaMask - sending phishing warning for', originDomain)
|
||||
this.sendPhishingWarning(connectionStream, originDomain)
|
||||
return
|
||||
@ -1279,8 +1321,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
// setup multiplexing
|
||||
const mux = setupMultiplex(connectionStream)
|
||||
// connect features
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'))
|
||||
const publicApi = this.setupPublicApi(mux.createStream('publicApi'), originDomain)
|
||||
this.setupProviderConnection(mux.createStream('provider'), originDomain, publicApi)
|
||||
this.setupPublicConfig(mux.createStream('publicConfig'), originDomain)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1353,7 +1396,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {*} outStream - The stream to provide over.
|
||||
* @param {string} origin - The URI of the requesting resource.
|
||||
*/
|
||||
setupProviderConnection (outStream, origin) {
|
||||
setupProviderConnection (outStream, origin, publicApi) {
|
||||
// setup json rpc engine stack
|
||||
const engine = new RpcEngine()
|
||||
const provider = this.provider
|
||||
@ -1373,6 +1416,11 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
engine.push(subscriptionManager.middleware)
|
||||
// watch asset
|
||||
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
|
||||
// requestAccounts
|
||||
engine.push(this.providerApprovalController.createMiddleware({
|
||||
origin,
|
||||
getSiteMetadata: publicApi && publicApi.getSiteMetadata,
|
||||
}))
|
||||
// forward to metamask primary provider
|
||||
engine.push(providerAsMiddleware(provider))
|
||||
|
||||
@ -1401,18 +1449,56 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
*
|
||||
* @param {*} outStream - The stream to provide public config over.
|
||||
*/
|
||||
setupPublicConfig (outStream) {
|
||||
const configStream = asStream(this.publicConfigStore)
|
||||
setupPublicConfig (outStream, originDomain) {
|
||||
const configStore = this.createPublicConfigStore({
|
||||
// check the providerApprovalController's approvedOrigins
|
||||
checkIsEnabled: () => this.providerApprovalController.shouldExposeAccounts(originDomain),
|
||||
})
|
||||
const configStream = asStream(configStore)
|
||||
|
||||
pump(
|
||||
configStream,
|
||||
outStream,
|
||||
(err) => {
|
||||
configStore.destroy()
|
||||
configStream.destroy()
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A method for providing our public api over a stream.
|
||||
* This includes a method for setting site metadata like title and image
|
||||
*
|
||||
* @param {*} outStream - The stream to provide the api over.
|
||||
*/
|
||||
setupPublicApi (outStream) {
|
||||
const dnode = Dnode()
|
||||
// connect dnode api to remote connection
|
||||
pump(
|
||||
outStream,
|
||||
dnode,
|
||||
outStream,
|
||||
(err) => {
|
||||
// report any error
|
||||
if (err) log.error(err)
|
||||
}
|
||||
)
|
||||
|
||||
const getRemote = createDnodeRemoteGetter(dnode)
|
||||
|
||||
const publicApi = {
|
||||
// wrap with an await remote
|
||||
getSiteMetadata: async () => {
|
||||
const remote = await getRemote()
|
||||
return await pify(remote.getSiteMetadata)()
|
||||
},
|
||||
}
|
||||
|
||||
return publicApi
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a KeyringController update
|
||||
* @param {object} state the KC state
|
||||
@ -1545,7 +1631,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @property {string} depositType - An abbreviation of the type of crypto currency to be deposited.
|
||||
*/
|
||||
createShapeShiftTx (depositAddress, depositType) {
|
||||
this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
|
||||
this.shapeshiftController.createTransaction(depositAddress, depositType)
|
||||
}
|
||||
|
||||
// network
|
||||
@ -1558,9 +1644,9 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @returns {Promise<String>} - The RPC Target URL confirmed.
|
||||
*/
|
||||
|
||||
async updateAndSetCustomRpc (rpcUrl, chainId, ticker = 'ETH', nickname) {
|
||||
await this.preferencesController.updateRpc({ rpcUrl, chainId, ticker, nickname })
|
||||
this.networkController.setRpcTarget(rpcUrl, chainId, ticker, nickname)
|
||||
async updateAndSetCustomRpc (rpcUrl, chainId, ticker = 'ETH', nickname, rpcPrefs) {
|
||||
await this.preferencesController.updateRpc({ rpcUrl, chainId, ticker, nickname, rpcPrefs })
|
||||
this.networkController.setRpcTarget(rpcUrl, chainId, ticker, nickname, rpcPrefs)
|
||||
return rpcUrl
|
||||
}
|
||||
|
||||
@ -1573,15 +1659,15 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
* @param {string} nickname - Optional nickname of the selected network.
|
||||
* @returns {Promise<String>} - The RPC Target URL confirmed.
|
||||
*/
|
||||
async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') {
|
||||
async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '', rpcPrefs = {}) {
|
||||
const frequentRpcListDetail = this.preferencesController.getFrequentRpcListDetail()
|
||||
const rpcSettings = frequentRpcListDetail.find((rpc) => rpcTarget === rpc.rpcUrl)
|
||||
|
||||
if (rpcSettings) {
|
||||
this.networkController.setRpcTarget(rpcSettings.rpcUrl, rpcSettings.chainId, rpcSettings.ticker, rpcSettings.nickname)
|
||||
this.networkController.setRpcTarget(rpcSettings.rpcUrl, rpcSettings.chainId, rpcSettings.ticker, rpcSettings.nickname, rpcPrefs)
|
||||
} else {
|
||||
this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname)
|
||||
await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname)
|
||||
this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname, rpcPrefs)
|
||||
await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname, rpcPrefs)
|
||||
}
|
||||
return rpcTarget
|
||||
}
|
||||
@ -1706,18 +1792,17 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a domain to the {@link BlacklistController} 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks MetaMask
|
||||
*/
|
||||
setLocked () {
|
||||
this.providerApprovalController.setLocked()
|
||||
return this.keyringController.setLocked()
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ function transformState (state) {
|
||||
const newState = state
|
||||
if (!newState.TransactionController) return newState
|
||||
const transactions = newState.TransactionController.transactions
|
||||
newState.TransactionController.transactions = transactions.map((txMeta, _, txList) => {
|
||||
newState.TransactionController.transactions = transactions.map((txMeta, _) => {
|
||||
if (
|
||||
txMeta.status === 'unapproved' &&
|
||||
txMeta.txParams &&
|
||||
|
@ -43,7 +43,7 @@ function normalizeTxParams (txParams) {
|
||||
// functions that handle normalizing of that key in txParams
|
||||
const whiteList = {
|
||||
from: from => ethUtil.addHexPrefix(from).toLowerCase(),
|
||||
to: to => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
|
||||
to: () => ethUtil.addHexPrefix(txParams.to).toLowerCase(),
|
||||
nonce: nonce => ethUtil.addHexPrefix(nonce),
|
||||
value: value => ethUtil.addHexPrefix(value),
|
||||
data: data => ethUtil.addHexPrefix(data),
|
||||
|
@ -60,20 +60,6 @@ class ExtensionPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
addMessageListener (cb) {
|
||||
extension.runtime.onMessage.addListener(cb)
|
||||
}
|
||||
|
||||
sendMessage (message, query = {}) {
|
||||
const id = query.id
|
||||
delete query.id
|
||||
extension.tabs.query({ ...query }, tabs => {
|
||||
tabs.forEach(tab => {
|
||||
extension.tabs.sendMessage(id || tab.id, message)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_showConfirmedTransaction (txMeta) {
|
||||
|
||||
this._subscribeToNotificationClicked()
|
||||
|
@ -1,20 +1,20 @@
|
||||
module.exports = {
|
||||
'confirm sig requests': {
|
||||
signMessage: (msgData, cb) => {
|
||||
signMessage: (_, cb) => {
|
||||
const stateUpdate = {
|
||||
unapprovedMsgs: {},
|
||||
unapprovedMsgCount: 0,
|
||||
}
|
||||
return cb(null, stateUpdate)
|
||||
},
|
||||
signPersonalMessage: (msgData, cb) => {
|
||||
signPersonalMessage: (_, cb) => {
|
||||
const stateUpdate = {
|
||||
unapprovedPersonalMsgs: {},
|
||||
unapprovedPersonalMsgCount: 0,
|
||||
}
|
||||
return cb(null, stateUpdate)
|
||||
},
|
||||
signTypedMessage: (msgData, cb) => {
|
||||
signTypedMessage: (_, cb) => {
|
||||
const stateUpdate = {
|
||||
unapprovedTypedMessages: {},
|
||||
unapprovedTypedMessagesCount: 0,
|
||||
|
@ -192,7 +192,8 @@
|
||||
"type": "testnet"
|
||||
},
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": []
|
||||
"lostAccounts": [],
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -133,7 +133,9 @@
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -156,7 +156,9 @@
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -115,7 +115,9 @@
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -137,7 +137,9 @@
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -116,7 +116,9 @@
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true,
|
||||
"showFiatInTestnets": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -87,7 +87,8 @@
|
||||
"type": "testnet"
|
||||
},
|
||||
"shapeShiftTxList": [],
|
||||
"lostAccounts": []
|
||||
"lostAccounts": [],
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -1058,7 +1058,9 @@
|
||||
"currentLocale": "en",
|
||||
"preferences": {
|
||||
"useNativeCurrencyAsPrimaryCurrency": true
|
||||
}
|
||||
},
|
||||
"completedUiMigration": true,
|
||||
"frequentRpcListDetail": []
|
||||
},
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
|
@ -48,11 +48,11 @@ async function start (fileRegEx, testGenerator) {
|
||||
}
|
||||
*/
|
||||
|
||||
async function startContainer (fileRegEx, testGenerator) {
|
||||
async function startContainer (fileRegEx) {
|
||||
const fileNames = await getAllFileNames('./ui/app')
|
||||
const sFiles = fileNames.filter(name => name.match(fileRegEx))
|
||||
|
||||
async.each(sFiles, async (sFile, cb) => {
|
||||
async.each(sFiles, async (sFile) => {
|
||||
console.log(`sFile`, sFile)
|
||||
const [, sRootPath, sPath] = sFile.match(/^(.+\/)([^/]+)$/)
|
||||
|
||||
@ -91,7 +91,7 @@ async function startContainer (fileRegEx, testGenerator) {
|
||||
const proxyquireObject = ('{\n ' + result
|
||||
.match(/import\s{[\s\S]+?}\sfrom\s.+/g)
|
||||
.map(s => s.replace(/\n/g, ''))
|
||||
.map((s, i) => {
|
||||
.map((s) => {
|
||||
const proxyKeys = s.match(/{.+}/)[0].match(/\w+/g)
|
||||
return '\'' + s.match(/'(.+)'/)[1] + '\': { ' + (proxyKeys.length > 1
|
||||
? '\n ' + proxyKeys.join(': () => {},\n ') + ': () => {},\n '
|
||||
|
@ -315,7 +315,7 @@ createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'dev:test-extension:j
|
||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:extension:js' })
|
||||
createTasksForBuildJsExtension({ buildJsFiles, taskPrefix: 'build:test:extension:js', testing: 'true' })
|
||||
|
||||
function createTasksForBuildJsUIDeps ({ dependenciesToBundle, filename }) {
|
||||
function createTasksForBuildJsUIDeps ({ filename }) {
|
||||
const destinations = browserPlatforms.map(platform => `./dist/${platform}`)
|
||||
|
||||
|
||||
|
585
package-lock.json
generated
585
package-lock.json
generated
@ -1746,6 +1746,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/invariant": {
|
||||
"version": "2.2.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.29.tgz",
|
||||
"integrity": "sha512-lRVw09gOvgviOfeUrKc/pmTiRZ7g7oDOU6OAutyuSHpm1/o2RaBQvRhgK8QEdu+FFuw/wnWb29A/iuxv9i8OpQ=="
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.124",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.124.tgz",
|
||||
"integrity": "sha512-6bKEUVbHJ8z34jisA7lseJZD2g31SIvee3cGX2KEZCS4XXWNbjPZpmO1/2rGNR9BhGtaYr6iYXPl1EzRrDAFTA=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.5.tgz",
|
||||
@ -1768,6 +1778,14 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/redux": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/redux/-/redux-3.6.0.tgz",
|
||||
"integrity": "sha1-8evh5UEVGAcuT9/KXHbhbnTBOZo=",
|
||||
"requires": {
|
||||
"redux": "*"
|
||||
}
|
||||
},
|
||||
"@yarnpkg/lockfile": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||
@ -8281,6 +8299,55 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"disposables": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.2.tgz",
|
||||
"integrity": "sha1-NsamdEdfVaLWkTVnpgFETkh7S24="
|
||||
},
|
||||
"dnd-core": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-3.0.2.tgz",
|
||||
"integrity": "sha1-6UdXdiBTHH7jelGM1d3hfQ798PM=",
|
||||
"requires": {
|
||||
"@types/invariant": "^2.2.29",
|
||||
"@types/lodash": "^4.14.107",
|
||||
"@types/node": "^8.10.11",
|
||||
"@types/redux": "^3.6.0",
|
||||
"asap": "^2.0.6",
|
||||
"invariant": "^2.0.0",
|
||||
"lodash": "^4.2.0",
|
||||
"redux": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "8.10.48",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.48.tgz",
|
||||
"integrity": "sha512-c35YEBTkL4rzXY2ucpSKy+UYHjUBIIkuJbWYbsGIrKLEWU5dgJMmLkkIb3qeC3O3Tpb1ZQCwecscvJTDjDjkRw=="
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"redux": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
|
||||
"integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"symbol-observable": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"dnode": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/dnode/-/dnode-1.2.2.tgz",
|
||||
@ -9759,8 +9826,8 @@
|
||||
}
|
||||
},
|
||||
"eth-contract-metadata": {
|
||||
"version": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f",
|
||||
"from": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f"
|
||||
"version": "github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8",
|
||||
"from": "github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8"
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
"version": "2.0.8",
|
||||
@ -9796,12 +9863,12 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
@ -9859,9 +9926,9 @@
|
||||
}
|
||||
},
|
||||
"eth-json-rpc-filters": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-3.0.1.tgz",
|
||||
"integrity": "sha512-F/UbtD47UnZDFILYP5GJLklYQ7witEI9TdCLgw0r4iag8ZLzz5h4Q+9odg2ASVZKkm8E50mrb7PaYCK0thVxfw==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-3.0.3.tgz",
|
||||
"integrity": "sha512-rX1EbEmRexMfbzntEUemevRM5qfpjschS/dsSqHYXyWnfAGVOegdSxLbLumiKpRBPMMTnQv6B9l6d/lGneXcaw==",
|
||||
"requires": {
|
||||
"await-semaphore": "^0.1.3",
|
||||
"eth-json-rpc-middleware": "^2.6.0",
|
||||
@ -9910,50 +9977,32 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8"
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
@ -9989,6 +10038,15 @@
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
},
|
||||
"json-rpc-engine": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz",
|
||||
@ -10113,12 +10171,12 @@
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@ -10249,16 +10307,41 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10315,8 +10398,34 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereum-common": {
|
||||
@ -10558,16 +10667,41 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#2863c40e0982acfc0b7163f0285d4c56427c7799",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.10.0",
|
||||
"ethereumjs-util": "^5.0.0"
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10766,8 +10900,34 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereum-common": {
|
||||
@ -11045,8 +11205,76 @@
|
||||
"resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz",
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereumjs-util": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethereumjs-util": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz",
|
||||
"integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.0",
|
||||
"create-hash": "^1.1.2",
|
||||
"ethjs-util": "0.1.6",
|
||||
"keccak": "^1.0.2",
|
||||
"rlp": "^2.0.0",
|
||||
"safe-buffer": "^5.1.1",
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
|
||||
"integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethereumjs-util": {
|
||||
@ -13098,15 +13326,16 @@
|
||||
"integrity": "sha1-8ESOgGmFW/Kj5oPNwdMg5+KgfvQ="
|
||||
},
|
||||
"gaba": {
|
||||
"version": "1.0.0-beta.65",
|
||||
"resolved": "https://registry.npmjs.org/gaba/-/gaba-1.0.0-beta.65.tgz",
|
||||
"integrity": "sha512-pX9hMd4RR5AXe7bwIamQEXLJe26fNvjOf7PjkHGKlRjKzBYmxZ03Y/Pa9nklNlG2Shc9sSgB6GXZpYlXNlJRIg==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gaba/-/gaba-1.0.1.tgz",
|
||||
"integrity": "sha512-67Zoaq6wnaBASIXGfu2L+jzx8m+l1tfn6FAEIZI/pMvn/ymk4V9raeqz73QQKq1fF4WcRy2H1Ru1r45J1tDQoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"await-semaphore": "^0.1.3",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#faa4f56fb17b3ae8579df68708be59d617732f31",
|
||||
"eth-json-rpc-infura": "^3.1.2",
|
||||
"eth-keyring-controller": "^4.0.0",
|
||||
"eth-method-registry": "1.1.0",
|
||||
"eth-phishing-detect": "^1.1.13",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-sig-util": "^2.1.0",
|
||||
@ -13183,12 +13412,12 @@
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#572d4bafe08a8a231137e1f9daeb0f8a23f197d2",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@ -13225,6 +13454,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eth-method-registry": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eth-method-registry/-/eth-method-registry-1.1.0.tgz",
|
||||
"integrity": "sha512-jGbbGYd19XJCtoGFtUD2qJYWefKCCbFcu7F/AQ5sJXvqTIVAHnFn3paaV2zhN5t7iyKYp1qxc+ugOky+72xcbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethjs": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"eth-phishing-detect": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/eth-phishing-detect/-/eth-phishing-detect-1.1.13.tgz",
|
||||
@ -13277,6 +13515,119 @@
|
||||
"secp256k1": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"ethjs": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/ethjs/-/ethjs-0.3.9.tgz",
|
||||
"integrity": "sha512-gOQzA3tDUjoLpNONSOALJ/rUFtHi5tXl2mholHasF1cvXhoddqi06yU4OJFJu9AGd6n9v9ywzHlYeIKg1t1hdw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"ethjs-abi": "0.2.1",
|
||||
"ethjs-contract": "0.2.2",
|
||||
"ethjs-filter": "0.1.8",
|
||||
"ethjs-provider-http": "0.1.6",
|
||||
"ethjs-query": "0.3.7",
|
||||
"ethjs-unit": "0.1.6",
|
||||
"ethjs-util": "0.1.3",
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.6",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
|
||||
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=",
|
||||
"dev": true
|
||||
},
|
||||
"ethjs-query": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.7.tgz",
|
||||
"integrity": "sha512-TZnKUwfkWjy0SowFdPLtmsytCorHi0i4vvkQn7Jg8rZt33cRzKhuzOwKr/G3vdigCc+ePXOhUGMcJSAPlOG44A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethjs-format": "0.2.7",
|
||||
"ethjs-rpc": "0.2.0",
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-abi": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.1.tgz",
|
||||
"integrity": "sha1-4KepOn6BFjqUR3utVu3lJKtt5TM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.6",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
|
||||
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-contract": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-contract/-/ethjs-contract-0.2.2.tgz",
|
||||
"integrity": "sha512-xxPqEjsULQ/QNWuvX6Ako0PGs5RxALA8N/H3+boLvnaXDFZVGpD7H63H1gBCRTZyYqCldPpVlVHuw/rD45vazw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethjs-abi": "0.2.0",
|
||||
"ethjs-filter": "0.1.8",
|
||||
"ethjs-util": "0.1.3",
|
||||
"js-sha3": "0.5.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.6",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
|
||||
"integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=",
|
||||
"dev": true
|
||||
},
|
||||
"ethjs-abi": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-abi/-/ethjs-abi-0.2.0.tgz",
|
||||
"integrity": "sha1-0+LCIQEVIPxJm3FoIDbBT8wvWyU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bn.js": "4.11.6",
|
||||
"js-sha3": "0.5.5",
|
||||
"number-to-bn": "1.7.0"
|
||||
}
|
||||
},
|
||||
"ethjs-util": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
|
||||
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-hex-prefixed": "1.0.0",
|
||||
"strip-hex-prefix": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ethjs-filter": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-filter/-/ethjs-filter-0.1.8.tgz",
|
||||
"integrity": "sha512-qTDPskDL2UadHwjvM8A+WG9HwM4/FoSY3p3rMJORkHltYcAuiQZd2otzOYKcL5w2Q3sbAkW/E3yt/FPFL/AVXA==",
|
||||
"dev": true
|
||||
},
|
||||
"ethjs-query": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.8.tgz",
|
||||
@ -13298,6 +13649,12 @@
|
||||
"promise-to-callback": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"js-sha3": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz",
|
||||
"integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=",
|
||||
"dev": true
|
||||
},
|
||||
"obs-store": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/obs-store/-/obs-store-2.4.1.tgz",
|
||||
@ -19504,6 +19861,7 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@ -29849,16 +30207,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
|
||||
},
|
||||
"ping-pong-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ping-pong-stream/-/ping-pong-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-TF6wm6atsCGInawNyr+45XcGhUo=",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"readable-stream": "^2.1.5",
|
||||
"tape": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||
@ -32546,6 +32894,84 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dnd": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-3.0.2.tgz",
|
||||
"integrity": "sha1-sMI9jYKWn1t740y8T4T6H/xcfdw=",
|
||||
"requires": {
|
||||
"disposables": "^1.0.1",
|
||||
"dnd-core": "^3.0.2",
|
||||
"hoist-non-react-statics": "^2.5.0",
|
||||
"invariant": "^2.1.0",
|
||||
"lodash": "^4.2.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"shallowequal": "^1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
|
||||
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
|
||||
},
|
||||
"shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-dnd-html5-backend": {
|
||||
"version": "7.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-7.4.4.tgz",
|
||||
"integrity": "sha512-X/lP92ateY0glHan8mU0JzjBuZL6VHv2Gc/H9OBBxaf/ZCN1oC16MLKdesqG4x1f/NWFTNtuG3W4B99r5gPVog==",
|
||||
"requires": {
|
||||
"dnd-core": "^7.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"dnd-core": {
|
||||
"version": "7.4.4",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-7.4.4.tgz",
|
||||
"integrity": "sha512-xR8SINDCJG9AmKSjXUMJ1PEl8ih1+xSHH8x4DgBtzScXnEtpCnV1ibDZNV0uyps9VgkXTTbYYzJdF04y0v0e3Q==",
|
||||
"requires": {
|
||||
"asap": "^2.0.6",
|
||||
"invariant": "^2.2.4",
|
||||
"redux": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"redux": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz",
|
||||
"integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"symbol-observable": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||
"integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-docgen": {
|
||||
"version": "3.0.0-beta9",
|
||||
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-3.0.0-beta9.tgz",
|
||||
@ -32666,6 +33092,11 @@
|
||||
"react-icon-base": "2.1.0"
|
||||
}
|
||||
},
|
||||
"react-idle-timer": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-4.2.5.tgz",
|
||||
"integrity": "sha512-8B/OwjG8E/DTx1fHYKTpZ4cnCbL9+LOc5I9t8SYe8tbEkP14KChiYg0xPIuyRpO33wUZHcgmQl93CVePaDhmRA=="
|
||||
},
|
||||
"react-input-autosize": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.1.2.tgz",
|
||||
@ -38915,12 +39346,12 @@
|
||||
"integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"ethereumjs-util": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"ethereumjs-abi": {
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#d84a96796079c8595a0c78accd1e7709f2277215",
|
||||
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#8431eab7b3384e65e8126a4602520b78031666fb",
|
||||
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
12
package.json
12
package.json
@ -10,6 +10,8 @@
|
||||
"start:test": "gulp dev:test",
|
||||
"build:test": "gulp build:test",
|
||||
"test": "npm run test:unit && npm run test:integration && npm run lint",
|
||||
"dapp": "static-server test/e2e/beta/contract-test --port 8080",
|
||||
"dapp-chain": "shell-parallel -s 'npm run ganache:start -- -b 2' -x 'sleep 5 && static-server test/e2e/beta/contract-test --port 8080'",
|
||||
"watch:test:unit": "nodemon --exec \"npm run test:unit\" ./test ./app ./ui",
|
||||
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require test/setup.js --recursive \"test/unit/**/*.js\" \"ui/app/**/*.test.js\"",
|
||||
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
|
||||
@ -84,10 +86,10 @@
|
||||
"ensnare": "^1.0.0",
|
||||
"eth-bin-to-ops": "^1.0.1",
|
||||
"eth-block-tracker": "^4.1.0",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#41a14e8004bdd37eaba5af5f2bb1fc4f4ff7063f",
|
||||
"eth-contract-metadata": "github:MetaMask/eth-contract-metadata#dc68506221859bc90792bc5e0279a6835f2484d8",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"eth-hd-keyring": "^1.2.2",
|
||||
"eth-json-rpc-filters": "^3.0.1",
|
||||
"eth-json-rpc-filters": "^3.0.3",
|
||||
"eth-json-rpc-infura": "^3.0.0",
|
||||
"eth-keyring-controller": "^3.3.1",
|
||||
"eth-ledger-bridge-keyring": "^0.2.0",
|
||||
@ -136,7 +138,6 @@
|
||||
"obs-store": "^3.0.2",
|
||||
"percentile": "^1.2.0",
|
||||
"pify": "^3.0.0",
|
||||
"ping-pong-stream": "^1.0.0",
|
||||
"pojo-migrator": "^2.1.0",
|
||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||
"post-message-stream": "^3.0.0",
|
||||
@ -150,8 +151,11 @@
|
||||
"ramda": "^0.24.1",
|
||||
"react": "^15.6.2",
|
||||
"react-addons-css-transition-group": "^15.6.0",
|
||||
"react-dnd": "^3.0.2",
|
||||
"react-dnd-html5-backend": "^7.4.4",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-hyperscript": "^3.0.0",
|
||||
"react-idle-timer": "^4.2.5",
|
||||
"react-inspector": "^2.3.0",
|
||||
"react-markdown": "^3.0.0",
|
||||
"react-media": "^1.8.0",
|
||||
@ -224,7 +228,7 @@
|
||||
"file-loader": "^1.1.11",
|
||||
"fs-extra": "^6.0.1",
|
||||
"fs-promise": "^2.0.3",
|
||||
"gaba": "1.0.0-beta.65",
|
||||
"gaba": "^1.0.1",
|
||||
"ganache-cli": "^6.1.0",
|
||||
"ganache-core": "^2.5.3",
|
||||
"geckodriver": "^1.14.1",
|
||||
|
@ -37,8 +37,10 @@ web3.currentProvider.enable().then(() => {
|
||||
const createToken = document.getElementById('createToken')
|
||||
const transferTokens = document.getElementById('transferTokens')
|
||||
const approveTokens = document.getElementById('approveTokens')
|
||||
const transferTokensWithoutGas = document.getElementById('transferTokensWithoutGas')
|
||||
const approveTokensWithoutGas = document.getElementById('approveTokensWithoutGas')
|
||||
|
||||
deployButton.addEventListener('click', async function (event) {
|
||||
deployButton.addEventListener('click', async function () {
|
||||
document.getElementById('contractStatus').innerHTML = 'Deploying'
|
||||
|
||||
var piggybank = await piggybankContract.new(
|
||||
@ -55,7 +57,7 @@ web3.currentProvider.enable().then(() => {
|
||||
|
||||
document.getElementById('contractStatus').innerHTML = 'Deployed'
|
||||
|
||||
depositButton.addEventListener('click', function (event) {
|
||||
depositButton.addEventListener('click', function () {
|
||||
document.getElementById('contractStatus').innerHTML = 'Deposit initiated'
|
||||
contract.deposit({ from: web3.eth.accounts[0], value: '0x3782dace9d900000' }, function (result) {
|
||||
console.log(result)
|
||||
@ -63,7 +65,7 @@ web3.currentProvider.enable().then(() => {
|
||||
})
|
||||
})
|
||||
|
||||
withdrawButton.addEventListener('click', function (event) {
|
||||
withdrawButton.addEventListener('click', function () {
|
||||
contract.withdraw('0xde0b6b3a7640000', { from: web3.eth.accounts[0] }, function (result) {
|
||||
console.log(result)
|
||||
document.getElementById('contractStatus').innerHTML = 'Withdrawn'
|
||||
@ -75,7 +77,7 @@ web3.currentProvider.enable().then(() => {
|
||||
console.log(piggybank)
|
||||
})
|
||||
|
||||
sendButton.addEventListener('click', function (event) {
|
||||
sendButton.addEventListener('click', function () {
|
||||
web3.eth.sendTransaction({
|
||||
from: web3.eth.accounts[0],
|
||||
to: '0x2f318C334780961FB129D2a6c30D0763d9a5C970',
|
||||
@ -88,7 +90,7 @@ web3.currentProvider.enable().then(() => {
|
||||
})
|
||||
|
||||
|
||||
createToken.addEventListener('click', async function (event) {
|
||||
createToken.addEventListener('click', async function () {
|
||||
var _initialAmount = 100
|
||||
var _tokenName = 'TST'
|
||||
var _decimalUnits = 0
|
||||
@ -124,7 +126,7 @@ web3.currentProvider.enable().then(() => {
|
||||
})
|
||||
})
|
||||
|
||||
approveTokens.addEventListener('click', function (event) {
|
||||
approveTokens.addEventListener('click', function () {
|
||||
contract.approve('0x2f318C334780961FB129D2a6c30D0763d9a5C970', '7', {
|
||||
from: web3.eth.accounts[0],
|
||||
to: contract.address,
|
||||
@ -135,6 +137,29 @@ web3.currentProvider.enable().then(() => {
|
||||
console.log(result)
|
||||
})
|
||||
})
|
||||
|
||||
transferTokensWithoutGas.addEventListener('click', function (event) {
|
||||
console.log(`event`, event)
|
||||
contract.transfer('0x2f318C334780961FB129D2a6c30D0763d9a5C970', '7', {
|
||||
from: web3.eth.accounts[0],
|
||||
to: contract.address,
|
||||
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
||||
gasPrice: '20000000000',
|
||||
}, function (result) {
|
||||
console.log('result', result)
|
||||
})
|
||||
})
|
||||
|
||||
approveTokensWithoutGas.addEventListener('click', function () {
|
||||
contract.approve('0x2f318C334780961FB129D2a6c30D0763d9a5C970', '7', {
|
||||
from: web3.eth.accounts[0],
|
||||
to: contract.address,
|
||||
data: '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005',
|
||||
gasPrice: '20000000000',
|
||||
}, function (result) {
|
||||
console.log(result)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
<button id="createToken">Create Token</button>
|
||||
<button id="transferTokens">Transfer Tokens</button>
|
||||
<button id="approveTokens">Approve Tokens</button>
|
||||
<button id="transferTokensWithoutGas">Transfer Tokens Without Gas</button>
|
||||
<button id="approveTokensWithoutGas">Approve Tokens Without Gas</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -812,10 +812,31 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input'))
|
||||
await gasPriceInput.clear()
|
||||
await gasPriceInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
|
||||
await delay(50)
|
||||
|
||||
await gasPriceInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasPriceInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasPriceInput.sendKeys('10')
|
||||
await gasLimitInput.clear()
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys('60001')
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'e'))
|
||||
await delay(50)
|
||||
|
||||
const save = await findElement(driver, By.xpath(`//button[contains(text(), 'Save')]`))
|
||||
await save.click()
|
||||
@ -1175,7 +1196,7 @@ describe('MetaMask', function () {
|
||||
const transferTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Approve Tokens')]`))
|
||||
await transferTokens.click()
|
||||
|
||||
await closeAllWindowHandlesExcept(driver, extension)
|
||||
await closeAllWindowHandlesExcept(driver, [extension, dapp])
|
||||
await driver.switchTo().window(extension)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
@ -1228,21 +1249,31 @@ describe('MetaMask', function () {
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const [gasPriceInput, gasLimitInput] = await findElements(driver, By.css('.advanced-tab__gas-edit-row__input'))
|
||||
await gasPriceInput.clear()
|
||||
await delay(tinyDelayMs)
|
||||
await gasPriceInput.sendKeys('10')
|
||||
await delay(tinyDelayMs)
|
||||
await gasLimitInput.clear()
|
||||
await delay(tinyDelayMs)
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
|
||||
await gasLimitInput.sendKeys('60000')
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'e'))
|
||||
await gasPriceInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
|
||||
await delay(50)
|
||||
|
||||
// Needed for different behaviour of input in different versions of firefox
|
||||
const gasLimitInputValue = await gasLimitInput.getAttribute('value')
|
||||
if (gasLimitInputValue === '600001') {
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
}
|
||||
await gasPriceInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasPriceInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasPriceInput.sendKeys('10')
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'a'))
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.BACK_SPACE)
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys('60001')
|
||||
await delay(50)
|
||||
await gasLimitInput.sendKeys(Key.chord(Key.CONTROL, 'e'))
|
||||
await delay(50)
|
||||
|
||||
const save = await findElement(driver, By.css('.page-container__footer-button'))
|
||||
await save.click()
|
||||
@ -1271,6 +1302,105 @@ describe('MetaMask', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Tranfers a custom token from dapp when no gas value is specified', () => {
|
||||
it('transfers an already created token, without specifying gas', async () => {
|
||||
const windowHandles = await driver.getAllWindowHandles()
|
||||
const extension = windowHandles[0]
|
||||
const dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles)
|
||||
await closeAllWindowHandlesExcept(driver, [extension, dapp])
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await driver.switchTo().window(dapp)
|
||||
|
||||
const transferTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Transfer Tokens Without Gas')]`))
|
||||
await transferTokens.click()
|
||||
|
||||
await closeAllWindowHandlesExcept(driver, [extension, dapp])
|
||||
await driver.switchTo().window(extension)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await driver.wait(async () => {
|
||||
const pendingTxes = await findElements(driver, By.css('.transaction-list__pending-transactions .transaction-list-item'))
|
||||
return pendingTxes.length === 1
|
||||
}, 10000)
|
||||
|
||||
const [txListItem] = await findElements(driver, By.css('.transaction-list-item'))
|
||||
const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
|
||||
await driver.wait(until.elementTextMatches(txListValue, /-7\s*TST/))
|
||||
await txListItem.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
await delay(regularDelayMs)
|
||||
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
|
||||
await confirmButton.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('finds the transaction in the transactions list', async function () {
|
||||
await driver.wait(async () => {
|
||||
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
|
||||
return confirmedTxes.length === 4
|
||||
}, 10000)
|
||||
|
||||
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
|
||||
await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/))
|
||||
const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
|
||||
await driver.wait(until.elementTextMatches(txStatuses[0], /Sent Tokens/))
|
||||
})
|
||||
})
|
||||
|
||||
describe('Approves a custom token from dapp when no gas value is specified', () => {
|
||||
it('approves an already created token', async () => {
|
||||
const windowHandles = await driver.getAllWindowHandles()
|
||||
const extension = windowHandles[0]
|
||||
const dapp = await switchToWindowWithTitle(driver, 'E2E Test Dapp', windowHandles)
|
||||
await closeAllWindowHandlesExcept(driver, [extension, dapp])
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await driver.switchTo().window(dapp)
|
||||
await delay(tinyDelayMs)
|
||||
|
||||
const transferTokens = await findElement(driver, By.xpath(`//button[contains(text(), 'Approve Tokens Without Gas')]`))
|
||||
await transferTokens.click()
|
||||
|
||||
await closeAllWindowHandlesExcept(driver, extension)
|
||||
await driver.switchTo().window(extension)
|
||||
await delay(regularDelayMs)
|
||||
|
||||
await driver.wait(async () => {
|
||||
const pendingTxes = await findElements(driver, By.css('.transaction-list__pending-transactions .transaction-list-item'))
|
||||
return pendingTxes.length === 1
|
||||
}, 10000)
|
||||
|
||||
const [txListItem] = await findElements(driver, By.css('.transaction-list-item'))
|
||||
const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
|
||||
await driver.wait(until.elementTextMatches(txListValue, /-7\s*TST/))
|
||||
await txListItem.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('submits the transaction', async function () {
|
||||
await delay(regularDelayMs)
|
||||
const confirmButton = await findElement(driver, By.xpath(`//button[contains(text(), 'Confirm')]`))
|
||||
await confirmButton.click()
|
||||
await delay(regularDelayMs)
|
||||
})
|
||||
|
||||
it('finds the transaction in the transactions list', async function () {
|
||||
await driver.wait(async () => {
|
||||
const confirmedTxes = await findElements(driver, By.css('.transaction-list__completed-transactions .transaction-list-item'))
|
||||
return confirmedTxes.length === 5
|
||||
}, 10000)
|
||||
|
||||
const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary'))
|
||||
await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/))
|
||||
const txStatuses = await findElements(driver, By.css('.transaction-list-item__action'))
|
||||
await driver.wait(until.elementTextMatches(txStatuses[0], /Approve/))
|
||||
})
|
||||
})
|
||||
|
||||
describe('Hide token', () => {
|
||||
it('hides the token when clicked', async () => {
|
||||
const [hideTokenEllipsis] = await findElements(driver, By.css('.token-list-item__ellipsis'))
|
||||
@ -1341,11 +1471,14 @@ describe('MetaMask', function () {
|
||||
await customRpcButton.click()
|
||||
await delay(regularDelayMs)
|
||||
|
||||
const customRpcInput = await findElement(driver, By.css('input[placeholder="New RPC URL"]'))
|
||||
await findElement(driver, By.css('.settings-page__sub-header-text'))
|
||||
|
||||
const customRpcInputs = await findElements(driver, By.css('input[type="text"]'))
|
||||
const customRpcInput = customRpcInputs[1]
|
||||
await customRpcInput.clear()
|
||||
await customRpcInput.sendKeys(customRpcUrl)
|
||||
|
||||
const customRpcSave = await findElement(driver, By.css('.settings-tab__rpc-save-button'))
|
||||
const customRpcSave = await findElement(driver, By.css('.page-container__footer-button'))
|
||||
await customRpcSave.click()
|
||||
await delay(largeDelayMs * 2)
|
||||
})
|
||||
|
@ -15,16 +15,25 @@ QUnit.test('successful confirmation of sig requests', (assert) => {
|
||||
})
|
||||
})
|
||||
|
||||
async function runConfirmSigRequestsTest (assert, done) {
|
||||
global.ethQuery = global.ethQuery || {}
|
||||
|
||||
async function runConfirmSigRequestsTest (assert) {
|
||||
const selectState = await queryAsync($, 'select')
|
||||
selectState.val('confirm sig requests')
|
||||
reactTriggerChange(selectState[0])
|
||||
|
||||
const realFetch = window.fetch.bind(window)
|
||||
global.fetch = (...args) => {
|
||||
if (args[0].match(/chromeextensionmm/)) {
|
||||
if (args[0] === 'https://ethgasstation.info/json/ethgasAPI.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasBasic)) })
|
||||
} else if (args[0] === 'https://ethgasstation.info/json/predictTable.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasPredictTable)) })
|
||||
} else if (args[0] === 'https://dev.blockscale.net/api/gasexpress.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.gasExpress)) })
|
||||
} else if (args[0].match(/chromeextensionmm/)) {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) })
|
||||
}
|
||||
return window.fetch(...args)
|
||||
return realFetch.fetch(...args)
|
||||
}
|
||||
|
||||
const pendingRequestItem = $.find('.transaction-list-item .transaction-list-item__grid')
|
||||
|
@ -16,16 +16,23 @@ QUnit.test('renders localized currency', (assert) => {
|
||||
})
|
||||
})
|
||||
|
||||
async function runCurrencyLocalizationTest (assert, done) {
|
||||
async function runCurrencyLocalizationTest (assert) {
|
||||
console.log('*** start runCurrencyLocalizationTest')
|
||||
const selectState = await queryAsync($, 'select')
|
||||
selectState.val('currency localization')
|
||||
|
||||
const realFetch = window.fetch.bind(window)
|
||||
global.fetch = (...args) => {
|
||||
if (args[0].match(/chromeextensionmm/)) {
|
||||
if (args[0] === 'https://ethgasstation.info/json/ethgasAPI.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasBasic)) })
|
||||
} else if (args[0] === 'https://ethgasstation.info/json/predictTable.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasPredictTable)) })
|
||||
} else if (args[0] === 'https://dev.blockscale.net/api/gasexpress.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.gasExpress)) })
|
||||
} else if (args[0].match(/chromeextensionmm/)) {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) })
|
||||
}
|
||||
return window.fetch(...args)
|
||||
return realFetch.fetch(...args)
|
||||
}
|
||||
|
||||
await timeout(1000)
|
||||
|
@ -22,9 +22,10 @@ global.ethQuery = {
|
||||
|
||||
global.ethereumProvider = {}
|
||||
|
||||
async function runSendFlowTest (assert, done) {
|
||||
async function runSendFlowTest (assert) {
|
||||
const tempFetch = global.fetch
|
||||
|
||||
const realFetch = window.fetch.bind(window)
|
||||
global.fetch = (...args) => {
|
||||
if (args[0] === 'https://ethgasstation.info/json/ethgasAPI.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasBasic)) })
|
||||
@ -35,7 +36,7 @@ async function runSendFlowTest (assert, done) {
|
||||
} else if (args[0].match(/chromeextensionmm/)) {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) })
|
||||
}
|
||||
return window.fetch(...args)
|
||||
return realFetch.fetch(...args)
|
||||
}
|
||||
|
||||
console.log('*** start runSendFlowTest')
|
||||
|
@ -20,17 +20,24 @@ global.ethQuery.getTransactionCount = (_, cb) => {
|
||||
cb(null, '0x4')
|
||||
}
|
||||
|
||||
async function runTxListItemsTest (assert, done) {
|
||||
async function runTxListItemsTest (assert) {
|
||||
console.log('*** start runTxListItemsTest')
|
||||
const selectState = await queryAsync($, 'select')
|
||||
selectState.val('tx list items')
|
||||
reactTriggerChange(selectState[0])
|
||||
|
||||
const realFetch = window.fetch.bind(window)
|
||||
global.fetch = (...args) => {
|
||||
if (args[0].match(/chromeextensionmm/)) {
|
||||
if (args[0] === 'https://ethgasstation.info/json/ethgasAPI.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasBasic)) })
|
||||
} else if (args[0] === 'https://ethgasstation.info/json/predictTable.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.ethGasPredictTable)) })
|
||||
} else if (args[0] === 'https://dev.blockscale.net/api/gasexpress.json') {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.gasExpress)) })
|
||||
} else if (args[0].match(/chromeextensionmm/)) {
|
||||
return Promise.resolve({ json: () => Promise.resolve(JSON.parse(fetchMockResponses.metametrics)) })
|
||||
}
|
||||
return window.fetch(...args)
|
||||
return realFetch.fetch(...args)
|
||||
}
|
||||
|
||||
const metamaskLogo = await queryAsync($, '.app-header__logo-container')
|
||||
|
@ -4,12 +4,12 @@ let cacheVal
|
||||
|
||||
module.exports = {
|
||||
|
||||
encrypt (password, dataObj) {
|
||||
encrypt (_, dataObj) {
|
||||
cacheVal = dataObj
|
||||
return Promise.resolve(mockHex)
|
||||
},
|
||||
|
||||
decrypt (password, text) {
|
||||
decrypt () {
|
||||
return Promise.resolve(cacheVal || {})
|
||||
},
|
||||
|
||||
@ -21,7 +21,7 @@ module.exports = {
|
||||
return this.decrypt(key, text)
|
||||
},
|
||||
|
||||
keyFromPassword (password) {
|
||||
keyFromPassword () {
|
||||
return Promise.resolve(mockKey)
|
||||
},
|
||||
|
||||
|
@ -6,7 +6,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
function timeout (time) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time || 1500)
|
||||
})
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ describe('tx confirmation screen', function () {
|
||||
describe('cancelTx', function () {
|
||||
before(function (done) {
|
||||
actions._setBackgroundConnection({
|
||||
approveTransaction (txId, cb) { cb('An error!') },
|
||||
cancelTransaction (txId, cb) { cb() },
|
||||
approveTransaction (_, cb) { cb('An error!') },
|
||||
cancelTransaction (_, cb) { cb() },
|
||||
clearSeedWordCache (cb) { cb() },
|
||||
getState (cb) { cb() },
|
||||
})
|
||||
|
@ -1,56 +0,0 @@
|
||||
const assert = require('assert')
|
||||
const BlacklistController = require('../../../../app/scripts/controllers/blacklist')
|
||||
|
||||
describe('blacklist controller', function () {
|
||||
let blacklistController
|
||||
|
||||
before(() => {
|
||||
blacklistController = new BlacklistController()
|
||||
})
|
||||
|
||||
describe('whitelistDomain', function () {
|
||||
it('should add hostname to the runtime whitelist', function () {
|
||||
blacklistController.whitelistDomain('foo.com')
|
||||
assert.deepEqual(blacklistController.store.getState().whitelist, ['foo.com'])
|
||||
|
||||
blacklistController.whitelistDomain('bar.com')
|
||||
assert.deepEqual(blacklistController.store.getState().whitelist, ['bar.com', 'foo.com'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('checkForPhishing', function () {
|
||||
it('should not flag whitelisted values', function () {
|
||||
const result = blacklistController.checkForPhishing('www.metamask.io')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
it('should flag explicit values', function () {
|
||||
const result = blacklistController.checkForPhishing('metamask.com')
|
||||
assert.equal(result, true)
|
||||
})
|
||||
it('should flag levenshtein values', function () {
|
||||
const result = blacklistController.checkForPhishing('metmask.io')
|
||||
assert.equal(result, true)
|
||||
})
|
||||
it('should not flag not-even-close values', function () {
|
||||
const result = blacklistController.checkForPhishing('example.com')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
it('should not flag the ropsten faucet domains', function () {
|
||||
const result = blacklistController.checkForPhishing('faucet.metamask.io')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
it('should not flag the mascara domain', function () {
|
||||
const result = blacklistController.checkForPhishing('zero.metamask.io')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
it('should not flag the mascara-faucet domain', function () {
|
||||
const result = blacklistController.checkForPhishing('zero-faucet.metamask.io')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
it('should not flag whitelisted domain', function () {
|
||||
blacklistController.whitelistDomain('metamask.com')
|
||||
const result = blacklistController.checkForPhishing('metamask.com')
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
@ -59,7 +59,7 @@ describe('currency-controller', function () {
|
||||
|
||||
|
||||
var promise = new Promise(
|
||||
function (resolve, reject) {
|
||||
function (resolve) {
|
||||
currencyController.setCurrentCurrency('jpy')
|
||||
currencyController.updateConversionRate().then(function () {
|
||||
resolve()
|
||||
|
@ -49,7 +49,7 @@ describe('MetaMaskController', function () {
|
||||
showUnapprovedTx: noop,
|
||||
showUnconfirmedMessage: noop,
|
||||
encryptor: {
|
||||
encrypt: function (password, object) {
|
||||
encrypt: function (_, object) {
|
||||
this.object = object
|
||||
return Promise.resolve('mock-encrypted')
|
||||
},
|
||||
@ -144,7 +144,7 @@ describe('MetaMaskController', function () {
|
||||
sandbox.stub(metamaskController, 'getBalance')
|
||||
metamaskController.getBalance.callsFake(() => { return Promise.resolve('0x0') })
|
||||
|
||||
await metamaskController.createNewVaultAndRestore(password, TEST_SEED.slice(0, -1)).catch((e) => null)
|
||||
await metamaskController.createNewVaultAndRestore(password, TEST_SEED.slice(0, -1)).catch(() => null)
|
||||
await metamaskController.createNewVaultAndRestore(password, TEST_SEED)
|
||||
|
||||
assert(metamaskController.keyringController.createNewVaultAndRestore.calledTwice)
|
||||
@ -207,7 +207,7 @@ describe('MetaMaskController', function () {
|
||||
const accounts = {}
|
||||
const balance = '0x14ced5122ce0a000'
|
||||
const ethQuery = new EthQuery()
|
||||
sinon.stub(ethQuery, 'getBalance').callsFake((account, callback) => {
|
||||
sinon.stub(ethQuery, 'getBalance').callsFake((_, callback) => {
|
||||
callback(undefined, balance)
|
||||
})
|
||||
|
||||
@ -295,7 +295,7 @@ describe('MetaMaskController', function () {
|
||||
|
||||
it('should add the Trezor Hardware keyring', async function () {
|
||||
sinon.spy(metamaskController.keyringController, 'addNewKeyring')
|
||||
await metamaskController.connectHardware('trezor', 0).catch((e) => null)
|
||||
await metamaskController.connectHardware('trezor', 0).catch(() => null)
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Trezor Hardware'
|
||||
)
|
||||
@ -305,7 +305,7 @@ describe('MetaMaskController', function () {
|
||||
|
||||
it('should add the Ledger Hardware keyring', async function () {
|
||||
sinon.spy(metamaskController.keyringController, 'addNewKeyring')
|
||||
await metamaskController.connectHardware('ledger', 0).catch((e) => null)
|
||||
await metamaskController.connectHardware('ledger', 0).catch(() => null)
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Ledger Hardware'
|
||||
)
|
||||
@ -325,7 +325,7 @@ describe('MetaMaskController', function () {
|
||||
})
|
||||
|
||||
it('should be locked by default', async function () {
|
||||
await metamaskController.connectHardware('trezor', 0).catch((e) => null)
|
||||
await metamaskController.connectHardware('trezor', 0).catch(() => null)
|
||||
const status = await metamaskController.checkHardwareStatus('trezor')
|
||||
assert.equal(status, false)
|
||||
})
|
||||
@ -341,7 +341,7 @@ describe('MetaMaskController', function () {
|
||||
})
|
||||
|
||||
it('should wipe all the keyring info', async function () {
|
||||
await metamaskController.connectHardware('trezor', 0).catch((e) => null)
|
||||
await metamaskController.connectHardware('trezor', 0).catch(() => null)
|
||||
await metamaskController.forgetDevice('trezor')
|
||||
const keyrings = await metamaskController.keyringController.getKeyringsByType(
|
||||
'Trezor Hardware'
|
||||
@ -376,7 +376,7 @@ describe('MetaMaskController', function () {
|
||||
sinon.spy(metamaskController.preferencesController, 'setAddresses')
|
||||
sinon.spy(metamaskController.preferencesController, 'setSelectedAddress')
|
||||
sinon.spy(metamaskController.preferencesController, 'setAccountLabel')
|
||||
await metamaskController.connectHardware('trezor', 0, `m/44/0'/0'`).catch((e) => null)
|
||||
await metamaskController.connectHardware('trezor', 0, `m/44/0'/0'`).catch(() => null)
|
||||
await metamaskController.unlockHardwareWalletAccount(accountToUnlock, 'trezor', `m/44/0'/0'`)
|
||||
})
|
||||
|
||||
@ -464,7 +464,7 @@ describe('MetaMaskController', function () {
|
||||
|
||||
depositAddress = '3EevLFfB4H4XMWQwYCgjLie1qCAGpd2WBc'
|
||||
depositType = 'ETH'
|
||||
shapeShiftTxList = metamaskController.shapeshiftController.store.getState().shapeShiftTxList
|
||||
shapeShiftTxList = metamaskController.shapeshiftController.state.shapeShiftTxList
|
||||
})
|
||||
|
||||
it('creates a shapeshift tx', async function () {
|
||||
@ -752,12 +752,11 @@ describe('MetaMaskController', function () {
|
||||
})
|
||||
|
||||
it('sets up phishing stream for untrusted communication ', async () => {
|
||||
await metamaskController.blacklistController.updatePhishingList()
|
||||
console.log(blacklistJSON.blacklist.includes(phishingUrl))
|
||||
await metamaskController.phishingController.updatePhishingLists()
|
||||
|
||||
const { promise, resolve } = deferredPromise()
|
||||
|
||||
streamTest = createThoughStream((chunk, enc, cb) => {
|
||||
streamTest = createThoughStream((chunk, _, cb) => {
|
||||
if (chunk.name !== 'phishing') return cb()
|
||||
assert.equal(chunk.data.hostname, phishingUrl)
|
||||
resolve()
|
||||
@ -777,7 +776,7 @@ describe('MetaMaskController', function () {
|
||||
})
|
||||
|
||||
it('sets up controller dnode api for trusted communication', function (done) {
|
||||
streamTest = createThoughStream((chunk, enc, cb) => {
|
||||
streamTest = createThoughStream((chunk, _, cb) => {
|
||||
assert.equal(chunk.name, 'controller')
|
||||
cb()
|
||||
done()
|
||||
|
@ -527,14 +527,14 @@ describe('preferences controller', function () {
|
||||
it('should add custom RPC url to state', function () {
|
||||
preferencesController.addToFrequentRpcList('rpc_url', 1)
|
||||
preferencesController.addToFrequentRpcList('http://localhost:8545', 1)
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }])
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '', rpcPrefs: {} }])
|
||||
preferencesController.addToFrequentRpcList('rpc_url', 1)
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }])
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '', rpcPrefs: {} }])
|
||||
})
|
||||
|
||||
it('should remove custom RPC url from state', function () {
|
||||
preferencesController.addToFrequentRpcList('rpc_url', 1)
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }])
|
||||
assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '', rpcPrefs: {} }])
|
||||
preferencesController.removeFromFrequentRpcList('other_rpc_url')
|
||||
preferencesController.removeFromFrequentRpcList('http://localhost:8545')
|
||||
preferencesController.removeFromFrequentRpcList('rpc_url')
|
||||
|
@ -100,7 +100,7 @@ describe('PendingTransactionTracker', function () {
|
||||
|
||||
describe('#_checkPendingTx', function () {
|
||||
it('should emit \'tx:failed\' if the txMeta does not have a hash', function (done) {
|
||||
pendingTxTracker.once('tx:failed', (txId, err) => {
|
||||
pendingTxTracker.once('tx:failed', (txId) => {
|
||||
assert(txId, txMetaNoHash.id, 'should pass txId')
|
||||
done()
|
||||
})
|
||||
@ -128,7 +128,7 @@ describe('PendingTransactionTracker', function () {
|
||||
pendingTxTracker.getPendingTransactions = () => txList
|
||||
pendingTxTracker._checkPendingTx = (tx) => { tx.resolve(tx) }
|
||||
Promise.all(txList.map((tx) => tx.processed))
|
||||
.then((txCompletedList) => done())
|
||||
.then(() => done())
|
||||
.catch(done)
|
||||
|
||||
pendingTxTracker.updatePendingTxs()
|
||||
@ -152,7 +152,7 @@ describe('PendingTransactionTracker', function () {
|
||||
pendingTxTracker.getPendingTransactions = () => txList
|
||||
pendingTxTracker._resubmitTx = async (tx) => { tx.resolve(tx) }
|
||||
Promise.all(txList.map((tx) => tx.processed))
|
||||
.then((txCompletedList) => done())
|
||||
.then(() => done())
|
||||
.catch(done)
|
||||
pendingTxTracker.resubmitPendingTxs(blockNumberStub)
|
||||
})
|
||||
@ -178,7 +178,7 @@ describe('PendingTransactionTracker', function () {
|
||||
throw new Error(knownErrors.pop())
|
||||
}
|
||||
Promise.all(txList.map((tx) => tx.processed))
|
||||
.then((txCompletedList) => done())
|
||||
.then(() => done())
|
||||
.catch(done)
|
||||
|
||||
pendingTxTracker.resubmitPendingTxs(blockNumberStub)
|
||||
@ -194,9 +194,9 @@ describe('PendingTransactionTracker', function () {
|
||||
})
|
||||
|
||||
pendingTxTracker.getPendingTransactions = () => txList
|
||||
pendingTxTracker._resubmitTx = async (tx) => { throw new TypeError('im some real error') }
|
||||
pendingTxTracker._resubmitTx = async () => { throw new TypeError('im some real error') }
|
||||
Promise.all(txList.map((tx) => tx.processed))
|
||||
.then((txCompletedList) => done())
|
||||
.then(() => done())
|
||||
.catch(done)
|
||||
|
||||
pendingTxTracker.resubmitPendingTxs(blockNumberStub)
|
||||
|
@ -8,6 +8,13 @@ const TransactionController = require('../../../../../app/scripts/controllers/tr
|
||||
const {
|
||||
TRANSACTION_TYPE_RETRY,
|
||||
} = require('../../../../../app/scripts/controllers/transactions/enums')
|
||||
const {
|
||||
TOKEN_METHOD_APPROVE,
|
||||
TOKEN_METHOD_TRANSFER,
|
||||
SEND_ETHER_ACTION_KEY,
|
||||
DEPLOY_CONTRACT_ACTION_KEY,
|
||||
CONTRACT_INTERACTION_KEY,
|
||||
} = require('../../../../../ui/app/helpers/constants/transactions.js')
|
||||
const { createTestProviderTools, getTestAccounts } = require('../../../../stub/provider')
|
||||
|
||||
const noop = () => true
|
||||
@ -537,6 +544,86 @@ describe('Transaction Controller', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#_determineTransactionCategory', function () {
|
||||
it('should return a simple send transactionCategory when to is truthy but data is falsey', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '0xabc',
|
||||
data: '',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: SEND_ETHER_ACTION_KEY, getCodeResponse: undefined })
|
||||
})
|
||||
|
||||
it('should return a token transfer transactionCategory when data is for the respective method call', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '0xabc',
|
||||
data: '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: TOKEN_METHOD_TRANSFER, getCodeResponse: undefined })
|
||||
})
|
||||
|
||||
it('should return a token approve transactionCategory when data is for the respective method call', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '0xabc',
|
||||
data: '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: TOKEN_METHOD_APPROVE, getCodeResponse: undefined })
|
||||
})
|
||||
|
||||
it('should return a contract deployment transactionCategory when to is falsey and there is data', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '',
|
||||
data: '0xabd',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: DEPLOY_CONTRACT_ACTION_KEY, getCodeResponse: undefined })
|
||||
})
|
||||
|
||||
it('should return a simple send transactionCategory with a 0x getCodeResponse when there is data and but the to address is not a contract address', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
||||
data: '0xabd',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: SEND_ETHER_ACTION_KEY, getCodeResponse: '0x' })
|
||||
})
|
||||
|
||||
it('should return a simple send transactionCategory with a null getCodeResponse when to is truthy and there is data and but getCode returns an error', async function () {
|
||||
const result = await txController._determineTransactionCategory({
|
||||
to: '0xabc',
|
||||
data: '0xabd',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: SEND_ETHER_ACTION_KEY, getCodeResponse: null })
|
||||
})
|
||||
|
||||
it('should return a contract interaction transactionCategory with the correct getCodeResponse when to is truthy and there is data and it is not a token transaction', async function () {
|
||||
const _providerResultStub = {
|
||||
// 1 gwei
|
||||
eth_gasPrice: '0x0de0b6b3a7640000',
|
||||
// by default, all accounts are external accounts (not contracts)
|
||||
eth_getCode: '0xa',
|
||||
}
|
||||
const _provider = createTestProviderTools({ scaffold: _providerResultStub }).provider
|
||||
const _fromAccount = getTestAccounts()[0]
|
||||
const _blockTrackerStub = new EventEmitter()
|
||||
_blockTrackerStub.getCurrentBlock = noop
|
||||
_blockTrackerStub.getLatestBlock = noop
|
||||
const _txController = new TransactionController({
|
||||
provider: _provider,
|
||||
getGasPrice: function () { return '0xee6b2800' },
|
||||
networkStore: new ObservableStore(currentNetworkId),
|
||||
txHistoryLimit: 10,
|
||||
blockTracker: _blockTrackerStub,
|
||||
signTransaction: (ethTx) => new Promise((resolve) => {
|
||||
ethTx.sign(_fromAccount.key)
|
||||
resolve()
|
||||
}),
|
||||
})
|
||||
const result = await _txController._determineTransactionCategory({
|
||||
to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
|
||||
data: 'abd',
|
||||
})
|
||||
assert.deepEqual(result, { transactionCategory: CONTRACT_INTERACTION_KEY, getCodeResponse: '0x0a' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getPendingTransactions', function () {
|
||||
beforeEach(function () {
|
||||
txController.txStateManager._saveTxList([
|
||||
|
@ -11,7 +11,7 @@ describe('txUtils', function () {
|
||||
|
||||
before(function () {
|
||||
txUtils = new TxUtils(new Proxy({}, {
|
||||
get: (obj, name) => {
|
||||
get: () => {
|
||||
return () => {}
|
||||
},
|
||||
}))
|
||||
|
@ -29,7 +29,7 @@ describe('Transaction state history helper', function () {
|
||||
|
||||
describe('#migrateFromSnapshotsToDiffs', function () {
|
||||
it('migrates history to diffs and can recover original values', function () {
|
||||
testVault.data.TransactionController.transactions.forEach((tx, index) => {
|
||||
testVault.data.TransactionController.transactions.forEach((tx) => {
|
||||
const newHistory = txStateHistoryHelper.migrateFromSnapshotsToDiffs(tx.history)
|
||||
newHistory.forEach((newEntry, index) => {
|
||||
if (index === 0) {
|
||||
|
@ -55,7 +55,7 @@ describe('TransactionStateManager', function () {
|
||||
it('should emit a rejected event to signal the exciton of callback', (done) => {
|
||||
const tx = { id: 1, status: 'unapproved', metamaskNetworkId: currentNetworkId, txParams: {} }
|
||||
txStateManager.addTx(tx)
|
||||
const noop = function (err, txId) {
|
||||
const noop = function (err) {
|
||||
if (err) {
|
||||
console.log('Error: ', err)
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ describe('EdgeEncryptor', function () {
|
||||
edgeEncryptor.encrypt(password, data)
|
||||
.then(function (encryptedData) {
|
||||
edgeEncryptor.decrypt('wrong password', encryptedData)
|
||||
.then(function (decryptedData) {
|
||||
.then(function () {
|
||||
assert.fail('could decrypt with wrong password')
|
||||
done()
|
||||
})
|
||||
|
@ -61,7 +61,7 @@ describe('Migrator', () => {
|
||||
const migrator = new Migrator({ migrations: [{ version: 1, migrate: async () => { throw new Error('test') } } ] })
|
||||
migrator.on('error', () => done())
|
||||
migrator.migrateData({ meta: {version: 0} })
|
||||
.then((migratedData) => {
|
||||
.then(() => {
|
||||
}).catch(done)
|
||||
})
|
||||
|
||||
|
@ -44,7 +44,7 @@ describe('Actions', () => {
|
||||
showUnapprovedTx: noop,
|
||||
showUnconfirmedMessage: noop,
|
||||
encryptor: {
|
||||
encrypt: function (password, object) {
|
||||
encrypt: function (_, object) {
|
||||
this.object = object
|
||||
return Promise.resolve('mock-encrypted')
|
||||
},
|
||||
@ -103,7 +103,7 @@ describe('Actions', () => {
|
||||
|
||||
submitPasswordSpy = sinon.stub(background, 'submitPassword')
|
||||
|
||||
submitPasswordSpy.callsFake((password, callback) => {
|
||||
submitPasswordSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error in submitPassword'))
|
||||
})
|
||||
|
||||
@ -235,7 +235,7 @@ describe('Actions', () => {
|
||||
|
||||
createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
|
||||
|
||||
createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
|
||||
createNewVaultAndRestoreSpy.callsFake((_, __, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -279,7 +279,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
createNewVaultAndKeychainSpy = sinon.stub(background, 'createNewVaultAndKeychain')
|
||||
createNewVaultAndKeychainSpy.callsFake((password, callback) => {
|
||||
createNewVaultAndKeychainSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -342,7 +342,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
submitPasswordSpy = sinon.stub(background, 'submitPassword')
|
||||
submitPasswordSpy.callsFake((password, callback) => {
|
||||
submitPasswordSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -414,7 +414,7 @@ describe('Actions', () => {
|
||||
|
||||
it('displays warning error message when submitPassword in background errors', () => {
|
||||
submitPasswordSpy = sinon.stub(background, 'submitPassword')
|
||||
submitPasswordSpy.callsFake((password, callback) => {
|
||||
submitPasswordSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -483,7 +483,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
removeAccountSpy = sinon.stub(background, 'removeAccount')
|
||||
removeAccountSpy.callsFake((address, callback) => {
|
||||
removeAccountSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -522,7 +522,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
|
||||
addNewKeyringSpy.callsFake((type, opts, callback) => {
|
||||
addNewKeyringSpy.callsFake((_, __, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -611,7 +611,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
importAccountWithStrategySpy = sinon.stub(background, 'importAccountWithStrategy')
|
||||
importAccountWithStrategySpy.callsFake((strategy, args, callback) => {
|
||||
importAccountWithStrategySpy.callsFake((_, __, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -668,7 +668,7 @@ describe('Actions', () => {
|
||||
{ type: 'HIDE_LOADING_INDICATION' },
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
setCurrentCurrencySpy.callsFake((currencyCode, callback) => {
|
||||
setCurrentCurrencySpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -720,7 +720,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
signMessageSpy = sinon.stub(background, 'signMessage')
|
||||
signMessageSpy.callsFake((msgData, callback) => {
|
||||
signMessageSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -775,7 +775,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
signPersonalMessageSpy = sinon.stub(background, 'signPersonalMessage')
|
||||
signPersonalMessageSpy.callsFake((msgData, callback) => {
|
||||
signPersonalMessageSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -812,7 +812,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
{ type: 'SHOW_CONF_TX_PAGE', transForward: true, id: undefined },
|
||||
]
|
||||
sendTransactionSpy.callsFake((txData, callback) => {
|
||||
sendTransactionSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -906,7 +906,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
|
||||
setSelectedAddressSpy.callsFake((address, callback) => {
|
||||
setSelectedAddressSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -941,7 +941,7 @@ describe('Actions', () => {
|
||||
{ type: 'HIDE_LOADING_INDICATION' },
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
setSelectedAddressSpy.callsFake((address, callback) => {
|
||||
setSelectedAddressSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -980,7 +980,7 @@ describe('Actions', () => {
|
||||
{ type: 'UPDATE_TOKENS', newTokens: undefined },
|
||||
]
|
||||
|
||||
addTokenSpy.callsFake((address, symbol, decimals, image, callback) => {
|
||||
addTokenSpy.callsFake((_, __, ___, ____, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1020,7 +1020,7 @@ describe('Actions', () => {
|
||||
{ type: 'UPDATE_TOKENS', newTokens: undefined },
|
||||
]
|
||||
|
||||
removeTokenSpy.callsFake((address, callback) => {
|
||||
removeTokenSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1054,7 +1054,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
||||
]
|
||||
|
||||
setProviderTypeSpy.callsFake((type, callback) => {
|
||||
setProviderTypeSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1087,7 +1087,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' },
|
||||
]
|
||||
|
||||
setRpcTargetSpy.callsFake((newRpc, chainId, ticker, nickname, callback) => {
|
||||
setRpcTargetSpy.callsFake((_, __, ___, ____, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1134,7 +1134,7 @@ describe('Actions', () => {
|
||||
exportAccountSpy = sinon.spy(background, 'exportAccount')
|
||||
|
||||
return store.dispatch(actions.exportAccount(password, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'))
|
||||
.then((result) => {
|
||||
.then(() => {
|
||||
assert(submitPasswordSpy.calledOnce)
|
||||
assert(exportAccountSpy.calledOnce)
|
||||
assert.deepEqual(store.getActions(), expectedActions)
|
||||
@ -1150,7 +1150,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
submitPasswordSpy = sinon.stub(background, 'submitPassword')
|
||||
submitPasswordSpy.callsFake((password, callback) => {
|
||||
submitPasswordSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1169,7 +1169,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
exportAccountSpy = sinon.stub(background, 'exportAccount')
|
||||
exportAccountSpy.callsFake((address, callback) => {
|
||||
exportAccountSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1196,7 +1196,6 @@ describe('Actions', () => {
|
||||
|
||||
describe('#pairUpdate', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
nock('https://shapeshift.io')
|
||||
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
|
||||
.get('/marketinfo/btc_eth')
|
||||
@ -1206,10 +1205,6 @@ describe('Actions', () => {
|
||||
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
|
||||
.get('/coins')
|
||||
.reply(200)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
nock.restore()
|
||||
})
|
||||
|
||||
it('', () => {
|
||||
@ -1251,7 +1246,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
|
||||
setFeatureFlagSpy.callsFake((feature, activated, callback) => {
|
||||
setFeatureFlagSpy.callsFake((_, __, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1305,7 +1300,7 @@ describe('Actions', () => {
|
||||
]
|
||||
|
||||
getTransactionCountSpy = sinon.stub(global.ethQuery, 'getTransactionCount')
|
||||
getTransactionCountSpy.callsFake((address, callback) => {
|
||||
getTransactionCountSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1343,7 +1338,7 @@ describe('Actions', () => {
|
||||
{ type: 'SET_USE_BLOCKIE', value: undefined },
|
||||
]
|
||||
|
||||
setUseBlockieSpy.callsFake((val, callback) => {
|
||||
setUseBlockieSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
@ -1390,7 +1385,7 @@ describe('Actions', () => {
|
||||
{ type: 'DISPLAY_WARNING', value: 'error' },
|
||||
]
|
||||
setCurrentLocaleSpy = sinon.stub(background, 'setCurrentLocale')
|
||||
setCurrentLocaleSpy.callsFake((key, callback) => {
|
||||
setCurrentLocaleSpy.callsFake((_, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
|
@ -12,7 +12,7 @@ web3.currentProvider.enable().then(() => {
|
||||
Object.keys(methodGroup).forEach(methodKey => {
|
||||
|
||||
const methodButton = document.getElementById(methodKey)
|
||||
methodButton.addEventListener('click', function (event) {
|
||||
methodButton.addEventListener('click', function () {
|
||||
|
||||
window.ethereum.sendAsync({
|
||||
method: methodKey,
|
||||
|
@ -69,18 +69,9 @@ AccountPanel.prototype.render = function () {
|
||||
)
|
||||
}
|
||||
|
||||
function balanceOrFaucetingIndication (account, isFauceting) {
|
||||
// Temporarily deactivating isFauceting indication
|
||||
// because it shows fauceting for empty restored accounts.
|
||||
if (/* isFauceting*/ false) {
|
||||
return {
|
||||
key: 'Account is auto-funding.',
|
||||
value: 'Please wait.',
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
key: 'BALANCE',
|
||||
value: formatBalance(account.balance),
|
||||
}
|
||||
function balanceOrFaucetingIndication (account) {
|
||||
return {
|
||||
key: 'BALANCE',
|
||||
value: formatBalance(account.balance),
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ BnAsDecimalInput.prototype.render = function () {
|
||||
)
|
||||
}
|
||||
|
||||
BnAsDecimalInput.prototype.setValid = function (message) {
|
||||
BnAsDecimalInput.prototype.setValid = function () {
|
||||
this.setState({ invalid: null })
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../../store/actions')
|
||||
const { getSelectedIdentity } = require('../../../selectors/selectors')
|
||||
const { getSelectedIdentity, getRpcPrefsForCurrentProvider } = require('../../../selectors/selectors')
|
||||
const genAccountLink = require('../../../../lib/account-link.js')
|
||||
const { Menu, Item, CloseArea } = require('./components/menu')
|
||||
|
||||
@ -20,6 +20,7 @@ function mapStateToProps (state) {
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
network: state.metamask.network,
|
||||
keyrings: state.metamask.keyrings,
|
||||
rpcPrefs: getRpcPrefsForCurrentProvider(state),
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,8 +29,8 @@ function mapDispatchToProps (dispatch) {
|
||||
showAccountDetailModal: () => {
|
||||
dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||
},
|
||||
viewOnEtherscan: (address, network) => {
|
||||
global.platform.openWindow({ url: genAccountLink(address, network) })
|
||||
viewOnEtherscan: (address, network, rpcPrefs) => {
|
||||
global.platform.openWindow({ url: genAccountLink(address, network, rpcPrefs) })
|
||||
},
|
||||
showRemoveAccountConfirmationModal: (identity) => {
|
||||
return dispatch(actions.showModal({ name: 'CONFIRM_REMOVE_ACCOUNT', identity }))
|
||||
@ -56,7 +57,9 @@ AccountDetailsDropdown.prototype.render = function () {
|
||||
keyrings,
|
||||
showAccountDetailModal,
|
||||
viewOnEtherscan,
|
||||
showRemoveAccountConfirmationModal } = this.props
|
||||
showRemoveAccountConfirmationModal,
|
||||
rpcPrefs,
|
||||
} = this.props
|
||||
|
||||
const address = selectedIdentity.address
|
||||
|
||||
@ -112,10 +115,12 @@ AccountDetailsDropdown.prototype.render = function () {
|
||||
name: 'Clicked View on Etherscan',
|
||||
},
|
||||
})
|
||||
viewOnEtherscan(address, network)
|
||||
viewOnEtherscan(address, network, rpcPrefs)
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('viewOnEtherscan'),
|
||||
text: (rpcPrefs.blockExplorerUrl
|
||||
? this.context.t('blockExplorerView', [rpcPrefs.blockExplorerUrl.match(/^https?:\/\/(.+)/)[1]])
|
||||
: this.context.t('viewOnEtherscan')),
|
||||
icon: h(`img`, { src: 'images/open-etherscan.svg', style: { height: '15px' } }),
|
||||
}),
|
||||
isRemovable ? h(Item, {
|
||||
|
@ -10,7 +10,7 @@ const Dropdown = require('./components/dropdown').Dropdown
|
||||
const DropdownMenuItem = require('./components/dropdown').DropdownMenuItem
|
||||
const NetworkDropdownIcon = require('./components/network-dropdown-icon')
|
||||
const R = require('ramda')
|
||||
const { ADVANCED_ROUTE } = require('../../../helpers/constants/routes')
|
||||
const { NETWORKS_ROUTE } = require('../../../helpers/constants/routes')
|
||||
|
||||
// classes from nodes of the toggle element.
|
||||
const notToggleElementClassnames = [
|
||||
@ -49,6 +49,7 @@ function mapDispatchToProps (dispatch) {
|
||||
},
|
||||
showNetworkDropdown: () => dispatch(actions.showNetworkDropdown()),
|
||||
hideNetworkDropdown: () => dispatch(actions.hideNetworkDropdown()),
|
||||
setNetworksTabAddMode: isInAddMode => dispatch(actions.setNetworksTabAddMode(isInAddMode)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +73,7 @@ module.exports = compose(
|
||||
// TODO: specify default props and proptypes
|
||||
NetworkDropdown.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { provider: { type: providerType, rpcTarget: activeNetwork } } = props
|
||||
const { provider: { type: providerType, rpcTarget: activeNetwork }, setNetworksTabAddMode } = props
|
||||
const rpcListDetail = props.frequentRpcListDetail
|
||||
const isOpen = this.props.networkDropdownOpen
|
||||
const dropdownMenuItemStyle = {
|
||||
@ -255,7 +256,10 @@ NetworkDropdown.prototype.render = function () {
|
||||
DropdownMenuItem,
|
||||
{
|
||||
closeMenu: () => this.props.hideNetworkDropdown(),
|
||||
onClick: () => this.props.history.push(ADVANCED_ROUTE),
|
||||
onClick: () => {
|
||||
setNetworksTabAddMode(true)
|
||||
this.props.history.push(NETWORKS_ROUTE)
|
||||
},
|
||||
style: dropdownMenuItemStyle,
|
||||
},
|
||||
[
|
||||
|
@ -144,7 +144,7 @@ EnsInput.prototype.ensIcon = function (recipient) {
|
||||
}, this.ensIconContents(recipient))
|
||||
}
|
||||
|
||||
EnsInput.prototype.ensIconContents = function (recipient) {
|
||||
EnsInput.prototype.ensIconContents = function () {
|
||||
const { loadingEns, ensFailure, ensResolution, toError } = this.state || { ensResolution: ZERO_ADDRESS }
|
||||
|
||||
if (toError) return
|
||||
|
@ -58,7 +58,7 @@ export default class AdvancedTabContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
gasInput ({ labelKey, value, onChange, insufficientBalance, showGWEI, customPriceIsSafe, isSpeedUp }) {
|
||||
gasInput ({ labelKey, value, onChange, insufficientBalance, customPriceIsSafe, isSpeedUp }) {
|
||||
const {
|
||||
isInError,
|
||||
errorText,
|
||||
|
@ -17,8 +17,8 @@ function convertGasLimitForInputs (gasLimitInHexWEI) {
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
showGasPriceInfoModal: modalName => dispatch(showModal({ name: 'GAS_PRICE_INFO_MODAL' })),
|
||||
showGasLimitInfoModal: modalName => dispatch(showModal({ name: 'GAS_LIMIT_INFO_MODAL' })),
|
||||
showGasPriceInfoModal: () => dispatch(showModal({ name: 'GAS_PRICE_INFO_MODAL' })),
|
||||
showGasLimitInfoModal: () => dispatch(showModal({ name: 'GAS_LIMIT_INFO_MODAL' })),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ export default class AdvancedTabContent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
gasInput ({ labelKey, value, onChange, insufficientBalance, showGWEI, customPriceIsSafe, isSpeedUp }) {
|
||||
gasInput ({ labelKey, value, onChange, insufficientBalance, customPriceIsSafe, isSpeedUp }) {
|
||||
const {
|
||||
isInError,
|
||||
errorText,
|
||||
@ -148,7 +148,6 @@ export default class AdvancedTabContent extends Component {
|
||||
customGasPrice,
|
||||
updateCustomGasPrice,
|
||||
customGasLimit,
|
||||
updateCustomGasLimit,
|
||||
insufficientBalance,
|
||||
customPriceIsSafe,
|
||||
isSpeedUp,
|
||||
|
@ -122,8 +122,6 @@ export default class GasModalPageContainer extends Component {
|
||||
}
|
||||
|
||||
renderTabs ({
|
||||
originalTotalFiat,
|
||||
originalTotalEth,
|
||||
newTotalFiat,
|
||||
newTotalEth,
|
||||
sendAmount,
|
||||
|
@ -49,7 +49,7 @@ export default class GasPriceButtonGroup extends Component {
|
||||
priceInHexWei,
|
||||
...renderableGasInfo
|
||||
}, {
|
||||
buttonDataLoading,
|
||||
buttonDataLoading: _,
|
||||
handleGasPriceSelection,
|
||||
...buttonContentPropsAndFlags
|
||||
}, index) {
|
||||
|
@ -68,7 +68,7 @@ export function handleChartUpdate ({ chart, gasPrices, newPrice, cssId }) {
|
||||
|
||||
export function getAdjacentGasPrices ({ gasPrices, priceToPosition }) {
|
||||
const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => e <= priceToPosition && a[i + 1] >= priceToPosition)
|
||||
const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => e > priceToPosition)
|
||||
const closestHigherValueIndex = gasPrices.findIndex((e) => e > priceToPosition)
|
||||
return {
|
||||
closestLowerValueIndex,
|
||||
closestHigherValueIndex,
|
||||
@ -133,7 +133,7 @@ export function setTickPosition (axis, n, newPosition, secondNewPosition) {
|
||||
d3.select('#chart')
|
||||
.select(`.c3-axis-${axis}`)
|
||||
.selectAll('.tick')
|
||||
.filter((d, i) => i === n)
|
||||
.filter((_, i) => i === n)
|
||||
.select('text')
|
||||
.attr(positionToShift, 0)
|
||||
.select('tspan')
|
||||
@ -284,7 +284,7 @@ export function generateChart (gasPrices, estimatedTimes, gasPricesMax, estimate
|
||||
})
|
||||
return text + '</table>' + "<div class='tooltip-arrow'></div>"
|
||||
},
|
||||
position: function (data) {
|
||||
position: function () {
|
||||
if (d3.select('#overlayed-circle').empty()) {
|
||||
return { top: -100, left: -100 }
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import shallow from '../../../../../../lib/shallow-with-context'
|
||||
import * as d3 from 'd3'
|
||||
|
||||
function timeout (time) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time)
|
||||
})
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../../store/actions')
|
||||
const AccountModalContainer = require('./account-modal-container')
|
||||
const { getSelectedIdentity } = require('../../../selectors/selectors')
|
||||
const { getSelectedIdentity, getRpcPrefsForCurrentProvider } = require('../../../selectors/selectors')
|
||||
const genAccountLink = require('../../../../lib/account-link.js')
|
||||
const QrView = require('../../ui/qr-code')
|
||||
const EditableLabel = require('../../ui/editable-label')
|
||||
@ -17,6 +17,7 @@ function mapStateToProps (state) {
|
||||
network: state.metamask.network,
|
||||
selectedIdentity: getSelectedIdentity(state),
|
||||
keyrings: state.metamask.keyrings,
|
||||
rpcPrefs: getRpcPrefsForCurrentProvider(state),
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +55,7 @@ AccountDetailsModal.prototype.render = function () {
|
||||
showExportPrivateKeyModal,
|
||||
setAccountLabel,
|
||||
keyrings,
|
||||
rpcPrefs,
|
||||
} = this.props
|
||||
const { name, address } = selectedIdentity
|
||||
|
||||
@ -86,8 +88,12 @@ AccountDetailsModal.prototype.render = function () {
|
||||
h(Button, {
|
||||
type: 'secondary',
|
||||
className: 'account-modal__button',
|
||||
onClick: () => global.platform.openWindow({ url: genAccountLink(address, network) }),
|
||||
}, this.context.t('etherscanView')),
|
||||
onClick: () => {
|
||||
global.platform.openWindow({ url: genAccountLink(address, network, rpcPrefs) })
|
||||
},
|
||||
}, (rpcPrefs.blockExplorerUrl
|
||||
? this.context.t('blockExplorerView', [rpcPrefs.blockExplorerUrl.match(/^https?:\/\/(.+)/)[1]])
|
||||
: this.context.t('viewOnEtherscan'))),
|
||||
|
||||
// Holding on redesign for Export Private Key functionality
|
||||
|
||||
|
@ -48,7 +48,7 @@ function mapDispatchToProps (dispatch) {
|
||||
}
|
||||
|
||||
inherits(DepositEtherModal, Component)
|
||||
function DepositEtherModal (props, context) {
|
||||
function DepositEtherModal (_, context) {
|
||||
Component.call(this)
|
||||
|
||||
// need to set after i18n locale has loaded
|
||||
|
@ -98,7 +98,7 @@ ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) {
|
||||
})
|
||||
}
|
||||
|
||||
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, password, address, hideModal) {
|
||||
ExportPrivateKeyModal.prototype.renderButtons = function (privateKey, address, hideModal) {
|
||||
return h('div.export-private-key-buttons', {}, [
|
||||
!privateKey && h(Button, {
|
||||
type: 'default',
|
||||
@ -171,7 +171,7 @@ ExportPrivateKeyModal.prototype.render = function () {
|
||||
|
||||
h('div.private-key-password-warning', this.context.t('privateKeyWarning')),
|
||||
|
||||
this.renderButtons(privateKey, this.state.password, address, hideModal),
|
||||
this.renderButtons(privateKey, address, hideModal),
|
||||
|
||||
])
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import MetaMetricsOptInModal from './metametrics-opt-in-modal.component'
|
||||
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props'
|
||||
import { setParticipateInMetaMetrics } from '../../../../store/actions'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const mapStateToProps = (_, ownProps) => {
|
||||
const { unapprovedTxCount } = ownProps
|
||||
|
||||
return {
|
||||
|
@ -71,7 +71,7 @@ export default class QrScanner extends Component {
|
||||
initCamera () {
|
||||
this.codeReader = new BrowserQRCodeReader()
|
||||
this.codeReader.getVideoInputDevices()
|
||||
.then(videoInputDevices => {
|
||||
.then(() => {
|
||||
clearTimeout(this.permissionChecker)
|
||||
this.checkPermisisions()
|
||||
this.codeReader.decodeFromInputVideoDevice(undefined, 'video')
|
||||
|
@ -3,7 +3,7 @@ import { compose } from 'recompose'
|
||||
import RejectTransactionsModal from './reject-transactions.component'
|
||||
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const mapStateToProps = (_, ownProps) => {
|
||||
const { unapprovedTxCount } = ownProps
|
||||
|
||||
return {
|
||||
|
@ -5,12 +5,11 @@ import { PageContainerFooter } from '../../ui/page-container'
|
||||
|
||||
export default class ProviderPageContainer extends PureComponent {
|
||||
static propTypes = {
|
||||
approveProviderRequest: PropTypes.func.isRequired,
|
||||
approveProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
rejectProviderRequestByOrigin: PropTypes.func.isRequired,
|
||||
origin: PropTypes.string.isRequired,
|
||||
rejectProviderRequest: PropTypes.func.isRequired,
|
||||
siteImage: PropTypes.string,
|
||||
siteTitle: PropTypes.string.isRequired,
|
||||
tabID: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@ -29,7 +28,7 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
}
|
||||
|
||||
onCancel = () => {
|
||||
const { tabID, rejectProviderRequest } = this.props
|
||||
const { origin, rejectProviderRequestByOrigin } = this.props
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Auth',
|
||||
@ -37,11 +36,11 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
name: 'Canceled',
|
||||
},
|
||||
})
|
||||
rejectProviderRequest(tabID)
|
||||
rejectProviderRequestByOrigin(origin)
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
const { approveProviderRequest, tabID } = this.props
|
||||
const { approveProviderRequestByOrigin, origin } = this.props
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Auth',
|
||||
@ -49,7 +48,7 @@ export default class ProviderPageContainer extends PureComponent {
|
||||
name: 'Confirmed',
|
||||
},
|
||||
})
|
||||
approveProviderRequest(tabID)
|
||||
approveProviderRequestByOrigin(origin)
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -155,7 +155,7 @@ TokenCell.prototype.send = function (address, event) {
|
||||
}
|
||||
}
|
||||
|
||||
TokenCell.prototype.view = function (address, userAddress, network, event) {
|
||||
TokenCell.prototype.view = function (address, userAddress, network) {
|
||||
const url = etherscanLinkFor(address, userAddress, network)
|
||||
if (url) {
|
||||
navigateTo(url)
|
||||
|
@ -1,13 +1,15 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import copyToClipboard from 'copy-to-clipboard'
|
||||
import {
|
||||
getBlockExplorerUrlForTx,
|
||||
} from '../../../helpers/utils/transactions.util'
|
||||
import SenderToRecipient from '../../ui/sender-to-recipient'
|
||||
import { FLAT_VARIANT } from '../../ui/sender-to-recipient/sender-to-recipient.constants'
|
||||
import TransactionActivityLog from '../transaction-activity-log'
|
||||
import TransactionBreakdown from '../transaction-breakdown'
|
||||
import Button from '../../ui/button'
|
||||
import Tooltip from '../../ui/tooltip'
|
||||
import prefixForNetwork from '../../../../lib/etherscan-prefix-for-network'
|
||||
|
||||
export default class TransactionListItemDetails extends PureComponent {
|
||||
static contextTypes = {
|
||||
@ -22,6 +24,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
showRetry: PropTypes.bool,
|
||||
cancelDisabled: PropTypes.bool,
|
||||
transactionGroup: PropTypes.object,
|
||||
rpcPrefs: PropTypes.object,
|
||||
}
|
||||
|
||||
state = {
|
||||
@ -30,12 +33,9 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
}
|
||||
|
||||
handleEtherscanClick = () => {
|
||||
const { transactionGroup: { primaryTransaction } } = this.props
|
||||
const { transactionGroup: { primaryTransaction }, rpcPrefs } = this.props
|
||||
const { hash, metamaskNetworkId } = primaryTransaction
|
||||
|
||||
const prefix = prefixForNetwork(metamaskNetworkId)
|
||||
const etherscanUrl = `https://${prefix}etherscan.io/tx/${hash}`
|
||||
|
||||
this.context.metricsEvent({
|
||||
eventOpts: {
|
||||
category: 'Navigation',
|
||||
@ -44,7 +44,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
},
|
||||
})
|
||||
|
||||
global.platform.openWindow({ url: etherscanUrl })
|
||||
global.platform.openWindow({ url: getBlockExplorerUrlForTx(metamaskNetworkId, hash, rpcPrefs) })
|
||||
}
|
||||
|
||||
handleCancel = event => {
|
||||
@ -125,6 +125,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
showRetry,
|
||||
onCancel,
|
||||
onRetry,
|
||||
rpcPrefs: { blockExplorerUrl } = {},
|
||||
} = this.props
|
||||
const { primaryTransaction: transaction } = transactionGroup
|
||||
const { txParams: { to, from } = {} } = transaction
|
||||
@ -158,7 +159,7 @@ export default class TransactionListItemDetails extends PureComponent {
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('viewOnEtherscan')}>
|
||||
<Tooltip title={blockExplorerUrl ? t('viewOnCustomBlockExplorer', [blockExplorerUrl]) : t('viewOnEtherscan')}>
|
||||
<Button
|
||||
type="raised"
|
||||
onClick={this.handleEtherscanClick}
|
||||
|
@ -33,6 +33,7 @@ export default class TransactionListItem extends PureComponent {
|
||||
value: PropTypes.string,
|
||||
fetchBasicGasAndTimeEstimates: PropTypes.func,
|
||||
fetchGasEstimates: PropTypes.func,
|
||||
rpcPrefs: PropTypes.object,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
@ -161,6 +162,7 @@ export default class TransactionListItem extends PureComponent {
|
||||
showRetry,
|
||||
tokenData,
|
||||
transactionGroup,
|
||||
rpcPrefs,
|
||||
} = this.props
|
||||
const { txParams = {} } = transaction
|
||||
const { showTransactionDetails } = this.state
|
||||
@ -216,6 +218,7 @@ export default class TransactionListItem extends PureComponent {
|
||||
onCancel={this.handleCancel}
|
||||
showCancel={showCancel}
|
||||
cancelDisabled={!hasEnoughCancelGas}
|
||||
rpcPrefs={rpcPrefs}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -18,12 +18,14 @@ import { getIsMainnet, preferencesSelector, getSelectedAddress, conversionRateSe
|
||||
import { isBalanceSufficient } from '../../../pages/send/send.utils'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { metamask: { knownMethodData, accounts } } = state
|
||||
const { metamask: { knownMethodData, accounts, provider, frequentRpcListDetail } } = state
|
||||
const { showFiatInTestnets } = preferencesSelector(state)
|
||||
const isMainnet = getIsMainnet(state)
|
||||
const { transactionGroup: { primaryTransaction } = {} } = ownProps
|
||||
const { txParams: { gas: gasLimit, gasPrice } = {} } = primaryTransaction
|
||||
const selectedAccountBalance = accounts[getSelectedAddress(state)].balance
|
||||
const selectRpcInfo = frequentRpcListDetail.find(rpcInfo => rpcInfo.rpcUrl === provider.rpcTarget)
|
||||
const { rpcPrefs } = selectRpcInfo || {}
|
||||
|
||||
const hasEnoughCancelGas = primaryTransaction.txParams && isBalanceSufficient({
|
||||
amount: '0x0',
|
||||
@ -40,6 +42,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
showFiat: (isMainnet || !!showFiatInTestnets),
|
||||
selectedAccountBalance,
|
||||
hasEnoughCancelGas,
|
||||
rpcPrefs,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../user-preferenced-currency-display.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
connect: (ms, _, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
|
@ -3,7 +3,7 @@ import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display.
|
||||
import { preferencesSelector, getIsMainnet } from '../../../selectors/selectors'
|
||||
import { ETH, PRIMARY, SECONDARY } from '../../../helpers/constants/common'
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
useNativeCurrencyAsPrimaryCurrency,
|
||||
showFiatInTestnets,
|
||||
|
@ -18,7 +18,7 @@ class Alert extends Component {
|
||||
if (!this.props.visible && nextProps.visible) {
|
||||
this.animateIn(nextProps)
|
||||
} else if (this.props.visible && !nextProps.visible) {
|
||||
this.animateOut(nextProps)
|
||||
this.animateOut()
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ class Alert extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
animateOut (props) {
|
||||
animateOut () {
|
||||
this.setState({
|
||||
msg: null,
|
||||
className: '.hidden',
|
||||
|
@ -23,7 +23,7 @@ export default class CurrencyDisplay extends PureComponent {
|
||||
render () {
|
||||
const { className, displayValue, prefix, prefixComponent, style, suffix, hideTitle } = this.props
|
||||
const text = `${prefix || ''}${displayValue}`
|
||||
const title = `${text} ${suffix}`
|
||||
const title = suffix ? `${text} ${suffix}` : text
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -5,7 +5,7 @@ let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-display.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
connect: (ms, _, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
|
@ -5,7 +5,7 @@ let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../currency-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
connect: (ms, _, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
|
@ -41,11 +41,11 @@ const styles = {
|
||||
inputFocused: {},
|
||||
inputRoot: {
|
||||
'label + &': {
|
||||
marginTop: '8px',
|
||||
marginTop: '9px',
|
||||
},
|
||||
border: '1px solid #d2d8dd',
|
||||
border: '2px solid #BBC0C5',
|
||||
height: '48px',
|
||||
borderRadius: '4px',
|
||||
borderRadius: '6px',
|
||||
padding: '0 16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
@ -5,7 +5,7 @@ let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../token-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, md, mp) => {
|
||||
connect: (ms, _, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
|
@ -58,7 +58,7 @@ export default class UnitInput extends PureComponent {
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
handleBlur = event => {
|
||||
handleBlur = () => {
|
||||
const { onBlur } = this.props
|
||||
typeof onBlur === 'function' && onBlur(this.state.value)
|
||||
}
|
||||
|
@ -77,6 +77,8 @@ function reduceApp (state, action) {
|
||||
ledger: `m/44'/60'/0'/0/0`,
|
||||
},
|
||||
lastSelectedProvider: null,
|
||||
networksTabSelectedRpcUrl: '',
|
||||
networksTabIsInAddMode: false,
|
||||
}, state.appState)
|
||||
|
||||
switch (action.type) {
|
||||
@ -751,6 +753,16 @@ function reduceApp (state, action) {
|
||||
lastSelectedProvider: action.value,
|
||||
})
|
||||
|
||||
case actions.SET_SELECTED_SETTINGS_RPC_URL:
|
||||
return extend(appState, {
|
||||
networksTabSelectedRpcUrl: action.value,
|
||||
})
|
||||
|
||||
case actions.SET_NETWORKS_TAB_ADD_MODE:
|
||||
return extend(appState, {
|
||||
networksTabIsInAddMode: action.value,
|
||||
})
|
||||
|
||||
default:
|
||||
return appState
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ export function setTransactionToConfirm (transactionId) {
|
||||
dispatch(updateMethodData(methodData))
|
||||
|
||||
try {
|
||||
const toSmartContract = await isSmartContractAddress(to)
|
||||
const toSmartContract = await isSmartContractAddress(to || '')
|
||||
dispatch(updateToSmartContract(toSmartContract))
|
||||
} catch (error) {
|
||||
log.error(error)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user