mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Connect the gas-button-group component to redux and a live api.
This commit is contained in:
parent
112d18e316
commit
0a7dfcd55d
@ -5,6 +5,7 @@ import classnames from 'classnames'
|
||||
export default class ButtonGroup extends PureComponent {
|
||||
static propTypes = {
|
||||
defaultActiveButtonIndex: PropTypes.number,
|
||||
noButtonActiveByDefault: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
children: PropTypes.array,
|
||||
className: PropTypes.string,
|
||||
@ -16,7 +17,9 @@ export default class ButtonGroup extends PureComponent {
|
||||
}
|
||||
|
||||
state = {
|
||||
activeButtonIndex: this.props.defaultActiveButtonIndex || 0,
|
||||
activeButtonIndex: this.props.noButtonActiveByDefault
|
||||
? null
|
||||
: this.props.defaultActiveButtonIndex || 0,
|
||||
}
|
||||
|
||||
handleButtonClick (activeButtonIndex) {
|
||||
|
@ -15,7 +15,13 @@ export default class BasicTabContent extends Component {
|
||||
return (
|
||||
<div className="basic-tab-content">
|
||||
<div className="basic-tab-content__title">Suggest gas fee increases</div>
|
||||
<GasPriceButtonGroup {...this.props.gasPriceButtonGroupProps} />
|
||||
<GasPriceButtonGroup
|
||||
{...this.props.gasPriceButtonGroupProps}
|
||||
className="gas-price-button-group"
|
||||
noButtonActiveByDefault={true}
|
||||
showCheck={true}
|
||||
handleGasPriceSelection={(newPrice) => console.log('New Price:', newPrice)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ export default class GasModalPageContainer extends Component {
|
||||
|
||||
renderBasicTabContent () {
|
||||
return (
|
||||
<BasicTabContent gasPriceButtonGroupProps={this.props.gasPriceButtonGroupProps} />
|
||||
<BasicTabContent
|
||||
gasPriceButtonGroupProps={this.props.gasPriceButtonGroupProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -4,45 +4,23 @@ import { hideModal } from '../../../actions'
|
||||
import {
|
||||
setCustomGasPrice,
|
||||
setCustomGasLimit,
|
||||
} from '../../../ducks/custom-gas'
|
||||
} from '../../../ducks/gas.duck'
|
||||
import {
|
||||
getCustomGasPrice,
|
||||
getCustomGasLimit,
|
||||
getRenderableBasicEstimateData,
|
||||
getBasicGasEstimateLoadingStatus,
|
||||
} from '../../../selectors/custom-gas'
|
||||
|
||||
const mockGasPriceButtonGroupProps = {
|
||||
buttonDataLoading: false,
|
||||
className: 'gas-price-button-group',
|
||||
gasButtonInfo: [
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.52',
|
||||
feeInSecondaryCurrency: '0.0048 ETH',
|
||||
timeEstimate: '~ 1 min 0 sec',
|
||||
priceInHexWei: '0xa1b2c3f',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.39',
|
||||
feeInSecondaryCurrency: '0.004 ETH',
|
||||
timeEstimate: '~ 1 min 30 sec',
|
||||
priceInHexWei: '0xa1b2c39',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.30',
|
||||
feeInSecondaryCurrency: '0.00354 ETH',
|
||||
timeEstimate: '~ 2 min 1 sec',
|
||||
priceInHexWei: '0xa1b2c30',
|
||||
},
|
||||
],
|
||||
handleGasPriceSelection: newPrice => console.log('NewPrice: ', newPrice),
|
||||
noButtonActiveByDefault: true,
|
||||
showCheck: true,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
|
||||
return {
|
||||
customGasPrice: getCustomGasPrice(state),
|
||||
customGasLimit: getCustomGasLimit(state),
|
||||
gasPriceButtonGroupProps: mockGasPriceButtonGroupProps,
|
||||
gasPriceButtonGroupProps: {
|
||||
buttonDataLoading,
|
||||
gasButtonInfo: getRenderableBasicEstimateData(state),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +29,7 @@ const mapDispatchToProps = dispatch => {
|
||||
hideModal: () => dispatch(hideModal()),
|
||||
updateCustomGasPrice: (newPrice) => dispatch(setCustomGasPrice(newPrice)),
|
||||
updateCustomGasLimit: (newLimit) => dispatch(setCustomGasLimit(newLimit)),
|
||||
handleGasPriceSelection: newPrice => console.log('NewPrice: ', newPrice),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,11 @@ proxyquire('../gas-modal-page-container.container.js', {
|
||||
'../../../selectors/custom-gas': {
|
||||
getCustomGasPrice: (s) => `mockGasPrice:${s}`,
|
||||
getCustomGasLimit: (s) => `mockGasLimit:${s}`,
|
||||
getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${s}`,
|
||||
getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${s}`,
|
||||
},
|
||||
'../../../actions': actionSpies,
|
||||
'../../../ducks/custom-gas': customGasActionSpies,
|
||||
'../../../ducks/gas.duck': customGasActionSpies,
|
||||
})
|
||||
|
||||
describe('gas-modal-page-container container', () => {
|
||||
@ -37,6 +39,8 @@ describe('gas-modal-page-container container', () => {
|
||||
it('should map the correct properties to props', () => {
|
||||
assert.equal(mapStateToProps('mockState').customGasPrice, 'mockGasPrice:mockState')
|
||||
assert.equal(mapStateToProps('mockState').customGasLimit, 'mockGasLimit:mockState')
|
||||
assert.equal(mapStateToProps('mockState').gasPriceButtonGroupProps.buttonDataLoading, 'mockBasicGasEstimateLoadingStatus:mockState')
|
||||
assert.equal(mapStateToProps('mockState').gasPriceButtonGroupProps.gasButtonInfo, 'mockRenderableBasicEstimateData:mockState')
|
||||
})
|
||||
|
||||
})
|
||||
|
14
ui/app/components/gas-customization/gas.selectors.js
Normal file
14
ui/app/components/gas-customization/gas.selectors.js
Normal file
@ -0,0 +1,14 @@
|
||||
const selectors = {
|
||||
getCurrentBlockTime,
|
||||
getBasicGasEstimateLoadingStatus,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
||||
function getCurrentBlockTime (state) {
|
||||
return state.gas.currentBlockTime
|
||||
}
|
||||
|
||||
function getBasicGasEstimateLoadingStatus (state) {
|
||||
return state.gas.basicEstimateIsLoading
|
||||
}
|
@ -162,6 +162,10 @@ export default class SendTransactionScreen extends PersistentForm {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.props.fetchGasEstimates()
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
const {
|
||||
from: { address },
|
||||
|
@ -36,6 +36,9 @@ import {
|
||||
resetSendState,
|
||||
updateSendErrors,
|
||||
} from '../../ducks/send.duck'
|
||||
import {
|
||||
fetchGasEstimates,
|
||||
} from '../../ducks/gas.duck'
|
||||
import {
|
||||
calcGasTotal,
|
||||
} from './send.utils.js'
|
||||
@ -104,5 +107,6 @@ function mapDispatchToProps (dispatch) {
|
||||
scanQrCode: () => dispatch(showQrScanner(SEND_ROUTE)),
|
||||
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
|
||||
updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)),
|
||||
fetchGasEstimates: () => dispatch(fetchGasEstimates()),
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const propsMethodSpies = {
|
||||
updateSendErrors: sinon.spy(),
|
||||
updateSendTokenBalance: sinon.spy(),
|
||||
resetSendState: sinon.spy(),
|
||||
fetchGasEstimates: sinon.spy(),
|
||||
}
|
||||
const utilsMethodStubs = {
|
||||
getAmountErrorObject: sinon.stub().returns({ amount: 'mockAmountError' }),
|
||||
@ -37,6 +38,7 @@ describe('Send Component', function () {
|
||||
blockGasLimit={'mockBlockGasLimit'}
|
||||
conversionRate={10}
|
||||
editingTransactionId={'mockEditingTransactionId'}
|
||||
fetchGasEstimates={propsMethodSpies.fetchGasEstimates}
|
||||
from={ { address: 'mockAddress', balance: 'mockBalance' } }
|
||||
gasLimit={'mockGasLimit'}
|
||||
gasPrice={'mockGasPrice'}
|
||||
@ -63,6 +65,7 @@ describe('Send Component', function () {
|
||||
utilsMethodStubs.doesAmountErrorRequireUpdate.resetHistory()
|
||||
utilsMethodStubs.getAmountErrorObject.resetHistory()
|
||||
utilsMethodStubs.getGasFeeErrorObject.resetHistory()
|
||||
propsMethodSpies.fetchGasEstimates.resetHistory()
|
||||
propsMethodSpies.updateAndSetGasTotal.resetHistory()
|
||||
propsMethodSpies.updateSendErrors.resetHistory()
|
||||
propsMethodSpies.updateSendTokenBalance.resetHistory()
|
||||
@ -82,6 +85,15 @@ describe('Send Component', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('componentDidMount', () => {
|
||||
it('should call props.fetchGasEstimates', () => {
|
||||
propsMethodSpies.fetchGasEstimates.resetHistory()
|
||||
assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 0)
|
||||
wrapper.instance().componentDidMount()
|
||||
assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('componentWillUnmount', () => {
|
||||
it('should call this.props.resetSendState', () => {
|
||||
propsMethodSpies.resetSendState.resetHistory()
|
||||
|
@ -1,67 +0,0 @@
|
||||
import extend from 'xtend'
|
||||
|
||||
// Actions
|
||||
const SET_CUSTOM_GAS_PRICE = 'metamask/custom-gas/SET_CUSTOM_GAS_PRICE'
|
||||
const SET_CUSTOM_GAS_LIMIT = 'metamask/custom-gas/SET_CUSTOM_GAS_LIMIT'
|
||||
const SET_CUSTOM_GAS_ERRORS = 'metamask/custom-gas/SET_CUSTOM_GAS_ERRORS'
|
||||
const RESET_CUSTOM_GAS_STATE = 'metamask/custom-gas/RESET_CUSTOM_GAS_STATE'
|
||||
|
||||
// TODO: determine if this approach to initState is consistent with conventional ducks pattern
|
||||
const initState = {
|
||||
price: 0,
|
||||
limit: 21000,
|
||||
errors: {},
|
||||
}
|
||||
|
||||
// Reducer
|
||||
export default function reducer ({ customGas: customGasState = initState }, action = {}) {
|
||||
const newState = extend({}, customGasState)
|
||||
|
||||
switch (action.type) {
|
||||
case SET_CUSTOM_GAS_PRICE:
|
||||
return extend(newState, {
|
||||
price: action.value,
|
||||
})
|
||||
case SET_CUSTOM_GAS_LIMIT:
|
||||
return extend(newState, {
|
||||
limit: action.value,
|
||||
})
|
||||
case SET_CUSTOM_GAS_ERRORS:
|
||||
return extend(newState, {
|
||||
errors: {
|
||||
...newState.errors,
|
||||
...action.value,
|
||||
},
|
||||
})
|
||||
case RESET_CUSTOM_GAS_STATE:
|
||||
return extend({}, initState)
|
||||
default:
|
||||
return newState
|
||||
}
|
||||
}
|
||||
|
||||
// Action Creators
|
||||
export function setCustomGasPrice (newPrice) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_PRICE,
|
||||
value: newPrice,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasLimit (newLimit) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_LIMIT,
|
||||
value: newLimit,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasErrors (newErrors) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_ERRORS,
|
||||
value: newErrors,
|
||||
}
|
||||
}
|
||||
|
||||
export function resetCustomGasState () {
|
||||
return { type: RESET_CUSTOM_GAS_STATE }
|
||||
}
|
189
ui/app/ducks/gas.duck.js
Normal file
189
ui/app/ducks/gas.duck.js
Normal file
@ -0,0 +1,189 @@
|
||||
import { clone } from 'ramda'
|
||||
|
||||
// Actions
|
||||
const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED'
|
||||
const BASIC_GAS_ESTIMATE_LOADING_STARTED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_STARTED'
|
||||
const RESET_CUSTOM_GAS_STATE = 'metamask/gas/RESET_CUSTOM_GAS_STATE'
|
||||
const SET_BASIC_GAS_ESTIMATE_DATA = 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA'
|
||||
const SET_CUSTOM_GAS_ERRORS = 'metamask/gas/SET_CUSTOM_GAS_ERRORS'
|
||||
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT'
|
||||
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE'
|
||||
const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL'
|
||||
|
||||
// TODO: determine if this approach to initState is consistent with conventional ducks pattern
|
||||
const initState = {
|
||||
customData: {
|
||||
price: 0,
|
||||
limit: 21000,
|
||||
},
|
||||
basicEstimates: {
|
||||
average: null,
|
||||
fastestWait: null,
|
||||
fastWait: null,
|
||||
fast: null,
|
||||
safeLowWait: null,
|
||||
blockNum: null,
|
||||
avgWait: null,
|
||||
blockTime: null,
|
||||
speed: null,
|
||||
fastest: null,
|
||||
safeLow: null,
|
||||
},
|
||||
basicEstimateIsLoading: true,
|
||||
errors: {},
|
||||
}
|
||||
|
||||
// Reducer
|
||||
export default function reducer ({ gas: gasState = initState }, action = {}) {
|
||||
const newState = clone(gasState)
|
||||
|
||||
switch (action.type) {
|
||||
case BASIC_GAS_ESTIMATE_LOADING_STARTED:
|
||||
return {
|
||||
...newState,
|
||||
basicEstimateIsLoading: true,
|
||||
}
|
||||
case BASIC_GAS_ESTIMATE_LOADING_FINISHED:
|
||||
return {
|
||||
...newState,
|
||||
basicEstimateIsLoading: false,
|
||||
}
|
||||
case SET_BASIC_GAS_ESTIMATE_DATA:
|
||||
return {
|
||||
...newState,
|
||||
basicEstimates: action.value,
|
||||
}
|
||||
case SET_CUSTOM_GAS_PRICE:
|
||||
return {
|
||||
...newState,
|
||||
customData: {
|
||||
...newState.customData,
|
||||
price: action.value,
|
||||
},
|
||||
}
|
||||
case SET_CUSTOM_GAS_LIMIT:
|
||||
return {
|
||||
...newState,
|
||||
customData: {
|
||||
...newState.customData,
|
||||
limit: action.value,
|
||||
},
|
||||
}
|
||||
case SET_CUSTOM_GAS_TOTAL:
|
||||
return {
|
||||
...newState,
|
||||
customData: {
|
||||
...newState.customData,
|
||||
total: action.value,
|
||||
},
|
||||
}
|
||||
case SET_CUSTOM_GAS_ERRORS:
|
||||
return {
|
||||
...newState,
|
||||
errors: {
|
||||
...newState.errors,
|
||||
...action.value,
|
||||
},
|
||||
}
|
||||
case RESET_CUSTOM_GAS_STATE:
|
||||
return clone(initState)
|
||||
default:
|
||||
return newState
|
||||
}
|
||||
}
|
||||
|
||||
// Action Creators
|
||||
export function basicGasEstimatesLoadingStarted () {
|
||||
return {
|
||||
type: BASIC_GAS_ESTIMATE_LOADING_STARTED,
|
||||
}
|
||||
}
|
||||
|
||||
export function basicGasEstimatesLoadingFinished () {
|
||||
return {
|
||||
type: BASIC_GAS_ESTIMATE_LOADING_FINISHED,
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchGasEstimates () {
|
||||
return (dispatch) => {
|
||||
dispatch(basicGasEstimatesLoadingStarted())
|
||||
|
||||
return fetch('https://ethgasstation.info/json/ethgasAPI.json', {
|
||||
'headers': {},
|
||||
'referrer': 'http://ethgasstation.info/json/',
|
||||
'referrerPolicy': 'no-referrer-when-downgrade',
|
||||
'body': null,
|
||||
'method': 'GET',
|
||||
'mode': 'cors'}
|
||||
)
|
||||
.then(r => r.json())
|
||||
.then(({
|
||||
average,
|
||||
avgWait,
|
||||
block_time: blockTime,
|
||||
blockNum,
|
||||
fast,
|
||||
fastest,
|
||||
fastestWait,
|
||||
fastWait,
|
||||
safeLow,
|
||||
safeLowWait,
|
||||
speed,
|
||||
}) => {
|
||||
dispatch(setBasicGasEstimateData({
|
||||
average,
|
||||
avgWait,
|
||||
blockTime,
|
||||
blockNum,
|
||||
fast,
|
||||
fastest,
|
||||
fastestWait,
|
||||
fastWait,
|
||||
safeLow,
|
||||
safeLowWait,
|
||||
speed,
|
||||
}))
|
||||
dispatch(basicGasEstimatesLoadingFinished())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function setBasicGasEstimateData (basicGasEstimateData) {
|
||||
return {
|
||||
type: SET_BASIC_GAS_ESTIMATE_DATA,
|
||||
value: basicGasEstimateData,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasPrice (newPrice) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_PRICE,
|
||||
value: newPrice,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasLimit (newLimit) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_LIMIT,
|
||||
value: newLimit,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasTotal (newTotal) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_TOTAL,
|
||||
value: newTotal,
|
||||
}
|
||||
}
|
||||
|
||||
export function setCustomGasErrors (newErrors) {
|
||||
return {
|
||||
type: SET_CUSTOM_GAS_ERRORS,
|
||||
value: newErrors,
|
||||
}
|
||||
}
|
||||
|
||||
export function resetCustomGasState () {
|
||||
return { type: RESET_CUSTOM_GAS_STATE }
|
||||
}
|
@ -95,7 +95,7 @@ export function formatCurrency (value, currencyCode) {
|
||||
const upperCaseCurrencyCode = currencyCode.toUpperCase()
|
||||
|
||||
return currencies.find(currency => currency.code === upperCaseCurrencyCode)
|
||||
? currencyFormatter.format(Number(value), { code: upperCaseCurrencyCode })
|
||||
? currencyFormatter.format(Number(value), { code: upperCaseCurrencyCode, style: 'currency' })
|
||||
: value
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ const reduceApp = require('./reducers/app')
|
||||
const reduceLocale = require('./reducers/locale')
|
||||
const reduceSend = require('./ducks/send.duck').default
|
||||
import reduceConfirmTransaction from './ducks/confirm-transaction.duck'
|
||||
import reduceCustomGas from './ducks/custom-gas'
|
||||
import reduceGas from './ducks/gas.duck'
|
||||
|
||||
window.METAMASK_CACHED_LOG_STATE = null
|
||||
|
||||
@ -50,7 +50,7 @@ function rootReducer (state, action) {
|
||||
|
||||
state.confirmTransaction = reduceConfirmTransaction(state, action)
|
||||
|
||||
state.customGas = reduceCustomGas(state, action)
|
||||
state.gas = reduceGas(state, action)
|
||||
|
||||
window.METAMASK_CACHED_LOG_STATE = state
|
||||
return state
|
||||
|
@ -1,19 +1,191 @@
|
||||
import { pipe, partialRight } from 'ramda'
|
||||
import {
|
||||
getConversionRate,
|
||||
getGasLimit,
|
||||
} from '../components/send/send.selectors'
|
||||
import {
|
||||
conversionUtil,
|
||||
multiplyCurrencies,
|
||||
} from '../conversion-util'
|
||||
import {
|
||||
getCurrentCurrency,
|
||||
} from '../selectors'
|
||||
import {
|
||||
formatCurrency,
|
||||
} from '../helpers/confirm-transaction/util'
|
||||
import {
|
||||
calcGasTotal,
|
||||
} from '../components/send/send.utils'
|
||||
import { addHexPrefix } from 'ethereumjs-util'
|
||||
|
||||
const selectors = {
|
||||
getCustomGasPrice,
|
||||
getCustomGasLimit,
|
||||
getCustomGasErrors,
|
||||
getCustomGasLimit,
|
||||
getCustomGasPrice,
|
||||
getCustomGasTotal,
|
||||
getRenderableBasicEstimateData,
|
||||
getBasicGasEstimateLoadingStatus,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
|
||||
function getCustomGasPrice (state) {
|
||||
return state.customGas.price
|
||||
function getCustomGasErrors (state) {
|
||||
return state.gas.errors
|
||||
}
|
||||
|
||||
function getCustomGasLimit (state) {
|
||||
return state.customGas.limit
|
||||
return state.gas.customData.limit
|
||||
}
|
||||
|
||||
function getCustomGasErrors (state) {
|
||||
return state.customGas.errors
|
||||
function getCustomGasPrice (state) {
|
||||
return state.gas.customData.price
|
||||
}
|
||||
|
||||
function getCustomGasTotal (state) {
|
||||
return state.gas.customData.total
|
||||
}
|
||||
|
||||
function getBasicGasEstimateLoadingStatus (state) {
|
||||
return state.gas.basicEstimateIsLoading
|
||||
}
|
||||
|
||||
|
||||
function apiEstimateModifiedToGWEI (estimate) {
|
||||
return multiplyCurrencies(estimate, 0.10, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 10,
|
||||
multiplierBase: 10,
|
||||
numberOfDecimals: 9,
|
||||
})
|
||||
}
|
||||
|
||||
function basicPriceEstimateToETHTotal (estimate, gasLimit) {
|
||||
return conversionUtil(calcGasTotal(gasLimit, estimate), {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
fromDenomination: 'GWEI',
|
||||
numberOfDecimals: 9,
|
||||
})
|
||||
}
|
||||
|
||||
function ethTotalToConvertedCurrency (ethTotal, convertedCurrency, conversionRate) {
|
||||
return conversionUtil(ethTotal, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'dec',
|
||||
fromCurrency: 'ETH',
|
||||
toCurrency: convertedCurrency,
|
||||
numberOfDecimals: 2,
|
||||
conversionRate,
|
||||
})
|
||||
}
|
||||
|
||||
function formatETHFee (ethFee) {
|
||||
return ethFee + ' ETH'
|
||||
}
|
||||
|
||||
function getRenderableEthFee (estimate, gasLimit) {
|
||||
return pipe(
|
||||
apiEstimateModifiedToGWEI,
|
||||
partialRight(basicPriceEstimateToETHTotal, [gasLimit]),
|
||||
formatETHFee
|
||||
)(estimate, gasLimit)
|
||||
}
|
||||
|
||||
function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate) {
|
||||
return pipe(
|
||||
apiEstimateModifiedToGWEI,
|
||||
partialRight(basicPriceEstimateToETHTotal, [gasLimit]),
|
||||
partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]),
|
||||
partialRight(formatCurrency, [convertedCurrency])
|
||||
)(estimate, gasLimit, convertedCurrency, conversionRate)
|
||||
}
|
||||
|
||||
function getTimeEstimateInSeconds (blockWaitEstimate, currentBlockTime) {
|
||||
return multiplyCurrencies(blockWaitEstimate, currentBlockTime, {
|
||||
toNumericBase: 'dec',
|
||||
multiplicandBase: 10,
|
||||
multiplierBase: 10,
|
||||
numberOfDecimals: 1,
|
||||
})
|
||||
}
|
||||
|
||||
function formatTimeEstimate (totalSeconds) {
|
||||
const minutes = Math.floor(totalSeconds / 60)
|
||||
const seconds = Math.floor(totalSeconds % 60)
|
||||
const formattedMin = `${minutes ? minutes + ' min' : ''}`
|
||||
const formattedSec = `${seconds ? seconds + ' sec' : ''}`
|
||||
const formattedCombined = formattedMin && formattedSec
|
||||
? `~${formattedMin} ${formattedSec}`
|
||||
: '~' + [formattedMin, formattedSec].find(t => t)
|
||||
|
||||
return formattedCombined
|
||||
}
|
||||
|
||||
function getRenderableTimeEstimate (blockWaitEstimate, currentBlockTime) {
|
||||
return pipe(
|
||||
getTimeEstimateInSeconds,
|
||||
formatTimeEstimate
|
||||
)(blockWaitEstimate, currentBlockTime)
|
||||
}
|
||||
|
||||
function priceEstimateToWei (priceEstimate) {
|
||||
return conversionUtil(priceEstimate, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'hex',
|
||||
fromDenomination: 'GWEI',
|
||||
toDenomination: 'WEI',
|
||||
numberOfDecimals: 9,
|
||||
})
|
||||
}
|
||||
|
||||
function getGasPriceInHexWei (price) {
|
||||
return pipe(
|
||||
apiEstimateModifiedToGWEI,
|
||||
priceEstimateToWei,
|
||||
addHexPrefix
|
||||
)(price)
|
||||
}
|
||||
|
||||
function getRenderableBasicEstimateData (state) {
|
||||
if (getBasicGasEstimateLoadingStatus(state)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const gasLimit = getGasLimit(state)
|
||||
const conversionRate = getConversionRate(state)
|
||||
const currentCurrency = getCurrentCurrency(state)
|
||||
const {
|
||||
gas: {
|
||||
basicEstimates: {
|
||||
safeLow,
|
||||
average,
|
||||
fast,
|
||||
blockTime,
|
||||
safeLowWait,
|
||||
avgWait,
|
||||
fastWait,
|
||||
},
|
||||
},
|
||||
} = state
|
||||
|
||||
return [
|
||||
{
|
||||
feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate),
|
||||
feeInSecondaryCurrency: getRenderableEthFee(fast, gasLimit),
|
||||
timeEstimate: getRenderableTimeEstimate(fastWait, blockTime),
|
||||
priceInHexWei: getGasPriceInHexWei(fast),
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(average, gasLimit, currentCurrency, conversionRate),
|
||||
feeInSecondaryCurrency: getRenderableEthFee(average, gasLimit),
|
||||
timeEstimate: getRenderableTimeEstimate(avgWait, blockTime),
|
||||
priceInHexWei: getGasPriceInHexWei(average),
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate),
|
||||
feeInSecondaryCurrency: getRenderableEthFee(safeLow, gasLimit),
|
||||
timeEstimate: getRenderableTimeEstimate(safeLowWait, blockTime),
|
||||
priceInHexWei: getGasPriceInHexWei(safeLow),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
140
ui/app/selectors/tests/custom-gas.test.js
Normal file
140
ui/app/selectors/tests/custom-gas.test.js
Normal file
@ -0,0 +1,140 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
const {
|
||||
getCustomGasErrors,
|
||||
getCustomGasLimit,
|
||||
getCustomGasPrice,
|
||||
getCustomGasTotal,
|
||||
getRenderableBasicEstimateData,
|
||||
} = proxyquire('../custom-gas', {})
|
||||
|
||||
describe('custom-gas selectors', () => {
|
||||
|
||||
describe('getCustomGasPrice()', () => {
|
||||
it('should return gas.customData.price', () => {
|
||||
const mockState = { gas: { customData: { price: 'mockPrice' } } }
|
||||
assert.equal(getCustomGasPrice(mockState), 'mockPrice')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCustomGasLimit()', () => {
|
||||
it('should return gas.customData.limit', () => {
|
||||
const mockState = { gas: { customData: { limit: 'mockLimit' } } }
|
||||
assert.equal(getCustomGasLimit(mockState), 'mockLimit')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCustomGasTotal()', () => {
|
||||
it('should return gas.customData.total', () => {
|
||||
const mockState = { gas: { customData: { total: 'mockTotal' } } }
|
||||
assert.equal(getCustomGasTotal(mockState), 'mockTotal')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getCustomGasErrors()', () => {
|
||||
it('should return gas.errors', () => {
|
||||
const mockState = { gas: { errors: 'mockErrors' } }
|
||||
assert.equal(getCustomGasErrors(mockState), 'mockErrors')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getRenderableBasicEstimateData()', () => {
|
||||
const tests = [
|
||||
{
|
||||
expectedResult: [
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.05',
|
||||
feeInSecondaryCurrency: '0.00021 ETH',
|
||||
timeEstimate: '~7 sec',
|
||||
priceInHexWei: '0x2540be400',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.03',
|
||||
feeInSecondaryCurrency: '0.000105 ETH',
|
||||
timeEstimate: '~46 sec',
|
||||
priceInHexWei: '0x12a05f200',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.01',
|
||||
feeInSecondaryCurrency: '0.0000525 ETH',
|
||||
timeEstimate: '~1 min 33 sec',
|
||||
priceInHexWei: '0x9502f900',
|
||||
},
|
||||
],
|
||||
mockState: {
|
||||
metamask: {
|
||||
conversionRate: 255.71,
|
||||
currentCurrency: 'usd',
|
||||
send: {
|
||||
gasLimit: '0x5208',
|
||||
},
|
||||
},
|
||||
gas: {
|
||||
basicEstimates: {
|
||||
blockTime: 14.16326530612245,
|
||||
safeLow: 25,
|
||||
safeLowWait: 6.6,
|
||||
average: 50,
|
||||
avgWait: 3.3,
|
||||
fast: 100,
|
||||
fastWait: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedResult: [
|
||||
{
|
||||
feeInPrimaryCurrency: '$1.07',
|
||||
feeInSecondaryCurrency: '0.00042 ETH',
|
||||
timeEstimate: '~14 sec',
|
||||
priceInHexWei: '0x4a817c800',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.54',
|
||||
feeInSecondaryCurrency: '0.00021 ETH',
|
||||
timeEstimate: '~1 min 33 sec',
|
||||
priceInHexWei: '0x2540be400',
|
||||
},
|
||||
{
|
||||
feeInPrimaryCurrency: '$0.27',
|
||||
feeInSecondaryCurrency: '0.000105 ETH',
|
||||
timeEstimate: '~3 min 7 sec',
|
||||
priceInHexWei: '0x12a05f200',
|
||||
},
|
||||
],
|
||||
mockState: {
|
||||
metamask: {
|
||||
conversionRate: 2557.1,
|
||||
currentCurrency: 'usd',
|
||||
send: {
|
||||
gasLimit: '0x5208',
|
||||
},
|
||||
},
|
||||
gas: {
|
||||
basicEstimates: {
|
||||
blockTime: 14.16326530612245,
|
||||
safeLow: 50,
|
||||
safeLowWait: 13.2,
|
||||
average: 100,
|
||||
avgWait: 6.6,
|
||||
fast: 200,
|
||||
fastWait: 1.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
it('should return renderable data about basic estimates', () => {
|
||||
tests.forEach(test => {
|
||||
assert.deepEqual(
|
||||
getRenderableBasicEstimateData(test.mockState),
|
||||
test.expectedResult
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
Loading…
Reference in New Issue
Block a user