1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-27 21:00:13 +01:00
metamask-extension/ui/app/ducks/confirm-transaction.duck.js

421 lines
11 KiB
JavaScript
Raw Normal View History

import {
conversionRateSelector,
currentCurrencySelector,
unconfirmedTransactionsHashSelector,
getNativeCurrency,
} from '../selectors/confirm-transaction'
import {
getValueFromWeiHex,
getTransactionFee,
getHexGasTotal,
addFiat,
addEth,
increaseLastGasPrice,
hexGreaterThan,
} from '../helpers/confirm-transaction/util'
import {
getTokenData,
getMethodData,
isSmartContractAddress,
sumHexes,
} from '../helpers/transactions.util'
import { getSymbolAndDecimals } from '../token-util'
import { conversionUtil } from '../conversion-util'
import { addHexPrefix } from 'ethereumjs-util'
// Actions
const createActionType = action => `metamask/confirm-transaction/${action}`
const UPDATE_TX_DATA = createActionType('UPDATE_TX_DATA')
const CLEAR_TX_DATA = createActionType('CLEAR_TX_DATA')
const UPDATE_TOKEN_DATA = createActionType('UPDATE_TOKEN_DATA')
const CLEAR_TOKEN_DATA = createActionType('CLEAR_TOKEN_DATA')
const UPDATE_METHOD_DATA = createActionType('UPDATE_METHOD_DATA')
const CLEAR_METHOD_DATA = createActionType('CLEAR_METHOD_DATA')
const CLEAR_CONFIRM_TRANSACTION = createActionType('CLEAR_CONFIRM_TRANSACTION')
const UPDATE_TRANSACTION_AMOUNTS = createActionType('UPDATE_TRANSACTION_AMOUNTS')
const UPDATE_TRANSACTION_FEES = createActionType('UPDATE_TRANSACTION_FEES')
const UPDATE_TRANSACTION_TOTALS = createActionType('UPDATE_TRANSACTION_TOTALS')
const UPDATE_TOKEN_PROPS = createActionType('UPDATE_TOKEN_PROPS')
const UPDATE_NONCE = createActionType('UPDATE_NONCE')
const UPDATE_TO_SMART_CONTRACT = createActionType('UPDATE_TO_SMART_CONTRACT')
const FETCH_DATA_START = createActionType('FETCH_DATA_START')
const FETCH_DATA_END = createActionType('FETCH_DATA_END')
// Initial state
const initState = {
txData: {},
tokenData: {},
methodData: {},
tokenProps: {
tokenDecimals: '',
tokenSymbol: '',
},
fiatTransactionAmount: '',
fiatTransactionFee: '',
fiatTransactionTotal: '',
ethTransactionAmount: '',
ethTransactionFee: '',
ethTransactionTotal: '',
hexTransactionAmount: '',
hexTransactionFee: '',
hexTransactionTotal: '',
nonce: '',
toSmartContract: false,
fetchingData: false,
}
// Reducer
export default function reducer ({ confirmTransaction: confirmState = initState }, action = {}) {
switch (action.type) {
case UPDATE_TX_DATA:
return {
...confirmState,
txData: {
...action.payload,
},
}
case CLEAR_TX_DATA:
return {
...confirmState,
txData: {},
}
case UPDATE_TOKEN_DATA:
return {
...confirmState,
tokenData: {
...action.payload,
},
}
case CLEAR_TOKEN_DATA:
return {
...confirmState,
tokenData: {},
}
case UPDATE_METHOD_DATA:
return {
...confirmState,
methodData: {
...action.payload,
},
}
case CLEAR_METHOD_DATA:
return {
...confirmState,
methodData: {},
}
case UPDATE_TRANSACTION_AMOUNTS:
const { fiatTransactionAmount, ethTransactionAmount, hexTransactionAmount } = action.payload
return {
...confirmState,
fiatTransactionAmount: fiatTransactionAmount || confirmState.fiatTransactionAmount,
ethTransactionAmount: ethTransactionAmount || confirmState.ethTransactionAmount,
hexTransactionAmount: hexTransactionAmount || confirmState.hexTransactionAmount,
}
case UPDATE_TRANSACTION_FEES:
const { fiatTransactionFee, ethTransactionFee, hexTransactionFee } = action.payload
return {
...confirmState,
fiatTransactionFee: fiatTransactionFee || confirmState.fiatTransactionFee,
ethTransactionFee: ethTransactionFee || confirmState.ethTransactionFee,
hexTransactionFee: hexTransactionFee || confirmState.hexTransactionFee,
}
case UPDATE_TRANSACTION_TOTALS:
const { fiatTransactionTotal, ethTransactionTotal, hexTransactionTotal } = action.payload
return {
...confirmState,
fiatTransactionTotal: fiatTransactionTotal || confirmState.fiatTransactionTotal,
ethTransactionTotal: ethTransactionTotal || confirmState.ethTransactionTotal,
hexTransactionTotal: hexTransactionTotal || confirmState.hexTransactionTotal,
}
case UPDATE_TOKEN_PROPS:
const { tokenSymbol = '', tokenDecimals = '' } = action.payload
return {
...confirmState,
tokenProps: {
...confirmState.tokenProps,
tokenSymbol,
tokenDecimals,
},
}
case UPDATE_NONCE:
return {
...confirmState,
nonce: action.payload,
}
case UPDATE_TO_SMART_CONTRACT:
return {
...confirmState,
toSmartContract: action.payload,
}
case FETCH_DATA_START:
return {
...confirmState,
fetchingData: true,
}
case FETCH_DATA_END:
return {
...confirmState,
fetchingData: false,
}
case CLEAR_CONFIRM_TRANSACTION:
return initState
default:
return confirmState
}
}
// Action Creators
export function updateTxData (txData) {
return {
type: UPDATE_TX_DATA,
payload: txData,
}
}
export function clearTxData () {
return {
type: CLEAR_TX_DATA,
}
}
export function updateTokenData (tokenData) {
return {
type: UPDATE_TOKEN_DATA,
payload: tokenData,
}
}
export function clearTokenData () {
return {
type: CLEAR_TOKEN_DATA,
}
}
export function updateMethodData (methodData) {
return {
type: UPDATE_METHOD_DATA,
payload: methodData,
}
}
export function clearMethodData () {
return {
type: CLEAR_METHOD_DATA,
}
}
export function updateTransactionAmounts (amounts) {
return {
type: UPDATE_TRANSACTION_AMOUNTS,
payload: amounts,
}
}
export function updateTransactionFees (fees) {
return {
type: UPDATE_TRANSACTION_FEES,
payload: fees,
}
}
export function updateTransactionTotals (totals) {
return {
type: UPDATE_TRANSACTION_TOTALS,
payload: totals,
}
}
export function updateTokenProps (tokenProps) {
return {
type: UPDATE_TOKEN_PROPS,
payload: tokenProps,
}
}
export function updateNonce (nonce) {
return {
type: UPDATE_NONCE,
payload: nonce,
}
}
export function updateToSmartContract (toSmartContract) {
return {
type: UPDATE_TO_SMART_CONTRACT,
payload: toSmartContract,
}
}
export function setFetchingData (isFetching) {
return {
type: isFetching ? FETCH_DATA_START : FETCH_DATA_END,
}
}
export function updateGasAndCalculate ({ gasLimit, gasPrice }) {
gasLimit = addHexPrefix(gasLimit)
gasPrice = addHexPrefix(gasPrice)
return (dispatch, getState) => {
const { confirmTransaction: { txData } } = getState()
const newTxData = {
...txData,
txParams: {
...txData.txParams,
gas: gasLimit,
gasPrice,
},
}
dispatch(updateTxDataAndCalculate(newTxData))
}
}
function increaseFromLastGasPrice (txData) {
const { lastGasPrice, txParams: { gasPrice: previousGasPrice } = {} } = txData
// Set the minimum to a 10% increase from the lastGasPrice.
const minimumGasPrice = increaseLastGasPrice(lastGasPrice)
const gasPriceBelowMinimum = hexGreaterThan(minimumGasPrice, previousGasPrice)
const gasPrice = (!previousGasPrice || gasPriceBelowMinimum) ? minimumGasPrice : previousGasPrice
return {
...txData,
txParams: {
...txData.txParams,
gasPrice,
},
}
}
export function updateTxDataAndCalculate (txData) {
return (dispatch, getState) => {
const state = getState()
const currentCurrency = currentCurrencySelector(state)
const conversionRate = conversionRateSelector(state)
const nativeCurrency = getNativeCurrency(state)
dispatch(updateTxData(txData))
const { txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData
const fiatTransactionAmount = getValueFromWeiHex({
value, fromCurrency: nativeCurrency, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2,
})
const ethTransactionAmount = getValueFromWeiHex({
value, fromCurrency: nativeCurrency, toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6,
})
dispatch(updateTransactionAmounts({
fiatTransactionAmount,
ethTransactionAmount,
hexTransactionAmount: value,
}))
const hexTransactionFee = getHexGasTotal({ gasLimit, gasPrice })
const fiatTransactionFee = getTransactionFee({
value: hexTransactionFee,
fromCurrency: nativeCurrency,
toCurrency: currentCurrency,
numberOfDecimals: 2,
conversionRate,
})
const ethTransactionFee = getTransactionFee({
value: hexTransactionFee,
fromCurrency: nativeCurrency,
toCurrency: nativeCurrency,
numberOfDecimals: 6,
conversionRate,
})
dispatch(updateTransactionFees({ fiatTransactionFee, ethTransactionFee, hexTransactionFee }))
const fiatTransactionTotal = addFiat(fiatTransactionFee, fiatTransactionAmount)
const ethTransactionTotal = addEth(ethTransactionFee, ethTransactionAmount)
const hexTransactionTotal = sumHexes(value, hexTransactionFee)
dispatch(updateTransactionTotals({
fiatTransactionTotal,
ethTransactionTotal,
hexTransactionTotal,
}))
}
}
export function setTransactionToConfirm (transactionId) {
return async (dispatch, getState) => {
const state = getState()
const unconfirmedTransactionsHash = unconfirmedTransactionsHashSelector(state)
const transaction = unconfirmedTransactionsHash[transactionId]
if (!transaction) {
console.error(`Transaction with id ${transactionId} not found`)
return
}
if (transaction.txParams) {
const { lastGasPrice } = transaction
const txData = lastGasPrice ? increaseFromLastGasPrice(transaction) : transaction
dispatch(updateTxDataAndCalculate(txData))
const { txParams } = transaction
const { to } = txParams
if (txParams.data) {
const { tokens: existingTokens } = state
const { data, to: tokenAddress } = txParams
try {
dispatch(setFetchingData(true))
const methodData = await getMethodData(data)
Metametrics (#6171) * Add metametrics provider and util. * Add backend api and state for participating in metametrics. * Add frontend action for participating in metametrics. * Add metametrics opt-in screen. * Add metametrics events to first time flow. * Add metametrics events for route changes * Add metametrics events for send and confirm screens * Add metametrics events to dropdowns, transactions, log in and out, settings, sig requests and main screen * Ensures each log in is measured as a new visit by metametrics. * Ensure metametrics is called with an empty string for dimensions params if specified * Adds opt in metametrics modal after unlock for existing users * Adds settings page toggle for opting in and out of MetaMetrics * Switch metametrics dimensions to page level scope * Lint, test and translation fixes for metametrics. * Update design for metametrics opt-in screen * Complete responsive styling of metametrics-opt-in modal * Use new chart image on metrics opt in screens * Incorporate the metametrics opt-in screen into the new onboarding flow * Update e2e tests to accomodate metametrics changes * Mock out metametrics network requests in integration tests * Fix tx-list integration test to support metametrics provider. * Send number of tokens and accounts data with every metametrics event. * Update metametrics event descriptor schema and add new events. * Fix import tos bug and send gas button bug due to metametrics changes. * Various small fixes on the metametrics branch. * Add origin custom variable type to metametrics.util * Fix names of onboarding complete actions (metametrics). * Fix names of Metrics Options actions (metametrics). * Clean up code related to metametrics. * Fix bad merge conflict resolution and improve promise handling in sendMetaMetrics event and confrim tx base * Don't send a second metrics event if user has gone back during first time flow. * Collect metametrics on going back from onboarding create/import. * Add missing custom variable constants for metametrics * Fix metametrics provider * Make height of opt-in modal responsive. * Adjust text content for opt-in modal. * Update metametrics event names and clean up code in opt-in-modal * Put phishing warning step next to last in onboarding flow * Link terms of service on create and import screens of first time flow * Add subtext to options on the onboarding select action screen. * Fix styling of bullet points on end of onboarding screen. * Combine phishing warning and congratulations screens. * Fix placement of users if unlocking after an incomplete onboarding import flow. * Fix capitalization in opt-in screen * Fix last onboarding screen translations * Add link to 'Learn More' on the last screen of onboarding * Code clean up: metametrics branch * Update e2e tests for phishing warning step removal * e2e tests passing on metametrics branch * Different tracking urls for metametrics on development and prod
2019-03-05 16:45:01 +01:00
dispatch(updateMethodData(methodData))
} catch (error) {
dispatch(updateMethodData({}))
dispatch(setFetchingData(false))
}
try {
const toSmartContract = await isSmartContractAddress(to)
dispatch(updateToSmartContract(toSmartContract))
dispatch(setFetchingData(false))
} catch (error) {
dispatch(setFetchingData(false))
}
const tokenData = getTokenData(data)
dispatch(updateTokenData(tokenData))
2018-07-06 20:58:41 +02:00
try {
const tokenSymbolData = await getSymbolAndDecimals(tokenAddress, existingTokens) || {}
const { symbol: tokenSymbol = '', decimals: tokenDecimals = '' } = tokenSymbolData
dispatch(updateTokenProps({ tokenSymbol, tokenDecimals }))
} catch (error) {
dispatch(updateTokenProps({ tokenSymbol: '', tokenDecimals: '' }))
}
}
if (txParams.nonce) {
const nonce = conversionUtil(txParams.nonce, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
})
dispatch(updateNonce(nonce))
}
} else {
dispatch(updateTxData(transaction))
}
}
}
export function clearConfirmTransaction () {
return {
type: CLEAR_CONFIRM_TRANSACTION,
}
}