mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
3c98be4214
Fiat amounts are now shown inline on token transfers in the transaction list, where possible (i.e. where the conversion rates are known). The logic for this hook is pretty tangled because it's used for so many fundamentally different types of items (eth transactions, token transactions, signature requests). In the future we should split these into different components. The documentation for the `useTokenFiatAmount` hook was updated to make `tokenAmount` optional, but in practice it already worked as expected without the amount being passed in.
157 lines
7.0 KiB
JavaScript
157 lines
7.0 KiB
JavaScript
import { useSelector } from 'react-redux'
|
|
import { getKnownMethodData } from '../selectors/selectors'
|
|
import { getTransactionActionKey, getStatusKey } from '../helpers/utils/transactions.util'
|
|
import { camelCaseToCapitalize } from '../helpers/utils/common.util'
|
|
import { useI18nContext } from './useI18nContext'
|
|
import { useTokenFiatAmount } from './useTokenFiatAmount'
|
|
import { PRIMARY, SECONDARY } from '../helpers/constants/common'
|
|
import { getTokenToAddress } from '../helpers/utils/token-util'
|
|
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'
|
|
import { formatDateWithYearContext, shortenAddress } from '../helpers/utils/util'
|
|
import {
|
|
CONTRACT_INTERACTION_KEY,
|
|
DEPLOY_CONTRACT_ACTION_KEY,
|
|
INCOMING_TRANSACTION,
|
|
TOKEN_METHOD_TRANSFER,
|
|
TOKEN_METHOD_TRANSFER_FROM,
|
|
SEND_ETHER_ACTION_KEY,
|
|
TRANSACTION_CATEGORY_APPROVAL,
|
|
TRANSACTION_CATEGORY_INTERACTION,
|
|
TRANSACTION_CATEGORY_RECEIVE,
|
|
TRANSACTION_CATEGORY_SEND,
|
|
TRANSACTION_CATEGORY_SIGNATURE_REQUEST,
|
|
TOKEN_METHOD_APPROVE,
|
|
PENDING_STATUS_HASH,
|
|
TOKEN_CATEGORY_HASH,
|
|
} from '../helpers/constants/transactions'
|
|
import { useCurrencyDisplay } from './useCurrencyDisplay'
|
|
import { useTokenDisplayValue } from './useTokenDisplayValue'
|
|
import { useTokenData } from './useTokenData'
|
|
import { getTokens } from '../ducks/metamask/metamask'
|
|
|
|
/**
|
|
* @typedef {Object} TransactionDisplayData
|
|
* @property {string} title - primary description of the transaction
|
|
* @property {string} subtitle - supporting text describing the transaction
|
|
* @property {string} category - the transaction category
|
|
* @property {string} primaryCurrency - the currency string to display in the primary position
|
|
* @property {string} [secondaryCurrency] - the currency string to display in the secondary position
|
|
* @property {string} status - the status of the transaction
|
|
* @property {string} senderAddress - the Ethereum address of the sender
|
|
* @property {string} recipientAddress - the Ethereum address of the recipient
|
|
*/
|
|
|
|
/**
|
|
* Get computed values used for displaying transaction data to a user
|
|
*
|
|
* The goal of this method is to perform all of the necessary computation and
|
|
* state access required to take a transactionGroup and derive from it a shape
|
|
* of data that can power all views related to a transaction. Presently the main
|
|
* case is for shared logic between transaction-list-item and transaction-detail-view
|
|
* @param {Object} transactionGroup group of transactions
|
|
* @return {TransactionDisplayData}
|
|
*/
|
|
export function useTransactionDisplayData (transactionGroup) {
|
|
const knownTokens = useSelector(getTokens)
|
|
const t = useI18nContext()
|
|
const { initialTransaction, primaryTransaction } = transactionGroup
|
|
// initialTransaction contains the data we need to derive the primary purpose of this transaction group
|
|
const { transactionCategory } = initialTransaction
|
|
|
|
const { from: senderAddress, to } = initialTransaction.txParams || {}
|
|
|
|
// for smart contract interactions, methodData can be used to derive the name of the action being taken
|
|
const methodData = useSelector((state) => getKnownMethodData(state, initialTransaction?.txParams?.data)) || {}
|
|
|
|
const actionKey = getTransactionActionKey(initialTransaction)
|
|
const status = getStatusKey(primaryTransaction)
|
|
|
|
const primaryValue = primaryTransaction.txParams?.value
|
|
let prefix = '-'
|
|
const date = formatDateWithYearContext(initialTransaction.time || 0)
|
|
let subtitle
|
|
let recipientAddress = to
|
|
|
|
// This value is used to determine whether we should look inside txParams.data
|
|
// to pull out and render token related information
|
|
const isTokenCategory = TOKEN_CATEGORY_HASH[transactionCategory]
|
|
|
|
// these values are always instantiated because they are either
|
|
// used by or returned from hooks. Hooks must be called at the top level,
|
|
// so as an additional safeguard against inappropriately associating token
|
|
// transfers, we pass an additional argument to these hooks that will be
|
|
// false for non-token transactions. This additional argument forces the
|
|
// hook to return null
|
|
const token = isTokenCategory && knownTokens.find((token) => token.address === recipientAddress)
|
|
const tokenData = useTokenData(initialTransaction?.txParams?.data, isTokenCategory)
|
|
const tokenDisplayValue = useTokenDisplayValue(initialTransaction?.txParams?.data, token, isTokenCategory)
|
|
const tokenFiatAmount = useTokenFiatAmount(token?.address, tokenDisplayValue, token?.symbol)
|
|
|
|
let category
|
|
let title
|
|
// There are four types of transaction entries that are currently differentiated in the design
|
|
// 1. (PENDING DESIGN) signature request
|
|
// 2. Send (sendEth sendTokens)
|
|
// 3. Deposit
|
|
// 4. Site interaction
|
|
// 5. Approval
|
|
if (transactionCategory == null) {
|
|
const origin = initialTransaction.msgParams?.origin || initialTransaction.origin
|
|
category = TRANSACTION_CATEGORY_SIGNATURE_REQUEST
|
|
title = t('signatureRequest')
|
|
subtitle = origin || ''
|
|
} else if (transactionCategory === TOKEN_METHOD_APPROVE) {
|
|
category = TRANSACTION_CATEGORY_APPROVAL
|
|
title = t('approve')
|
|
subtitle = initialTransaction.origin
|
|
} else if (transactionCategory === DEPLOY_CONTRACT_ACTION_KEY || transactionCategory === CONTRACT_INTERACTION_KEY) {
|
|
category = TRANSACTION_CATEGORY_INTERACTION
|
|
title = (methodData?.name && camelCaseToCapitalize(methodData.name)) || (actionKey && t(actionKey)) || ''
|
|
subtitle = initialTransaction.origin
|
|
} else if (transactionCategory === INCOMING_TRANSACTION) {
|
|
category = TRANSACTION_CATEGORY_RECEIVE
|
|
title = t('receive')
|
|
prefix = ''
|
|
subtitle = t('fromAddress', [shortenAddress(senderAddress)])
|
|
} else if (transactionCategory === TOKEN_METHOD_TRANSFER_FROM || transactionCategory === TOKEN_METHOD_TRANSFER) {
|
|
category = TRANSACTION_CATEGORY_SEND
|
|
title = t('sendSpecifiedTokens', [token?.symbol || t('token')])
|
|
recipientAddress = getTokenToAddress(tokenData.params)
|
|
subtitle = t('toAddress', [shortenAddress(recipientAddress)])
|
|
} else if (transactionCategory === SEND_ETHER_ACTION_KEY) {
|
|
category = TRANSACTION_CATEGORY_SEND
|
|
title = t('sendETH')
|
|
subtitle = t('toAddress', [shortenAddress(recipientAddress)])
|
|
}
|
|
|
|
const primaryCurrencyPreferences = useUserPreferencedCurrency(PRIMARY)
|
|
const secondaryCurrencyPreferences = useUserPreferencedCurrency(SECONDARY)
|
|
|
|
const [primaryCurrency] = useCurrencyDisplay(primaryValue, {
|
|
prefix,
|
|
displayValue: isTokenCategory ? tokenDisplayValue : undefined,
|
|
suffix: isTokenCategory ? token?.symbol : undefined,
|
|
...primaryCurrencyPreferences,
|
|
})
|
|
|
|
const [secondaryCurrency] = useCurrencyDisplay(primaryValue, {
|
|
prefix,
|
|
displayValue: isTokenCategory ? tokenFiatAmount : undefined,
|
|
hideLabel: isTokenCategory ? true : undefined,
|
|
...secondaryCurrencyPreferences,
|
|
})
|
|
|
|
return {
|
|
title,
|
|
category,
|
|
date,
|
|
subtitle,
|
|
primaryCurrency,
|
|
senderAddress,
|
|
recipientAddress,
|
|
secondaryCurrency: isTokenCategory && !tokenFiatAmount ? undefined : secondaryCurrency,
|
|
status,
|
|
isPending: status in PENDING_STATUS_HASH,
|
|
}
|
|
}
|