Merge branch 'develop' into vb-rsk-dpath-update

This commit is contained in:
Victor Baranov 2020-05-04 14:53:43 +03:00 committed by GitHub
commit fa96fd072e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 598 additions and 754 deletions

View File

@ -2,6 +2,15 @@
## Current Master
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - Fix notifications order in batch requests
## 5.0.3 Fri May 01 2020
- [#373](https://github.com/poanetwork/nifty-wallet/pull/373) - (Feature) Add STAKE token
- [#372](https://github.com/poanetwork/nifty-wallet/pull/372) - (Chore) Update RSK contracts metadata repo
- [#369](https://github.com/poanetwork/nifty-wallet/pull/369) - (Fix) RSK: fix GasPrice calculation (changed interface of minimumGasPrice - hex instead of integer)
- [#368](https://github.com/poanetwork/nifty-wallet/pull/368) - (Fix) Ability to import Keystore file if it is not secured by password
- [#366](https://github.com/poanetwork/nifty-wallet/pull/366) - (Fix) Increase max token symbol length up to 12
- [#363](https://github.com/poanetwork/nifty-wallet/pull/363) - (Fix) token decimals display in pending tx screen
- [#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

View File

@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
"version": "5.0.2",
"version": "5.0.3",
"manifest_version": 2,
"author": "POA Network",
"description": "__MSG_appDescription__",

View File

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

View File

@ -1,8 +1,8 @@
import Web3 from 'web3'
import contractsETH from 'eth-contract-metadata'
import contractsPOA from 'poa-contract-metadata'
import contractsRSK from 'rsk-contract-metadata'
import contractsRSKTest from 'rsk-test-contract-metadata'
import contractsRSK from '@rsksmart/rsk-contract-metadata'
import contractsRSKTest from '@rsksmart/rsk-testnet-contract-metadata'
import { warn } from 'loglevel'
const { MAINNET, POA, RSK, RSK_TESTNET } = require('./network/enums')
// By default, poll every 3 minutes

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,
})
@ -1893,7 +1893,7 @@ module.exports = class MetamaskController extends EventEmitter {
resolve(gasPrice)
}
} else if (isRSK) {
gasPrice = this.getGasPriceFromLastBlockRSK(networkId)
gasPrice = this.getGasPriceFromLastBlockRSK()
resolve(gasPrice)
} else {
gasPrice = this.getGasPriceFromBlocks(networkId)
@ -1944,19 +1944,17 @@ module.exports = class MetamaskController extends EventEmitter {
* Related issue: https://github.com/poanetwork/nifty-wallet/issues/301
* @returns {string} A hex representation of the suggested wei gas price.
*/
getGasPriceFromLastBlockRSK (networkId) {
getGasPriceFromLastBlockRSK () {
const { recentBlocksController } = this
const { recentBlocks } = recentBlocksController.store.getState()
const recentBlock = recentBlocks
.sort((block1, block2) => block1.number - block2.number)[recentBlocks.length - 1]
const gasPrice = recentBlock && recentBlock.minimumGasPrice
const gasPrice = recentBlock && recentBlock.minimumGasPrice && recentBlock.minimumGasPrice.toString()
const gasPriceInt = parseInt(gasPrice, 10)
if (gasPriceInt !== 0) {
return '0x' + gasPriceInt.toString(16)
if (gasPrice !== '0x' && gasPrice !== '0x0' && gasPrice !== '') {
return gasPrice
} else {
return '0x' + GWEI_BN.toString(16)
}

View File

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

View File

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

View File

@ -73,11 +73,11 @@ createCopyTasks('contractImagesPOA', {
destinations: commonPlatforms.map(platform => `./dist/${platform}/images/contractPOA`),
})
createCopyTasks('contractImagesRSK', {
source: './node_modules/rsk-contract-metadata/images/',
source: './node_modules/@rsksmart/rsk-contract-metadata/images/',
destinations: commonPlatforms.map(platform => `./dist/${platform}/images/contractRSK`),
})
createCopyTasks('contractImagesRSKTest', {
source: './node_modules/rsk-test-contract-metadata/images/',
source: './node_modules/@rsksmart/rsk-testnet-contract-metadata/images/',
destinations: commonPlatforms.map(platform => `./dist/${platform}/images/contractRSKTest`),
})
createCopyTasks('fonts', {

View File

@ -1,9 +1,9 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
import { connect } from 'react-redux'
const actions = require('../../../../ui/app/actions')
const FileInput = require('react-simple-file-input').default
const PropTypes = require('prop-types')
import PropTypes from 'prop-types'
class JsonImportSubview extends Component {
constructor (props) {
@ -75,31 +75,26 @@ class JsonImportSubview extends Component {
}
createNewKeychain () {
const { displayWarning, importNewJsonAccount } = this.props
const { fileContents } = this.state
if (!fileContents) {
const message = 'You must select a file to import.'
return this.props.displayWarning(message)
return displayWarning(message)
}
const passwordInput = document.getElementById('json-password-box')
const password = passwordInput.value
if (!password) {
const message = 'You must enter a password for the selected file.'
return this.props.displayWarning(message)
}
this.props.importNewAccount([ fileContents, password ])
// JS runtime requires caught rejections but failures are handled by Redux
.catch()
importNewJsonAccount([ fileContents, password ])
.catch((err) => err && displayWarning(err.message || err))
}
}
JsonImportSubview.propTypes = {
error: PropTypes.string,
displayWarning: PropTypes.func,
importNewAccount: PropTypes.func,
importNewJsonAccount: PropTypes.func,
}
const mapStateToProps = state => {
@ -112,7 +107,7 @@ const mapDispatchToProps = dispatch => {
return {
goHome: () => dispatch(actions.goHome()),
displayWarning: warning => dispatch(actions.displayWarning(warning)),
importNewAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
}
}

View File

@ -368,9 +368,9 @@ export default class AddTokenScreen extends Component {
}
const symbolLen = symbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
const validSymbol = symbolLen > 0 && symbolLen < 23
if (!validSymbol) {
msg += 'Symbol must be between 0 and 10 characters.'
msg += 'Symbol must be between 0 and 23 characters.'
}
let ownAddress = identitiesList.includes(standardAddress)
@ -527,8 +527,8 @@ export default class AddTokenScreen extends Component {
const symbolLength = customSymbol.length
let customSymbolError = null
if (symbolLength <= 0 || symbolLength >= 10) {
customSymbolError = 'Symbol must be between 0 and 10 characters.' /* this.context.t('symbolBetweenZeroTen')*/
if (symbolLength <= 0 || symbolLength >= 23) {
customSymbolError = 'Symbol must be between 0 and 23 characters.' /* this.context.t('symbolBetweenZeroTen')*/
}
this.setState({ customSymbol, customSymbolError })

View File

@ -2,8 +2,8 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import contractMapETH from 'eth-contract-metadata'
import contractMapPOA from 'poa-contract-metadata'
import contractMapRSK from 'rsk-contract-metadata'
import contractMapRSKTest from 'rsk-test-contract-metadata'
import contractMapRSK from '@rsksmart/rsk-contract-metadata'
import contractMapRSKTest from '@rsksmart/rsk-testnet-contract-metadata'
import Fuse from 'fuse.js'
import InputAdornment from '@material-ui/core/InputAdornment'
import TextField from '../../../../../ui/app/components/text-field'

View File

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

View File

@ -5,7 +5,7 @@ import PropTypes from 'prop-types'
import clone from 'clone'
import log from 'loglevel'
const ethUtil = require('ethereumjs-util')
import ethUtil from 'ethereumjs-util'
const BN = ethUtil.BN
const hexToBn = require('../../../app/scripts/lib/hex-to-bn')
const util = require('../util')
@ -47,12 +47,12 @@ class PendingTx extends Component {
isUnlocked: PropTypes.bool,
currentCurrency: PropTypes.string,
conversionRate: PropTypes.number,
unconfTxListLength: PropTypes.number,
provider: PropTypes.object,
index: PropTypes.number,
blockGasLimit: PropTypes.string,
tokensToSend: PropTypes.objectOf(BigNumber),
tokensTransferTo: PropTypes.string,
unapprovedTxs: PropTypes.object,
}
constructor (opts = {}) {
@ -132,12 +132,13 @@ class PendingTx extends Component {
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
const { totalTx, positionOfCurrentTx, nextTxId, prevTxId, showNavigation } = this.getNavigateTxData()
const balanceBn = hexToBn(balance)
const insufficientBalance = balanceBn.lt(maxCost)
const dangerousGasLimit = gasBn.gte(saferGasLimitBN)
const gasLimitSpecified = txMeta.gasLimitSpecified
const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting
const showRejectAll = props.unconfTxListLength > 1
const isNotification = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION
@ -159,7 +160,6 @@ class PendingTx extends Component {
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
return (
h('div', {
@ -180,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',
},
@ -198,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),
}),
])],
),
@ -520,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',
@ -732,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 () {
@ -756,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,
@ -765,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()),
},

View File

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

View File

@ -19,8 +19,8 @@ const defaultTokens = []
const contractsETH = require('eth-contract-metadata')
const contractsPOA = require('poa-contract-metadata')
const contractsRSK = require('rsk-contract-metadata')
const contractsRSKTest = require('rsk-test-contract-metadata')
const contractsRSK = require('@rsksmart/rsk-contract-metadata')
const contractsRSKTest = require('@rsksmart/rsk-testnet-contract-metadata')
for (const address in contractsETH) {
const contract = contractsETH[address]
if (contract.erc20) {

View File

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

View File

@ -1,8 +1,8 @@
import { isValidAddress } from 'ethereumjs-util'
import contractMapETH from 'eth-contract-metadata'
import contractMapPOA from 'poa-contract-metadata'
import contractMapRSK from 'rsk-contract-metadata'
import contractMapRSKTest from 'rsk-test-contract-metadata'
import contractMapRSK from '@rsksmart/rsk-contract-metadata'
import contractMapRSKTest from '@rsksmart/rsk-testnet-contract-metadata'
import { MAINNET_CODE, POA_CODE, RSK_CODE, RSK_TESTNET_CODE } from '../../app/scripts/controllers/network/enums'
const colors = require('../../colors')
const { toChecksumAddress, getTokenImageFolder } = require('../app/util')

22
package-lock.json generated
View File

@ -1966,6 +1966,14 @@
"react-lifecycles-compat": "^3.0.4"
}
},
"@rsksmart/rsk-contract-metadata": {
"version": "github:rsksmart/rsk-contract-metadata#d7913739e5ee93dac8667043e2c17b0ef339c206",
"from": "github:rsksmart/rsk-contract-metadata#master"
},
"@rsksmart/rsk-testnet-contract-metadata": {
"version": "github:rsksmart/rsk-testnet-contract-metadata#2b89e70d36d2aa58cae68ac817debbf3c451690a",
"from": "github:rsksmart/rsk-testnet-contract-metadata#master"
},
"@sentry/cli": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.52.0.tgz",
@ -11355,9 +11363,9 @@
}
},
"eth-contract-metadata": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/eth-contract-metadata/-/eth-contract-metadata-1.12.1.tgz",
"integrity": "sha512-9u2jUcdxaKIv4RvA9RtjyD4+M2yWt4yCulR5bpdQTiG3HUFnN9lHtNL5NIRDpvQVJKerFhexrgEM2WdGP3a6VA=="
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/eth-contract-metadata/-/eth-contract-metadata-1.13.0.tgz",
"integrity": "sha512-9CjXHX8IdXysUEvOHdbCsjdAwM1E98jaeK2HeOqm/9S/vOZ8YryaBBt/YSiBq3MkpCwf+d1pEQ53p96rsdy52w=="
},
"eth-ens-namehash": {
"version": "2.0.8",
@ -49791,14 +49799,6 @@
"uuid": "^3.3.2"
}
},
"rsk-contract-metadata": {
"version": "github:rsksmart/rsk-contract-metadata#262495abfa6ff83fb9cd46c7fb9f85690e4e5d4b",
"from": "github:rsksmart/rsk-contract-metadata#master"
},
"rsk-test-contract-metadata": {
"version": "github:rsksmart/rsk-testnet-contract-metadata#69ff2d652b286648e9264e2689009e940ec7ccad",
"from": "github:rsksmart/rsk-testnet-contract-metadata#master"
},
"rst-selector-parser": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",

View File

@ -85,6 +85,8 @@
"dependencies": {
"@babel/runtime": "^7.5.5",
"@material-ui/core": "^4.1.1",
"@rsksmart/rsk-contract-metadata": "github:rsksmart/rsk-contract-metadata#master",
"@rsksmart/rsk-testnet-contract-metadata": "github:rsksmart/rsk-testnet-contract-metadata#master",
"@zxing/library": "^0.8.0",
"abi-decoder": "^1.2.0",
"asmcrypto.js": "0.22.0",
@ -109,7 +111,7 @@
"dnode": "^1.2.2",
"end-of-stream": "^1.4.4",
"eth-block-tracker": "^4.4.2",
"eth-contract-metadata": "^1.12.1",
"eth-contract-metadata": "^1.13.0",
"eth-ens-namehash": "^2.0.8",
"eth-json-rpc-errors": "^2.0.2",
"eth-json-rpc-filters": "github:poanetwork/eth-json-rpc-filters#3.0.2",
@ -198,8 +200,6 @@
"reselect": "^3.0.1",
"rockicon": "^1.0.0",
"rpc-cap": "^2.0.0",
"rsk-contract-metadata": "github:rsksmart/rsk-contract-metadata#master",
"rsk-test-contract-metadata": "github:rsksmart/rsk-testnet-contract-metadata#master",
"sandwich-expando": "^1.1.3",
"semaphore": "^1.0.5",
"semver": "^5.4.1",

View File

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

View File

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

View File

@ -161,9 +161,9 @@ describe('MetaMaskController', function () {
getState: () => {
return {
recentBlocks: [
{ number: '0x1', minimumGasPrice: '59240010' },
{ number: '0x2', minimumGasPrice: '59240005' },
{ number: '0x3', minimumGasPrice: '59240000' },
{ number: '0x1', minimumGasPrice: '0x387ee48' },
{ number: '0x2', minimumGasPrice: '0x387ee42' },
{ number: '0x3', minimumGasPrice: '0x387ee40' },
],
}
},
@ -175,7 +175,7 @@ describe('MetaMaskController', function () {
getState: () => {
return {
recentBlocks: [
{ number: '0x4', minimumGasPrice: '0' },
{ number: '0x4', minimumGasPrice: '0x' },
],
}
},

View File

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

View File

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

View File

@ -19,6 +19,7 @@ const { POA,
CLASSIC } = require('../../app/scripts/controllers/network/enums')
const { hasUnconfirmedTransactions } = require('./helpers/confirm-transaction/util')
const WebcamUtils = require('../lib/webcam-utils')
import { getEnvironmentType } from '../../app/scripts/lib/util'
const actions = {
_setBackgroundConnection: _setBackgroundConnection,
@ -30,6 +31,9 @@ const actions = {
MODAL_CLOSE: 'UI_MODAL_CLOSE',
showModal: showModal,
hideModal: hideModal,
CLOSE_NOTIFICATION_WINDOW: 'CLOSE_NOTIFICATION_WINDOW',
// sidebar state
SIDEBAR_OPEN: 'UI_SIDEBAR_OPEN',
SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE',
@ -173,7 +177,6 @@ const actions = {
COMPLETED_TX: 'COMPLETED_TX',
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
NEXT_TX: 'NEXT_TX',
PREVIOUS_TX: 'PREV_TX',
EDIT_TX: 'EDIT_TX',
signMsg: signMsg,
cancelMsg: cancelMsg,
@ -186,14 +189,12 @@ const actions = {
signTokenTx: signTokenTx,
updateTransaction,
updateAndApproveTx,
cancelTx: cancelTx,
cancelTx,
cancelTxs,
completedTx: completedTx,
txError: txError,
nextTx: nextTx,
editTx,
previousTx: previousTx,
cancelAllTx: cancelAllTx,
viewPendingTx: viewPendingTx,
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
updateTransactionParams,
@ -370,8 +371,9 @@ const actions = {
getRequestAccountTabIds,
setOpenMetamaskTabsIDs,
getOpenMetamaskTabsIds,
isCreatedWithCorrectDPath,
closeCurrentNotificationWindow,
closeNotificationWindow,
}
module.exports = actions
@ -945,9 +947,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) => {
@ -962,10 +963,7 @@ function signMsg (msgData) {
}
dispatch(actions.completedTx(msgData.metamaskId))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -975,7 +973,7 @@ function signMsg (msgData) {
function signPersonalMsg (msgData) {
log.debug('action - signPersonalMsg')
return (dispatch, getState) => {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
@ -993,9 +991,7 @@ function signPersonalMsg (msgData) {
dispatch(actions.completedTx(msgData.metamaskId))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -1005,7 +1001,7 @@ function signPersonalMsg (msgData) {
function signTypedMsg (msgData) {
log.debug('action - signTypedMsg')
return (dispatch, getState) => {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
@ -1023,9 +1019,7 @@ function signTypedMsg (msgData) {
dispatch(actions.completedTx(msgData.metamaskId))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -1232,7 +1226,7 @@ function clearSend () {
function sendTx (txData) {
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
return (dispatch, getState) => {
return (dispatch) => {
log.debug(`actions calling background.approveTransaction`)
background.approveTransaction(txData.id, (err) => {
if (err) {
@ -1241,10 +1235,7 @@ function sendTx (txData) {
return log.error(err)
}
dispatch(actions.completedTx(txData.id))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
})
}
}
@ -1294,10 +1285,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))
@ -1320,11 +1310,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
})
@ -1358,7 +1344,7 @@ function txError (err) {
}
function cancelMsg (msgData) {
return (dispatch, getState) => {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
@ -1373,9 +1359,7 @@ function cancelMsg (msgData) {
dispatch(actions.completedTx(msgData.id))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -1384,7 +1368,7 @@ function cancelMsg (msgData) {
}
function cancelPersonalMsg (msgData) {
return (dispatch, getState) => {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
@ -1399,9 +1383,7 @@ function cancelPersonalMsg (msgData) {
dispatch(actions.completedTx(id))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -1410,7 +1392,7 @@ function cancelPersonalMsg (msgData) {
}
function cancelTypedMsg (msgData) {
return (dispatch, getState) => {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => {
@ -1425,9 +1407,7 @@ function cancelTypedMsg (msgData) {
dispatch(actions.completedTx(id))
if (!hasUnconfirmedTransactions(getState())) {
return global.platform.closeNotificationWindow()
}
dispatch(actions.closeCurrentNotificationWindow())
return resolve(msgData)
})
@ -1436,12 +1416,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)
}
@ -1450,15 +1429,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
})
@ -1471,9 +1447,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) {
@ -1495,30 +1471,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
//
@ -1789,9 +1747,10 @@ function showConfTxPage (screenParams) {
}
}
function nextTx () {
function nextTx (txId) {
return {
type: actions.NEXT_TX,
value: txId,
}
}
@ -1802,12 +1761,6 @@ function viewPendingTx (txId) {
}
}
function previousTx () {
return {
type: actions.PREVIOUS_TX,
}
}
function editTx (txId) {
return {
type: actions.EDIT_TX,
@ -2158,6 +2111,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,
@ -2362,7 +2332,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,
})

View File

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

View File

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

View File

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

View File

@ -2,8 +2,8 @@ import log from 'loglevel'
import BigNumber from 'bignumber.js'
import contractMapETH from 'eth-contract-metadata'
import contractMapPOA from 'poa-contract-metadata'
import contractMapRSK from 'rsk-contract-metadata'
import contractMapRSKTest from 'rsk-test-contract-metadata'
import contractMapRSK from '@rsksmart/rsk-contract-metadata'
import contractMapRSKTest from '@rsksmart/rsk-testnet-contract-metadata'
const util = require('./util')
const casedContractMapETH = Object.keys(contractMapETH).reduce((acc, base) => {

View File

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