diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 2faf8220b..fa162c21f 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -19,6 +19,7 @@ class PreferencesController { * @property {boolean} store.useBlockie The users preference for blockie identicons within the UI * @property {object} store.featureFlags A key-boolean map, where keys refer to features and booleans to whether the * user wishes to see that feature + * @property {object} store.knownMethodData Contains all data methods known by the user * @property {string} store.currentLocale The preferred language locale key * @property {string} store.selectedAddress A hex string that matches the currently selected address in the app * @@ -36,6 +37,7 @@ class PreferencesController { betaUI: true, skipAnnounceBetaUI: true, }, + knownMethodData: {}, currentLocale: opts.initLangCode, identities: {}, lostIdentities: {}, @@ -98,6 +100,18 @@ class PreferencesController { this.store.updateState({ suggestedTokens: suggested }) } + /** + * Add new methodData to state, to avoid requesting this information again through Infura + * + * @param {string} fourBytePrefix Four-byte method signature + * @param {string} methodData Corresponding data method + */ + addKnownMethodData (fourBytePrefix, methodData) { + const knownMethodData = this.store.getState().knownMethodData + knownMethodData[fourBytePrefix] = methodData + this.store.updateState({ knownMethodData }) + } + /** * RPC engine middleware for requesting new asset added * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c7e9cfcc7..ea57582a0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -425,6 +425,7 @@ module.exports = class MetamaskController extends EventEmitter { setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController), setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController), setPreference: nodeify(preferencesController.setPreference, preferencesController), + addKnownMethodData: nodeify(preferencesController.addKnownMethodData, preferencesController), // BlacklistController whitelistPhishingDomain: this.whitelistPhishingDomain.bind(this), diff --git a/ui/app/actions.js b/ui/app/actions.js index 5a4389d67..7cc88e2b3 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -236,6 +236,7 @@ var actions = { removeToken, updateTokens, removeSuggestedTokens, + addKnownMethodData, UPDATE_TOKENS: 'UPDATE_TOKENS', setRpcTarget: setRpcTarget, delRpcTarget: delRpcTarget, @@ -1490,7 +1491,6 @@ const backgroundSetLocked = () => { if (error) { return reject(error) } - resolve() }) }) @@ -1721,6 +1721,12 @@ function removeSuggestedTokens () { } } +function addKnownMethodData (fourBytePrefix, methodData) { + return (dispatch) => { + background.addKnownMethodData(fourBytePrefix, methodData) + } +} + function updateTokens (newTokens) { return { type: actions.UPDATE_TOKENS, diff --git a/ui/app/components/transaction-list-item/transaction-list-item.container.js b/ui/app/components/transaction-list-item/transaction-list-item.container.js index e08d3232f..45777057c 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.container.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.container.js @@ -3,7 +3,7 @@ import { withRouter } from 'react-router-dom' import { compose } from 'recompose' import withMethodData from '../../higher-order-components/with-method-data' import TransactionListItem from './transaction-list-item.component' -import { setSelectedToken, showModal, showSidebar } from '../../actions' +import { setSelectedToken, showModal, showSidebar, addKnownMethodData } from '../../actions' import { hexToDecimal } from '../../helpers/conversions.util' import { getTokenData } from '../../helpers/transactions.util' import { increaseLastGasPrice } from '../../helpers/confirm-transaction/util' @@ -15,11 +15,19 @@ import { setCustomGasLimit, } from '../../ducks/gas.duck' +const mapStateToProps = state => { + const { metamask: { knownMethodData } } = state + return { + knownMethodData, + } +} + const mapDispatchToProps = dispatch => { return { fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()), fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)), setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)), + addKnownMethodData: (fourBytePrefix, methodData) => dispatch(addKnownMethodData(fourBytePrefix, methodData)), retryTransaction: (transaction, gasPrice) => { dispatch(setCustomGasPriceForRetry(gasPrice || transaction.txParams.gasPrice)) dispatch(setCustomGasLimit(transaction.txParams.gas)) @@ -64,6 +72,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { export default compose( withRouter, - connect(null, mapDispatchToProps, mergeProps), + connect(mapStateToProps, mapDispatchToProps, mergeProps), withMethodData, )(TransactionListItem) diff --git a/ui/app/helpers/transactions.util.js b/ui/app/helpers/transactions.util.js index 0f1ed70a3..d5b7f4958 100644 --- a/ui/app/helpers/transactions.util.js +++ b/ui/app/helpers/transactions.util.js @@ -59,6 +59,18 @@ export function isConfirmDeployContract (txData = {}) { return !txParams.to } +/** + * Returns four-byte method signature from data + * + * @param {string} data - The hex data (@code txParams.data) of a transaction + * @returns {string} - The four-byte method signature + */ +export function getFourBytePrefix (data = '') { + const prefixedData = ethUtil.addHexPrefix(data) + const fourBytePrefix = prefixedData.slice(0, 10) + return fourBytePrefix +} + /** * Returns the action of a transaction as a key to be passed into the translator. * @param {Object} transaction - txData object diff --git a/ui/app/higher-order-components/with-method-data/with-method-data.component.js b/ui/app/higher-order-components/with-method-data/with-method-data.component.js index fed7d9865..08b9083e1 100644 --- a/ui/app/higher-order-components/with-method-data/with-method-data.component.js +++ b/ui/app/higher-order-components/with-method-data/with-method-data.component.js @@ -1,15 +1,18 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' -import { getMethodData } from '../../helpers/transactions.util' +import { getMethodData, getFourBytePrefix } from '../../helpers/transactions.util' export default function withMethodData (WrappedComponent) { return class MethodDataWrappedComponent extends PureComponent { static propTypes = { transaction: PropTypes.object, + knownMethodData: PropTypes.object, + addKnownMethodData: PropTypes.func, } static defaultProps = { transaction: {}, + knownMethodData: {}, } state = { @@ -23,12 +26,22 @@ export default function withMethodData (WrappedComponent) { } async fetchMethodData () { - const { transaction } = this.props + const { transaction, knownMethodData, addKnownMethodData } = this.props const { txParams: { data = '' } = {} } = transaction if (data) { try { - const methodData = await getMethodData(data) + let methodData + const fourBytePrefix = getFourBytePrefix(data) + if (fourBytePrefix in knownMethodData) { + methodData = knownMethodData[fourBytePrefix] + } else { + methodData = await getMethodData(data) + if (!Object.entries(methodData).length === 0) { + addKnownMethodData(fourBytePrefix, methodData) + } + } + this.setState({ methodData, done: true }) } catch (error) { this.setState({ done: true, error }) diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 302d1627a..97052ab87 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -54,6 +54,7 @@ function reduceMetamask (state, action) { preferences: { useNativeCurrencyAsPrimaryCurrency: true, }, + knownMethodData: {}, }, state.metamask) switch (action.type) {