From e3ea4f2cd044d48c61b6a35ef206fa942f3b43d1 Mon Sep 17 00:00:00 2001 From: Alex Donesky Date: Thu, 17 Mar 2022 13:35:40 -0500 Subject: [PATCH] Fix issue where we show contract address as recipient when calling safe transfer method on erc721 or erc1155 contracts (#13535) * fix issue where we show contract address as recipient when calling safe transfer method on erc721 or erc1155 contracts * updates function name getTransactionData -> parseStandardTokenTransactionData, and adds documentation --- app/_locales/en/messages.json | 3 ++ app/scripts/metamask-controller.js | 4 +- package.json | 1 + shared/constants/transaction.js | 1 + shared/modules/transaction.utils.js | 52 +++++++++++++++++-- shared/modules/transaction.utils.test.js | 19 +++++++ ...onfirm-page-container-summary.component.js | 4 +- .../confirm-transaction.duck.js | 8 ++- ui/helpers/constants/routes.js | 3 ++ ui/helpers/utils/token-util.js | 8 +-- ui/helpers/utils/transactions.util.js | 21 ++------ ui/helpers/utils/transactions.util.test.js | 20 ------- ui/hooks/useAssetDetails.js | 5 +- ui/hooks/useTokenData.js | 4 +- ui/hooks/useTokenDisplayValue.test.js | 9 ++-- .../confirm-approve/confirm-approve.util.js | 4 +- .../confirm-transaction-base.container.js | 7 ++- .../confirm-transaction-switch.component.js | 5 ++ .../confirm-token-transaction-switch.js | 24 +++++++++ ui/pages/swaps/view-quote/view-quote.js | 4 +- yarn.lock | 2 +- 21 files changed, 142 insertions(+), 66 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a3afaeebb..1997ea777 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -2594,6 +2594,9 @@ "rpcUrl": { "message": "New RPC URL" }, + "safeTransferFrom": { + "message": "Safe Transfer From" + }, "save": { "message": "Save" }, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bd5fbdb5e..2abae9066 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -85,8 +85,8 @@ import { import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; import { getTokenValueParam } from '../../ui/helpers/utils/token-util'; -import { getTransactionData } from '../../ui/helpers/utils/transactions.util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; import ComposableObservableStore from './lib/ComposableObservableStore'; import AccountTracker from './lib/account-tracker'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; @@ -777,7 +777,7 @@ export default class MetamaskController extends EventEmitter { from: userAddress, } = txMeta.txParams; const { chainId } = txMeta; - const transactionData = getTransactionData(data); + const transactionData = parseStandardTokenTransactionData(data); const tokenAmountOrTokenId = getTokenValueParam(transactionData); const { allCollectibles } = this.collectiblesController.state; diff --git a/package.json b/package.json index 65fb850f6..5b17dcc77 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "@metamask/iframe-execution-environment-service": "^0.10.2", "@metamask/jazzicon": "^2.0.0", "@metamask/logo": "^3.1.1", + "@metamask/metamask-eth-abis": "^3.0.0", "@metamask/obs-store": "^5.0.0", "@metamask/post-message-stream": "^4.0.0", "@metamask/providers": "^8.1.1", diff --git a/shared/constants/transaction.js b/shared/constants/transaction.js index b1ea01c39..1e461f491 100644 --- a/shared/constants/transaction.js +++ b/shared/constants/transaction.js @@ -48,6 +48,7 @@ export const TRANSACTION_TYPES = { RETRY: 'retry', TOKEN_METHOD_TRANSFER: 'transfer', TOKEN_METHOD_TRANSFER_FROM: 'transferfrom', + TOKEN_METHOD_SAFE_TRANSFER_FROM: 'safetransferfrom', TOKEN_METHOD_APPROVE: 'approve', INCOMING: 'incoming', SIMPLE_SEND: 'simpleSend', diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js index e79e569c3..08adb46e3 100644 --- a/shared/modules/transaction.utils.js +++ b/shared/modules/transaction.utils.js @@ -1,6 +1,6 @@ import { isHexString } from 'ethereumjs-util'; import { ethers } from 'ethers'; -import abi from 'human-standard-token-abi'; +import { abiERC721, abiERC20, abiERC1155 } from '@metamask/metamask-eth-abis'; import log from 'loglevel'; import { TOKEN_STANDARDS } from '../../ui/helpers/constants/common'; import { ASSET_TYPES, TRANSACTION_TYPES } from '../constants/transaction'; @@ -19,7 +19,22 @@ import { isEqualCaseInsensitive } from './string-utils'; * code */ -const hstInterface = new ethers.utils.Interface(abi); +/** + * @typedef EthersContractCall + * @type object + * @property {any[]} args - The args/params to the function call. + * An array-like object with numerical and string indices. + * @property {string} name - The name of the function. + * @property {string} signature - The function signature. + * @property {string} sighash - The function signature hash. + * @property {EthersBigNumber} value - The ETH value associated with the call. + * @property {FunctionFragment} functionFragment - The Ethers function fragment + * representation of the function. + */ + +const erc20Interface = new ethers.utils.Interface(abiERC20); +const erc721Interface = new ethers.utils.Interface(abiERC721); +const erc1155Interface = new ethers.utils.Interface(abiERC1155); export function transactionMatchesNetwork(transaction, chainId, networkId) { if (typeof transaction.chainId !== 'undefined') { @@ -83,6 +98,36 @@ export function txParamsAreDappSuggested(transaction) { ); } +/** + * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155. + * The data will decode correctly if the transaction is an interaction with a contract that matches one of these + * contract standards + * + * @param data - encoded transaction data + * @returns {EthersContractCall | undefined} + */ +export function parseStandardTokenTransactionData(data) { + try { + return erc20Interface.parseTransaction({ data }); + } catch { + // ignore and next try to parse with erc721 ABI + } + + try { + return erc721Interface.parseTransaction({ data }); + } catch { + // ignore and next try to parse with erc1155 ABI + } + + try { + return erc1155Interface.parseTransaction({ data }); + } catch { + // ignore and return undefined + } + + return undefined; +} + /** * Determines the type of the transaction by analyzing the txParams. * This method will return one of the types defined in shared/constants/transactions @@ -98,7 +143,7 @@ export async function determineTransactionType(txParams, query) { const { data, to } = txParams; let name; try { - name = data && hstInterface.parseTransaction({ data }).name; + ({ name } = data && parseStandardTokenTransactionData(data)); } catch (error) { log.debug('Failed to parse transaction data.', error, data); } @@ -107,6 +152,7 @@ export async function determineTransactionType(txParams, query) { TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ].find((methodName) => isEqualCaseInsensitive(methodName, name)); let result; diff --git a/shared/modules/transaction.utils.test.js b/shared/modules/transaction.utils.test.js index f6d816160..fba8c7827 100644 --- a/shared/modules/transaction.utils.test.js +++ b/shared/modules/transaction.utils.test.js @@ -5,9 +5,28 @@ import { determineTransactionType, isEIP1559Transaction, isLegacyTransaction, + parseStandardTokenTransactionData, } from './transaction.utils'; describe('Transaction.utils', function () { + describe('parseStandardTokenTransactionData', () => { + it('should return token data', () => { + const tokenData = parseStandardTokenTransactionData( + '0xa9059cbb00000000000000000000000050a9d56c2b8ba9a5c7f2c08c3d26e0499f23a7060000000000000000000000000000000000000000000000000000000000004e20', + ); + expect(tokenData).toStrictEqual(expect.anything()); + const { name, args } = tokenData; + expect(name).toStrictEqual(TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER); + const to = args._to; + const value = args._value.toString(); + expect(to).toStrictEqual('0x50A9D56C2B8BA9A5c7f2C08C3d26E0499F23a706'); + expect(value).toStrictEqual('20000'); + }); + + it('should not throw errors when called without arguments', () => { + expect(() => parseStandardTokenTransactionData()).not.toThrow(); + }); + }); describe('isEIP1559Transaction', function () { it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => { expect( diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js index 7da4ddeba..218fc99a7 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js @@ -38,6 +38,7 @@ const ConfirmPageContainerSummary = (props) => { TRANSACTION_TYPES.CONTRACT_INTERACTION, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ]; const isContractTypeTransaction = contractInitiatedTransactionType.includes( transactionType, @@ -49,7 +50,8 @@ const ConfirmPageContainerSummary = (props) => { // type of contract interaction it is passed as toAddress contractAddress = transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER || - transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM + transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM || + transactionType === TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM ? tokenAddress : toAddress; } diff --git a/ui/ducks/confirm-transaction/confirm-transaction.duck.js b/ui/ducks/confirm-transaction/confirm-transaction.duck.js index c05050f28..b16d60312 100644 --- a/ui/ducks/confirm-transaction/confirm-transaction.duck.js +++ b/ui/ducks/confirm-transaction/confirm-transaction.duck.js @@ -13,14 +13,12 @@ import { addEth, } from '../../helpers/utils/confirm-tx.util'; -import { - getTransactionData, - sumHexes, -} from '../../helpers/utils/transactions.util'; +import { sumHexes } from '../../helpers/utils/transactions.util'; import { conversionUtil } from '../../../shared/modules/conversion.utils'; import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; // Actions const createActionType = (action) => `metamask/confirm-transaction/${action}`; @@ -285,7 +283,7 @@ export function setTransactionToConfirm(transactionId) { if (txParams.data) { const { to: tokenAddress, data } = txParams; - const tokenData = getTransactionData(data); + const tokenData = parseStandardTokenTransactionData(data); const tokens = getTokens(state); const currentToken = tokens?.find(({ address }) => isEqualCaseInsensitive(tokenAddress, address), diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js index 83d659dbf..519e0a098 100644 --- a/ui/helpers/constants/routes.js +++ b/ui/helpers/constants/routes.js @@ -88,6 +88,7 @@ const CONFIRM_SEND_TOKEN_PATH = '/send-token'; const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract'; const CONFIRM_APPROVE_PATH = '/approve'; const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from'; +const CONFIRM_SAFE_TRANSFER_FROM_PATH = '/safe-transfer-from'; const CONFIRM_TOKEN_METHOD_PATH = '/token-method'; const SIGNATURE_REQUEST_PATH = '/signature-request'; const DECRYPT_MESSAGE_REQUEST_PATH = '/decrypt-message-request'; @@ -140,6 +141,7 @@ const PATH_NAME_MAP = { [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_DEPLOY_CONTRACT_PATH}`]: 'Confirm Deploy Contract Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_APPROVE_PATH}`]: 'Confirm Approve Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_TRANSFER_FROM_PATH}`]: 'Confirm Transfer From Transaction Page', + [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_SAFE_TRANSFER_FROM_PATH}`]: 'Confirm Safe Transfer From Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${SIGNATURE_REQUEST_PATH}`]: 'Signature Request Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${DECRYPT_MESSAGE_REQUEST_PATH}`]: 'Decrypt Message Request Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${ENCRYPTION_PUBLIC_KEY_REQUEST_PATH}`]: 'Encryption Public Key Request Page', @@ -200,6 +202,7 @@ export { CONFIRM_DEPLOY_CONTRACT_PATH, CONFIRM_APPROVE_PATH, CONFIRM_TRANSFER_FROM_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, DECRYPT_MESSAGE_REQUEST_PATH, diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js index a75db6da6..b934ed176 100644 --- a/ui/helpers/utils/token-util.js +++ b/ui/helpers/utils/token-util.js @@ -7,15 +7,14 @@ import { import { getTokenStandardAndDetails } from '../../store/actions'; import { ERC1155, ERC721 } from '../constants/common'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import * as util from './util'; import { formatCurrency } from './confirm-tx.util'; -import { getTransactionData } from './transactions.util'; const DEFAULT_SYMBOL = ''; async function getSymbolFromContract(tokenAddress) { const token = util.getContractAtAddress(tokenAddress); - try { const result = await token.symbol(); return result[0]; @@ -136,7 +135,8 @@ export function calcTokenValue(value, decimals) { * @returns {string | undefined} A lowercase address string. */ export function getTokenAddressParam(tokenData = {}) { - const value = tokenData?.args?._to || tokenData?.args?.[0]; + const value = + tokenData?.args?._to || tokenData?.args?.to || tokenData?.args?.[0]; return value?.toString().toLowerCase(); } @@ -223,7 +223,7 @@ export async function getAssetDetails( transactionData, existingCollectibles, ) { - const tokenData = getTransactionData(transactionData); + const tokenData = parseStandardTokenTransactionData(transactionData); if (!tokenData) { throw new Error('Unable to detect valid token data'); } diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js index 6bdf6f548..25a38593f 100644 --- a/ui/helpers/utils/transactions.util.js +++ b/ui/helpers/utils/transactions.util.js @@ -1,6 +1,4 @@ import { MethodRegistry } from 'eth-method-registry'; -import abi from 'human-standard-token-abi'; -import { ethers } from 'ethers'; import log from 'loglevel'; import { addHexPrefix } from '../../../app/scripts/lib/util'; @@ -14,8 +12,6 @@ import { addCurrencies } from '../../../shared/modules/conversion.utils'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import fetchWithCache from './fetch-with-cache'; -const hstInterface = new ethers.utils.Interface(abi); - /** * @typedef EthersContractCall * @type object @@ -29,19 +25,6 @@ const hstInterface = new ethers.utils.Interface(abi); * representation of the function. */ -/** - * @param data - * @returns {EthersContractCall | undefined} - */ -export function getTransactionData(data) { - try { - return hstInterface.parseTransaction({ data }); - } catch (error) { - log.debug('Failed to parse transaction data.', error, data); - return undefined; - } -} - async function getMethodFrom4Byte(fourBytePrefix) { const fourByteResponse = await fetchWithCache( `https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, @@ -122,6 +105,7 @@ export function isTokenMethodAction(type) { TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ].includes(type); } @@ -215,6 +199,9 @@ export function getTransactionTypeTitle(t, type, nativeCurrency = 'ETH') { case TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM: { return t('transferFrom'); } + case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: { + return t('safeTransferFrom'); + } case TRANSACTION_TYPES.TOKEN_METHOD_APPROVE: { return t('approve'); } diff --git a/ui/helpers/utils/transactions.util.test.js b/ui/helpers/utils/transactions.util.test.js index ce5225076..591e46d07 100644 --- a/ui/helpers/utils/transactions.util.test.js +++ b/ui/helpers/utils/transactions.util.test.js @@ -1,5 +1,4 @@ import { - TRANSACTION_TYPES, TRANSACTION_GROUP_STATUSES, TRANSACTION_STATUSES, TRANSACTION_ENVELOPE_TYPES, @@ -7,25 +6,6 @@ import { import * as utils from './transactions.util'; describe('Transactions utils', () => { - describe('getTransactionData', () => { - it('should return token data', () => { - const tokenData = utils.getTransactionData( - '0xa9059cbb00000000000000000000000050a9d56c2b8ba9a5c7f2c08c3d26e0499f23a7060000000000000000000000000000000000000000000000000000000000004e20', - ); - expect(tokenData).toStrictEqual(expect.anything()); - const { name, args } = tokenData; - expect(name).toStrictEqual(TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER); - const to = args._to; - const value = args._value.toString(); - expect(to).toStrictEqual('0x50A9D56C2B8BA9A5c7f2C08C3d26E0499F23a706'); - expect(value).toStrictEqual('20000'); - }); - - it('should not throw errors when called without arguments', () => { - expect(() => utils.getTransactionData()).not.toThrow(); - }); - }); - describe('getStatusKey', () => { it('should return the correct status', () => { const tests = [ diff --git a/ui/hooks/useAssetDetails.js b/ui/hooks/useAssetDetails.js index ac12ed494..ce9ef30ba 100644 --- a/ui/hooks/useAssetDetails.js +++ b/ui/hooks/useAssetDetails.js @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; import { getCollectibles, getTokens } from '../ducks/metamask/metamask'; import { ERC1155, ERC721, ERC20 } from '../helpers/constants/common'; import { @@ -8,14 +9,12 @@ import { getTokenAddressParam, getTokenValueParam, } from '../helpers/utils/token-util'; -import { getTransactionData } from '../helpers/utils/transactions.util'; import { getTokenList } from '../selectors'; import { hideLoadingIndication, showLoadingIndication } from '../store/actions'; import { usePrevious } from './usePrevious'; export function useAssetDetails(tokenAddress, userAddress, transactionData) { const dispatch = useDispatch(); - // state selectors const tokens = useSelector(getTokens); const collectibles = useSelector(getCollectibles); @@ -84,7 +83,7 @@ export function useAssetDetails(tokenAddress, userAddress, transactionData) { balance, decimals: currentAssetDecimals, } = currentAsset; - const tokenData = getTransactionData(transactionData); + const tokenData = parseStandardTokenTransactionData(transactionData); assetStandard = standard; assetAddress = tokenAddress; tokenSymbol = symbol; diff --git a/ui/hooks/useTokenData.js b/ui/hooks/useTokenData.js index d1d7e1ffe..654bb50ff 100644 --- a/ui/hooks/useTokenData.js +++ b/ui/hooks/useTokenData.js @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { getTransactionData } from '../helpers/utils/transactions.util'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; /** * useTokenData @@ -19,6 +19,6 @@ export function useTokenData(transactionData, isTokenTransaction = true) { if (!isTokenTransaction || !transactionData) { return null; } - return getTransactionData(transactionData); + return parseStandardTokenTransactionData(transactionData); }, [isTokenTransaction, transactionData]); } diff --git a/ui/hooks/useTokenDisplayValue.test.js b/ui/hooks/useTokenDisplayValue.test.js index 9be126f81..26e2a4374 100644 --- a/ui/hooks/useTokenDisplayValue.test.js +++ b/ui/hooks/useTokenDisplayValue.test.js @@ -1,7 +1,7 @@ import { renderHook } from '@testing-library/react-hooks'; import sinon from 'sinon'; import * as tokenUtil from '../helpers/utils/token-util'; -import * as txUtil from '../helpers/utils/transactions.util'; +import * as txUtil from '../../shared/modules/transaction.utils'; import { useTokenDisplayValue } from './useTokenDisplayValue'; const tests = [ @@ -122,9 +122,12 @@ describe('useTokenDisplayValue', () => { describe(`when input is decimals: ${token.decimals} and value: ${tokenValue}`, () => { it(`should return ${displayValue} as displayValue`, () => { const getTokenValueStub = sinon.stub(tokenUtil, 'getTokenValueParam'); - const getTokenDataStub = sinon.stub(txUtil, 'getTransactionData'); + const parseStandardTokenTransactionDataStub = sinon.stub( + txUtil, + 'parseStandardTokenTransactionData', + ); - getTokenDataStub.callsFake(() => tokenData); + parseStandardTokenTransactionDataStub.callsFake(() => tokenData); getTokenValueStub.callsFake(() => tokenValue); const { result } = renderHook(() => diff --git a/ui/pages/confirm-approve/confirm-approve.util.js b/ui/pages/confirm-approve/confirm-approve.util.js index 7bc3ea550..022dc108a 100644 --- a/ui/pages/confirm-approve/confirm-approve.util.js +++ b/ui/pages/confirm-approve/confirm-approve.util.js @@ -1,16 +1,16 @@ import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import { decimalToHex } from '../../helpers/utils/conversions.util'; import { calcTokenValue, getTokenAddressParam, } from '../../helpers/utils/token-util'; -import { getTransactionData } from '../../helpers/utils/transactions.util'; export function getCustomTxParamsData( data, { customPermissionAmount, decimals }, ) { - const tokenData = getTransactionData(data); + const tokenData = parseStandardTokenTransactionData(data); if (!tokenData) { throw new Error('Invalid data'); diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index e5c325242..e375a3c96 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -43,6 +43,7 @@ import { } from '../../ducks/metamask/metamask'; import { + parseStandardTokenTransactionData, transactionMatchesNetwork, txParamsAreDappSuggested, } from '../../../shared/modules/transaction.utils'; @@ -52,6 +53,7 @@ import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { getTokenAddressParam } from '../../helpers/utils/token-util'; import ConfirmTransactionBase from './confirm-transaction-base.component'; let customNonceValue = ''; @@ -104,9 +106,12 @@ const mapStateToProps = (state, ownProps) => { } = (transaction && transaction.txParams) || txParams; const accounts = getMetaMaskAccounts(state); + const transactionData = parseStandardTokenTransactionData(data); + const tokenToAddress = getTokenAddressParam(transactionData); + const { balance } = accounts[fromAddress]; const { name: fromName } = identities[fromAddress]; - const toAddress = propsToAddress || txParamsToAddress; + const toAddress = propsToAddress || tokenToAddress || txParamsToAddress; const tokenList = getTokenList(state); const useTokenDetection = getUseTokenDetection(state); diff --git a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js index 7e9882d65..db58cc640 100644 --- a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js +++ b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js @@ -13,6 +13,7 @@ import { SIGNATURE_REQUEST_PATH, DECRYPT_MESSAGE_REQUEST_PATH, ENCRYPTION_PUBLIC_KEY_REQUEST_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, } from '../../helpers/constants/routes'; import { MESSAGE_TYPE } from '../../../shared/constants/app'; import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; @@ -50,6 +51,10 @@ export default class ConfirmTransactionSwitch extends Component { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TRANSFER_FROM_PATH}`; return ; } + case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: { + const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SAFE_TRANSFER_FROM_PATH}`; + return ; + } default: { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TOKEN_METHOD_PATH}`; return ; diff --git a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js index ea06c0d0b..036f2b149 100644 --- a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js +++ b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js @@ -4,6 +4,7 @@ import { useSelector } from 'react-redux'; import { Switch, Route } from 'react-router-dom'; import { CONFIRM_APPROVE_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, CONFIRM_SEND_TOKEN_PATH, CONFIRM_TRANSACTION_ROUTE, CONFIRM_TRANSFER_FROM_PATH, @@ -88,6 +89,29 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) { /> )} /> + ( + + )} + />