Merge pull request #364 from poanetwork/vb-fix-notifications-order
Fix notifications order in batch requests
This commit is contained in:
commit
502b69d5f4
|
@ -2,6 +2,8 @@
|
|||
|
||||
## Current Master
|
||||
|
||||
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - Fix notifications order in batch requests
|
||||
|
||||
## 5.0.3 Fri May 01 2020
|
||||
|
||||
- [#373](https://github.com/poanetwork/nifty-wallet/pull/373) - (Feature) Add STAKE token
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
// this needs to run before anything else
|
||||
require('./lib/setupFetchDebugging')()
|
||||
|
||||
const endOfStream = require('end-of-stream')
|
||||
const pump = require('pump')
|
||||
const debounce = require('debounce-stream')
|
||||
const log = require('loglevel')
|
||||
const extension = require('extensionizer')
|
||||
import endOfStream from 'end-of-stream'
|
||||
import pump from 'pump'
|
||||
import debounce from 'debounce-stream'
|
||||
import log from 'loglevel'
|
||||
import extension from 'extensionizer'
|
||||
const LocalStorageStore = require('obs-store/lib/localStorage')
|
||||
const LocalStore = require('./lib/local-store')
|
||||
const storeTransform = require('obs-store/lib/transform')
|
||||
|
@ -19,13 +19,11 @@ const Migrator = require('./lib/migrator/')
|
|||
const migrations = require('./migrations/')
|
||||
const PortStream = require('extension-port-stream')
|
||||
const createStreamSink = require('./lib/createStreamSink')
|
||||
const NotificationManager = require('./lib/notification-manager.js')
|
||||
import NotificationManager from './lib/notification-manager.js'
|
||||
const MetamaskController = require('./metamask-controller')
|
||||
const rawFirstTimeState = require('./first-time-state')
|
||||
const setupRaven = require('./lib/setupRaven')
|
||||
const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
|
||||
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
|
||||
const EdgeEncryptor = require('./edge-encryptor')
|
||||
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
|
||||
const getObjStructure = require('./lib/getObjStructure')
|
||||
|
||||
|
@ -51,12 +49,6 @@ global.METAMASK_NOTIFIER = notificationManager
|
|||
const release = platform.getVersion()
|
||||
const raven = setupRaven({ release })
|
||||
|
||||
// browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
|
||||
// Internet Explorer 6-11
|
||||
const isIE = !!document.documentMode
|
||||
// Edge 20+
|
||||
const isEdge = !isIE && !!window.StyleMedia
|
||||
|
||||
let popupIsOpen = false
|
||||
let notificationIsOpen = false
|
||||
const openMetamaskTabsIDs = {}
|
||||
|
@ -70,9 +62,6 @@ let versionedData
|
|||
// initialization flow
|
||||
initialize().catch(log.error)
|
||||
|
||||
// setup metamask mesh testing container
|
||||
setupMetamaskMeshMetrics()
|
||||
|
||||
|
||||
/**
|
||||
* An object representing a transaction, in whatever state it is in.
|
||||
|
@ -177,6 +166,7 @@ async function initialize () {
|
|||
async function loadStateFromPersistence () {
|
||||
// migrations
|
||||
const migrator = new Migrator({ migrations })
|
||||
migrator.on('error', console.warn)
|
||||
|
||||
// read from disk
|
||||
// first from preferred, async API:
|
||||
|
@ -256,7 +246,7 @@ function setupController (initState, initLangCode) {
|
|||
showUnconfirmedMessage: triggerUi,
|
||||
unlockAccountMessage: triggerUi,
|
||||
showUnapprovedTx: triggerUi,
|
||||
showWatchAssetUi: showWatchAssetUi,
|
||||
openPopup: openPopup,
|
||||
// initial state
|
||||
initState,
|
||||
// initial locale code
|
||||
|
@ -269,7 +259,6 @@ function setupController (initState, initLangCode) {
|
|||
getOpenMetamaskTabsIds: () => {
|
||||
return openMetamaskTabsIDs
|
||||
},
|
||||
encryptor: isEdge ? new EdgeEncryptor() : undefined,
|
||||
})
|
||||
global.metamaskController = controller
|
||||
|
||||
|
@ -460,28 +449,27 @@ function setupController (initState, initLangCode) {
|
|||
/**
|
||||
* Opens the browser popup for user confirmation
|
||||
*/
|
||||
function triggerUi () {
|
||||
extension.tabs.query({ active: true }, tabs => {
|
||||
const currentlyActiveMetamaskTab = Boolean(tabs.find(tab => openMetamaskTabsIDs[tab.id]))
|
||||
/**
|
||||
* https://github.com/poanetwork/metamask-extension/issues/19
|
||||
* !notificationIsOpen was removed from the check, because notification can be opened, but it can be behind the DApp
|
||||
* for some reasons. For example, if notification popup was opened, but user moved focus to DApp.
|
||||
* New transaction, in this case, will not appear in front of DApp.
|
||||
*/
|
||||
if (!popupIsOpen && !currentlyActiveMetamaskTab) {
|
||||
notificationManager.showPopup()
|
||||
}
|
||||
})
|
||||
async function triggerUi () {
|
||||
const tabs = await platform.getActiveTabs()
|
||||
const currentlyActiveMetamaskTab = Boolean(tabs.find((tab) => openMetamaskTabsIDs[tab.id]))
|
||||
/**
|
||||
* https://github.com/poanetwork/metamask-extension/issues/19
|
||||
* !notificationIsOpen was removed from the check, because notification can be opened, but it can be behind the DApp
|
||||
* for some reasons. For example, if notification popup was opened, but user moved focus to DApp.
|
||||
* New transaction, in this case, will not appear in front of DApp.
|
||||
*/
|
||||
if (!popupIsOpen && !currentlyActiveMetamaskTab) {
|
||||
await notificationManager.showPopup()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the browser popup for user confirmation of watchAsset
|
||||
* then it waits until user interact with the UI
|
||||
*/
|
||||
function showWatchAssetUi () {
|
||||
triggerUi()
|
||||
return new Promise(
|
||||
async function openPopup () {
|
||||
await triggerUi()
|
||||
await new Promise(
|
||||
(resolve) => {
|
||||
const interval = setInterval(() => {
|
||||
if (!notificationIsOpen) {
|
||||
|
|
|
@ -47,7 +47,7 @@ class PreferencesController {
|
|||
this.diagnostics = opts.diagnostics
|
||||
this.network = opts.network
|
||||
this.store = new ObservableStore(initState)
|
||||
this.showWatchAssetUi = opts.showWatchAssetUi
|
||||
this.openPopup = opts.openPopup
|
||||
this._subscribeProviderType()
|
||||
}
|
||||
// PUBLIC METHODS
|
||||
|
@ -591,7 +591,7 @@ class PreferencesController {
|
|||
}
|
||||
const tokenOpts = { rawAddress, decimals, symbol, image }
|
||||
this.addSuggestedERC20Asset(tokenOpts)
|
||||
return this.showWatchAssetUi().then(() => {
|
||||
return this.openPopup().then(() => {
|
||||
const tokenAddresses = this.getTokens().filter(token => token.address === normalizeAddress(rawAddress))
|
||||
return tokenAddresses.length > 0
|
||||
})
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
const asmcrypto = require('asmcrypto.js')
|
||||
const Unibabel = require('browserify-unibabel')
|
||||
|
||||
/**
|
||||
* A Microsoft Edge-specific encryption class that exposes
|
||||
* the interface expected by eth-keykeyring-controller
|
||||
*/
|
||||
class EdgeEncryptor {
|
||||
/**
|
||||
* Encrypts an arbitrary object to ciphertext
|
||||
*
|
||||
* @param {string} password Used to generate a key to encrypt the data
|
||||
* @param {Object} dataObject Data to encrypt
|
||||
* @returns {Promise<string>} Promise resolving to an object with ciphertext
|
||||
*/
|
||||
encrypt (password, dataObject) {
|
||||
const salt = this._generateSalt()
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
const data = JSON.stringify(dataObject)
|
||||
const dataBuffer = Unibabel.utf8ToBuffer(data)
|
||||
const vector = global.crypto.getRandomValues(new Uint8Array(16))
|
||||
const resultbuffer = asmcrypto.AES_GCM.encrypt(dataBuffer, key, vector)
|
||||
|
||||
const buffer = new Uint8Array(resultbuffer)
|
||||
const vectorStr = Unibabel.bufferToBase64(vector)
|
||||
const vaultStr = Unibabel.bufferToBase64(buffer)
|
||||
return JSON.stringify({
|
||||
data: vaultStr,
|
||||
iv: vectorStr,
|
||||
salt: salt,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts an arbitrary object from ciphertext
|
||||
*
|
||||
* @param {string} password Used to generate a key to decrypt the data
|
||||
* @param {string} text Ciphertext of an encrypted object
|
||||
* @returns {Promise<Object>} Promise resolving to copy of decrypted object
|
||||
*/
|
||||
decrypt (password, text) {
|
||||
const payload = JSON.parse(text)
|
||||
const salt = payload.salt
|
||||
return this._keyFromPassword(password, salt)
|
||||
.then(function (key) {
|
||||
const encryptedData = Unibabel.base64ToBuffer(payload.data)
|
||||
const vector = Unibabel.base64ToBuffer(payload.iv)
|
||||
return new Promise((resolve, reject) => {
|
||||
let result
|
||||
try {
|
||||
result = asmcrypto.AES_GCM.decrypt(encryptedData, key, vector)
|
||||
} catch (err) {
|
||||
return reject(new Error('Incorrect password'))
|
||||
}
|
||||
const decryptedData = new Uint8Array(result)
|
||||
const decryptedStr = Unibabel.bufferToUtf8(decryptedData)
|
||||
const decryptedObj = JSON.parse(decryptedStr)
|
||||
resolve(decryptedObj)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a cryptographic key using a password
|
||||
*
|
||||
* @private
|
||||
* @param {string} password Password used to unlock a cryptographic key
|
||||
* @param {string} salt Random base64 data
|
||||
* @returns {Promise<Object>} Promise resolving to a derived key
|
||||
*/
|
||||
_keyFromPassword (password, salt) {
|
||||
|
||||
const passBuffer = Unibabel.utf8ToBuffer(password)
|
||||
const saltBuffer = Unibabel.base64ToBuffer(salt)
|
||||
return new Promise((resolve) => {
|
||||
const key = asmcrypto.PBKDF2_HMAC_SHA256.bytes(passBuffer, saltBuffer, 10000)
|
||||
resolve(key)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random base64 encoded data
|
||||
*
|
||||
* @private
|
||||
* @returns {string} Randomized base64 encoded data
|
||||
*/
|
||||
_generateSalt (byteCount = 32) {
|
||||
const view = new Uint8Array(byteCount)
|
||||
global.crypto.getRandomValues(view)
|
||||
const b64encoded = btoa(String.fromCharCode.apply(null, view))
|
||||
return b64encoded
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EdgeEncryptor
|
|
@ -1,7 +1,7 @@
|
|||
const extension = require('extensionizer')
|
||||
const height = 620
|
||||
const width = 360
|
||||
import ExtensionPlatform from '../platforms/extension'
|
||||
|
||||
const NOTIFICATION_HEIGHT = 620
|
||||
const NOTIFICATION_WIDTH = 360
|
||||
|
||||
class NotificationManager {
|
||||
|
||||
|
@ -12,47 +12,45 @@ class NotificationManager {
|
|||
*
|
||||
*/
|
||||
|
||||
constructor () {
|
||||
this.platform = new ExtensionPlatform()
|
||||
}
|
||||
|
||||
/**
|
||||
* Either brings an existing MetaMask notification window into focus, or creates a new notification window. New
|
||||
* notification windows are given a 'popup' type.
|
||||
*
|
||||
*/
|
||||
showPopup () {
|
||||
this._getPopup((err, popup) => {
|
||||
if (err) throw err
|
||||
async showPopup () {
|
||||
const popup = await this._getPopup()
|
||||
|
||||
// Bring focus to chrome popup
|
||||
if (popup) {
|
||||
// bring focus to existing chrome popup
|
||||
extension.windows.update(popup.id, { focused: true })
|
||||
} else {
|
||||
const cb = (currentPopup) => {
|
||||
this._popupId = currentPopup.id
|
||||
extension.windows.update(currentPopup.id, { focused: true })
|
||||
}
|
||||
// create new notification popup
|
||||
const creation = extension.windows.create({
|
||||
url: 'notification.html',
|
||||
type: 'popup',
|
||||
width,
|
||||
height,
|
||||
}, cb)
|
||||
creation && creation.then && creation.then(cb)
|
||||
}
|
||||
})
|
||||
// Bring focus to chrome popup
|
||||
if (popup) {
|
||||
// bring focus to existing chrome popup
|
||||
await this.platform.focusWindow(popup.id)
|
||||
} else {
|
||||
|
||||
// create new notification popup
|
||||
const popupWindow = await this.platform.openWindow({
|
||||
url: 'notification.html',
|
||||
type: 'popup',
|
||||
width: NOTIFICATION_WIDTH,
|
||||
height: NOTIFICATION_HEIGHT,
|
||||
})
|
||||
this._popupId = popupWindow.id
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a MetaMask notification if it window exists.
|
||||
*
|
||||
*/
|
||||
closePopup () {
|
||||
// closes notification popup
|
||||
this._getPopup((err, popup) => {
|
||||
if (err) throw err
|
||||
if (!popup) return
|
||||
extension.windows.remove(popup.id, console.error)
|
||||
})
|
||||
async closePopup () {
|
||||
const popup = this._getPopup()
|
||||
if (!popup) {
|
||||
return
|
||||
}
|
||||
await this.platform.removeWindow(popup.id)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,39 +58,19 @@ class NotificationManager {
|
|||
* type 'popup')
|
||||
*
|
||||
* @private
|
||||
* @param {Function} cb A node style callback that to whcih the found notification window will be passed.
|
||||
* @param {Function} cb - A node style callback that to whcih the found notification window will be passed.
|
||||
*
|
||||
*/
|
||||
_getPopup (cb) {
|
||||
this._getWindows((err, windows) => {
|
||||
if (err) throw err
|
||||
cb(null, this._getPopupIn(windows))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all open MetaMask windows.
|
||||
*
|
||||
* @private
|
||||
* @param {Function} cb A node style callback that to which the windows will be passed.
|
||||
*
|
||||
*/
|
||||
_getWindows (cb) {
|
||||
// Ignore in test environment
|
||||
if (!extension.windows) {
|
||||
return cb()
|
||||
}
|
||||
|
||||
extension.windows.getAll({}, (windows) => {
|
||||
cb(null, windows)
|
||||
})
|
||||
async _getPopup () {
|
||||
const windows = await this.platform.getAllWindows()
|
||||
return this._getPopupIn(windows)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of windows, returns the 'popup' that has been opened by MetaMask, or null if no such window exists.
|
||||
*
|
||||
* @private
|
||||
* @param {array} windows An array of objects containing data about the open MetaMask extension windows.
|
||||
* @param {array} windows - An array of objects containing data about the open MetaMask extension windows.
|
||||
*
|
||||
*/
|
||||
_getPopupIn (windows) {
|
||||
|
@ -104,4 +82,4 @@ class NotificationManager {
|
|||
|
||||
}
|
||||
|
||||
module.exports = NotificationManager
|
||||
export default NotificationManager
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
module.exports = setupMetamaskMeshMetrics
|
||||
|
||||
/**
|
||||
* Injects an iframe into the current document for testing
|
||||
*/
|
||||
function setupMetamaskMeshMetrics () {
|
||||
const testingContainer = document.createElement('iframe')
|
||||
testingContainer.src = 'https://metamask.github.io/mesh-testing/'
|
||||
console.log('Injecting Nifty Wallet Mesh testing client')
|
||||
document.head.appendChild(testingContainer)
|
||||
}
|
|
@ -6,6 +6,7 @@ const {
|
|||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_BACKGROUND,
|
||||
PLATFORM_FIREFOX,
|
||||
PLATFORM_OPERA,
|
||||
PLATFORM_CHROME,
|
||||
|
@ -13,17 +14,6 @@ const {
|
|||
PLATFORM_BRAVE,
|
||||
} = require('./enums')
|
||||
|
||||
/**
|
||||
* Generates an example stack trace
|
||||
*
|
||||
* @returns {string} A stack trace
|
||||
*
|
||||
*/
|
||||
function getStack () {
|
||||
const stack = new Error('Stack trace generator - not an error').stack
|
||||
return stack
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine the window type through which the app is being viewed.
|
||||
* - 'popup' refers to the extension opened through the browser app icon (in top right corner in chrome and firefox)
|
||||
|
@ -34,12 +24,15 @@ function getStack () {
|
|||
*
|
||||
*/
|
||||
const getEnvironmentType = (url = window.location.href) => {
|
||||
if (url.match(/popup.html(?:#.*)*$/)) {
|
||||
const parsedUrl = new URL(url)
|
||||
if (parsedUrl.pathname === '/popup.html') {
|
||||
return ENVIRONMENT_TYPE_POPUP
|
||||
} else if (url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) {
|
||||
} else if (['/home.html', '/phishing.html'].includes(parsedUrl.pathname)) {
|
||||
return ENVIRONMENT_TYPE_FULLSCREEN
|
||||
} else {
|
||||
} else if (parsedUrl.pathname === '/notification.html') {
|
||||
return ENVIRONMENT_TYPE_NOTIFICATION
|
||||
} else {
|
||||
return ENVIRONMENT_TYPE_BACKGROUND
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +168,6 @@ module.exports = {
|
|||
removeListeners,
|
||||
applyListeners,
|
||||
getPlatform,
|
||||
getStack,
|
||||
getEnvironmentType,
|
||||
sufficientBalance,
|
||||
hexToBn,
|
||||
|
|
|
@ -111,7 +111,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
this.preferencesController = new PreferencesController({
|
||||
initState: initState.PreferencesController,
|
||||
initLangCode: opts.initLangCode,
|
||||
showWatchAssetUi: opts.showWatchAssetUi,
|
||||
openPopup: opts.openPopup,
|
||||
network: this.networkController,
|
||||
})
|
||||
|
||||
|
@ -1945,7 +1945,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
.sort((block1, block2) => block1.number - block2.number)[recentBlocks.length - 1]
|
||||
|
||||
const gasPrice = recentBlock && recentBlock.minimumGasPrice && recentBlock.minimumGasPrice.toString()
|
||||
|
||||
|
||||
if (gasPrice !== '0x' && gasPrice !== '0x0' && gasPrice !== '') {
|
||||
return gasPrice
|
||||
} else {
|
||||
|
|
|
@ -12,8 +12,64 @@ class ExtensionPlatform {
|
|||
extension.runtime.reload()
|
||||
}
|
||||
|
||||
openWindow ({ url }) {
|
||||
extension.tabs.create({ url })
|
||||
openTab (options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.create(options, (newTab) => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve(newTab)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
openWindow (options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.windows.create(options, (newWindow) => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve(newWindow)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
closeWindow (windowId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.windows.remove(windowId, () => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
focusWindow (windowId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.windows.update(windowId, { focused: true }, () => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getLastFocusedWindow () {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.windows.getLastFocused((windowObject) => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve(windowObject)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
closeCurrentWindow () {
|
||||
|
@ -22,27 +78,6 @@ class ExtensionPlatform {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all notifications windows, when action is confirmed in popup
|
||||
* or closes notification window itself, when action is confirmed from it
|
||||
*/
|
||||
closeNotificationWindow () {
|
||||
return extension.windows.getCurrent((curWindowsDetails) => {
|
||||
if (curWindowsDetails.type === 'popup') {
|
||||
return extension.windows.remove(curWindowsDetails.id)
|
||||
} else {
|
||||
extension.windows.getAll((windowsDetails) => {
|
||||
const windowsDetailsFiltered = windowsDetails.filter((windowDetails) => windowDetails.id !== curWindowsDetails.id)
|
||||
return windowsDetailsFiltered.forEach((windowDetails) => {
|
||||
if (windowDetails.type === 'popup') {
|
||||
extension.windows.remove(windowDetails.id)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getVersion () {
|
||||
return extension.runtime.getManifest().version
|
||||
}
|
||||
|
@ -57,7 +92,7 @@ class ExtensionPlatform {
|
|||
if (route) {
|
||||
extensionURL += `#${route}`
|
||||
}
|
||||
this.openWindow({ url: extensionURL })
|
||||
this.openTab({ url: extensionURL })
|
||||
if (getEnvironmentType() !== ENVIRONMENT_TYPE_BACKGROUND) {
|
||||
window.close()
|
||||
}
|
||||
|
@ -86,6 +121,30 @@ class ExtensionPlatform {
|
|||
}
|
||||
}
|
||||
|
||||
getAllWindows () {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.windows.getAll((windows) => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve(windows)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getActiveTabs () {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.query({ active: true }, (tabs) => {
|
||||
const error = checkForError()
|
||||
if (error) {
|
||||
return reject(error)
|
||||
}
|
||||
return resolve(tabs)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
currentTab () {
|
||||
return new Promise((resolve, reject) => {
|
||||
extension.tabs.getCurrent((tab) => {
|
||||
|
|
|
@ -3,11 +3,8 @@ const OldMetaMaskUiCss = require('../../old-ui/css')
|
|||
const startPopup = require('./popup-core')
|
||||
const PortStream = require('extension-port-stream')
|
||||
const { getEnvironmentType } = require('./lib/util')
|
||||
const { ENVIRONMENT_TYPE_NOTIFICATION } = require('./lib/enums')
|
||||
import extension from 'extensionizer'
|
||||
const ExtensionPlatform = require('./platforms/extension')
|
||||
const NotificationManager = require('./lib/notification-manager')
|
||||
const notificationManager = new NotificationManager()
|
||||
const setupRaven = require('./lib/setupRaven')
|
||||
const log = require('loglevel')
|
||||
|
||||
|
@ -29,7 +26,6 @@ async function start () {
|
|||
// identify window type (popup, notification)
|
||||
const windowType = getEnvironmentType(window.location.href)
|
||||
global.METAMASK_UI_TYPE = windowType
|
||||
closePopupIfOpen(windowType)
|
||||
|
||||
// setup stream to background
|
||||
const extensionPort = extension.runtime.connect({ name: windowType })
|
||||
|
@ -51,13 +47,6 @@ async function start () {
|
|||
})
|
||||
|
||||
|
||||
function closePopupIfOpen (windowType) {
|
||||
if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||
// should close only chrome popup
|
||||
notificationManager.closePopup()
|
||||
}
|
||||
}
|
||||
|
||||
function displayCriticalError (err) {
|
||||
container.innerHTML = '<div class="critical-error">The Nifty Wallet app failed to load: please open and close Nifty Wallet again to restart.</div>'
|
||||
container.style.height = '80px'
|
||||
|
|
|
@ -74,7 +74,7 @@ class ConnectScreen extends Component {
|
|||
<Button
|
||||
type="primary"
|
||||
large={true}
|
||||
onClick={() => global.platform.openWindow({
|
||||
onClick={() => global.platform.openTab({
|
||||
url: 'https://google.com/chrome',
|
||||
})}
|
||||
>Download Google Chrome</Button>
|
||||
|
|
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types'
|
|||
import clone from 'clone'
|
||||
import log from 'loglevel'
|
||||
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
const BN = ethUtil.BN
|
||||
const hexToBn = require('../../../app/scripts/lib/hex-to-bn')
|
||||
const util = require('../util')
|
||||
|
@ -47,12 +47,12 @@ class PendingTx extends Component {
|
|||
isUnlocked: PropTypes.bool,
|
||||
currentCurrency: PropTypes.string,
|
||||
conversionRate: PropTypes.number,
|
||||
unconfTxListLength: PropTypes.number,
|
||||
provider: PropTypes.object,
|
||||
index: PropTypes.number,
|
||||
blockGasLimit: PropTypes.string,
|
||||
tokensToSend: PropTypes.objectOf(BigNumber),
|
||||
tokensTransferTo: PropTypes.string,
|
||||
unapprovedTxs: PropTypes.object,
|
||||
}
|
||||
|
||||
constructor (opts = {}) {
|
||||
|
@ -132,12 +132,13 @@ class PendingTx extends Component {
|
|||
|
||||
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
|
||||
|
||||
const { totalTx, positionOfCurrentTx, nextTxId, prevTxId, showNavigation } = this.getNavigateTxData()
|
||||
|
||||
const balanceBn = hexToBn(balance)
|
||||
const insufficientBalance = balanceBn.lt(maxCost)
|
||||
const dangerousGasLimit = gasBn.gte(saferGasLimitBN)
|
||||
const gasLimitSpecified = txMeta.gasLimitSpecified
|
||||
const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting
|
||||
const showRejectAll = props.unconfTxListLength > 1
|
||||
|
||||
const isNotification = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION
|
||||
|
||||
|
@ -158,7 +159,6 @@ class PendingTx extends Component {
|
|||
}
|
||||
|
||||
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
|
||||
|
||||
return (
|
||||
|
||||
h('div', {
|
||||
|
@ -179,7 +179,7 @@ class PendingTx extends Component {
|
|||
h('.flex-row.flex-center', {
|
||||
style: {
|
||||
maxWidth: '100%',
|
||||
padding: showRejectAll ? '20px 20px 50px 20px' : '20px 20px 20px 20px',
|
||||
padding: showNavigation ? '20px 20px 50px 20px' : '20px 20px 20px 20px',
|
||||
background: 'linear-gradient(rgb(84, 36, 147), rgb(104, 45, 182))',
|
||||
position: 'relative',
|
||||
},
|
||||
|
@ -197,22 +197,22 @@ class PendingTx extends Component {
|
|||
h('h3', {
|
||||
style: {
|
||||
alignSelf: 'center',
|
||||
display: props.unconfTxListLength > 1 ? 'block' : 'none',
|
||||
display: showNavigation ? 'block' : 'none',
|
||||
fontSize: '14px',
|
||||
},
|
||||
}, [
|
||||
h('i.fa.white-arrow-left.fa-lg.cursor-pointer', {
|
||||
style: {
|
||||
display: props.index === 0 ? 'none' : 'inline-block',
|
||||
display: positionOfCurrentTx === 1 ? 'none' : 'inline-block',
|
||||
},
|
||||
onClick: () => props.actions.previousTx(),
|
||||
onClick: () => props.actions.nextTx(prevTxId),
|
||||
}),
|
||||
` ${props.index + 1} of ${props.unconfTxListLength} `,
|
||||
` ${positionOfCurrentTx} of ${totalTx} `,
|
||||
h('i.fa.white-arrow-right.fa-lg.cursor-pointer', {
|
||||
style: {
|
||||
display: props.index + 1 === props.unconfTxListLength ? 'none' : 'inline-block',
|
||||
display: positionOfCurrentTx === totalTx ? 'none' : 'inline-block',
|
||||
},
|
||||
onClick: () => props.actions.nextTx(),
|
||||
onClick: () => props.actions.nextTx(nextTxId),
|
||||
}),
|
||||
])],
|
||||
),
|
||||
|
@ -519,7 +519,7 @@ class PendingTx extends Component {
|
|||
onClick: props.cancelTransaction,
|
||||
}, 'Reject'),
|
||||
]),
|
||||
showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', {
|
||||
showNavigation ? h('.flex-row.flex-space-around.conf-buttons', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
|
@ -731,6 +731,23 @@ class PendingTx extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
getNavigateTxData () {
|
||||
const { unapprovedTxs, network, txData: { id } = {} } = this.props
|
||||
const currentNetworkUnapprovedTxs = Object.keys(unapprovedTxs)
|
||||
.filter((key) => unapprovedTxs[key].metamaskNetworkId === network)
|
||||
.reduce((acc, key) => ({ ...acc, [key]: unapprovedTxs[key] }), {})
|
||||
const enumUnapprovedTxs = Object.keys(currentNetworkUnapprovedTxs)
|
||||
const currentPosition = enumUnapprovedTxs.indexOf(id ? id.toString() : '')
|
||||
|
||||
return {
|
||||
totalTx: enumUnapprovedTxs.length,
|
||||
positionOfCurrentTx: currentPosition + 1,
|
||||
nextTxId: enumUnapprovedTxs[currentPosition + 1],
|
||||
prevTxId: enumUnapprovedTxs[currentPosition - 1],
|
||||
showNavigation: enumUnapprovedTxs.length > 1,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function forwardCarrat () {
|
||||
|
@ -755,7 +772,7 @@ function mapStateToProps (state) {
|
|||
unapprovedMsgs: state.metamask.unapprovedMsgs,
|
||||
unapprovedPersonalMsgs: state.metamask.unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages: state.metamask.unapprovedTypedMessages,
|
||||
index: state.appState.currentView.pendingTxIndex || 0,
|
||||
index: state.appState.currentView.key || 0,
|
||||
warning: state.appState.warning,
|
||||
network: state.metamask.network,
|
||||
provider: state.metamask.provider,
|
||||
|
@ -764,14 +781,14 @@ function mapStateToProps (state) {
|
|||
currentCurrency: state.metamask.currentCurrency,
|
||||
blockGasLimit: state.metamask.currentBlockGasLimit,
|
||||
computedBalances: state.metamask.computedBalances,
|
||||
pendingTxIndex: state.appState.currentView.pendingTxIndex || 0,
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
actions: {
|
||||
previousTx: () => dispatch(actions.previousTx()),
|
||||
nextTx: () => dispatch(actions.nextTx()),
|
||||
nextTx: (txId) => dispatch(actions.nextTx(txId)),
|
||||
displayWarning: (msg) => dispatch(actions.displayWarning(msg)),
|
||||
goHome: () => dispatch(actions.goHome()),
|
||||
},
|
||||
|
|
|
@ -212,7 +212,7 @@ ShiftListItem.prototype.renderInfo = function () {
|
|||
paddingLeft: '29px',
|
||||
textAlign: 'left',
|
||||
},
|
||||
onClick: () => global.platform.openWindow({ url }),
|
||||
onClick: () => global.platform.openTab({ url }),
|
||||
}, [
|
||||
h('div', {
|
||||
style: {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const inherits = require('util').inherits
|
||||
const Component = require('react').Component
|
||||
import PropTypes from 'prop-types'
|
||||
import { Component } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../ui/app/actions')
|
||||
const LoadingIndicator = require('./components/loading')
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
const log = require('loglevel')
|
||||
import log from 'loglevel'
|
||||
const { getCurrentKeyring, ifContractAcc } = require('./util')
|
||||
|
||||
const PendingTx = require('./components/pending-tx')
|
||||
|
@ -15,118 +15,224 @@ import PendingTypedMsg from './components/pending-typed-msg'
|
|||
const Loading = require('./components/loading')
|
||||
const { DAI_CODE, POA_SOKOL_CODE, RSK_TESTNET_CODE, GOERLI_TESTNET_CODE } = require('../../app/scripts/controllers/network/enums')
|
||||
const { getMetaMaskAccounts } = require('../../ui/app/selectors')
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
module.exports = connect(mapStateToProps)(ConfirmTxScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { metamask, appState } = state
|
||||
const { screenParams, pendingTxIndex } = appState.currentView
|
||||
return {
|
||||
identities: metamask.identities,
|
||||
accounts: getMetaMaskAccounts(state),
|
||||
keyrings: metamask.keyrings,
|
||||
selectedAddress: metamask.selectedAddress,
|
||||
unapprovedTxs: metamask.unapprovedTxs,
|
||||
unapprovedMsgs: metamask.unapprovedMsgs,
|
||||
unapprovedPersonalMsgs: metamask.unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages: metamask.unapprovedTypedMessages,
|
||||
index: pendingTxIndex || 0,
|
||||
warning: appState.warning,
|
||||
network: metamask.network,
|
||||
provider: metamask.provider,
|
||||
conversionRate: metamask.conversionRate,
|
||||
currentCurrency: metamask.currentCurrency,
|
||||
blockGasLimit: metamask.currentBlockGasLimit,
|
||||
computedBalances: metamask.computedBalances,
|
||||
isToken: (screenParams && screenParams.isToken),
|
||||
tokenSymbol: (screenParams && screenParams.tokenSymbol),
|
||||
tokensToSend: (screenParams && screenParams.tokensToSend),
|
||||
tokensTransferTo: (screenParams && screenParams.tokensTransferTo),
|
||||
isContractExecutionByUser: (screenParams && screenParams.isContractExecutionByUser),
|
||||
}
|
||||
}
|
||||
|
||||
inherits(ConfirmTxScreen, Component)
|
||||
function ConfirmTxScreen () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { network, unapprovedTxs, currentCurrency, computedBalances,
|
||||
unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, blockGasLimit } = props
|
||||
let { conversionRate } = props
|
||||
|
||||
const isTestnet = parseInt(network) === POA_SOKOL_CODE || parseInt(network) === RSK_TESTNET_CODE || parseInt(network) === GOERLI_TESTNET_CODE
|
||||
const isDai = parseInt(network) === DAI_CODE
|
||||
if (isTestnet) {
|
||||
conversionRate = 0
|
||||
} else if (isDai) {
|
||||
conversionRate = 1
|
||||
class ConfirmTxScreen extends Component {
|
||||
static propTypes = {
|
||||
network: PropTypes.string,
|
||||
identities: PropTypes.objectOf(PropTypes.object),
|
||||
keyrings: PropTypes.array,
|
||||
actions: PropTypes.objectOf(PropTypes.func),
|
||||
isToken: PropTypes.bool,
|
||||
isContractExecutionByUser: PropTypes.bool,
|
||||
selectedAddress: PropTypes.string,
|
||||
warning: PropTypes.string,
|
||||
unapprovedTxs: PropTypes.object,
|
||||
unapprovedMsgs: PropTypes.object,
|
||||
unapprovedPersonalMsgs: PropTypes.object,
|
||||
unapprovedTypedMessages: PropTypes.object,
|
||||
pendingTxIndex: PropTypes.number,
|
||||
blockGasLimit: PropTypes.string,
|
||||
accounts: PropTypes.object,
|
||||
currentCurrency: PropTypes.string,
|
||||
computedBalances: PropTypes.object,
|
||||
conversionRate: PropTypes.number,
|
||||
tokenSymbol: PropTypes.string,
|
||||
tokensToSend: PropTypes.objectOf(BigNumber),
|
||||
tokensTransferTo: PropTypes.string,
|
||||
}
|
||||
|
||||
const unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network)
|
||||
const ind = props.index || 0
|
||||
const txData = unconfTxList[ind] || {}
|
||||
const txParams = txData.params || {}
|
||||
render () {
|
||||
const props = this.props
|
||||
const { network, unapprovedTxs, currentCurrency, computedBalances,
|
||||
unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, blockGasLimit } = props
|
||||
let { conversionRate } = props
|
||||
|
||||
log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
|
||||
if (unconfTxList.length === 0) return h(Loading, { isLoading: true })
|
||||
const isTestnet = parseInt(network) === POA_SOKOL_CODE || parseInt(network) === RSK_TESTNET_CODE || parseInt(network) === GOERLI_TESTNET_CODE
|
||||
const isDai = parseInt(network) === DAI_CODE
|
||||
if (isTestnet) {
|
||||
conversionRate = 0
|
||||
} else if (isDai) {
|
||||
conversionRate = 1
|
||||
}
|
||||
|
||||
const unconfTxListLength = unconfTxList.length
|
||||
const unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network)
|
||||
const ind = props.pendingTxIndex || 0
|
||||
const txData = unconfTxList[ind] || {}
|
||||
const txParams = txData.params || {}
|
||||
|
||||
return (
|
||||
log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
|
||||
if (unconfTxList.length === 0) return h(Loading, { isLoading: true })
|
||||
|
||||
h('.flex-column.flex-grow', {
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}, [
|
||||
const unconfTxListLength = unconfTxList.length
|
||||
|
||||
h(LoadingIndicator, {
|
||||
isLoading: this.state ? !this.state.bypassLoadingScreen : txData.loadingDefaults,
|
||||
loadingMessage: 'Estimating transaction cost…',
|
||||
canBypass: true,
|
||||
bypass: () => {
|
||||
this.setState({bypassLoadingScreen: true})
|
||||
return (
|
||||
|
||||
h('.flex-column.flex-grow', {
|
||||
style: {
|
||||
width: '100%',
|
||||
},
|
||||
}),
|
||||
}, [
|
||||
|
||||
// subtitle and nav
|
||||
h(LoadingIndicator, {
|
||||
isLoading: this.state ? !this.state.bypassLoadingScreen : txData.loadingDefaults,
|
||||
loadingMessage: 'Estimating transaction cost…',
|
||||
canBypass: true,
|
||||
bypass: () => {
|
||||
this.setState({bypassLoadingScreen: true})
|
||||
},
|
||||
}),
|
||||
|
||||
warningIfExists(props.warning),
|
||||
// subtitle and nav
|
||||
|
||||
currentTxView({
|
||||
// Properties
|
||||
txData: txData,
|
||||
key: txData.id,
|
||||
selectedAddress: props.selectedAddress,
|
||||
accounts: props.accounts,
|
||||
identities: props.identities,
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
blockGasLimit,
|
||||
unconfTxListLength,
|
||||
computedBalances,
|
||||
network,
|
||||
isToken: props.isToken,
|
||||
tokenSymbol: props.tokenSymbol,
|
||||
tokensToSend: props.tokensToSend,
|
||||
tokensTransferTo: props.tokensTransferTo,
|
||||
// Actions
|
||||
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress, props.isContractExecutionByUser),
|
||||
sendTransaction: this.sendTransaction.bind(this),
|
||||
cancelTransaction: this.cancelTransaction.bind(this, txData),
|
||||
cancelAllTransactions: this.cancelAllTransactions.bind(this, unconfTxList),
|
||||
signMessage: this.signMessage.bind(this, txData),
|
||||
signPersonalMessage: this.signPersonalMessage.bind(this, txData),
|
||||
signTypedMessage: this.signTypedMessage.bind(this, txData),
|
||||
cancelMessage: this.cancelMessage.bind(this, txData),
|
||||
cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
|
||||
cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
|
||||
}),
|
||||
])
|
||||
)
|
||||
warningIfExists(props.warning),
|
||||
|
||||
currentTxView({
|
||||
// Properties
|
||||
txData: txData,
|
||||
key: txData.id,
|
||||
selectedAddress: props.selectedAddress,
|
||||
accounts: props.accounts,
|
||||
identities: props.identities,
|
||||
conversionRate,
|
||||
currentCurrency,
|
||||
blockGasLimit,
|
||||
unconfTxListLength,
|
||||
computedBalances,
|
||||
network,
|
||||
isToken: props.isToken,
|
||||
tokenSymbol: props.tokenSymbol,
|
||||
tokensToSend: props.tokensToSend,
|
||||
tokensTransferTo: props.tokensTransferTo,
|
||||
// Actions
|
||||
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress, props.isContractExecutionByUser),
|
||||
sendTransaction: this.sendTransaction.bind(this),
|
||||
cancelTransaction: this.cancelTransaction.bind(this, txData),
|
||||
cancelAllTransactions: this.cancelAllTransactions.bind(this, unconfTxList),
|
||||
signMessage: this.signMessage.bind(this, txData),
|
||||
signPersonalMessage: this.signPersonalMessage.bind(this, txData),
|
||||
signTypedMessage: this.signTypedMessage.bind(this, txData),
|
||||
cancelMessage: this.cancelMessage.bind(this, txData),
|
||||
cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
|
||||
cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
|
||||
}),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
buyEth (address, isContractExecutionByUser, event) {
|
||||
event.preventDefault()
|
||||
this.props.actions.buyEthView(address, isContractExecutionByUser)
|
||||
}
|
||||
|
||||
sendTransaction (txData, event) {
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.updateAndApproveTx(txData)
|
||||
this._checkIfContractExecutionAndUnlockContract(txData)
|
||||
}
|
||||
|
||||
cancelTransaction (txData, event) {
|
||||
this.stopPropagation(event)
|
||||
event.preventDefault()
|
||||
this.props.actions.cancelTx(txData)
|
||||
this._checkIfContractExecutionAndUnlockContract(txData)
|
||||
}
|
||||
|
||||
cancelAllTransactions (unconfTxList, event) {
|
||||
this.stopPropagation(event)
|
||||
event.preventDefault()
|
||||
this.props.actions.cancelTxs(unconfTxList)
|
||||
this._checkIfMultipleContractExecutionAndUnlockContract(unconfTxList)
|
||||
}
|
||||
|
||||
signMessage (msgData, event) {
|
||||
log.info('conf-tx.js: signing message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.signMsg(params)
|
||||
}
|
||||
|
||||
stopPropagation (event) {
|
||||
if (event.stopPropagation) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
signPersonalMessage (msgData, event) {
|
||||
log.info('conf-tx.js: signing personal message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.signPersonalMsg(params)
|
||||
}
|
||||
|
||||
signTypedMessage (msgData, event) {
|
||||
log.info('conf-tx.js: signing typed message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.signTypedMsg(params)
|
||||
}
|
||||
|
||||
cancelMessage (msgData, event) {
|
||||
log.info('canceling message')
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.cancelMsg(msgData)
|
||||
}
|
||||
|
||||
cancelPersonalMessage (msgData, event) {
|
||||
log.info('canceling personal message')
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.cancelPersonalMsg(msgData)
|
||||
}
|
||||
|
||||
cancelTypedMessage (msgData, event) {
|
||||
log.info('canceling typed message')
|
||||
this.stopPropagation(event)
|
||||
this.props.actions.cancelTypedMsg(msgData)
|
||||
}
|
||||
|
||||
_checkIfMultipleContractExecutionAndUnlockContract (unconfTxList) {
|
||||
const areTxsToOneContractFromTheList = unconfTxList.slice(0).reduce((res, txData, ind, unconfTxList) => {
|
||||
if (txData.txParams.data && this.props.isContractExecutionByUser) {
|
||||
const to = txData && txData.txParams && txData.txParams.to
|
||||
const targetContractIsInTheList = Object.keys(this.props.accounts).some((acc) => acc === to)
|
||||
if (targetContractIsInTheList && Object.keys(res).length === 0) {
|
||||
res = { status: true, to }
|
||||
} else if (res.status && res.to !== to) {
|
||||
res = { status: false }
|
||||
unconfTxList.splice(1)
|
||||
}
|
||||
} else {
|
||||
res = { status: false }
|
||||
unconfTxList.splice(1)
|
||||
}
|
||||
return res
|
||||
}, {})
|
||||
|
||||
if (areTxsToOneContractFromTheList.status) {
|
||||
this._unlockContract(areTxsToOneContractFromTheList.to)
|
||||
}
|
||||
}
|
||||
|
||||
_checkIfContractExecutionAndUnlockContract (txData) {
|
||||
if (txData.txParams.data && this.props.isContractExecutionByUser) {
|
||||
const to = txData && txData.txParams && txData.txParams.to
|
||||
const targetContractIsInTheList = Object.keys(this.props.accounts).some((acc) => acc === to)
|
||||
if (targetContractIsInTheList) {
|
||||
this._unlockContract(to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_unlockContract (to) {
|
||||
const currentKeyring = getCurrentKeyring(to, this.props.network, this.props.keyrings, this.props.identities)
|
||||
if (ifContractAcc(currentKeyring)) {
|
||||
this.props.actions.showAccountDetail(to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function currentTxView (opts) {
|
||||
|
@ -153,119 +259,6 @@ function currentTxView (opts) {
|
|||
}
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.buyEth = function (address, isContractExecutionByUser, event) {
|
||||
event.preventDefault()
|
||||
this.props.dispatch(actions.buyEthView(address, isContractExecutionByUser))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.updateAndApproveTx(txData))
|
||||
this._checkIfContractExecutionAndUnlockContract(txData)
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
|
||||
this.stopPropagation(event)
|
||||
event.preventDefault()
|
||||
this.props.dispatch(actions.cancelTx(txData))
|
||||
this._checkIfContractExecutionAndUnlockContract(txData)
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelAllTransactions = function (unconfTxList, event) {
|
||||
this.stopPropagation(event)
|
||||
event.preventDefault()
|
||||
this.props.dispatch(actions.cancelAllTx(unconfTxList))
|
||||
this._checkIfMultipleContractExecutionAndUnlockContract(unconfTxList)
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.signMessage = function (msgData, event) {
|
||||
log.info('conf-tx.js: signing message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.signMsg(params))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.stopPropagation = function (event) {
|
||||
if (event.stopPropagation) {
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.signPersonalMessage = function (msgData, event) {
|
||||
log.info('conf-tx.js: signing personal message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.signPersonalMsg(params))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.signTypedMessage = function (msgData, event) {
|
||||
log.info('conf-tx.js: signing typed message')
|
||||
const params = msgData.msgParams
|
||||
params.metamaskId = msgData.id
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.signTypedMsg(params))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelMessage = function (msgData, event) {
|
||||
log.info('canceling message')
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.cancelMsg(msgData))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelPersonalMessage = function (msgData, event) {
|
||||
log.info('canceling personal message')
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.cancelPersonalMsg(msgData))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype.cancelTypedMessage = function (msgData, event) {
|
||||
log.info('canceling typed message')
|
||||
this.stopPropagation(event)
|
||||
this.props.dispatch(actions.cancelTypedMsg(msgData))
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype._checkIfMultipleContractExecutionAndUnlockContract = function (unconfTxList) {
|
||||
const areTxsToOneContractFromTheList = unconfTxList.slice(0).reduce((res, txData, ind, unconfTxList) => {
|
||||
if (txData.txParams.data && this.props.isContractExecutionByUser) {
|
||||
const to = txData && txData.txParams && txData.txParams.to
|
||||
const targetContractIsInTheList = Object.keys(this.props.accounts).some((acc) => acc === to)
|
||||
if (targetContractIsInTheList && Object.keys(res).length === 0) {
|
||||
res = { status: true, to }
|
||||
} else if (res.status && res.to !== to) {
|
||||
res = { status: false }
|
||||
unconfTxList.splice(1)
|
||||
}
|
||||
} else {
|
||||
res = { status: false }
|
||||
unconfTxList.splice(1)
|
||||
}
|
||||
return res
|
||||
}, {})
|
||||
|
||||
if (areTxsToOneContractFromTheList.status) {
|
||||
this._unlockContract(areTxsToOneContractFromTheList.to)
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype._checkIfContractExecutionAndUnlockContract = function (txData) {
|
||||
if (txData.txParams.data && this.props.isContractExecutionByUser) {
|
||||
const to = txData && txData.txParams && txData.txParams.to
|
||||
const targetContractIsInTheList = Object.keys(this.props.accounts).some((acc) => acc === to)
|
||||
if (targetContractIsInTheList) {
|
||||
this._unlockContract(to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmTxScreen.prototype._unlockContract = function (to) {
|
||||
const currentKeyring = getCurrentKeyring(to, this.props.network, this.props.keyrings, this.props.identities)
|
||||
if (ifContractAcc(currentKeyring)) {
|
||||
this.props.dispatch(actions.showAccountDetail(to))
|
||||
}
|
||||
}
|
||||
|
||||
function warningIfExists (warning) {
|
||||
if (warning &&
|
||||
// Do not display user rejections on this screen:
|
||||
|
@ -277,3 +270,51 @@ function warningIfExists (warning) {
|
|||
}, warning)
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const { metamask, appState } = state
|
||||
const { screenParams, pendingTxIndex } = appState.currentView
|
||||
return {
|
||||
identities: metamask.identities,
|
||||
accounts: getMetaMaskAccounts(state),
|
||||
keyrings: metamask.keyrings,
|
||||
selectedAddress: metamask.selectedAddress,
|
||||
unapprovedTxs: metamask.unapprovedTxs,
|
||||
unapprovedMsgs: metamask.unapprovedMsgs,
|
||||
unapprovedPersonalMsgs: metamask.unapprovedPersonalMsgs,
|
||||
unapprovedTypedMessages: metamask.unapprovedTypedMessages,
|
||||
pendingTxIndex: pendingTxIndex || 0,
|
||||
warning: appState.warning,
|
||||
network: metamask.network,
|
||||
provider: metamask.provider,
|
||||
conversionRate: metamask.conversionRate,
|
||||
currentCurrency: metamask.currentCurrency,
|
||||
blockGasLimit: metamask.currentBlockGasLimit,
|
||||
computedBalances: metamask.computedBalances,
|
||||
isToken: (screenParams && screenParams.isToken),
|
||||
tokenSymbol: (screenParams && screenParams.tokenSymbol),
|
||||
tokensToSend: (screenParams && screenParams.tokensToSend),
|
||||
tokensTransferTo: (screenParams && screenParams.tokensTransferTo),
|
||||
isContractExecutionByUser: (screenParams && screenParams.isContractExecutionByUser),
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
actions: {
|
||||
buyEthView: (address, isContractExecutionByUser) => dispatch(actions.buyEthView(address, isContractExecutionByUser)),
|
||||
updateAndApproveTx: (txData) => dispatch(actions.updateAndApproveTx(txData)),
|
||||
cancelTx: (txData) => dispatch(actions.cancelTx(txData)),
|
||||
cancelTxs: (unconfTxList) => dispatch(actions.cancelTxs(unconfTxList)),
|
||||
signMsg: (params) => dispatch(actions.signMsg(params)),
|
||||
signPersonalMsg: (params) => dispatch(actions.signPersonalMsg(params)),
|
||||
signTypedMsg: (params) => dispatch(actions.signTypedMsg(params)),
|
||||
cancelMsg: (msgData) => dispatch(actions.cancelMsg(msgData)),
|
||||
cancelPersonalMsg: (msgData) => dispatch(actions.cancelPersonalMsg(msgData)),
|
||||
cancelTypedMsg: (msgData) => dispatch(actions.cancelTypedMsg(msgData)),
|
||||
showAccountDetail: (to) => dispatch(actions.showAccountDetail(to)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ConfirmTxScreen)
|
||||
|
|
|
@ -298,7 +298,7 @@ module.exports = {
|
|||
accountName: By.className('font-medium color-forest'),
|
||||
edit: By.className('edit-text'),
|
||||
iconCopy: By.className('clipboard cursor-pointer white'),
|
||||
transactionList: By.css('#app-content > div > div.app-primary.from-left > div > section > section > div > div > div > div.ether-balance.ether-balance-amount > div > div > div > div:nth-child(1)'),
|
||||
transactionList: By.css('#app-content > div > div.app-primary.from-left > div > section > section > div > div > div > div.ether-balance.ether-balance-amount > div > div.flex-column > div > div:nth-child(1)'),
|
||||
buttons: {
|
||||
send: By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(4)'),
|
||||
buy: By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > button:nth-child(3)'),
|
||||
|
@ -309,7 +309,7 @@ module.exports = {
|
|||
},
|
||||
network: By.className('network-name'),
|
||||
sent: {
|
||||
menu: By.className('wallet-view__tab-history'),
|
||||
menu: By.css('#wallet-view__tab-history'),
|
||||
tokens: By.className('activeForm right'),
|
||||
},
|
||||
// balance: By.css('#app-content > div > div.app-primary.from-right > div > div > div.flex-row > div.ether-balance.ether-balance-amount > div > div > div:nth-child(1) > div:nth-child(1)'),
|
||||
|
|
|
@ -60,6 +60,8 @@ const importGanacheSeedPhrase = async (f, account2, password) => {
|
|||
})
|
||||
|
||||
it('finds the transaction in the transactions list', async () => {
|
||||
const sentTab = await f.waitUntilShowUp(screens.main.sent.menu)
|
||||
await sentTab.click()
|
||||
const transactionAmount = await f.waitUntilShowUp(screens.main.transactionList)
|
||||
assert.equal(await transactionAmount.getText(), '10.0')
|
||||
})
|
||||
|
|
|
@ -443,7 +443,7 @@ describe('preferences controller', function () {
|
|||
req.params.options = { address, symbol, decimals, image }
|
||||
|
||||
sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
|
||||
preferencesController.showWatchAssetUi = async () => {}
|
||||
preferencesController.openPopup = async () => {}
|
||||
|
||||
await preferencesController._handleWatchAssetERC20(req.params.options)
|
||||
const suggested = preferencesController.getSuggestedTokens()
|
||||
|
@ -463,7 +463,7 @@ describe('preferences controller', function () {
|
|||
req.params.options = { address, symbol, decimals, image }
|
||||
|
||||
sandbox.stub(preferencesController, '_validateERC20AssetParams').returns(true)
|
||||
preferencesController.showWatchAssetUi = async () => {
|
||||
preferencesController.openPopup = async () => {
|
||||
await preferencesController.addToken(address, symbol, decimals, image)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
const assert = require('assert')
|
||||
|
||||
const EdgeEncryptor = require('../../../app/scripts/edge-encryptor')
|
||||
|
||||
var password = 'passw0rd1'
|
||||
var data = 'some random data'
|
||||
|
||||
global.crypto = global.crypto || {
|
||||
getRandomValues: function (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
array[i] = Math.random() * 100
|
||||
}
|
||||
return array
|
||||
},
|
||||
}
|
||||
|
||||
describe('EdgeEncryptor', function () {
|
||||
|
||||
const edgeEncryptor = new EdgeEncryptor()
|
||||
describe('encrypt', function () {
|
||||
|
||||
it('should encrypt the data.', function (done) {
|
||||
edgeEncryptor.encrypt(password, data)
|
||||
.then(function (encryptedData) {
|
||||
assert.notEqual(data, encryptedData)
|
||||
assert.notEqual(encryptedData.length, 0)
|
||||
done()
|
||||
}).catch(function (err) {
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should return proper format.', function (done) {
|
||||
edgeEncryptor.encrypt(password, data)
|
||||
.then(function (encryptedData) {
|
||||
const encryptedObject = JSON.parse(encryptedData)
|
||||
assert.ok(encryptedObject.data, 'there is no data')
|
||||
assert.ok(encryptedObject.iv && encryptedObject.iv.length !== 0, 'there is no iv')
|
||||
assert.ok(encryptedObject.salt && encryptedObject.salt.length !== 0, 'there is no salt')
|
||||
done()
|
||||
}).catch(function (err) {
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not return the same twice.', function (done) {
|
||||
|
||||
const encryptPromises = []
|
||||
encryptPromises.push(edgeEncryptor.encrypt(password, data))
|
||||
encryptPromises.push(edgeEncryptor.encrypt(password, data))
|
||||
|
||||
Promise.all(encryptPromises).then((encryptedData) => {
|
||||
assert.equal(encryptedData.length, 2)
|
||||
assert.notEqual(encryptedData[0], encryptedData[1])
|
||||
assert.notEqual(encryptedData[0].length, 0)
|
||||
assert.notEqual(encryptedData[1].length, 0)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('decrypt', function () {
|
||||
it('should be able to decrypt the encrypted data.', function (done) {
|
||||
|
||||
edgeEncryptor.encrypt(password, data)
|
||||
.then(function (encryptedData) {
|
||||
edgeEncryptor.decrypt(password, encryptedData)
|
||||
.then(function (decryptedData) {
|
||||
assert.equal(decryptedData, data)
|
||||
done()
|
||||
})
|
||||
.catch(function (err) {
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot decrypt the encrypted data with wrong password.', function (done) {
|
||||
|
||||
edgeEncryptor.encrypt(password, data)
|
||||
.then(function (encryptedData) {
|
||||
edgeEncryptor.decrypt('wrong password', encryptedData)
|
||||
.then(function (decryptedData) {
|
||||
assert.fail('could decrypt with wrong password')
|
||||
done()
|
||||
})
|
||||
.catch(function (err) {
|
||||
assert.ok(err instanceof Error)
|
||||
assert.equal(err.message, 'Incorrect password')
|
||||
done()
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
done(err)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -19,6 +19,7 @@ const { POA,
|
|||
CLASSIC } = require('../../app/scripts/controllers/network/enums')
|
||||
const { hasUnconfirmedTransactions } = require('./helpers/confirm-transaction/util')
|
||||
const WebcamUtils = require('../lib/webcam-utils')
|
||||
import { getEnvironmentType } from '../../app/scripts/lib/util'
|
||||
|
||||
const actions = {
|
||||
_setBackgroundConnection: _setBackgroundConnection,
|
||||
|
@ -30,6 +31,9 @@ const actions = {
|
|||
MODAL_CLOSE: 'UI_MODAL_CLOSE',
|
||||
showModal: showModal,
|
||||
hideModal: hideModal,
|
||||
|
||||
CLOSE_NOTIFICATION_WINDOW: 'CLOSE_NOTIFICATION_WINDOW',
|
||||
|
||||
// sidebar state
|
||||
SIDEBAR_OPEN: 'UI_SIDEBAR_OPEN',
|
||||
SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE',
|
||||
|
@ -173,7 +177,6 @@ const actions = {
|
|||
COMPLETED_TX: 'COMPLETED_TX',
|
||||
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
|
||||
NEXT_TX: 'NEXT_TX',
|
||||
PREVIOUS_TX: 'PREV_TX',
|
||||
EDIT_TX: 'EDIT_TX',
|
||||
signMsg: signMsg,
|
||||
cancelMsg: cancelMsg,
|
||||
|
@ -186,14 +189,12 @@ const actions = {
|
|||
signTokenTx: signTokenTx,
|
||||
updateTransaction,
|
||||
updateAndApproveTx,
|
||||
cancelTx: cancelTx,
|
||||
cancelTx,
|
||||
cancelTxs,
|
||||
completedTx: completedTx,
|
||||
txError: txError,
|
||||
nextTx: nextTx,
|
||||
editTx,
|
||||
previousTx: previousTx,
|
||||
cancelAllTx: cancelAllTx,
|
||||
viewPendingTx: viewPendingTx,
|
||||
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
|
||||
updateTransactionParams,
|
||||
|
@ -370,6 +371,8 @@ const actions = {
|
|||
getRequestAccountTabIds,
|
||||
setOpenMetamaskTabsIDs,
|
||||
getOpenMetamaskTabsIds,
|
||||
closeCurrentNotificationWindow,
|
||||
closeNotificationWindow,
|
||||
}
|
||||
|
||||
module.exports = actions
|
||||
|
@ -918,9 +921,8 @@ function setCurrentCurrency (currencyCode) {
|
|||
|
||||
function signMsg (msgData) {
|
||||
log.debug('action - signMsg')
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
log.debug(`actions calling background.signMessage`)
|
||||
background.signMessage(msgData, (err, newState) => {
|
||||
|
@ -935,10 +937,7 @@ function signMsg (msgData) {
|
|||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -948,7 +947,7 @@ function signMsg (msgData) {
|
|||
|
||||
function signPersonalMsg (msgData) {
|
||||
log.debug('action - signPersonalMsg')
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -966,9 +965,7 @@ function signPersonalMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -978,7 +975,7 @@ function signPersonalMsg (msgData) {
|
|||
|
||||
function signTypedMsg (msgData) {
|
||||
log.debug('action - signTypedMsg')
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -996,9 +993,7 @@ function signTypedMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1205,7 +1200,7 @@ function clearSend () {
|
|||
|
||||
function sendTx (txData) {
|
||||
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
log.debug(`actions calling background.approveTransaction`)
|
||||
background.approveTransaction(txData.id, (err) => {
|
||||
if (err) {
|
||||
|
@ -1214,10 +1209,7 @@ function sendTx (txData) {
|
|||
return log.error(err)
|
||||
}
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1267,10 +1259,9 @@ function updateTransaction (txData) {
|
|||
|
||||
function updateAndApproveTx (txData) {
|
||||
log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData))
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
log.debug(`actions calling background.updateAndApproveTx`)
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
background.updateAndApproveTransaction(txData, err => {
|
||||
dispatch(actions.updateTransactionParams(txData.id, txData.txParams))
|
||||
|
@ -1293,11 +1284,7 @@ function updateAndApproveTx (txData) {
|
|||
dispatch(actions.clearSend())
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
dispatch(actions.setCurrentAccountTab('history'))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return txData
|
||||
})
|
||||
|
@ -1331,7 +1318,7 @@ function txError (err) {
|
|||
}
|
||||
|
||||
function cancelMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1346,9 +1333,7 @@ function cancelMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1357,7 +1342,7 @@ function cancelMsg (msgData) {
|
|||
}
|
||||
|
||||
function cancelPersonalMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1372,9 +1357,7 @@ function cancelPersonalMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1383,7 +1366,7 @@ function cancelPersonalMsg (msgData) {
|
|||
}
|
||||
|
||||
function cancelTypedMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1398,9 +1381,7 @@ function cancelTypedMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1409,12 +1390,11 @@ function cancelTypedMsg (msgData) {
|
|||
}
|
||||
|
||||
function cancelTx (txData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
log.debug(`background.cancelTransaction`)
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
background.cancelTransaction(txData.id, err => {
|
||||
background.cancelTransaction(txData.id, (err) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
|
@ -1423,15 +1403,12 @@ function cancelTx (txData) {
|
|||
})
|
||||
})
|
||||
.then(() => updateMetamaskStateFromBackground())
|
||||
.then(newState => dispatch(actions.updateMetamaskState(newState)))
|
||||
.then((newState) => dispatch(actions.updateMetamaskState(newState)))
|
||||
.then(() => {
|
||||
dispatch(actions.clearSend())
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return txData
|
||||
})
|
||||
|
@ -1444,9 +1421,9 @@ function cancelTx (txData) {
|
|||
* @return {function(*): Promise<void>}
|
||||
*/
|
||||
function cancelTxs (txDataList) {
|
||||
return async (dispatch, getState) => {
|
||||
return async (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
const txIds = txDataList.map(({id}) => id)
|
||||
const txIds = txDataList.map(({ id }) => id)
|
||||
const cancellations = txIds.map((id) => new Promise((resolve, reject) => {
|
||||
background.cancelTransaction(id, (err) => {
|
||||
if (err) {
|
||||
|
@ -1468,30 +1445,12 @@ function cancelTxs (txDataList) {
|
|||
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||
return global.platform.closeCurrentWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param {Array<object>} txsData
|
||||
* @return {Function}
|
||||
*/
|
||||
function cancelAllTx (txsData) {
|
||||
return (dispatch) => {
|
||||
txsData.forEach((txData, i) => {
|
||||
background.cancelTransaction(txData.id, () => {
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
if (i === txsData.length - 1) {
|
||||
dispatch(actions.goHome())
|
||||
global.platform.closeNotificationWindow()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
//
|
||||
// initialize screen
|
||||
//
|
||||
|
@ -1762,9 +1721,10 @@ function showConfTxPage (screenParams) {
|
|||
}
|
||||
}
|
||||
|
||||
function nextTx () {
|
||||
function nextTx (txId) {
|
||||
return {
|
||||
type: actions.NEXT_TX,
|
||||
value: txId,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1775,12 +1735,6 @@ function viewPendingTx (txId) {
|
|||
}
|
||||
}
|
||||
|
||||
function previousTx () {
|
||||
return {
|
||||
type: actions.PREVIOUS_TX,
|
||||
}
|
||||
}
|
||||
|
||||
function editTx (txId) {
|
||||
return {
|
||||
type: actions.EDIT_TX,
|
||||
|
@ -2131,6 +2085,23 @@ function hideModal (payload) {
|
|||
}
|
||||
}
|
||||
|
||||
function closeCurrentNotificationWindow () {
|
||||
return (dispatch, getState) => {
|
||||
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||
!hasUnconfirmedTransactions(getState())) {
|
||||
global.platform.closeCurrentWindow()
|
||||
|
||||
dispatch(actions.closeNotificationWindow())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeNotificationWindow () {
|
||||
return {
|
||||
type: actions.CLOSE_NOTIFICATION_WINDOW,
|
||||
}
|
||||
}
|
||||
|
||||
function showSidebar ({ transitionName, type }) {
|
||||
return {
|
||||
type: actions.SIDEBAR_OPEN,
|
||||
|
@ -2335,7 +2306,7 @@ function showSendContractPage ({methodSelected, methodABI, inputValues}) {
|
|||
function buyEth (opts) {
|
||||
return (dispatch) => {
|
||||
const url = getBuyEthUrl(opts)
|
||||
global.platform.openWindow({ url })
|
||||
global.platform.openTab({ url })
|
||||
dispatch({
|
||||
type: actions.BUY_ETH,
|
||||
})
|
||||
|
|
|
@ -68,7 +68,7 @@ TokenMenuDropdown.prototype.render = function () {
|
|||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
const url = ethNetProps.explorerLinks.getExplorerAccountLinkFor(this.props.token.address, this.props.network)
|
||||
global.platform.openWindow({ url })
|
||||
global.platform.openTab({ url })
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('viewOnEtherscan'),
|
||||
|
|
|
@ -79,7 +79,8 @@ function reduceApp (state, action) {
|
|||
customHdPaths: customHdPaths,
|
||||
}, state.appState)
|
||||
|
||||
let curPendingTxIndex = appState.currentView.pendingTxIndex || 0
|
||||
const curPendingTxIndex = appState.currentView.pendingTxIndex || 0
|
||||
const curPendingTxId = appState.currentView.pendingTxId || 0
|
||||
|
||||
switch (action.type) {
|
||||
// dropdown methods
|
||||
|
@ -507,6 +508,7 @@ function reduceApp (state, action) {
|
|||
currentView: {
|
||||
name: 'confTx',
|
||||
pendingTxIndex: action.id ? indexForPending(state, action.id) : 0,
|
||||
pendingTxId: action.id,
|
||||
screenParams: action.value,
|
||||
},
|
||||
transForward: action.transForward,
|
||||
|
@ -559,11 +561,14 @@ function reduceApp (state, action) {
|
|||
}
|
||||
|
||||
case actions.NEXT_TX:
|
||||
const increment = (action.value - curPendingTxId)
|
||||
return extend(appState, {
|
||||
transForward: true,
|
||||
currentView: {
|
||||
name: 'confTx',
|
||||
pendingTxIndex: ++curPendingTxIndex,
|
||||
pendingTxIndex: curPendingTxIndex + increment,
|
||||
pendingTxId: action.value,
|
||||
index: curPendingTxIndex + increment,
|
||||
warning: null,
|
||||
},
|
||||
})
|
||||
|
@ -575,16 +580,7 @@ function reduceApp (state, action) {
|
|||
currentView: {
|
||||
name: 'confTx',
|
||||
pendingTxIndex,
|
||||
warning: null,
|
||||
},
|
||||
})
|
||||
|
||||
case actions.PREVIOUS_TX:
|
||||
return extend(appState, {
|
||||
transForward: false,
|
||||
currentView: {
|
||||
name: 'confTx',
|
||||
pendingTxIndex: --curPendingTxIndex,
|
||||
pendingTxId: action.value,
|
||||
warning: null,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -6,6 +6,8 @@ import { roundExponential } from '../helpers/confirm-transaction/util'
|
|||
const unapprovedTxsSelector = state => state.metamask.unapprovedTxs
|
||||
const unapprovedMsgsSelector = state => state.metamask.unapprovedMsgs
|
||||
const unapprovedPersonalMsgsSelector = state => state.metamask.unapprovedPersonalMsgs
|
||||
const unapprovedDecryptMsgsSelector = (state) => state.metamask.unapprovedDecryptMsgs
|
||||
const unapprovedEncryptionPublicKeyMsgsSelector = (state) => state.metamask.unapprovedEncryptionPublicKeyMsgs
|
||||
const unapprovedTypedMessagesSelector = state => state.metamask.unapprovedTypedMessages
|
||||
const networkSelector = state => state.metamask.network
|
||||
|
||||
|
@ -13,18 +15,24 @@ export const unconfirmedTransactionsListSelector = createSelector(
|
|||
unapprovedTxsSelector,
|
||||
unapprovedMsgsSelector,
|
||||
unapprovedPersonalMsgsSelector,
|
||||
unapprovedDecryptMsgsSelector,
|
||||
unapprovedEncryptionPublicKeyMsgsSelector,
|
||||
unapprovedTypedMessagesSelector,
|
||||
networkSelector,
|
||||
(
|
||||
unapprovedTxs = {},
|
||||
unapprovedMsgs = {},
|
||||
unapprovedPersonalMsgs = {},
|
||||
unapprovedDecryptMsgs = {},
|
||||
unapprovedEncryptionPublicKeyMsgs = {},
|
||||
unapprovedTypedMessages = {},
|
||||
network,
|
||||
) => txHelper(
|
||||
unapprovedTxs,
|
||||
unapprovedMsgs,
|
||||
unapprovedPersonalMsgs,
|
||||
unapprovedDecryptMsgs,
|
||||
unapprovedEncryptionPublicKeyMsgs,
|
||||
unapprovedTypedMessages,
|
||||
network,
|
||||
) || [],
|
||||
|
@ -34,12 +42,16 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||
unapprovedTxsSelector,
|
||||
unapprovedMsgsSelector,
|
||||
unapprovedPersonalMsgsSelector,
|
||||
unapprovedDecryptMsgsSelector,
|
||||
unapprovedEncryptionPublicKeyMsgsSelector,
|
||||
unapprovedTypedMessagesSelector,
|
||||
networkSelector,
|
||||
(
|
||||
unapprovedTxs = {},
|
||||
unapprovedMsgs = {},
|
||||
unapprovedPersonalMsgs = {},
|
||||
unapprovedDecryptMsgs = {},
|
||||
unapprovedEncryptionPublicKeyMsgs = {},
|
||||
unapprovedTypedMessages = {},
|
||||
network,
|
||||
) => {
|
||||
|
@ -58,6 +70,8 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||
...filteredUnapprovedTxs,
|
||||
...unapprovedMsgs,
|
||||
...unapprovedPersonalMsgs,
|
||||
...unapprovedDecryptMsgs,
|
||||
...unapprovedEncryptionPublicKeyMsgs,
|
||||
...unapprovedTypedMessages,
|
||||
}
|
||||
},
|
||||
|
@ -65,18 +79,24 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||
|
||||
const unapprovedMsgCountSelector = state => state.metamask.unapprovedMsgCount
|
||||
const unapprovedPersonalMsgCountSelector = state => state.metamask.unapprovedPersonalMsgCount
|
||||
const unapprovedDecryptMsgCountSelector = (state) => state.metamask.unapprovedDecryptMsgCount
|
||||
const unapprovedEncryptionPublicKeyMsgCountSelector = (state) => state.metamask.unapprovedEncryptionPublicKeyMsgCount
|
||||
const unapprovedTypedMessagesCountSelector = state => state.metamask.unapprovedTypedMessagesCount
|
||||
|
||||
export const unconfirmedTransactionsCountSelector = createSelector(
|
||||
unapprovedTxsSelector,
|
||||
unapprovedMsgCountSelector,
|
||||
unapprovedPersonalMsgCountSelector,
|
||||
unapprovedDecryptMsgCountSelector,
|
||||
unapprovedEncryptionPublicKeyMsgCountSelector,
|
||||
unapprovedTypedMessagesCountSelector,
|
||||
networkSelector,
|
||||
(
|
||||
unapprovedTxs = {},
|
||||
unapprovedMsgCount = 0,
|
||||
unapprovedPersonalMsgCount = 0,
|
||||
unapprovedDecryptMsgCount = 0,
|
||||
unapprovedEncryptionPublicKeyMsgCount = 0,
|
||||
unapprovedTypedMessagesCount = 0,
|
||||
network,
|
||||
) => {
|
||||
|
@ -86,7 +106,7 @@ export const unconfirmedTransactionsCountSelector = createSelector(
|
|||
})
|
||||
|
||||
return filteredUnapprovedTxIds.length + unapprovedTypedMessagesCount + unapprovedMsgCount +
|
||||
unapprovedPersonalMsgCount
|
||||
unapprovedPersonalMsgCount + unapprovedDecryptMsgCount + unapprovedEncryptionPublicKeyMsgCount
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -132,15 +152,16 @@ export const tokenAmountAndToAddressSelector = createSelector(
|
|||
let tokenAmount = 0
|
||||
|
||||
if (params && params.length) {
|
||||
const toParam = params.find(param => param.name === TOKEN_PARAM_TO)
|
||||
const valueParam = params.find(param => param.name === TOKEN_PARAM_VALUE)
|
||||
const toParam = params.find((param) => param.name === TOKEN_PARAM_TO)
|
||||
const valueParam = params.find((param) => param.name === TOKEN_PARAM_VALUE)
|
||||
toAddress = toParam ? toParam.value : params[0].value
|
||||
const value = valueParam ? Number(valueParam.value) : Number(params[1].value)
|
||||
tokenAmount = roundExponential(value)
|
||||
|
||||
if (tokenDecimals) {
|
||||
tokenAmount = calcTokenAmount(value, tokenDecimals)
|
||||
tokenAmount = calcTokenAmount(value, tokenDecimals).toNumber()
|
||||
}
|
||||
|
||||
tokenAmount = roundExponential(tokenAmount)
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -158,11 +179,11 @@ export const approveTokenAmountAndToAddressSelector = createSelector(
|
|||
let tokenAmount = 0
|
||||
|
||||
if (params && params.length) {
|
||||
toAddress = params.find(param => param.name === TOKEN_PARAM_SPENDER).value
|
||||
const value = Number(params.find(param => param.name === TOKEN_PARAM_VALUE).value)
|
||||
toAddress = params.find((param) => param.name === TOKEN_PARAM_SPENDER).value
|
||||
const value = Number(params.find((param) => param.name === TOKEN_PARAM_VALUE).value)
|
||||
|
||||
if (tokenDecimals) {
|
||||
tokenAmount = calcTokenAmount(value, tokenDecimals)
|
||||
tokenAmount = calcTokenAmount(value, tokenDecimals).toNumber()
|
||||
}
|
||||
|
||||
tokenAmount = roundExponential(tokenAmount)
|
||||
|
@ -183,11 +204,11 @@ export const sendTokenTokenAmountAndToAddressSelector = createSelector(
|
|||
let tokenAmount = 0
|
||||
|
||||
if (params && params.length) {
|
||||
toAddress = params.find(param => param.name === TOKEN_PARAM_TO).value
|
||||
let value = Number(params.find(param => param.name === TOKEN_PARAM_VALUE).value)
|
||||
toAddress = params.find((param) => param.name === TOKEN_PARAM_TO).value
|
||||
let value = Number(params.find((param) => param.name === TOKEN_PARAM_VALUE).value)
|
||||
|
||||
if (tokenDecimals) {
|
||||
value = calcTokenAmount(value, tokenDecimals)
|
||||
value = calcTokenAmount(value, tokenDecimals).toNumber()
|
||||
}
|
||||
|
||||
tokenAmount = roundExponential(value)
|
||||
|
|
|
@ -54,9 +54,8 @@ async function startApp (metamaskState, accountManager, opts) {
|
|||
const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network)
|
||||
const numberOfUnapprivedTx = unapprovedTxsAll.length
|
||||
if (numberOfUnapprivedTx > 0) {
|
||||
|
||||
store.dispatch(actions.showConfTxPage({
|
||||
id: unapprovedTxsAll[numberOfUnapprivedTx - 1].id,
|
||||
id: unapprovedTxsAll[0].id,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue