1
0
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:
Dan Finlay 2019-05-14 13:22:54 -07:00 committed by GitHub
commit bf5b7f8e77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
154 changed files with 3408 additions and 1342 deletions

View File

@ -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,

View File

@ -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

View File

@ -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 youre 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"
},

View File

@ -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__",

View File

@ -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 }))
}

View 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

View File

@ -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':

View File

@ -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

View File

@ -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
}

View File

@ -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 }),

View File

@ -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 }),

View File

@ -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 }),

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)
})

View File

@ -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
//

View 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

View 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
}

View File

@ -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: {},

View File

@ -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: {},

View File

@ -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()
}
}

View File

@ -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 &&

View File

@ -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),

View File

@ -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()

View File

@ -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,

View File

@ -192,7 +192,8 @@
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": []
"lostAccounts": [],
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -133,7 +133,9 @@
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true,
"showFiatInTestnets": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -156,7 +156,9 @@
"currentLocale": "en",
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -115,7 +115,9 @@
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true,
"showFiatInTestnets": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -137,7 +137,9 @@
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true,
"showFiatInTestnets": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -116,7 +116,9 @@
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true,
"showFiatInTestnets": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -87,7 +87,8 @@
"type": "testnet"
},
"shapeShiftTxList": [],
"lostAccounts": []
"lostAccounts": [],
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -1058,7 +1058,9 @@
"currentLocale": "en",
"preferences": {
"useNativeCurrencyAsPrimaryCurrency": true
}
},
"completedUiMigration": true,
"frequentRpcListDetail": []
},
"appState": {
"menuOpen": false,

View File

@ -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 '

View File

@ -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
View File

@ -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": {

View File

@ -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",

View File

@ -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)
})
})
}
})

View File

@ -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>

View File

@ -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)
})

View File

@ -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')

View File

@ -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)

View File

@ -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')

View File

@ -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')

View File

@ -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)
},

View File

@ -6,7 +6,7 @@ module.exports = {
}
function timeout (time) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
setTimeout(resolve, time || 1500)
})
}

View File

@ -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() },
})

View File

@ -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)
})
})
})

View File

@ -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()

View File

@ -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()

View File

@ -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')

View File

@ -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)

View File

@ -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([

View File

@ -11,7 +11,7 @@ describe('txUtils', function () {
before(function () {
txUtils = new TxUtils(new Proxy({}, {
get: (obj, name) => {
get: () => {
return () => {}
},
}))

View File

@ -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) {

View File

@ -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)
}

View File

@ -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()
})

View File

@ -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)
})

View File

@ -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'))
})

View File

@ -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,

View File

@ -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),
}
}

View File

@ -116,7 +116,7 @@ BnAsDecimalInput.prototype.render = function () {
)
}
BnAsDecimalInput.prototype.setValid = function (message) {
BnAsDecimalInput.prototype.setValid = function () {
this.setState({ invalid: null })
}

View File

@ -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, {

View File

@ -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,
},
[

View File

@ -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

View File

@ -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,

View File

@ -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' })),
}
}

View File

@ -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,

View File

@ -122,8 +122,6 @@ export default class GasModalPageContainer extends Component {
}
renderTabs ({
originalTotalFiat,
originalTotalEth,
newTotalFiat,
newTotalEth,
sendAmount,

View File

@ -49,7 +49,7 @@ export default class GasPriceButtonGroup extends Component {
priceInHexWei,
...renderableGasInfo
}, {
buttonDataLoading,
buttonDataLoading: _,
handleGasPriceSelection,
...buttonContentPropsAndFlags
}, index) {

View File

@ -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 }
}

View File

@ -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)
})
}

View File

@ -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

View File

@ -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

View File

@ -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),
])
}

View File

@ -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 {

View File

@ -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')

View File

@ -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 {

View File

@ -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 () {

View File

@ -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)

View File

@ -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}

View File

@ -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>
)

View File

@ -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,
}
}

View File

@ -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 () => ({})

View File

@ -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,

View File

@ -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',

View File

@ -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

View File

@ -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 () => ({})

View File

@ -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 () => ({})

View File

@ -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',

View File

@ -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 () => ({})

View File

@ -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)
}

View File

@ -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
}

View File

@ -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