1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 18:41:38 +01:00
metamask-extension/ui/app/store/actions.js
2020-02-15 17:04:21 -03:30

2521 lines
63 KiB
JavaScript

import abi from 'human-standard-token-abi'
import pify from 'pify'
import getBuyEthUrl from '../../../app/scripts/lib/buy-eth-url'
import { getTokenAddressFromTokenObject, checksumAddress } from '../helpers/utils/util'
import { calcTokenBalance, estimateGas } from '../pages/send/send.utils'
import ethUtil from 'ethereumjs-util'
import { fetchLocale } from '../helpers/utils/i18n-helper'
import { getMethodDataAsync } from '../helpers/utils/transactions.util'
import { fetchSymbolAndDecimals } from '../helpers/utils/token-util'
import switchDirection from '../helpers/utils/switch-direction'
import log from 'loglevel'
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../app/scripts/lib/enums'
import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util'
import { setCustomGasLimit } from '../ducks/gas/gas.duck'
import txHelper from '../../lib/tx-helper'
export const actionConstants = {
GO_HOME: 'GO_HOME',
// modal state
MODAL_OPEN: 'UI_MODAL_OPEN',
MODAL_CLOSE: 'UI_MODAL_CLOSE',
// notification state
CLOSE_NOTIFICATION_WINDOW: 'CLOSE_NOTIFICATION_WINDOW',
// sidebar state
SIDEBAR_OPEN: 'UI_SIDEBAR_OPEN',
SIDEBAR_CLOSE: 'UI_SIDEBAR_CLOSE',
// sidebar state
ALERT_OPEN: 'UI_ALERT_OPEN',
ALERT_CLOSE: 'UI_ALERT_CLOSE',
QR_CODE_DETECTED: 'UI_QR_CODE_DETECTED',
// network dropdown open
NETWORK_DROPDOWN_OPEN: 'UI_NETWORK_DROPDOWN_OPEN',
NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE',
// transition state
TRANSITION_FORWARD: 'TRANSITION_FORWARD',
// remote state
UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE',
FORGOT_PASSWORD: 'FORGOT_PASSWORD',
CLOSE_WELCOME_SCREEN: 'CLOSE_WELCOME_SCREEN',
// unlock screen
UNLOCK_IN_PROGRESS: 'UNLOCK_IN_PROGRESS',
UNLOCK_FAILED: 'UNLOCK_FAILED',
UNLOCK_SUCCEEDED: 'UNLOCK_SUCCEEDED',
UNLOCK_METAMASK: 'UNLOCK_METAMASK',
LOCK_METAMASK: 'LOCK_METAMASK',
// error handling
DISPLAY_WARNING: 'DISPLAY_WARNING',
HIDE_WARNING: 'HIDE_WARNING',
// accounts screen
SET_SELECTED_TOKEN: 'SET_SELECTED_TOKEN',
SHOW_ACCOUNT_DETAIL: 'SHOW_ACCOUNT_DETAIL',
SHOW_ACCOUNTS_PAGE: 'SHOW_ACCOUNTS_PAGE',
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
// account detail screen
SHOW_SEND_TOKEN_PAGE: 'SHOW_SEND_TOKEN_PAGE',
SHOW_PRIVATE_KEY: 'SHOW_PRIVATE_KEY',
SET_ACCOUNT_LABEL: 'SET_ACCOUNT_LABEL',
SET_NETWORK_NONCE: 'SET_NETWORK_NONCE',
// tx conf screen
COMPLETED_TX: 'COMPLETED_TX',
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
UPDATE_TRANSACTION_PARAMS: 'UPDATE_TRANSACTION_PARAMS',
SET_NEXT_NONCE: 'SET_NEXT_NONCE',
// send screen
UPDATE_GAS_LIMIT: 'UPDATE_GAS_LIMIT',
UPDATE_GAS_PRICE: 'UPDATE_GAS_PRICE',
UPDATE_GAS_TOTAL: 'UPDATE_GAS_TOTAL',
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_ERRORS: 'UPDATE_SEND_ERRORS',
UPDATE_MAX_MODE: 'UPDATE_MAX_MODE',
UPDATE_SEND: 'UPDATE_SEND',
CLEAR_SEND: 'CLEAR_SEND',
GAS_LOADING_STARTED: 'GAS_LOADING_STARTED',
GAS_LOADING_FINISHED: 'GAS_LOADING_FINISHED',
UPDATE_SEND_ENS_RESOLUTION: 'UPDATE_SEND_ENS_RESOLUTION',
UPDATE_SEND_ENS_RESOLUTION_ERROR: 'UPDATE_SEND_ENS_RESOLUTION_ERROR',
// config screen
SET_RPC_TARGET: 'SET_RPC_TARGET',
SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE',
SET_PREVIOUS_PROVIDER: 'SET_PREVIOUS_PROVIDER',
UPDATE_TOKENS: 'UPDATE_TOKENS',
SET_HARDWARE_WALLET_DEFAULT_HD_PATH: 'SET_HARDWARE_WALLET_DEFAULT_HD_PATH',
// loading overlay
SHOW_LOADING: 'SHOW_LOADING_INDICATION',
HIDE_LOADING: 'HIDE_LOADING_INDICATION',
// buy Eth with coinbase
BUY_ETH: 'BUY_ETH',
PAIR_UPDATE: 'PAIR_UPDATE',
SHOW_SUB_LOADING_INDICATION: 'SHOW_SUB_LOADING_INDICATION',
HIDE_SUB_LOADING_INDICATION: 'HIDE_SUB_LOADING_INDICATION',
// QR STUFF:
SHOW_QR: 'SHOW_QR',
SHOW_QR_VIEW: 'SHOW_QR_VIEW',
TOGGLE_ACCOUNT_MENU: 'TOGGLE_ACCOUNT_MENU',
SET_USE_BLOCKIE: 'SET_USE_BLOCKIE',
SET_USE_NONCEFIELD: 'SET_USE_NONCEFIELD',
UPDATE_CUSTOM_NONCE: 'UPDATE_CUSTOM_NONCE',
SET_IPFS_GATEWAY: 'SET_IPFS_GATEWAY',
SET_PARTICIPATE_IN_METAMETRICS: 'SET_PARTICIPATE_IN_METAMETRICS',
SET_METAMETRICS_SEND_COUNT: 'SET_METAMETRICS_SEND_COUNT',
// locale
SET_CURRENT_LOCALE: 'SET_CURRENT_LOCALE',
// Feature Flags
UPDATE_FEATURE_FLAGS: 'UPDATE_FEATURE_FLAGS',
// Preferences
UPDATE_PREFERENCES: 'UPDATE_PREFERENCES',
// Onboarding
COMPLETE_ONBOARDING: 'COMPLETE_ONBOARDING',
SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE',
// Network
SET_PENDING_TOKENS: 'SET_PENDING_TOKENS',
CLEAR_PENDING_TOKENS: 'CLEAR_PENDING_TOKENS',
SET_FIRST_TIME_FLOW_TYPE: 'SET_FIRST_TIME_FLOW_TYPE',
SET_SELECTED_SETTINGS_RPC_URL: 'SET_SELECTED_SETTINGS_RPC_URL',
SET_NETWORKS_TAB_ADD_MODE: 'SET_NETWORKS_TAB_ADD_MODE',
LOADING_METHOD_DATA_STARTED: 'LOADING_METHOD_DATA_STARTED',
LOADING_METHOD_DATA_FINISHED: 'LOADING_METHOD_DATA_FINISHED',
LOADING_TOKEN_PARAMS_STARTED: 'LOADING_TOKEN_PARAMS_STARTED',
LOADING_TOKEN_PARAMS_FINISHED: 'LOADING_TOKEN_PARAMS_FINISHED',
SET_REQUEST_ACCOUNT_TABS: 'SET_REQUEST_ACCOUNT_TABS',
SET_CURRENT_WINDOW_TAB: 'SET_CURRENT_WINDOW_TAB',
SET_OPEN_METAMASK_TAB_IDS: 'SET_OPEN_METAMASK_TAB_IDS',
}
let background = null
export function _setBackgroundConnection (backgroundConnection) {
background = backgroundConnection
}
export function goHome () {
return {
type: actionConstants.GO_HOME,
}
}
// async actions
export function tryUnlockMetamask (password) {
return (dispatch) => {
dispatch(showLoadingIndication())
dispatch(unlockInProgress())
log.debug(`background.submitPassword`)
return new Promise((resolve, reject) => {
background.submitPassword(password, (error) => {
if (error) {
return reject(error)
}
resolve()
})
})
.then(() => {
dispatch(unlockSucceeded())
return forceUpdateMetamaskState(dispatch)
})
.then(() => {
return new Promise((resolve, reject) => {
background.verifySeedPhrase((err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
})
.then(() => {
dispatch(transitionForward())
dispatch(hideLoadingIndication())
})
.catch((err) => {
dispatch(unlockFailed(err.message))
dispatch(hideLoadingIndication())
return Promise.reject(err)
})
}
}
export function transitionForward () {
return {
type: actionConstants.TRANSITION_FORWARD,
}
}
export function createNewVaultAndRestore (password, seed) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.createNewVaultAndRestore`)
let vault
return new Promise((resolve, reject) => {
background.createNewVaultAndRestore(password, seed, (err, _vault) => {
if (err) {
return reject(err)
}
vault = _vault
resolve()
})
})
.then(() => dispatch(unMarkPasswordForgotten()))
.then(() => {
dispatch(showAccountsPage())
dispatch(hideLoadingIndication())
return vault
})
.catch((err) => {
dispatch(displayWarning(err.message))
dispatch(hideLoadingIndication())
return Promise.reject(err)
})
}
}
export function createNewVaultAndGetSeedPhrase (password) {
return async (dispatch) => {
dispatch(showLoadingIndication())
try {
await createNewVault(password)
const seedWords = await verifySeedPhrase()
dispatch(hideLoadingIndication())
return seedWords
} catch (error) {
dispatch(hideLoadingIndication())
dispatch(displayWarning(error.message))
throw new Error(error.message)
}
}
}
export function unlockAndGetSeedPhrase (password) {
return async (dispatch) => {
dispatch(showLoadingIndication())
try {
await submitPassword(password)
const seedWords = await verifySeedPhrase()
await forceUpdateMetamaskState(dispatch)
dispatch(hideLoadingIndication())
return seedWords
} catch (error) {
dispatch(hideLoadingIndication())
dispatch(displayWarning(error.message))
throw new Error(error.message)
}
}
}
export function submitPassword (password) {
return new Promise((resolve, reject) => {
background.submitPassword(password, (error) => {
if (error) {
return reject(error)
}
resolve()
})
})
}
export function createNewVault (password) {
return new Promise((resolve, reject) => {
background.createNewVaultAndKeychain(password, (error) => {
if (error) {
return reject(error)
}
resolve(true)
})
})
}
export function verifyPassword (password) {
return new Promise((resolve, reject) => {
background.submitPassword(password, (error) => {
if (error) {
return reject(error)
}
resolve(true)
})
})
}
export function verifySeedPhrase () {
return new Promise((resolve, reject) => {
background.verifySeedPhrase((error, seedWords) => {
if (error) {
return reject(error)
}
resolve(seedWords)
})
})
}
export function requestRevealSeedWords (password) {
return async (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.submitPassword`)
try {
await verifyPassword(password)
const seedWords = await verifySeedPhrase()
dispatch(hideLoadingIndication())
return seedWords
} catch (error) {
dispatch(hideLoadingIndication())
dispatch(displayWarning(error.message))
throw new Error(error.message)
}
}
}
export function tryReverseResolveAddress (address) {
return () => {
return new Promise((resolve) => {
background.tryReverseResolveAddress(address, (err) => {
if (err) {
log.error(err)
}
resolve()
})
})
}
}
export function fetchInfoToSync () {
return (dispatch) => {
log.debug(`background.fetchInfoToSync`)
return new Promise((resolve, reject) => {
background.fetchInfoToSync((err, result) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve(result)
})
})
}
}
export function resetAccount () {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.resetAccount((err, account) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
log.info('Transaction history reset for ' + account)
dispatch(showAccountsPage())
resolve(account)
})
})
}
}
export function removeAccount (address) {
return async (dispatch) => {
dispatch(showLoadingIndication())
try {
await new Promise((resolve, reject) => {
background.removeAccount(address, (error, account) => {
if (error) {
return reject(error)
}
return resolve(account)
})
})
await forceUpdateMetamaskState(dispatch)
} catch (error) {
dispatch(displayWarning(error.message))
throw error
} finally {
dispatch(hideLoadingIndication())
}
log.info('Account removed: ' + address)
dispatch(showAccountsPage())
}
}
export function addNewKeyring (type, opts) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.addNewKeyring`)
background.addNewKeyring(type, opts, (err) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
}
dispatch(showAccountsPage())
})
}
}
export function importNewAccount (strategy, args) {
return async (dispatch) => {
let newState
dispatch(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(hideLoadingIndication())
dispatch(displayWarning(err.message))
throw err
}
dispatch(hideLoadingIndication())
dispatch(updateMetamaskState(newState))
if (newState.selectedAddress) {
dispatch({
type: actionConstants.SHOW_ACCOUNT_DETAIL,
value: newState.selectedAddress,
})
}
return newState
}
}
export function addNewAccount () {
log.debug(`background.addNewAccount`)
return (dispatch, getState) => {
const oldIdentities = getState().metamask.identities
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.addNewAccount((err, { identities: newIdentities }) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
const newAccountAddress = Object.keys(newIdentities).find((address) => !oldIdentities[address])
dispatch(hideLoadingIndication())
forceUpdateMetamaskState(dispatch)
return resolve(newAccountAddress)
})
})
}
}
export function checkHardwareStatus (deviceName, hdPath) {
log.debug(`background.checkHardwareStatus`, deviceName, hdPath)
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.checkHardwareStatus(deviceName, hdPath, (err, unlocked) => {
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(hideLoadingIndication())
forceUpdateMetamaskState(dispatch)
return resolve(unlocked)
})
})
}
}
export function forgetDevice (deviceName) {
log.debug(`background.forgetDevice`, deviceName)
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.forgetDevice(deviceName, (err) => {
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(hideLoadingIndication())
forceUpdateMetamaskState(dispatch)
return resolve()
})
})
}
}
export function connectHardware (deviceName, page, hdPath) {
log.debug(`background.connectHardware`, deviceName, page, hdPath)
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.connectHardware(deviceName, page, hdPath, (err, accounts) => {
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(hideLoadingIndication())
forceUpdateMetamaskState(dispatch)
return resolve(accounts)
})
})
}
}
export function unlockHardwareWalletAccount (index, deviceName, hdPath) {
log.debug(`background.unlockHardwareWalletAccount`, index, deviceName, hdPath)
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.unlockHardwareWalletAccount(index, deviceName, hdPath, (err) => {
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(hideLoadingIndication())
return resolve()
})
})
}
}
export function showQrScanner () {
return (dispatch) => {
dispatch(showModal({
name: 'QR_SCANNER',
}))
}
}
export function setCurrentCurrency (currencyCode) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setCurrentCurrency`)
background.setCurrentCurrency(currencyCode, (err, data) => {
dispatch(hideLoadingIndication())
if (err) {
log.error(err.stack)
return dispatch(displayWarning(err.message))
}
dispatch({
type: actionConstants.SET_CURRENT_FIAT,
value: {
currentCurrency: data.currentCurrency,
conversionRate: data.conversionRate,
conversionDate: data.conversionDate,
},
})
})
}
}
export function signMsg (msgData) {
log.debug('action - signMsg')
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signMessage`)
background.signMessage(msgData, (err, newState) => {
log.debug('signMessage called back')
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(completedTx(msgData.metamaskId))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function signPersonalMsg (msgData) {
log.debug('action - signPersonalMsg')
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signPersonalMessage`)
background.signPersonalMessage(msgData, (err, newState) => {
log.debug('signPersonalMessage called back')
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(completedTx(msgData.metamaskId))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function signTypedMsg (msgData) {
log.debug('action - signTypedMsg')
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
log.debug(`actions calling background.signTypedMessage`)
background.signTypedMessage(msgData, (err, newState) => {
log.debug('signTypedMessage called back')
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
log.error(err)
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(completedTx(msgData.metamaskId))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function signTx (txData) {
return (dispatch) => {
global.ethQuery.sendTransaction(txData, (err) => {
if (err) {
return dispatch(displayWarning(err.message))
}
})
dispatch(showConfTxPage())
}
}
export function setGasLimit (gasLimit) {
return {
type: actionConstants.UPDATE_GAS_LIMIT,
value: gasLimit,
}
}
export function setGasPrice (gasPrice) {
return {
type: actionConstants.UPDATE_GAS_PRICE,
value: gasPrice,
}
}
export function setGasTotal (gasTotal) {
return {
type: actionConstants.UPDATE_GAS_TOTAL,
value: gasTotal,
}
}
export function updateGasData ({
gasPrice,
blockGasLimit,
selectedAddress,
selectedToken,
to,
value,
data,
}) {
return (dispatch) => {
dispatch(gasLoadingStarted())
return estimateGas({
estimateGasMethod: background.estimateGas,
blockGasLimit,
selectedAddress,
selectedToken,
to,
value,
estimateGasPrice: gasPrice,
data,
})
.then((gas) => {
dispatch(setGasLimit(gas))
dispatch(setCustomGasLimit(gas))
dispatch(updateSendErrors({ gasLoadingError: null }))
dispatch(gasLoadingFinished())
})
.catch((err) => {
log.error(err)
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' }))
dispatch(gasLoadingFinished())
})
}
}
export function gasLoadingStarted () {
return {
type: actionConstants.GAS_LOADING_STARTED,
}
}
export function gasLoadingFinished () {
return {
type: actionConstants.GAS_LOADING_FINISHED,
}
}
export 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' })
})
}
}
export function updateSendErrors (errorObject) {
return {
type: actionConstants.UPDATE_SEND_ERRORS,
value: errorObject,
}
}
export function setSendTokenBalance (tokenBalance) {
return {
type: actionConstants.UPDATE_SEND_TOKEN_BALANCE,
value: tokenBalance,
}
}
export function updateSendHexData (value) {
return {
type: actionConstants.UPDATE_SEND_HEX_DATA,
value,
}
}
export function updateSendTo (to, nickname = '') {
return {
type: actionConstants.UPDATE_SEND_TO,
value: { to, nickname },
}
}
export function updateSendAmount (amount) {
return {
type: actionConstants.UPDATE_SEND_AMOUNT,
value: amount,
}
}
export function updateCustomNonce (value) {
return {
type: actionConstants.UPDATE_CUSTOM_NONCE,
value: value,
}
}
export function setMaxModeTo (bool) {
return {
type: actionConstants.UPDATE_MAX_MODE,
value: bool,
}
}
export function updateSend (newSend) {
return {
type: actionConstants.UPDATE_SEND,
value: newSend,
}
}
export function clearSend () {
return {
type: actionConstants.CLEAR_SEND,
}
}
export function updateSendEnsResolution (ensResolution) {
return {
type: actionConstants.UPDATE_SEND_ENS_RESOLUTION,
payload: ensResolution,
}
}
export function updateSendEnsResolutionError (errorMessage) {
return {
type: actionConstants.UPDATE_SEND_ENS_RESOLUTION_ERROR,
payload: errorMessage,
}
}
export function signTokenTx (tokenAddress, toAddress, amount, txData) {
return (dispatch) => {
dispatch(showLoadingIndication())
const token = global.eth.contract(abi).at(tokenAddress)
token.transfer(toAddress, ethUtil.addHexPrefix(amount), txData)
.catch((err) => {
dispatch(hideLoadingIndication())
dispatch(displayWarning(err.message))
})
dispatch(showConfTxPage())
}
}
const updateMetamaskStateFromBackground = () => {
log.debug(`background.getState`)
return new Promise((resolve, reject) => {
background.getState((error, newState) => {
if (error) {
return reject(error)
}
resolve(newState)
})
})
}
export function updateTransaction (txData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.updateTransaction(txData, (err) => {
dispatch(updateTransactionParams(txData.id, txData.txParams))
if (err) {
dispatch(txError(err))
dispatch(goHome())
log.error(err.message)
return reject(err)
}
resolve(txData)
})
})
.then(() => updateMetamaskStateFromBackground())
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => {
dispatch(showConfTxPage({ id: txData.id }))
dispatch(hideLoadingIndication())
return txData
})
}
}
export function updateAndApproveTx (txData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.updateAndApproveTransaction(txData, (err) => {
dispatch(updateTransactionParams(txData.id, txData.txParams))
dispatch(clearSend())
if (err) {
dispatch(txError(err))
dispatch(goHome())
log.error(err.message)
return reject(err)
}
resolve(txData)
})
})
.then(() => updateMetamaskStateFromBackground())
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => {
dispatch(clearSend())
dispatch(completedTx(txData.id))
dispatch(hideLoadingIndication())
dispatch(updateCustomNonce(''))
dispatch(closeCurrentNotificationWindow())
return txData
})
.catch((err) => {
dispatch(hideLoadingIndication())
return Promise.reject(err)
})
}
}
export function completedTx (id) {
return (dispatch, getState) => {
const state = getState()
const {
unapprovedTxs,
unapprovedMsgs,
unapprovedPersonalMsgs,
unapprovedTypedMessages,
network,
} = state.metamask
const unconfirmedActions = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network)
const otherUnconfirmedActions = unconfirmedActions.filter((tx) => tx.id !== id)
dispatch({
type: actionConstants.COMPLETED_TX,
value: {
id,
unconfirmedActionsCount: otherUnconfirmedActions.length,
},
})
}
}
export function updateTransactionParams (id, txParams) {
return {
type: actionConstants.UPDATE_TRANSACTION_PARAMS,
id,
value: txParams,
}
}
export function txError (err) {
return {
type: actionConstants.TRANSACTION_ERROR,
message: err.message,
}
}
export function cancelMsg (msgData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.cancelMessage(msgData.id, (err, newState) => {
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(completedTx(msgData.id))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function cancelPersonalMsg (msgData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
const id = msgData.id
background.cancelPersonalMessage(id, (err, newState) => {
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(completedTx(id))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function cancelTypedMsg (msgData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
const id = msgData.id
background.cancelTypedMessage(id, (err, newState) => {
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
if (err) {
return reject(err)
}
dispatch(completedTx(id))
dispatch(closeCurrentNotificationWindow())
return resolve(msgData)
})
})
}
}
export function cancelTx (txData) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.cancelTransaction(txData.id, (err) => {
if (err) {
return reject(err)
}
resolve()
})
})
.then(() => updateMetamaskStateFromBackground())
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => {
dispatch(clearSend())
dispatch(completedTx(txData.id))
dispatch(hideLoadingIndication())
dispatch(closeCurrentNotificationWindow())
return txData
})
}
}
/**
* Cancels all of the given transactions
* @param {Array<object>} txDataList - a list of tx data objects
* @returns {function(*): Promise<void>}
*/
export function cancelTxs (txDataList) {
return async (dispatch) => {
dispatch(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(updateMetamaskState(newState))
dispatch(clearSend())
txIds.forEach((id) => {
dispatch(completedTx(id))
})
dispatch(hideLoadingIndication())
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
return global.platform.closeCurrentWindow()
}
}
}
export function markPasswordForgotten () {
return (dispatch) => {
return background.markPasswordForgotten(() => {
dispatch(hideLoadingIndication())
dispatch(forgotPassword())
forceUpdateMetamaskState(dispatch)
})
}
}
export function unMarkPasswordForgotten () {
return (dispatch) => {
return new Promise((resolve) => {
background.unMarkPasswordForgotten(() => {
dispatch(forgotPassword(false))
resolve()
})
})
.then(() => forceUpdateMetamaskState(dispatch))
}
}
export function forgotPassword (forgotPasswordState = true) {
return {
type: actionConstants.FORGOT_PASSWORD,
value: forgotPasswordState,
}
}
export function closeWelcomeScreen () {
return {
type: actionConstants.CLOSE_WELCOME_SCREEN,
}
}
//
// unlock screen
//
export function unlockInProgress () {
return {
type: actionConstants.UNLOCK_IN_PROGRESS,
}
}
export function unlockFailed (message) {
return {
type: actionConstants.UNLOCK_FAILED,
value: message,
}
}
export function unlockSucceeded (message) {
return {
type: actionConstants.UNLOCK_SUCCEEDED,
value: message,
}
}
export function unlockMetamask (account) {
return {
type: actionConstants.UNLOCK_METAMASK,
value: account,
}
}
export function updateMetamaskState (newState) {
return {
type: actionConstants.UPDATE_METAMASK_STATE,
value: newState,
}
}
const backgroundSetLocked = () => {
return new Promise((resolve, reject) => {
background.setLocked((error) => {
if (error) {
return reject(error)
}
resolve()
})
})
}
export function lockMetamask () {
log.debug(`background.setLocked`)
return (dispatch) => {
dispatch(showLoadingIndication())
return backgroundSetLocked()
.then(() => updateMetamaskStateFromBackground())
.catch((error) => {
dispatch(displayWarning(error.message))
return Promise.reject(error)
})
.then((newState) => {
dispatch(updateMetamaskState(newState))
dispatch(hideLoadingIndication())
dispatch({ type: actionConstants.LOCK_METAMASK })
})
.catch(() => {
dispatch(hideLoadingIndication())
dispatch({ type: actionConstants.LOCK_METAMASK })
})
}
}
export function setCurrentAccountTab (newTabName) {
log.debug(`background.setCurrentAccountTab: ${newTabName}`)
return callBackgroundThenUpdateNoSpinner(background.setCurrentAccountTab, newTabName)
}
export function setSelectedToken (tokenAddress) {
return {
type: actionConstants.SET_SELECTED_TOKEN,
value: tokenAddress || null,
}
}
export function setSelectedAddress (address) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setSelectedAddress`)
background.setSelectedAddress(address, (err) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
}
})
}
}
export function showAccountDetail (address) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setSelectedAddress`)
background.setSelectedAddress(address, (err, tokens) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
}
background.handleNewAccountSelected(origin, address)
dispatch(updateTokens(tokens))
dispatch({
type: actionConstants.SHOW_ACCOUNT_DETAIL,
value: address,
})
dispatch(setSelectedToken())
})
}
}
export function showAccountsPage () {
return {
type: actionConstants.SHOW_ACCOUNTS_PAGE,
}
}
export function showConfTxPage ({ transForward = true, id } = {}) {
return {
type: actionConstants.SHOW_CONF_TX_PAGE,
transForward,
id,
}
}
export function addToken (address, symbol, decimals, image) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.addToken(address, symbol, decimals, image, (err, tokens) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(updateTokens(tokens))
resolve(tokens)
})
})
}
}
export function removeToken (address) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.removeToken(address, (err, tokens) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(updateTokens(tokens))
resolve(tokens)
})
})
}
}
export function addTokens (tokens) {
return (dispatch) => {
if (Array.isArray(tokens)) {
dispatch(setSelectedToken(getTokenAddressFromTokenObject(tokens[0])))
return Promise.all(tokens.map(({ address, symbol, decimals }) => (
dispatch(addToken(address, symbol, decimals))
)))
} else {
dispatch(setSelectedToken(getTokenAddressFromTokenObject(tokens)))
return Promise.all(
Object
.entries(tokens)
.map(([_, { address, symbol, decimals }]) => (
dispatch(addToken(address, symbol, decimals))
))
)
}
}
}
export function removeSuggestedTokens () {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve) => {
background.removeSuggestedTokens((err, suggestedTokens) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
}
dispatch(clearPendingTokens())
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION) {
return global.platform.closeCurrentWindow()
}
resolve(suggestedTokens)
})
})
.then(() => updateMetamaskStateFromBackground())
.then((suggestedTokens) => dispatch(updateMetamaskState({ ...suggestedTokens })))
}
}
export function addKnownMethodData (fourBytePrefix, methodData) {
return () => {
background.addKnownMethodData(fourBytePrefix, methodData)
}
}
export function updateTokens (newTokens) {
return {
type: actionConstants.UPDATE_TOKENS,
newTokens,
}
}
export function clearPendingTokens () {
return {
type: actionConstants.CLEAR_PENDING_TOKENS,
}
}
export 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(displayWarning(err.message))
return reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
newTxId = id
resolve(newState)
})
})
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => newTxId)
}
}
export 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(displayWarning(err.message))
return reject(err)
}
const { selectedAddressTxList } = newState
const { id } = selectedAddressTxList[selectedAddressTxList.length - 1]
newTxId = id
resolve(newState)
})
})
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => newTxId)
}
}
export 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(displayWarning(err.message))
return reject(err)
}
const { selectedAddressTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
resolve(newState)
})
})
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => newTx)
}
}
export function createRetryTransaction (txId, customGasPrice) {
log.debug('background.createRetryTransaction')
let newTx
return (dispatch) => {
return new Promise((resolve, reject) => {
background.createSpeedUpTransaction(txId, customGasPrice, (err, newState) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
const { selectedAddressTxList } = newState
newTx = selectedAddressTxList[selectedAddressTxList.length - 1]
resolve(newState)
})
})
.then((newState) => dispatch(updateMetamaskState(newState)))
.then(() => newTx)
}
}
//
// config
//
export function setProviderType (type) {
return (dispatch, getState) => {
const { type: currentProviderType } = getState().metamask.provider
log.debug(`background.setProviderType`, type)
background.setProviderType(type, (err) => {
if (err) {
log.error(err)
return dispatch(displayWarning('Had a problem changing networks!'))
}
dispatch(setPreviousProvider(currentProviderType))
dispatch(updateProviderType(type))
dispatch(setSelectedToken())
})
}
}
export function updateProviderType (type) {
return {
type: actionConstants.SET_PROVIDER_TYPE,
value: type,
}
}
export function setPreviousProvider (type) {
return {
type: actionConstants.SET_PREVIOUS_PROVIDER,
value: type,
}
}
export function updateAndSetCustomRpc (newRpc, chainId, ticker = 'ETH', nickname, rpcPrefs) {
return (dispatch) => {
log.debug(`background.updateAndSetCustomRpc: ${newRpc} ${chainId} ${ticker} ${nickname}`)
background.updateAndSetCustomRpc(newRpc, chainId, ticker, nickname || newRpc, rpcPrefs, (err) => {
if (err) {
log.error(err)
return dispatch(displayWarning('Had a problem changing networks!'))
}
dispatch({
type: actionConstants.SET_RPC_TARGET,
value: newRpc,
})
})
}
}
export function editRpc (oldRpc, newRpc, chainId, ticker = 'ETH', nickname, rpcPrefs) {
return (dispatch) => {
log.debug(`background.delRpcTarget: ${oldRpc}`)
background.delCustomRpc(oldRpc, (err) => {
if (err) {
log.error(err)
return dispatch(displayWarning('Had a problem removing network!'))
}
dispatch(setSelectedToken())
background.updateAndSetCustomRpc(newRpc, chainId, ticker, nickname || newRpc, rpcPrefs, (err) => {
if (err) {
log.error(err)
return dispatch(displayWarning('Had a problem changing networks!'))
}
dispatch({
type: actionConstants.SET_RPC_TARGET,
value: newRpc,
})
})
})
}
}
export 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) => {
if (err) {
log.error(err)
return dispatch(displayWarning('Had a problem changing networks!'))
}
dispatch(setSelectedToken())
})
}
}
export function delRpcTarget (oldRpc) {
return (dispatch) => {
log.debug(`background.delRpcTarget: ${oldRpc}`)
return new Promise((resolve, reject) => {
background.delCustomRpc(oldRpc, (err) => {
if (err) {
log.error(err)
dispatch(displayWarning('Had a problem removing network!'))
return reject(err)
}
dispatch(setSelectedToken())
resolve()
})
})
}
}
// Calls the addressBookController to add a new address.
export function addToAddressBook (recipient, nickname = '', memo = '') {
log.debug(`background.addToAddressBook`)
return (dispatch, getState) => {
const chainId = getState().metamask.network
background.setAddressBook(checksumAddress(recipient), nickname, chainId, memo, (err, set) => {
if (err) {
log.error(err)
dispatch(displayWarning('Address book failed to update'))
throw err
}
if (!set) {
return dispatch(displayWarning('Address book failed to update'))
}
})
}
}
/**
* @description Calls the addressBookController to remove an existing address.
* @param {string} addressToRemove - Address of the entry to remove from the address book
*/
export function removeFromAddressBook (chainId, addressToRemove) {
log.debug(`background.removeFromAddressBook`)
return () => {
background.removeFromAddressBook(chainId, checksumAddress(addressToRemove))
}
}
export function useEtherscanProvider () {
log.debug(`background.useEtherscanProvider`)
background.useEtherscanProvider()
return {
type: actionConstants.USE_ETHERSCAN_PROVIDER,
}
}
export function showNetworkDropdown () {
return {
type: actionConstants.NETWORK_DROPDOWN_OPEN,
}
}
export function hideNetworkDropdown () {
return {
type: actionConstants.NETWORK_DROPDOWN_CLOSE,
}
}
export function showModal (payload) {
return {
type: actionConstants.MODAL_OPEN,
payload,
}
}
export function hideModal (payload) {
return {
type: actionConstants.MODAL_CLOSE,
payload,
}
}
export function closeCurrentNotificationWindow () {
return (dispatch, getState) => {
if (global.METAMASK_UI_TYPE === ENVIRONMENT_TYPE_NOTIFICATION &&
!hasUnconfirmedTransactions(getState())) {
global.platform.closeCurrentWindow()
dispatch(closeNotifacationWindow())
}
}
}
export function closeNotifacationWindow () {
return {
type: actionConstants.CLOSE_NOTIFICATION_WINDOW,
}
}
export function showSidebar ({ transitionName, type, props }) {
return {
type: actionConstants.SIDEBAR_OPEN,
value: {
transitionName,
type,
props,
},
}
}
export function hideSidebar () {
return {
type: actionConstants.SIDEBAR_CLOSE,
}
}
export function showAlert (msg) {
return {
type: actionConstants.ALERT_OPEN,
value: msg,
}
}
export function hideAlert () {
return {
type: actionConstants.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)
*/
export function qrCodeDetected (qrCodeData) {
return {
type: actionConstants.QR_CODE_DETECTED,
value: qrCodeData,
}
}
export function showLoadingIndication (message) {
return {
type: actionConstants.SHOW_LOADING,
value: message,
}
}
export function setHardwareWalletDefaultHdPath ({ device, path }) {
return {
type: actionConstants.SET_HARDWARE_WALLET_DEFAULT_HD_PATH,
value: { device, path },
}
}
export function hideLoadingIndication () {
return {
type: actionConstants.HIDE_LOADING,
}
}
export function showSubLoadingIndication () {
return {
type: actionConstants.SHOW_SUB_LOADING_INDICATION,
}
}
export function hideSubLoadingIndication () {
return {
type: actionConstants.HIDE_SUB_LOADING_INDICATION,
}
}
export function displayWarning (text) {
return {
type: actionConstants.DISPLAY_WARNING,
value: text,
}
}
export function hideWarning () {
return {
type: actionConstants.HIDE_WARNING,
}
}
export function exportAccount (password, address) {
return function (dispatch) {
dispatch(showLoadingIndication())
log.debug(`background.submitPassword`)
return new Promise((resolve, reject) => {
background.submitPassword(password, function (err) {
if (err) {
log.error('Error in submiting password.')
dispatch(hideLoadingIndication())
dispatch(displayWarning('Incorrect Password.'))
return reject(err)
}
log.debug(`background.exportAccount`)
return background.exportAccount(address, function (err, result) {
dispatch(hideLoadingIndication())
if (err) {
log.error(err)
dispatch(displayWarning('Had a problem exporting the account.'))
return reject(err)
}
dispatch(showPrivateKey(result))
return resolve(result)
})
})
})
}
}
export function showPrivateKey (key) {
return {
type: actionConstants.SHOW_PRIVATE_KEY,
value: key,
}
}
export function setAccountLabel (account, label) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setAccountLabel`)
return new Promise((resolve, reject) => {
background.setAccountLabel(account, label, (err) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch({
type: actionConstants.SET_ACCOUNT_LABEL,
value: { account, label },
})
resolve(account)
})
})
}
}
export function showSendTokenPage () {
return {
type: actionConstants.SHOW_SEND_TOKEN_PAGE,
}
}
export function buyEth (opts) {
return (dispatch) => {
const url = getBuyEthUrl(opts)
global.platform.openWindow({ url })
dispatch({
type: actionConstants.BUY_ETH,
})
}
}
export function pairUpdate (coin) {
return (dispatch) => {
dispatch(showSubLoadingIndication())
dispatch(hideWarning())
shapeShiftRequest('marketinfo', { pair: `${coin.toLowerCase()}_eth` }, (mktResponse) => {
dispatch(hideSubLoadingIndication())
if (mktResponse.error) {
return dispatch(displayWarning(mktResponse.error))
}
dispatch({
type: actionConstants.PAIR_UPDATE,
value: {
marketinfo: mktResponse,
},
})
})
}
}
export function showQrView (data, message) {
return {
type: actionConstants.SHOW_QR_VIEW,
value: {
message: message,
data: data,
},
}
}
export function reshowQrCode (data, coin) {
return (dispatch) => {
dispatch(showLoadingIndication())
shapeShiftRequest('marketinfo', { pair: `${coin.toLowerCase()}_eth` }, (mktResponse) => {
if (mktResponse.error) {
return dispatch(displayWarning(mktResponse.error))
}
const message = [
`Deposit your ${coin} to the address below:`,
`Deposit Limit: ${mktResponse.limit}`,
`Deposit Minimum:${mktResponse.minimum}`,
]
dispatch(hideLoadingIndication())
return dispatch(showQrView(data, message))
})
}
}
export function shapeShiftRequest (query, options = {}, cb) {
let queryResponse, method
options.method ? method = options.method : method = 'GET'
const requestListner = function () {
try {
queryResponse = JSON.parse(this.responseText)
if (cb) {
cb(queryResponse)
}
return queryResponse
} catch (e) {
if (cb) {
cb({ error: e })
}
return e
}
}
const shapShiftReq = new XMLHttpRequest()
shapShiftReq.addEventListener('load', requestListner)
shapShiftReq.open(method, `https://shapeshift.io/${query}/${options.pair ? options.pair : ''}`, true)
if (options.method === 'POST') {
const jsonObj = JSON.stringify(options.data)
shapShiftReq.setRequestHeader('Content-Type', 'application/json')
return shapShiftReq.send(jsonObj)
} else {
return shapShiftReq.send()
}
}
export function setFeatureFlag (feature, activated, notificationType) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.setFeatureFlag(feature, activated, (err, updatedFeatureFlags) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(updateFeatureFlags(updatedFeatureFlags))
notificationType && dispatch(showModal({ name: notificationType }))
resolve(updatedFeatureFlags)
})
})
}
}
export function updateFeatureFlags (updatedFeatureFlags) {
return {
type: actionConstants.UPDATE_FEATURE_FLAGS,
value: updatedFeatureFlags,
}
}
export function setPreference (preference, value) {
return (dispatch) => {
dispatch(showLoadingIndication())
return new Promise((resolve, reject) => {
background.setPreference(preference, value, (err, updatedPreferences) => {
dispatch(hideLoadingIndication())
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(updatePreferences(updatedPreferences))
resolve(updatedPreferences)
})
})
}
}
export function updatePreferences (value) {
return {
type: actionConstants.UPDATE_PREFERENCES,
value,
}
}
export function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) {
return setPreference('useNativeCurrencyAsPrimaryCurrency', value)
}
export function setShowFiatConversionOnTestnetsPreference (value) {
return setPreference('showFiatInTestnets', value)
}
export function setAutoLockTimeLimit (value) {
return setPreference('autoLockTimeLimit', value)
}
export function setCompletedOnboarding () {
return async (dispatch) => {
dispatch(showLoadingIndication())
try {
await pify(background.completeOnboarding).call(background)
} catch (err) {
dispatch(displayWarning(err.message))
throw err
}
dispatch(completeOnboarding())
dispatch(hideLoadingIndication())
}
}
export function completeOnboarding () {
return {
type: actionConstants.COMPLETE_ONBOARDING,
}
}
export function setNetworkNonce (networkNonce) {
return {
type: actionConstants.SET_NETWORK_NONCE,
value: networkNonce,
}
}
export function updateNetworkNonce (address) {
return (dispatch) => {
return new Promise((resolve, reject) => {
global.ethQuery.getTransactionCount(address, (err, data) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(setNetworkNonce(data))
resolve(data)
})
})
}
}
export function setMouseUserState (isMouseUser) {
return {
type: actionConstants.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.
export function callBackgroundThenUpdateNoSpinner (method, ...args) {
return (dispatch) => {
method.call(background, ...args, (err) => {
if (err) {
return dispatch(displayWarning(err.message))
}
forceUpdateMetamaskState(dispatch)
})
}
}
export function forceUpdateMetamaskState (dispatch) {
log.debug(`background.getState`)
return new Promise((resolve, reject) => {
background.getState((err, newState) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(updateMetamaskState(newState))
resolve(newState)
})
})
}
export function toggleAccountMenu () {
return {
type: actionConstants.TOGGLE_ACCOUNT_MENU,
}
}
export 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(displayWarning(err.message))
return reject(err)
}
dispatch({
type: actionConstants.SET_PARTICIPATE_IN_METAMETRICS,
value: val,
})
resolve([val, metaMetricsId])
})
})
}
}
export function setMetaMetricsSendCount (val) {
return (dispatch) => {
log.debug(`background.setMetaMetricsSendCount`)
return new Promise((resolve, reject) => {
background.setMetaMetricsSendCount(val, (err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch({
type: actionConstants.SET_METAMETRICS_SEND_COUNT,
value: val,
})
resolve(val)
})
})
}
}
export function setUseBlockie (val) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setUseBlockie`)
background.setUseBlockie(val, (err) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
}
})
dispatch({
type: actionConstants.SET_USE_BLOCKIE,
value: val,
})
}
}
export function setUseNonceField (val) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setUseNonceField`)
background.setUseNonceField(val, (err) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
}
})
dispatch({
type: actionConstants.SET_USE_NONCEFIELD,
value: val,
})
}
}
export function setIpfsGateway (val) {
return (dispatch) => {
dispatch(showLoadingIndication())
log.debug(`background.setIpfsGateway`)
background.setIpfsGateway(val, (err) => {
dispatch(hideLoadingIndication())
if (err) {
return dispatch(displayWarning(err.message))
} else {
dispatch({
type: actionConstants.SET_IPFS_GATEWAY,
value: val,
})
}
})
}
}
export function updateCurrentLocale (key) {
return (dispatch) => {
dispatch(showLoadingIndication())
return fetchLocale(key)
.then((localeMessages) => {
log.debug(`background.setCurrentLocale`)
background.setCurrentLocale(key, (err, textDirection) => {
if (err) {
dispatch(hideLoadingIndication())
return dispatch(displayWarning(err.message))
}
switchDirection(textDirection)
dispatch(setCurrentLocale(key, localeMessages))
dispatch(hideLoadingIndication())
})
})
}
}
export function setCurrentLocale (locale, messages) {
return {
type: actionConstants.SET_CURRENT_LOCALE,
value: {
locale,
messages,
},
}
}
export 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: actionConstants.SET_PENDING_TOKENS,
payload: tokens,
}
}
// Permissions
/**
* Approves the permissions request.
* @param {Object} request - The permissions request to approve
* @param {string[]} accounts - The accounts to expose, if any.
*/
export function approvePermissionsRequest (request, accounts) {
return () => {
background.approvePermissionsRequest(request, accounts)
}
}
/**
* Rejects the permissions request with the given ID.
* @param {string} requestId - The id of the request to be rejected
*/
export function rejectPermissionsRequest (requestId) {
return () => {
background.rejectPermissionsRequest(requestId)
}
}
/**
* Exposes the given account(s) to the given origin.
* Call ONLY as a result of direct user action.
*/
export function legacyExposeAccounts (origin, accounts) {
return () => {
return background.legacyExposeAccounts(origin, accounts)
}
}
/**
* Clears the given permissions for the given origin.
*/
export function removePermissionsFor (domains) {
return () => {
background.removePermissionsFor(domains)
background.removeLastSelectedAddressesFor(Object.keys(domains))
}
}
/**
* Clears all permissions for all domains.
*/
export function clearPermissions () {
return () => {
background.clearPermissions()
background.clearLastSelectedAddressHistory()
}
}
export function setFirstTimeFlowType (type) {
return (dispatch) => {
log.debug(`background.setFirstTimeFlowType`)
background.setFirstTimeFlowType(type, (err) => {
if (err) {
return dispatch(displayWarning(err.message))
}
})
dispatch({
type: actionConstants.SET_FIRST_TIME_FLOW_TYPE,
value: type,
})
}
}
export function setSelectedSettingsRpcUrl (newRpcUrl) {
return {
type: actionConstants.SET_SELECTED_SETTINGS_RPC_URL,
value: newRpcUrl,
}
}
export function setNetworksTabAddMode (isInAddMode) {
return {
type: actionConstants.SET_NETWORKS_TAB_ADD_MODE,
value: isInAddMode,
}
}
export function setLastActiveTime () {
return (dispatch) => {
background.setLastActiveTime((err) => {
if (err) {
return dispatch(displayWarning(err.message))
}
})
}
}
export function setMkrMigrationReminderTimestamp (timestamp) {
return (dispatch) => {
background.setMkrMigrationReminderTimestamp(timestamp, (err) => {
if (err) {
return dispatch(displayWarning(err.message))
}
})
}
}
export function loadingMethoDataStarted () {
return {
type: actionConstants.LOADING_METHOD_DATA_STARTED,
}
}
export function loadingMethoDataFinished () {
return {
type: actionConstants.LOADING_METHOD_DATA_FINISHED,
}
}
export function getContractMethodData (data = '') {
return (dispatch, getState) => {
const prefixedData = ethUtil.addHexPrefix(data)
const fourBytePrefix = prefixedData.slice(0, 10)
const { knownMethodData } = getState().metamask
if (knownMethodData && knownMethodData[fourBytePrefix]) {
return Promise.resolve(knownMethodData[fourBytePrefix])
}
dispatch(loadingMethoDataStarted())
log.debug(`loadingMethodData`)
return getMethodDataAsync(fourBytePrefix)
.then(({ name, params }) => {
dispatch(loadingMethoDataFinished())
background.addKnownMethodData(fourBytePrefix, { name, params })
return { name, params }
})
}
}
export function loadingTokenParamsStarted () {
return {
type: actionConstants.LOADING_TOKEN_PARAMS_STARTED,
}
}
export function loadingTokenParamsFinished () {
return {
type: actionConstants.LOADING_TOKEN_PARAMS_FINISHED,
}
}
export function getTokenParams (tokenAddress) {
return (dispatch, getState) => {
const existingTokens = getState().metamask.tokens
const existingToken = existingTokens.find(({ address }) => tokenAddress === address)
if (existingToken) {
return Promise.resolve({
symbol: existingToken.symbol,
decimals: existingToken.decimals,
})
}
dispatch(loadingTokenParamsStarted())
log.debug(`loadingTokenParams`)
return fetchSymbolAndDecimals(tokenAddress, existingTokens)
.then(({ symbol, decimals }) => {
dispatch(addToken(tokenAddress, symbol, decimals))
dispatch(loadingTokenParamsFinished())
})
}
}
export function setSeedPhraseBackedUp (seedPhraseBackupState) {
return (dispatch) => {
log.debug(`background.setSeedPhraseBackedUp`)
return new Promise((resolve, reject) => {
background.setSeedPhraseBackedUp(seedPhraseBackupState, (err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
return forceUpdateMetamaskState(dispatch)
.then(resolve)
.catch(reject)
})
})
}
}
export function hideSeedPhraseBackupAfterOnboarding () {
return {
type: actionConstants.HIDE_SEED_PHRASE_BACKUP_AFTER_ONBOARDING,
}
}
export function initializeThreeBox () {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.initializeThreeBox((err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
}
}
export function setShowRestorePromptToFalse () {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.setShowRestorePromptToFalse((err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
}
}
export function turnThreeBoxSyncingOn () {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.turnThreeBoxSyncingOn((err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
}
}
export function restoreFromThreeBox (accountAddress) {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.restoreFromThreeBox(accountAddress, (err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
}
}
export function getThreeBoxLastUpdated () {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.getThreeBoxLastUpdated((err, lastUpdated) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve(lastUpdated)
})
})
}
}
export function setThreeBoxSyncingPermission (threeBoxSyncingAllowed) {
return (dispatch) => {
return new Promise((resolve, reject) => {
background.setThreeBoxSyncingPermission(threeBoxSyncingAllowed, (err) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
resolve()
})
})
}
}
export function turnThreeBoxSyncingOnAndInitialize () {
return async (dispatch) => {
await dispatch(setThreeBoxSyncingPermission(true))
await dispatch(turnThreeBoxSyncingOn())
await dispatch(initializeThreeBox(true))
}
}
export function setNextNonce (nextNonce) {
return {
type: actionConstants.SET_NEXT_NONCE,
value: nextNonce,
}
}
export function getNextNonce () {
return (dispatch, getState) => {
const address = getState().metamask.selectedAddress
return new Promise((resolve, reject) => {
background.getNextNonce(address, (err, nextNonce) => {
if (err) {
dispatch(displayWarning(err.message))
return reject(err)
}
dispatch(setNextNonce(nextNonce))
resolve(nextNonce)
})
})
}
}
export function setRequestAccountTabIds (requestAccountTabIds) {
return {
type: actionConstants.SET_REQUEST_ACCOUNT_TABS,
value: requestAccountTabIds,
}
}
export function getRequestAccountTabIds () {
return async (dispatch) => {
const requestAccountTabIds = await pify(background.getRequestAccountTabIds).call(background)
dispatch(setRequestAccountTabIds(requestAccountTabIds))
}
}
export function setOpenMetamaskTabsIDs (openMetaMaskTabIDs) {
return {
type: actionConstants.SET_OPEN_METAMASK_TAB_IDS,
value: openMetaMaskTabIDs,
}
}
export function getOpenMetamaskTabsIds () {
return async (dispatch) => {
const openMetaMaskTabIDs = await pify(background.getOpenMetamaskTabsIds).call(background)
dispatch(setOpenMetamaskTabsIDs(openMetaMaskTabIDs))
}
}
export function setCurrentWindowTab (currentWindowTab) {
return {
type: actionConstants.SET_CURRENT_WINDOW_TAB,
value: currentWindowTab,
}
}
export function getCurrentWindowTab () {
return async (dispatch) => {
const currentWindowTab = await global.platform.currentTab()
dispatch(setCurrentWindowTab(currentWindowTab))
}
}