aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/helpers')
-rw-r--r--ui/app/helpers/constants/common.js10
-rw-r--r--ui/app/helpers/constants/error-keys.js4
-rw-r--r--ui/app/helpers/constants/infura-conversion.json653
-rw-r--r--ui/app/helpers/constants/routes.js83
-rw-r--r--ui/app/helpers/constants/transactions.js24
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/authenticated.component.js22
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/authenticated.container.js12
-rw-r--r--ui/app/helpers/higher-order-components/authenticated/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/i18n-provider.js55
-rw-r--r--ui/app/helpers/higher-order-components/initialized/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/initialized/initialized.component.js14
-rw-r--r--ui/app/helpers/higher-order-components/initialized/initialized.container.js12
-rw-r--r--ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js106
-rw-r--r--ui/app/helpers/higher-order-components/with-method-data/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js65
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js43
-rw-r--r--ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js21
-rw-r--r--ui/app/helpers/higher-order-components/with-token-tracker/index.js1
-rw-r--r--ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js106
-rw-r--r--ui/app/helpers/utils/common.util.js (renamed from ui/app/helpers/common.util.js)0
-rw-r--r--ui/app/helpers/utils/common.util.test.js (renamed from ui/app/helpers/tests/common.util.test.js)2
-rw-r--r--ui/app/helpers/utils/confirm-tx.util.js (renamed from ui/app/helpers/confirm-transaction/util.js)2
-rw-r--r--ui/app/helpers/utils/confirm-tx.util.test.js (renamed from ui/app/helpers/confirm-transaction/util.test.js)2
-rw-r--r--ui/app/helpers/utils/conversion-util.js251
-rw-r--r--ui/app/helpers/utils/conversion-util.test.js22
-rw-r--r--ui/app/helpers/utils/conversions.util.js (renamed from ui/app/helpers/conversions.util.js)2
-rw-r--r--ui/app/helpers/utils/formatters.js (renamed from ui/app/helpers/formatters.js)0
-rw-r--r--ui/app/helpers/utils/i18n-helper.js44
-rw-r--r--ui/app/helpers/utils/metametrics.util.js184
-rw-r--r--ui/app/helpers/utils/token-util.js118
-rw-r--r--ui/app/helpers/utils/transactions.util.js (renamed from ui/app/helpers/transactions.util.js)4
-rw-r--r--ui/app/helpers/utils/transactions.util.test.js (renamed from ui/app/helpers/tests/transactions.util.test.js)2
-rw-r--r--ui/app/helpers/utils/util.js326
34 files changed, 2187 insertions, 7 deletions
diff --git a/ui/app/helpers/constants/common.js b/ui/app/helpers/constants/common.js
new file mode 100644
index 000000000..c6e566b8b
--- /dev/null
+++ b/ui/app/helpers/constants/common.js
@@ -0,0 +1,10 @@
+export const ETH = 'ETH'
+export const GWEI = 'GWEI'
+export const WEI = 'WEI'
+
+export const PRIMARY = 'PRIMARY'
+export const SECONDARY = 'SECONDARY'
+
+export const NETWORK_TYPES = {
+ MAINNET: 'mainnet',
+}
diff --git a/ui/app/helpers/constants/error-keys.js b/ui/app/helpers/constants/error-keys.js
new file mode 100644
index 000000000..704064c96
--- /dev/null
+++ b/ui/app/helpers/constants/error-keys.js
@@ -0,0 +1,4 @@
+export const INSUFFICIENT_FUNDS_ERROR_KEY = 'insufficientFunds'
+export const GAS_LIMIT_TOO_LOW_ERROR_KEY = 'gasLimitTooLow'
+export const TRANSACTION_ERROR_KEY = 'transactionError'
+export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract'
diff --git a/ui/app/helpers/constants/infura-conversion.json b/ui/app/helpers/constants/infura-conversion.json
new file mode 100644
index 000000000..9a96fe069
--- /dev/null
+++ b/ui/app/helpers/constants/infura-conversion.json
@@ -0,0 +1,653 @@
+{
+ "objects": [
+ {
+ "symbol": "ethaud",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "aud",
+ "name": "Australian Dollar"
+ }
+ },
+ {
+ "symbol": "ethhkd",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "hkd",
+ "name": "Hong Kong Dollar"
+ }
+ },
+ {
+ "symbol": "ethsgd",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "sgd",
+ "name": "Singapore Dollar"
+ }
+ },
+ {
+ "symbol": "ethidr",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "idr",
+ "name": "Indonesian Rupiah"
+ }
+ },
+ {
+ "symbol": "ethphp",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "php",
+ "name": "Philippine Peso"
+ }
+ },
+ {
+ "symbol": "eth1st",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "1st",
+ "name": "FirstBlood"
+ }
+ },
+ {
+ "symbol": "ethadt",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "adt",
+ "name": "adToken"
+ }
+ },
+ {
+ "symbol": "ethadx",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "adx",
+ "name": "AdEx"
+ }
+ },
+ {
+ "symbol": "ethant",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "ant",
+ "name": "Aragon"
+ }
+ },
+ {
+ "symbol": "ethbat",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "bat",
+ "name": "Basic Attention Token"
+ }
+ },
+ {
+ "symbol": "ethbnt",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "bnt",
+ "name": "Bancor"
+ }
+ },
+ {
+ "symbol": "ethbtc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "btc",
+ "name": "Bitcoin"
+ }
+ },
+ {
+ "symbol": "ethcad",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "cad",
+ "name": "Canadian Dollar"
+ }
+ },
+ {
+ "symbol": "ethcfi",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "cfi",
+ "name": "Cofound.it"
+ }
+ },
+ {
+ "symbol": "ethcrb",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "crb",
+ "name": "CreditBit"
+ }
+ },
+ {
+ "symbol": "ethcvc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "cvc",
+ "name": "Civic"
+ }
+ },
+ {
+ "symbol": "ethdash",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "dash",
+ "name": "Dash"
+ }
+ },
+ {
+ "symbol": "ethdgd",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "dgd",
+ "name": "DigixDAO"
+ }
+ },
+ {
+ "symbol": "ethetc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "etc",
+ "name": "Ethereum Classic"
+ }
+ },
+ {
+ "symbol": "etheur",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "eur",
+ "name": "Euro"
+ }
+ },
+ {
+ "symbol": "ethfun",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "fun",
+ "name": "FunFair"
+ }
+ },
+ {
+ "symbol": "ethgbp",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "gbp",
+ "name": "Pound Sterling"
+ }
+ },
+ {
+ "symbol": "ethgno",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "gno",
+ "name": "Gnosis"
+ }
+ },
+ {
+ "symbol": "ethgnt",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "gnt",
+ "name": "Golem"
+ }
+ },
+ {
+ "symbol": "ethgup",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "gup",
+ "name": "Matchpool"
+ }
+ },
+ {
+ "symbol": "ethhmq",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "hmq",
+ "name": "Humaniq"
+ }
+ },
+ {
+ "symbol": "ethjpy",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "jpy",
+ "name": "Japanese Yen"
+ }
+ },
+ {
+ "symbol": "ethlgd",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "lgd",
+ "name": "Legends Room"
+ }
+ },
+ {
+ "symbol": "ethlsk",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "lsk",
+ "name": "Lisk"
+ }
+ },
+ {
+ "symbol": "ethltc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "ltc",
+ "name": "Litecoin"
+ }
+ },
+ {
+ "symbol": "ethlun",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "lun",
+ "name": "Lunyr"
+ }
+ },
+ {
+ "symbol": "ethmco",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "mco",
+ "name": "Monaco"
+ }
+ },
+ {
+ "symbol": "ethmtl",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "mtl",
+ "name": "Metal"
+ }
+ },
+ {
+ "symbol": "ethmyst",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "myst",
+ "name": "Mysterium"
+ }
+ },
+ {
+ "symbol": "ethnmr",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "nmr",
+ "name": "Numeraire"
+ }
+ },
+ {
+ "symbol": "ethomg",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "omg",
+ "name": "OmiseGO"
+ }
+ },
+ {
+ "symbol": "ethpay",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "pay",
+ "name": "TenX"
+ }
+ },
+ {
+ "symbol": "ethptoy",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "ptoy",
+ "name": "Patientory"
+ }
+ },
+ {
+ "symbol": "ethqrl",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "qrl",
+ "name": "Quantum-Resistant Ledger"
+ }
+ },
+ {
+ "symbol": "ethqtum",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "qtum",
+ "name": "Qtum"
+ }
+ },
+ {
+ "symbol": "ethrep",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "rep",
+ "name": "Augur"
+ }
+ },
+ {
+ "symbol": "ethrlc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "rlc",
+ "name": "iEx.ec"
+ }
+ },
+ {
+ "symbol": "ethrub",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "rub",
+ "name": "Russian Ruble"
+ }
+ },
+ {
+ "symbol": "ethsc",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "sc",
+ "name": "Siacoin"
+ }
+ },
+ {
+ "symbol": "ethsngls",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "sngls",
+ "name": "SingularDTV"
+ }
+ },
+ {
+ "symbol": "ethsnt",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "snt",
+ "name": "Status"
+ }
+ },
+ {
+ "symbol": "ethsteem",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "steem",
+ "name": "Steem"
+ }
+ },
+ {
+ "symbol": "ethstorj",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "storj",
+ "name": "Storj"
+ }
+ },
+ {
+ "symbol": "ethtime",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "time",
+ "name": "ChronoBank"
+ }
+ },
+ {
+ "symbol": "ethtkn",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "tkn",
+ "name": "TokenCard"
+ }
+ },
+ {
+ "symbol": "ethtrst",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "trst",
+ "name": "WeTrust"
+ }
+ },
+ {
+ "symbol": "ethuah",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "uah",
+ "name": "Ukrainian Hryvnia"
+ }
+ },
+ {
+ "symbol": "ethusd",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "usd",
+ "name": "United States Dollar"
+ }
+ },
+ {
+ "symbol": "ethwings",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "wings",
+ "name": "Wings"
+ }
+ },
+ {
+ "symbol": "ethxem",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "xem",
+ "name": "NEM"
+ }
+ },
+ {
+ "symbol": "ethxlm",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "xlm",
+ "name": "Stellar Lumen"
+ }
+ },
+ {
+ "symbol": "ethxmr",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "xmr",
+ "name": "Monero"
+ }
+ },
+ {
+ "symbol": "ethxrp",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "xrp",
+ "name": "Ripple"
+ }
+ },
+ {
+ "symbol": "ethzec",
+ "base": {
+ "code": "eth",
+ "name": "Ethereum"
+ },
+ "quote": {
+ "code": "zec",
+ "name": "Zcash"
+ }
+ }
+ ]
+}
diff --git a/ui/app/helpers/constants/routes.js b/ui/app/helpers/constants/routes.js
new file mode 100644
index 000000000..932dfa7df
--- /dev/null
+++ b/ui/app/helpers/constants/routes.js
@@ -0,0 +1,83 @@
+const DEFAULT_ROUTE = '/'
+const UNLOCK_ROUTE = '/unlock'
+const LOCK_ROUTE = '/lock'
+const SETTINGS_ROUTE = '/settings'
+const INFO_ROUTE = '/settings/info'
+const REVEAL_SEED_ROUTE = '/seed'
+const MOBILE_SYNC_ROUTE = '/mobile-sync'
+const CONFIRM_SEED_ROUTE = '/confirm-seed'
+const RESTORE_VAULT_ROUTE = '/restore-vault'
+const ADD_TOKEN_ROUTE = '/add-token'
+const CONFIRM_ADD_TOKEN_ROUTE = '/confirm-add-token'
+const CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE = '/confirm-add-suggested-token'
+const NEW_ACCOUNT_ROUTE = '/new-account'
+const IMPORT_ACCOUNT_ROUTE = '/new-account/import'
+const CONNECT_HARDWARE_ROUTE = '/new-account/connect'
+const SEND_ROUTE = '/send'
+const NOTICE_ROUTE = '/notice'
+const WELCOME_ROUTE = '/welcome'
+
+const INITIALIZE_ROUTE = '/initialize'
+const INITIALIZE_WELCOME_ROUTE = '/initialize/welcome'
+const INITIALIZE_UNLOCK_ROUTE = '/initialize/unlock'
+const INITIALIZE_CREATE_PASSWORD_ROUTE = '/initialize/create-password'
+const INITIALIZE_IMPORT_ACCOUNT_ROUTE = '/initialize/create-password/import-account'
+const INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE = '/initialize/create-password/import-with-seed-phrase'
+const INITIALIZE_UNIQUE_IMAGE_ROUTE = '/initialize/create-password/unique-image'
+const INITIALIZE_NOTICE_ROUTE = '/initialize/notice'
+const INITIALIZE_SELECT_ACTION_ROUTE = '/initialize/select-action'
+const INITIALIZE_SEED_PHRASE_ROUTE = '/initialize/seed-phrase'
+const INITIALIZE_END_OF_FLOW_ROUTE = '/initialize/end-of-flow'
+const INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE = '/initialize/seed-phrase/confirm'
+const INITIALIZE_METAMETRICS_OPT_IN_ROUTE = '/initialize/metametrics-opt-in'
+
+const CONFIRM_TRANSACTION_ROUTE = '/confirm-transaction'
+const CONFIRM_SEND_ETHER_PATH = '/send-ether'
+const CONFIRM_SEND_TOKEN_PATH = '/send-token'
+const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract'
+const CONFIRM_APPROVE_PATH = '/approve'
+const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from'
+const CONFIRM_TOKEN_METHOD_PATH = '/token-method'
+const SIGNATURE_REQUEST_PATH = '/signature-request'
+
+module.exports = {
+ DEFAULT_ROUTE,
+ UNLOCK_ROUTE,
+ LOCK_ROUTE,
+ SETTINGS_ROUTE,
+ INFO_ROUTE,
+ REVEAL_SEED_ROUTE,
+ MOBILE_SYNC_ROUTE,
+ CONFIRM_SEED_ROUTE,
+ RESTORE_VAULT_ROUTE,
+ ADD_TOKEN_ROUTE,
+ CONFIRM_ADD_TOKEN_ROUTE,
+ CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE,
+ NEW_ACCOUNT_ROUTE,
+ IMPORT_ACCOUNT_ROUTE,
+ CONNECT_HARDWARE_ROUTE,
+ SEND_ROUTE,
+ NOTICE_ROUTE,
+ WELCOME_ROUTE,
+ INITIALIZE_ROUTE,
+ INITIALIZE_WELCOME_ROUTE,
+ INITIALIZE_UNLOCK_ROUTE,
+ INITIALIZE_CREATE_PASSWORD_ROUTE,
+ INITIALIZE_IMPORT_ACCOUNT_ROUTE,
+ INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE,
+ INITIALIZE_UNIQUE_IMAGE_ROUTE,
+ INITIALIZE_NOTICE_ROUTE,
+ INITIALIZE_SELECT_ACTION_ROUTE,
+ INITIALIZE_SEED_PHRASE_ROUTE,
+ INITIALIZE_CONFIRM_SEED_PHRASE_ROUTE,
+ INITIALIZE_END_OF_FLOW_ROUTE,
+ CONFIRM_TRANSACTION_ROUTE,
+ CONFIRM_SEND_ETHER_PATH,
+ CONFIRM_SEND_TOKEN_PATH,
+ CONFIRM_DEPLOY_CONTRACT_PATH,
+ CONFIRM_APPROVE_PATH,
+ CONFIRM_TRANSFER_FROM_PATH,
+ CONFIRM_TOKEN_METHOD_PATH,
+ SIGNATURE_REQUEST_PATH,
+ INITIALIZE_METAMETRICS_OPT_IN_ROUTE,
+}
diff --git a/ui/app/helpers/constants/transactions.js b/ui/app/helpers/constants/transactions.js
new file mode 100644
index 000000000..d0a819b9b
--- /dev/null
+++ b/ui/app/helpers/constants/transactions.js
@@ -0,0 +1,24 @@
+export const UNAPPROVED_STATUS = 'unapproved'
+export const REJECTED_STATUS = 'rejected'
+export const APPROVED_STATUS = 'approved'
+export const SIGNED_STATUS = 'signed'
+export const SUBMITTED_STATUS = 'submitted'
+export const CONFIRMED_STATUS = 'confirmed'
+export const FAILED_STATUS = 'failed'
+export const DROPPED_STATUS = 'dropped'
+export const CANCELLED_STATUS = 'cancelled'
+
+export const TOKEN_METHOD_TRANSFER = 'transfer'
+export const TOKEN_METHOD_APPROVE = 'approve'
+export const TOKEN_METHOD_TRANSFER_FROM = 'transferfrom'
+
+export const SEND_ETHER_ACTION_KEY = 'sentEther'
+export const DEPLOY_CONTRACT_ACTION_KEY = 'contractDeployment'
+export const APPROVE_ACTION_KEY = 'approve'
+export const SEND_TOKEN_ACTION_KEY = 'sentTokens'
+export const TRANSFER_FROM_ACTION_KEY = 'transferFrom'
+export const SIGNATURE_REQUEST_KEY = 'signatureRequest'
+export const CONTRACT_INTERACTION_KEY = 'contractInteraction'
+export const CANCEL_ATTEMPT_ACTION_KEY = 'cancelAttempt'
+
+export const TRANSACTION_TYPE_SHAPESHIFT = 'shapeshift'
diff --git a/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js b/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js
new file mode 100644
index 000000000..c195d0e21
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/authenticated.component.js
@@ -0,0 +1,22 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { Redirect, Route } from 'react-router-dom'
+import { UNLOCK_ROUTE, INITIALIZE_ROUTE } from '../../constants/routes'
+
+export default function Authenticated (props) {
+ const { isUnlocked, completedOnboarding } = props
+
+ switch (true) {
+ case isUnlocked && completedOnboarding:
+ return <Route { ...props } />
+ case !completedOnboarding:
+ return <Redirect to={{ pathname: INITIALIZE_ROUTE }} />
+ default:
+ return <Redirect to={{ pathname: UNLOCK_ROUTE }} />
+ }
+}
+
+Authenticated.propTypes = {
+ isUnlocked: PropTypes.bool,
+ completedOnboarding: PropTypes.bool,
+}
diff --git a/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
new file mode 100644
index 000000000..6124b0fcd
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/authenticated.container.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import Authenticated from './authenticated.component'
+
+const mapStateToProps = state => {
+ const { metamask: { isUnlocked, completedOnboarding } } = state
+ return {
+ isUnlocked,
+ completedOnboarding,
+ }
+}
+
+export default connect(mapStateToProps)(Authenticated)
diff --git a/ui/app/helpers/higher-order-components/authenticated/index.js b/ui/app/helpers/higher-order-components/authenticated/index.js
new file mode 100644
index 000000000..05632ed21
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/authenticated/index.js
@@ -0,0 +1 @@
+export { default } from './authenticated.container'
diff --git a/ui/app/helpers/higher-order-components/i18n-provider.js b/ui/app/helpers/higher-order-components/i18n-provider.js
new file mode 100644
index 000000000..0e34e17e0
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/i18n-provider.js
@@ -0,0 +1,55 @@
+const { Component } = require('react')
+const connect = require('react-redux').connect
+const PropTypes = require('prop-types')
+const { withRouter } = require('react-router-dom')
+const { compose } = require('recompose')
+const t = require('../utils/i18n-helper').getMessage
+
+class I18nProvider extends Component {
+ tOrDefault = (key, defaultValue, ...args) => {
+ const { localeMessages: { current, en } = {} } = this.props
+ return t(current, key, ...args) || t(en, key, ...args) || defaultValue
+ }
+
+ getChildContext () {
+ const { localeMessages } = this.props
+ const { current, en } = localeMessages
+ return {
+ t (key, ...args) {
+ return t(current, key, ...args) || t(en, key, ...args) || `[${key}]`
+ },
+ tOrDefault: this.tOrDefault,
+ tOrKey (key, ...args) {
+ return this.tOrDefault(key, key, ...args)
+ },
+ }
+ }
+
+ render () {
+ return this.props.children
+ }
+}
+
+I18nProvider.propTypes = {
+ localeMessages: PropTypes.object,
+ children: PropTypes.object,
+}
+
+I18nProvider.childContextTypes = {
+ t: PropTypes.func,
+ tOrDefault: PropTypes.func,
+ tOrKey: PropTypes.func,
+}
+
+const mapStateToProps = state => {
+ const { localeMessages } = state
+ return {
+ localeMessages,
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps)
+)(I18nProvider)
+
diff --git a/ui/app/helpers/higher-order-components/initialized/index.js b/ui/app/helpers/higher-order-components/initialized/index.js
new file mode 100644
index 000000000..863fcb389
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/index.js
@@ -0,0 +1 @@
+export { default } from './initialized.container.js'
diff --git a/ui/app/helpers/higher-order-components/initialized/initialized.component.js b/ui/app/helpers/higher-order-components/initialized/initialized.component.js
new file mode 100644
index 000000000..2042c0046
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/initialized.component.js
@@ -0,0 +1,14 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { Redirect, Route } from 'react-router-dom'
+import { INITIALIZE_ROUTE } from '../../constants/routes'
+
+export default function Initialized (props) {
+ return props.completedOnboarding
+ ? <Route { ...props } />
+ : <Redirect to={{ pathname: INITIALIZE_ROUTE }} />
+}
+
+Initialized.propTypes = {
+ completedOnboarding: PropTypes.bool,
+}
diff --git a/ui/app/helpers/higher-order-components/initialized/initialized.container.js b/ui/app/helpers/higher-order-components/initialized/initialized.container.js
new file mode 100644
index 000000000..0e7f72bcb
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/initialized/initialized.container.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import Initialized from './initialized.component'
+
+const mapStateToProps = state => {
+ const { metamask: { completedOnboarding } } = state
+
+ return {
+ completedOnboarding,
+ }
+}
+
+export default connect(mapStateToProps)(Initialized)
diff --git a/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js b/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js
new file mode 100644
index 000000000..6086e03fb
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/metametrics/metametrics.provider.js
@@ -0,0 +1,106 @@
+import { Component } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import { withRouter } from 'react-router-dom'
+import { compose } from 'recompose'
+import {
+ getCurrentNetworkId,
+ getSelectedAsset,
+ getAccountType,
+ getNumberOfAccounts,
+ getNumberOfTokens,
+} from '../../../selectors/selectors'
+import {
+ txDataSelector,
+} from '../../../selectors/confirm-transaction'
+import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
+import {
+ sendMetaMetricsEvent,
+ sendCountIsTrackable,
+} from '../../utils/metametrics.util'
+
+class MetaMetricsProvider extends Component {
+ static propTypes = {
+ network: PropTypes.string.isRequired,
+ environmentType: PropTypes.string.isRequired,
+ activeCurrency: PropTypes.string.isRequired,
+ accountType: PropTypes.string.isRequired,
+ metaMetricsSendCount: PropTypes.number.isRequired,
+ children: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ }
+
+ static childContextTypes = {
+ metricsEvent: PropTypes.func,
+ }
+
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ previousPath: '',
+ currentPath: window.location.href,
+ }
+
+ props.history.listen(locationObj => {
+ this.setState({
+ previousPath: this.state.currentPath,
+ currentPath: window.location.href,
+ })
+ })
+ }
+
+ getChildContext () {
+ const props = this.props
+ const { pathname } = location
+ const { previousPath, currentPath } = this.state
+
+ return {
+ metricsEvent: (config = {}, overrides = {}) => {
+ const { eventOpts = {} } = config
+ const { name = '' } = eventOpts
+ const { pathname: overRidePathName = '' } = overrides
+ const isSendFlow = Boolean(name.match(/^send|^confirm/) || overRidePathName.match(/send|confirm/))
+
+ if (props.participateInMetaMetrics || config.isOptIn) {
+ return sendMetaMetricsEvent({
+ ...props,
+ ...config,
+ previousPath,
+ currentPath,
+ pathname,
+ excludeMetaMetricsId: isSendFlow && !sendCountIsTrackable(props.metaMetricsSendCount + 1),
+ ...overrides,
+ })
+ }
+ },
+ }
+ }
+
+ render () {
+ return this.props.children
+ }
+}
+
+const mapStateToProps = state => {
+ const txData = txDataSelector(state) || {}
+
+ return {
+ network: getCurrentNetworkId(state),
+ environmentType: getEnvironmentType(),
+ activeCurrency: getSelectedAsset(state),
+ accountType: getAccountType(state),
+ confirmTransactionOrigin: txData.origin,
+ metaMetricsId: state.metamask.metaMetricsId,
+ participateInMetaMetrics: state.metamask.participateInMetaMetrics,
+ metaMetricsSendCount: state.metamask.metaMetricsSendCount,
+ numberOfTokens: getNumberOfTokens(state),
+ numberOfAccounts: getNumberOfAccounts(state),
+ }
+}
+
+module.exports = compose(
+ withRouter,
+ connect(mapStateToProps)
+)(MetaMetricsProvider)
+
diff --git a/ui/app/helpers/higher-order-components/with-method-data/index.js b/ui/app/helpers/higher-order-components/with-method-data/index.js
new file mode 100644
index 000000000..f511e1ae7
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-method-data/index.js
@@ -0,0 +1 @@
+export { default } from './with-method-data.component'
diff --git a/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js b/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js
new file mode 100644
index 000000000..efa9ad0a2
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-method-data/with-method-data.component.js
@@ -0,0 +1,65 @@
+import React, { PureComponent } from 'react'
+import PropTypes from 'prop-types'
+import { getMethodData, getFourBytePrefix } from '../../utils/transactions.util'
+
+export default function withMethodData (WrappedComponent) {
+ return class MethodDataWrappedComponent extends PureComponent {
+ static propTypes = {
+ transaction: PropTypes.object,
+ knownMethodData: PropTypes.object,
+ addKnownMethodData: PropTypes.func,
+ }
+
+ static defaultProps = {
+ transaction: {},
+ knownMethodData: {},
+ }
+
+ state = {
+ methodData: {},
+ done: false,
+ error: null,
+ }
+
+ componentDidMount () {
+ this.fetchMethodData()
+ }
+
+ async fetchMethodData () {
+ const { transaction, knownMethodData, addKnownMethodData } = this.props
+ const { txParams: { data = '' } = {} } = transaction
+
+ if (data) {
+ try {
+ let methodData
+ const fourBytePrefix = getFourBytePrefix(data)
+ if (fourBytePrefix in knownMethodData) {
+ methodData = knownMethodData[fourBytePrefix]
+ } else {
+ methodData = await getMethodData(data)
+ if (!Object.entries(methodData).length === 0) {
+ addKnownMethodData(fourBytePrefix, methodData)
+ }
+ }
+
+ this.setState({ methodData, done: true })
+ } catch (error) {
+ this.setState({ done: true, error })
+ }
+ } else {
+ this.setState({ done: true })
+ }
+ }
+
+ render () {
+ const { methodData, done, error } = this.state
+
+ return (
+ <WrappedComponent
+ { ...this.props }
+ methodData={{ data: methodData, done, error }}
+ />
+ )
+ }
+ }
+}
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/index.js b/ui/app/helpers/higher-order-components/with-modal-props/index.js
new file mode 100644
index 000000000..e476b51d2
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/index.js
@@ -0,0 +1 @@
+export { default } from './with-modal-props'
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js b/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js
new file mode 100644
index 000000000..654e7062a
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/tests/with-modal-props.test.js
@@ -0,0 +1,43 @@
+
+import assert from 'assert'
+import configureMockStore from 'redux-mock-store'
+import { mount } from 'enzyme'
+import React from 'react'
+import withModalProps from '../with-modal-props'
+
+const mockState = {
+ appState: {
+ modal: {
+ modalState: {
+ props: {
+ prop1: 'prop1',
+ prop2: 2,
+ prop3: true,
+ },
+ },
+ },
+ },
+}
+
+describe('withModalProps', () => {
+ it('should return a component wrapped with modal state props', () => {
+ const TestComponent = props => (
+ <div className="test">Testing</div>
+ )
+ const WrappedComponent = withModalProps(TestComponent)
+ const store = configureMockStore()(mockState)
+ const wrapper = mount(
+ <WrappedComponent store={store} />
+ )
+
+ assert.ok(wrapper)
+ const testComponent = wrapper.find(TestComponent).at(0)
+ assert.equal(testComponent.length, 1)
+ assert.equal(testComponent.find('.test').text(), 'Testing')
+ const testComponentProps = testComponent.props()
+ assert.equal(testComponentProps.prop1, 'prop1')
+ assert.equal(testComponentProps.prop2, 2)
+ assert.equal(testComponentProps.prop3, true)
+ assert.equal(typeof testComponentProps.hideModal, 'function')
+ })
+})
diff --git a/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js b/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js
new file mode 100644
index 000000000..aac6b5a61
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-modal-props/with-modal-props.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux'
+import { hideModal } from '../../../store/actions'
+
+const mapStateToProps = state => {
+ const { appState } = state
+ const { props: modalProps } = appState.modal.modalState
+
+ return {
+ ...modalProps,
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ hideModal: () => dispatch(hideModal()),
+ }
+}
+
+export default function withModalProps (Component) {
+ return connect(mapStateToProps, mapDispatchToProps)(Component)
+}
diff --git a/ui/app/helpers/higher-order-components/with-token-tracker/index.js b/ui/app/helpers/higher-order-components/with-token-tracker/index.js
new file mode 100644
index 000000000..d401e81f1
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-token-tracker/index.js
@@ -0,0 +1 @@
+export { default } from './with-token-tracker.component'
diff --git a/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js b/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js
new file mode 100644
index 000000000..36f6a6efd
--- /dev/null
+++ b/ui/app/helpers/higher-order-components/with-token-tracker/with-token-tracker.component.js
@@ -0,0 +1,106 @@
+import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import TokenTracker from 'eth-token-tracker'
+
+export default function withTokenTracker (WrappedComponent) {
+ return class TokenTrackerWrappedComponent extends Component {
+ static propTypes = {
+ userAddress: PropTypes.string.isRequired,
+ token: PropTypes.object.isRequired,
+ }
+
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ string: '',
+ symbol: '',
+ error: null,
+ }
+
+ this.tracker = null
+ this.updateBalance = this.updateBalance.bind(this)
+ this.setError = this.setError.bind(this)
+ }
+
+ componentDidMount () {
+ this.createFreshTokenTracker()
+ }
+
+ componentDidUpdate (prevProps) {
+ const { userAddress: newAddress, token: { address: newTokenAddress } } = this.props
+ const { userAddress: oldAddress, token: { address: oldTokenAddress } } = prevProps
+
+ if ((oldAddress === newAddress) && (oldTokenAddress === newTokenAddress)) {
+ return
+ }
+
+ if ((!oldAddress || !newAddress) && (!oldTokenAddress || !newTokenAddress)) {
+ return
+ }
+
+ this.createFreshTokenTracker()
+ }
+
+ componentWillUnmount () {
+ this.removeListeners()
+ }
+
+ createFreshTokenTracker () {
+ this.removeListeners()
+
+ if (!global.ethereumProvider) {
+ return
+ }
+
+ const { userAddress, token } = this.props
+
+ this.tracker = new TokenTracker({
+ userAddress,
+ provider: global.ethereumProvider,
+ tokens: [token],
+ pollingInterval: 8000,
+ })
+
+ this.tracker.on('update', this.updateBalance)
+ this.tracker.on('error', this.setError)
+
+ this.tracker.updateBalances()
+ .then(() => this.updateBalance(this.tracker.serialize()))
+ .catch(error => this.setState({ error: error.message }))
+ }
+
+ setError (error) {
+ this.setState({ error })
+ }
+
+ updateBalance (tokens = []) {
+ if (!this.tracker.running) {
+ return
+ }
+ const [{ string, symbol }] = tokens
+ this.setState({ string, symbol, error: null })
+ }
+
+ removeListeners () {
+ if (this.tracker) {
+ this.tracker.stop()
+ this.tracker.removeListener('update', this.updateBalance)
+ this.tracker.removeListener('error', this.setError)
+ }
+ }
+
+ render () {
+ const { string, symbol, error } = this.state
+
+ return (
+ <WrappedComponent
+ { ...this.props }
+ string={string}
+ symbol={symbol}
+ error={error}
+ />
+ )
+ }
+ }
+}
diff --git a/ui/app/helpers/common.util.js b/ui/app/helpers/utils/common.util.js
index 0c02481e6..0c02481e6 100644
--- a/ui/app/helpers/common.util.js
+++ b/ui/app/helpers/utils/common.util.js
diff --git a/ui/app/helpers/tests/common.util.test.js b/ui/app/helpers/utils/common.util.test.js
index a52b91a10..6259f4a89 100644
--- a/ui/app/helpers/tests/common.util.test.js
+++ b/ui/app/helpers/utils/common.util.test.js
@@ -1,4 +1,4 @@
-import * as utils from '../common.util'
+import * as utils from './common.util'
import assert from 'assert'
describe('Common utils', () => {
diff --git a/ui/app/helpers/confirm-transaction/util.js b/ui/app/helpers/utils/confirm-tx.util.js
index 0451824e8..f843db118 100644
--- a/ui/app/helpers/confirm-transaction/util.js
+++ b/ui/app/helpers/utils/confirm-tx.util.js
@@ -8,7 +8,7 @@ import {
addCurrencies,
multiplyCurrencies,
conversionGreaterThan,
-} from '../../conversion-util'
+} from './conversion-util'
import { unconfirmedTransactionsCountSelector } from '../../selectors/confirm-transaction'
diff --git a/ui/app/helpers/confirm-transaction/util.test.js b/ui/app/helpers/utils/confirm-tx.util.test.js
index 4c1a3e16b..e818601ca 100644
--- a/ui/app/helpers/confirm-transaction/util.test.js
+++ b/ui/app/helpers/utils/confirm-tx.util.test.js
@@ -1,4 +1,4 @@
-import * as utils from './util'
+import * as utils from './confirm-tx.util'
import assert from 'assert'
describe('Confirm Transaction utils', () => {
diff --git a/ui/app/helpers/utils/conversion-util.js b/ui/app/helpers/utils/conversion-util.js
new file mode 100644
index 000000000..8cc531773
--- /dev/null
+++ b/ui/app/helpers/utils/conversion-util.js
@@ -0,0 +1,251 @@
+/* Currency Conversion Utility
+* This utility function can be used for converting currency related values within metamask.
+* The caller should be able to pass it a value, along with information about the value's
+* numeric base, denomination and currency, and the desired numeric base, denomination and
+* currency. It should return a single value.
+*
+* @param {(number | string | BN)} value The value to convert.
+* @param {Object} [options] Options to specify details of the conversion
+* @param {string} [options.fromCurrency = 'ETH' | 'USD'] The currency of the passed value
+* @param {string} [options.toCurrency = 'ETH' | 'USD'] The desired currency of the result
+* @param {string} [options.fromNumericBase = 'hex' | 'dec' | 'BN'] The numeric basic of the passed value.
+* @param {string} [options.toNumericBase = 'hex' | 'dec' | 'BN'] The desired numeric basic of the result.
+* @param {string} [options.fromDenomination = 'WEI'] The denomination of the passed value
+* @param {string} [options.numberOfDecimals] The desired number of decimals in the result
+* @param {string} [options.roundDown] The desired number of decimals to round down to
+* @param {number} [options.conversionRate] The rate to use to make the fromCurrency -> toCurrency conversion
+* @returns {(number | string | BN)}
+*
+* The utility passes value along with the options as a single object to the `converter` function.
+* `converter` uses Ramda.js to apply a composition of conditional setters to the `value` property, depending
+* on the accompanying options. Some of these conditional setters are selected via key-value maps, where
+* the keys are specified in the options parameters and the values are setter functions.
+*/
+
+const BigNumber = require('bignumber.js')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const R = require('ramda')
+const { stripHexPrefix } = require('ethereumjs-util')
+
+BigNumber.config({
+ ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN,
+})
+
+// Big Number Constants
+const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000')
+const BIG_NUMBER_GWEI_MULTIPLIER = new BigNumber('1000000000')
+const BIG_NUMBER_ETH_MULTIPLIER = new BigNumber('1')
+
+// Individual Setters
+const convert = R.invoker(1, 'times')
+const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_HALF_DOWN)
+const roundDown = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN)
+const invertConversionRate = conversionRate => () => new BigNumber(1.0).div(conversionRate)
+const decToBigNumberViaString = n => R.pipe(String, toBigNumber['dec'])
+
+// Setter Maps
+const toBigNumber = {
+ hex: n => new BigNumber(stripHexPrefix(n), 16),
+ dec: n => new BigNumber(String(n), 10),
+ BN: n => new BigNumber(n.toString(16), 16),
+}
+const toNormalizedDenomination = {
+ WEI: bigNumber => bigNumber.div(BIG_NUMBER_WEI_MULTIPLIER),
+ GWEI: bigNumber => bigNumber.div(BIG_NUMBER_GWEI_MULTIPLIER),
+ ETH: bigNumber => bigNumber.div(BIG_NUMBER_ETH_MULTIPLIER),
+}
+const toSpecifiedDenomination = {
+ WEI: bigNumber => bigNumber.times(BIG_NUMBER_WEI_MULTIPLIER).round(),
+ GWEI: bigNumber => bigNumber.times(BIG_NUMBER_GWEI_MULTIPLIER).round(9),
+ ETH: bigNumber => bigNumber.times(BIG_NUMBER_ETH_MULTIPLIER).round(9),
+}
+const baseChange = {
+ hex: n => n.toString(16),
+ dec: n => (new BigNumber(n)).toString(10),
+ BN: n => new BN(n.toString(16)),
+}
+
+// Predicates
+const fromAndToCurrencyPropsNotEqual = R.compose(
+ R.not,
+ R.eqBy(R.__, 'fromCurrency', 'toCurrency'),
+ R.flip(R.prop)
+)
+
+// Lens
+const valuePropertyLens = R.over(R.lensProp('value'))
+const conversionRateLens = R.over(R.lensProp('conversionRate'))
+
+// conditional conversionRate setting wrapper
+const whenPredSetCRWithPropAndSetter = (pred, prop, setter) => R.when(
+ pred,
+ R.converge(
+ conversionRateLens,
+ [R.pipe(R.prop(prop), setter), R.identity]
+ )
+)
+
+// conditional 'value' setting wrappers
+const whenPredSetWithPropAndSetter = (pred, prop, setter) => R.when(
+ pred,
+ R.converge(
+ valuePropertyLens,
+ [R.pipe(R.prop(prop), setter), R.identity]
+ )
+)
+const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter(
+ R.prop(prop),
+ prop,
+ R.prop(R.__, setterMap)
+)
+
+// Conversion utility function
+const converter = R.pipe(
+ whenPredSetCRWithPropAndSetter(R.prop('conversionRate'), 'conversionRate', decToBigNumberViaString),
+ whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate),
+ whenPropApplySetterMap('fromNumericBase', toBigNumber),
+ whenPropApplySetterMap('fromDenomination', toNormalizedDenomination),
+ whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert),
+ whenPropApplySetterMap('toDenomination', toSpecifiedDenomination),
+ whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round),
+ whenPredSetWithPropAndSetter(R.prop('roundDown'), 'roundDown', roundDown),
+ whenPropApplySetterMap('toNumericBase', baseChange),
+ R.view(R.lensProp('value'))
+)
+
+const conversionUtil = (value, {
+ fromCurrency = null,
+ toCurrency = fromCurrency,
+ fromNumericBase,
+ toNumericBase,
+ fromDenomination,
+ toDenomination,
+ numberOfDecimals,
+ conversionRate,
+ invertConversionRate,
+}) => converter({
+ fromCurrency,
+ toCurrency,
+ fromNumericBase,
+ toNumericBase,
+ fromDenomination,
+ toDenomination,
+ numberOfDecimals,
+ conversionRate,
+ invertConversionRate,
+ value: value || '0',
+})
+
+const addCurrencies = (a, b, options = {}) => {
+ const {
+ aBase,
+ bBase,
+ ...conversionOptions
+ } = options
+ const value = (new BigNumber(a.toString(), aBase)).add(b.toString(), bBase)
+
+ return converter({
+ value,
+ ...conversionOptions,
+ })
+}
+
+const subtractCurrencies = (a, b, options = {}) => {
+ const {
+ aBase,
+ bBase,
+ ...conversionOptions
+ } = options
+ const value = (new BigNumber(String(a), aBase)).minus(b, bBase)
+
+ return converter({
+ value,
+ ...conversionOptions,
+ })
+}
+
+const multiplyCurrencies = (a, b, options = {}) => {
+ const {
+ multiplicandBase,
+ multiplierBase,
+ ...conversionOptions
+ } = options
+
+ const bigNumberA = new BigNumber(String(a), multiplicandBase)
+ const bigNumberB = new BigNumber(String(b), multiplierBase)
+
+ const value = bigNumberA.times(bigNumberB)
+
+ return converter({
+ value,
+ ...conversionOptions,
+ })
+}
+
+const conversionGreaterThan = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+
+ return firstValue.gt(secondValue)
+}
+
+const conversionLessThan = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+
+ return firstValue.lt(secondValue)
+}
+
+const conversionMax = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstIsGreater = conversionGreaterThan(
+ { ...firstProps },
+ { ...secondProps }
+ )
+
+ return firstIsGreater ? firstProps.value : secondProps.value
+}
+
+const conversionGTE = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+ return firstValue.greaterThanOrEqualTo(secondValue)
+}
+
+const conversionLTE = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstValue = converter({ ...firstProps })
+ const secondValue = converter({ ...secondProps })
+ return firstValue.lessThanOrEqualTo(secondValue)
+}
+
+const toNegative = (n, options = {}) => {
+ return multiplyCurrencies(n, -1, options)
+}
+
+module.exports = {
+ conversionUtil,
+ addCurrencies,
+ multiplyCurrencies,
+ conversionGreaterThan,
+ conversionLessThan,
+ conversionGTE,
+ conversionLTE,
+ conversionMax,
+ toNegative,
+ subtractCurrencies,
+}
diff --git a/ui/app/helpers/utils/conversion-util.test.js b/ui/app/helpers/utils/conversion-util.test.js
new file mode 100644
index 000000000..368ce3bba
--- /dev/null
+++ b/ui/app/helpers/utils/conversion-util.test.js
@@ -0,0 +1,22 @@
+import assert from 'assert'
+import {addCurrencies} from './conversion-util'
+
+
+describe('conversion utils', () => {
+ describe('addCurrencies()', () => {
+ it('add whole numbers', () => {
+ const result = addCurrencies(3, 9)
+ assert.equal(result.toNumber(), 12)
+ })
+
+ it('add decimals', () => {
+ const result = addCurrencies(1.3, 1.9)
+ assert.equal(result.toNumber(), 3.2)
+ })
+
+ it('add repeating decimals', () => {
+ const result = addCurrencies(1 / 3, 1 / 9)
+ assert.equal(result.toNumber(), 0.4444444444444444)
+ })
+ })
+})
diff --git a/ui/app/helpers/conversions.util.js b/ui/app/helpers/utils/conversions.util.js
index 065d67e8e..b4ec50626 100644
--- a/ui/app/helpers/conversions.util.js
+++ b/ui/app/helpers/utils/conversions.util.js
@@ -1,6 +1,6 @@
import ethUtil from 'ethereumjs-util'
import { ETH, GWEI, WEI } from '../constants/common'
-import { conversionUtil, addCurrencies } from '../conversion-util'
+import { conversionUtil, addCurrencies } from './conversion-util'
export function bnToHex (inputBn) {
return ethUtil.addHexPrefix(inputBn.toString(16))
diff --git a/ui/app/helpers/formatters.js b/ui/app/helpers/utils/formatters.js
index 106a2520d..106a2520d 100644
--- a/ui/app/helpers/formatters.js
+++ b/ui/app/helpers/utils/formatters.js
diff --git a/ui/app/helpers/utils/i18n-helper.js b/ui/app/helpers/utils/i18n-helper.js
new file mode 100644
index 000000000..db07049e1
--- /dev/null
+++ b/ui/app/helpers/utils/i18n-helper.js
@@ -0,0 +1,44 @@
+// cross-browser connection to extension i18n API
+const log = require('loglevel')
+
+/**
+ * Returns a localized message for the given key
+ * @param {object} locale The locale
+ * @param {string} key The message key
+ * @param {string[]} substitutions A list of message substitution replacements
+ * @return {null|string} The localized message
+ */
+const getMessage = (locale, key, substitutions) => {
+ if (!locale) {
+ return null
+ }
+ if (!locale[key]) {
+ log.warn(`Translator - Unable to find value for key "${key}"`)
+ return null
+ }
+ const entry = locale[key]
+ let phrase = entry.message
+ // perform substitutions
+ if (substitutions && substitutions.length) {
+ substitutions.forEach((substitution, index) => {
+ const regex = new RegExp(`\\$${index + 1}`, 'g')
+ phrase = phrase.replace(regex, substitution)
+ })
+ }
+ return phrase
+}
+
+async function fetchLocale (localeName) {
+ try {
+ const response = await fetch(`./_locales/${localeName}/messages.json`)
+ return await response.json()
+ } catch (error) {
+ log.error(`failed to fetch ${localeName} locale because of ${error}`)
+ return {}
+ }
+}
+
+module.exports = {
+ getMessage,
+ fetchLocale,
+}
diff --git a/ui/app/helpers/utils/metametrics.util.js b/ui/app/helpers/utils/metametrics.util.js
new file mode 100644
index 000000000..01984bd5e
--- /dev/null
+++ b/ui/app/helpers/utils/metametrics.util.js
@@ -0,0 +1,184 @@
+/* eslint camelcase: 0 */
+
+const ethUtil = require('ethereumjs-util')
+
+const inDevelopment = process.env.NODE_ENV === 'development'
+
+const METAMETRICS_BASE_URL = 'https://chromeextensionmm.innocraft.cloud/piwik.php'
+const METAMETRICS_REQUIRED_PARAMS = `?idsite=${inDevelopment ? 1 : 2}&rec=1&apiv=1`
+const METAMETRICS_BASE_FULL = METAMETRICS_BASE_URL + METAMETRICS_REQUIRED_PARAMS
+
+const METAMETRICS_TRACKING_URL = inDevelopment
+ ? 'http://www.metamask.io/metametrics'
+ : 'http://www.metamask.io/metametrics-prod'
+
+const METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE = 'gasLimitChange'
+const METAMETRICS_CUSTOM_GAS_PRICE_CHANGE = 'gasPriceChange'
+const METAMETRICS_CUSTOM_FUNCTION_TYPE = 'functionType'
+const METAMETRICS_CUSTOM_RECIPIENT_KNOWN = 'recipientKnown'
+const METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN = 'origin'
+const METAMETRICS_CUSTOM_FROM_NETWORK = 'fromNetwork'
+const METAMETRICS_CUSTOM_TO_NETWORK = 'toNetwork'
+const METAMETRICS_CUSTOM_ERROR_FIELD = 'errorField'
+const METAMETRICS_CUSTOM_ERROR_MESSAGE = 'errorMessage'
+const METAMETRICS_CUSTOM_RPC_NETWORK_ID = 'networkId'
+const METAMETRICS_CUSTOM_RPC_CHAIN_ID = 'chainId'
+
+const METAMETRICS_CUSTOM_NETWORK = 'network'
+const METAMETRICS_CUSTOM_ENVIRONMENT_TYPE = 'environmentType'
+const METAMETRICS_CUSTOM_ACTIVE_CURRENCY = 'activeCurrency'
+const METAMETRICS_CUSTOM_ACCOUNT_TYPE = 'accountType'
+const METAMETRICS_CUSTOM_NUMBER_OF_TOKENS = 'numberOfTokens'
+const METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS = 'numberOfAccounts'
+
+const customVariableNameIdMap = {
+ [METAMETRICS_CUSTOM_FUNCTION_TYPE]: 1,
+ [METAMETRICS_CUSTOM_RECIPIENT_KNOWN]: 2,
+ [METAMETRICS_CUSTOM_CONFIRM_SCREEN_ORIGIN]: 3,
+ [METAMETRICS_CUSTOM_GAS_LIMIT_CHANGE]: 4,
+ [METAMETRICS_CUSTOM_GAS_PRICE_CHANGE]: 5,
+ [METAMETRICS_CUSTOM_FROM_NETWORK]: 1,
+ [METAMETRICS_CUSTOM_TO_NETWORK]: 2,
+ [METAMETRICS_CUSTOM_RPC_NETWORK_ID]: 1,
+ [METAMETRICS_CUSTOM_RPC_CHAIN_ID]: 2,
+ [METAMETRICS_CUSTOM_ERROR_FIELD]: 1,
+ [METAMETRICS_CUSTOM_ERROR_MESSAGE]: 2,
+}
+
+const customDimensionsNameIdMap = {
+ [METAMETRICS_CUSTOM_NETWORK]: 5,
+ [METAMETRICS_CUSTOM_ENVIRONMENT_TYPE]: 6,
+ [METAMETRICS_CUSTOM_ACTIVE_CURRENCY]: 7,
+ [METAMETRICS_CUSTOM_ACCOUNT_TYPE]: 8,
+ [METAMETRICS_CUSTOM_NUMBER_OF_TOKENS]: 9,
+ [METAMETRICS_CUSTOM_NUMBER_OF_ACCOUNTS]: 10,
+}
+
+function composeUrlRefParamAddition (previousPath, confirmTransactionOrigin) {
+ const externalOrigin = confirmTransactionOrigin && confirmTransactionOrigin !== 'MetaMask'
+ return `&urlref=${externalOrigin ? 'EXTERNAL' : encodeURIComponent(previousPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
+}
+
+function composeCustomDimensionParamAddition (customDimensions) {
+ const customDimensionParamStrings = Object.keys(customDimensions).reduce((acc, name) => {
+ return [...acc, `dimension${customDimensionsNameIdMap[name]}=${customDimensions[name]}`]
+ }, [])
+ return `&${customDimensionParamStrings.join('&')}`
+}
+
+function composeCustomVarParamAddition (customVariables) {
+ const customVariableIdValuePairs = Object.keys(customVariables).reduce((acc, name) => {
+ return {
+ [customVariableNameIdMap[name]]: [name, customVariables[name]],
+ ...acc,
+ }
+ }, {})
+ return `&cvar=${encodeURIComponent(JSON.stringify(customVariableIdValuePairs))}`
+}
+
+function composeParamAddition (paramValue, paramName) {
+ return paramValue !== 0 && !paramValue
+ ? ''
+ : `&${paramName}=${paramValue}`
+}
+
+function composeUrl (config, permissionPreferences = {}) {
+ const {
+ eventOpts = {},
+ customVariables = '',
+ pageOpts = '',
+ network,
+ environmentType,
+ activeCurrency,
+ accountType,
+ numberOfTokens,
+ numberOfAccounts,
+ previousPath = '',
+ currentPath,
+ metaMetricsId,
+ confirmTransactionOrigin,
+ url: configUrl,
+ excludeMetaMetricsId,
+ isNewVisit,
+ } = config
+ const base = METAMETRICS_BASE_FULL
+
+ const e_c = composeParamAddition(eventOpts.category, 'e_c')
+ const e_a = composeParamAddition(eventOpts.action, 'e_a')
+ const e_n = composeParamAddition(eventOpts.name, 'e_n')
+ const new_visit = isNewVisit ? `&new_visit=1` : ''
+
+ const cvar = customVariables && composeCustomVarParamAddition(customVariables) || ''
+
+ const action_name = ''
+
+ const urlref = previousPath && composeUrlRefParamAddition(previousPath, confirmTransactionOrigin)
+
+ const dimensions = !pageOpts.hideDimensions ? composeCustomDimensionParamAddition({
+ network,
+ environmentType,
+ activeCurrency,
+ accountType,
+ numberOfTokens: customVariables && customVariables.numberOfTokens || numberOfTokens,
+ numberOfAccounts: customVariables && customVariables.numberOfAccounts || numberOfAccounts,
+ }) : ''
+ const url = configUrl || `&url=${encodeURIComponent(currentPath.replace(/chrome-extension:\/\/\w+/, METAMETRICS_TRACKING_URL))}`
+ const _id = metaMetricsId && !excludeMetaMetricsId ? `&_id=${metaMetricsId.slice(2, 18)}` : ''
+ const rand = `&rand=${String(Math.random()).slice(2)}`
+ const pv_id = `&pv_id=${ethUtil.bufferToHex(ethUtil.sha3(url || currentPath.match(/chrome-extension:\/\/\w+\/(.+)/)[0])).slice(2, 8)}`
+ const uid = metaMetricsId && !excludeMetaMetricsId
+ ? `&uid=${metaMetricsId.slice(2, 18)}`
+ : excludeMetaMetricsId
+ ? '&uid=0000000000000000'
+ : ''
+
+ return [ base, e_c, e_a, e_n, cvar, action_name, urlref, dimensions, url, _id, rand, pv_id, uid, new_visit ].join('')
+}
+
+export function sendMetaMetricsEvent (config, permissionPreferences) {
+ return fetch(composeUrl(config, permissionPreferences), {
+ 'headers': {},
+ 'method': 'GET',
+ })
+}
+
+export function verifyUserPermission (config, props) {
+ const {
+ eventOpts = {},
+ } = config
+ const { userPermissionPreferences } = props
+ const {
+ allowAll,
+ allowNone,
+ allowSendMetrics,
+ } = userPermissionPreferences
+
+ if (allowNone) {
+ return false
+ } else if (allowAll) {
+ return true
+ } else if (allowSendMetrics && eventOpts.name === 'send') {
+ return true
+ } else {
+ return false
+ }
+}
+
+const trackableSendCounts = {
+ 1: true,
+ 10: true,
+ 30: true,
+ 50: true,
+ 100: true,
+ 250: true,
+ 500: true,
+ 1000: true,
+ 2500: true,
+ 5000: true,
+ 10000: true,
+ 25000: true,
+}
+
+export function sendCountIsTrackable (sendCount) {
+ return Boolean(trackableSendCounts[sendCount])
+}
diff --git a/ui/app/helpers/utils/token-util.js b/ui/app/helpers/utils/token-util.js
new file mode 100644
index 000000000..35a19a69f
--- /dev/null
+++ b/ui/app/helpers/utils/token-util.js
@@ -0,0 +1,118 @@
+const log = require('loglevel')
+const util = require('./util')
+const BigNumber = require('bignumber.js')
+import contractMap from 'eth-contract-metadata'
+
+const casedContractMap = Object.keys(contractMap).reduce((acc, base) => {
+ return {
+ ...acc,
+ [base.toLowerCase()]: contractMap[base],
+ }
+}, {})
+
+const DEFAULT_SYMBOL = ''
+const DEFAULT_DECIMALS = '0'
+
+async function getSymbolFromContract (tokenAddress) {
+ const token = util.getContractAtAddress(tokenAddress)
+
+ try {
+ const result = await token.symbol()
+ return result[0]
+ } catch (error) {
+ log.warn(`symbol() call for token at address ${tokenAddress} resulted in error:`, error)
+ }
+}
+
+async function getDecimalsFromContract (tokenAddress) {
+ const token = util.getContractAtAddress(tokenAddress)
+
+ try {
+ const result = await token.decimals()
+ const decimalsBN = result[0]
+ return decimalsBN && decimalsBN.toString()
+ } catch (error) {
+ log.warn(`decimals() call for token at address ${tokenAddress} resulted in error:`, error)
+ }
+}
+
+function getContractMetadata (tokenAddress) {
+ return tokenAddress && casedContractMap[tokenAddress.toLowerCase()]
+}
+
+async function getSymbol (tokenAddress) {
+ let symbol = await getSymbolFromContract(tokenAddress)
+
+ if (!symbol) {
+ const contractMetadataInfo = getContractMetadata(tokenAddress)
+
+ if (contractMetadataInfo) {
+ symbol = contractMetadataInfo.symbol
+ }
+ }
+
+ return symbol
+}
+
+async function getDecimals (tokenAddress) {
+ let decimals = await getDecimalsFromContract(tokenAddress)
+
+ if (!decimals || decimals === '0') {
+ const contractMetadataInfo = getContractMetadata(tokenAddress)
+
+ if (contractMetadataInfo) {
+ decimals = contractMetadataInfo.decimals
+ }
+ }
+
+ return decimals
+}
+
+export async function getSymbolAndDecimals (tokenAddress, existingTokens = []) {
+ const existingToken = existingTokens.find(({ address }) => tokenAddress === address)
+
+ if (existingToken) {
+ return {
+ symbol: existingToken.symbol,
+ decimals: existingToken.decimals,
+ }
+ }
+
+ let symbol, decimals
+
+ try {
+ symbol = await getSymbol(tokenAddress)
+ decimals = await getDecimals(tokenAddress)
+ } catch (error) {
+ log.warn(`symbol() and decimal() calls for token at address ${tokenAddress} resulted in error:`, error)
+ }
+
+ return {
+ symbol: symbol || DEFAULT_SYMBOL,
+ decimals: decimals || DEFAULT_DECIMALS,
+ }
+}
+
+export function tokenInfoGetter () {
+ const tokens = {}
+
+ return async (address) => {
+ if (tokens[address]) {
+ return tokens[address]
+ }
+
+ tokens[address] = await getSymbolAndDecimals(address)
+
+ return tokens[address]
+ }
+}
+
+export function calcTokenAmount (value, decimals) {
+ const multiplier = Math.pow(10, Number(decimals || 0))
+ return new BigNumber(String(value)).div(multiplier)
+}
+
+export function getTokenValue (tokenParams = []) {
+ const valueData = tokenParams.find(param => param.name === '_value')
+ return valueData && valueData.value
+}
diff --git a/ui/app/helpers/transactions.util.js b/ui/app/helpers/utils/transactions.util.js
index d5b7f4958..edf2e24f6 100644
--- a/ui/app/helpers/transactions.util.js
+++ b/ui/app/helpers/utils/transactions.util.js
@@ -5,7 +5,7 @@ import abiDecoder from 'abi-decoder'
import {
TRANSACTION_TYPE_CANCEL,
TRANSACTION_STATUS_CONFIRMED,
-} from '../../../app/scripts/controllers/transactions/enums'
+} from '../../../../app/scripts/controllers/transactions/enums'
import {
TOKEN_METHOD_TRANSFER,
@@ -21,7 +21,7 @@ import {
CANCEL_ATTEMPT_ACTION_KEY,
} from '../constants/transactions'
-import { addCurrencies } from '../conversion-util'
+import { addCurrencies } from './conversion-util'
abiDecoder.addABI(abi)
diff --git a/ui/app/helpers/tests/transactions.util.test.js b/ui/app/helpers/utils/transactions.util.test.js
index 838522e35..4a8ca5c9d 100644
--- a/ui/app/helpers/tests/transactions.util.test.js
+++ b/ui/app/helpers/utils/transactions.util.test.js
@@ -1,4 +1,4 @@
-import * as utils from '../transactions.util'
+import * as utils from './transactions.util'
import assert from 'assert'
describe('Transactions utils', () => {
diff --git a/ui/app/helpers/utils/util.js b/ui/app/helpers/utils/util.js
new file mode 100644
index 000000000..c50d7cbe5
--- /dev/null
+++ b/ui/app/helpers/utils/util.js
@@ -0,0 +1,326 @@
+const abi = require('human-standard-token-abi')
+const ethUtil = require('ethereumjs-util')
+const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
+import { DateTime } from 'luxon'
+
+const MIN_GAS_PRICE_GWEI_BN = new ethUtil.BN(1)
+const GWEI_FACTOR = new ethUtil.BN(1e9)
+const MIN_GAS_PRICE_BN = MIN_GAS_PRICE_GWEI_BN.mul(GWEI_FACTOR)
+
+// formatData :: ( date: <Unix Timestamp> ) -> String
+function formatDate (date, format = 'M/d/y \'at\' T') {
+ return DateTime.fromMillis(date).toFormat(format)
+}
+
+var valueTable = {
+ wei: '1000000000000000000',
+ kwei: '1000000000000000',
+ mwei: '1000000000000',
+ gwei: '1000000000',
+ szabo: '1000000',
+ finney: '1000',
+ ether: '1',
+ kether: '0.001',
+ mether: '0.000001',
+ gether: '0.000000001',
+ tether: '0.000000000001',
+}
+var bnTable = {}
+for (var currency in valueTable) {
+ bnTable[currency] = new ethUtil.BN(valueTable[currency], 10)
+}
+
+module.exports = {
+ valuesFor: valuesFor,
+ addressSummary: addressSummary,
+ miniAddressSummary: miniAddressSummary,
+ isAllOneCase: isAllOneCase,
+ isValidAddress: isValidAddress,
+ isValidENSAddress,
+ numericBalance: numericBalance,
+ parseBalance: parseBalance,
+ formatBalance: formatBalance,
+ generateBalanceObject: generateBalanceObject,
+ dataSize: dataSize,
+ readableDate: readableDate,
+ normalizeToWei: normalizeToWei,
+ normalizeEthStringToWei: normalizeEthStringToWei,
+ normalizeNumberToWei: normalizeNumberToWei,
+ valueTable: valueTable,
+ bnTable: bnTable,
+ isHex: isHex,
+ formatDate,
+ bnMultiplyByFraction,
+ getTxFeeBn,
+ shortenBalance,
+ getContractAtAddress,
+ exportAsFile: exportAsFile,
+ isInvalidChecksumAddress,
+ allNull,
+ getTokenAddressFromTokenObject,
+ checksumAddress,
+ addressSlicer,
+ isEthNetwork,
+}
+
+function isEthNetwork (netId) {
+ if (!netId || netId === '1' || netId === '3' || netId === '4' || netId === '42' || netId === '5777') {
+ return true
+ }
+
+ return false
+}
+
+function valuesFor (obj) {
+ if (!obj) return []
+ return Object.keys(obj)
+ .map(function (key) { return obj[key] })
+}
+
+function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) {
+ if (!address) return ''
+ let checked = checksumAddress(address)
+ if (!includeHex) {
+ checked = ethUtil.stripHexPrefix(checked)
+ }
+ return checked ? checked.slice(0, firstSegLength) + '...' + checked.slice(checked.length - lastSegLength) : '...'
+}
+
+function miniAddressSummary (address) {
+ if (!address) return ''
+ var checked = checksumAddress(address)
+ return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...'
+}
+
+function isValidAddress (address, network) {
+ var prefixed = ethUtil.addHexPrefix(address)
+ if (address === '0x0000000000000000000000000000000000000000') return false
+ return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
+}
+
+function isValidENSAddress (address) {
+ return address.match(/^.{7,}\.(eth|test)$/)
+}
+
+function isInvalidChecksumAddress (address) {
+ var prefixed = ethUtil.addHexPrefix(address)
+ if (address === '0x0000000000000000000000000000000000000000') return false
+ return !isAllOneCase(prefixed) && !ethUtil.isValidChecksumAddress(prefixed) && ethUtil.isValidAddress(prefixed)
+}
+
+function isAllOneCase (address) {
+ if (!address) return true
+ var lower = address.toLowerCase()
+ var upper = address.toUpperCase()
+ return address === lower || address === upper
+}
+
+// Takes wei Hex, returns wei BN, even if input is null
+function numericBalance (balance) {
+ if (!balance) return new ethUtil.BN(0, 16)
+ var stripped = ethUtil.stripHexPrefix(balance)
+ return new ethUtil.BN(stripped, 16)
+}
+
+// Takes hex, returns [beforeDecimal, afterDecimal]
+function parseBalance (balance) {
+ var beforeDecimal, afterDecimal
+ const wei = numericBalance(balance)
+ var weiString = wei.toString()
+ const trailingZeros = /0+$/
+
+ beforeDecimal = weiString.length > 18 ? weiString.slice(0, weiString.length - 18) : '0'
+ afterDecimal = ('000000000000000000' + wei).slice(-18).replace(trailingZeros, '')
+ if (afterDecimal === '') { afterDecimal = '0' }
+ return [beforeDecimal, afterDecimal]
+}
+
+// Takes wei hex, returns an object with three properties.
+// Its "formatted" property is what we generally use to render values.
+function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') {
+ var parsed = needsParse ? parseBalance(balance) : balance.split('.')
+ var beforeDecimal = parsed[0]
+ var afterDecimal = parsed[1]
+ var formatted = 'None'
+ if (decimalsToKeep === undefined) {
+ if (beforeDecimal === '0') {
+ if (afterDecimal !== '0') {
+ var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits
+ if (sigFigs) { afterDecimal = sigFigs[0] }
+ formatted = '0.' + afterDecimal + ` ${ticker}`
+ }
+ } else {
+ formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}`
+ }
+ } else {
+ afterDecimal += Array(decimalsToKeep).join('0')
+ formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}`
+ }
+ return formatted
+}
+
+
+function generateBalanceObject (formattedBalance, decimalsToKeep = 1) {
+ var balance = formattedBalance.split(' ')[0]
+ var label = formattedBalance.split(' ')[1]
+ var beforeDecimal = balance.split('.')[0]
+ var afterDecimal = balance.split('.')[1]
+ var shortBalance = shortenBalance(balance, decimalsToKeep)
+
+ if (beforeDecimal === '0' && afterDecimal.substr(0, 5) === '00000') {
+ // eslint-disable-next-line eqeqeq
+ if (afterDecimal == 0) {
+ balance = '0'
+ } else {
+ balance = '<1.0e-5'
+ }
+ } else if (beforeDecimal !== '0') {
+ balance = `${beforeDecimal}.${afterDecimal.slice(0, decimalsToKeep)}`
+ }
+
+ return { balance, label, shortBalance }
+}
+
+function shortenBalance (balance, decimalsToKeep = 1) {
+ var truncatedValue
+ var convertedBalance = parseFloat(balance)
+ if (convertedBalance > 1000000) {
+ truncatedValue = (balance / 1000000).toFixed(decimalsToKeep)
+ return `${truncatedValue}m`
+ } else if (convertedBalance > 1000) {
+ truncatedValue = (balance / 1000).toFixed(decimalsToKeep)
+ return `${truncatedValue}k`
+ } else if (convertedBalance === 0) {
+ return '0'
+ } else if (convertedBalance < 0.001) {
+ return '<0.001'
+ } else if (convertedBalance < 1) {
+ var stringBalance = convertedBalance.toString()
+ if (stringBalance.split('.')[1].length > 3) {
+ return convertedBalance.toFixed(3)
+ } else {
+ return stringBalance
+ }
+ } else {
+ return convertedBalance.toFixed(decimalsToKeep)
+ }
+}
+
+function dataSize (data) {
+ var size = data ? ethUtil.stripHexPrefix(data).length : 0
+ return size + ' bytes'
+}
+
+// Takes a BN and an ethereum currency name,
+// returns a BN in wei
+function normalizeToWei (amount, currency) {
+ try {
+ return amount.mul(bnTable.wei).div(bnTable[currency])
+ } catch (e) {}
+ return amount
+}
+
+function normalizeEthStringToWei (str) {
+ const parts = str.split('.')
+ let eth = new ethUtil.BN(parts[0], 10).mul(bnTable.wei)
+ if (parts[1]) {
+ var decimal = parts[1]
+ while (decimal.length < 18) {
+ decimal += '0'
+ }
+ if (decimal.length > 18) {
+ decimal = decimal.slice(0, 18)
+ }
+ const decimalBN = new ethUtil.BN(decimal, 10)
+ eth = eth.add(decimalBN)
+ }
+ return eth
+}
+
+var multiple = new ethUtil.BN('10000', 10)
+function normalizeNumberToWei (n, currency) {
+ var enlarged = n * 10000
+ var amount = new ethUtil.BN(String(enlarged), 10)
+ return normalizeToWei(amount, currency).div(multiple)
+}
+
+function readableDate (ms) {
+ var date = new Date(ms)
+ var month = date.getMonth()
+ var day = date.getDate()
+ var year = date.getFullYear()
+ var hours = date.getHours()
+ var minutes = '0' + date.getMinutes()
+ var seconds = '0' + date.getSeconds()
+
+ var dateStr = `${month}/${day}/${year}`
+ var time = `${hours}:${minutes.substr(-2)}:${seconds.substr(-2)}`
+ return `${dateStr} ${time}`
+}
+
+function isHex (str) {
+ return Boolean(str.match(/^(0x)?[0-9a-fA-F]+$/))
+}
+
+function bnMultiplyByFraction (targetBN, numerator, denominator) {
+ const numBN = new ethUtil.BN(numerator)
+ const denomBN = new ethUtil.BN(denominator)
+ return targetBN.mul(numBN).div(denomBN)
+}
+
+function getTxFeeBn (gas, gasPrice = MIN_GAS_PRICE_BN.toString(16), blockGasLimit) {
+ const gasBn = hexToBn(gas)
+ const gasPriceBn = hexToBn(gasPrice)
+ const txFeeBn = gasBn.mul(gasPriceBn)
+
+ return txFeeBn.toString(16)
+}
+
+function getContractAtAddress (tokenAddress) {
+ return global.eth.contract(abi).at(tokenAddress)
+}
+
+function exportAsFile (filename, data, type = 'text/csv') {
+ // source: https://stackoverflow.com/a/33542499 by Ludovic Feltz
+ const blob = new Blob([data], {type})
+ if (window.navigator.msSaveOrOpenBlob) {
+ window.navigator.msSaveBlob(blob, filename)
+ } else {
+ const elem = window.document.createElement('a')
+ elem.target = '_blank'
+ elem.href = window.URL.createObjectURL(blob)
+ elem.download = filename
+ document.body.appendChild(elem)
+ elem.click()
+ document.body.removeChild(elem)
+ }
+}
+
+function allNull (obj) {
+ return Object.entries(obj).every(([key, value]) => value === null)
+}
+
+function getTokenAddressFromTokenObject (token) {
+ return Object.values(token)[0].address.toLowerCase()
+}
+
+/**
+ * Safely checksumms a potentially-null address
+ *
+ * @param {String} [address] - address to checksum
+ * @param {String} [network] - network id
+ * @returns {String} - checksummed address
+ *
+ */
+function checksumAddress (address, network) {
+ const checksummed = address ? ethUtil.toChecksumAddress(address) : ''
+ return checksummed
+}
+
+function addressSlicer (address = '') {
+ if (address.length < 11) {
+ return address
+ }
+
+ return `${address.slice(0, 6)}...${address.slice(-4)}`
+}