commit
cb1169a6a9
|
@ -2,6 +2,13 @@
|
|||
|
||||
## Current Master
|
||||
|
||||
## 5.1.0 Tue May 12 2020
|
||||
|
||||
- [#356](https://github.com/poanetwork/nifty-wallet/pull/356) - (Backwards-compatibility feature) Custom derivation paths and access to funds in accounts derived from ETH dPath
|
||||
- [#379](https://github.com/poanetwork/nifty-wallet/pull/379) - (Feature) Ability to set custom nonce of tx
|
||||
- [#377](https://github.com/poanetwork/nifty-wallet/pull/377) - (Fix) Sign message screen: do not decode message if it is not hex encoded
|
||||
- [#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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "__MSG_appName__",
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "5.0.3",
|
||||
"version": "5.1.0",
|
||||
"manifest_version": 2,
|
||||
"author": "POA Network",
|
||||
"description": "__MSG_appDescription__",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -12,10 +12,10 @@ import createJsonRpcClient from './createJsonRpcClient'
|
|||
import createLocalhostClient from './createLocalhostClient'
|
||||
const createPocketClient = require('./createPocketClient')
|
||||
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
|
||||
const ethNetProps = require('eth-net-props')
|
||||
import ethNetProps from 'eth-net-props'
|
||||
import parse from 'url-parse'
|
||||
const networks = { networkList: {} }
|
||||
const { isKnownProvider, getDPath } = require('../../../../old-ui/app/util')
|
||||
const { isKnownProvider } = require('../../../../old-ui/app/util')
|
||||
|
||||
const {
|
||||
ROPSTEN,
|
||||
|
@ -205,8 +205,6 @@ module.exports = class NetworkController extends EventEmitter {
|
|||
const previousNetworkID = this.getNetworkState()
|
||||
this.setNetworkState('loading')
|
||||
this._configureProvider(opts)
|
||||
const dPath = getDPath(opts.type)
|
||||
this.store.updateState({ dPath })
|
||||
this.emit('networkDidChange', opts.type, previousNetworkID)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -290,7 +290,8 @@ class TransactionController extends EventEmitter {
|
|||
*/
|
||||
async updateAndApproveTransaction (txMeta) {
|
||||
this.txStateManager.updateTx(txMeta, 'confTx: user approved transaction')
|
||||
await this.approveTransaction(txMeta.id)
|
||||
const customNonce = txMeta.txParams.nonce
|
||||
await this.approveTransaction(txMeta.id, customNonce)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -301,7 +302,7 @@ class TransactionController extends EventEmitter {
|
|||
if any of these steps fails the tx status will be set to failed
|
||||
@param txId {number} - the tx's Id
|
||||
*/
|
||||
async approveTransaction (txId) {
|
||||
async approveTransaction (txId, customNonce) {
|
||||
let nonceLock
|
||||
try {
|
||||
// approve
|
||||
|
@ -315,7 +316,7 @@ class TransactionController extends EventEmitter {
|
|||
// if txMeta has lastGasPrice then it is a retry at same nonce with higher
|
||||
// gas price transaction and their for the nonce should not be calculated
|
||||
const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
|
||||
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
|
||||
txMeta.txParams.nonce = customNonce || ethUtil.addHexPrefix(nonce.toString(16))
|
||||
// add nonce debugging information to txMeta
|
||||
txMeta.nonceDetails = nonceLock.nonceDetails
|
||||
this.txStateManager.updateTx(txMeta, 'transactions#approveTransaction')
|
||||
|
|
|
@ -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,5 +1,5 @@
|
|||
const KeyringController = require('eth-keychain-controller')
|
||||
const log = require('loglevel')
|
||||
import KeyringController from 'eth-keychain-controller'
|
||||
import log from 'loglevel'
|
||||
const { getDPath } = require('../../../old-ui/app/util')
|
||||
|
||||
const seedPhraseVerifier = {
|
||||
|
@ -17,14 +17,14 @@ const seedPhraseVerifier = {
|
|||
* @returns {Promise<void>} Promises undefined
|
||||
*
|
||||
*/
|
||||
verifyAccounts (createdAccounts, seedWords, network) {
|
||||
verifyAccounts (createdAccounts, seedWords, network, isCreatedWithCorrectDPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (!createdAccounts || createdAccounts.length < 1) {
|
||||
return reject(new Error('No created accounts defined.'))
|
||||
}
|
||||
|
||||
const dPath = getDPath(network)
|
||||
const dPath = getDPath(network, isCreatedWithCorrectDPath)
|
||||
const keyringController = new KeyringController({})
|
||||
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
|
||||
const opts = {
|
||||
|
@ -56,4 +56,4 @@ const seedPhraseVerifier = {
|
|||
},
|
||||
}
|
||||
|
||||
module.exports = seedPhraseVerifier
|
||||
export default seedPhraseVerifier
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -52,7 +52,7 @@ const version = require('../manifest.json').version
|
|||
import ethUtil, { BN } from 'ethereumjs-util'
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
import percentile from 'percentile'
|
||||
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
|
||||
import seedPhraseVerifier from './lib/seed-phrase-verifier'
|
||||
import log from 'loglevel'
|
||||
const TrezorKeyring = require('eth-trezor-keyring')
|
||||
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
|
||||
|
@ -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,
|
||||
})
|
||||
|
||||
|
@ -169,26 +169,32 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
})
|
||||
|
||||
// ensure accountTracker updates balances after network change
|
||||
this.networkController.on('networkDidChange', (newType, previousNetworkIDStr) => {
|
||||
const dPath = getDPath(newType)
|
||||
this.deriveKeyringFromNewDPath(dPath)
|
||||
.then(accounts => {
|
||||
this.accountTracker._updateAccounts()
|
||||
this.detectTokensController.restartTokenDetection()
|
||||
this.networkController.on('networkDidChange', (newNetworkType, previousNetworkIDStr) => {
|
||||
this.keyringController.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
const dPath = getDPath(newNetworkType, isCreatedWithCorrectDPath)
|
||||
this.deriveKeyringFromNewDPath(dPath)
|
||||
.then(_accounts => {
|
||||
this.accountTracker._updateAccounts()
|
||||
this.detectTokensController.restartTokenDetection()
|
||||
|
||||
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
|
||||
const nextNetwork = getNetworkID({network: newType})
|
||||
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
|
||||
if (nextNetworkID !== previousNetworkID) {
|
||||
const isPreviousETC = previousNetworkID === CLASSIC_CODE
|
||||
const isPreviousRSK = ifRSK(previousNetworkID)
|
||||
const isNextETC = nextNetworkID === CLASSIC_CODE
|
||||
const isNextRSK = ifRSK(nextNetworkID)
|
||||
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
|
||||
this.forgetDevice(LEDGER, false)
|
||||
this.forgetDevice(TREZOR, false)
|
||||
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
|
||||
const nextNetwork = getNetworkID({network: newNetworkType})
|
||||
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
|
||||
if (nextNetworkID !== previousNetworkID) {
|
||||
const isPreviousETC = previousNetworkID === CLASSIC_CODE
|
||||
const isPreviousRSK = ifRSK(previousNetworkID)
|
||||
const isNextETC = nextNetworkID === CLASSIC_CODE
|
||||
const isNextRSK = ifRSK(nextNetworkID)
|
||||
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
|
||||
this.forgetDevice(LEDGER, false)
|
||||
this.forgetDevice(TREZOR, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
|
@ -493,6 +499,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController),
|
||||
addNewMultisig: nodeify(keyringController.addNewMultisig, keyringController),
|
||||
exportAccount: nodeify(keyringController.exportAccount, keyringController),
|
||||
isCreatedWithCorrectDPath: nodeify(keyringController.isCreatedWithCorrectDPath, keyringController),
|
||||
|
||||
// txController
|
||||
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
||||
|
@ -582,7 +589,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @param {} password
|
||||
* @param {} seed
|
||||
*/
|
||||
async createNewVaultAndRestore (password, seed) {
|
||||
async createNewVaultAndRestore (password, seed, dPath) {
|
||||
const releaseLock = await this.createVaultMutex.acquire()
|
||||
try {
|
||||
let accounts, lastBalance
|
||||
|
@ -592,9 +599,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
// clear known identities
|
||||
this.preferencesController.setAddresses([])
|
||||
// create new vault
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
const dPath = getDPath(network)
|
||||
this.store.updateState({dPath})
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
const isCreatedWithCorrectDPath = true
|
||||
const vault = await keyringController.createNewVaultAndRestore(password, seed, dPath)
|
||||
|
||||
const ethQuery = new EthQuery(this.provider)
|
||||
|
@ -606,7 +612,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
|
||||
setDPath(primaryKeyring, network)
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
|
||||
// seek out the first zero balance
|
||||
while (lastBalance !== '0x0') {
|
||||
|
@ -911,13 +917,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @returns {} keyState
|
||||
*/
|
||||
async addNewAccount () {
|
||||
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
const keyringController = this.keyringController
|
||||
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
if (!primaryKeyring) {
|
||||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, network)
|
||||
const keyringController = this.keyringController
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
const oldAccounts = await keyringController.getAccounts()
|
||||
const keyState = await keyringController.addNewAccount(primaryKeyring)
|
||||
const newAccounts = await keyringController.getAccounts()
|
||||
|
@ -965,12 +972,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @returns {Promise<string>} Seed phrase to be confirmed by the user.
|
||||
*/
|
||||
async verifySeedPhrase () {
|
||||
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
const keyringController = this.keyringController
|
||||
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
|
||||
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
if (!primaryKeyring) {
|
||||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, network)
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
|
||||
const serialized = await primaryKeyring.serialize()
|
||||
const seedWords = serialized.mnemonic
|
||||
|
@ -981,7 +990,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
|
||||
try {
|
||||
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, network)
|
||||
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, networkType, isCreatedWithCorrectDPath)
|
||||
return seedWords
|
||||
} catch (err) {
|
||||
log.error(err.message)
|
||||
|
@ -1086,8 +1095,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
const privateKey = await accountImporter.importAccount(strategy, args)
|
||||
keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(keyring, network)
|
||||
const accounts = await keyring.getAccounts()
|
||||
// update accounts in preferences controller
|
||||
const allAccounts = await this.keyringController.getAccounts()
|
||||
|
@ -1945,7 +1952,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'
|
||||
|
|
|
@ -4,7 +4,7 @@ const Component = require('react').Component
|
|||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../ui/app/actions')
|
||||
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress } = require('./util')
|
||||
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress, ifLooseAcc, ifRSK, ifETC } = require('./util')
|
||||
const Identicon = require('./components/identicon')
|
||||
const EthBalance = require('./components/eth-balance')
|
||||
const TransactionList = require('./components/transaction-list')
|
||||
|
@ -14,10 +14,10 @@ const TabBar = require('./components/tab-bar')
|
|||
const TokenList = require('./components/token-list')
|
||||
const AccountDropdowns = require('./components/account-dropdowns/account-dropdowns.component').AccountDropdowns
|
||||
const CopyButton = require('./components/copy/copy-button')
|
||||
const ToastComponent = require('./components/toast')
|
||||
import * as Toast from './components/toast'
|
||||
import { getMetaMaskAccounts } from '../../ui/app/selectors'
|
||||
|
||||
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const accounts = getMetaMaskAccounts(state)
|
||||
|
@ -42,11 +42,66 @@ function mapStateToProps (state) {
|
|||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
actions: {
|
||||
showSendPage: () => dispatch(actions.showSendPage()),
|
||||
showSendContractPage: ({ methodSelected, methodABI, inputValues }) => dispatch(actions.showSendContractPage({methodSelected, methodABI, inputValues})),
|
||||
buyEthView: (selected) => dispatch(actions.buyEthView(selected)),
|
||||
viewPendingTx: (txId) => dispatch(actions.viewPendingTx(txId)),
|
||||
setAccountLabel: (account, label) => dispatch(actions.setAccountLabel(account, label)),
|
||||
showRemoveTokenPage: (token) => dispatch(actions.showRemoveTokenPage(token)),
|
||||
showAddSuggestedTokenPage: () => dispatch(actions.showAddSuggestedTokenPage()),
|
||||
showAddTokenPage: () => dispatch(actions.showAddTokenPage()),
|
||||
setCurrentAccountTab: (key) => dispatch(actions.setCurrentAccountTab(key)),
|
||||
displayToast: (msg) => dispatch(actions.displayToast(msg)),
|
||||
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
inherits(AccountDetailScreen, Component)
|
||||
function AccountDetailScreen () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.componentDidMount = function () {
|
||||
const props = this.props
|
||||
const { address, network, keyrings, identities } = props
|
||||
props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (!isCreatedWithCorrectDPath) {
|
||||
const currentKeyring = getCurrentKeyring(address, network, keyrings, identities)
|
||||
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(network) || ifETC(network))) {
|
||||
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.componentWillUpdate = function (nextProps) {
|
||||
const {
|
||||
network: oldNet,
|
||||
} = this.props
|
||||
const {
|
||||
network: newNet,
|
||||
} = nextProps
|
||||
|
||||
if (oldNet !== newNet) {
|
||||
const props = this.props
|
||||
const { address, keyrings, identities } = props
|
||||
props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (!isCreatedWithCorrectDPath) {
|
||||
const currentKeyring = getCurrentKeyring(address, newNet, keyrings, identities)
|
||||
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(newNet) || ifETC(newNet))) {
|
||||
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { network, conversionRate, currentCurrency } = props
|
||||
|
@ -56,7 +111,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
const account = props.accounts[selected]
|
||||
|
||||
if (Object.keys(props.suggestedTokens).length > 0) {
|
||||
this.props.dispatch(actions.showAddSuggestedTokenPage())
|
||||
this.props.actions.showAddSuggestedTokenPage()
|
||||
}
|
||||
|
||||
const currentKeyring = getCurrentKeyring(props.address, network, props.keyrings, props.identities)
|
||||
|
@ -65,8 +120,9 @@ AccountDetailScreen.prototype.render = function () {
|
|||
|
||||
h('.account-detail-section.full-flex-height', [
|
||||
|
||||
h(ToastComponent, {
|
||||
isSuccess: false,
|
||||
h(Toast.ToastComponent, {
|
||||
type: Toast.TOAST_TYPE_ERROR,
|
||||
hideManually: true,
|
||||
}),
|
||||
|
||||
// identicon, label, balance, etc
|
||||
|
@ -108,7 +164,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
isEditingLabel: false,
|
||||
},
|
||||
saveText: (text) => {
|
||||
props.dispatch(actions.setAccountLabel(selected, text))
|
||||
props.actions.setAccountLabel(selected, text)
|
||||
},
|
||||
}, [
|
||||
|
||||
|
@ -223,16 +279,16 @@ AccountDetailScreen.prototype.render = function () {
|
|||
h('.flex-grow'),
|
||||
|
||||
!ifContractAcc(currentKeyring) ? h('button', {
|
||||
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||
onClick: () => props.actions.buyEthView(selected),
|
||||
style: { marginRight: '10px' },
|
||||
}, 'Buy') : null,
|
||||
|
||||
h('button', {
|
||||
onClick: () => {
|
||||
if (ifContractAcc(currentKeyring)) {
|
||||
return props.dispatch(actions.showSendContractPage({}))
|
||||
return props.actions.showSendContractPage({})
|
||||
} else {
|
||||
return props.dispatch(actions.showSendPage())
|
||||
return props.actions.showSendPage()
|
||||
}
|
||||
},
|
||||
}, ifContractAcc(currentKeyring) ? 'Execute methods' : 'Send'),
|
||||
|
@ -278,7 +334,7 @@ AccountDetailScreen.prototype.tabSections = function () {
|
|||
],
|
||||
defaultTab: currentAccountTab || 'history',
|
||||
tabSelected: (key) => {
|
||||
this.props.dispatch(actions.setCurrentAccountTab(key))
|
||||
this.props.actions.setCurrentAccountTab(key)
|
||||
},
|
||||
}),
|
||||
|
||||
|
@ -297,8 +353,8 @@ AccountDetailScreen.prototype.tabSwitchView = function () {
|
|||
userAddress: address,
|
||||
network,
|
||||
tokens,
|
||||
addToken: () => this.props.dispatch(actions.showAddTokenPage()),
|
||||
removeToken: (token) => this.props.dispatch(actions.showRemoveTokenPage(token)),
|
||||
addToken: () => this.props.actions.showAddTokenPage(),
|
||||
removeToken: (token) => this.props.actions.showRemoveTokenPage(token),
|
||||
})
|
||||
default:
|
||||
return this.transactionList()
|
||||
|
@ -317,7 +373,7 @@ AccountDetailScreen.prototype.transactionList = function () {
|
|||
address,
|
||||
shapeShiftTxList,
|
||||
viewPendingTx: (txId) => {
|
||||
this.props.dispatch(actions.viewPendingTx(txId))
|
||||
this.props.actions.viewPendingTx(txId)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import actions from '../../../../ui/app/actions'
|
|||
import { connect } from 'react-redux'
|
||||
import { DropdownMenuItem } from '../dropdown'
|
||||
import Identicon from '../identicon'
|
||||
import { ifLooseAcc, ifContractAcc, ifHardwareAcc } from '../../util'
|
||||
import { ifLooseAcc, ifContractAcc, ifHardwareAcc, ifRSK, ifETC } from '../../util'
|
||||
import { getHdPaths, isLedger } from '../connect-hardware/util'
|
||||
import { LEDGER } from '../connect-hardware/enum'
|
||||
import { importTypes, labels } from '../../accounts/import/enums'
|
||||
import { ERROR_ON_INCORRECT_DPATH } from '../toast'
|
||||
|
||||
class AccountsDropdownItemView extends Component {
|
||||
static propTypes = {
|
||||
|
@ -100,7 +101,7 @@ class AccountsDropdownItemView extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
ifProxyAcc (address, setProxy) {
|
||||
ifProxyAcc (address, _setProxy) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.actions.getContract(address)
|
||||
.then(contractProps => {
|
||||
|
@ -138,6 +139,15 @@ class AccountsDropdownItemView extends Component {
|
|||
} else {
|
||||
this.preventToast()
|
||||
}
|
||||
} else if (!ifLooseAcc(keyring) && !ifContractAcc(keyring) && (ifRSK(this.props.network) || ifETC(this.props.network))) {
|
||||
this.props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (isCreatedWithCorrectDPath) {
|
||||
this.preventToast()
|
||||
} else {
|
||||
this.props.actions.displayToast(ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.preventToast()
|
||||
}
|
||||
|
@ -152,6 +162,13 @@ class AccountsDropdownItemView extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const result = {
|
||||
network: state.metamask.network,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
|
@ -163,10 +180,11 @@ const mapDispatchToProps = (dispatch) => {
|
|||
return dispatch(actions.connectHardwareAndUnlockAddress(deviceName, hdPath, address))
|
||||
},
|
||||
displayToast: (msg) => dispatch(actions.displayToast(msg)),
|
||||
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AccountsDropdownItemView: connect(null, mapDispatchToProps)(AccountsDropdownItemView),
|
||||
AccountsDropdownItemView: connect(mapStateToProps, mapDispatchToProps)(AccountsDropdownItemView),
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const Component = require('react').Component
|
||||
import { Component } from 'react'
|
||||
const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const extend = require('xtend')
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import extend from 'xtend'
|
||||
|
||||
module.exports = BinaryRenderer
|
||||
|
||||
|
@ -14,7 +14,7 @@ function BinaryRenderer () {
|
|||
BinaryRenderer.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { value, style } = props
|
||||
const text = this.hexToText(value)
|
||||
const message = this.msgHexToText(value)
|
||||
|
||||
const defaultStyle = extend({
|
||||
width: '100%',
|
||||
|
@ -30,16 +30,16 @@ BinaryRenderer.prototype.render = function () {
|
|||
h('textarea.font-small', {
|
||||
readOnly: true,
|
||||
style: defaultStyle,
|
||||
defaultValue: text,
|
||||
defaultValue: message,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
BinaryRenderer.prototype.hexToText = function (hex) {
|
||||
BinaryRenderer.prototype.msgHexToText = (hex) => {
|
||||
try {
|
||||
const stripped = ethUtil.stripHexPrefix(hex)
|
||||
const buff = Buffer.from(stripped, 'hex')
|
||||
return buff.toString('utf8')
|
||||
return buff.length === 32 ? hex : buff.toString('utf8')
|
||||
} catch (e) {
|
||||
return hex
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
@ -25,7 +25,7 @@ const { tokenInfoGetter, calcTokenAmount } = require('../../../ui/app/token-util
|
|||
import BigNumber from 'bignumber.js'
|
||||
import ethNetProps from 'eth-net-props'
|
||||
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
|
||||
import ToastComponent from './toast'
|
||||
import * as Toast from './toast'
|
||||
|
||||
const MIN_GAS_PRICE_BN = new BN('0')
|
||||
const MIN_GAS_LIMIT_BN = new BN('21000')
|
||||
|
@ -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
|
||||
|
||||
|
@ -157,15 +158,15 @@ class PendingTx extends Component {
|
|||
fontSize: '14px',
|
||||
}
|
||||
|
||||
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
|
||||
|
||||
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
|
||||
return (
|
||||
|
||||
h('div', {
|
||||
key: txMeta.id,
|
||||
}, [
|
||||
h(ToastComponent, {
|
||||
isSuccess: false,
|
||||
h(Toast.ToastComponent, {
|
||||
type: Toast.TOAST_TYPE_ERROR,
|
||||
}),
|
||||
|
||||
h('form#pending-tx-form', {
|
||||
|
@ -179,7 +180,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 +198,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 +520,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 +732,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 +773,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 +782,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()),
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ import PersistentForm from '../../../lib/persistent-form'
|
|||
import SendProfile from './send-profile'
|
||||
import SendHeader from './send-header'
|
||||
import ErrorComponent from '../error'
|
||||
import ToastComponent from '../toast'
|
||||
import * as Toast from '../toast'
|
||||
import Select from 'react-select'
|
||||
import actions from '../../../../ui/app/actions'
|
||||
import abi from 'web3-eth-abi'
|
||||
|
@ -154,7 +154,7 @@ class SendTransactionScreen extends PersistentForm {
|
|||
<SendProfile />
|
||||
<SendHeader title="Execute Method" />
|
||||
<ErrorComponent error={error} />
|
||||
<ToastComponent isSuccess={true} />
|
||||
<Toast.ToastComponent type={Toast.TOAST_TYPE_SUCCESS} />
|
||||
<div style={{ padding: '0 30px' }}>
|
||||
<Select
|
||||
clearable={false}
|
||||
|
|
|
@ -1,23 +1,220 @@
|
|||
const inherits = require('util').inherits
|
||||
const PersistentForm = require('../../../lib/persistent-form')
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../../../ui/app/actions')
|
||||
const {
|
||||
import React from 'react'
|
||||
import PersistentForm from '../../../lib/persistent-form'
|
||||
import { connect } from 'react-redux'
|
||||
import actions from '../../../../ui/app/actions'
|
||||
import {
|
||||
numericBalance,
|
||||
isHex,
|
||||
normalizeEthStringToWei,
|
||||
isInvalidChecksumAddress,
|
||||
isValidAddress,
|
||||
} = require('../../util')
|
||||
const EnsInput = require('../ens-input')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
} from '../../util'
|
||||
import EnsInput from '../ens-input'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import SendProfile from './send-profile'
|
||||
import SendHeader from './send-header'
|
||||
import ErrorComponent from '../error'
|
||||
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||
import ToastComponent from '../toast'
|
||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
||||
import * as Toast from '../toast'
|
||||
|
||||
const optionalDataLabelStyle = {
|
||||
background: '#ffffff',
|
||||
color: '#333333',
|
||||
marginTop: '16px',
|
||||
marginBottom: '16px',
|
||||
}
|
||||
const optionalDataValueStyle = {
|
||||
width: '100%',
|
||||
resize: 'none',
|
||||
}
|
||||
|
||||
class SendTransactionScreen extends PersistentForm {
|
||||
render () {
|
||||
this.persistentFormParentId = 'send-tx-form'
|
||||
|
||||
const props = this.props
|
||||
const {
|
||||
network,
|
||||
identities,
|
||||
addressBook,
|
||||
error,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className="send-screen flex-column flex-grow">
|
||||
<Toast.ToastComponent type={Toast.TOAST_TYPE_ERROR} />
|
||||
<SendProfile/>
|
||||
|
||||
<SendHeader
|
||||
title= "Send Transaction"
|
||||
/>
|
||||
|
||||
<ErrorComponent
|
||||
error={error}
|
||||
/>
|
||||
|
||||
<section className="flex-row flex-center">
|
||||
<EnsInput
|
||||
name="address"
|
||||
placeholder="Recipient Address"
|
||||
onChange={this.recipientDidChange.bind(this)}
|
||||
network={network}
|
||||
identities={identities}
|
||||
addressBook={addressBook}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="flex-row flex-center">
|
||||
|
||||
<input className="large-input"
|
||||
name= "amount"
|
||||
placeholder= "Amount"
|
||||
type= "number"
|
||||
style= {{
|
||||
marginRight: '6px',
|
||||
}}
|
||||
dataset={{
|
||||
persistentFormid: 'tx-amount',
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={this.onSubmit.bind(this)}>
|
||||
Next
|
||||
</button>
|
||||
|
||||
</section>
|
||||
|
||||
<h3 className="flex-center"
|
||||
style={optionalDataLabelStyle}
|
||||
>
|
||||
Transaction Data (optional)
|
||||
</h3>
|
||||
|
||||
<section className="flex-column flex-center">
|
||||
<input className="large-input"
|
||||
name= "txData"
|
||||
placeholder= "e.g. 0x01234"
|
||||
style={optionalDataValueStyle}
|
||||
dataset={{
|
||||
persistentFormid: 'tx-data',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<h3 className="flex-center"
|
||||
style={optionalDataLabelStyle}
|
||||
>
|
||||
Custom nonce (optional)
|
||||
</h3>
|
||||
|
||||
<section className="flex-column flex-center">
|
||||
<input className="large-input"
|
||||
name= "txCustomNonce"
|
||||
type= "number"
|
||||
placeholder= "e.g. 42"
|
||||
style={optionalDataValueStyle}
|
||||
dataset={{
|
||||
persistentFormid: 'tx-custom-nonce',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.props.dispatch(actions.displayWarning(''))
|
||||
}
|
||||
|
||||
navigateToAccounts (event) {
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.showAccountsPage())
|
||||
}
|
||||
|
||||
recipientDidChange (recipient, nickname) {
|
||||
this.setState({
|
||||
recipient: recipient,
|
||||
nickname: nickname,
|
||||
})
|
||||
}
|
||||
|
||||
onSubmit () {
|
||||
const state = this.state || {}
|
||||
let recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
||||
let nickname = state.nickname || ' '
|
||||
if (typeof recipient === 'object') {
|
||||
if (recipient.toAddress) {
|
||||
recipient = recipient.toAddress
|
||||
}
|
||||
if (recipient.nickname) {
|
||||
nickname = recipient.nickname
|
||||
}
|
||||
}
|
||||
const input = document.querySelector('input[name="amount"]').value
|
||||
const parts = input.split('.')
|
||||
|
||||
let message
|
||||
|
||||
if (isNaN(input) || input === '') {
|
||||
message = 'Invalid ether value.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (parts[1]) {
|
||||
const decimal = parts[1]
|
||||
if (decimal.length > 18) {
|
||||
message = 'Ether amount is too precise.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
}
|
||||
|
||||
const value = normalizeEthStringToWei(input)
|
||||
const txData = document.querySelector('input[name="txData"]').value
|
||||
const txCustomNonce = document.querySelector('input[name="txCustomNonce"]').value
|
||||
const balance = this.props.balance
|
||||
|
||||
if (value.gt(balance)) {
|
||||
message = 'Insufficient funds.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (input < 0) {
|
||||
message = 'Can not send negative amounts of ETH.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if ((isInvalidChecksumAddress(recipient, this.props.network))) {
|
||||
message = 'Recipient address checksum is invalid.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if ((!isValidAddress(recipient, this.props.network) && !txData) || (!recipient && !txData)) {
|
||||
message = 'Recipient address is invalid.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (!isHex(ethUtil.stripHexPrefix(txData)) && txData) {
|
||||
message = 'Transaction data must be hex string.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
this.props.dispatch(actions.hideWarning())
|
||||
|
||||
this.props.dispatch(actions.addToAddressBook(recipient, nickname))
|
||||
|
||||
const txParams = {
|
||||
from: this.props.address,
|
||||
value: '0x' + value.toString(16),
|
||||
}
|
||||
|
||||
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
|
||||
if (txData) txParams.data = txData
|
||||
if (txCustomNonce) txParams.nonce = '0x' + parseInt(txCustomNonce, 10).toString(16)
|
||||
|
||||
this.props.dispatch(actions.signTx(txParams))
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const accounts = getMetaMaskAccounts(state)
|
||||
|
@ -37,200 +234,4 @@ function mapStateToProps (state) {
|
|||
return result
|
||||
}
|
||||
|
||||
inherits(SendTransactionScreen, PersistentForm)
|
||||
function SendTransactionScreen () {
|
||||
PersistentForm.call(this)
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.render = function () {
|
||||
this.persistentFormParentId = 'send-tx-form'
|
||||
|
||||
const props = this.props
|
||||
const {
|
||||
network,
|
||||
identities,
|
||||
addressBook,
|
||||
error,
|
||||
} = props
|
||||
|
||||
return (
|
||||
|
||||
h('.send-screen.flex-column.flex-grow', [
|
||||
|
||||
h(ToastComponent, {
|
||||
isSuccess: false,
|
||||
}),
|
||||
|
||||
//
|
||||
// Sender Profile
|
||||
//
|
||||
|
||||
h(SendProfile),
|
||||
|
||||
//
|
||||
// Send Header
|
||||
//
|
||||
|
||||
h(SendHeader, {
|
||||
title: 'Send Transaction',
|
||||
}),
|
||||
|
||||
// error message
|
||||
h(ErrorComponent, {
|
||||
error,
|
||||
}),
|
||||
|
||||
// 'to' field
|
||||
h('section.flex-row.flex-center', [
|
||||
h(EnsInput, {
|
||||
name: 'address',
|
||||
placeholder: 'Recipient Address',
|
||||
onChange: this.recipientDidChange.bind(this),
|
||||
network,
|
||||
identities,
|
||||
addressBook,
|
||||
}),
|
||||
]),
|
||||
|
||||
// 'amount' and send button
|
||||
h('section.flex-row.flex-center', [
|
||||
|
||||
h('input.large-input', {
|
||||
name: 'amount',
|
||||
placeholder: 'Amount',
|
||||
type: 'number',
|
||||
style: {
|
||||
marginRight: '6px',
|
||||
},
|
||||
dataset: {
|
||||
persistentFormid: 'tx-amount',
|
||||
},
|
||||
}),
|
||||
|
||||
h('button', {
|
||||
onClick: this.onSubmit.bind(this),
|
||||
}, 'Next'),
|
||||
|
||||
]),
|
||||
|
||||
//
|
||||
// Optional Fields
|
||||
//
|
||||
h('h3.flex-center', {
|
||||
style: {
|
||||
background: '#ffffff',
|
||||
color: '#333333',
|
||||
marginTop: '16px',
|
||||
marginBottom: '16px',
|
||||
},
|
||||
}, [
|
||||
'Transaction Data (optional)',
|
||||
]),
|
||||
|
||||
// 'data' field
|
||||
h('section.flex-column.flex-center', [
|
||||
h('input.large-input', {
|
||||
name: 'txData',
|
||||
placeholder: '0x01234',
|
||||
style: {
|
||||
width: '100%',
|
||||
resize: 'none',
|
||||
},
|
||||
dataset: {
|
||||
persistentFormid: 'tx-data',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.componentWillUnmount = function () {
|
||||
this.props.dispatch(actions.displayWarning(''))
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.navigateToAccounts = function (event) {
|
||||
event.stopPropagation()
|
||||
this.props.dispatch(actions.showAccountsPage())
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
|
||||
this.setState({
|
||||
recipient: recipient,
|
||||
nickname: nickname,
|
||||
})
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.onSubmit = function () {
|
||||
const state = this.state || {}
|
||||
let recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
|
||||
let nickname = state.nickname || ' '
|
||||
if (typeof recipient === 'object') {
|
||||
if (recipient.toAddress) {
|
||||
recipient = recipient.toAddress
|
||||
}
|
||||
if (recipient.nickname) {
|
||||
nickname = recipient.nickname
|
||||
}
|
||||
}
|
||||
const input = document.querySelector('input[name="amount"]').value
|
||||
const parts = input.split('.')
|
||||
|
||||
let message
|
||||
|
||||
if (isNaN(input) || input === '') {
|
||||
message = 'Invalid ether value.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (parts[1]) {
|
||||
const decimal = parts[1]
|
||||
if (decimal.length > 18) {
|
||||
message = 'Ether amount is too precise.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
}
|
||||
|
||||
const value = normalizeEthStringToWei(input)
|
||||
const txData = document.querySelector('input[name="txData"]').value
|
||||
const balance = this.props.balance
|
||||
|
||||
if (value.gt(balance)) {
|
||||
message = 'Insufficient funds.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (input < 0) {
|
||||
message = 'Can not send negative amounts of ETH.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if ((isInvalidChecksumAddress(recipient, this.props.network))) {
|
||||
message = 'Recipient address checksum is invalid.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if ((!isValidAddress(recipient, this.props.network) && !txData) || (!recipient && !txData)) {
|
||||
message = 'Recipient address is invalid.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
if (!isHex(ethUtil.stripHexPrefix(txData)) && txData) {
|
||||
message = 'Transaction data must be hex string.'
|
||||
return this.props.dispatch(actions.displayWarning(message))
|
||||
}
|
||||
|
||||
this.props.dispatch(actions.hideWarning())
|
||||
|
||||
this.props.dispatch(actions.addToAddressBook(recipient, nickname))
|
||||
|
||||
const txParams = {
|
||||
from: this.props.address,
|
||||
value: '0x' + value.toString(16),
|
||||
}
|
||||
|
||||
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
|
||||
if (txData) txParams.data = txData
|
||||
|
||||
this.props.dispatch(actions.signTx(txParams))
|
||||
}
|
||||
module.exports = connect(mapStateToProps)(SendTransactionScreen)
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -4,12 +4,18 @@ import { connect } from 'react-redux'
|
|||
import classnames from 'classnames'
|
||||
import actions from '../../../ui/app/actions'
|
||||
|
||||
const TOAST_TYPE_SUCCESS = 'success'
|
||||
const TOAST_TYPE_ERROR = 'error'
|
||||
|
||||
const ERROR_ON_INCORRECT_DPATH = 'The account is derived from ETH derivation path despite you connected to another chain. If you are ready to switch to correct derivation path, just restore from the same seed phrase.'
|
||||
|
||||
class ToastComponent extends Component {
|
||||
static propTypes = {
|
||||
msg: PropTypes.string,
|
||||
toastMsg: PropTypes.string,
|
||||
isSuccess: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
hideToast: PropTypes.func,
|
||||
hideManually: PropTypes.bool,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
|
@ -19,10 +25,12 @@ class ToastComponent extends Component {
|
|||
|
||||
componentDidUpdate (prevProps) {
|
||||
if ((!prevProps.msg && this.props.msg) || (!prevProps.toastMsg && this.props.toastMsg)) {
|
||||
this.timerID = setTimeout(() => {
|
||||
this.props.hideToast()
|
||||
clearTimeout(this.timerID)
|
||||
}, 4000)
|
||||
if (!this.props.hideManually) {
|
||||
this.timerID = setTimeout(() => {
|
||||
this.props.hideToast()
|
||||
clearTimeout(this.timerID)
|
||||
}, 4000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,15 +39,23 @@ class ToastComponent extends Component {
|
|||
clearTimeout(this.timerID)
|
||||
}
|
||||
|
||||
_getClass (type) {
|
||||
switch (type) {
|
||||
case TOAST_TYPE_SUCCESS:
|
||||
return 'green'
|
||||
case TOAST_TYPE_ERROR:
|
||||
return 'red'
|
||||
default:
|
||||
return 'green'
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let toastMsg = this.props.msg || this.props.toastMsg
|
||||
toastMsg = (toastMsg && toastMsg.message) || toastMsg
|
||||
return toastMsg ? (
|
||||
<div
|
||||
className={classnames('toast', {
|
||||
'green': this.props.isSuccess,
|
||||
'red': !this.props.isSuccess,
|
||||
})}
|
||||
className={classnames('toast', this._getClass(this.props.type))}
|
||||
onClick={(e) => this.props.hideToast()}
|
||||
>{toastMsg}</div>
|
||||
) : null
|
||||
|
@ -58,4 +74,9 @@ function mapDispatchToProps (dispatch) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ToastComponent)
|
||||
module.exports = {
|
||||
ToastComponent: connect(mapStateToProps, mapDispatchToProps)(ToastComponent),
|
||||
TOAST_TYPE_SUCCESS,
|
||||
TOAST_TYPE_ERROR,
|
||||
ERROR_ON_INCORRECT_DPATH,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -293,7 +293,7 @@ app sections
|
|||
color: #ffffff !important;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
padding: 20px;
|
||||
width: 357px;
|
||||
line-height: 14px;
|
||||
position: fixed;
|
||||
|
|
|
@ -4,6 +4,7 @@ const Component = require('react').Component
|
|||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../../../../../ui/app/actions')
|
||||
const { getDPath } = require('../../../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
|
||||
|
||||
|
@ -16,6 +17,7 @@ function mapStateToProps (state) {
|
|||
return {
|
||||
warning: state.appState.warning,
|
||||
dPath: state.metamask.dPath,
|
||||
provider: state.metamask.provider,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +117,9 @@ RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
|
|||
}
|
||||
}
|
||||
|
||||
RevealSeedConfirmation.prototype.revealSeedWords = function () {
|
||||
RevealSeedConfirmation.prototype.revealSeedWords = async function () {
|
||||
const password = document.getElementById('password-box').value
|
||||
this.props.dispatch(actions.requestRevealSeed(password, this.props.dPath))
|
||||
const isCreatedWithCorrectDPath = this.props.dispatch(actions.isCreatedWithCorrectDPath())
|
||||
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
|
||||
this.props.dispatch(actions.requestRevealSeed(password, dPath))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const PersistentForm = require('../../../lib/persistent-form')
|
|||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../../../../ui/app/actions')
|
||||
const { getDPath } = require('../../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
|
||||
|
||||
|
@ -15,6 +16,7 @@ function mapStateToProps (state) {
|
|||
return {
|
||||
warning: state.appState.warning,
|
||||
forgottenPassword: state.appState.forgottenPassword,
|
||||
provider: state.metamask.provider,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,5 +187,7 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
|
|||
// submit
|
||||
this.warning = null
|
||||
this.props.dispatch(actions.displayWarning(this.warning))
|
||||
this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
|
||||
const isCreatedWithCorrectDPath = true
|
||||
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
|
||||
this.props.dispatch(actions.createNewVaultAndRestore(password, seed, dPath))
|
||||
}
|
||||
|
|
|
@ -111,11 +111,13 @@ UnlockScreen.prototype.onKeyPress = function (event) {
|
|||
UnlockScreen.prototype.submitPassword = async function (event) {
|
||||
const element = event.target
|
||||
const password = element.value
|
||||
const props = this.props
|
||||
// reset input
|
||||
element.value = ''
|
||||
try {
|
||||
const dPath = getDPath(this.props.provider.type) || this.props.dPath
|
||||
await this.props.dispatch(actions.tryUnlockMetamask(password, dPath))
|
||||
const isCreatedWithCorrectDPath = await props.dispatch(actions.isCreatedWithCorrectDPath())
|
||||
const dPath = getDPath(props.provider.type, isCreatedWithCorrectDPath) || props.dPath
|
||||
await props.dispatch(actions.tryUnlockMetamask(password, dPath))
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ const {
|
|||
RSK,
|
||||
RSK_TESTNET,
|
||||
RSK_TICK,
|
||||
// customDPaths,
|
||||
customDPaths,
|
||||
} = require('../../app/scripts/controllers/network/enums')
|
||||
|
||||
const valueTable = {
|
||||
|
@ -88,6 +88,7 @@ module.exports = {
|
|||
ifHardwareAcc,
|
||||
getAllKeyRingsAccounts,
|
||||
ifRSK,
|
||||
ifETC,
|
||||
ifRSKByProviderType,
|
||||
ifPOA,
|
||||
toChecksumAddress,
|
||||
|
@ -432,6 +433,12 @@ function ifRSK (network) {
|
|||
return numericNet === RSK_CODE || numericNet === RSK_TESTNET_CODE
|
||||
}
|
||||
|
||||
function ifETC (network) {
|
||||
if (!network) return false
|
||||
const numericNet = isNaN(network) ? network : parseInt(network)
|
||||
return numericNet === CLASSIC_CODE
|
||||
}
|
||||
|
||||
function ifRSKByProviderType (type) {
|
||||
if (!type) return false
|
||||
return type === RSK || type === RSK_TESTNET
|
||||
|
@ -557,14 +564,16 @@ function getNetworkID ({ network }) {
|
|||
}
|
||||
}
|
||||
|
||||
function getDPath (network) {
|
||||
// todo: return when the robust solution will be ready
|
||||
return `m/44'/60'/0'/0`
|
||||
// return customDPaths[network] || `m/44'/60'/0'/0`
|
||||
function getDPath (networkType, isCreatedWithCorrectDPath) {
|
||||
if (isCreatedWithCorrectDPath) {
|
||||
return customDPaths[networkType] || `m/44'/60'/0'/0`
|
||||
} else {
|
||||
return `m/44'/60'/0'/0`
|
||||
}
|
||||
}
|
||||
|
||||
function setDPath (keyring, network) {
|
||||
const dPath = getDPath(network)
|
||||
function setDPath (keyring, networkType, isCreatedWithCorrectDPath) {
|
||||
const dPath = getDPath(networkType, isCreatedWithCorrectDPath)
|
||||
if (dPath && keyring.setHdPath) {
|
||||
keyring.setHdPath(dPath)
|
||||
}
|
||||
|
|
|
@ -328,6 +328,6 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": "10.19.0",
|
||||
"npm": "^6.13.4"
|
||||
"npm": "^6.14.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)'),
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
const path = require('path')
|
||||
const Func = require('./func').Functions
|
||||
const account1 = '0x2E428ABd9313D256d64D1f69fe3929C3BE18fD1f'
|
||||
// todo:
|
||||
// const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
|
||||
const account1RSK = '0x2E428aBd9313D256d64D1f69fe3929c3Be18Fd1F'
|
||||
const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
|
||||
const account2 = '0xd7b7AFeCa35e32594e29504771aC847E2a803742'
|
||||
const testsFolder = './test-cases'
|
||||
const setup = require(`${testsFolder}/setup.spec`)
|
||||
const login = require(`${testsFolder}/login.spec`)
|
||||
const { accountCreation } = require(`${testsFolder}/account-creation.spec`)
|
||||
const { accountCreation, getCreatedAccounts } = require(`${testsFolder}/account-creation.spec`)
|
||||
const connectHDWallet = require(`${testsFolder}/connect-hd-wallet.spec`)
|
||||
const importAccount = require(`${testsFolder}/import-account.spec`)
|
||||
// const importContractAccount = require(`${testsFolder}/import-contract-account.spec`)
|
||||
const importContractAccount = require(`${testsFolder}/import-contract-account.spec`)
|
||||
const deleteImportedAccount = require(`${testsFolder}/delete-imported-account.spec`)
|
||||
const signData = require(`${testsFolder}/sign-data.spec`)
|
||||
const exportPrivateKey = require(`${testsFolder}/export-private-key.spec`)
|
||||
|
@ -100,9 +98,9 @@ describe('Metamask popup page', async function () {
|
|||
await importAccount(f)
|
||||
})
|
||||
|
||||
// describe('Import Contract account', async () => {
|
||||
// await importContractAccount(f, account1, getCreatedAccounts)
|
||||
// })
|
||||
describe('Import Contract account', async () => {
|
||||
await importContractAccount(f, account1, getCreatedAccounts)
|
||||
})
|
||||
|
||||
describe('Delete Imported Account', async () => {
|
||||
await deleteImportedAccount(f)
|
||||
|
|
|
@ -15,7 +15,7 @@ const deleteImportedAccount = async (f) => {
|
|||
})
|
||||
|
||||
it("Can't remove imported account with 'No' button", async function () {
|
||||
const button = await f.waitUntilShowUp(deleteImportedAccountScr.buttons.no)
|
||||
const button = await f.waitUntilShowUp(deleteImportedAccountScr.buttons.no2)
|
||||
assert.equal(await button.getText(), 'No', 'button has incorrect name')
|
||||
await f.click(button)
|
||||
await f.driver.findElements(main.container)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -345,7 +345,6 @@ describe('Transaction Controller', function () {
|
|||
to: '0x1678a085c290ebd122dc42cba69373b5953b831d',
|
||||
gasPrice: '0x77359400',
|
||||
gas: '0x7b0d',
|
||||
nonce: '0x4b',
|
||||
},
|
||||
metamaskNetworkId: currentNetworkId,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,8 +1,8 @@
|
|||
const assert = require('assert')
|
||||
const clone = require('clone')
|
||||
const KeyringController = require('eth-keychain-controller')
|
||||
import assert from 'assert'
|
||||
import clone from 'clone'
|
||||
import KeyringController from 'eth-keychain-controller'
|
||||
import seedPhraseVerifier from '../../../app/scripts/lib/seed-phrase-verifier'
|
||||
const firstTimeState = require('../../../app/scripts/first-time-state')
|
||||
const seedPhraseVerifier = require('../../../app/scripts/lib/seed-phrase-verifier')
|
||||
const mockEncryptor = require('../../lib/mock-encryptor')
|
||||
|
||||
describe('SeedPhraseVerifier', function () {
|
||||
|
|
|
@ -12,12 +12,12 @@ describe('BinaryRenderer', function () {
|
|||
})
|
||||
|
||||
it('recovers message', function () {
|
||||
const result = binaryRenderer.hexToText(hex)
|
||||
const result = binaryRenderer.msgHexToText(hex)
|
||||
assert.equal(result, message)
|
||||
})
|
||||
|
||||
it('recovers message with hex prefix', function () {
|
||||
const result = binaryRenderer.hexToText('0x' + hex)
|
||||
const result = binaryRenderer.msgHexToText('0x' + hex)
|
||||
assert.equal(result, message)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -235,7 +235,7 @@ describe('Actions', () => {
|
|||
|
||||
createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
|
||||
|
||||
createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
|
||||
createNewVaultAndRestoreSpy.callsFake((password, seed, dPath, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -181,19 +184,16 @@ const actions = {
|
|||
cancelPersonalMsg,
|
||||
signTypedMsg,
|
||||
cancelTypedMsg,
|
||||
sendTx: sendTx,
|
||||
signTx: signTx,
|
||||
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 +370,9 @@ const actions = {
|
|||
getRequestAccountTabIds,
|
||||
setOpenMetamaskTabsIDs,
|
||||
getOpenMetamaskTabsIds,
|
||||
isCreatedWithCorrectDPath,
|
||||
closeCurrentNotificationWindow,
|
||||
closeNotificationWindow,
|
||||
}
|
||||
|
||||
module.exports = actions
|
||||
|
@ -404,7 +407,12 @@ function tryUnlockMetamask (password, dPath) {
|
|||
})
|
||||
.then(() => {
|
||||
dispatch(actions.unlockSucceeded())
|
||||
return forceUpdateMetamaskState(dispatch)
|
||||
return updateMetamaskStateFromBackground()
|
||||
.then(newState => {
|
||||
newState = Object.assign(newState, {dPath: dPath})
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
forceUpdateMetamaskState(dispatch)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
|
@ -434,6 +442,21 @@ function tryUnlockMetamask (password, dPath) {
|
|||
}
|
||||
}
|
||||
|
||||
function isCreatedWithCorrectDPath () {
|
||||
return dispatch => {
|
||||
return new Promise((resolve, reject) => {
|
||||
background.isCreatedWithCorrectDPath((err, isCreatedWithCorrectDPath) => {
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve(isCreatedWithCorrectDPath)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function transitionForward () {
|
||||
return {
|
||||
type: this.TRANSITION_FORWARD,
|
||||
|
@ -466,7 +489,7 @@ function confirmSeedWords () {
|
|||
}
|
||||
}
|
||||
|
||||
function createNewVaultAndRestore (password, seed) {
|
||||
function createNewVaultAndRestore (password, seed, dPath) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
log.debug(`background.createNewVaultAndRestore`)
|
||||
|
@ -477,7 +500,7 @@ function createNewVaultAndRestore (password, seed) {
|
|||
return reject(err)
|
||||
}
|
||||
|
||||
background.createNewVaultAndRestore(password, seed, (err) => {
|
||||
background.createNewVaultAndRestore(password, seed, dPath, (err) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
|
@ -489,6 +512,11 @@ function createNewVaultAndRestore (password, seed) {
|
|||
.then(() => dispatch(actions.unMarkPasswordForgotten()))
|
||||
.then(() => {
|
||||
dispatch(actions.showAccountsPage())
|
||||
updateMetamaskStateFromBackground()
|
||||
.then(newState => {
|
||||
newState = Object.assign(newState, {dPath: dPath})
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
})
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
})
|
||||
.catch(err => {
|
||||
|
@ -918,9 +946,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 +962,7 @@ function signMsg (msgData) {
|
|||
}
|
||||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -948,7 +972,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 +990,7 @@ function signPersonalMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -978,7 +1000,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 +1018,7 @@ function signTypedMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.metamaskId))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1203,25 +1223,6 @@ function clearSend () {
|
|||
}
|
||||
|
||||
|
||||
function sendTx (txData) {
|
||||
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
|
||||
return (dispatch, getState) => {
|
||||
log.debug(`actions calling background.approveTransaction`)
|
||||
background.approveTransaction(txData.id, (err) => {
|
||||
if (err) {
|
||||
err = err.message || err.error || err
|
||||
dispatch(actions.txError(err))
|
||||
return log.error(err)
|
||||
}
|
||||
dispatch(actions.completedTx(txData.id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function signTokenTx (tokenAddress, toAddress, amount, txData, confTxScreenParams) {
|
||||
return dispatch => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
@ -1267,10 +1268,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 +1293,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 +1327,7 @@ function txError (err) {
|
|||
}
|
||||
|
||||
function cancelMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1346,9 +1342,7 @@ function cancelMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(msgData.id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1357,7 +1351,7 @@ function cancelMsg (msgData) {
|
|||
}
|
||||
|
||||
function cancelPersonalMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1372,9 +1366,7 @@ function cancelPersonalMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1383,7 +1375,7 @@ function cancelPersonalMsg (msgData) {
|
|||
}
|
||||
|
||||
function cancelTypedMsg (msgData) {
|
||||
return (dispatch, getState) => {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -1398,9 +1390,7 @@ function cancelTypedMsg (msgData) {
|
|||
|
||||
dispatch(actions.completedTx(id))
|
||||
|
||||
if (!hasUnconfirmedTransactions(getState())) {
|
||||
return global.platform.closeNotificationWindow()
|
||||
}
|
||||
dispatch(actions.closeCurrentNotificationWindow())
|
||||
|
||||
return resolve(msgData)
|
||||
})
|
||||
|
@ -1409,12 +1399,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 +1412,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 +1430,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 +1454,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 +1730,10 @@ function showConfTxPage (screenParams) {
|
|||
}
|
||||
}
|
||||
|
||||
function nextTx () {
|
||||
function nextTx (txId) {
|
||||
return {
|
||||
type: actions.NEXT_TX,
|
||||
value: txId,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1775,12 +1744,6 @@ function viewPendingTx (txId) {
|
|||
}
|
||||
}
|
||||
|
||||
function previousTx () {
|
||||
return {
|
||||
type: actions.PREVIOUS_TX,
|
||||
}
|
||||
}
|
||||
|
||||
function editTx (txId) {
|
||||
return {
|
||||
type: actions.EDIT_TX,
|
||||
|
@ -2131,6 +2094,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 +2315,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'),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const extend = require('xtend')
|
||||
import extend from 'xtend'
|
||||
import log from 'loglevel'
|
||||
const actions = require('../actions')
|
||||
const txHelper = require('../../lib/tx-helper')
|
||||
const { customHdPaths } = require('../../../old-ui/app/components/connect-hardware/util.js')
|
||||
const log = require('loglevel')
|
||||
|
||||
module.exports = reduceApp
|
||||
|
||||
|
@ -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