diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f85dbb2..905f1f98f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## Current Master +## 3.13.7 2018-1-22 + +- Add ability to bypass gas estimation loading indicator. +- Forward failed transactions to Sentry error reporting service +- Re-add changes from 3.13.5 + +## 3.13.6 2017-1-18 + +- Roll back changes to 3.13.4 to fix some issues with the new Infura REST provider. + ## 3.13.5 2018-1-16 - Estimating gas limit for simple ether sends now faster & cheaper, by avoiding VM usage on recipients with no code. diff --git a/app/manifest.json b/app/manifest.json index d65374330..d795a225a 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "MetaMask", "short_name": "Metamask", - "version": "4.0.9", + "version": "3.13.7", "manifest_version": 2, "author": "https://metamask.io", "description": "Ethereum Browser Extension", diff --git a/app/scripts/background.js b/app/scripts/background.js index da022c490..0471cee3b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -13,6 +13,7 @@ const PortStream = require('./lib/port-stream.js') const NotificationManager = require('./lib/notification-manager.js') const MetamaskController = require('./metamask-controller') const firstTimeState = require('./first-time-state') +const setupRaven = require('./setupRaven') const STORAGE_KEY = 'metamask-config' const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' @@ -24,6 +25,10 @@ const platform = new ExtensionPlatform() const notificationManager = new NotificationManager() global.METAMASK_NOTIFIER = notificationManager +// setup sentry error reporting +const release = platform.getVersion() +const raven = setupRaven({ release }) + let popupIsOpen = false // state persistence @@ -72,6 +77,16 @@ function setupController (initState) { }) global.metamaskController = controller + // report failed transactions to Sentry + controller.txController.on(`tx:status-update`, (txId, status) => { + if (status !== 'failed') return + const txMeta = controller.txController.txStateManager.getTx(txId) + raven.captureMessage('Transaction Failed', { + // "extra" key is required by Sentry + extra: txMeta, + }) + }) + // setup state persistence pump( asStream(controller.store), diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js index 290444bbb..91ec42b69 100644 --- a/app/scripts/controllers/transactions.js +++ b/app/scripts/controllers/transactions.js @@ -43,6 +43,28 @@ module.exports = class TransactionController extends EventEmitter { txHistoryLimit: opts.txHistoryLimit, getNetwork: this.getNetwork.bind(this), }) + + this.txStateManager.getFilteredTxList({ + status: 'unapproved', + loadingDefaults: true, + }).forEach((tx) => { + this.addTxDefaults(tx) + .then((txMeta) => { + txMeta.loadingDefaults = false + this.txStateManager.updateTx(txMeta, 'transactions: gas estimation for tx on boot') + }).catch((error) => { + this.txStateManager.setTxStatusFailed(tx.id, error) + }) + }) + + this.txStateManager.getFilteredTxList({ + status: 'approved', + }).forEach((txMeta) => { + const txSignError = new Error('Transaction found as "approved" during boot - possibly stuck during signing') + this.txStateManager.setTxStatusFailed(txMeta.id, txSignError) + }) + + this.store = this.txStateManager.store this.txStateManager.on('tx:status-update', this.emit.bind(this, 'tx:status-update')) this.nonceTracker = new NonceTracker({ @@ -171,11 +193,17 @@ module.exports = class TransactionController extends EventEmitter { this.addTx(txMeta) this.emit('newUnapprovedTx', txMeta) // add default tx params - await this.addTxDefaults(txMeta) - + try { + await this.addTxDefaults(txMeta) + } catch (error) { + console.log(error) + this.txStateManager.setTxStatusFailed(txMeta.id, error) + throw error + } txMeta.loadingDefaults = false // save txMeta this.txStateManager.updateTx(txMeta) + return txMeta } diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js index e80e0467e..f68f3a9e2 100644 --- a/app/scripts/lib/tx-gas-utils.js +++ b/app/scripts/lib/tx-gas-utils.js @@ -12,7 +12,8 @@ its passed ethquery and used to do things like calculate gas of a tx. */ -module.exports = class txProvideUtil { +module.exports = class TxGasUtil { + constructor (provider) { this.query = new EthQuery(provider) } diff --git a/app/scripts/popup.js b/app/scripts/popup.js index 1a2e425dc..53ab00e00 100644 --- a/app/scripts/popup.js +++ b/app/scripts/popup.js @@ -8,10 +8,19 @@ const extension = require('extensionizer') const ExtensionPlatform = require('./platforms/extension') const NotificationManager = require('./lib/notification-manager') const notificationManager = new NotificationManager() +const setupRaven = require('./setupRaven') // create platform global global.platform = new ExtensionPlatform() +// setup sentry error reporting +const release = global.platform.getVersion() +setupRaven({ release }) + +// inject css +// const css = MetaMaskUiCss() +// injectCss(css) + // identify window type (popup, notification) const windowType = isPopupOrNotification() global.METAMASK_UI_TYPE = windowType diff --git a/app/scripts/setupRaven.js b/app/scripts/setupRaven.js new file mode 100644 index 000000000..7beffeff9 --- /dev/null +++ b/app/scripts/setupRaven.js @@ -0,0 +1,26 @@ +const Raven = require('./vendor/raven.min.js') +const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG' +const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' +const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' + +module.exports = setupRaven + +// Setup raven / sentry remote error reporting +function setupRaven(opts) { + const { release } = opts + let ravenTarget + + if (METAMASK_DEBUG) { + console.log('Setting up Sentry Remote Error Reporting: DEV') + ravenTarget = DEV + } else { + console.log('Setting up Sentry Remote Error Reporting: PROD') + ravenTarget = PROD + } + + Raven.config(ravenTarget, { + release, + }).install() + + return Raven +} diff --git a/app/scripts/vendor/raven.min.js b/app/scripts/vendor/raven.min.js new file mode 100644 index 000000000..b439aeae6 --- /dev/null +++ b/app/scripts/vendor/raven.min.js @@ -0,0 +1,3 @@ +/*! Raven.js 3.22.1 (7584197) | github.com/getsentry/raven-js */ +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Raven=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gthis.k.maxBreadcrumbs&&this.u.shift(),this},addPlugin:function(a){var b=[].slice.call(arguments,1);return this.r.push([a,b]),this.n&&this.E(),this},setUserContext:function(a){return this.j.user=a,this},setExtraContext:function(a){return this.T("extra",a),this},setTagsContext:function(a){return this.T("tags",a),this},clearContext:function(){return this.j={},this},getContext:function(){return JSON.parse(h(this.j))},setEnvironment:function(a){return this.k.environment=a,this},setRelease:function(a){return this.k.release=a,this},setDataCallback:function(a){var b=this.k.dataCallback;return this.k.dataCallback=e(b,a),this},setBreadcrumbCallback:function(a){var b=this.k.breadcrumbCallback;return this.k.breadcrumbCallback=e(b,a),this},setShouldSendCallback:function(a){var b=this.k.shouldSendCallback;return this.k.shouldSendCallback=e(b,a),this},setTransport:function(a){return this.k.transport=a,this},lastException:function(){return this.d},lastEventId:function(){return this.f},isSetup:function(){return!!this.a&&(!!this.g||(this.ravenNotConfiguredError||(this.ravenNotConfiguredError=!0,this.z("error","Error: Raven has not been configured.")),!1))},afterLoad:function(){var a=J.RavenConfig;a&&this.config(a.dsn,a.config).install()},showReportDialog:function(a){if(K){a=a||{};var b=a.eventId||this.lastEventId();if(!b)throw new i("Missing eventId");var c=a.dsn||this.G;if(!c)throw new i("Missing DSN");var d=encodeURIComponent,e="";e+="?eventId="+d(b),e+="&dsn="+d(c);var f=a.user||this.j.user;f&&(f.name&&(e+="&name="+d(f.name)),f.email&&(e+="&email="+d(f.email)));var g=this.I(this.F(c)),h=K.createElement("script");h.async=!0,h.src=g+"/api/embed/error-page/"+e,(K.head||K.body).appendChild(h)}},K:function(){var a=this;this.m+=1,setTimeout(function(){a.m-=1})},U:function(a,b){var c,d;if(this.b){b=b||{},a="raven"+a.substr(0,1).toUpperCase()+a.substr(1),K.createEvent?(c=K.createEvent("HTMLEvents"),c.initEvent(a,!0,!0)):(c=K.createEventObject(),c.eventType=a);for(d in b)w(b,d)&&(c[d]=b[d]);if(K.createEvent)K.dispatchEvent(c);else try{K.fireEvent("on"+c.eventType.toLowerCase(),c)}catch(e){}}},V:function(a){var b=this;return function(c){if(b.W=null,b.v!==c){b.v=c;var d;try{d=A(c.target)}catch(e){d=""}b.captureBreadcrumb({category:"ui."+a,message:d})}}},X:function(){var a=this,b=1e3;return function(c){var d;try{d=c.target}catch(e){return}var f=d&&d.tagName;if(f&&("INPUT"===f||"TEXTAREA"===f||d.isContentEditable)){var g=a.W;g||a.V("input")(c),clearTimeout(g),a.W=setTimeout(function(){a.W=null},b)}}},Y:function(a,b){var c=D(this.w.href),d=D(b),e=D(a);this.x=b,c.protocol===d.protocol&&c.host===d.host&&(b=d.relative),c.protocol===e.protocol&&c.host===e.host&&(a=e.relative),this.captureBreadcrumb({category:"navigation",data:{to:b,from:a}})},B:function(){var a=this;a.Z=Function.prototype.toString,Function.prototype.toString=function(){return"function"==typeof this&&this.L?a.Z.apply(this.N,arguments):a.Z.apply(this,arguments)}},O:function(){this.Z&&(Function.prototype.toString=this.Z)},C:function(){function a(a){return function(b,d){for(var e=new Array(arguments.length),f=0;f2?arguments[2]:void 0;return c&&b.Y(b.x,c+""),a.apply(this,arguments)}};E(history,"pushState",j,d),E(history,"replaceState",j,d)}if(c.console&&"console"in J&&console.log){var k=function(a,c){b.captureBreadcrumb({message:a,level:c.level,category:"console"})};s(["debug","info","warn","error","log"],function(a,b){G(console,b,k)})}},P:function(){for(var a;this.t.length;){a=this.t.shift();var b=a[0],c=a[1],d=a[2];b[c]=d}},E:function(){var a=this;s(this.r,function(b,c){var d=c[0],e=c[1];d.apply(a,[a].concat(e))})},F:function(a){var b=I.exec(a),c={},d=7;try{for(;d--;)c[H[d]]=b[d]||""}catch(e){throw new i("Invalid DSN: "+a)}if(c.pass&&!this.k.allowSecretKey)throw new i("Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key");return c},I:function(a){var b="//"+a.host+(a.port?":"+a.port:"");return a.protocol&&(b=a.protocol+":"+b),b},A:function(){this.m||this.Q.apply(this,arguments)},Q:function(a,b){var c=this.R(a,b);this.U("handle",{stackInfo:a,options:b}),this._(a.name,a.message,a.url,a.lineno,c,b)},R:function(a,b){var c=this,d=[];if(a.stack&&a.stack.length&&(s(a.stack,function(b,e){var f=c.aa(e,a.url);f&&d.push(f)}),b&&b.trimHeadFrames))for(var e=0;e0&&(a.breadcrumbs={values:[].slice.call(this.u,0)}),this.j.user&&(a.user=this.j.user),b.environment&&(a.environment=b.environment),b.release&&(a.release=b.release),b.serverName&&(a.server_name=b.serverName),Object.keys(a).forEach(function(b){(null==a[b]||""===a[b]||r(a[b]))&&delete a[b]}),o(b.dataCallback)&&(a=b.dataCallback(a)||a),a&&!r(a)&&(!o(b.shouldSendCallback)||b.shouldSendCallback(a)))return this.ga()?void this.z("warn","Raven dropped error due to backoff: ",a):void("number"==typeof b.sampleRate?Math.random() ",i=h.length;a&&f++1&&g+e.length*i+b.length>=d));)e.push(b),g+=b.length,a=a.parentNode;return e.reverse().join(h)}function y(a){var b,c,d,e,f,g=[];if(!a||!a.tagName)return"";if(g.push(a.tagName.toLowerCase()),a.id&&g.push("#"+a.id),b=a.className,b&&i(b))for(c=b.split(/\s+/),f=0;f=0;--b)s[b]===a&&s.splice(b,1)}function c(){n(),s=[]}function k(a,b){var c=null;if(!b||f.collectWindowErrors){for(var d in s)if(s.hasOwnProperty(d))try{s[d].apply(null,[a].concat(h.call(arguments,2)))}catch(e){c=e}if(c)throw c}}function l(a,b,c,g,h){var l=null,m=e.isErrorEvent(h)?h.error:h,n=e.isErrorEvent(a)?a.message:a;if(v)f.computeStackTrace.augmentStackTraceWithInitialElement(v,b,c,n),o();else if(m&&e.isError(m))l=f.computeStackTrace(m),k(l,!0);else{var p,r={url:b,line:c,column:g},s=void 0;if("[object String]"==={}.toString.call(n)){var p=n.match(j);p&&(s=p[1],n=p[2])}r.func=i,l={name:s,message:n,url:d(),stack:[r]},k(l,!0)}return!!q&&q.apply(this,arguments)}function m(){r||(q=g.onerror,g.onerror=l,r=!0)}function n(){r&&(g.onerror=q,r=!1,q=void 0)}function o(){var a=v,b=t;t=null,v=null,u=null,k.apply(null,[a,!1].concat(b))}function p(a,b){var c=h.call(arguments,1);if(v){if(u===a)return;o()}var d=f.computeStackTrace(a);if(v=d,u=a,t=c,setTimeout(function(){u===a&&o()},d.incomplete?2e3:0),b!==!1)throw a}var q,r,s=[],t=null,u=null,v=null;return p.subscribe=a,p.unsubscribe=b,p.uninstall=c,p}(),f.computeStackTrace=function(){function a(a){if("undefined"!=typeof a.stack&&a.stack){for(var b,c,e,f=/^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,g=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i,h=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,j=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,k=/\((\S*)(?::(\d+))(?::(\d+))\)/,l=a.stack.split("\n"),m=[],n=(/^(.*) is undefined$/.exec(a.message),0),o=l.length;n eval")>-1;q&&(b=j.exec(c[3]))?(c[3]=b[1],c[4]=b[2],c[5]=null):0!==n||c[5]||"undefined"==typeof a.columnNumber||(m[0].column=a.columnNumber+1),e={url:c[3],func:c[1]||i,args:c[2]?c[2].split(","):[],line:c[4]?+c[4]:null,column:c[5]?+c[5]:null}}!e.func&&e.line&&(e.func=i),m.push(e)}return m.length?{name:a.name,message:a.message,url:d(),stack:m}:null}}function b(a,b,c,d){var e={url:b,line:c};if(e.url&&e.line){if(a.incomplete=!1,e.func||(e.func=i),a.stack.length>0&&a.stack[0].url===e.url){if(a.stack[0].line===e.line)return!1;if(!a.stack[0].line&&a.stack[0].func===e.func)return a.stack[0].line=e.line,!1}return a.stack.unshift(e),a.partial=!0,!0}return a.incomplete=!0,!1}function c(a,g){for(var h,j,k=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,l=[],m={},n=!1,o=c.caller;o&&!n;o=o.caller)if(o!==e&&o!==f.report){if(j={url:null,func:i,line:null,column:null},o.name?j.func=o.name:(h=k.exec(o.toString()))&&(j.func=h[1]),"undefined"==typeof j.func)try{j.func=h.input.substring(0,h.input.indexOf("{"))}catch(p){}m[""+o]?n=!0:m[""+o]=!0,l.push(j)}g&&l.splice(0,g);var q={name:a.name,message:a.message,url:d(),stack:l};return b(q,a.sourceURL||a.fileName,a.line||a.lineNumber,a.message||a.description),q}function e(b,e){var g=null;e=null==e?0:+e;try{if(g=a(b))return g}catch(h){if(f.debug)throw h}try{if(g=c(b,e+1))return g}catch(h){if(f.debug)throw h}return{name:b.name,message:b.message,url:d()}}return e.augmentStackTraceWithInitialElement=b,e.computeStackTraceFromStackProp=a,e}(),b.exports=f}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{5:5}],7:[function(a,b,c){function d(a,b){for(var c=0;c0){var i=d(c,this);~i?c.splice(i+1):c.push(this),~i?e.splice(i,1/0,g):e.push(g),~d(c,h)&&(h=b.call(this,g,h))}else c.push(h);return null==a?h instanceof Error?f(h):h:a.call(this,g,h)}}c=b.exports=e,c.getSerialize=g},{}]},{},[4])(4)}); +//# sourceMappingURL=raven.min.js.map \ No newline at end of file diff --git a/development/mockExtension.js b/development/mockExtension.js index 55799b2bf..ac03d965c 100644 --- a/development/mockExtension.js +++ b/development/mockExtension.js @@ -37,3 +37,8 @@ apis.forEach(function (api) { extension.runtime.reload = noop extension.tabs.create = noop +extension.runtime.getManifest = function () { + return { + version: 'development' + } +} \ No newline at end of file diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json new file mode 100644 index 000000000..6ea8e64cd --- /dev/null +++ b/development/states/confirm-new-ui.json @@ -0,0 +1,154 @@ +{ + "metamask": { + "isInitialized": true, + "isUnlocked": true, + "featureFlags": {"betaUI": true}, + "rpcTarget": "https://rawtestrpc.metamask.io/", + "identities": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "name": "Send Account 1" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "name": "Send Account 2" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "name": "Send Account 3" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "address": "0xd85a4b6a394794842887b8284293d69163007bbb", + "name": "Send Account 4" + } + }, + "unapprovedTxs": {}, + "currentCurrency": "USD", + "conversionRate": 1200.88200327, + "conversionDate": 1489013762, + "noActiveNotices": true, + "frequentRpcList": [], + "network": "3", + "accounts": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "code": "0x", + "balance": "0x47c9d71831c76efe", + "nonce": "0x1b", + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "code": "0x", + "balance": "0x37452b1315889f80", + "nonce": "0xa", + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "code": "0x", + "balance": "0x30c9d71831c76efe", + "nonce": "0x1c", + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xd85a4b6a394794842887b8284293d69163007bbb" + } + }, + "addressBook": [ + { + "address": "0x06195827297c7a80a443b6894d3bdb8824b43896", + "name": "Address Book Account 1" + } + ], + "tokens": [], + "transactions": {}, + "selectedAddressTxList": [], + "unapprovedTxs": { + "4768706228115573": { + "id": 4768706228115573, + "time": 1487363153561, + "status": "unapproved", + "gasMultiplier": 1, + "metamaskNetworkId": "3", + "txParams": { + "from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "value": "0x1bc16d674ec80000", + "metamaskId": 4768706228115573, + "metamaskNetworkId": "3", + "gas": "0xea60", + "gasPrice": "0xba43b7400" + } + } + }, + "unapprovedMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "fdea65c8e26263f6d9a1b5de9555d2931a33b825", + "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "2f8d4a878cfa04a6e60d46362f5644deab66572d" + ] + }, + { + "type": "Simple Key Pair", + "accounts": [ + "0xd85a4b6a394794842887b8284293d69163007bbb" + ] + } + ], + "selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb", + "currentCurrency": "USD", + "provider": { + "type": "testnet" + }, + "shapeShiftTxList": [], + "lostAccounts": [], + "send": { + "gasLimit": "0xea60", + "gasPrice": "0xba43b7400", + "gasTotal": "0xb451dc41b578", + "tokenBalance": null, + "from": { + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "balance": "0x37452b1315889f80" + }, + "to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "amount": "0x1bc16d674ec80000", + "memo": "", + "errors": {}, + "maxModeOn": false, + "editingTransactionId": null + } + }, + "appState": { + "menuOpen": false, + "currentView": { + "name": "confTx", + "detailView": null, + "context": 0 + }, + "accountDetail": { + "subview": "transactions" + }, + "modal": { + "modalState": {}, + "previousModalState": {} + }, + "transForward": true, + "isLoading": false, + "warning": null, + "scrollToBottom": false, + "forgottenPassword": null + }, + "identities": {} +} diff --git a/development/states/send-edit.json b/development/states/send-edit.json new file mode 100644 index 000000000..6ea8e64cd --- /dev/null +++ b/development/states/send-edit.json @@ -0,0 +1,154 @@ +{ + "metamask": { + "isInitialized": true, + "isUnlocked": true, + "featureFlags": {"betaUI": true}, + "rpcTarget": "https://rawtestrpc.metamask.io/", + "identities": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "name": "Send Account 1" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "name": "Send Account 2" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "name": "Send Account 3" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "address": "0xd85a4b6a394794842887b8284293d69163007bbb", + "name": "Send Account 4" + } + }, + "unapprovedTxs": {}, + "currentCurrency": "USD", + "conversionRate": 1200.88200327, + "conversionDate": 1489013762, + "noActiveNotices": true, + "frequentRpcList": [], + "network": "3", + "accounts": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "code": "0x", + "balance": "0x47c9d71831c76efe", + "nonce": "0x1b", + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "code": "0x", + "balance": "0x37452b1315889f80", + "nonce": "0xa", + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "code": "0x", + "balance": "0x30c9d71831c76efe", + "nonce": "0x1c", + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xd85a4b6a394794842887b8284293d69163007bbb" + } + }, + "addressBook": [ + { + "address": "0x06195827297c7a80a443b6894d3bdb8824b43896", + "name": "Address Book Account 1" + } + ], + "tokens": [], + "transactions": {}, + "selectedAddressTxList": [], + "unapprovedTxs": { + "4768706228115573": { + "id": 4768706228115573, + "time": 1487363153561, + "status": "unapproved", + "gasMultiplier": 1, + "metamaskNetworkId": "3", + "txParams": { + "from": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "value": "0x1bc16d674ec80000", + "metamaskId": 4768706228115573, + "metamaskNetworkId": "3", + "gas": "0xea60", + "gasPrice": "0xba43b7400" + } + } + }, + "unapprovedMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "fdea65c8e26263f6d9a1b5de9555d2931a33b825", + "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "2f8d4a878cfa04a6e60d46362f5644deab66572d" + ] + }, + { + "type": "Simple Key Pair", + "accounts": [ + "0xd85a4b6a394794842887b8284293d69163007bbb" + ] + } + ], + "selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb", + "currentCurrency": "USD", + "provider": { + "type": "testnet" + }, + "shapeShiftTxList": [], + "lostAccounts": [], + "send": { + "gasLimit": "0xea60", + "gasPrice": "0xba43b7400", + "gasTotal": "0xb451dc41b578", + "tokenBalance": null, + "from": { + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "balance": "0x37452b1315889f80" + }, + "to": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "amount": "0x1bc16d674ec80000", + "memo": "", + "errors": {}, + "maxModeOn": false, + "editingTransactionId": null + } + }, + "appState": { + "menuOpen": false, + "currentView": { + "name": "confTx", + "detailView": null, + "context": 0 + }, + "accountDetail": { + "subview": "transactions" + }, + "modal": { + "modalState": {}, + "previousModalState": {} + }, + "transForward": true, + "isLoading": false, + "warning": null, + "scrollToBottom": false, + "forgottenPassword": null + }, + "identities": {} +} diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json new file mode 100644 index 000000000..a0a2c66e4 --- /dev/null +++ b/development/states/send-new-ui.json @@ -0,0 +1,133 @@ +{ + "metamask": { + "isInitialized": true, + "isUnlocked": true, + "featureFlags": {"betaUI": true}, + "rpcTarget": "https://rawtestrpc.metamask.io/", + "identities": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825", + "name": "Send Account 1" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "name": "Send Account 2" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d", + "name": "Send Account 3" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "address": "0xd85a4b6a394794842887b8284293d69163007bbb", + "name": "Send Account 4" + } + }, + "unapprovedTxs": {}, + "currentCurrency": "USD", + "conversionRate": 1200.88200327, + "conversionDate": 1489013762, + "noActiveNotices": true, + "frequentRpcList": [], + "network": "3", + "accounts": { + "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": { + "code": "0x", + "balance": "0x47c9d71831c76efe", + "nonce": "0x1b", + "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825" + }, + "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": { + "code": "0x", + "balance": "0x37452b1315889f80", + "nonce": "0xa", + "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb" + }, + "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": { + "code": "0x", + "balance": "0x30c9d71831c76efe", + "nonce": "0x1c", + "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d" + }, + "0xd85a4b6a394794842887b8284293d69163007bbb": { + "code": "0x", + "balance": "0x0", + "nonce": "0x0", + "address": "0xd85a4b6a394794842887b8284293d69163007bbb" + } + }, + "addressBook": [ + { + "address": "0x06195827297c7a80a443b6894d3bdb8824b43896", + "name": "Address Book Account 1" + } + ], + "tokens": [], + "transactions": {}, + "selectedAddressTxList": [], + "unapprovedMsgs": {}, + "unapprovedMsgCount": 0, + "unapprovedPersonalMsgs": {}, + "unapprovedPersonalMsgCount": 0, + "keyringTypes": [ + "Simple Key Pair", + "HD Key Tree" + ], + "keyrings": [ + { + "type": "HD Key Tree", + "accounts": [ + "fdea65c8e26263f6d9a1b5de9555d2931a33b825", + "c5b8dbac4c1d3f152cdeb400e2313f309c410acb", + "2f8d4a878cfa04a6e60d46362f5644deab66572d" + ] + }, + { + "type": "Simple Key Pair", + "accounts": [ + "0xd85a4b6a394794842887b8284293d69163007bbb" + ] + } + ], + "selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb", + "currentCurrency": "USD", + "provider": { + "type": "testnet" + }, + "shapeShiftTxList": [], + "lostAccounts": [], + "send": { + "gasLimit": null, + "gasPrice": null, + "gasTotal": "0xb451dc41b578", + "tokenBalance": null, + "from": "", + "to": "", + "amount": "0x0", + "memo": "", + "errors": {}, + "maxModeOn": false, + "editingTransactionId": null + } + }, + "appState": { + "menuOpen": false, + "currentView": { + "name": "accountDetail", + "detailView": null, + "context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" + }, + "accountDetail": { + "subview": "transactions" + }, + "modal": { + "modalState": {}, + "previousModalState": {} + }, + "transForward": true, + "isLoading": false, + "warning": null, + "scrollToBottom": false, + "forgottenPassword": null + }, + "identities": {} +} diff --git a/gulpfile.js b/gulpfile.js index 26c70080f..0d4a3e5c0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -179,7 +179,7 @@ gulp.task('deps', function (cb) { gulp.task('lint', function () { // Ignoring node_modules, dist/firefox, and docs folders: - return gulp.src(['app/**/*.js', 'ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js']) + return gulp.src(['app/**/*.js', '!app/scripts/vendor/**/*.js', 'ui/**/*.js', 'mascara/src/*.js', 'mascara/server/*.js', '!node_modules/**', '!dist/firefox/**', '!docs/**', '!app/scripts/chromereload.js', '!mascara/test/jquery-3.1.0.min.js']) .pipe(eslint(fs.readFileSync(path.join(__dirname, '.eslintrc')))) // eslint.format() outputs the lint results to the console. // Alternatively use eslint.formatEach() (see Docs). diff --git a/mock-dev.js b/mock-dev.js index 0a3eb12ce..8827ddfdd 100644 --- a/mock-dev.js +++ b/mock-dev.js @@ -23,6 +23,7 @@ const states = require('./development/states') const Selector = require('./development/selector') const MetamaskController = require('./app/scripts/metamask-controller') const firstTimeState = require('./app/scripts/first-time-state') +const ExtensionPlatform = require('./app/scripts/platforms/extension') const extension = require('./development/mockExtension') const noop = function () {} @@ -67,6 +68,7 @@ const controller = new MetamaskController({ initState: firstTimeState, }) global.metamaskController = controller +global.platform = new ExtensionPlatform // // User Interface diff --git a/old-ui/app/components/loading.js b/old-ui/app/components/loading.js index 163792584..b8e2eb599 100644 --- a/old-ui/app/components/loading.js +++ b/old-ui/app/components/loading.js @@ -11,7 +11,7 @@ function LoadingIndicator () { } LoadingIndicator.prototype.render = function () { - const { isLoading, loadingMessage } = this.props + const { isLoading, loadingMessage, canBypass, bypass } = this.props return ( isLoading ? h('.full-flex-height', { @@ -28,6 +28,16 @@ LoadingIndicator.prototype.render = function () { background: 'rgba(255, 255, 255, 0.8)', }, }, [ + canBypass ? h( 'i.fa.fa-close.cursor-pointer.close-loading', { + style: { + position: 'absolute', + top: '1px', + right: '15px', + color: '#AEAEAE', + }, + onClick: bypass, + }) : null, + h('img', { src: 'images/loading.svg', }), diff --git a/old-ui/app/conf-tx.js b/old-ui/app/conf-tx.js index 5e2ae9e78..1bb8eb97c 100644 --- a/old-ui/app/conf-tx.js +++ b/old-ui/app/conf-tx.js @@ -62,8 +62,12 @@ ConfirmTxScreen.prototype.render = function () { h('.flex-column.flex-grow', [ h(LoadingIndicator, { - isLoading: txData.loadingDefaults, + isLoading: this.state ? !this.state.bypassLoadingScreen : txData.loadingDefaults, loadingMessage: 'Estimating transaction cost…', + canBypass: true, + bypass: () => { + this.setState({bypassLoadingScreen: true}) + }, }), // subtitle and nav diff --git a/package.json b/package.json index ea9b937d3..e85eb91a1 100644 --- a/package.json +++ b/package.json @@ -76,9 +76,9 @@ "ensnare": "^1.0.0", "eslint-plugin-react": "^7.4.0", "eth-bin-to-ops": "^1.0.1", - "eth-block-tracker": "^2.2.0", + "eth-block-tracker": "^2.3.0", "eth-json-rpc-filters": "^1.2.5", - "eth-json-rpc-infura": "^2.0.5", + "eth-json-rpc-infura": "^2.0.11", "eth-keyring-controller": "^2.1.4", "eth-contract-metadata": "^1.1.5", "eth-hd-keyring": "^1.2.1", @@ -113,7 +113,7 @@ "iframe-stream": "^3.0.0", "inject-css": "^0.1.1", "jazzicon": "^1.2.0", - "json-rpc-engine": "3.2.0", + "json-rpc-engine": "^3.6.1", "json-rpc-middleware-stream": "^1.0.1", "lodash.debounce": "^4.0.8", "lodash.memoize": "^4.1.2", @@ -150,6 +150,7 @@ "react-router-dom": "^4.2.2", "react-select": "^1.0.0", "react-simple-file-input": "^2.0.0", + "react-tippy": "^1.2.2", "react-toggle-button": "^2.2.0", "react-tooltip-component": "^0.3.0", "react-transition-group": "^2.2.1", @@ -171,7 +172,7 @@ "valid-url": "^1.0.9", "vreme": "^3.0.2", "web3": "^0.20.1", - "web3-provider-engine": "^13.5.0", + "web3-provider-engine": "^13.5.6", "web3-stream-provider": "^3.0.1", "xtend": "^4.0.1" }, @@ -189,6 +190,7 @@ "brfs": "^1.4.3", "browserify": "^14.4.0", "chai": "^4.1.0", + "compression": "^1.7.1", "coveralls": "^3.0.0", "deep-freeze-strict": "^1.1.1", "del": "^3.0.0", @@ -200,7 +202,7 @@ "eslint-plugin-react": "^7.4.0", "eth-json-rpc-middleware": "^1.2.7", "fs-promise": "^2.0.3", - "gulp": "github:gulpjs/gulp#4.0", + "gulp": "github:gulpjs/gulp#6d71a658c61edb3090221579d8f97dbe086ba2ed", "gulp-babel": "^7.0.0", "gulp-if": "^2.0.2", "gulp-json-editor": "^2.2.1", diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js index e59897713..06325ab98 100644 --- a/test/integration/lib/first-time.js +++ b/test/integration/lib/first-time.js @@ -39,9 +39,8 @@ async function runFirstTimeUsageTest(assert, done) { await timeout() // Scroll through terms - const title = app.find('h1').text() - // TODO Find where Metamask is getting added twice in the title - assert.equal(title, 'MetaMaskMetaMask', 'title screen') + const title = app.find('h1')[1] + assert.equal(title.textContent, 'MetaMask', 'title screen') // enter password const pwBox = app.find('#password-box')[0] @@ -67,19 +66,19 @@ async function runFirstTimeUsageTest(assert, done) { await timeout(1000) - const detail = app.find('.wallet-view')[0] + const detail = app.find('.account-detail-section')[0] assert.ok(detail, 'Account detail section loaded.') - await timeout(1000) - - const menu = app.find('.account-menu__icon')[0] - menu.click() + const sandwich = app.find('.sandwich-expando')[0] + sandwich.click() - await timeout(1000) + await timeout() - const lock = app.find('.account-menu__logout-button')[0] - assert.ok(lock, 'Lock menu item found') - lock.click() + const menu = app.find('.menu-droppo')[0] + const children = menu.children + const logout = children[2] + assert.ok(logout, 'Lock menu item found') + logout.click() await timeout(1000) @@ -91,30 +90,36 @@ async function runFirstTimeUsageTest(assert, done) { await timeout(1000) - const detail2 = app.find('.wallet-view')[0] + const detail2 = app.find('.account-detail-section')[0] assert.ok(detail2, 'Account detail section loaded again.') await timeout() // open account settings dropdown - const qrButton = app.find('.wallet-view__details-button')[0] + const qrButton = app.find('.fa.fa-ellipsis-h')[0] qrButton.click() await timeout(1000) - const qrHeader = app.find('.editable-label__value')[0] - const qrContainer = app.find('.qr-wrapper')[0] + // qr code item + const qrButton2 = app.find('.dropdown-menu-item')[1] + qrButton2.click() + + await timeout(1000) + + const qrHeader = app.find('.qr-header')[0] + const qrContainer = app.find('#qr-container')[0] assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.') assert.ok(qrContainer, 'QR Container found') await timeout() - const networkMenu = app.find('.network-component')[0] + const networkMenu = app.find('.network-indicator')[0] networkMenu.click() await timeout() - const networkMenu2 = app.find('.menu-droppo')[0] + const networkMenu2 = app.find('.network-indicator')[0] const children2 = networkMenu2.children children2.length[3] assert.ok(children2, 'All network options present') diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js new file mode 100644 index 000000000..99571d0e8 --- /dev/null +++ b/test/integration/lib/send-new-ui.js @@ -0,0 +1,229 @@ +const reactTriggerChange = require('react-trigger-change') + +const PASSWORD = 'password123' + +QUnit.module('new ui send flow') + +QUnit.test('successful send flow', (assert) => { + const done = assert.async() + runSendFlowTest(assert).then(done).catch((err) => { + assert.notOk(err, `Error was thrown: ${err.stack}`) + done() + }) +}) + +global.ethQuery = { + sendTransaction: () => {}, +} + +async function runSendFlowTest(assert, done) { + console.log('*** start runSendFlowTest') + const selectState = $('select') + selectState.val('send new ui') + reactTriggerChange(selectState[0]) + + await timeout(2000) + + const sendScreenButton = $('button.btn-clear.hero-balance-button') + assert.ok(sendScreenButton[1], 'send screen button present') + sendScreenButton[1].click() + + await timeout(1000) + + const sendContainer = $('.send-v2__container') + assert.ok(sendContainer[0], 'send container renders') + + const sendHeader = $('.send-v2__send-header-icon') + assert.ok(sendHeader[0], 'send screen has a header icon') + + const sendTitle = $('.send-v2__title') + assert.equal(sendTitle[0].textContent, 'Send Funds', 'Send screen title is correct') + + const sendCopy = $('.send-v2__copy') + assert.equal(sendCopy[0].textContent, 'Only send ETH to an Ethereum address.', 'Send screen has copy') + + const sendFromField = $('.send-v2__form-field') + assert.ok(sendFromField[0], 'send screen has a from field') + + let sendFromFieldItemAddress = $('.account-list-item__account-name') + assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 4', 'send from field shows correct account name') + + const sendFromFieldItem = $('.account-list-item') + sendFromFieldItem[0].click() + + await timeout() + + const sendFromDropdownList = $('.send-v2__from-dropdown__list') + assert.equal(sendFromDropdownList.children().length, 4, 'send from dropdown shows all accounts') + console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromDropdownList.children()[1]`, sendFromDropdownList.children()[1]); + sendFromDropdownList.children()[1].click() + + await timeout() + + sendFromFieldItemAddress = $('.account-list-item__account-name') + console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sendFromFieldItemAddress[0]`, sendFromFieldItemAddress[0]); + assert.equal(sendFromFieldItemAddress[0].textContent, 'Send Account 2', 'send from field dropdown changes account name') + + let sendToFieldInput = $('.send-v2__to-autocomplete__input') + sendToFieldInput[0].focus() + + await timeout() + + const sendToDropdownList = $('.send-v2__from-dropdown__list') + assert.equal(sendToDropdownList.children().length, 5, 'send to dropdown shows all accounts and address book accounts') + + sendToDropdownList.children()[2].click() + + await timeout() + + const sendToAccountAddress = sendToFieldInput.val() + assert.equal(sendToAccountAddress, '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', 'send to dropdown selects the correct address') + + const sendAmountField = $('.send-v2__form-row:eq(2)') + sendAmountField.find('.currency-display')[0].click() + + await timeout() + + const sendAmountFieldInput = sendAmountField.find('input:text') + sendAmountFieldInput.val('5.1') + reactTriggerChange(sendAmountField.find('input')[0]) + + await timeout() + + let errorMessage = $('.send-v2__error') + assert.equal(errorMessage[0].textContent, 'Insufficient funds.', 'send should render an insufficient fund error message') + + sendAmountFieldInput.val('2.0') + reactTriggerChange(sendAmountFieldInput[0]) + + await timeout() + errorMessage = $('.send-v2__error') + assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected') + + const sendGasField = $('.send-v2__gas-fee-display') + assert.equal( + sendGasField.find('.currency-display__input-wrapper > input').val(), + '0.000198', + 'send gas field should show estimated gas total' + ) + assert.equal( + sendGasField.find('.currency-display__converted-value')[0].textContent, + '0.24 USD', + 'send gas field should show estimated gas total converted to USD' + ) + + const sendGasOpenCustomizeModalButton = $('.send-v2__sliders-icon-container' + ) + sendGasOpenCustomizeModalButton[0].click() + + await timeout(1000) + + const customizeGasModal = $('.send-v2__customize-gas') + assert.ok(customizeGasModal[0], 'should render the customize gas modal') + + const customizeGasPriceInput = $('.send-v2__gas-modal-card').first().find('input') + customizeGasPriceInput.val(50) + reactTriggerChange(customizeGasPriceInput[0]) + const customizeGasLimitInput = $('.send-v2__gas-modal-card').last().find('input') + customizeGasLimitInput.val(60000) + reactTriggerChange(customizeGasLimitInput[0]) + + await timeout() + + const customizeGasSaveButton = $('.send-v2__customize-gas__save') + customizeGasSaveButton[0].click() + + await timeout() + + assert.equal( + sendGasField.find('.currency-display__input-wrapper > input').val(), + '0.003', + 'send gas field should show customized gas total' + ) + assert.equal( + sendGasField.find('.currency-display__converted-value')[0].textContent, + '3.60 USD', + 'send gas field should show customized gas total converted to USD' + ) + + const sendButton = $('.send-v2__next-btn') + sendButton[0].click() + + await timeout(2000) + + selectState.val('send edit') + reactTriggerChange(selectState[0]) + + await timeout(2000) + + const confirmFromName = $('.confirm-screen-account-name').first() + assert.equal(confirmFromName[0].textContent, 'Send Account 2', 'confirm screen should show correct from name') + + const confirmToName = $('.confirm-screen-account-name').last() + assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name') + + const confirmScreenRows = $('.confirm-screen-rows') + const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2] + assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas') + const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3] + assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total') + + const confirmScreenBackButton = $('.confirm-screen-back-button') + confirmScreenBackButton[0].click() + + await timeout(1000) + + const sendFromFieldItemInEdit = $('.account-list-item') + sendFromFieldItemInEdit[0].click() + + await timeout() + + const sendFromDropdownListInEdit = $('.send-v2__from-dropdown__list') + sendFromDropdownListInEdit.children()[2].click() + + await timeout() + + const sendToFieldInputInEdit = $('.send-v2__to-autocomplete__input') + sendToFieldInputInEdit[0].focus() + sendToFieldInputInEdit.val('0xd85a4b6a394794842887b8284293d69163007bbb') + + await timeout() + + const sendAmountFieldInEdit = $('.send-v2__form-row:eq(2)') + sendAmountFieldInEdit.find('.currency-display')[0].click() + + await timeout() + + const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('input:text') + sendAmountFieldInputInEdit.val('1.0') + reactTriggerChange(sendAmountFieldInputInEdit[0]) + + await timeout() + + const sendButtonInEdit = $('.send-v2__next-btn') + sendButtonInEdit[0].click() + + await timeout() + + // TODO: Need a way to mock background so that we can test correct transition from editing to confirm + selectState.val('confirm new ui') + reactTriggerChange(selectState[0]) + + await timeout(2000) + const confirmScreenConfirmButton = $('.confirm-screen-confirm-button') + console.log(`+++++++++++++++++++++++++++++++= confirmScreenConfirmButton[0]`, confirmScreenConfirmButton[0]); + confirmScreenConfirmButton[0].click() + + await timeout(2000) + + const txView = $('.tx-view') + console.log(`++++++++++++++++++++++++++++++++ txView[0]`, txView[0]); + + assert.ok(txView[0], 'Should return to the account details screen after confirming') +} + +function timeout (time) { + return new Promise((resolve, reject) => { + setTimeout(resolve, time || 1500) + }) +} \ No newline at end of file diff --git a/test/stub/provider.js b/test/stub/provider.js index 85e1da707..e77db4e28 100644 --- a/test/stub/provider.js +++ b/test/stub/provider.js @@ -1,12 +1,12 @@ const JsonRpcEngine = require('json-rpc-engine') const scaffoldMiddleware = require('eth-json-rpc-middleware/scaffold') +const TestBlockchain = require('eth-block-tracker/test/util/testBlockMiddleware') module.exports = { createEngineForTestData, providerFromEngine, scaffoldMiddleware, - createEthJsQueryStub, - createStubedProvider, + createTestProviderTools, } @@ -19,20 +19,13 @@ function providerFromEngine (engine) { return provider } -function createEthJsQueryStub (stubProvider) { - return new Proxy({}, { - get: (obj, method) => { - return (...params) => { - return new Promise((resolve, reject) => { - stubProvider.sendAsync({ method: `eth_${method}`, params }, (err, ress) => resolve(ress.result)) - }) - } - }, - }) -} - -function createStubedProvider (resultStub) { +function createTestProviderTools (opts = {}) { const engine = createEngineForTestData() - engine.push(scaffoldMiddleware(resultStub)) - return providerFromEngine(engine) -} \ No newline at end of file + const testBlockchain = new TestBlockchain() + // handle provided hooks + engine.push(scaffoldMiddleware(opts.scaffold || {})) + // handle block tracker methods + engine.push(testBlockchain.createMiddleware()) + const provider = providerFromEngine(engine) + return { provider, engine, testBlockchain } +} diff --git a/test/unit/pending-tx-test.js b/test/unit/pending-tx-test.js index 64547a505..f0b4e3bfc 100644 --- a/test/unit/pending-tx-test.js +++ b/test/unit/pending-tx-test.js @@ -3,7 +3,7 @@ const ethUtil = require('ethereumjs-util') const EthTx = require('ethereumjs-tx') const ObservableStore = require('obs-store') const clone = require('clone') -const { createStubedProvider } = require('../stub/provider') +const { createTestProviderTools } = require('../stub/provider') const PendingTransactionTracker = require('../../app/scripts/lib/pending-tx-tracker') const MockTxGen = require('../lib/mock-tx-gen') const sinon = require('sinon') @@ -40,7 +40,7 @@ describe('PendingTransactionTracker', function () { txParams: { from: '0x1678a085c290ebd122dc42cba69373b5953b831d'}, } providerResultStub = {} - provider = createStubedProvider(providerResultStub) + provider = createTestProviderTools({ scaffold: providerResultStub }).provider pendingTxTracker = new PendingTransactionTracker({ provider, diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js index 36f3e1c68..cc99afee4 100644 --- a/test/unit/tx-controller-test.js +++ b/test/unit/tx-controller-test.js @@ -1,11 +1,12 @@ const assert = require('assert') const ethUtil = require('ethereumjs-util') const EthTx = require('ethereumjs-tx') +const EthjsQuery = require('ethjs-query') const ObservableStore = require('obs-store') const sinon = require('sinon') const TransactionController = require('../../app/scripts/controllers/transactions') const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') -const { createStubedProvider, createEthJsQueryStub } = require('../stub/provider') +const { createTestProviderTools } = require('../stub/provider') const noop = () => true const currentNetworkId = 42 @@ -14,11 +15,18 @@ const privKey = new Buffer('8718b9618a37d1fc78c436511fc6df3c8258d3250635bba617f3 describe('Transaction Controller', function () { - let txController, provider, providerResultStub + let txController, provider, providerResultStub, testBlockchain beforeEach(function () { - providerResultStub = {} - provider = createStubedProvider(providerResultStub) + providerResultStub = { + // 1 gwei + eth_gasPrice: '0x0de0b6b3a7640000', + // by default, all accounts are external accounts (not contracts) + eth_getCode: '0x', + } + const providerTools = createTestProviderTools({ scaffold: providerResultStub }) + provider = providerTools.provider + testBlockchain = providerTools.testBlockchain txController = new TransactionController({ provider, @@ -30,10 +38,7 @@ describe('Transaction Controller', function () { resolve() }), }) - txController.query = createEthJsQueryStub(provider) - txController.txGasUtil.query = createEthJsQueryStub(provider) txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }) - txController.txProviderUtils = new TxGasUtils(txController.provider) }) describe('#getState', function () { @@ -155,15 +160,6 @@ describe('Transaction Controller', function () { }) describe('#addUnapprovedTransaction', function () { - let addTxDefaults - beforeEach(() => { - addTxDefaults = txController.addTxDefaults - txController.addTxDefaults = function addTxDefaultsStub () { return Promise.resolve() } - - }) - afterEach(() => { - txController.addTxDefaults = addTxDefaults - }) it('should add an unapproved transaction and return a valid txMeta', function (done) { txController.addUnapprovedTransaction({}) @@ -219,7 +215,7 @@ describe('Transaction Controller', function () { var sample = { value: '0x01', } - txController.txProviderUtils.validateTxParams(sample).then(() => { + txController.txGasUtil.validateTxParams(sample).then(() => { done() }).catch(done) }) @@ -228,7 +224,7 @@ describe('Transaction Controller', function () { var sample = { value: '-0x01', } - txController.txProviderUtils.validateTxParams(sample) + txController.txGasUtil.validateTxParams(sample) .then(() => done('expected to thrown on negativity values but didn\'t')) .catch((err) => { assert.ok(err, 'error') diff --git a/test/unit/tx-gas-util-test.js b/test/unit/tx-gas-util-test.js index ccef31359..d9a12d1c3 100644 --- a/test/unit/tx-gas-util-test.js +++ b/test/unit/tx-gas-util-test.js @@ -1,12 +1,12 @@ const assert = require('assert') const TxGasUtils = require('../../app/scripts/lib/tx-gas-utils') -const { createStubedProvider } = require('../stub/provider') +const { createTestProviderTools } = require('../stub/provider') describe('Tx Gas Util', function () { let txGasUtil, provider, providerResultStub beforeEach(function () { providerResultStub = {} - provider = createStubedProvider(providerResultStub) + provider = createTestProviderTools({ scaffold: providerResultStub }).provider txGasUtil = new TxGasUtils({ provider, }) diff --git a/ui/app/actions.js b/ui/app/actions.js index da69b8195..e5b35daaa 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -55,6 +55,7 @@ var actions = { SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM', unlockMetamask: unlockMetamask, unlockFailed: unlockFailed, + unlockSucceeded, showCreateVault: showCreateVault, showRestoreVault: showRestoreVault, showInitializeMenu: showInitializeMenu, @@ -78,6 +79,7 @@ var actions = { // unlock screen UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', UNLOCK_FAILED: 'UNLOCK_FAILED', + UNLOCK_SUCCEEDED: 'UNLOCK_SUCCEEDED', UNLOCK_METAMASK: 'UNLOCK_METAMASK', LOCK_METAMASK: 'LOCK_METAMASK', tryUnlockMetamask: tryUnlockMetamask, @@ -281,12 +283,14 @@ function tryUnlockMetamask (password) { log.debug(`background.submitPassword`) return new Promise((resolve, reject) => { - background.submitPassword(password, err => { + background.submitPassword(password, (err) => { dispatch(actions.hideLoadingIndication()) if (err) { + dispatch(actions.unlockFailed(err.message)) reject(err) } else { + dispatch(actions.unlockSucceeded()) dispatch(actions.transitionForward()) return forceUpdateMetamaskState(dispatch).then(resolve) } @@ -967,6 +971,13 @@ function unlockFailed (message) { } } +function unlockSucceeded (message) { + return { + type: actions.UNLOCK_SUCCEEDED, + value: message, + } +} + function unlockMetamask (account) { return { type: actions.UNLOCK_METAMASK, diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 3f147159b..3e91fa807 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -24,7 +24,7 @@ Network.prototype.render = function () { let iconName, hoverText if (networkNumber === 'loading') { - return h('span.pointer', { + return h('span.pointer.network-indicator', { style: { display: 'flex', alignItems: 'center', diff --git a/ui/app/components/pages/add-token.js b/ui/app/components/pages/add-token.js index 080f3eca5..40cfedafd 100644 --- a/ui/app/components/pages/add-token.js +++ b/ui/app/components/pages/add-token.js @@ -43,7 +43,6 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return { - goHome: () => dispatch(actions.goHome()), addTokens: tokens => dispatch(actions.addTokens(tokens)), } } diff --git a/ui/app/components/pages/create-account/import-account/index.js b/ui/app/components/pages/create-account/import-account/index.js index 0c901c09b..71eb9ae23 100644 --- a/ui/app/components/pages/create-account/import-account/index.js +++ b/ui/app/components/pages/create-account/import-account/index.js @@ -37,7 +37,7 @@ AccountImportSubview.prototype.render = function () { h('div.new-account-import-form__select-section', [ - h('div.new-account-import-form__select-label', 'SELECT TYPE'), + h('div.new-account-import-form__select-label', 'Select Type'), h(Select, { className: 'new-account-import-form__select', diff --git a/ui/app/components/pages/create-account/import-account/private-key.js b/ui/app/components/pages/create-account/import-account/private-key.js index 785bf2cb6..fb10bdb48 100644 --- a/ui/app/components/pages/create-account/import-account/private-key.js +++ b/ui/app/components/pages/create-account/import-account/private-key.js @@ -37,15 +37,20 @@ PrivateKeyImportView.prototype.render = function () { return ( h('div.new-account-import-form__private-key', [ - h('span.new-account-create-form__instruction', 'Paste your private key string here:'), - h('input.new-account-import-form__input-password', { - type: 'password', - id: 'private-key-box', - onKeyPress: () => this.createKeyringOnEnter(), - }), + h('div.new-account-import-form__private-key-password-container', [ - h('div.new-account-create-form__buttons', {}, [ + h('span.new-account-import-form__instruction', 'Paste your private key string here:'), + + h('input.new-account-import-form__input-password', { + type: 'password', + id: 'private-key-box', + onKeyPress: () => this.createKeyringOnEnter(), + }), + + ]), + + h('div.new-account-import-form__buttons', {}, [ h('button.new-account-create-form__button-cancel', { onClick: () => this.props.history.push(DEFAULT_ROUTE), diff --git a/ui/app/components/pages/create-account/new-account.js b/ui/app/components/pages/create-account/new-account.js index a3bfefc84..fbd456a75 100644 --- a/ui/app/components/pages/create-account/new-account.js +++ b/ui/app/components/pages/create-account/new-account.js @@ -8,16 +8,18 @@ const { DEFAULT_ROUTE } = require('../../../routes') class NewAccountCreateForm extends Component { constructor (props) { super(props) + const { numberOfExistingAccounts = 0 } = props const newAccountNumber = numberOfExistingAccounts + 1 this.state = { - newAccountName: `Account ${newAccountNumber}`, + newAccountName: '', + defaultAccountName: `Account ${newAccountNumber}`, } } render () { - const { newAccountName } = this.state + const { newAccountName, defaultAccountName } = this.state const { history, createAccount } = this.props return h('div.new-account-create-form', [ @@ -28,8 +30,8 @@ class NewAccountCreateForm extends Component { h('div.new-account-create-form__input-wrapper', {}, [ h('input.new-account-create-form__input', { - value: this.state.newAccountName, - placeholder: 'E.g. My new account', + value: newAccountName, + placeholder: defaultAccountName, onChange: event => this.setState({ newAccountName: event.target.value }), }, []), ]), @@ -44,7 +46,7 @@ class NewAccountCreateForm extends Component { h('button.new-account-create-form__button-create', { onClick: () => { - createAccount(newAccountName) + createAccount(newAccountName || defaultAccountName) .then(() => history.push(DEFAULT_ROUTE)) }, }, [ diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js index 1ebbe597c..b3c73f9b6 100644 --- a/ui/app/components/send/send-v2-container.js +++ b/ui/app/components/send/send-v2-container.js @@ -83,7 +83,6 @@ function mapDispatchToProps (dispatch) { updateSendAmount: newAmount => dispatch(actions.updateSendAmount(newAmount)), updateSendMemo: newMemo => dispatch(actions.updateSendMemo(newMemo)), updateSendErrors: newError => dispatch(actions.updateSendErrors(newError)), - goHome: () => dispatch(actions.goHome()), clearSend: () => dispatch(actions.clearSend()), setMaxModeTo: bool => dispatch(actions.setMaxModeTo(bool)), } diff --git a/ui/app/components/tooltip-v2.js b/ui/app/components/tooltip-v2.js new file mode 100644 index 000000000..133a0f16a --- /dev/null +++ b/ui/app/components/tooltip-v2.js @@ -0,0 +1,31 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const ReactTippy = require('react-tippy').Tooltip + +module.exports = Tooltip + +inherits(Tooltip, Component) +function Tooltip () { + Component.call(this) +} + +Tooltip.prototype.render = function () { + const props = this.props + const { position, title, children, wrapperClassName } = props + + return h('div', { + className: wrapperClassName, + }, [ + + h(ReactTippy, { + title, + position: position || 'left', + trigger: 'mouseenter', + hideOnClick: false, + size: 'small', + arrow: true, + }, children), + + ]) +} diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index c7f81a305..ce0902a91 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -4,8 +4,10 @@ const h = require('react-hyperscript') const { withRouter } = require('react-router-dom') const { compose } = require('recompose') const inherits = require('util').inherits +const classnames = require('classnames') const Identicon = require('./identicon') // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns +const Tooltip = require('./tooltip-v2.js') const copyToClipboard = require('copy-to-clipboard') const actions = require('../actions') const BalanceComponent = require('./balance-component') @@ -51,6 +53,7 @@ function WalletView () { Component.call(this) this.state = { hasCopied: false, + copyToClipboardPressed: false, } } @@ -140,17 +143,30 @@ WalletView.prototype.render = function () { ]), ]), - - h('div.wallet-view__address', { - onClick: () => { - copyToClipboard(selectedAddress) - this.setState({ hasCopied: true }) - setTimeout(() => this.setState({ hasCopied: false }), 3000) - }, + h(Tooltip, { + position: 'bottom', + title: this.state.hasCopied ? 'Copied!' : 'Copy to clipboard', + wrapperClassName: 'wallet-view__tooltip', }, [ - this.state.hasCopied && 'Copied to Clipboard', - !this.state.hasCopied && `${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`, - h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }), + h('button.wallet-view__address', { + className: classnames({ + 'wallet-view__address__pressed': this.state.copyToClipboardPressed, + }), + onClick: () => { + copyToClipboard(selectedAddress) + this.setState({ hasCopied: true }) + setTimeout(() => this.setState({ hasCopied: false }), 3000) + }, + onMouseDown: () => { + this.setState({ copyToClipboardPressed: true }) + }, + onMouseUp: () => { + this.setState({ copyToClipboardPressed: false }) + }, + }, [ + `${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`, + h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }), + ]), ]), this.renderWalletBalance(), diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js index 05bbd2696..913563d12 100644 --- a/ui/app/conf-tx.js +++ b/ui/app/conf-tx.js @@ -126,7 +126,6 @@ ConfirmTxScreen.prototype.render = function () { cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData), cancelTypedMessage: this.cancelTypedMessage.bind(this, txData), }) - } function currentTxView (opts) { diff --git a/ui/app/css/itcss/components/index.scss b/ui/app/css/itcss/components/index.scss index 97f7ab785..a1a716b9a 100644 --- a/ui/app/css/itcss/components/index.scss +++ b/ui/app/css/itcss/components/index.scss @@ -55,3 +55,5 @@ @import './pages/index.scss'; @import './new-account.scss'; + +@import './tooltip.scss'; diff --git a/ui/app/css/itcss/components/new-account.scss b/ui/app/css/itcss/components/new-account.scss index 5837a80bb..119d5c479 100644 --- a/ui/app/css/itcss/components/new-account.scss +++ b/ui/app/css/itcss/components/new-account.scss @@ -56,11 +56,17 @@ } .new-account-import-form { + display: flex; + flex-flow: column; + align-items: center; + padding: 0 30px; + &__select-section { display: flex; - justify-content: space-evenly; + justify-content: space-between; align-items: center; margin-top: 29px; + width: 100%; } &__select-label { @@ -92,19 +98,25 @@ } } + &__private-key-password-container { + display: flex; + flex-flow: column; + align-items: center; + width: 100%; + } + &__instruction { color: $scorpion; font-family: Roboto; font-size: 16px; line-height: 21px; align-self: flex-start; - margin-left: 30px; } &__private-key { display: flex; flex-flow: column; - align-items: center; + align-items: flex-start; margin-top: 34px; } @@ -127,6 +139,13 @@ align-items: center; margin-top: 29px; } + + &__buttons { + margin-top: 39px; + display: flex; + width: 100%; + justify-content: space-between; + } } .new-account-create-form { diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss index 1c26882b5..37fabf254 100644 --- a/ui/app/css/itcss/components/newui-sections.scss +++ b/ui/app/css/itcss/components/newui-sections.scss @@ -89,6 +89,12 @@ $wallet-view-bg: $alabaster; flex: 0 0 auto; } + &__tooltip { + display: flex; + justify-content: center; + padding: 24px; + } + &__address { border-radius: 3px; background-color: $alto; @@ -96,10 +102,13 @@ $wallet-view-bg: $alabaster; font-size: 14px; line-height: 12px; padding: 4px 12px; - margin: 24px auto; font-weight: 300; cursor: pointer; flex: 0 0 auto; + + &__pressed { + background-color: $manatee, + } } &__sidebar-close { diff --git a/ui/app/css/itcss/components/tooltip.scss b/ui/app/css/itcss/components/tooltip.scss new file mode 100644 index 000000000..78325865e --- /dev/null +++ b/ui/app/css/itcss/components/tooltip.scss @@ -0,0 +1,7 @@ +.metamask-tooltip { + padding: 5px !important; +} + +// needed for react-tippy +// copied from node_modules/react-tippy/dist/tippy.css +.tippy-touch{cursor:pointer!important}.tippy-notransition{transition:none!important}.tippy-popper{max-width:400px;-webkit-perspective:800px;perspective:800px;z-index:9999;outline:0;transition-timing-function:cubic-bezier(.165,.84,.44,1);pointer-events:none}.tippy-popper.html-template{max-width:96%;max-width:calc(100% - 20px)}.tippy-popper[x-placement^=top] [x-arrow]{border-top:7px solid #333;border-right:7px solid transparent;border-left:7px solid transparent;bottom:-7px;margin:0 9px}.tippy-popper[x-placement^=top] [x-arrow].arrow-small{border-top:5px solid #333;border-right:5px solid transparent;border-left:5px solid transparent;bottom:-5px}.tippy-popper[x-placement^=top] [x-arrow].arrow-big{border-top:10px solid #333;border-right:10px solid transparent;border-left:10px solid transparent;bottom:-10px}.tippy-popper[x-placement^=top] [x-circle]{-webkit-transform-origin:0 33%;transform-origin:0 33%}.tippy-popper[x-placement^=top] [x-circle].enter{-webkit-transform:scale(1) translate(-50%,-55%);transform:scale(1) translate(-50%,-55%);opacity:1}.tippy-popper[x-placement^=top] [x-circle].leave{-webkit-transform:scale(.15) translate(-50%,-50%);transform:scale(.15) translate(-50%,-50%);opacity:0}.tippy-popper[x-placement^=top] .tippy-tooltip.light-theme [x-circle]{background-color:#fff}.tippy-popper[x-placement^=top] .tippy-tooltip.light-theme [x-arrow]{border-top:7px solid #fff;border-right:7px solid transparent;border-left:7px solid transparent}.tippy-popper[x-placement^=top] .tippy-tooltip.light-theme [x-arrow].arrow-small{border-top:5px solid #fff;border-right:5px solid transparent;border-left:5px solid transparent}.tippy-popper[x-placement^=top] .tippy-tooltip.light-theme [x-arrow].arrow-big{border-top:10px solid #fff;border-right:10px solid transparent;border-left:10px solid transparent}.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-circle]{background-color:rgba(0,0,0,.7)}.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow]{border-top:7px solid rgba(0,0,0,.7);border-right:7px solid transparent;border-left:7px solid transparent}.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow].arrow-small{border-top:5px solid rgba(0,0,0,.7);border-right:5px solid transparent;border-left:5px solid transparent}.tippy-popper[x-placement^=top] .tippy-tooltip.transparent-theme [x-arrow].arrow-big{border-top:10px solid rgba(0,0,0,.7);border-right:10px solid transparent;border-left:10px solid transparent}.tippy-popper[x-placement^=top] [data-animation=perspective]{-webkit-transform-origin:bottom;transform-origin:bottom}.tippy-popper[x-placement^=top] [data-animation=perspective].enter{opacity:1;-webkit-transform:translateY(-10px) rotateX(0);transform:translateY(-10px) rotateX(0)}.tippy-popper[x-placement^=top] [data-animation=perspective].leave{opacity:0;-webkit-transform:translateY(0) rotateX(90deg);transform:translateY(0) rotateX(90deg)}.tippy-popper[x-placement^=top] [data-animation=fade].enter{opacity:1;-webkit-transform:translateY(-10px);transform:translateY(-10px)}.tippy-popper[x-placement^=top] [data-animation=fade].leave{opacity:0;-webkit-transform:translateY(-10px);transform:translateY(-10px)}.tippy-popper[x-placement^=top] [data-animation=shift].enter{opacity:1;-webkit-transform:translateY(-10px);transform:translateY(-10px)}.tippy-popper[x-placement^=top] [data-animation=shift].leave{opacity:0;-webkit-transform:translateY(0);transform:translateY(0)}.tippy-popper[x-placement^=top] [data-animation=scale].enter{opacity:1;-webkit-transform:translateY(-10px) scale(1);transform:translateY(-10px) scale(1)}.tippy-popper[x-placement^=top] [data-animation=scale].leave{opacity:0;-webkit-transform:translateY(0) scale(0);transform:translateY(0) scale(0)}.tippy-popper[x-placement^=bottom] [x-arrow]{border-bottom:7px solid #333;border-right:7px solid transparent;border-left:7px solid transparent;top:-7px;margin:0 9px}.tippy-popper[x-placement^=bottom] [x-arrow].arrow-small{border-bottom:5px solid #333;border-right:5px solid transparent;border-left:5px solid transparent;top:-5px}.tippy-popper[x-placement^=bottom] [x-arrow].arrow-big{border-bottom:10px solid #333;border-right:10px solid transparent;border-left:10px solid transparent;top:-10px}.tippy-popper[x-placement^=bottom] [x-circle]{-webkit-transform-origin:0 -50%;transform-origin:0 -50%}.tippy-popper[x-placement^=bottom] [x-circle].enter{-webkit-transform:scale(1) translate(-50%,-45%);transform:scale(1) translate(-50%,-45%);opacity:1}.tippy-popper[x-placement^=bottom] [x-circle].leave{-webkit-transform:scale(.15) translate(-50%,-5%);transform:scale(.15) translate(-50%,-5%);opacity:0}.tippy-popper[x-placement^=bottom] .tippy-tooltip.light-theme [x-circle]{background-color:#fff}.tippy-popper[x-placement^=bottom] .tippy-tooltip.light-theme [x-arrow]{border-bottom:7px solid #fff;border-right:7px solid transparent;border-left:7px solid transparent}.tippy-popper[x-placement^=bottom] .tippy-tooltip.light-theme [x-arrow].arrow-small{border-bottom:5px solid #fff;border-right:5px solid transparent;border-left:5px solid transparent}.tippy-popper[x-placement^=bottom] .tippy-tooltip.light-theme [x-arrow].arrow-big{border-bottom:10px solid #fff;border-right:10px solid transparent;border-left:10px solid transparent}.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-circle]{background-color:rgba(0,0,0,.7)}.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow]{border-bottom:7px solid rgba(0,0,0,.7);border-right:7px solid transparent;border-left:7px solid transparent}.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow].arrow-small{border-bottom:5px solid rgba(0,0,0,.7);border-right:5px solid transparent;border-left:5px solid transparent}.tippy-popper[x-placement^=bottom] .tippy-tooltip.transparent-theme [x-arrow].arrow-big{border-bottom:10px solid rgba(0,0,0,.7);border-right:10px solid transparent;border-left:10px solid transparent}.tippy-popper[x-placement^=bottom] [data-animation=perspective]{-webkit-transform-origin:top;transform-origin:top}.tippy-popper[x-placement^=bottom] [data-animation=perspective].enter{opacity:1;-webkit-transform:translateY(10px) rotateX(0);transform:translateY(10px) rotateX(0)}.tippy-popper[x-placement^=bottom] [data-animation=perspective].leave{opacity:0;-webkit-transform:translateY(0) rotateX(-90deg);transform:translateY(0) rotateX(-90deg)}.tippy-popper[x-placement^=bottom] [data-animation=fade].enter{opacity:1;-webkit-transform:translateY(10px);transform:translateY(10px)}.tippy-popper[x-placement^=bottom] [data-animation=fade].leave{opacity:0;-webkit-transform:translateY(10px);transform:translateY(10px)}.tippy-popper[x-placement^=bottom] [data-animation=shift].enter{opacity:1;-webkit-transform:translateY(10px);transform:translateY(10px)}.tippy-popper[x-placement^=bottom] [data-animation=shift].leave{opacity:0;-webkit-transform:translateY(0);transform:translateY(0)}.tippy-popper[x-placement^=bottom] [data-animation=scale].enter{opacity:1;-webkit-transform:translateY(10px) scale(1);transform:translateY(10px) scale(1)}.tippy-popper[x-placement^=bottom] [data-animation=scale].leave{opacity:0;-webkit-transform:translateY(0) scale(0);transform:translateY(0) scale(0)}.tippy-popper[x-placement^=left] [x-arrow]{border-left:7px solid #333;border-top:7px solid transparent;border-bottom:7px solid transparent;right:-7px;margin:6px 0}.tippy-popper[x-placement^=left] [x-arrow].arrow-small{border-left:5px solid #333;border-top:5px solid transparent;border-bottom:5px solid transparent;right:-5px}.tippy-popper[x-placement^=left] [x-arrow].arrow-big{border-left:10px solid #333;border-top:10px solid transparent;border-bottom:10px solid transparent;right:-10px}.tippy-popper[x-placement^=left] [x-circle]{-webkit-transform-origin:50% 0;transform-origin:50% 0}.tippy-popper[x-placement^=left] [x-circle].enter{-webkit-transform:scale(1) translate(-50%,-50%);transform:scale(1) translate(-50%,-50%);opacity:1}.tippy-popper[x-placement^=left] [x-circle].leave{-webkit-transform:scale(.15) translate(-50%,-50%);transform:scale(.15) translate(-50%,-50%);opacity:0}.tippy-popper[x-placement^=left] .tippy-tooltip.light-theme [x-circle]{background-color:#fff}.tippy-popper[x-placement^=left] .tippy-tooltip.light-theme [x-arrow]{border-left:7px solid #fff;border-top:7px solid transparent;border-bottom:7px solid transparent}.tippy-popper[x-placement^=left] .tippy-tooltip.light-theme [x-arrow].arrow-small{border-left:5px solid #fff;border-top:5px solid transparent;border-bottom:5px solid transparent}.tippy-popper[x-placement^=left] .tippy-tooltip.light-theme [x-arrow].arrow-big{border-left:10px solid #fff;border-top:10px solid transparent;border-bottom:10px solid transparent}.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-circle]{background-color:rgba(0,0,0,.7)}.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow]{border-left:7px solid rgba(0,0,0,.7);border-top:7px solid transparent;border-bottom:7px solid transparent}.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow].arrow-small{border-left:5px solid rgba(0,0,0,.7);border-top:5px solid transparent;border-bottom:5px solid transparent}.tippy-popper[x-placement^=left] .tippy-tooltip.transparent-theme [x-arrow].arrow-big{border-left:10px solid rgba(0,0,0,.7);border-top:10px solid transparent;border-bottom:10px solid transparent}.tippy-popper[x-placement^=left] [data-animation=perspective]{-webkit-transform-origin:right;transform-origin:right}.tippy-popper[x-placement^=left] [data-animation=perspective].enter{opacity:1;-webkit-transform:translateX(-10px) rotateY(0);transform:translateX(-10px) rotateY(0)}.tippy-popper[x-placement^=left] [data-animation=perspective].leave{opacity:0;-webkit-transform:translateX(0) rotateY(-90deg);transform:translateX(0) rotateY(-90deg)}.tippy-popper[x-placement^=left] [data-animation=fade].enter{opacity:1;-webkit-transform:translateX(-10px);transform:translateX(-10px)}.tippy-popper[x-placement^=left] [data-animation=fade].leave{opacity:0;-webkit-transform:translateX(-10px);transform:translateX(-10px)}.tippy-popper[x-placement^=left] [data-animation=shift].enter{opacity:1;-webkit-transform:translateX(-10px);transform:translateX(-10px)}.tippy-popper[x-placement^=left] [data-animation=shift].leave{opacity:0;-webkit-transform:translateX(0);transform:translateX(0)}.tippy-popper[x-placement^=left] [data-animation=scale].enter{opacity:1;-webkit-transform:translateX(-10px) scale(1);transform:translateX(-10px) scale(1)}.tippy-popper[x-placement^=left] [data-animation=scale].leave{opacity:0;-webkit-transform:translateX(0) scale(0);transform:translateX(0) scale(0)}.tippy-popper[x-placement^=right] [x-arrow]{border-right:7px solid #333;border-top:7px solid transparent;border-bottom:7px solid transparent;left:-7px;margin:6px 0}.tippy-popper[x-placement^=right] [x-arrow].arrow-small{border-right:5px solid #333;border-top:5px solid transparent;border-bottom:5px solid transparent;left:-5px}.tippy-popper[x-placement^=right] [x-arrow].arrow-big{border-right:10px solid #333;border-top:10px solid transparent;border-bottom:10px solid transparent;left:-10px}.tippy-popper[x-placement^=right] [x-circle]{-webkit-transform-origin:-50% 0;transform-origin:-50% 0}.tippy-popper[x-placement^=right] [x-circle].enter{-webkit-transform:scale(1) translate(-50%,-50%);transform:scale(1) translate(-50%,-50%);opacity:1}.tippy-popper[x-placement^=right] [x-circle].leave{-webkit-transform:scale(.15) translate(-50%,-50%);transform:scale(.15) translate(-50%,-50%);opacity:0}.tippy-popper[x-placement^=right] .tippy-tooltip.light-theme [x-circle]{background-color:#fff}.tippy-popper[x-placement^=right] .tippy-tooltip.light-theme [x-arrow]{border-right:7px solid #fff;border-top:7px solid transparent;border-bottom:7px solid transparent}.tippy-popper[x-placement^=right] .tippy-tooltip.light-theme [x-arrow].arrow-small{border-right:5px solid #fff;border-top:5px solid transparent;border-bottom:5px solid transparent}.tippy-popper[x-placement^=right] .tippy-tooltip.light-theme [x-arrow].arrow-big{border-right:10px solid #fff;border-top:10px solid transparent;border-bottom:10px solid transparent}.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-circle]{background-color:rgba(0,0,0,.7)}.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow]{border-right:7px solid rgba(0,0,0,.7);border-top:7px solid transparent;border-bottom:7px solid transparent}.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-small{border-right:5px solid rgba(0,0,0,.7);border-top:5px solid transparent;border-bottom:5px solid transparent}.tippy-popper[x-placement^=right] .tippy-tooltip.transparent-theme [x-arrow].arrow-big{border-right:10px solid rgba(0,0,0,.7);border-top:10px solid transparent;border-bottom:10px solid transparent}.tippy-popper[x-placement^=right] [data-animation=perspective]{-webkit-transform-origin:left;transform-origin:left}.tippy-popper[x-placement^=right] [data-animation=perspective].enter{opacity:1;-webkit-transform:translateX(10px) rotateY(0);transform:translateX(10px) rotateY(0)}.tippy-popper[x-placement^=right] [data-animation=perspective].leave{opacity:0;-webkit-transform:translateX(0) rotateY(90deg);transform:translateX(0) rotateY(90deg)}.tippy-popper[x-placement^=right] [data-animation=fade].enter{opacity:1;-webkit-transform:translateX(10px);transform:translateX(10px)}.tippy-popper[x-placement^=right] [data-animation=fade].leave{opacity:0;-webkit-transform:translateX(10px);transform:translateX(10px)}.tippy-popper[x-placement^=right] [data-animation=shift].enter{opacity:1;-webkit-transform:translateX(10px);transform:translateX(10px)}.tippy-popper[x-placement^=right] [data-animation=shift].leave{opacity:0;-webkit-transform:translateX(0);transform:translateX(0)}.tippy-popper[x-placement^=right] [data-animation=scale].enter{opacity:1;-webkit-transform:translateX(10px) scale(1);transform:translateX(10px) scale(1)}.tippy-popper[x-placement^=right] [data-animation=scale].leave{opacity:0;-webkit-transform:translateX(0) scale(0);transform:translateX(0) scale(0)}.tippy-popper .tippy-tooltip.transparent-theme{background-color:rgba(0,0,0,.7)}.tippy-popper .tippy-tooltip.transparent-theme[data-animatefill]{background-color:transparent}.tippy-popper .tippy-tooltip.light-theme{color:#26323d;box-shadow:0 4px 20px 4px rgba(0,20,60,.1),0 4px 80px -8px rgba(0,20,60,.2);background-color:#fff}.tippy-popper .tippy-tooltip.light-theme[data-animatefill]{background-color:transparent}.tippy-tooltip{position:relative;color:#fff;border-radius:4px;font-size:.95rem;padding:.4rem .8rem;text-align:center;will-change:transform;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#333}.tippy-tooltip--small{padding:.25rem .5rem;font-size:.8rem}.tippy-tooltip--big{padding:.6rem 1.2rem;font-size:1.2rem}.tippy-tooltip[data-animatefill]{overflow:hidden;background-color:transparent}.tippy-tooltip[data-interactive]{pointer-events:auto}.tippy-tooltip[data-inertia]{transition-timing-function:cubic-bezier(.53,2,.36,.85)}.tippy-tooltip [x-arrow]{position:absolute;width:0;height:0}.tippy-tooltip [x-circle]{position:absolute;will-change:transform;background-color:#333;border-radius:50%;width:130%;width:calc(110% + 2rem);left:50%;top:50%;z-index:-1;overflow:hidden;transition:all ease}.tippy-tooltip [x-circle]:before{content:"";padding-top:90%;float:left}@media (max-width:450px){.tippy-popper{max-width:96%;max-width:calc(100% - 20px)}} diff --git a/ui/app/info.js b/ui/app/info.js index 49ff9f24a..92cc80c20 100644 --- a/ui/app/info.js +++ b/ui/app/info.js @@ -15,134 +15,91 @@ function InfoScreen () { Component.call(this) } -InfoScreen.prototype.render = function () { - const state = this.props - const version = global.platform.getVersion() - +InfoScreen.prototype.renderLogo = function () { return ( - h('.flex-column.flex-grow', { - style: { - maxWidth: '400px', - }, - }, [ + h('div.settings__info-logo-wrapper', [ + h('img.settings__info-logo', { src: 'images/info-logo.png' }), + ]) + ) +} - // subtitle and nav - h('.section-title.flex-row.flex-center', [ - h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', { - onClick: (event) => { - state.dispatch(actions.goHome()) - }, - }), - h('h2.page-subtitle', 'Info'), +InfoScreen.prototype.renderInfoLinks = function () { + return ( + h('div.settings__content-item.settings__content-item--without-height', [ + h('div.settings__info-link-header', 'Links'), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/privacy.html', + target: '_blank', + }, [ + h('span.settings__info-link', 'Privacy Policy'), + ]), ]), - - // main view - h('.flex-column.flex-justify-center.flex-grow.select-none', [ - h('.flex-space-around', { - style: { - padding: '20px', - }, + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/terms.html', + target: '_blank', }, [ - // current version number - - h('.info.info-gray', [ - h('div', 'Metamask'), - h('div', { - style: { - marginBottom: '10px', - }, - }, `Version: ${version}`), - ]), - - h('div', { - style: { - marginBottom: '5px', - }}, - [ - h('div', [ - h('a', { - href: 'https://metamask.io/privacy.html', - target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, - }, [ - h('div.info', 'Privacy Policy'), - ]), - ]), - h('div', [ - h('a', { - href: 'https://metamask.io/terms.html', - target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, - }, [ - h('div.info', 'Terms of Use'), - ]), - ]), - h('div', [ - h('a', { - href: 'https://metamask.io/attributions.html', - target: '_blank', - onClick (event) { this.navigateTo(event.target.href) }, - }, [ - h('div.info', 'Attributions'), - ]), - ]), - ] - ), - - h('hr', { - style: { - margin: '10px 0 ', - width: '7em', - }, - }), - - h('div', { - style: { - paddingLeft: '30px', - }}, - [ - h('div.fa.fa-support', [ - h('a.info', { - href: 'https://metamask.helpscoutdocs.com/', - target: '_blank', - }, 'Visit our Knowledge Base'), - ]), - - h('div', [ - h('a', { - href: 'https://metamask.io/', - target: '_blank', - }, [ - h('img.icon-size', { - src: 'images/icon-128.png', - style: { - // IE6-9 - filter: 'grayscale(100%)', - // Microsoft Edge and Firefox 35+ - WebkitFilter: 'grayscale(100%)', - }, - }), - h('div.info', 'Visit our web site'), - ]), - ]), + h('span.settings__info-link', 'Terms of Use'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/attributions.html', + target: '_blank', + }, [ + h('span.settings__info-link', 'Attributions'), + ]), + ]), + h('hr.settings__info-separator'), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://support.metamask.io', + target: '_blank', + }, [ + h('span.settings__info-link', 'Visit our Support Center'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + href: 'https://metamask.io/', + target: '_blank', + }, [ + h('span.settings__info-link', 'Visit our web site'), + ]), + ]), + h('div.settings__info-link-item', [ + h('a', { + target: '_blank', + href: 'mailto:help@metamask.io?subject=Feedback', + }, [ + h('span.settings__info-link', 'Email us!'), + ]), + ]), + ]) + ) +} - h('div', [ - h('.fa.fa-twitter', [ - h('a.info', { - href: 'https://twitter.com/metamask_io', - target: '_blank', - }, 'Follow us on Twitter'), - ]), - ]), +InfoScreen.prototype.render = function () { + const version = global.platform.getVersion() - h('div.fa.fa-envelope', [ - h('a.info', { - target: '_blank', - href: 'mailto:support@metamask.io?subject=MetaMask Support', - }, 'Email us!'), - ]), - ]), + return ( + h('div.settings__content', [ + h('div.settings__content-row', [ + h('div.settings__content-item.settings__content-item--without-height', [ + this.renderLogo(), + h('div.settings__info-item', [ + h('div.settings__info-version-header', 'MetaMask Version'), + h('div.settings__info-version-number', `${version}`), + ]), + h('div.settings__info-item', [ + h( + 'div.settings__info-about', + 'MetaMask is designed and built in California.' + ), + ]), ]), + this.renderInfoLinks(), ]), ]) ) diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index c3ade5cdc..6885d029a 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -484,6 +484,11 @@ function reduceApp (state, action) { warning: action.value || 'Incorrect password. Try again.', }) + case actions.UNLOCK_SUCCEEDED: + return extend(appState, { + warning: '', + }) + case actions.SHOW_LOADING: return extend(appState, { isLoading: true, diff --git a/yarn.lock b/yarn.lock index dc01bc5f7..78bf2ac58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2503,6 +2503,24 @@ component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" +compressible@~2.0.11: + version "2.0.12" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66" + dependencies: + mime-db ">= 1.30.0 < 2" + +compression@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db" + dependencies: + accepts "~1.3.4" + bytes "3.0.0" + compressible "~2.0.11" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.1" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3805,7 +3823,18 @@ eth-block-tracker@^1.0.7: pify "^2.3.0" tape "^4.6.3" -eth-block-tracker@^2.1.2, eth-block-tracker@^2.2.0, eth-block-tracker@^2.2.2: +eth-block-tracker@^2.1.2, eth-block-tracker@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.2.2.tgz#b3d72cd82ba5ee37471d22bac4f56387ee4137cf" + dependencies: + async-eventemitter ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c + babelify "^7.3.0" + eth-query "^2.1.0" + ethjs-util "^0.1.3" + pify "^2.3.0" + tape "^4.6.3" + +eth-block-tracker@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-2.3.0.tgz#4cb782c8ef8fde2f5dc894921ae1f5c1077c35a4" dependencies: @@ -3849,7 +3878,7 @@ eth-json-rpc-filters@^1.2.5: json-rpc-engine "^3.4.0" lodash.flatmap "^4.5.0" -eth-json-rpc-infura@^2.0.5: +eth-json-rpc-infura@^2.0.11: version "2.0.11" resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-2.0.11.tgz#134bf54ff15e96a9116424c0db9b66aa079bfbbe" dependencies: @@ -6379,16 +6408,27 @@ json-parse-better-errors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" -json-rpc-engine@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.2.0.tgz#d34dff106c8339c337a894da801f73b1f77b1bc8" +json-rpc-engine@^3.0.1, json-rpc-engine@^3.1.0, json-rpc-engine@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.4.0.tgz#8a1647a7f2cc7018f4802f41ec8208d281f78bfc" + dependencies: + async "^2.0.1" + babel-preset-env "^1.3.2" + babelify "^7.3.0" + json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" + +json-rpc-engine@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.6.0.tgz#0cc673dcb4b71103523fec81d1bba195a457f993" dependencies: async "^2.0.1" babel-preset-env "^1.3.2" babelify "^7.3.0" json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" -json-rpc-engine@^3.0.1, json-rpc-engine@^3.1.0, json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: +json-rpc-engine@^3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.6.1.tgz#f53084726dc6dedeead0e2c457eeb997135f1e25" dependencies: @@ -7341,6 +7381,10 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" +"mime-db@>= 1.30.0 < 2": + version "1.32.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.32.0.tgz#485b3848b01a3cda5f968b4882c0771e58e09414" + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" @@ -8040,6 +8084,10 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -8479,6 +8527,10 @@ polyfill-crypto.getrandomvalues@^1.0.0: dependencies: mersenne-twister "^1.0.1" +popper.js@^1.11.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.13.0.tgz#e1e7ff65cc43f7cf9cf16f1510a75e81f84f4565" + portfinder@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-0.2.1.tgz#b2b9b0164f9e17fa3a9c7db2304d0a75140c71ad" @@ -9040,6 +9092,12 @@ react-testutils-additions@^15.2.0: object-assign "3.0.0" sizzle "2.3.3" +react-tippy@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/react-tippy/-/react-tippy-1.2.2.tgz#061467d34d29e7a5a9421822d125c451d6bb5153" + dependencies: + popper.js "^1.11.1" + react-toggle-button@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/react-toggle-button/-/react-toggle-button-2.2.0.tgz#a1b92143aa0df414642fcb141f0879f545bc5a89" @@ -11553,7 +11611,31 @@ weak@^1.0.0: bindings "^1.2.1" nan "^2.0.5" -web3-provider-engine@^13.3.2, web3-provider-engine@^13.5.0: +web3-provider-engine@^13.3.2: + version "13.4.0" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.4.0.tgz#78c2794ba926d0c5b94c6e8955abb994bb8e8854" + dependencies: + async "^2.5.0" + clone "^2.0.0" + eth-block-tracker "^2.2.2" + eth-sig-util "^1.3.0" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.1" + ethereumjs-vm "^2.0.2" + fetch-ponyfill "^4.0.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.67.0" + semaphore "^1.0.3" + solc "^0.4.2" + tape "^4.4.0" + xhr "^2.2.0" + xtend "^4.0.1" + +web3-provider-engine@^13.5.6: version "13.6.0" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.6.0.tgz#836f51c4ee48bd7583acf3696033779c704c2214" dependencies: