From 748801f4179d353959f40049cf6ca27851eebd0e Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Tue, 18 Jun 2019 09:47:14 -0230 Subject: 4byte fallback (#6551) * Adds 4byte registry fallback to getMethodData() (#6435) * Adds fetchWithCache to guard against unnecessary API calls * Add custom fetch wrapper with abort on timeout * Use opts and cacheRefreshTime in fetch-with-cache util * Use custom fetch wrapper with timeout for fetch-with-cache * Improve contract method data fetching (#6623) * Remove async call from getTransactionActionKey() * Stop blocking confirm screen rendering on method data loading, and base screen route on transactionCategory * Remove use of withMethodData, fix use of knownMethodData, in relation to transaction-list-item.component * Load data contract method data progressively, making it non-blocking; requires simplifying conf-tx-base lifecycle logic. * Allow editing of gas price while loading on the confirm screen. * Fix transactionAction component and its unit tests. * Fix confirm transaction components for cases of route transitions within metamask. * Only call toString on id if truthy in getNavigateTxData() * Fix knownMethodData retrieval and data fetching from fourbyte --- ui/app/helpers/utils/transactions.util.js | 99 +++++++++++++++++-------------- 1 file changed, 56 insertions(+), 43 deletions(-) (limited to 'ui/app/helpers/utils/transactions.util.js') diff --git a/ui/app/helpers/utils/transactions.util.js b/ui/app/helpers/utils/transactions.util.js index 99ccc3478..c84053ec7 100644 --- a/ui/app/helpers/utils/transactions.util.js +++ b/ui/app/helpers/utils/transactions.util.js @@ -7,7 +7,7 @@ import { TRANSACTION_STATUS_CONFIRMED, } from '../../../../app/scripts/controllers/transactions/enums' import prefixForNetwork from '../../../lib/etherscan-prefix-for-network' - +import fetchWithCache from './fetch-with-cache' import { TOKEN_METHOD_TRANSFER, @@ -32,39 +32,57 @@ export function getTokenData (data = '') { return abiDecoder.decodeMethod(data) } +async function getMethodFrom4Byte (fourBytePrefix) { + const fourByteResponse = (await fetchWithCache(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, { + referrerPolicy: 'no-referrer-when-downgrade', + body: null, + method: 'GET', + mode: 'cors', + })) + + const fourByteJSON = await fourByteResponse.json() + + if (fourByteJSON.count === 1) { + return fourByteJSON.results[0].text_signature + } else { + return null + } +} + const registry = new MethodRegistry({ provider: global.ethereumProvider }) /** - * Attempts to return the method data from the MethodRegistry library, if the method exists in the - * registry. Otherwise, returns an empty object. - * @param {string} data - The hex data (@code txParams.data) of a transaction + * Attempts to return the method data from the MethodRegistry library, the message registry library and the token abi, in that order of preference + * @param {string} fourBytePrefix - The prefix from the method code associated with the data * @returns {Object} */ - export async function getMethodData (data = '') { - const prefixedData = ethUtil.addHexPrefix(data) - const fourBytePrefix = prefixedData.slice(0, 10) - - try { - const sig = await registry.lookup(fourBytePrefix) - - if (!sig) { - return {} - } - - const parsedResult = registry.parse(sig) - - return { - name: parsedResult.name, - params: parsedResult.args, - } - } catch (error) { - log.error(error) - const contractData = getTokenData(data) - const { name } = contractData || {} - return { name } +export async function getMethodDataAsync (fourBytePrefix) { + try { + const fourByteSig = getMethodFrom4Byte(fourBytePrefix).catch((e) => { + log.error(e) + return null + }) + + let sig = await registry.lookup(fourBytePrefix) + + if (!sig) { + sig = await fourByteSig } + if (!sig) { + return {} + } + + const parsedResult = registry.parse(sig) + return { + name: parsedResult.name, + params: parsedResult.args, + } + } catch (error) { + log.error(error) + return {} + } } export function isConfirmDeployContract (txData = {}) { @@ -87,11 +105,10 @@ export function getFourBytePrefix (data = '') { /** * Returns the action of a transaction as a key to be passed into the translator. * @param {Object} transaction - txData object - * @param {Object} methodData - Data returned from eth-method-registry * @returns {string|undefined} */ -export async function getTransactionActionKey (transaction, methodData) { - const { txParams: { data, to } = {}, msgParams, type } = transaction +export function getTransactionActionKey (transaction) { + const { msgParams, type, transactionCategory } = transaction if (type === 'cancel') { return CANCEL_ATTEMPT_ACTION_KEY @@ -105,27 +122,23 @@ export async function getTransactionActionKey (transaction, methodData) { return DEPLOY_CONTRACT_ACTION_KEY } - if (data) { - const toSmartContract = await isSmartContractAddress(to) - - if (!toSmartContract) { - return SEND_ETHER_ACTION_KEY - } - - const { name } = methodData - const methodName = name && name.toLowerCase() - - if (!methodName) { - return CONTRACT_INTERACTION_KEY - } + const isTokenAction = [ + TOKEN_METHOD_TRANSFER, + TOKEN_METHOD_APPROVE, + TOKEN_METHOD_TRANSFER_FROM, + ].find(actionName => actionName === transactionCategory) + const isNonTokenSmartContract = transactionCategory === CONTRACT_INTERACTION_KEY - switch (methodName) { + if (isTokenAction || isNonTokenSmartContract) { + switch (transactionCategory) { case TOKEN_METHOD_TRANSFER: return SEND_TOKEN_ACTION_KEY case TOKEN_METHOD_APPROVE: return APPROVE_ACTION_KEY case TOKEN_METHOD_TRANSFER_FROM: return TRANSFER_FROM_ACTION_KEY + case CONTRACT_INTERACTION_KEY: + return CONTRACT_INTERACTION_KEY default: return undefined } -- cgit