diff options
author | Chi Kei Chan <chikeichan@gmail.com> | 2019-03-22 07:03:30 +0800 |
---|---|---|
committer | Dan J Miller <danjm.com@gmail.com> | 2019-03-22 07:03:30 +0800 |
commit | 31175625b446cb5d18b17db23018bca8b14d280c (patch) | |
tree | f54e159883deef003fb281267025edf796eb8004 /ui/app/store | |
parent | 7287133e15fab22299e07704206e85bc855d1064 (diff) | |
download | tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.gz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.bz2 tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.lz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.xz tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.tar.zst tangerine-wallet-browser-31175625b446cb5d18b17db23018bca8b14d280c.zip |
Folder restructure (#6304)
* Remove ui/app/keychains/
* Remove ui/app/img/ (unused images)
* Move conversion-util to helpers/utils/
* Move token-util to helpers/utils/
* Move /helpers/*.js inside /helpers/utils/
* Move util tests inside /helpers/utils/
* Renameand move confirm-transaction/util.js to helpers/utils/
* Move higher-order-components to helpers/higher-order-components/
* Move infura-conversion.json to helpers/constants/
* Move all utility functions to helpers/utils/
* Move pages directory to top-level
* Move all constants to helpers/constants/
* Move metametrics inside helpers/
* Move app and root inside pages/
* Move routes inside helpers/
* Re-organize ducks/
* Move reducers to ducks/
* Move selectors inside selectors/
* Move test out of test folder
* Move action, reducer, store inside store/
* Move ui components inside ui/
* Move UI components inside ui/
* Move connected components inside components/app/
* Move i18n-helper inside helpers/
* Fix unit tests
* Fix unit test
* Move pages components
* Rename routes component
* Move reducers to ducks/index
* Fix bad path in unit test
Diffstat (limited to 'ui/app/store')
-rw-r--r-- | ui/app/store/actions.js | 2761 | ||||
-rw-r--r-- | ui/app/store/store.js | 21 |
2 files changed, 2782 insertions, 0 deletions
diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js new file mode 100644 index 000000000..b99b051cb --- /dev/null +++ b/ui/app/store/actions.js @@ -0,0 +1,2761 @@ +const abi = require('human-standard-token-abi') +const pify = require('pify') +const getBuyEthUrl = require('../../../app/scripts/lib/buy-eth-url') +const { getTokenAddressFromTokenObject } = require('../helpers/utils/util') +const { + calcTokenBalance, + estimateGas, +} = require('../components/app/send/send.utils') +const ethUtil = require('ethereumjs-util') +const { fetchLocale } = require('../helpers/utils/i18n-helper') +const log = require('loglevel') +const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../../app/scripts/lib/enums') +const { hasUnconfirmedTransactions } = require('../helpers/utils/confirm-tx.util') +const gasDuck = require('../ducks/gas/gas.duck') +const WebcamUtils = require('../../lib/webcam-utils') + +var actions = { + _setBackgroundConnection: _setBackgroundConnection, + + GO_HOME: 'GO_HOME', + goHome: goHome, + // modal state + MODAL_OPEN: 'UI_MODAL_OPEN', + MODAL_CLOSE: 'UI_MODAL_CLOSE', + showModal: showModal, + hideModal: hideModal, + // sidebar state + SIDEBAR_OPEN: 'UI_SIDEBAR_OPEN', + SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE', + showSidebar: showSidebar, + hideSidebar: hideSidebar, + // sidebar state + ALERT_OPEN: 'UI_ALERT_OPEN', + ALERT_CLOSE: 'UI_ALERT_CLOSE', + showAlert: showAlert, + hideAlert: hideAlert, + QR_CODE_DETECTED: 'UI_QR_CODE_DETECTED', + qrCodeDetected, + // network dropdown open + NETWORK_DROPDOWN_OPEN: 'UI_NETWORK_DROPDOWN_OPEN', + NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE', + showNetworkDropdown: showNetworkDropdown, + hideNetworkDropdown: hideNetworkDropdown, + // menu state/ + getNetworkStatus: 'getNetworkStatus', + // transition state + TRANSITION_FORWARD: 'TRANSITION_FORWARD', + TRANSITION_BACKWARD: 'TRANSITION_BACKWARD', + transitionForward, + transitionBackward, + // remote state + UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE', + updateMetamaskState: updateMetamaskState, + // notices + MARK_NOTICE_READ: 'MARK_NOTICE_READ', + markNoticeRead: markNoticeRead, + SHOW_NOTICE: 'SHOW_NOTICE', + showNotice: showNotice, + CLEAR_NOTICES: 'CLEAR_NOTICES', + clearNotices: clearNotices, + markAccountsFound, + // intialize screen + CREATE_NEW_VAULT_IN_PROGRESS: 'CREATE_NEW_VAULT_IN_PROGRESS', + SHOW_CREATE_VAULT: 'SHOW_CREATE_VAULT', + SHOW_RESTORE_VAULT: 'SHOW_RESTORE_VAULT', + fetchInfoToSync, + FORGOT_PASSWORD: 'FORGOT_PASSWORD', + forgotPassword: forgotPassword, + markPasswordForgotten, + unMarkPasswordForgotten, + SHOW_INIT_MENU: 'SHOW_INIT_MENU', + SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED', + SHOW_INFO_PAGE: 'SHOW_INFO_PAGE', + SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE', + SHOW_NEW_ACCOUNT_PAGE: 'SHOW_NEW_ACCOUNT_PAGE', + SET_NEW_ACCOUNT_FORM: 'SET_NEW_ACCOUNT_FORM', + unlockMetamask: unlockMetamask, + unlockFailed: unlockFailed, + unlockSucceeded, + showCreateVault: showCreateVault, + showRestoreVault: showRestoreVault, + showInitializeMenu: showInitializeMenu, + showImportPage, + showNewAccountPage, + setNewAccountForm, + createNewVaultAndKeychain: createNewVaultAndKeychain, + createNewVaultAndRestore: createNewVaultAndRestore, + createNewVaultInProgress: createNewVaultInProgress, + createNewVaultAndGetSeedPhrase, + unlockAndGetSeedPhrase, + addNewKeyring, + importNewAccount, + addNewAccount, + connectHardware, + checkHardwareStatus, + forgetDevice, + unlockHardwareWalletAccount, + NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN', + navigateToNewAccountScreen, + resetAccount, + removeAccount, + showNewVaultSeed: showNewVaultSeed, + showInfoPage: showInfoPage, + CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN', + closeWelcomeScreen, + // seed recovery actions + REVEAL_SEED_CONFIRMATION: 'REVEAL_SEED_CONFIRMATION', + revealSeedConfirmation: revealSeedConfirmation, + requestRevealSeed: requestRevealSeed, + requestRevealSeedWords, + // unlock screen + UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS', + UNLOCK_FAILED: 'UNLOCK_FAILED', + UNLOCK_SUCCEEDED: 'UNLOCK_SUCCEEDED', + UNLOCK_METAMASK: 'UNLOCK_METAMASK', + LOCK_METAMASK: 'LOCK_METAMASK', + tryUnlockMetamask: tryUnlockMetamask, + lockMetamask: lockMetamask, + unlockInProgress: unlockInProgress, + // error handling + displayWarning: displayWarning, + DISPLAY_WARNING: 'DISPLAY_WARNING', + HIDE_WARNING: 'HIDE_WARNING', + hideWarning: hideWarning, + // accounts screen + SET_SELECTED_ACCOUNT: 'SET_SELECTED_ACCOUNT', + SET_SELECTED_TOKEN: 'SET_SELECTED_TOKEN', + setSelectedToken, + SHOW_ACCOUNT_DETAIL: 'SHOW_ACCOUNT_DETAIL', + SHOW_ACCOUNTS_PAGE: 'SHOW_ACCOUNTS_PAGE', + SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE', + SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE', + SET_CURRENT_FIAT: 'SET_CURRENT_FIAT', + showQrScanner, + setCurrentCurrency, + setCurrentAccountTab, + // account detail screen + SHOW_SEND_PAGE: 'SHOW_SEND_PAGE', + showSendPage: showSendPage, + SHOW_SEND_TOKEN_PAGE: 'SHOW_SEND_TOKEN_PAGE', + showSendTokenPage, + ADD_TO_ADDRESS_BOOK: 'ADD_TO_ADDRESS_BOOK', + addToAddressBook: addToAddressBook, + REQUEST_ACCOUNT_EXPORT: 'REQUEST_ACCOUNT_EXPORT', + requestExportAccount: requestExportAccount, + EXPORT_ACCOUNT: 'EXPORT_ACCOUNT', + exportAccount: exportAccount, + SHOW_PRIVATE_KEY: 'SHOW_PRIVATE_KEY', + showPrivateKey: showPrivateKey, + exportAccountComplete, + SET_ACCOUNT_LABEL: 'SET_ACCOUNT_LABEL', + setAccountLabel, + updateNetworkNonce, + SET_NETWORK_NONCE: 'SET_NETWORK_NONCE', + // tx conf screen + COMPLETED_TX: 'COMPLETED_TX', + TRANSACTION_ERROR: 'TRANSACTION_ERROR', + NEXT_TX: 'NEXT_TX', + PREVIOUS_TX: 'PREV_TX', + EDIT_TX: 'EDIT_TX', + signMsg: signMsg, + cancelMsg: cancelMsg, + signPersonalMsg, + cancelPersonalMsg, + signTypedMsg, + cancelTypedMsg, + sendTx: sendTx, + signTx: signTx, + signTokenTx: signTokenTx, + updateTransaction, + updateAndApproveTx, + cancelTx: cancelTx, + cancelTxs, + completedTx: completedTx, + txError: txError, + nextTx: nextTx, + editTx, + previousTx: previousTx, + cancelAllTx: cancelAllTx, + viewPendingTx: viewPendingTx, + VIEW_PENDING_TX: 'VIEW_PENDING_TX', + updateTransactionParams, + UPDATE_TRANSACTION_PARAMS: 'UPDATE_TRANSACTION_PARAMS', + // send screen + UPDATE_GAS_LIMIT: 'UPDATE_GAS_LIMIT', + UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE', + UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL', + UPDATE_SEND_FROM: 'UPDATE_SEND_FROM', + UPDATE_SEND_HEX_DATA: 'UPDATE_SEND_HEX_DATA', + UPDATE_SEND_TOKEN_BALANCE: 'UPDATE_SEND_TOKEN_BALANCE', + UPDATE_SEND_TO: 'UPDATE_SEND_TO', + UPDATE_SEND_AMOUNT: 'UPDATE_SEND_AMOUNT', + UPDATE_SEND_MEMO: 'UPDATE_SEND_MEMO', + UPDATE_SEND_ERRORS: 'UPDATE_SEND_ERRORS', + UPDATE_SEND_WARNINGS: 'UPDATE_SEND_WARNINGS', + UPDATE_MAX_MODE: 'UPDATE_MAX_MODE', + UPDATE_SEND: 'UPDATE_SEND', + CLEAR_SEND: 'CLEAR_SEND', + OPEN_FROM_DROPDOWN: 'OPEN_FROM_DROPDOWN', + CLOSE_FROM_DROPDOWN: 'CLOSE_FROM_DROPDOWN', + GAS_LOADING_STARTED: 'GAS_LOADING_STARTED', + GAS_LOADING_FINISHED: 'GAS_LOADING_FINISHED', + setGasLimit, + setGasPrice, + updateGasData, + setGasTotal, + setSendTokenBalance, + updateSendTokenBalance, + updateSendHexData, + updateSendTo, + updateSendAmount, + updateSendMemo, + setMaxModeTo, + updateSend, + updateSendErrors, + updateSendWarnings, + clearSend, + setSelectedAddress, + gasLoadingStarted, + gasLoadingFinished, + // app messages + confirmSeedWords: confirmSeedWords, + showAccountDetail: showAccountDetail, + BACK_TO_ACCOUNT_DETAIL: 'BACK_TO_ACCOUNT_DETAIL', + backToAccountDetail: backToAccountDetail, + showAccountsPage: showAccountsPage, + showConfTxPage: showConfTxPage, + // config screen + SHOW_CONFIG_PAGE: 'SHOW_CONFIG_PAGE', + SET_RPC_TARGET: 'SET_RPC_TARGET', + SET_DEFAULT_RPC_TARGET: 'SET_DEFAULT_RPC_TARGET', + SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE', + SET_PREVIOUS_PROVIDER: 'SET_PREVIOUS_PROVIDER', + showConfigPage, + SHOW_ADD_TOKEN_PAGE: 'SHOW_ADD_TOKEN_PAGE', + SHOW_ADD_SUGGESTED_TOKEN_PAGE: 'SHOW_ADD_SUGGESTED_TOKEN_PAGE', + showAddTokenPage, + showAddSuggestedTokenPage, + addToken, + addTokens, + removeToken, + updateTokens, + removeSuggestedTokens, + addKnownMethodData, + UPDATE_TOKENS: 'UPDATE_TOKENS', + updateAndSetCustomRpc: updateAndSetCustomRpc, + setRpcTarget: setRpcTarget, + delRpcTarget: delRpcTarget, + setProviderType: setProviderType, + SET_HARDWARE_WALLET_DEFAULT_HD_PATH: 'SET_HARDWARE_WALLET_DEFAULT_HD_PATH', + setHardwareWalletDefaultHdPath, + updateProviderType, + // loading overlay + SHOW_LOADING: 'SHOW_LOADING_INDICATION', + HIDE_LOADING: 'HIDE_LOADING_INDICATION', + showLoadingIndication: showLoadingIndication, + hideLoadingIndication: hideLoadingIndication, + // buy Eth with coinbase + onboardingBuyEthView, + ONBOARDING_BUY_ETH_VIEW: 'ONBOARDING_BUY_ETH_VIEW', + BUY_ETH: 'BUY_ETH', + buyEth: buyEth, + buyEthView: buyEthView, + buyWithShapeShift, + BUY_ETH_VIEW: 'BUY_ETH_VIEW', + COINBASE_SUBVIEW: 'COINBASE_SUBVIEW', + coinBaseSubview: coinBaseSubview, + SHAPESHIFT_SUBVIEW: 'SHAPESHIFT_SUBVIEW', + shapeShiftSubview: shapeShiftSubview, + PAIR_UPDATE: 'PAIR_UPDATE', + pairUpdate: pairUpdate, + coinShiftRquest: coinShiftRquest, + SHOW_SUB_LOADING_INDICATION: 'SHOW_SUB_LOADING_INDICATION', + showSubLoadingIndication: showSubLoadingIndication, + HIDE_SUB_LOADING_INDICATION: 'HIDE_SUB_LOADING_INDICATION', + hideSubLoadingIndication: hideSubLoadingIndication, +// QR STUFF: + SHOW_QR: 'SHOW_QR', + showQrView: showQrView, + reshowQrCode: reshowQrCode, + SHOW_QR_VIEW: 'SHOW_QR_VIEW', +// FORGOT PASSWORD: + BACK_TO_INIT_MENU: 'BACK_TO_INIT_MENU', + goBackToInitView: goBackToInitView, + RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS', + BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW', + backToUnlockView: backToUnlockView, + // SHOWING KEYCHAIN + SHOW_NEW_KEYCHAIN: 'SHOW_NEW_KEYCHAIN', + showNewKeychain: showNewKeychain, + + callBackgroundThenUpdate, + forceUpdateMetamaskState, + + TOGGLE_ACCOUNT_MENU: 'TOGGLE_ACCOUNT_MENU', + toggleAccountMenu, + + useEtherscanProvider, + + SET_USE_BLOCKIE: 'SET_USE_BLOCKIE', + setUseBlockie, + + SET_PARTICIPATE_IN_METAMETRICS: 'SET_PARTICIPATE_IN_METAMETRICS', + SET_METAMETRICS_SEND_COUNT: 'SET_METAMETRICS_SEND_COUNT', + setParticipateInMetaMetrics, + setMetaMetricsSendCount, + + // locale + SET_CURRENT_LOCALE: 'SET_CURRENT_LOCALE', + SET_LOCALE_MESSAGES: 'SET_LOCALE_MESSAGES', + setCurrentLocale, + updateCurrentLocale, + setLocaleMessages, + // + // Feature Flags + setFeatureFlag, + updateFeatureFlags, + UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS', + + // Preferences + setPreference, + updatePreferences, + UPDATE_PREFERENCES: 'UPDATE_PREFERENCES', + setUseNativeCurrencyAsPrimaryCurrencyPreference, + setShowFiatConversionOnTestnetsPreference, + + // Migration of users to new UI + setCompletedUiMigration, + completeUiMigration, + COMPLETE_UI_MIGRATION: 'COMPLETE_UI_MIGRATION', + + // Onboarding + setCompletedOnboarding, + completeOnboarding, + COMPLETE_ONBOARDING: 'COMPLETE_ONBOARDING', + + setMouseUserState, + SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE', + + // Network + updateNetworkEndpointType, + UPDATE_NETWORK_ENDPOINT_TYPE: 'UPDATE_NETWORK_ENDPOINT_TYPE', + + retryTransaction, + SET_PENDING_TOKENS: 'SET_PENDING_TOKENS', + CLEAR_PENDING_TOKENS: 'CLEAR_PENDING_TOKENS', + setPendingTokens, + clearPendingTokens, + + createCancelTransaction, + createSpeedUpTransaction, + + approveProviderRequest, + rejectProviderRequest, + clearApprovedOrigins, + + setFirstTimeFlowType, + SET_FIRST_TIME_FLOW_TYPE: 'SET_FIRST_TIME_FLOW_TYPE', +} + +module.exports = actions + +var background = null +function _setBackgroundConnection (backgroundConnection) { + background = backgroundConnection +} + +function goHome () { + return { + type: actions.GO_HOME, + } +} + +// async actions + +function tryUnlockMetamask (password) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + dispatch(actions.unlockInProgress()) + log.debug(`background.submitPassword`) + + return new Promise((resolve, reject) => { + background.submitPassword(password, error => { + if (error) { + return reject(error) + } + + resolve() + }) + }) + .then(() => { + dispatch(actions.unlockSucceeded()) + return forceUpdateMetamaskState(dispatch) + }) + .then(() => { + return new Promise((resolve, reject) => { + background.verifySeedPhrase(err => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + resolve() + }) + }) + }) + .then(() => { + dispatch(actions.transitionForward()) + dispatch(actions.hideLoadingIndication()) + }) + .catch(err => { + dispatch(actions.unlockFailed(err.message)) + dispatch(actions.hideLoadingIndication()) + return Promise.reject(err) + }) + } +} + +function transitionForward () { + return { + type: this.TRANSITION_FORWARD, + } +} + +function transitionBackward () { + return { + type: this.TRANSITION_BACKWARD, + } +} + +function confirmSeedWords () { + return dispatch => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.clearSeedWordCache`) + return new Promise((resolve, reject) => { + background.clearSeedWordCache((err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.info('Seed word cache cleared. ' + account) + dispatch(actions.showAccountsPage()) + resolve(account) + }) + }) + } +} + +function createNewVaultAndRestore (password, seed) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.createNewVaultAndRestore`) + + return new Promise((resolve, reject) => { + background.clearSeedWordCache((err) => { + if (err) { + return reject(err) + } + + background.createNewVaultAndRestore(password, seed, (err) => { + if (err) { + return reject(err) + } + + resolve() + }) + }) + }) + .then(() => dispatch(actions.unMarkPasswordForgotten())) + .then(() => { + dispatch(actions.showAccountsPage()) + dispatch(actions.hideLoadingIndication()) + }) + .catch(err => { + dispatch(actions.displayWarning(err.message)) + dispatch(actions.hideLoadingIndication()) + return Promise.reject(err) + }) + } +} + +function createNewVaultAndKeychain (password) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.createNewVaultAndKeychain`) + + return new Promise((resolve, reject) => { + background.createNewVaultAndKeychain(password, err => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.debug(`background.placeSeedWords`) + + background.placeSeedWords((err) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + resolve() + }) + }) + }) + .then(() => forceUpdateMetamaskState(dispatch)) + .then(() => dispatch(actions.hideLoadingIndication())) + .catch(() => dispatch(actions.hideLoadingIndication())) + } +} + +function createNewVaultAndGetSeedPhrase (password) { + return async dispatch => { + dispatch(actions.showLoadingIndication()) + + try { + await createNewVault(password) + const seedWords = await verifySeedPhrase() + dispatch(actions.hideLoadingIndication()) + return seedWords + } catch (error) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(error.message)) + throw new Error(error.message) + } + } +} + +function unlockAndGetSeedPhrase (password) { + return async dispatch => { + dispatch(actions.showLoadingIndication()) + + try { + await submitPassword(password) + const seedWords = await verifySeedPhrase() + await forceUpdateMetamaskState(dispatch) + dispatch(actions.hideLoadingIndication()) + return seedWords + } catch (error) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(error.message)) + throw new Error(error.message) + } + } +} + +function revealSeedConfirmation () { + return { + type: this.REVEAL_SEED_CONFIRMATION, + } +} + +function submitPassword (password) { + return new Promise((resolve, reject) => { + background.submitPassword(password, error => { + if (error) { + return reject(error) + } + + resolve() + }) + }) +} + +function createNewVault (password) { + return new Promise((resolve, reject) => { + background.createNewVaultAndKeychain(password, error => { + if (error) { + return reject(error) + } + + resolve(true) + }) + }) +} + +function verifyPassword (password) { + return new Promise((resolve, reject) => { + background.submitPassword(password, error => { + if (error) { + return reject(error) + } + + resolve(true) + }) + }) +} + +function verifySeedPhrase () { + return new Promise((resolve, reject) => { + background.verifySeedPhrase((error, seedWords) => { + if (error) { + return reject(error) + } + + resolve(seedWords) + }) + }) +} + +function requestRevealSeed (password) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.submitPassword`) + return new Promise((resolve, reject) => { + background.submitPassword(password, err => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.debug(`background.placeSeedWords`) + background.placeSeedWords((err, result) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.showNewVaultSeed(result)) + dispatch(actions.hideLoadingIndication()) + resolve() + }) + }) + }) + } +} + +function requestRevealSeedWords (password) { + return async dispatch => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.submitPassword`) + + try { + await verifyPassword(password) + const seedWords = await verifySeedPhrase() + dispatch(actions.hideLoadingIndication()) + return seedWords + } catch (error) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(error.message)) + throw new Error(error.message) + } + } +} + +function fetchInfoToSync () { + return dispatch => { + log.debug(`background.fetchInfoToSync`) + return new Promise((resolve, reject) => { + background.fetchInfoToSync((err, result) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + resolve(result) + }) + }) + } +} + +function resetAccount () { + return dispatch => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.resetAccount((err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.info('Transaction history reset for ' + account) + dispatch(actions.showAccountsPage()) + resolve(account) + }) + }) + } +} + +function removeAccount (address) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.removeAccount(address, (err, account) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + log.info('Account removed: ' + account) + dispatch(actions.showAccountsPage()) + resolve() + }) + }) + } +} + +function addNewKeyring (type, opts) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.addNewKeyring`) + background.addNewKeyring(type, opts, (err) => { + dispatch(actions.hideLoadingIndication()) + if (err) return dispatch(actions.displayWarning(err.message)) + dispatch(actions.showAccountsPage()) + }) + } +} + +function importNewAccount (strategy, args) { + return async (dispatch) => { + let newState + dispatch(actions.showLoadingIndication('This may take a while, please be patient.')) + try { + log.debug(`background.importAccountWithStrategy`) + await pify(background.importAccountWithStrategy).call(background, strategy, args) + log.debug(`background.getState`) + newState = await pify(background.getState).call(background) + } catch (err) { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(err.message)) + throw err + } + dispatch(actions.hideLoadingIndication()) + dispatch(actions.updateMetamaskState(newState)) + if (newState.selectedAddress) { + dispatch({ + type: actions.SHOW_ACCOUNT_DETAIL, + value: newState.selectedAddress, + }) + } + return newState + } +} + +function navigateToNewAccountScreen () { + return { + type: this.NEW_ACCOUNT_SCREEN, + } +} + +function addNewAccount () { + log.debug(`background.addNewAccount`) + return (dispatch, getState) => { + const oldIdentities = getState().metamask.identities + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.addNewAccount((err, { identities: newIdentities}) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + const newAccountAddress = Object.keys(newIdentities).find(address => !oldIdentities[address]) + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(newAccountAddress) + }) + }) + } +} + +function checkHardwareStatus (deviceName, hdPath) { + log.debug(`background.checkHardwareStatus`, deviceName, hdPath) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.checkHardwareStatus(deviceName, hdPath, (err, unlocked) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(unlocked) + }) + }) + } +} + +function forgetDevice (deviceName) { + log.debug(`background.forgetDevice`, deviceName) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.forgetDevice(deviceName, (err, response) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve() + }) + }) + } +} + +function connectHardware (deviceName, page, hdPath) { + log.debug(`background.connectHardware`, deviceName, page, hdPath) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.connectHardware(deviceName, page, hdPath, (err, accounts) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + + forceUpdateMetamaskState(dispatch) + return resolve(accounts) + }) + }) + } +} + +function unlockHardwareWalletAccount (index, deviceName, hdPath) { + log.debug(`background.unlockHardwareWalletAccount`, index, deviceName, hdPath) + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.unlockHardwareWalletAccount(index, deviceName, hdPath, (err, accounts) => { + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.hideLoadingIndication()) + return resolve() + }) + }) + } +} + +function showInfoPage () { + return { + type: actions.SHOW_INFO_PAGE, + } +} + +function showQrScanner (ROUTE) { + return (dispatch, getState) => { + return WebcamUtils.checkStatus() + .then(status => { + if (!status.environmentReady) { + // We need to switch to fullscreen mode to ask for permission + global.platform.openExtensionInBrowser(`${ROUTE}`, `scan=true`) + } else { + dispatch(actions.showModal({ + name: 'QR_SCANNER', + })) + } + }).catch(e => { + dispatch(actions.showModal({ + name: 'QR_SCANNER', + error: true, + errorType: e.type, + })) + }) + } +} + +function setCurrentCurrency (currencyCode) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.setCurrentCurrency`) + background.setCurrentCurrency(currencyCode, (err, data) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + log.error(err.stack) + return dispatch(actions.displayWarning(err.message)) + } + dispatch({ + type: actions.SET_CURRENT_FIAT, + value: { + currentCurrency: data.currentCurrency, + conversionRate: data.conversionRate, + conversionDate: data.conversionDate, + }, + }) + }) + } +} + +function signMsg (msgData) { + log.debug('action - signMsg') + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + log.debug(`actions calling background.signMessage`) + background.signMessage(msgData, (err, newState) => { + log.debug('signMessage called back') + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.completedTx(msgData.metamaskId)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function signPersonalMsg (msgData) { + log.debug('action - signPersonalMsg') + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + log.debug(`actions calling background.signPersonalMessage`) + background.signPersonalMessage(msgData, (err, newState) => { + log.debug('signPersonalMessage called back') + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.completedTx(msgData.metamaskId)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function signTypedMsg (msgData) { + log.debug('action - signTypedMsg') + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + log.debug(`actions calling background.signTypedMessage`) + background.signTypedMessage(msgData, (err, newState) => { + log.debug('signTypedMessage called back') + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + log.error(err) + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.completedTx(msgData.metamaskId)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function signTx (txData) { + return (dispatch) => { + global.ethQuery.sendTransaction(txData, (err, data) => { + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + }) + dispatch(actions.showConfTxPage({})) + } +} + +function setGasLimit (gasLimit) { + return { + type: actions.UPDATE_GAS_LIMIT, + value: gasLimit, + } +} + +function setGasPrice (gasPrice) { + return { + type: actions.UPDATE_GAS_PRICE, + value: gasPrice, + } +} + +function setGasTotal (gasTotal) { + return { + type: actions.UPDATE_GAS_TOTAL, + value: gasTotal, + } +} + +function updateGasData ({ + gasPrice, + blockGasLimit, + recentBlocks, + selectedAddress, + selectedToken, + to, + value, + data, +}) { + return (dispatch) => { + dispatch(actions.gasLoadingStarted()) + return estimateGas({ + estimateGasMethod: background.estimateGas, + blockGasLimit, + selectedAddress, + selectedToken, + to, + value, + estimateGasPrice: gasPrice, + data, + }) + .then(gas => { + dispatch(actions.setGasLimit(gas)) + dispatch(gasDuck.setCustomGasLimit(gas)) + dispatch(updateSendErrors({ gasLoadingError: null })) + dispatch(actions.gasLoadingFinished()) + }) + .catch(err => { + log.error(err) + dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' })) + dispatch(actions.gasLoadingFinished()) + }) + } +} + +function gasLoadingStarted () { + return { + type: actions.GAS_LOADING_STARTED, + } +} + +function gasLoadingFinished () { + return { + type: actions.GAS_LOADING_FINISHED, + } +} + +function updateSendTokenBalance ({ + selectedToken, + tokenContract, + address, +}) { + return (dispatch) => { + const tokenBalancePromise = tokenContract + ? tokenContract.balanceOf(address) + : Promise.resolve() + return tokenBalancePromise + .then(usersToken => { + if (usersToken) { + const newTokenBalance = calcTokenBalance({ selectedToken, usersToken }) + dispatch(setSendTokenBalance(newTokenBalance)) + } + }) + .catch(err => { + log.error(err) + updateSendErrors({ tokenBalance: 'tokenBalanceError' }) + }) + } +} + +function updateSendErrors (errorObject) { + return { + type: actions.UPDATE_SEND_ERRORS, + value: errorObject, + } +} + +function updateSendWarnings (warningObject) { + return { + type: actions.UPDATE_SEND_WARNINGS, + value: warningObject, + } +} + +function setSendTokenBalance (tokenBalance) { + return { + type: actions.UPDATE_SEND_TOKEN_BALANCE, + value: tokenBalance, + } +} + +function updateSendHexData (value) { + return { + type: actions.UPDATE_SEND_HEX_DATA, + value, + } +} + +function updateSendTo (to, nickname = '') { + return { + type: actions.UPDATE_SEND_TO, + value: { to, nickname }, + } +} + +function updateSendAmount (amount) { + return { + type: actions.UPDATE_SEND_AMOUNT, + value: amount, + } +} + +function updateSendMemo (memo) { + return { + type: actions.UPDATE_SEND_MEMO, + value: memo, + } +} + +function setMaxModeTo (bool) { + return { + type: actions.UPDATE_MAX_MODE, + value: bool, + } +} + +function updateSend (newSend) { + return { + type: actions.UPDATE_SEND, + value: newSend, + } +} + +function clearSend () { + return { + type: actions.CLEAR_SEND, + } +} + + +function sendTx (txData) { + log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`) + return (dispatch, getState) => { + log.debug(`actions calling background.approveTransaction`) + background.approveTransaction(txData.id, (err) => { + if (err) { + dispatch(actions.txError(err)) + return log.error(err.message) + } + dispatch(actions.completedTx(txData.id)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + }) + } +} + +function signTokenTx (tokenAddress, toAddress, amount, txData) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + const token = global.eth.contract(abi).at(tokenAddress) + token.transfer(toAddress, ethUtil.addHexPrefix(amount), txData) + .catch(err => { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.displayWarning(err.message)) + }) + dispatch(actions.showConfTxPage({})) + } +} + +function updateTransaction (txData) { + log.info('actions: updateTx: ' + JSON.stringify(txData)) + return dispatch => { + log.debug(`actions calling background.updateTx`) + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.updateTransaction(txData, (err) => { + dispatch(actions.updateTransactionParams(txData.id, txData.txParams)) + if (err) { + dispatch(actions.txError(err)) + dispatch(actions.goHome()) + log.error(err.message) + return reject(err) + } + + resolve(txData) + }) + }) + .then(() => updateMetamaskStateFromBackground()) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => { + dispatch(actions.showConfTxPage({ id: txData.id })) + dispatch(actions.hideLoadingIndication()) + return txData + }) + } +} + +function updateAndApproveTx (txData) { + log.info('actions: updateAndApproveTx: ' + JSON.stringify(txData)) + return (dispatch, getState) => { + 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)) + dispatch(actions.clearSend()) + + if (err) { + dispatch(actions.txError(err)) + dispatch(actions.goHome()) + log.error(err.message) + reject(err) + } + + resolve(txData) + }) + }) + .then(() => updateMetamaskStateFromBackground()) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => { + dispatch(actions.clearSend()) + dispatch(actions.completedTx(txData.id)) + dispatch(actions.hideLoadingIndication()) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return txData + }) + .catch((err) => { + dispatch(actions.hideLoadingIndication()) + return Promise.reject(err) + }) + } +} + +function completedTx (id) { + return { + type: actions.COMPLETED_TX, + value: id, + } +} + +function updateTransactionParams (id, txParams) { + return { + type: actions.UPDATE_TRANSACTION_PARAMS, + id, + value: txParams, + } +} + +function txError (err) { + return { + type: actions.TRANSACTION_ERROR, + message: err.message, + } +} + +function cancelMsg (msgData) { + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + log.debug(`background.cancelMessage`) + background.cancelMessage(msgData.id, (err, newState) => { + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + return reject(err) + } + + dispatch(actions.completedTx(msgData.id)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function cancelPersonalMsg (msgData) { + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + const id = msgData.id + background.cancelPersonalMessage(id, (err, newState) => { + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + return reject(err) + } + + dispatch(actions.completedTx(id)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function cancelTypedMsg (msgData) { + return (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + const id = msgData.id + background.cancelTypedMessage(id, (err, newState) => { + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + + if (err) { + return reject(err) + } + + dispatch(actions.completedTx(id)) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return resolve(msgData) + }) + }) + } +} + +function cancelTx (txData) { + return (dispatch, getState) => { + log.debug(`background.cancelTransaction`) + dispatch(actions.showLoadingIndication()) + + return new Promise((resolve, reject) => { + background.cancelTransaction(txData.id, err => { + if (err) { + return reject(err) + } + + resolve() + }) + }) + .then(() => updateMetamaskStateFromBackground()) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => { + dispatch(actions.clearSend()) + dispatch(actions.completedTx(txData.id)) + dispatch(actions.hideLoadingIndication()) + + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION && + !hasUnconfirmedTransactions(getState())) { + return global.platform.closeCurrentWindow() + } + + return txData + }) + } +} + +/** + * Cancels all of the given transactions + * @param {Array<object>} txDataList a list of tx data objects + * @return {function(*): Promise<void>} + */ +function cancelTxs (txDataList) { + return async (dispatch, getState) => { + dispatch(actions.showLoadingIndication()) + const txIds = txDataList.map(({id}) => id) + const cancellations = txIds.map((id) => new Promise((resolve, reject) => { + background.cancelTransaction(id, (err) => { + if (err) { + return reject(err) + } + + resolve() + }) + })) + + await Promise.all(cancellations) + const newState = await updateMetamaskStateFromBackground() + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.clearSend()) + + txIds.forEach((id) => { + dispatch(actions.completedTx(id)) + }) + + dispatch(actions.hideLoadingIndication()) + + if (global.METAMASK_UI_TYPE === 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)) + i === txsData.length - 1 ? dispatch(actions.goHome()) : null + }) + }) + } +} +// +// initialize screen +// + +function showCreateVault () { + return { + type: actions.SHOW_CREATE_VAULT, + } +} + +function showRestoreVault () { + return { + type: actions.SHOW_RESTORE_VAULT, + } +} + +function markPasswordForgotten () { + return (dispatch) => { + return background.markPasswordForgotten(() => { + dispatch(actions.hideLoadingIndication()) + dispatch(actions.forgotPassword()) + forceUpdateMetamaskState(dispatch) + }) + } +} + +function unMarkPasswordForgotten () { + return dispatch => { + return new Promise(resolve => { + background.unMarkPasswordForgotten(() => { + dispatch(actions.forgotPassword(false)) + resolve() + }) + }) + .then(() => forceUpdateMetamaskState(dispatch)) + } +} + +function forgotPassword (forgotPasswordState = true) { + return { + type: actions.FORGOT_PASSWORD, + value: forgotPasswordState, + } +} + +function showInitializeMenu () { + return { + type: actions.SHOW_INIT_MENU, + } +} + +function showImportPage () { + return { + type: actions.SHOW_IMPORT_PAGE, + } +} + +function showNewAccountPage (formToSelect) { + return { + type: actions.SHOW_NEW_ACCOUNT_PAGE, + formToSelect, + } +} + +function setNewAccountForm (formToSelect) { + return { + type: actions.SET_NEW_ACCOUNT_FORM, + formToSelect, + } +} + +function createNewVaultInProgress () { + return { + type: actions.CREATE_NEW_VAULT_IN_PROGRESS, + } +} + +function showNewVaultSeed (seed) { + return { + type: actions.SHOW_NEW_VAULT_SEED, + value: seed, + } +} + +function closeWelcomeScreen () { + return { + type: actions.CLOSE_WELCOME_SCREEN, + } +} + +function backToUnlockView () { + return { + type: actions.BACK_TO_UNLOCK_VIEW, + } +} + +function showNewKeychain () { + return { + type: actions.SHOW_NEW_KEYCHAIN, + } +} + +// +// unlock screen +// + +function unlockInProgress () { + return { + type: actions.UNLOCK_IN_PROGRESS, + } +} + +function unlockFailed (message) { + return { + type: actions.UNLOCK_FAILED, + value: message, + } +} + +function unlockSucceeded (message) { + return { + type: actions.UNLOCK_SUCCEEDED, + value: message, + } +} + +function unlockMetamask (account) { + return { + type: actions.UNLOCK_METAMASK, + value: account, + } +} + +function updateMetamaskState (newState) { + return { + type: actions.UPDATE_METAMASK_STATE, + value: newState, + } +} + +const backgroundSetLocked = () => { + return new Promise((resolve, reject) => { + background.setLocked(error => { + if (error) { + return reject(error) + } + resolve() + }) + }) +} + +const updateMetamaskStateFromBackground = () => { + log.debug(`background.getState`) + + return new Promise((resolve, reject) => { + background.getState((error, newState) => { + if (error) { + return reject(error) + } + + resolve(newState) + }) + }) +} + +function lockMetamask () { + log.debug(`background.setLocked`) + + return dispatch => { + dispatch(actions.showLoadingIndication()) + + return backgroundSetLocked() + .then(() => updateMetamaskStateFromBackground()) + .catch(error => { + dispatch(actions.displayWarning(error.message)) + return Promise.reject(error) + }) + .then(newState => { + dispatch(actions.updateMetamaskState(newState)) + dispatch(actions.hideLoadingIndication()) + dispatch({ type: actions.LOCK_METAMASK }) + }) + .catch(() => { + dispatch(actions.hideLoadingIndication()) + dispatch({ type: actions.LOCK_METAMASK }) + }) + } +} + +function setCurrentAccountTab (newTabName) { + log.debug(`background.setCurrentAccountTab: ${newTabName}`) + return callBackgroundThenUpdateNoSpinner(background.setCurrentAccountTab, newTabName) +} + +function setSelectedToken (tokenAddress) { + return { + type: actions.SET_SELECTED_TOKEN, + value: tokenAddress || null, + } +} + +function setSelectedAddress (address) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.setSelectedAddress`) + background.setSelectedAddress(address, (err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + }) + } +} + +function showAccountDetail (address) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.setSelectedAddress`) + background.setSelectedAddress(address, (err, tokens) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + dispatch(updateTokens(tokens)) + dispatch({ + type: actions.SHOW_ACCOUNT_DETAIL, + value: address, + }) + dispatch(actions.setSelectedToken()) + }) + } +} + +function backToAccountDetail (address) { + return { + type: actions.BACK_TO_ACCOUNT_DETAIL, + value: address, + } +} + +function showAccountsPage () { + return { + type: actions.SHOW_ACCOUNTS_PAGE, + } +} + +function showConfTxPage ({transForward = true, id}) { + return { + type: actions.SHOW_CONF_TX_PAGE, + transForward, + id, + } +} + +function nextTx () { + return { + type: actions.NEXT_TX, + } +} + +function viewPendingTx (txId) { + return { + type: actions.VIEW_PENDING_TX, + value: txId, + } +} + +function previousTx () { + return { + type: actions.PREVIOUS_TX, + } +} + +function editTx (txId) { + return { + type: actions.EDIT_TX, + value: txId, + } +} + +function showConfigPage (transitionForward = true) { + return { + type: actions.SHOW_CONFIG_PAGE, + value: transitionForward, + } +} + +function showAddTokenPage (transitionForward = true) { + return { + type: actions.SHOW_ADD_TOKEN_PAGE, + value: transitionForward, + } +} + +function showAddSuggestedTokenPage (transitionForward = true) { + return { + type: actions.SHOW_ADD_SUGGESTED_TOKEN_PAGE, + value: transitionForward, + } +} + +function addToken (address, symbol, decimals, image) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.addToken(address, symbol, decimals, image, (err, tokens) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + dispatch(actions.updateTokens(tokens)) + resolve(tokens) + }) + }) + } +} + +function removeToken (address) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.removeToken(address, (err, tokens) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + dispatch(actions.updateTokens(tokens)) + resolve(tokens) + }) + }) + } +} + +function addTokens (tokens) { + return dispatch => { + if (Array.isArray(tokens)) { + dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens[0]))) + return Promise.all(tokens.map(({ address, symbol, decimals }) => ( + dispatch(addToken(address, symbol, decimals)) + ))) + } else { + dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens))) + return Promise.all( + Object + .entries(tokens) + .map(([_, { address, symbol, decimals }]) => ( + dispatch(addToken(address, symbol, decimals)) + )) + ) + } + } +} + +function removeSuggestedTokens () { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.removeSuggestedTokens((err, suggestedTokens) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + } + dispatch(actions.clearPendingTokens()) + if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) { + return global.platform.closeCurrentWindow() + } + resolve(suggestedTokens) + }) + }) + .then(() => updateMetamaskStateFromBackground()) + .then(suggestedTokens => dispatch(actions.updateMetamaskState({...suggestedTokens}))) + } +} + +function addKnownMethodData (fourBytePrefix, methodData) { + return (dispatch) => { + background.addKnownMethodData(fourBytePrefix, methodData) + } +} + +function updateTokens (newTokens) { + return { + type: actions.UPDATE_TOKENS, + newTokens, + } +} + +function clearPendingTokens () { + return { + type: actions.CLEAR_PENDING_TOKENS, + } +} + +function goBackToInitView () { + return { + type: actions.BACK_TO_INIT_MENU, + } +} + +// +// notice +// + +function markNoticeRead (notice) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.markNoticeRead`) + return new Promise((resolve, reject) => { + background.markNoticeRead(notice, (err, notice) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + if (notice) { + dispatch(actions.showNotice(notice)) + resolve(true) + } else { + dispatch(actions.clearNotices()) + resolve(false) + } + }) + }) + } +} + +function showNotice (notice) { + return { + type: actions.SHOW_NOTICE, + value: notice, + } +} + +function clearNotices () { + return { + type: actions.CLEAR_NOTICES, + } +} + +function markAccountsFound () { + log.debug(`background.markAccountsFound`) + return callBackgroundThenUpdate(background.markAccountsFound) +} + +function retryTransaction (txId, gasPrice) { + log.debug(`background.retryTransaction`) + let newTxId + + return dispatch => { + return new Promise((resolve, reject) => { + background.retryTransaction(txId, gasPrice, (err, newState) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + const { selectedAddressTxList } = newState + const { id } = selectedAddressTxList[selectedAddressTxList.length - 1] + newTxId = id + resolve(newState) + }) + }) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => newTxId) + } +} + +function createCancelTransaction (txId, customGasPrice) { + log.debug('background.cancelTransaction') + let newTxId + + return dispatch => { + return new Promise((resolve, reject) => { + background.createCancelTransaction(txId, customGasPrice, (err, newState) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + const { selectedAddressTxList } = newState + const { id } = selectedAddressTxList[selectedAddressTxList.length - 1] + newTxId = id + resolve(newState) + }) + }) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => newTxId) + } +} + +function createSpeedUpTransaction (txId, customGasPrice) { + log.debug('background.createSpeedUpTransaction') + let newTx + + return dispatch => { + return new Promise((resolve, reject) => { + background.createSpeedUpTransaction(txId, customGasPrice, (err, newState) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + const { selectedAddressTxList } = newState + newTx = selectedAddressTxList[selectedAddressTxList.length - 1] + resolve(newState) + }) + }) + .then(newState => dispatch(actions.updateMetamaskState(newState))) + .then(() => newTx) + } +} + +// +// config +// + +function setProviderType (type) { + return (dispatch, getState) => { + const { type: currentProviderType } = getState().metamask.provider + log.debug(`background.setProviderType`, type) + background.setProviderType(type, (err, result) => { + if (err) { + log.error(err) + return dispatch(actions.displayWarning('Had a problem changing networks!')) + } + dispatch(setPreviousProvider(currentProviderType)) + dispatch(actions.updateProviderType(type)) + dispatch(actions.setSelectedToken()) + }) + + } +} + +function updateProviderType (type) { + return { + type: actions.SET_PROVIDER_TYPE, + value: type, + } +} + +function setPreviousProvider (type) { + return { + type: actions.SET_PREVIOUS_PROVIDER, + value: type, + } +} + +function updateAndSetCustomRpc (newRpc, chainId, ticker = 'ETH', nickname) { + return (dispatch) => { + log.debug(`background.updateAndSetCustomRpc: ${newRpc} ${chainId} ${ticker} ${nickname}`) + background.updateAndSetCustomRpc(newRpc, chainId, ticker, nickname || newRpc, (err, result) => { + if (err) { + log.error(err) + return dispatch(actions.displayWarning('Had a problem changing networks!')) + } + dispatch({ + type: actions.SET_RPC_TARGET, + value: newRpc, + }) + }) + } +} + +function setRpcTarget (newRpc, chainId, ticker = 'ETH', nickname) { + return (dispatch) => { + log.debug(`background.setRpcTarget: ${newRpc} ${chainId} ${ticker} ${nickname}`) + background.setCustomRpc(newRpc, chainId, ticker, nickname || newRpc, (err, result) => { + if (err) { + log.error(err) + return dispatch(actions.displayWarning('Had a problem changing networks!')) + } + dispatch(actions.setSelectedToken()) + }) + } +} + +function delRpcTarget (oldRpc) { + return (dispatch) => { + log.debug(`background.delRpcTarget: ${oldRpc}`) + background.delCustomRpc(oldRpc, (err, result) => { + if (err) { + log.error(err) + return dispatch(self.displayWarning('Had a problem removing network!')) + } + dispatch(actions.setSelectedToken()) + }) + } +} + +// Calls the addressBookController to add a new address. +function addToAddressBook (recipient, nickname = '') { + log.debug(`background.addToAddressBook`) + return (dispatch) => { + background.setAddressBook(recipient, nickname, (err, result) => { + if (err) { + log.error(err) + return dispatch(self.displayWarning('Address book failed to update')) + } + }) + } +} + +function useEtherscanProvider () { + log.debug(`background.useEtherscanProvider`) + background.useEtherscanProvider() + return { + type: actions.USE_ETHERSCAN_PROVIDER, + } +} + +function showNetworkDropdown () { + return { + type: actions.NETWORK_DROPDOWN_OPEN, + } +} + +function hideNetworkDropdown () { + return { + type: actions.NETWORK_DROPDOWN_CLOSE, + } +} + + +function showModal (payload) { + return { + type: actions.MODAL_OPEN, + payload, + } +} + +function hideModal (payload) { + return { + type: actions.MODAL_CLOSE, + payload, + } +} + +function showSidebar ({ transitionName, type, props }) { + return { + type: actions.SIDEBAR_OPEN, + value: { + transitionName, + type, + props, + }, + } +} + +function hideSidebar () { + return { + type: actions.SIDEBAR_CLOSE, + } +} + +function showAlert (msg) { + return { + type: actions.ALERT_OPEN, + value: msg, + } +} + +function hideAlert () { + return { + type: actions.ALERT_CLOSE, + } +} + +/** + * This action will receive two types of values via qrCodeData + * an object with the following structure {type, values} + * or null (used to clear the previous value) + */ +function qrCodeDetected (qrCodeData) { + return { + type: actions.QR_CODE_DETECTED, + value: qrCodeData, + } +} + +function showLoadingIndication (message) { + return { + type: actions.SHOW_LOADING, + value: message, + } +} + +function setHardwareWalletDefaultHdPath ({ device, path }) { + return { + type: actions.SET_HARDWARE_WALLET_DEFAULT_HD_PATH, + value: {device, path}, + } +} + +function hideLoadingIndication () { + return { + type: actions.HIDE_LOADING, + } +} + +function showSubLoadingIndication () { + return { + type: actions.SHOW_SUB_LOADING_INDICATION, + } +} + +function hideSubLoadingIndication () { + return { + type: actions.HIDE_SUB_LOADING_INDICATION, + } +} + +function displayWarning (text) { + return { + type: actions.DISPLAY_WARNING, + value: text, + } +} + +function hideWarning () { + return { + type: actions.HIDE_WARNING, + } +} + +function requestExportAccount () { + return { + type: actions.REQUEST_ACCOUNT_EXPORT, + } +} + +function exportAccount (password, address) { + var self = this + + return function (dispatch) { + dispatch(self.showLoadingIndication()) + + log.debug(`background.submitPassword`) + return new Promise((resolve, reject) => { + background.submitPassword(password, function (err) { + if (err) { + log.error('Error in submiting password.') + dispatch(self.hideLoadingIndication()) + dispatch(self.displayWarning('Incorrect Password.')) + return reject(err) + } + log.debug(`background.exportAccount`) + return background.exportAccount(address, function (err, result) { + dispatch(self.hideLoadingIndication()) + + if (err) { + log.error(err) + dispatch(self.displayWarning('Had a problem exporting the account.')) + return reject(err) + } + + // dispatch(self.exportAccountComplete()) + dispatch(self.showPrivateKey(result)) + + return resolve(result) + }) + }) + }) + } +} + +function exportAccountComplete () { + return { + type: actions.EXPORT_ACCOUNT, + } +} + +function showPrivateKey (key) { + return { + type: actions.SHOW_PRIVATE_KEY, + value: key, + } +} + +function setAccountLabel (account, label) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.setAccountLabel`) + + return new Promise((resolve, reject) => { + background.setAccountLabel(account, label, (err) => { + dispatch(actions.hideLoadingIndication()) + + if (err) { + dispatch(actions.displayWarning(err.message)) + reject(err) + } + + dispatch({ + type: actions.SET_ACCOUNT_LABEL, + value: { account, label }, + }) + + resolve(account) + }) + }) + } +} + +function showSendPage () { + return { + type: actions.SHOW_SEND_PAGE, + } +} + +function showSendTokenPage () { + return { + type: actions.SHOW_SEND_TOKEN_PAGE, + } +} + +function buyEth (opts) { + return (dispatch) => { + const url = getBuyEthUrl(opts) + global.platform.openWindow({ url }) + dispatch({ + type: actions.BUY_ETH, + }) + } +} + +function onboardingBuyEthView (address) { + return { + type: actions.ONBOARDING_BUY_ETH_VIEW, + value: address, + } +} + +function buyEthView (address) { + return { + type: actions.BUY_ETH_VIEW, + value: address, + } +} + +function coinBaseSubview () { + return { + type: actions.COINBASE_SUBVIEW, + } +} + +function pairUpdate (coin) { + return (dispatch) => { + dispatch(actions.showSubLoadingIndication()) + dispatch(actions.hideWarning()) + shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => { + dispatch(actions.hideSubLoadingIndication()) + if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error)) + dispatch({ + type: actions.PAIR_UPDATE, + value: { + marketinfo: mktResponse, + }, + }) + }) + } +} + +function shapeShiftSubview (network) { + var pair = 'btc_eth' + return (dispatch) => { + dispatch(actions.showSubLoadingIndication()) + shapeShiftRequest('marketinfo', {pair}, (mktResponse) => { + shapeShiftRequest('getcoins', {}, (response) => { + dispatch(actions.hideSubLoadingIndication()) + if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error)) + dispatch({ + type: actions.SHAPESHIFT_SUBVIEW, + value: { + marketinfo: mktResponse, + coinOptions: response, + }, + }) + }) + }) + } +} + +function coinShiftRquest (data, marketData) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + shapeShiftRequest('shift', { method: 'POST', data}, (response) => { + dispatch(actions.hideLoadingIndication()) + if (response.error) return dispatch(actions.displayWarning(response.error)) + var message = ` + Deposit your ${response.depositType} to the address below:` + log.debug(`background.createShapeShiftTx`) + background.createShapeShiftTx(response.deposit, response.depositType) + dispatch(actions.showQrView(response.deposit, [message].concat(marketData))) + }) + } +} + +function buyWithShapeShift (data) { + return dispatch => new Promise((resolve, reject) => { + shapeShiftRequest('shift', { method: 'POST', data}, (response) => { + if (response.error) { + return reject(response.error) + } + background.createShapeShiftTx(response.deposit, response.depositType) + return resolve(response) + }) + }) +} + +function showQrView (data, message) { + return { + type: actions.SHOW_QR_VIEW, + value: { + message: message, + data: data, + }, + } +} +function reshowQrCode (data, coin) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => { + if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error)) + + var message = [ + `Deposit your ${coin} to the address below:`, + `Deposit Limit: ${mktResponse.limit}`, + `Deposit Minimum:${mktResponse.minimum}`, + ] + + dispatch(actions.hideLoadingIndication()) + return dispatch(actions.showQrView(data, message)) + // return dispatch(actions.showModal({ + // name: 'SHAPESHIFT_DEPOSIT_TX', + // Qr: { data, message }, + // })) + }) + } +} + +function shapeShiftRequest (query, options, cb) { + var queryResponse, method + !options ? options = {} : null + options.method ? method = options.method : method = 'GET' + + var requestListner = function (request) { + try { + queryResponse = JSON.parse(this.responseText) + cb ? cb(queryResponse) : null + return queryResponse + } catch (e) { + cb ? cb({error: e}) : null + return e + } + } + + var shapShiftReq = new XMLHttpRequest() + shapShiftReq.addEventListener('load', requestListner) + shapShiftReq.open(method, `https://shapeshift.io/${query}/${options.pair ? options.pair : ''}`, true) + + if (options.method === 'POST') { + var jsonObj = JSON.stringify(options.data) + shapShiftReq.setRequestHeader('Content-Type', 'application/json') + return shapShiftReq.send(jsonObj) + } else { + return shapShiftReq.send() + } +} + +function setFeatureFlag (feature, activated, notificationType) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.setFeatureFlag(feature, activated, (err, updatedFeatureFlags) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + dispatch(actions.updateFeatureFlags(updatedFeatureFlags)) + notificationType && dispatch(actions.showModal({ name: notificationType })) + resolve(updatedFeatureFlags) + }) + }) + } +} + +function updateFeatureFlags (updatedFeatureFlags) { + return { + type: actions.UPDATE_FEATURE_FLAGS, + value: updatedFeatureFlags, + } +} + +function setPreference (preference, value) { + return dispatch => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.setPreference(preference, value, (err, updatedPreferences) => { + dispatch(actions.hideLoadingIndication()) + + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.updatePreferences(updatedPreferences)) + resolve(updatedPreferences) + }) + }) + } +} + +function updatePreferences (value) { + return { + type: actions.UPDATE_PREFERENCES, + value, + } +} + +function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) { + return setPreference('useNativeCurrencyAsPrimaryCurrency', value) +} + +function setShowFiatConversionOnTestnetsPreference (value) { + return setPreference('showFiatInTestnets', value) +} + +function setCompletedOnboarding () { + return dispatch => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.completeOnboarding(err => { + dispatch(actions.hideLoadingIndication()) + + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.completeOnboarding()) + resolve() + }) + }) + } +} + +function completeOnboarding () { + return { + type: actions.COMPLETE_ONBOARDING, + } +} + +function setCompletedUiMigration () { + return dispatch => { + dispatch(actions.showLoadingIndication()) + return new Promise((resolve, reject) => { + background.completeUiMigration(err => { + dispatch(actions.hideLoadingIndication()) + + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.completeUiMigration()) + resolve() + }) + }) + } +} + +function completeUiMigration () { + return { + type: actions.COMPLETE_UI_MIGRATION, + } +} + +function setNetworkNonce (networkNonce) { + return { + type: actions.SET_NETWORK_NONCE, + value: networkNonce, + } +} + +function updateNetworkNonce (address) { + return (dispatch) => { + return new Promise((resolve, reject) => { + global.ethQuery.getTransactionCount(address, (err, data) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + dispatch(setNetworkNonce(data)) + resolve(data) + }) + }) + } +} + +function setMouseUserState (isMouseUser) { + return { + type: actions.SET_MOUSE_USER_STATE, + value: isMouseUser, + } +} + +// Call Background Then Update +// +// A function generator for a common pattern wherein: +// We show loading indication. +// We call a background method. +// We hide loading indication. +// If it errored, we show a warning. +// If it didn't, we update the state. +function callBackgroundThenUpdateNoSpinner (method, ...args) { + return (dispatch) => { + method.call(background, ...args, (err) => { + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + forceUpdateMetamaskState(dispatch) + }) + } +} + +function callBackgroundThenUpdate (method, ...args) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + method.call(background, ...args, (err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + forceUpdateMetamaskState(dispatch) + }) + } +} + +function forceUpdateMetamaskState (dispatch) { + log.debug(`background.getState`) + return new Promise((resolve, reject) => { + background.getState((err, newState) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch(actions.updateMetamaskState(newState)) + resolve(newState) + }) + }) +} + +function toggleAccountMenu () { + return { + type: actions.TOGGLE_ACCOUNT_MENU, + } +} + +function setParticipateInMetaMetrics (val) { + return (dispatch) => { + log.debug(`background.setParticipateInMetaMetrics`) + return new Promise((resolve, reject) => { + background.setParticipateInMetaMetrics(val, (err, metaMetricsId) => { + log.debug(err) + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch({ + type: actions.SET_PARTICIPATE_IN_METAMETRICS, + value: val, + }) + + resolve([val, metaMetricsId]) + }) + }) + } +} + +function setMetaMetricsSendCount (val) { + return (dispatch) => { + log.debug(`background.setMetaMetricsSendCount`) + return new Promise((resolve, reject) => { + background.setMetaMetricsSendCount(val, (err) => { + if (err) { + dispatch(actions.displayWarning(err.message)) + return reject(err) + } + + dispatch({ + type: actions.SET_METAMETRICS_SEND_COUNT, + value: val, + }) + + resolve(val) + }) + }) + } +} + +function setUseBlockie (val) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + log.debug(`background.setUseBlockie`) + background.setUseBlockie(val, (err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + }) + dispatch({ + type: actions.SET_USE_BLOCKIE, + value: val, + }) + } +} + +function updateCurrentLocale (key) { + return (dispatch) => { + dispatch(actions.showLoadingIndication()) + return fetchLocale(key) + .then((localeMessages) => { + log.debug(`background.setCurrentLocale`) + background.setCurrentLocale(key, (err) => { + dispatch(actions.hideLoadingIndication()) + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + dispatch(actions.setCurrentLocale(key)) + dispatch(actions.setLocaleMessages(localeMessages)) + }) + }) + } +} + +function setCurrentLocale (key) { + return { + type: actions.SET_CURRENT_LOCALE, + value: key, + } +} + +function setLocaleMessages (localeMessages) { + return { + type: actions.SET_LOCALE_MESSAGES, + value: localeMessages, + } +} + +function updateNetworkEndpointType (networkEndpointType) { + return { + type: actions.UPDATE_NETWORK_ENDPOINT_TYPE, + value: networkEndpointType, + } +} + +function setPendingTokens (pendingTokens) { + const { customToken = {}, selectedTokens = {} } = pendingTokens + const { address, symbol, decimals } = customToken + const tokens = address && symbol && decimals + ? { ...selectedTokens, [address]: { ...customToken, isCustom: true } } + : selectedTokens + + return { + type: actions.SET_PENDING_TOKENS, + payload: tokens, + } +} + +function approveProviderRequest (tabID) { + return (dispatch) => { + background.approveProviderRequest(tabID) + } +} + +function rejectProviderRequest (tabID) { + return (dispatch) => { + background.rejectProviderRequest(tabID) + } +} + +function clearApprovedOrigins () { + return (dispatch) => { + background.clearApprovedOrigins() + } +} + +function setFirstTimeFlowType (type) { + return (dispatch) => { + log.debug(`background.setFirstTimeFlowType`) + background.setFirstTimeFlowType(type, (err) => { + if (err) { + return dispatch(actions.displayWarning(err.message)) + } + }) + dispatch({ + type: actions.SET_FIRST_TIME_FLOW_TYPE, + value: type, + }) + } +} diff --git a/ui/app/store/store.js b/ui/app/store/store.js new file mode 100644 index 000000000..9f12f469e --- /dev/null +++ b/ui/app/store/store.js @@ -0,0 +1,21 @@ +const createStore = require('redux').createStore +const applyMiddleware = require('redux').applyMiddleware +const thunkMiddleware = require('redux-thunk').default +const rootReducer = require('../ducks') +const createLogger = require('redux-logger').createLogger + +global.METAMASK_DEBUG = process.env.METAMASK_DEBUG + +module.exports = configureStore + +const loggerMiddleware = createLogger({ + predicate: () => global.METAMASK_DEBUG, +}) + +const middlewares = [thunkMiddleware, loggerMiddleware] + +const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore) + +function configureStore (initialState) { + return createStoreWithMiddleware(rootReducer, initialState) +} |