aboutsummaryrefslogblamecommitdiffstats
path: root/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js
blob: 1671f95fa4fa77b9f6ab2219027b02e8b2a9b216 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                     
                                          
                                                                        



              

                           
                         


                    
                  
                        

                                
                                
        


                                 





                                                
                       

                              
                     
                               
                                   
                               

                    
                              

                        
                                 
                               
                    
                                      
        


                                        












                                                           
                      

                                              
                                                                                             
 

                                              
                                                                   

                                                                 
                                                                                                       

                                                                              

                                                                                   
                                                                                         
 
                                                                                       





                                                                                                         

                                                                   

                                                                     

                                                    







                                                    
 
          
              
                                

                             
                   
                                                                 
                 
                                                                                              
                                                   
                                                

                               
                                                                                                     

                    
                    
                                   

                     
                                                    
                                           
      




                                                                                                      

                                                                       
      


                                                  
                        

   

                                        

                                                                                              
          



                                 
                                           







                                                                                                                  

                                                                      

                                                                    


                                                               
                                                             
                                                                                                             
                                               

                                                                                   


   
                                                             
                                                                                                                  

                                                       
                                                   

                                                                           

                                                               

                                           






                          


                                                                  
                           


                                                        
                                

                                              
                                    
                                

       



                                                            





                              
                                                                            








                                                                                              

                                                     

 

                                                   

 
                                             
                                                                      



                                                                                       
                    
                                   
                                                                         


















                                                                                           
 
                                                                                 








                                                     




                            
                                                                              





                                                     
                                          

    
                                                                                                          
 
import { connect } from 'react-redux'
import { pipe, partialRight } from 'ramda'
import GasModalPageContainer from './gas-modal-page-container.component'
import {
  hideModal,
  setGasLimit,
  setGasPrice,
  createSpeedUpTransaction,
  hideSidebar,
} from '../../../actions'
import {
  setCustomGasPrice,
  setCustomGasLimit,
  resetCustomData,
  setCustomTimeEstimate,
  fetchGasEstimates,
  fetchBasicGasAndTimeEstimates,
} from '../../../ducks/gas.duck'
import {
  hideGasButtonGroup,
} from '../../../ducks/send.duck'
import {
  updateGasAndCalculate,
} from '../../../ducks/confirm-transaction.duck'
import {
  getCurrentCurrency,
  conversionRateSelector as getConversionRate,
  getSelectedToken,
  getCurrentEthBalance,
} from '../../../selectors.js'
import {
  formatTimeEstimate,
  getFastPriceEstimateInHexWEI,
  getBasicGasEstimateLoadingStatus,
  getGasEstimatesLoadingStatus,
  getCustomGasLimit,
  getCustomGasPrice,
  getDefaultActiveButtonIndex,
  getEstimatedGasPrices,
  getEstimatedGasTimes,
  getRenderableBasicEstimateData,
  getBasicGasEstimateBlockTime,
  isCustomPriceSafe,
} from '../../../selectors/custom-gas'
import {
  submittedPendingTransactionsSelector,
} from '../../../selectors/transactions'
import {
  formatCurrency,
} from '../../../helpers/confirm-transaction/util'
import {
  addHexWEIsToDec,
  decEthToConvertedCurrency as ethTotalToConvertedCurrency,
  decGWEIToHexWEI,
  hexWEIToDecGWEI,
} from '../../../helpers/conversions.util'
import {
  formatETHFee,
} from '../../../helpers/formatters'
import {
  calcGasTotal,
  isBalanceSufficient,
} from '../../send/send.utils'
import { addHexPrefix } from 'ethereumjs-util'
import { getAdjacentGasPrices, extrapolateY } from '../gas-price-chart/gas-price-chart.utils'

const mapStateToProps = (state, ownProps) => {
  const { transaction = {} } = ownProps
  const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
  const gasEstimatesLoading = getGasEstimatesLoadingStatus(state)

  const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = getTxParams(state, transaction.id)
  const customModalGasPriceInHex = getCustomGasPrice(state) || currentGasPrice
  const customModalGasLimitInHex = getCustomGasLimit(state) || currentGasLimit
  const gasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)

  const customGasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)

  const gasButtonInfo = getRenderableBasicEstimateData(state, customModalGasLimitInHex)

  const currentCurrency = getCurrentCurrency(state)
  const conversionRate = getConversionRate(state)

  const newTotalFiat = addHexWEIsToRenderableFiat(value, customGasTotal, currentCurrency, conversionRate)

  const hideBasic = state.appState.modal.modalState.props.hideBasic

  const customGasPrice = calcCustomGasPrice(customModalGasPriceInHex)

  const gasPrices = getEstimatedGasPrices(state)
  const estimatedTimes = getEstimatedGasTimes(state)
  const balance = getCurrentEthBalance(state)

  const insufficientBalance = !isBalanceSufficient({
    amount: value,
    gasTotal,
    balance,
    conversionRate,
  })

  return {
    hideBasic,
    isConfirm: isConfirm(state),
    customModalGasPriceInHex,
    customModalGasLimitInHex,
    customGasPrice,
    customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
    newTotalFiat,
    currentTimeEstimate: getRenderableTimeEstimate(customGasPrice, gasPrices, estimatedTimes),
    blockTime: getBasicGasEstimateBlockTime(state),
    customPriceIsSafe: isCustomPriceSafe(state),
    gasPriceButtonGroupProps: {
      buttonDataLoading,
      defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex),
      gasButtonInfo,
    },
    gasChartProps: {
      currentPrice: customGasPrice,
      gasPrices,
      estimatedTimes,
      gasPricesMax: gasPrices[gasPrices.length - 1],
      estimatedTimesMax: estimatedTimes[0],
    },
    infoRowProps: {
      originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate),
      originalTotalEth: addHexWEIsToRenderableEth(value, gasTotal),
      newTotalFiat,
      newTotalEth: addHexWEIsToRenderableEth(value, customGasTotal),
      transactionFee: addHexWEIsToRenderableEth('0x0', customGasTotal),
      sendAmount: addHexWEIsToRenderableEth(value, '0x0'),
    },
    isSpeedUp: transaction.status === 'submitted',
    txId: transaction.id,
    insufficientBalance,
    gasEstimatesLoading,
  }
}

const mapDispatchToProps = dispatch => {
  const updateCustomGasPrice = newPrice => dispatch(setCustomGasPrice(addHexPrefix(newPrice)))

  return {
    cancelAndClose: () => {
      dispatch(resetCustomData())
      dispatch(hideModal())
    },
    hideModal: () => dispatch(hideModal()),
    updateCustomGasPrice,
    convertThenUpdateCustomGasPrice: newPrice => updateCustomGasPrice(decGWEIToHexWEI(newPrice)),
    convertThenUpdateCustomGasLimit: newLimit => dispatch(setCustomGasLimit(addHexPrefix(newLimit.toString(16)))),
    setGasData: (newLimit, newPrice) => {
      dispatch(setGasLimit(newLimit))
      dispatch(setGasPrice(newPrice))
    },
    updateConfirmTxGasAndCalculate: (gasLimit, gasPrice) => {
      updateCustomGasPrice(gasPrice)
      dispatch(setCustomGasLimit(addHexPrefix(gasLimit.toString(16))))
      return dispatch(updateGasAndCalculate({ gasLimit, gasPrice }))
    },
    createSpeedUpTransaction: (txId, gasPrice) => {
      return dispatch(createSpeedUpTransaction(txId, gasPrice))
    },
    hideGasButtonGroup: () => dispatch(hideGasButtonGroup()),
    setCustomTimeEstimate: (timeEstimateInSeconds) => dispatch(setCustomTimeEstimate(timeEstimateInSeconds)),
    hideSidebar: () => dispatch(hideSidebar()),
    fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)),
    fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()),
  }
}

