diff --git a/ui/components/app/asset-list-item/asset-list-item.js b/ui/components/app/asset-list-item/asset-list-item.js index 7096664d4..c147297b5 100644 --- a/ui/components/app/asset-list-item/asset-list-item.js +++ b/ui/components/app/asset-list-item/asset-list-item.js @@ -10,7 +10,7 @@ import InfoIcon from '../../ui/icon/info-icon.component'; import Button from '../../ui/button'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { useMetricEvent } from '../../../hooks/useMetricEvent'; -import { updateSendToken } from '../../../store/actions'; +import { updateSendToken } from '../../../ducks/send/send.duck'; import { SEND_ROUTE } from '../../../helpers/constants/routes'; import { SEVERITIES } from '../../../helpers/constants/design-system'; diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js index bcbbaa9d1..6b36af651 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js @@ -1,6 +1,6 @@ import sinon from 'sinon'; -import { hideModal, setGasLimit, setGasPrice } from '../../../../store/actions'; +import { hideModal } from '../../../../store/actions'; import { setCustomGasPrice, @@ -8,7 +8,11 @@ import { resetCustomData, } from '../../../../ducks/gas/gas.duck'; -import { hideGasButtonGroup } from '../../../../ducks/send/send.duck'; +import { + hideGasButtonGroup, + setGasLimit, + setGasPrice, +} from '../../../../ducks/send/send.duck'; let mapDispatchToProps; let mergeProps; @@ -29,7 +33,7 @@ jest.mock('../../../../selectors', () => ({ getDefaultActiveButtonIndex: (a, b) => a + b, getCurrentEthBalance: (state) => state.metamask.balance || '0x0', getSendToken: () => null, - getTokenBalance: (state) => state.metamask.send.tokenBalance || '0x0', + getTokenBalance: (state) => state.send.tokenBalance || '0x0', getCustomGasPrice: (state) => state.gas.customData.price || '0x0', getCustomGasLimit: (state) => state.gas.customData.limit || '0x0', getCurrentCurrency: jest.fn().mockReturnValue('usd'), @@ -44,8 +48,6 @@ jest.mock('../../../../selectors', () => ({ jest.mock('../../../../store/actions', () => ({ hideModal: jest.fn(), - setGasLimit: jest.fn(), - setGasPrice: jest.fn(), updateTransaction: jest.fn(), })); @@ -57,6 +59,8 @@ jest.mock('../../../../ducks/gas/gas.duck', () => ({ jest.mock('../../../../ducks/send/send.duck', () => ({ hideGasButtonGroup: jest.fn(), + setGasLimit: jest.fn(), + setGasPrice: jest.fn(), })); require('./gas-modal-page-container.container'); diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 0c1597fe3..c02eca852 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -2,13 +2,9 @@ import { connect } from 'react-redux'; import { addHexPrefix } from '../../../../../app/scripts/lib/util'; import { hideModal, - setGasLimit, - setGasPrice, createRetryTransaction, createSpeedUpTransaction, hideSidebar, - updateSendAmount, - setGasTotal, updateTransaction, } from '../../../../store/actions'; import { @@ -19,6 +15,10 @@ import { } from '../../../../ducks/gas/gas.duck'; import { hideGasButtonGroup, + setGasLimit, + setGasPrice, + setGasTotal, + updateSendAmount, updateSendErrors, } from '../../../../ducks/send/send.duck'; import { @@ -60,7 +60,10 @@ import { GAS_LIMITS } from '../../../../../shared/constants/gas'; import GasModalPageContainer from './gas-modal-page-container.component'; const mapStateToProps = (state, ownProps) => { - const { currentNetworkTxList, send } = state.metamask; + const { + metamask: { currentNetworkTxList }, + send, + } = state; const { modalState: { props: modalProps } = {} } = state.appState.modal || {}; const { txData = {} } = modalProps || {}; const { transaction = {}, onSubmit } = ownProps; diff --git a/ui/components/app/wallet-overview/token-overview.js b/ui/components/app/wallet-overview/token-overview.js index 4b71e3dba..9bdb87533 100644 --- a/ui/components/app/wallet-overview/token-overview.js +++ b/ui/components/app/wallet-overview/token-overview.js @@ -17,7 +17,7 @@ import { } from '../../../hooks/useMetricEvent'; import { useTokenTracker } from '../../../hooks/useTokenTracker'; import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount'; -import { updateSendToken } from '../../../store/actions'; +import { updateSendToken } from '../../../ducks/send/send.duck'; import { setSwapsFromToken } from '../../../ducks/swaps/swaps'; import { getAssetImages, diff --git a/ui/ducks/app/app.js b/ui/ducks/app/app.js index 20bab88f7..b19072d18 100644 --- a/ui/ducks/app/app.js +++ b/ui/ducks/app/app.js @@ -37,7 +37,6 @@ export default function reduceApp(state = {}, action) { warning: null, buyView: {}, isMouseUser: false, - gasIsLoading: false, defaultHdPaths: { trezor: `m/44'/60'/0'/0`, ledger: `m/44'/60'/0'/0/0`, @@ -293,18 +292,6 @@ export default function reduceApp(state = {}, action) { isMouseUser: action.value, }; - case actionConstants.GAS_LOADING_STARTED: - return { - ...appState, - gasIsLoading: true, - }; - - case actionConstants.GAS_LOADING_FINISHED: - return { - ...appState, - gasIsLoading: false, - }; - case actionConstants.SET_SELECTED_SETTINGS_RPC_URL: return { ...appState, @@ -377,3 +364,8 @@ export function hideWhatsNewPopup() { type: actionConstants.HIDE_WHATS_NEW_POPUP, }; } + +// Selectors +export function getQrCodeData(state) { + return state.appState.qrCodeData; +} diff --git a/ui/ducks/app/app.test.js b/ui/ducks/app/app.test.js index 7a25bfe2b..6f536c903 100644 --- a/ui/ducks/app/app.test.js +++ b/ui/ducks/app/app.test.js @@ -352,22 +352,4 @@ describe('App State', () => { expect(state.isMouseUser).toStrictEqual(true); }); - - it('sets gas loading', () => { - const state = reduceApp(metamaskState, { - type: actions.GAS_LOADING_STARTED, - }); - - expect(state.gasIsLoading).toStrictEqual(true); - }); - - it('unsets gas loading', () => { - const gasLoadingState = { gasIsLoading: true }; - const oldState = { ...metamaskState, ...gasLoadingState }; - const state = reduceApp(oldState, { - type: actions.GAS_LOADING_FINISHED, - }); - - expect(state.gasIsLoading).toStrictEqual(false); - }); }); diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 13ab8b709..21a0476ea 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -19,22 +19,6 @@ export default function reduceMetamask(state = {}, action) { tokens: [], pendingTokens: {}, customNonceValue: '', - send: { - gasLimit: null, - gasPrice: null, - gasTotal: null, - tokenBalance: '0x0', - from: '', - to: '', - amount: '0', - memo: '', - errors: {}, - maxModeOn: false, - editingTransactionId: null, - toNickname: '', - ensResolution: null, - ensResolutionError: '', - }, useBlockie: false, featureFlags: {}, welcomeScreenSeen: false, @@ -106,28 +90,11 @@ export default function reduceMetamask(state = {}, action) { tokens: action.newTokens, }; - // metamask.send - case actionConstants.UPDATE_GAS_LIMIT: - return { - ...metamaskState, - send: { - ...metamaskState.send, - gasLimit: action.value, - }, - }; case actionConstants.UPDATE_CUSTOM_NONCE: return { ...metamaskState, customNonceValue: action.value, }; - case actionConstants.UPDATE_GAS_PRICE: - return { - ...metamaskState, - send: { - ...metamaskState.send, - gasPrice: action.value, - }, - }; case actionConstants.TOGGLE_ACCOUNT_MENU: return { @@ -135,139 +102,6 @@ export default function reduceMetamask(state = {}, action) { isAccountMenuOpen: !metamaskState.isAccountMenuOpen, }; - case actionConstants.UPDATE_GAS_TOTAL: - return { - ...metamaskState, - send: { - ...metamaskState.send, - gasTotal: action.value, - }, - }; - - case actionConstants.UPDATE_SEND_TOKEN_BALANCE: - return { - ...metamaskState, - send: { - ...metamaskState.send, - tokenBalance: action.value, - }, - }; - - case actionConstants.UPDATE_SEND_HEX_DATA: - return { - ...metamaskState, - send: { - ...metamaskState.send, - data: action.value, - }, - }; - - case actionConstants.UPDATE_SEND_TO: - return { - ...metamaskState, - send: { - ...metamaskState.send, - to: action.value.to, - toNickname: action.value.nickname, - }, - }; - - case actionConstants.UPDATE_SEND_AMOUNT: - return { - ...metamaskState, - send: { - ...metamaskState.send, - amount: action.value, - }, - }; - - case actionConstants.UPDATE_MAX_MODE: - return { - ...metamaskState, - send: { - ...metamaskState.send, - maxModeOn: action.value, - }, - }; - - case actionConstants.UPDATE_SEND: - return Object.assign(metamaskState, { - send: { - ...metamaskState.send, - ...action.value, - }, - }); - - case actionConstants.UPDATE_SEND_TOKEN: { - const newSend = { - ...metamaskState.send, - token: action.value, - }; - // erase token-related state when switching back to native currency - if (newSend.editingTransactionId && !newSend.token) { - const unapprovedTx = - newSend?.unapprovedTxs?.[newSend.editingTransactionId] || {}; - const txParams = unapprovedTx.txParams || {}; - Object.assign(newSend, { - tokenBalance: null, - balance: '0', - from: unapprovedTx.from || '', - unapprovedTxs: { - ...newSend.unapprovedTxs, - [newSend.editingTransactionId]: { - ...unapprovedTx, - txParams: { - ...txParams, - data: '', - }, - }, - }, - }); - } - return Object.assign(metamaskState, { - send: newSend, - }); - } - - case actionConstants.UPDATE_SEND_ENS_RESOLUTION: - return { - ...metamaskState, - send: { - ...metamaskState.send, - ensResolution: action.payload, - ensResolutionError: '', - }, - }; - - case actionConstants.UPDATE_SEND_ENS_RESOLUTION_ERROR: - return { - ...metamaskState, - send: { - ...metamaskState.send, - ensResolution: null, - ensResolutionError: action.payload, - }, - }; - - case actionConstants.CLEAR_SEND: - return { - ...metamaskState, - send: { - gasLimit: null, - gasPrice: null, - gasTotal: null, - tokenBalance: null, - from: '', - to: '', - amount: '0x0', - memo: '', - errors: {}, - maxModeOn: false, - editingTransactionId: null, - toNickname: '', - }, - }; - case actionConstants.UPDATE_TRANSACTION_PARAMS: { const { id: txId, value } = action; let { currentNetworkTxList } = metamaskState; diff --git a/ui/ducks/metamask/metamask.test.js b/ui/ducks/metamask/metamask.test.js index 8d158742b..702c7c319 100644 --- a/ui/ducks/metamask/metamask.test.js +++ b/ui/ducks/metamask/metamask.test.js @@ -150,13 +150,11 @@ describe('MetaMask Reducers', () => { {}, { type: actionConstants.SHOW_ACCOUNT_DETAIL, - value: 'test address', }, ); expect(state.isUnlocked).toStrictEqual(true); expect(state.isInitialized).toStrictEqual(true); - expect(state.selectedAddress).toStrictEqual('test address'); }); it('sets account label', () => { @@ -194,30 +192,6 @@ describe('MetaMask Reducers', () => { expect(state.tokens).toStrictEqual(newTokens); }); - it('updates send gas limit', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_GAS_LIMIT, - value: '0xGasLimit', - }, - ); - - expect(state.send.gasLimit).toStrictEqual('0xGasLimit'); - }); - - it('updates send gas price', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_GAS_PRICE, - value: '0xGasPrice', - }, - ); - - expect(state.send.gasPrice).toStrictEqual('0xGasPrice'); - }); - it('toggles account menu', () => { const state = reduceMetamask( {}, @@ -229,153 +203,6 @@ describe('MetaMask Reducers', () => { expect(state.isAccountMenuOpen).toStrictEqual(true); }); - it('updates gas total', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_GAS_TOTAL, - value: '0xGasTotal', - }, - ); - - expect(state.send.gasTotal).toStrictEqual('0xGasTotal'); - }); - - it('updates send token balance', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_TOKEN_BALANCE, - value: '0xTokenBalance', - }, - ); - - expect(state.send.tokenBalance).toStrictEqual('0xTokenBalance'); - }); - - it('updates data', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_HEX_DATA, - value: '0xData', - }, - ); - - expect(state.send.data).toStrictEqual('0xData'); - }); - - it('updates send to', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_TO, - value: { - to: '0xAddress', - nickname: 'nickname', - }, - }, - ); - - expect(state.send.to).toStrictEqual('0xAddress'); - expect(state.send.toNickname).toStrictEqual('nickname'); - }); - - it('update send amount', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_AMOUNT, - value: '0xAmount', - }, - ); - - expect(state.send.amount).toStrictEqual('0xAmount'); - }); - - it('updates max mode', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_MAX_MODE, - value: true, - }, - ); - - expect(state.send.maxModeOn).toStrictEqual(true); - }); - - it('update send', () => { - const value = { - gasLimit: '0xGasLimit', - gasPrice: '0xGasPrice', - gasTotal: '0xGasTotal', - tokenBalance: '0xBalance', - from: '0xAddress', - to: '0xAddress', - toNickname: '', - maxModeOn: false, - amount: '0xAmount', - memo: '0xMemo', - errors: {}, - editingTransactionId: 22, - ensResolution: null, - ensResolutionError: '', - }; - - const sendState = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND, - value, - }, - ); - - expect(sendState.send).toStrictEqual(value); - }); - - it('clears send', () => { - const initStateSend = { - send: { - gasLimit: null, - gasPrice: null, - gasTotal: null, - tokenBalance: null, - from: '', - to: '', - amount: '0x0', - memo: '', - errors: {}, - maxModeOn: false, - editingTransactionId: null, - toNickname: '', - }, - }; - - const sendState = { - send: { - gasLimit: '0xGasLimit', - gasPrice: '0xGasPrice', - gasTotal: '0xGasTotal', - tokenBalance: '0xBalance', - from: '0xAddress', - to: '0xAddress', - toNickname: '', - maxModeOn: false, - amount: '0xAmount', - memo: '0xMemo', - errors: {}, - editingTransactionId: 22, - }, - }; - - const state = reduceMetamask(sendState, { - type: actionConstants.CLEAR_SEND, - }); - - expect(state.send).toStrictEqual(initStateSend.send); - }); - it('updates value of tx by id', () => { const oldState = { currentNetworkTxList: [ @@ -480,32 +307,6 @@ describe('MetaMask Reducers', () => { expect(state.pendingTokens).toStrictEqual({}); }); - it('update ensResolution', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_ENS_RESOLUTION, - payload: '0x1337', - }, - ); - - expect(state.send.ensResolution).toStrictEqual('0x1337'); - expect(state.send.ensResolutionError).toStrictEqual(''); - }); - - it('update ensResolutionError', () => { - const state = reduceMetamask( - {}, - { - type: actionConstants.UPDATE_SEND_ENS_RESOLUTION_ERROR, - payload: 'ens name not found', - }, - ); - - expect(state.send.ensResolutionError).toStrictEqual('ens name not found'); - expect(state.send.ensResolution).toBeNull(); - }); - describe('metamask state selectors', () => { describe('getBlockGasLimit', () => { it('should return the current block gas limit', () => { diff --git a/ui/ducks/send/send-duck.test.js b/ui/ducks/send/send-duck.test.js index 12ef5bbb3..7c05e8689 100644 --- a/ui/ducks/send/send-duck.test.js +++ b/ui/ducks/send/send-duck.test.js @@ -12,8 +12,22 @@ describe('Send Duck', () => { }; const initState = { toDropdownOpen: false, - errors: {}, gasButtonGroupShown: true, + errors: {}, + gasLimit: null, + gasPrice: null, + gasTotal: null, + tokenBalance: '0x0', + from: '', + to: '', + amount: '0', + memo: '', + maxModeOn: false, + editingTransactionId: null, + toNickname: '', + ensResolution: null, + ensResolutionError: '', + gasIsLoading: false, }; const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN'; const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN'; diff --git a/ui/ducks/send/send.duck.js b/ui/ducks/send/send.duck.js index b8d974457..82d9b9d82 100644 --- a/ui/ducks/send/send.duck.js +++ b/ui/ducks/send/send.duck.js @@ -1,3 +1,11 @@ +import log from 'loglevel'; +import { estimateGas } from '../../store/actions'; +import { setCustomGasLimit } from '../gas/gas.duck'; +import { + estimateGasForSend, + calcTokenBalance, +} from '../../pages/send/send.utils'; + // Actions const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN'; const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN'; @@ -5,11 +13,40 @@ const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS'; const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE'; const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP'; const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP'; +const UPDATE_GAS_LIMIT = 'UPDATE_GAS_LIMIT'; +const UPDATE_GAS_PRICE = 'UPDATE_GAS_PRICE'; +const UPDATE_GAS_TOTAL = 'UPDATE_GAS_TOTAL'; +const UPDATE_SEND_HEX_DATA = 'UPDATE_SEND_HEX_DATA'; +const UPDATE_SEND_TOKEN_BALANCE = 'UPDATE_SEND_TOKEN_BALANCE'; +const UPDATE_SEND_TO = 'UPDATE_SEND_TO'; +const UPDATE_SEND_AMOUNT = 'UPDATE_SEND_AMOUNT'; +const UPDATE_MAX_MODE = 'UPDATE_MAX_MODE'; +const UPDATE_SEND = 'UPDATE_SEND'; +const UPDATE_SEND_TOKEN = 'UPDATE_SEND_TOKEN'; +const CLEAR_SEND = 'CLEAR_SEND'; +const GAS_LOADING_STARTED = 'GAS_LOADING_STARTED'; +const GAS_LOADING_FINISHED = 'GAS_LOADING_FINISHED'; +const UPDATE_SEND_ENS_RESOLUTION = 'UPDATE_SEND_ENS_RESOLUTION'; +const UPDATE_SEND_ENS_RESOLUTION_ERROR = 'UPDATE_SEND_ENS_RESOLUTION_ERROR'; const initState = { toDropdownOpen: false, gasButtonGroupShown: true, errors: {}, + gasLimit: null, + gasPrice: null, + gasTotal: null, + tokenBalance: '0x0', + from: '', + to: '', + amount: '0', + memo: '', + maxModeOn: false, + editingTransactionId: null, + toNickname: '', + ensResolution: null, + ensResolutionError: '', + gasIsLoading: false, }; // Reducer @@ -43,8 +80,118 @@ export default function reducer(state = initState, action) { ...state, gasButtonGroupShown: false, }; + case UPDATE_GAS_LIMIT: + return { + ...state, + gasLimit: action.value, + }; + case UPDATE_GAS_PRICE: + return { + ...state, + gasPrice: action.value, + }; case RESET_SEND_STATE: return { ...initState }; + case UPDATE_GAS_TOTAL: + return { + ...state, + gasTotal: action.value, + }; + case UPDATE_SEND_TOKEN_BALANCE: + return { + ...state, + tokenBalance: action.value, + }; + case UPDATE_SEND_HEX_DATA: + return { + ...state, + data: action.value, + }; + case UPDATE_SEND_TO: + return { + ...state, + to: action.value.to, + toNickname: action.value.nickname, + }; + case UPDATE_SEND_AMOUNT: + return { + ...state, + amount: action.value, + }; + case UPDATE_MAX_MODE: + return { + ...state, + maxModeOn: action.value, + }; + case UPDATE_SEND: + return Object.assign(state, action.value); + case UPDATE_SEND_TOKEN: { + const newSend = { + ...state, + token: action.value, + }; + // erase token-related state when switching back to native currency + if (newSend.editingTransactionId && !newSend.token) { + const unapprovedTx = + newSend?.unapprovedTxs?.[newSend.editingTransactionId] || {}; + const txParams = unapprovedTx.txParams || {}; + Object.assign(newSend, { + tokenBalance: null, + balance: '0', + from: unapprovedTx.from || '', + unapprovedTxs: { + ...newSend.unapprovedTxs, + [newSend.editingTransactionId]: { + ...unapprovedTx, + txParams: { + ...txParams, + data: '', + }, + }, + }, + }); + } + return Object.assign(state, newSend); + } + case UPDATE_SEND_ENS_RESOLUTION: + return { + ...state, + ensResolution: action.payload, + ensResolutionError: '', + }; + case UPDATE_SEND_ENS_RESOLUTION_ERROR: + return { + ...state, + ensResolution: null, + ensResolutionError: action.payload, + }; + case CLEAR_SEND: + return { + ...state, + gasLimit: null, + gasPrice: null, + gasTotal: null, + tokenBalance: null, + from: '', + to: '', + amount: '0x0', + memo: '', + errors: {}, + maxModeOn: false, + editingTransactionId: null, + toNickname: '', + }; + case GAS_LOADING_STARTED: + return { + ...state, + gasIsLoading: true, + }; + + case GAS_LOADING_FINISHED: + return { + ...state, + gasIsLoading: false, + }; default: return state; } @@ -77,3 +224,159 @@ export function updateSendErrors(errorObject) { export function resetSendState() { return { type: RESET_SEND_STATE }; } + +export function setGasLimit(gasLimit) { + return { + type: UPDATE_GAS_LIMIT, + value: gasLimit, + }; +} + +export function setGasPrice(gasPrice) { + return { + type: UPDATE_GAS_PRICE, + value: gasPrice, + }; +} + +export function setGasTotal(gasTotal) { + return { + type: UPDATE_GAS_TOTAL, + value: gasTotal, + }; +} + +export function updateGasData({ + gasPrice, + blockGasLimit, + selectedAddress, + sendToken, + to, + value, + data, +}) { + return (dispatch) => { + dispatch(gasLoadingStarted()); + return estimateGasForSend({ + estimateGasMethod: estimateGas, + blockGasLimit, + selectedAddress, + sendToken, + 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: GAS_LOADING_STARTED, + }; +} + +export function gasLoadingFinished() { + return { + type: GAS_LOADING_FINISHED, + }; +} + +export function updateSendTokenBalance({ sendToken, tokenContract, address }) { + return (dispatch) => { + const tokenBalancePromise = tokenContract + ? tokenContract.balanceOf(address) + : Promise.resolve(); + return tokenBalancePromise + .then((usersToken) => { + if (usersToken) { + const newTokenBalance = calcTokenBalance({ sendToken, usersToken }); + dispatch(setSendTokenBalance(newTokenBalance)); + } + }) + .catch((err) => { + log.error(err); + updateSendErrors({ tokenBalance: 'tokenBalanceError' }); + }); + }; +} + +export function setSendTokenBalance(tokenBalance) { + return { + type: UPDATE_SEND_TOKEN_BALANCE, + value: tokenBalance, + }; +} + +export function updateSendHexData(value) { + return { + type: UPDATE_SEND_HEX_DATA, + value, + }; +} + +export function updateSendTo(to, nickname = '') { + return { + type: UPDATE_SEND_TO, + value: { to, nickname }, + }; +} + +export function updateSendAmount(amount) { + return { + type: UPDATE_SEND_AMOUNT, + value: amount, + }; +} + +export function setMaxModeTo(bool) { + return { + type: UPDATE_MAX_MODE, + value: bool, + }; +} + +export function updateSend(newSend) { + return { + type: UPDATE_SEND, + value: newSend, + }; +} + +export function updateSendToken(token) { + return { + type: UPDATE_SEND_TOKEN, + value: token, + }; +} + +export function clearSend() { + return { + type: CLEAR_SEND, + }; +} + +export function updateSendEnsResolution(ensResolution) { + return { + type: UPDATE_SEND_ENS_RESOLUTION, + payload: ensResolution, + }; +} + +export function updateSendEnsResolutionError(errorMessage) { + return { + type: UPDATE_SEND_ENS_RESOLUTION_ERROR, + payload: errorMessage, + }; +} diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.container.js b/ui/pages/confirm-send-ether/confirm-send-ether.container.js index 35f1ccd73..475ee5213 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.container.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.container.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; -import { updateSend } from '../../store/actions'; +import { updateSend } from '../../ducks/send/send.duck'; import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'; import ConfirmSendEther from './confirm-send-ether.component'; diff --git a/ui/pages/confirm-send-token/confirm-send-token.container.js b/ui/pages/confirm-send-token/confirm-send-token.container.js index e7514acf9..fd869a9a7 100644 --- a/ui/pages/confirm-send-token/confirm-send-token.container.js +++ b/ui/pages/confirm-send-token/confirm-send-token.container.js @@ -2,13 +2,14 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'; -import { updateSend, showSendTokenPage } from '../../store/actions'; +import { showSendTokenPage } from '../../store/actions'; import { conversionUtil } from '../../helpers/utils/conversion-util'; import { getTokenValueParam, getTokenAddressParam, } from '../../helpers/utils/token-util'; import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors'; +import { updateSend } from '../../ducks/send/send.duck'; import ConfirmSendToken from './confirm-send-token.component'; const mapStateToProps = (state) => { diff --git a/ui/pages/confirm-transaction/conf-tx.js b/ui/pages/confirm-transaction/conf-tx.js index 23f6e2375..4f2028197 100644 --- a/ui/pages/confirm-transaction/conf-tx.js +++ b/ui/pages/confirm-transaction/conf-tx.js @@ -38,7 +38,7 @@ function mapStateToProps(state) { unapprovedMsgCount, unapprovedPersonalMsgCount, unapprovedTypedMessagesCount, - send: state.metamask.send, + send: state.send, currentNetworkTxList: state.metamask.currentNetworkTxList, }; } diff --git a/ui/pages/confirm-transaction/confirm-transaction.container.js b/ui/pages/confirm-transaction/confirm-transaction.container.js index 21acaa7ff..b04ee76fa 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.container.js +++ b/ui/pages/confirm-transaction/confirm-transaction.container.js @@ -19,7 +19,8 @@ import ConfirmTransaction from './confirm-transaction.component'; const mapStateToProps = (state, ownProps) => { const { - metamask: { send, unapprovedTxs }, + metamask: { unapprovedTxs }, + send, } = state; const { match: { params = {} }, diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.container.js b/ui/pages/send/send-content/add-recipient/add-recipient.container.js index 2e3ea94fc..c131ebb7f 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.container.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.container.js @@ -7,7 +7,7 @@ import { getAddressBookEntry, } from '../../../../selectors'; -import { updateSendTo } from '../../../../store/actions'; +import { updateSendTo } from '../../../../ducks/send/send.duck'; import AddRecipient from './add-recipient.component'; export default connect(mapStateToProps, mapDispatchToProps)(AddRecipient); diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js b/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js index 696ca926b..1d8e05bdc 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js @@ -1,6 +1,5 @@ import sinon from 'sinon'; - -import { updateSendTo } from '../../../../store/actions'; +import { updateSendTo } from '../../../../ducks/send/send.duck'; let mapStateToProps; let mapDispatchToProps; @@ -24,7 +23,7 @@ jest.mock('../../../../selectors', () => ({ ], })); -jest.mock('../../../../store/actions', () => ({ +jest.mock('../../../../ducks/send/send.duck.js', () => ({ updateSendTo: jest.fn(), })); diff --git a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js index 593d3e57a..a2fe64b94 100644 --- a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js +++ b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.js @@ -7,8 +7,11 @@ import { getSendMaxModeState, getBasicGasEstimateLoadingStatus, } from '../../../../../selectors'; -import { updateSendAmount, setMaxModeTo } from '../../../../../store/actions'; -import { updateSendErrors } from '../../../../../ducks/send/send.duck'; +import { + updateSendErrors, + updateSendAmount, + setMaxModeTo, +} from '../../../../../ducks/send/send.duck'; import { calcMaxAmount } from './amount-max-button.utils'; import AmountMaxButton from './amount-max-button.component'; diff --git a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.test.js b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.test.js index 90f95e6cb..cb86c88ff 100644 --- a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.test.js +++ b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.container.test.js @@ -1,8 +1,10 @@ import sinon from 'sinon'; -import { setMaxModeTo, updateSendAmount } from '../../../../../store/actions'; - -import { updateSendErrors } from '../../../../../ducks/send/send.duck'; +import { + updateSendErrors, + setMaxModeTo, + updateSendAmount, +} from '../../../../../ducks/send/send.duck'; let mapStateToProps; let mapDispatchToProps; @@ -28,11 +30,9 @@ jest.mock('./amount-max-button.utils.js', () => ({ calcMaxAmount: (mockObj) => mockObj.val + 1, })); -jest.mock('../../../../../store/actions', () => ({ +jest.mock('../../../../../ducks/send/send.duck', () => ({ setMaxModeTo: jest.fn(), updateSendAmount: jest.fn(), -})); -jest.mock('../../../../../ducks/send/send.duck', () => ({ updateSendErrors: jest.fn(), })); diff --git a/ui/pages/send/send-content/send-amount-row/send-amount-row.container.js b/ui/pages/send/send-content/send-amount-row/send-amount-row.container.js index 661864fa9..ea76e87a4 100644 --- a/ui/pages/send/send-content/send-amount-row/send-amount-row.container.js +++ b/ui/pages/send/send-content/send-amount-row/send-amount-row.container.js @@ -10,8 +10,11 @@ import { sendAmountIsInError, } from '../../../../selectors'; import { getAmountErrorObject, getGasFeeErrorObject } from '../../send.utils'; -import { setMaxModeTo, updateSendAmount } from '../../../../store/actions'; -import { updateSendErrors } from '../../../../ducks/send/send.duck'; +import { + updateSendErrors, + setMaxModeTo, + updateSendAmount, +} from '../../../../ducks/send/send.duck'; import { getConversionRate } from '../../../../ducks/metamask/metamask'; import SendAmountRow from './send-amount-row.component'; diff --git a/ui/pages/send/send-content/send-amount-row/send-amount-row.container.test.js b/ui/pages/send/send-content/send-amount-row/send-amount-row.container.test.js index 6d3b06aef..edad05014 100644 --- a/ui/pages/send/send-content/send-amount-row/send-amount-row.container.test.js +++ b/ui/pages/send/send-content/send-amount-row/send-amount-row.container.test.js @@ -1,8 +1,10 @@ import sinon from 'sinon'; -import { setMaxModeTo, updateSendAmount } from '../../../../store/actions'; - -import { updateSendErrors } from '../../../../ducks/send/send.duck'; +import { + updateSendErrors, + setMaxModeTo, + updateSendAmount, +} from '../../../../ducks/send/send.duck'; let mapDispatchToProps; @@ -28,13 +30,10 @@ jest.mock('../../send.utils', () => ({ }), })); -jest.mock('../../../../store/actions', () => ({ - setMaxModeTo: jest.fn(), - updateSendAmount: jest.fn(), -})); - jest.mock('../../../../ducks/send/send.duck', () => ({ updateSendErrors: jest.fn(), + setMaxModeTo: jest.fn(), + updateSendAmount: jest.fn(), })); require('./send-amount-row.container.js'); diff --git a/ui/pages/send/send-content/send-asset-row/send-asset-row.container.js b/ui/pages/send/send-content/send-asset-row/send-asset-row.container.js index 5207b0396..61c659434 100644 --- a/ui/pages/send/send-content/send-asset-row/send-asset-row.container.js +++ b/ui/pages/send/send-content/send-asset-row/send-asset-row.container.js @@ -6,7 +6,7 @@ import { getSendTokenAddress, getAssetImages, } from '../../../../selectors'; -import { updateSendToken } from '../../../../store/actions'; +import { updateSendToken } from '../../../../ducks/send/send.duck'; import SendAssetRow from './send-asset-row.component'; function mapStateToProps(state) { diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js index 8a2aacfe2..84d6886fb 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.js @@ -25,20 +25,18 @@ import { calcMaxAmount } from '../send-amount-row/amount-max-button/amount-max-b import { showGasButtonGroup, updateSendErrors, + setGasPrice, + setGasLimit, + setGasTotal, + updateSendAmount, } from '../../../../ducks/send/send.duck'; import { resetCustomData, setCustomGasPrice, setCustomGasLimit, } from '../../../../ducks/gas/gas.duck'; -import { - showModal, - setGasPrice, - setGasLimit, - setGasTotal, - updateSendAmount, -} from '../../../../store/actions'; import { getConversionRate } from '../../../../ducks/metamask/metamask'; +import { showModal } from '../../../../store/actions'; import SendGasRow from './send-gas-row.component'; export default connect( diff --git a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.test.js b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.test.js index db322d25a..80757f230 100644 --- a/ui/pages/send/send-content/send-gas-row/send-gas-row.container.test.js +++ b/ui/pages/send/send-content/send-gas-row/send-gas-row.container.test.js @@ -1,11 +1,6 @@ import sinon from 'sinon'; -import { - showModal, - setGasPrice, - setGasTotal, - setGasLimit, -} from '../../../../store/actions'; +import { showModal } from '../../../../store/actions'; import { resetCustomData, @@ -13,7 +8,12 @@ import { setCustomGasLimit, } from '../../../../ducks/gas/gas.duck'; -import { showGasButtonGroup } from '../../../../ducks/send/send.duck'; +import { + showGasButtonGroup, + setGasPrice, + setGasTotal, + setGasLimit, +} from '../../../../ducks/send/send.duck'; let mapDispatchToProps; let mergeProps; @@ -39,13 +39,13 @@ jest.mock('../../send.utils.js', () => ({ jest.mock('../../../../store/actions', () => ({ showModal: jest.fn(), - setGasPrice: jest.fn(), - setGasTotal: jest.fn(), - setGasLimit: jest.fn(), })); jest.mock('../../../../ducks/send/send.duck', () => ({ showGasButtonGroup: jest.fn(), + setGasPrice: jest.fn(), + setGasTotal: jest.fn(), + setGasLimit: jest.fn(), })); jest.mock('../../../../ducks/gas/gas.duck', () => ({ diff --git a/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.container.js b/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.container.js index 20a8cad43..f645aff7a 100644 --- a/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.container.js +++ b/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.container.js @@ -1,12 +1,12 @@ import { connect } from 'react-redux'; -import { updateSendHexData } from '../../../../store/actions'; +import { updateSendHexData } from '../../../../ducks/send/send.duck'; import SendHexDataRow from './send-hex-data-row.component'; export default connect(mapStateToProps, mapDispatchToProps)(SendHexDataRow); function mapStateToProps(state) { return { - data: state.metamask.send.data, + data: state.send.data, }; } diff --git a/ui/pages/send/send-footer/send-footer.container.js b/ui/pages/send/send-footer/send-footer.container.js index 4b90217fa..8848255d3 100644 --- a/ui/pages/send/send-footer/send-footer.container.js +++ b/ui/pages/send/send-footer/send-footer.container.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import { addToAddressBook, - clearSend, signTokenTx, signTx, updateTransaction, @@ -30,6 +29,7 @@ import { getSendToAccounts, getUnapprovedTxs, } from '../../../ducks/metamask/metamask'; +import { clearSend } from '../../../ducks/send/send.duck'; import SendFooter from './send-footer.component'; import { addressIsNew, @@ -88,7 +88,7 @@ function mapDispatchToProps(dispatch) { to, }); - sendToken + return sendToken ? dispatch(signTokenTx(sendToken.address, to, amount, txParams)) : dispatch(signTx(txParams)); }, diff --git a/ui/pages/send/send-footer/send-footer.container.test.js b/ui/pages/send/send-footer/send-footer.container.test.js index 7fb79cbae..3cb6e474e 100644 --- a/ui/pages/send/send-footer/send-footer.container.test.js +++ b/ui/pages/send/send-footer/send-footer.container.test.js @@ -1,11 +1,7 @@ import sinon from 'sinon'; +import { clearSend } from '../../../ducks/send/send.duck'; -import { - clearSend, - signTx, - signTokenTx, - addToAddressBook, -} from '../../../store/actions'; +import { signTx, signTokenTx, addToAddressBook } from '../../../store/actions'; import { addressIsNew, constructTxParams, @@ -23,12 +19,15 @@ jest.mock('react-redux', () => ({ jest.mock('../../../store/actions.js', () => ({ addToAddressBook: jest.fn(), - clearSend: jest.fn(), signTokenTx: jest.fn(), signTx: jest.fn(), updateTransaction: jest.fn(), })); +jest.mock('../../../ducks/send/send.duck.js', () => ({ + clearSend: jest.fn(), +})); + jest.mock('../../../selectors/send.js', () => ({ getGasLimit: (s) => `mockGasLimit:${s}`, getGasPrice: (s) => `mockGasPrice:${s}`, diff --git a/ui/pages/send/send-header/send-header.container.js b/ui/pages/send/send-header/send-header.container.js index 9f67cb2af..b66a9ba89 100644 --- a/ui/pages/send/send-header/send-header.container.js +++ b/ui/pages/send/send-header/send-header.container.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { clearSend } from '../../../store/actions'; +import { clearSend } from '../../../ducks/send/send.duck'; import { getTitleKey } from '../../../selectors'; import { getMostRecentOverviewPage } from '../../../ducks/history/history'; import SendHeader from './send-header.component'; diff --git a/ui/pages/send/send.container.js b/ui/pages/send/send.container.js index 3362dadea..f942131dd 100644 --- a/ui/pages/send/send.container.js +++ b/ui/pages/send/send.container.js @@ -23,17 +23,17 @@ import { getCurrentChainId, } from '../../selectors'; +import { showQrScanner, qrCodeDetected } from '../../store/actions'; import { + resetSendState, + updateSendErrors, updateSendTo, updateSendTokenBalance, updateGasData, setGasTotal, - showQrScanner, - qrCodeDetected, updateSendEnsResolution, updateSendEnsResolutionError, -} from '../../store/actions'; -import { resetSendState, updateSendErrors } from '../../ducks/send/send.duck'; +} from '../../ducks/send/send.duck'; import { fetchBasicGasEstimates } from '../../ducks/gas/gas.duck'; import { getBlockGasLimit, diff --git a/ui/pages/send/send.container.test.js b/ui/pages/send/send.container.test.js index bb2aca2dd..3072b3243 100644 --- a/ui/pages/send/send.container.test.js +++ b/ui/pages/send/send.container.test.js @@ -4,9 +4,9 @@ import { updateSendTokenBalance, updateGasData, setGasTotal, -} from '../../store/actions'; - -import { updateSendErrors, resetSendState } from '../../ducks/send/send.duck'; + updateSendErrors, + resetSendState, +} from '../../ducks/send/send.duck'; let mapDispatchToProps; @@ -25,14 +25,12 @@ jest.mock('redux', () => ({ compose: (_, arg2) => () => arg2(), })); -jest.mock('../../store/actions', () => ({ - updateSendTokenBalance: jest.fn(), - updateGasData: jest.fn(), - setGasTotal: jest.fn(), -})); jest.mock('../../ducks/send/send.duck', () => ({ updateSendErrors: jest.fn(), resetSendState: jest.fn(), + updateSendTokenBalance: jest.fn(), + updateGasData: jest.fn(), + setGasTotal: jest.fn(), })); jest.mock('./send.utils.js', () => ({ diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js index ce921eda5..041d971d2 100644 --- a/ui/selectors/custom-gas.js +++ b/ui/selectors/custom-gas.js @@ -296,9 +296,7 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) { const isMainnet = getIsMainnet(state); const showFiat = isMainnet || Boolean(showFiatInTestnets); const gasLimit = - state.metamask.send.gasLimit || - getCustomGasLimit(state) || - GAS_LIMITS.SIMPLE; + state.send.gasLimit || getCustomGasLimit(state) || GAS_LIMITS.SIMPLE; const { conversionRate } = state.metamask; const currentCurrency = getCurrentCurrency(state); const { diff --git a/ui/selectors/custom-gas.test.js b/ui/selectors/custom-gas.test.js index a81982f81..91344b96b 100644 --- a/ui/selectors/custom-gas.test.js +++ b/ui/selectors/custom-gas.test.js @@ -111,10 +111,8 @@ describe('custom-gas selectors', () => { }); it('should return false gas.basicEstimates.price 0x28bed01600 (175) (checkSend=true)', () => { const mockState = { - metamask: { - send: { - gasPrice: '0x28bed0160', - }, + send: { + gasPrice: '0x28bed0160', }, gas: { customData: { price: null }, @@ -125,10 +123,8 @@ describe('custom-gas selectors', () => { }); it('should return true gas.basicEstimates.price 0x30e4f9b400 (210) (checkSend=true)', () => { const mockState = { - metamask: { - send: { - gasPrice: '0x30e4f9b400', - }, + send: { + gasPrice: '0x30e4f9b400', }, gas: { customData: { price: null }, @@ -221,9 +217,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: false, }, @@ -232,6 +225,9 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { blockTime: 14.16326530612245, @@ -272,9 +268,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: false, }, @@ -283,6 +276,9 @@ describe('custom-gas selectors', () => { chainId: '0x4', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { blockTime: 14.16326530612245, @@ -323,9 +319,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: true, }, @@ -334,6 +327,9 @@ describe('custom-gas selectors', () => { chainId: '0x4', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { safeLow: 5, @@ -368,9 +364,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: true, }, @@ -379,6 +372,9 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { safeLow: 5, @@ -429,9 +425,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 255.71, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: false, }, @@ -440,6 +433,9 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { safeLow: 25, @@ -474,9 +470,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: false, }, @@ -485,6 +478,9 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { blockTime: 14.16326530612245, @@ -525,9 +521,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: false, }, @@ -536,6 +529,9 @@ describe('custom-gas selectors', () => { chainId: '0x4', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { blockTime: 14.16326530612245, @@ -576,9 +572,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: true, }, @@ -587,6 +580,9 @@ describe('custom-gas selectors', () => { chainId: '0x4', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { safeLow: 50, @@ -621,9 +617,6 @@ describe('custom-gas selectors', () => { metamask: { conversionRate: 2557.1, currentCurrency: 'usd', - send: { - gasLimit: GAS_LIMITS.SIMPLE, - }, preferences: { showFiatInTestnets: true, }, @@ -632,6 +625,9 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, + send: { + gasLimit: GAS_LIMITS.SIMPLE, + }, gas: { basicEstimates: { safeLow: 50, diff --git a/ui/selectors/send-selectors-test-data.js b/ui/selectors/send-selectors-test-data.js index e6c0d230c..b2663aadb 100644 --- a/ui/selectors/send-selectors-test-data.js +++ b/ui/selectors/send-selectors-test-data.js @@ -150,21 +150,6 @@ const state = { }, ], selectedAddress: '0xd85a4b6a394794842887b8284293d69163007bbb', - send: { - gasLimit: '0xFFFF', - gasPrice: '0xaa', - gasTotal: '0xb451dc41b578', - tokenBalance: 3434, - from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', - to: '0x987fedabc', - amount: '0x080', - memo: '', - errors: { - someError: null, - }, - maxModeOn: false, - editingTransactionId: 97531, - }, unapprovedTxs: { 4768706228115573: { id: 4768706228115573, @@ -210,8 +195,19 @@ const state = { identities: {}, send: { fromDropdownOpen: false, - toDropdownOpen: false, - errors: { someError: null }, + gasLimit: '0xFFFF', + gasPrice: '0xaa', + gasTotal: '0xb451dc41b578', + tokenBalance: 3434, + from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', + to: '0x987fedabc', + amount: '0x080', + memo: '', + errors: { + someError: null, + }, + maxModeOn: false, + editingTransactionId: 97531, }, }; diff --git a/ui/selectors/send.js b/ui/selectors/send.js index ddd5addee..3d0b11d83 100644 --- a/ui/selectors/send.js +++ b/ui/selectors/send.js @@ -7,11 +7,11 @@ import { } from '.'; export function getGasLimit(state) { - return state.metamask.send.gasLimit || '0'; + return state.send.gasLimit || '0'; } export function getGasPrice(state) { - return state.metamask.send.gasPrice || getAveragePriceEstimateInHexWEI(state); + return state.send.gasPrice || getAveragePriceEstimateInHexWEI(state); } export function getGasTotal(state) { @@ -24,7 +24,7 @@ export function getPrimaryCurrency(state) { } export function getSendToken(state) { - return state.metamask.send.token; + return state.send.token; } export function getSendTokenAddress(state) { @@ -39,15 +39,15 @@ export function getSendTokenContract(state) { } export function getSendAmount(state) { - return state.metamask.send.amount; + return state.send.amount; } export function getSendHexData(state) { - return state.metamask.send.data; + return state.send.data; } export function getSendEditingTransactionId(state) { - return state.metamask.send.editingTransactionId; + return state.send.editingTransactionId; } export function getSendErrors(state) { @@ -59,7 +59,7 @@ export function sendAmountIsInError(state) { } export function getSendFrom(state) { - return state.metamask.send.from; + return state.send.from; } export function getSendFromBalance(state) { @@ -75,27 +75,27 @@ export function getSendFromObject(state) { } export function getSendMaxModeState(state) { - return state.metamask.send.maxModeOn; + return state.send.maxModeOn; } export function getSendTo(state) { - return state.metamask.send.to; + return state.send.to; } export function getSendToNickname(state) { - return state.metamask.send.toNickname; + return state.send.toNickname; } export function getTokenBalance(state) { - return state.metamask.send.tokenBalance; + return state.send.tokenBalance; } export function getSendEnsResolution(state) { - return state.metamask.send.ensResolution; + return state.send.ensResolution; } export function getSendEnsResolutionError(state) { - return state.metamask.send.ensResolutionError; + return state.send.ensResolutionError; } export function getQrCodeData(state) { diff --git a/ui/selectors/send.test.js b/ui/selectors/send.test.js index cf4fabc88..aadbc28e5 100644 --- a/ui/selectors/send.test.js +++ b/ui/selectors/send.test.js @@ -111,7 +111,7 @@ describe('send selectors', () => { it('should return the symbol of the send token', () => { expect( getPrimaryCurrency({ - metamask: { send: { token: { symbol: 'DEF' } } }, + send: { token: { symbol: 'DEF' } }, }), ).toStrictEqual('DEF'); }); @@ -121,13 +121,11 @@ describe('send selectors', () => { it('should return the current send token if set', () => { expect( getSendToken({ - metamask: { - send: { - token: { - address: '0x8d6b81208414189a58339873ab429b6c47ab92d3', - decimals: 4, - symbol: 'DEF', - }, + send: { + token: { + address: '0x8d6b81208414189a58339873ab429b6c47ab92d3', + decimals: 4, + symbol: 'DEF', }, }, }), @@ -143,13 +141,11 @@ describe('send selectors', () => { it('should return the contract at the send token address', () => { expect( getSendTokenContract({ - metamask: { - send: { - token: { - address: '0x8d6b81208414189a58339873ab429b6c47ab92d3', - decimals: 4, - symbol: 'DEF', - }, + send: { + token: { + address: '0x8d6b81208414189a58339873ab429b6c47ab92d3', + decimals: 4, + symbol: 'DEF', }, }, }), @@ -157,10 +153,7 @@ describe('send selectors', () => { }); it('should return null if send token is not set', () => { - const modifiedMetamaskState = { ...mockState.metamask, send: {} }; - expect( - getSendTokenContract({ ...mockState, metamask: modifiedMetamaskState }), - ).toBeNull(); + expect(getSendTokenContract({ ...mockState, send: {} })).toBeNull(); }); }); @@ -197,11 +190,10 @@ describe('send selectors', () => { it('should get the selected account balance if the send.from does not exist', () => { const editedMockState = { - metamask: { - ...mockState.metamask, - send: { - from: null, - }, + ...mockState, + send: { + ...mockState.send, + from: null, }, }; expect(getSendFromBalance(editedMockState)).toStrictEqual('0x0'); @@ -220,11 +212,9 @@ describe('send selectors', () => { it('should return the current account if send.from does not exist', () => { const editedMockState = { - metamask: { - ...mockState.metamask, - send: { - from: null, - }, + ...mockState, + send: { + from: null, }, }; expect(getSendFromObject(editedMockState)).toStrictEqual({ @@ -339,9 +329,7 @@ describe('send selectors', () => { describe('send-header selectors', () => { const getMetamaskSendMockState = (send) => { return { - metamask: { - send: { ...send }, - }, + send: { ...send }, }; }; diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js index 8a9f5cd84..d16caa61f 100644 --- a/ui/store/actionConstants.js +++ b/ui/store/actionConstants.js @@ -39,24 +39,6 @@ export const COMPLETED_TX = 'COMPLETED_TX'; export const TRANSACTION_ERROR = 'TRANSACTION_ERROR'; export const UPDATE_TRANSACTION_PARAMS = 'UPDATE_TRANSACTION_PARAMS'; export const SET_NEXT_NONCE = 'SET_NEXT_NONCE'; -// send screen -export const UPDATE_GAS_LIMIT = 'UPDATE_GAS_LIMIT'; -export const UPDATE_GAS_PRICE = 'UPDATE_GAS_PRICE'; -export const UPDATE_GAS_TOTAL = 'UPDATE_GAS_TOTAL'; -export const UPDATE_SEND_HEX_DATA = 'UPDATE_SEND_HEX_DATA'; -export const UPDATE_SEND_TOKEN_BALANCE = 'UPDATE_SEND_TOKEN_BALANCE'; -export const UPDATE_SEND_TO = 'UPDATE_SEND_TO'; -export const UPDATE_SEND_AMOUNT = 'UPDATE_SEND_AMOUNT'; -export const UPDATE_SEND_ERRORS = 'UPDATE_SEND_ERRORS'; -export const UPDATE_MAX_MODE = 'UPDATE_MAX_MODE'; -export const UPDATE_SEND = 'UPDATE_SEND'; -export const UPDATE_SEND_TOKEN = 'UPDATE_SEND_TOKEN'; -export const CLEAR_SEND = 'CLEAR_SEND'; -export const GAS_LOADING_STARTED = 'GAS_LOADING_STARTED'; -export const GAS_LOADING_FINISHED = 'GAS_LOADING_FINISHED'; -export const UPDATE_SEND_ENS_RESOLUTION = 'UPDATE_SEND_ENS_RESOLUTION'; -export const UPDATE_SEND_ENS_RESOLUTION_ERROR = - 'UPDATE_SEND_ENS_RESOLUTION_ERROR'; // config screen export const SET_RPC_TARGET = 'SET_RPC_TARGET'; export const SET_PROVIDER_TYPE = 'SET_PROVIDER_TYPE'; diff --git a/ui/store/actions.js b/ui/store/actions.js index 668eafdeb..f2269cdad 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -3,7 +3,6 @@ import pify from 'pify'; import log from 'loglevel'; import { capitalize } from 'lodash'; import getBuyEthUrl from '../../app/scripts/lib/buy-eth-url'; -import { calcTokenBalance, estimateGasForSend } from '../pages/send/send.utils'; import { fetchLocale, loadRelativeTimeFormatLocaleData, @@ -13,7 +12,6 @@ import { getSymbolAndDecimals } from '../helpers/utils/token-util'; import switchDirection from '../helpers/utils/switch-direction'; import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../shared/constants/app'; import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util'; -import { setCustomGasLimit } from '../ducks/gas/gas.duck'; import txHelper from '../helpers/utils/tx-helper'; import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util'; import { @@ -24,6 +22,7 @@ import { switchedToUnconnectedAccount } from '../ducks/alerts/unconnected-accoun import { getUnconnectedAccountAlertEnabledness } from '../ducks/metamask/metamask'; import { LISTED_CONTRACT_ADDRESSES } from '../../shared/constants/tokens'; import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils'; +import { clearSend } from '../ducks/send/send.duck'; import * as actionConstants from './actionConstants'; let background = null; @@ -38,7 +37,6 @@ export function goHome() { type: actionConstants.GO_HOME, }; } - // async actions export function tryUnlockMetamask(password) { @@ -624,138 +622,18 @@ export function signTypedMsg(msgData) { } export function signTx(txData) { - return (dispatch) => { + return async (dispatch) => { + dispatch(showLoadingIndication()); global.ethQuery.sendTransaction(txData, (err) => { if (err) { dispatch(displayWarning(err.message)); } }); + dispatch(hideLoadingIndication()); 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, - sendToken, - to, - value, - data, -}) { - return (dispatch) => { - dispatch(gasLoadingStarted()); - return estimateGasForSend({ - estimateGasMethod: promisifiedBackground.estimateGas, - blockGasLimit, - selectedAddress, - sendToken, - 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({ sendToken, tokenContract, address }) { - return (dispatch) => { - const tokenBalancePromise = tokenContract - ? tokenContract.balanceOf(address) - : Promise.resolve(); - return tokenBalancePromise - .then((usersToken) => { - if (usersToken) { - const newTokenBalance = calcTokenBalance({ sendToken, 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, @@ -763,57 +641,15 @@ export function updateCustomNonce(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 updateSendToken(token) { - return { - type: actionConstants.UPDATE_SEND_TOKEN, - value: token, - }; -} - -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 async (dispatch) => { dispatch(showLoadingIndication()); try { const token = global.eth.contract(abi).at(tokenAddress); - const txPromise = token.transfer(toAddress, addHexPrefix(amount), txData); + token.transfer(toAddress, addHexPrefix(amount), txData); dispatch(showConfTxPage()); dispatch(hideLoadingIndication()); - await txPromise; } catch (error) { dispatch(hideLoadingIndication()); dispatch(displayWarning(error.message)); @@ -2590,18 +2426,6 @@ export function setRecoveryPhraseReminderLastShown(lastShown) { }; } -export async function setAlertEnabledness(alertId, enabledness) { - await promisifiedBackground.setAlertEnabledness(alertId, enabledness); -} - -export async function setUnconnectedAccountAlertShown(origin) { - await promisifiedBackground.setUnconnectedAccountAlertShown(origin); -} - -export async function setWeb3ShimUsageAlertDismissed(origin) { - await promisifiedBackground.setWeb3ShimUsageAlertDismissed(origin); -} - export function loadingMethodDataStarted() { return { type: actionConstants.LOADING_METHOD_DATA_STARTED, @@ -2868,6 +2692,18 @@ export function setLedgerLivePreference(value) { }; } +// Wrappers around promisifedBackground +/** + * The "actions" below are not actions nor action creators. They cannot use + * dispatch nor should they be dispatched when used. Instead they can be + * called directly. These wrappers will be moved into their location at some + * point in the future. + */ + +export function estimateGas(params) { + return promisifiedBackground.estimateGas(params); +} + // MetaMetrics /** * @typedef {import('../../shared/constants/metametrics').MetaMetricsEventPayload} MetaMetricsEventPayload @@ -2899,3 +2735,15 @@ export function updateViewedNotifications(notificationIdViewedStatusMap) { notificationIdViewedStatusMap, ); } + +export async function setAlertEnabledness(alertId, enabledness) { + await promisifiedBackground.setAlertEnabledness(alertId, enabledness); +} + +export async function setUnconnectedAccountAlertShown(origin) { + await promisifiedBackground.setUnconnectedAccountAlertShown(origin); +} + +export async function setWeb3ShimUsageAlertDismissed(origin) { + await promisifiedBackground.setWeb3ShimUsageAlertDismissed(origin); +} diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index cf32bac46..d28defb8c 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -838,7 +838,9 @@ describe('Actions', () => { it('errors in when sendTransaction throws', async () => { const store = mockStore(); const expectedActions = [ + { type: 'SHOW_LOADING_INDICATION', value: undefined }, { type: 'DISPLAY_WARNING', value: 'error' }, + { type: 'HIDE_LOADING_INDICATION' }, { type: 'SHOW_CONF_TX_PAGE', id: undefined }, ]; @@ -853,67 +855,6 @@ describe('Actions', () => { }); }); - describe('#updatedGasData', () => { - it('errors when get code does not return', async () => { - const store = mockStore(); - - background.estimateGas = sinon.stub().rejects(); - - actions._setBackgroundConnection(background); - - global.eth = { - getCode: sinon.stub().rejects(), - }; - - const expectedActions = [ - { type: 'GAS_LOADING_STARTED' }, - { - type: 'UPDATE_SEND_ERRORS', - value: { gasLoadingError: 'gasLoadingError' }, - }, - { type: 'GAS_LOADING_FINISHED' }, - ]; - - const mockData = { - gasPrice: '0x3b9aca00', // - blockGasLimit: '0x6ad79a', // 7002010 - selectedAddress: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc', - to: '0xEC1Adf982415D2Ef5ec55899b9Bfb8BC0f29251B', - value: '0xde0b6b3a7640000', // 1000000000000000000 - }; - - await store.dispatch(actions.updateGasData(mockData)); - - expect(store.getActions()).toStrictEqual(expectedActions); - }); - - it('returns default gas limit for basic eth transaction', async () => { - const mockData = { - gasPrice: '0x3b9aca00', - blockGasLimit: '0x6ad79a', // 7002010 - selectedAddress: '0x0DCD5D886577d5081B0c52e242Ef29E70Be3E7bc', - to: '0xEC1Adf982415D2Ef5ec55899b9Bfb8BC0f29251B', - value: '0xde0b6b3a7640000', // 1000000000000000000 - }; - - global.eth = { - getCode: sinon.stub().returns('0x'), - }; - const store = mockStore(); - - const expectedActions = [ - { type: 'GAS_LOADING_STARTED' }, - { type: 'UPDATE_GAS_LIMIT', value: GAS_LIMITS.SIMPLE }, - { type: 'metamask/gas/SET_CUSTOM_GAS_LIMIT', value: GAS_LIMITS.SIMPLE }, - { type: 'UPDATE_SEND_ERRORS', value: { gasLoadingError: null } }, - { type: 'GAS_LOADING_FINISHED' }, - ]; - - await store.dispatch(actions.updateGasData(mockData)); - expect(store.getActions()).toStrictEqual(expectedActions); - }); - }); - describe('#signTokenTx', () => { it('calls eth.contract', async () => { global.eth = {