1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 19:26:13 +02:00
metamask-extension/ui/app/hooks/useTransactionTimeRemaining.js
Mark Stacey fd2e02274a
Catch gas estimate errors (#9025)
There were two cases where bad gas estimate data was resulting in
crashes. These two places have been wrapped in a `try ... catch` to
handle the absence of gas estimate data.

The errors are still reported to Sentry so that we can track down the
root cause of this corrupted gas estimate data at some point in the
future. We plan on adding additional context to Sentry soon that should
help with this.

Fixes #8992
2020-07-16 20:29:22 -03:00

104 lines
4.2 KiB
JavaScript

import { getEstimatedGasPrices, getEstimatedGasTimes, getFeatureFlags, getIsMainnet } from '../selectors'
import { hexWEIToDecGWEI } from '../helpers/utils/conversions.util'
import { useSelector } from 'react-redux'
import { useRef, useEffect, useState, useMemo } from 'react'
import { isEqual } from 'lodash'
import { captureException } from '@sentry/browser'
import { getRawTimeEstimateData } from '../helpers/utils/gas-time-estimates.util'
import { getCurrentLocale } from '../ducks/metamask/metamask'
/**
* Calculate the number of minutes remaining until the transaction completes.
* @param {number} initialTimeEstimate - timestamp for the projected completion time
* @param {number} submittedTime - timestamp of when the tx was submitted
* @return {number} minutes remaining
*/
function calcTransactionTimeRemaining (initialTimeEstimate, submittedTime) {
const currentTime = (new Date()).getTime()
const timeElapsedSinceSubmission = (currentTime - submittedTime) / 1000
const timeRemainingOnEstimate = initialTimeEstimate - timeElapsedSinceSubmission
const renderingTimeRemainingEstimate = Math.round(timeRemainingOnEstimate / 60)
return renderingTimeRemainingEstimate
}
/**
* returns a string representing the number of minutes predicted for the transaction to be
* completed. Only returns this prediction if the transaction is the earliest pending
* transaction, and the feature flag for showing timing is enabled.
* @param {bool} isPending - is the transaction currently pending
* @param {bool} isEarliestNonce - is this transaction the earliest nonce in list
* @param {number} submittedTime - the timestamp for when the transaction was submitted
* @param {number} currentGasPrice - gas price to use for calculation of time
* @returns {string | undefined} i18n formatted string if applicable
*/
export function useTransactionTimeRemaining (
isPending,
isEarliestNonce,
submittedTime,
currentGasPrice,
) {
// the following two selectors return the result of mapping over an array, as such they
// will always be new objects and trigger effects. To avoid this, we use isEqual as the
// equalityFn to only update when the data is new.
const gasPrices = useSelector(getEstimatedGasPrices, isEqual)
const estimatedTimes = useSelector(getEstimatedGasTimes, isEqual)
const locale = useSelector(getCurrentLocale)
const isMainNet = useSelector(getIsMainnet)
const interval = useRef()
const [timeRemaining, setTimeRemaining] = useState(null)
const featureFlags = useSelector(getFeatureFlags)
const transactionTimeFeatureActive = featureFlags?.transactionTime
const rtf = new Intl.RelativeTimeFormat(locale.replace('_', '-'), { numeric: 'auto', style: 'narrow' })
// Memoize this value so it can be used as a dependency in the effect below
const initialTimeEstimate = useMemo(() => {
const customGasPrice = Number(hexWEIToDecGWEI(currentGasPrice))
try {
const {
newTimeEstimate,
} = getRawTimeEstimateData(customGasPrice, gasPrices, estimatedTimes)
return newTimeEstimate
} catch (error) {
captureException(error)
return NaN
}
}, [ currentGasPrice, gasPrices, estimatedTimes ])
useEffect(() => {
if (
isMainNet &&
transactionTimeFeatureActive &&
isPending &&
isEarliestNonce &&
!isNaN(initialTimeEstimate)
) {
clearInterval(interval.current)
setTimeRemaining(
calcTransactionTimeRemaining(initialTimeEstimate, submittedTime),
)
interval.current = setInterval(() => {
setTimeRemaining(
calcTransactionTimeRemaining(initialTimeEstimate, submittedTime),
)
}, 10000)
return () => clearInterval(interval.current)
}
}, [
isMainNet,
transactionTimeFeatureActive,
isEarliestNonce,
isPending,
submittedTime,
initialTimeEstimate,
])
// there are numerous checks to determine if time should be displayed.
// if any of the following are true, the timeRemaining will be null
// User is currently not on the mainnet
// User does not have the transactionTime feature flag enabled
// The transaction is not pending, or isn't the earliest nonce
return timeRemaining ? rtf.format(timeRemaining, 'minute') : undefined
}