const mergeProps = (stateProps, dispatchProps, ownProps) => {
  const { gasPriceButtonGroupProps, isConfirm, txId, isSpeedUp, insufficientBalance, customGasPrice } = stateProps
  const {
    updateCustomGasPrice: dispatchUpdateCustomGasPrice,
    hideGasButtonGroup: dispatchHideGasButtonGroup,
    setGasData: dispatchSetGasData,
    updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
    createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
    hideSidebar: dispatchHideSidebar,
    cancelAndClose: dispatchCancelAndClose,
    hideModal: dispatchHideModal,
    ...otherDispatchProps
  } = dispatchProps

  return {
    ...stateProps,
    ...otherDispatchProps,
    ...ownProps,
    onSubmit: (gasLimit, gasPrice) => {
      if (isConfirm) {
        dispatchUpdateConfirmTxGasAndCalculate(gasLimit, gasPrice)
        dispatchHideModal()
      } else if (isSpeedUp) {
        dispatchCreateSpeedUpTransaction(txId, gasPrice)
        dispatchHideSidebar()
        dispatchCancelAndClose()
      } else {
        dispatchSetGasData(gasLimit, gasPrice)
        dispatchHideGasButtonGroup()
        dispatchCancelAndClose()
      }
    },
    gasPriceButtonGroupProps: {
      ...gasPriceButtonGroupProps,
      handleGasPriceSelection: dispatchUpdateCustomGasPrice,
    },
    cancelAndClose: () => {
      dispatchCancelAndClose()
      if (isSpeedUp) {
        dispatchHideSidebar()
      }
    },
    disableSave: insufficientBalance || (isSpeedUp && customGasPrice === 0),
  }
}

export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(GasModalPageContainer)

function isConfirm (state) {
  return Boolean(Object.keys(state.confirmTransaction.txData).length)
}

function calcCustomGasPrice (customGasPriceInHex) {
  return Number(hexWEIToDecGWEI(customGasPriceInHex))
}

function calcCustomGasLimit (customGasLimitInHex) {
  return parseInt(customGasLimitInHex, 16)
}

function getTxParams (state, transactionId) {
  const { confirmTransaction: { txData }, metamask: { send } } = state
  const pendingTransactions = submittedPendingTransactionsSelector(state)
  const pendingTransaction = pendingTransactions.find(({ id }) => id === transactionId)
  const { txParams: pendingTxParams } = pendingTransaction || {}
  return txData.txParams || pendingTxParams || {
    from: send.from,
    gas: send.gasLimit || '0x5208',
    gasPrice: send.gasPrice || getFastPriceEstimateInHexWEI(state, true),
    to: send.to,
    value: getSelectedToken(state) ? '0x0' : send.amount,
  }
}

function addHexWEIsToRenderableEth (aHexWEI, bHexWEI) {
  return pipe(
    addHexWEIsToDec,
    formatETHFee
  )(aHexWEI, bHexWEI)
}

function addHexWEIsToRenderableFiat (aHexWEI, bHexWEI, convertedCurrency, conversionRate) {
  return pipe(
    addHexWEIsToDec,
    partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]),
    partialRight(formatCurrency, [convertedCurrency]),
  )(aHexWEI, bHexWEI)
}

function getRenderableTimeEstimate (currentGasPrice, gasPrices, estimatedTimes) {
  const minGasPrice = gasPrices[0]
  const maxGasPrice = gasPrices[gasPrices.length - 1]
  let priceForEstimation = currentGasPrice
  if (currentGasPrice < minGasPrice) {
    priceForEstimation = minGasPrice
  } else if (currentGasPrice > maxGasPrice) {
    priceForEstimation = maxGasPrice
  }

  const {
    closestLowerValueIndex,
    closestHigherValueIndex,
    closestHigherValue,
    closestLowerValue,
  } = getAdjacentGasPrices({ gasPrices, priceToPosition: priceForEstimation })

  const newTimeEstimate = extrapolateY({
    higherY: estimatedTimes[closestHigherValueIndex],
    lowerY: estimatedTimes[closestLowerValueIndex],
    higherX: closestHigherValue,
    lowerX: closestLowerValue,
    xForExtrapolation: priceForEstimation,
  })

  return formatTimeEstimate(newTimeEstimate, currentGasPrice > maxGasPrice, currentGasPrice < minGasPrice)
}