Merge pull request #11439 from MetaMask/Version-v9.8.0
Version v9.8.0 RCfeature/default_network_editable
commit
c87dce9ce5
@ -0,0 +1,219 @@ |
||||
const { strict: assert } = require('assert'); |
||||
const { withFixtures, regularDelayMs } = require('../helpers'); |
||||
|
||||
describe('Send ETH from inside MetaMask using default gas', function () { |
||||
const ganacheOptions = { |
||||
accounts: [ |
||||
{ |
||||
secretKey: |
||||
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', |
||||
balance: 25000000000000000000, |
||||
}, |
||||
], |
||||
}; |
||||
it('finds the transaction in the transactions list', async function () { |
||||
await withFixtures( |
||||
{ |
||||
fixtures: 'imported-account', |
||||
ganacheOptions, |
||||
title: this.test.title, |
||||
}, |
||||
async ({ driver }) => { |
||||
await driver.navigate(); |
||||
await driver.fill('#password', 'correct horse battery staple'); |
||||
await driver.press('#password', driver.Key.ENTER); |
||||
|
||||
await driver.clickElement('[data-testid="eth-overview-send"]'); |
||||
|
||||
await driver.fill( |
||||
'input[placeholder="Search, public address (0x), or ENS"]', |
||||
'0x2f318C334780961FB129D2a6c30D0763d9a5C970', |
||||
); |
||||
|
||||
const inputAmount = await driver.findElement('.unit-input__input'); |
||||
await inputAmount.fill('1000'); |
||||
|
||||
const errorAmount = await driver.findElement('.send-v2__error-amount'); |
||||
assert.equal( |
||||
await errorAmount.getText(), |
||||
'Insufficient funds.', |
||||
'send screen should render an insufficient fund error message', |
||||
); |
||||
|
||||
await inputAmount.press(driver.Key.BACK_SPACE); |
||||
await inputAmount.press(driver.Key.BACK_SPACE); |
||||
await inputAmount.press(driver.Key.BACK_SPACE); |
||||
await driver.delay(regularDelayMs); |
||||
|
||||
await driver.assertElementNotPresent('.send-v2__error-amount'); |
||||
|
||||
const amountMax = await driver.findClickableElement( |
||||
'.send-v2__amount-max', |
||||
); |
||||
await amountMax.click(); |
||||
|
||||
let inputValue = await inputAmount.getAttribute('value'); |
||||
|
||||
assert(Number(inputValue) > 24); |
||||
|
||||
await amountMax.click(); |
||||
|
||||
assert.equal(await inputAmount.isEnabled(), true); |
||||
|
||||
await inputAmount.fill('1'); |
||||
|
||||
inputValue = await inputAmount.getAttribute('value'); |
||||
assert.equal(inputValue, '1'); |
||||
|
||||
// Continue to next screen
|
||||
await driver.clickElement({ text: 'Next', tag: 'button' }); |
||||
|
||||
await driver.clickElement({ text: 'Confirm', tag: 'button' }); |
||||
|
||||
await driver.clickElement('[data-testid="home__activity-tab"]'); |
||||
await driver.wait(async () => { |
||||
const confirmedTxes = await driver.findElements( |
||||
'.transaction-list__completed-transactions .transaction-list-item', |
||||
); |
||||
return confirmedTxes.length === 1; |
||||
}, 10000); |
||||
|
||||
await driver.waitForSelector({ |
||||
css: '.transaction-list-item__primary-currency', |
||||
text: '-1 ETH', |
||||
}); |
||||
}, |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
describe('Send ETH from inside MetaMask using fast gas option', function () { |
||||
const ganacheOptions = { |
||||
accounts: [ |
||||
{ |
||||
secretKey: |
||||
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', |
||||
balance: 25000000000000000000, |
||||
}, |
||||
], |
||||
}; |
||||
it('finds the transaction in the transactions list', async function () { |
||||
await withFixtures( |
||||
{ |
||||
fixtures: 'imported-account', |
||||
ganacheOptions, |
||||
title: this.test.title, |
||||
}, |
||||
async ({ driver }) => { |
||||
await driver.navigate(); |
||||
await driver.fill('#password', 'correct horse battery staple'); |
||||
await driver.press('#password', driver.Key.ENTER); |
||||
|
||||
await driver.clickElement('[data-testid="eth-overview-send"]'); |
||||
|
||||
await driver.fill( |
||||
'input[placeholder="Search, public address (0x), or ENS"]', |
||||
'0x2f318C334780961FB129D2a6c30D0763d9a5C970', |
||||
); |
||||
|
||||
const inputAmount = await driver.findElement('.unit-input__input'); |
||||
await inputAmount.fill('1'); |
||||
|
||||
const inputValue = await inputAmount.getAttribute('value'); |
||||
assert.equal(inputValue, '1'); |
||||
|
||||
// Set the gas price
|
||||
await driver.clickElement({ text: 'Fast', tag: 'button/div/div' }); |
||||
|
||||
// Continue to next screen
|
||||
await driver.clickElement({ text: 'Next', tag: 'button' }); |
||||
|
||||
await driver.clickElement({ text: 'Confirm', tag: 'button' }); |
||||
|
||||
await driver.waitForSelector( |
||||
'.transaction-list__completed-transactions .transaction-list-item', |
||||
); |
||||
await driver.waitForSelector({ |
||||
css: '.transaction-list-item__primary-currency', |
||||
text: '-1 ETH', |
||||
}); |
||||
}, |
||||
); |
||||
}); |
||||
}); |
||||
|
||||
describe('Send ETH from inside MetaMask using advanced gas modal', function () { |
||||
const ganacheOptions = { |
||||
accounts: [ |
||||
{ |
||||
secretKey: |
||||
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', |
||||
balance: 25000000000000000000, |
||||
}, |
||||
], |
||||
}; |
||||
it('finds the transaction in the transactions list', async function () { |
||||
await withFixtures( |
||||
{ |
||||
fixtures: 'imported-account', |
||||
ganacheOptions, |
||||
title: this.test.title, |
||||
}, |
||||
async ({ driver }) => { |
||||
await driver.navigate(); |
||||
await driver.fill('#password', 'correct horse battery staple'); |
||||
await driver.press('#password', driver.Key.ENTER); |
||||
|
||||
await driver.clickElement('[data-testid="eth-overview-send"]'); |
||||
|
||||
await driver.fill( |
||||
'input[placeholder="Search, public address (0x), or ENS"]', |
||||
'0x2f318C334780961FB129D2a6c30D0763d9a5C970', |
||||
); |
||||
|
||||
const inputAmount = await driver.findElement('.unit-input__input'); |
||||
await inputAmount.fill('1'); |
||||
|
||||
const inputValue = await inputAmount.getAttribute('value'); |
||||
assert.equal(inputValue, '1'); |
||||
|
||||
// Set the gas limit
|
||||
await driver.clickElement('.advanced-gas-options-btn'); |
||||
|
||||
// wait for gas modal to be visible
|
||||
const gasModal = await driver.findVisibleElement('span .modal'); |
||||
|
||||
await driver.clickElement({ text: 'Save', tag: 'button' }); |
||||
|
||||
// Wait for gas modal to be removed from DOM
|
||||
await gasModal.waitForElementState('hidden'); |
||||
|
||||
// Continue to next screen
|
||||
await driver.clickElement({ text: 'Next', tag: 'button' }); |
||||
|
||||
const transactionAmounts = await driver.findElements( |
||||
'.currency-display-component__text', |
||||
); |
||||
const transactionAmount = transactionAmounts[0]; |
||||
assert.equal(await transactionAmount.getText(), '1'); |
||||
|
||||
await driver.clickElement({ text: 'Confirm', tag: 'button' }); |
||||
|
||||
await driver.wait(async () => { |
||||
const confirmedTxes = await driver.findElements( |
||||
'.transaction-list__completed-transactions .transaction-list-item', |
||||
); |
||||
return confirmedTxes.length === 1; |
||||
}, 10000); |
||||
|
||||
await driver.waitForSelector( |
||||
{ |
||||
css: '.transaction-list-item__primary-currency', |
||||
text: '-1 ETH', |
||||
}, |
||||
{ timeout: 10000 }, |
||||
); |
||||
}, |
||||
); |
||||
}); |
||||
}); |
@ -1 +1,2 @@ |
||||
export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network'; |
||||
export const METASWAP_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network'; |
||||
|
@ -0,0 +1,200 @@ |
||||
import { createSlice } from '@reduxjs/toolkit'; |
||||
import ENS from 'ethjs-ens'; |
||||
import log from 'loglevel'; |
||||
import networkMap from 'ethereum-ens-network-map'; |
||||
import { isConfusing } from 'unicode-confusables'; |
||||
import { isHexString } from 'ethereumjs-util'; |
||||
|
||||
import { getCurrentChainId } from '../selectors'; |
||||
import { |
||||
CHAIN_ID_TO_NETWORK_ID_MAP, |
||||
MAINNET_NETWORK_ID, |
||||
} from '../../shared/constants/network'; |
||||
import { |
||||
CONFUSING_ENS_ERROR, |
||||
ENS_ILLEGAL_CHARACTER, |
||||
ENS_NOT_FOUND_ON_NETWORK, |
||||
ENS_NOT_SUPPORTED_ON_NETWORK, |
||||
ENS_NO_ADDRESS_FOR_NAME, |
||||
ENS_REGISTRATION_ERROR, |
||||
ENS_UNKNOWN_ERROR, |
||||
} from '../pages/send/send.constants'; |
||||
import { isValidDomainName } from '../helpers/utils/util'; |
||||
import { CHAIN_CHANGED } from '../store/actionConstants'; |
||||
import { |
||||
BURN_ADDRESS, |
||||
isBurnAddress, |
||||
isValidHexAddress, |
||||
} from '../../shared/modules/hexstring-utils'; |
||||
|
||||
// Local Constants
|
||||
const ZERO_X_ERROR_ADDRESS = '0x'; |
||||
|
||||
const initialState = { |
||||
stage: 'UNINITIALIZED', |
||||
resolution: null, |
||||
error: null, |
||||
warning: null, |
||||
network: null, |
||||
}; |
||||
|
||||
export const ensInitialState = initialState; |
||||
|
||||
const name = 'ENS'; |
||||
|
||||
let ens = null; |
||||
|
||||
const slice = createSlice({ |
||||
name, |
||||
initialState, |
||||
reducers: { |
||||
ensLookup: (state, action) => { |
||||
// first clear out the previous state
|
||||
state.resolution = null; |
||||
state.error = null; |
||||
state.warning = null; |
||||
const { address, ensName, error, network } = action.payload; |
||||
|
||||
if (error) { |
||||
if ( |
||||
isValidDomainName(ensName) && |
||||
error.message === 'ENS name not defined.' |
||||
) { |
||||
state.error = |
||||
network === MAINNET_NETWORK_ID |
||||
? ENS_NO_ADDRESS_FOR_NAME |
||||
: ENS_NOT_FOUND_ON_NETWORK; |
||||
} else if (error.message === 'Illegal Character for ENS.') { |
||||
state.error = ENS_ILLEGAL_CHARACTER; |
||||
} else { |
||||
log.error(error); |
||||
state.error = ENS_UNKNOWN_ERROR; |
||||
} |
||||
} else if (address) { |
||||
if (address === BURN_ADDRESS) { |
||||
state.error = ENS_NO_ADDRESS_FOR_NAME; |
||||
} else if (address === ZERO_X_ERROR_ADDRESS) { |
||||
state.error = ENS_REGISTRATION_ERROR; |
||||
} else { |
||||
state.resolution = address; |
||||
} |
||||
if (isValidDomainName(address) && isConfusing(address)) { |
||||
state.warning = CONFUSING_ENS_ERROR; |
||||
} |
||||
} |
||||
}, |
||||
enableEnsLookup: (state, action) => { |
||||
state.stage = 'INITIALIZED'; |
||||
state.error = null; |
||||
state.resolution = null; |
||||
state.warning = null; |
||||
state.network = action.payload; |
||||
}, |
||||
disableEnsLookup: (state) => { |
||||
state.stage = 'NO_NETWORK_SUPPORT'; |
||||
state.error = null; |
||||
state.warning = null; |
||||
state.resolution = null; |
||||
state.network = null; |
||||
}, |
||||
ensNotSupported: (state) => { |
||||
state.resolution = null; |
||||
state.warning = null; |
||||
state.error = ENS_NOT_SUPPORTED_ON_NETWORK; |
||||
}, |
||||
resetEnsResolution: (state) => { |
||||
state.resolution = null; |
||||
state.warning = null; |
||||
state.error = null; |
||||
}, |
||||
}, |
||||
extraReducers: (builder) => { |
||||
builder.addCase(CHAIN_CHANGED, (state, action) => { |
||||
if (action.payload !== state.currentChainId) { |
||||
state.stage = 'UNINITIALIZED'; |
||||
ens = null; |
||||
} |
||||
}); |
||||
}, |
||||
}); |
||||
|
||||
const { reducer, actions } = slice; |
||||
export default reducer; |
||||
|
||||
const { |
||||
disableEnsLookup, |
||||
ensLookup, |
||||
enableEnsLookup, |
||||
ensNotSupported, |
||||
resetEnsResolution, |
||||
} = actions; |
||||
export { resetEnsResolution }; |
||||
|
||||
export function initializeEnsSlice() { |
||||
return (dispatch, getState) => { |
||||
const state = getState(); |
||||
const chainId = getCurrentChainId(state); |
||||
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId]; |
||||
const networkIsSupported = Boolean(networkMap[network]); |
||||
if (networkIsSupported) { |
||||
ens = new ENS({ provider: global.ethereumProvider, network }); |
||||
dispatch(enableEnsLookup(network)); |
||||
} else { |
||||
ens = null; |
||||
dispatch(disableEnsLookup()); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
export function lookupEnsName(ensName) { |
||||
return async (dispatch, getState) => { |
||||
const trimmedEnsName = ensName.trim(); |
||||
let state = getState(); |
||||
if (state[name].stage === 'UNINITIALIZED') { |
||||
await dispatch(initializeEnsSlice()); |
||||
} |
||||
state = getState(); |
||||
if ( |
||||
state[name].stage === 'NO_NETWORK_SUPPORT' && |
||||
!( |
||||
isBurnAddress(trimmedEnsName) === false && |
||||
isValidHexAddress(trimmedEnsName, { mixedCaseUseChecksum: true }) |
||||
) && |
||||
!isHexString(trimmedEnsName) |
||||
) { |
||||
await dispatch(ensNotSupported()); |
||||
} else { |
||||
log.info(`ENS attempting to resolve name: ${trimmedEnsName}`); |
||||
let address; |
||||
let error; |
||||
try { |
||||
address = await ens.lookup(trimmedEnsName); |
||||
} catch (err) { |
||||
error = err; |
||||
} |
||||
const chainId = getCurrentChainId(state); |
||||
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId]; |
||||
await dispatch( |
||||
ensLookup({ |
||||
ensName: trimmedEnsName, |
||||
address, |
||||
error, |
||||
chainId, |
||||
network, |
||||
}), |
||||
); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
export function getEnsResolution(state) { |
||||
return state[name].resolution; |
||||
} |
||||
|
||||
export function getEnsError(state) { |
||||
return state[name].error; |
||||
} |
||||
|
||||
export function getEnsWarning(state) { |
||||
return state[name].warning; |
||||
} |
@ -0,0 +1,14 @@ |
||||
// This file has been separated because it is required in both the gas and send
|
||||
// slices. This created a circular dependency problem as both slices also
|
||||
// import from the actions and selectors files. This easiest path for
|
||||
// untangling is having the constants separate.
|
||||
|
||||
// Actions
|
||||
export const BASIC_GAS_ESTIMATE_STATUS = |
||||
'metamask/gas/BASIC_GAS_ESTIMATE_STATUS'; |
||||
export const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA'; |
||||
export const SET_BASIC_GAS_ESTIMATE_DATA = |
||||
'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA'; |
||||
export const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT'; |
||||
export const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE'; |
||||
export const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE'; |
@ -0,0 +1 @@ |
||||
export * from './send'; |
@ -1,142 +0,0 @@ |
||||
import SendReducer, { |
||||
openToDropdown, |
||||
closeToDropdown, |
||||
updateSendErrors, |
||||
showGasButtonGroup, |
||||
hideGasButtonGroup, |
||||
} from './send.duck'; |
||||
|
||||
describe('Send Duck', () => { |
||||
const mockState = { |
||||
mockProp: 123, |
||||
}; |
||||
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, |
||||
}; |
||||
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN'; |
||||
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN'; |
||||
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'; |
||||
|
||||
describe('SendReducer()', () => { |
||||
it('should initialize state', () => { |
||||
expect(SendReducer(undefined, {})).toStrictEqual(initState); |
||||
}); |
||||
|
||||
it('should return state unchanged if it does not match a dispatched actions type', () => { |
||||
expect( |
||||
SendReducer(mockState, { |
||||
type: 'someOtherAction', |
||||
value: 'someValue', |
||||
}), |
||||
).toStrictEqual(mockState); |
||||
}); |
||||
|
||||
it('should set toDropdownOpen to true when receiving a OPEN_TO_DROPDOWN action', () => { |
||||
expect( |
||||
SendReducer(mockState, { |
||||
type: OPEN_TO_DROPDOWN, |
||||
}), |
||||
).toStrictEqual({ toDropdownOpen: true, ...mockState }); |
||||
}); |
||||
|
||||
it('should set toDropdownOpen to false when receiving a CLOSE_TO_DROPDOWN action', () => { |
||||
expect( |
||||
SendReducer(mockState, { |
||||
type: CLOSE_TO_DROPDOWN, |
||||
}), |
||||
).toStrictEqual({ toDropdownOpen: false, ...mockState }); |
||||
}); |
||||
|
||||
it('should set gasButtonGroupShown to true when receiving a SHOW_GAS_BUTTON_GROUP action', () => { |
||||
expect( |
||||
SendReducer( |
||||
{ ...mockState, gasButtonGroupShown: false }, |
||||
{ type: SHOW_GAS_BUTTON_GROUP }, |
||||
), |
||||
).toStrictEqual({ gasButtonGroupShown: true, ...mockState }); |
||||
}); |
||||
|
||||
it('should set gasButtonGroupShown to false when receiving a HIDE_GAS_BUTTON_GROUP action', () => { |
||||
expect( |
||||
SendReducer(mockState, { type: HIDE_GAS_BUTTON_GROUP }), |
||||
).toStrictEqual({ gasButtonGroupShown: false, ...mockState }); |
||||
}); |
||||
|
||||
it('should extend send.errors with the value of a UPDATE_SEND_ERRORS action', () => { |
||||
const modifiedMockState = { |
||||
...mockState, |
||||
errors: { |
||||
someError: false, |
||||
}, |
||||
}; |
||||
expect( |
||||
SendReducer(modifiedMockState, { |
||||
type: UPDATE_SEND_ERRORS, |
||||
value: { someOtherError: true }, |
||||
}), |
||||
).toStrictEqual({ |
||||
...modifiedMockState, |
||||
errors: { |
||||
someError: false, |
||||
someOtherError: true, |
||||
}, |
||||
}); |
||||
}); |
||||
|
||||
it('should return the initial state in response to a RESET_SEND_STATE action', () => { |
||||
expect( |
||||
SendReducer(mockState, { |
||||
type: RESET_SEND_STATE, |
||||
}), |
||||
).toStrictEqual(initState); |
||||
}); |
||||
}); |
||||
|
||||
describe('Send Duck Actions', () => { |
||||
it('calls openToDropdown action', () => { |
||||
expect(openToDropdown()).toStrictEqual({ type: OPEN_TO_DROPDOWN }); |
||||
}); |
||||
|
||||
it('calls closeToDropdown action', () => { |
||||
expect(closeToDropdown()).toStrictEqual({ type: CLOSE_TO_DROPDOWN }); |
||||
}); |
||||
|
||||
it('calls showGasButtonGroup action', () => { |
||||
expect(showGasButtonGroup()).toStrictEqual({ |
||||
type: SHOW_GAS_BUTTON_GROUP, |
||||
}); |
||||
}); |
||||
|
||||
it('calls hideGasButtonGroup action', () => { |
||||
expect(hideGasButtonGroup()).toStrictEqual({ |
||||
type: HIDE_GAS_BUTTON_GROUP, |
||||
}); |
||||
}); |
||||
|
||||
it('calls updateSendErrors action', () => { |
||||
expect(updateSendErrors('mockErrorObject')).toStrictEqual({ |
||||
type: UPDATE_SEND_ERRORS, |
||||
value: 'mockErrorObject', |
||||
}); |
||||
}); |
||||
}); |
||||
}); |
@ -1,382 +0,0 @@ |
||||
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'; |
||||
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
|
||||
export default function reducer(state = initState, action) { |
||||
switch (action.type) { |
||||
case OPEN_TO_DROPDOWN: |
||||
return { |
||||
...state, |
||||
toDropdownOpen: true, |
||||
}; |
||||
case CLOSE_TO_DROPDOWN: |
||||
return { |
||||
...state, |
||||
toDropdownOpen: false, |
||||
}; |
||||
case UPDATE_SEND_ERRORS: |
||||
return { |
||||
...state, |
||||
errors: { |
||||
...state.errors, |
||||
...action.value, |
||||
}, |
||||
}; |
||||
case SHOW_GAS_BUTTON_GROUP: |
||||
return { |
||||
...state, |
||||
gasButtonGroupShown: true, |
||||
}; |
||||
case HIDE_GAS_BUTTON_GROUP: |
||||
return { |
||||
...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; |
||||
} |
||||
} |
||||
|
||||
// Action Creators
|
||||
export function openToDropdown() { |
||||
return { type: OPEN_TO_DROPDOWN }; |
||||
} |
||||
|
||||
export function closeToDropdown() { |
||||
return { type: CLOSE_TO_DROPDOWN }; |
||||
} |
||||
|
||||
export function showGasButtonGroup() { |
||||
return { type: SHOW_GAS_BUTTON_GROUP }; |
||||
} |
||||
|
||||
export function hideGasButtonGroup() { |
||||
return { type: HIDE_GAS_BUTTON_GROUP }; |
||||
} |
||||
|
||||
export function updateSendErrors(errorObject) { |
||||
return { |
||||
type: UPDATE_SEND_ERRORS, |
||||
value: 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, |
||||
}; |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue