mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
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
This commit is contained in:
parent
41974cec3b
commit
e3ea4f2cd0
3
app/_locales/en/messages.json
generated
3
app/_locales/en/messages.json
generated
@ -2594,6 +2594,9 @@
|
|||||||
"rpcUrl": {
|
"rpcUrl": {
|
||||||
"message": "New RPC URL"
|
"message": "New RPC URL"
|
||||||
},
|
},
|
||||||
|
"safeTransferFrom": {
|
||||||
|
"message": "Safe Transfer From"
|
||||||
|
},
|
||||||
"save": {
|
"save": {
|
||||||
"message": "Save"
|
"message": "Save"
|
||||||
},
|
},
|
||||||
|
@ -85,8 +85,8 @@ import {
|
|||||||
|
|
||||||
import { hexToDecimal } from '../../ui/helpers/utils/conversions.util';
|
import { hexToDecimal } from '../../ui/helpers/utils/conversions.util';
|
||||||
import { getTokenValueParam } from '../../ui/helpers/utils/token-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 { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils';
|
||||||
import ComposableObservableStore from './lib/ComposableObservableStore';
|
import ComposableObservableStore from './lib/ComposableObservableStore';
|
||||||
import AccountTracker from './lib/account-tracker';
|
import AccountTracker from './lib/account-tracker';
|
||||||
import createLoggerMiddleware from './lib/createLoggerMiddleware';
|
import createLoggerMiddleware from './lib/createLoggerMiddleware';
|
||||||
@ -777,7 +777,7 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
from: userAddress,
|
from: userAddress,
|
||||||
} = txMeta.txParams;
|
} = txMeta.txParams;
|
||||||
const { chainId } = txMeta;
|
const { chainId } = txMeta;
|
||||||
const transactionData = getTransactionData(data);
|
const transactionData = parseStandardTokenTransactionData(data);
|
||||||
const tokenAmountOrTokenId = getTokenValueParam(transactionData);
|
const tokenAmountOrTokenId = getTokenValueParam(transactionData);
|
||||||
const { allCollectibles } = this.collectiblesController.state;
|
const { allCollectibles } = this.collectiblesController.state;
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
"@metamask/iframe-execution-environment-service": "^0.10.2",
|
"@metamask/iframe-execution-environment-service": "^0.10.2",
|
||||||
"@metamask/jazzicon": "^2.0.0",
|
"@metamask/jazzicon": "^2.0.0",
|
||||||
"@metamask/logo": "^3.1.1",
|
"@metamask/logo": "^3.1.1",
|
||||||
|
"@metamask/metamask-eth-abis": "^3.0.0",
|
||||||
"@metamask/obs-store": "^5.0.0",
|
"@metamask/obs-store": "^5.0.0",
|
||||||
"@metamask/post-message-stream": "^4.0.0",
|
"@metamask/post-message-stream": "^4.0.0",
|
||||||
"@metamask/providers": "^8.1.1",
|
"@metamask/providers": "^8.1.1",
|
||||||
|
@ -48,6 +48,7 @@ export const TRANSACTION_TYPES = {
|
|||||||
RETRY: 'retry',
|
RETRY: 'retry',
|
||||||
TOKEN_METHOD_TRANSFER: 'transfer',
|
TOKEN_METHOD_TRANSFER: 'transfer',
|
||||||
TOKEN_METHOD_TRANSFER_FROM: 'transferfrom',
|
TOKEN_METHOD_TRANSFER_FROM: 'transferfrom',
|
||||||
|
TOKEN_METHOD_SAFE_TRANSFER_FROM: 'safetransferfrom',
|
||||||
TOKEN_METHOD_APPROVE: 'approve',
|
TOKEN_METHOD_APPROVE: 'approve',
|
||||||
INCOMING: 'incoming',
|
INCOMING: 'incoming',
|
||||||
SIMPLE_SEND: 'simpleSend',
|
SIMPLE_SEND: 'simpleSend',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { isHexString } from 'ethereumjs-util';
|
import { isHexString } from 'ethereumjs-util';
|
||||||
import { ethers } from 'ethers';
|
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 log from 'loglevel';
|
||||||
import { TOKEN_STANDARDS } from '../../ui/helpers/constants/common';
|
import { TOKEN_STANDARDS } from '../../ui/helpers/constants/common';
|
||||||
import { ASSET_TYPES, TRANSACTION_TYPES } from '../constants/transaction';
|
import { ASSET_TYPES, TRANSACTION_TYPES } from '../constants/transaction';
|
||||||
@ -19,7 +19,22 @@ import { isEqualCaseInsensitive } from './string-utils';
|
|||||||
* code
|
* 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) {
|
export function transactionMatchesNetwork(transaction, chainId, networkId) {
|
||||||
if (typeof transaction.chainId !== 'undefined') {
|
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.
|
* Determines the type of the transaction by analyzing the txParams.
|
||||||
* This method will return one of the types defined in shared/constants/transactions
|
* 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;
|
const { data, to } = txParams;
|
||||||
let name;
|
let name;
|
||||||
try {
|
try {
|
||||||
name = data && hstInterface.parseTransaction({ data }).name;
|
({ name } = data && parseStandardTokenTransactionData(data));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.debug('Failed to parse transaction data.', error, data);
|
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_APPROVE,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
|
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
||||||
|
TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
|
||||||
].find((methodName) => isEqualCaseInsensitive(methodName, name));
|
].find((methodName) => isEqualCaseInsensitive(methodName, name));
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
|
@ -5,9 +5,28 @@ import {
|
|||||||
determineTransactionType,
|
determineTransactionType,
|
||||||
isEIP1559Transaction,
|
isEIP1559Transaction,
|
||||||
isLegacyTransaction,
|
isLegacyTransaction,
|
||||||
|
parseStandardTokenTransactionData,
|
||||||
} from './transaction.utils';
|
} from './transaction.utils';
|
||||||
|
|
||||||
describe('Transaction.utils', function () {
|
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 () {
|
describe('isEIP1559Transaction', function () {
|
||||||
it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => {
|
it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => {
|
||||||
expect(
|
expect(
|
||||||
|
@ -38,6 +38,7 @@ const ConfirmPageContainerSummary = (props) => {
|
|||||||
TRANSACTION_TYPES.CONTRACT_INTERACTION,
|
TRANSACTION_TYPES.CONTRACT_INTERACTION,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
|
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
||||||
|
TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
|
||||||
];
|
];
|
||||||
const isContractTypeTransaction = contractInitiatedTransactionType.includes(
|
const isContractTypeTransaction = contractInitiatedTransactionType.includes(
|
||||||
transactionType,
|
transactionType,
|
||||||
@ -49,7 +50,8 @@ const ConfirmPageContainerSummary = (props) => {
|
|||||||
// type of contract interaction it is passed as toAddress
|
// type of contract interaction it is passed as toAddress
|
||||||
contractAddress =
|
contractAddress =
|
||||||
transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER ||
|
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
|
? tokenAddress
|
||||||
: toAddress;
|
: toAddress;
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,12 @@ import {
|
|||||||
addEth,
|
addEth,
|
||||||
} from '../../helpers/utils/confirm-tx.util';
|
} from '../../helpers/utils/confirm-tx.util';
|
||||||
|
|
||||||
import {
|
import { sumHexes } from '../../helpers/utils/transactions.util';
|
||||||
getTransactionData,
|
|
||||||
sumHexes,
|
|
||||||
} from '../../helpers/utils/transactions.util';
|
|
||||||
|
|
||||||
import { conversionUtil } from '../../../shared/modules/conversion.utils';
|
import { conversionUtil } from '../../../shared/modules/conversion.utils';
|
||||||
import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas';
|
import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas';
|
||||||
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const createActionType = (action) => `metamask/confirm-transaction/${action}`;
|
const createActionType = (action) => `metamask/confirm-transaction/${action}`;
|
||||||
@ -285,7 +283,7 @@ export function setTransactionToConfirm(transactionId) {
|
|||||||
if (txParams.data) {
|
if (txParams.data) {
|
||||||
const { to: tokenAddress, data } = txParams;
|
const { to: tokenAddress, data } = txParams;
|
||||||
|
|
||||||
const tokenData = getTransactionData(data);
|
const tokenData = parseStandardTokenTransactionData(data);
|
||||||
const tokens = getTokens(state);
|
const tokens = getTokens(state);
|
||||||
const currentToken = tokens?.find(({ address }) =>
|
const currentToken = tokens?.find(({ address }) =>
|
||||||
isEqualCaseInsensitive(tokenAddress, address),
|
isEqualCaseInsensitive(tokenAddress, address),
|
||||||
|
@ -88,6 +88,7 @@ const CONFIRM_SEND_TOKEN_PATH = '/send-token';
|
|||||||
const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract';
|
const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract';
|
||||||
const CONFIRM_APPROVE_PATH = '/approve';
|
const CONFIRM_APPROVE_PATH = '/approve';
|
||||||
const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from';
|
const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from';
|
||||||
|
const CONFIRM_SAFE_TRANSFER_FROM_PATH = '/safe-transfer-from';
|
||||||
const CONFIRM_TOKEN_METHOD_PATH = '/token-method';
|
const CONFIRM_TOKEN_METHOD_PATH = '/token-method';
|
||||||
const SIGNATURE_REQUEST_PATH = '/signature-request';
|
const SIGNATURE_REQUEST_PATH = '/signature-request';
|
||||||
const DECRYPT_MESSAGE_REQUEST_PATH = '/decrypt-message-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_DEPLOY_CONTRACT_PATH}`]: 'Confirm Deploy Contract Transaction Page',
|
||||||
[`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_APPROVE_PATH}`]: 'Confirm Approve 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_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${SIGNATURE_REQUEST_PATH}`]: 'Signature Request Page',
|
||||||
[`${CONFIRM_TRANSACTION_ROUTE}/:id${DECRYPT_MESSAGE_REQUEST_PATH}`]: 'Decrypt Message 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',
|
[`${CONFIRM_TRANSACTION_ROUTE}/:id${ENCRYPTION_PUBLIC_KEY_REQUEST_PATH}`]: 'Encryption Public Key Request Page',
|
||||||
@ -200,6 +202,7 @@ export {
|
|||||||
CONFIRM_DEPLOY_CONTRACT_PATH,
|
CONFIRM_DEPLOY_CONTRACT_PATH,
|
||||||
CONFIRM_APPROVE_PATH,
|
CONFIRM_APPROVE_PATH,
|
||||||
CONFIRM_TRANSFER_FROM_PATH,
|
CONFIRM_TRANSFER_FROM_PATH,
|
||||||
|
CONFIRM_SAFE_TRANSFER_FROM_PATH,
|
||||||
CONFIRM_TOKEN_METHOD_PATH,
|
CONFIRM_TOKEN_METHOD_PATH,
|
||||||
SIGNATURE_REQUEST_PATH,
|
SIGNATURE_REQUEST_PATH,
|
||||||
DECRYPT_MESSAGE_REQUEST_PATH,
|
DECRYPT_MESSAGE_REQUEST_PATH,
|
||||||
|
@ -7,15 +7,14 @@ import {
|
|||||||
import { getTokenStandardAndDetails } from '../../store/actions';
|
import { getTokenStandardAndDetails } from '../../store/actions';
|
||||||
import { ERC1155, ERC721 } from '../constants/common';
|
import { ERC1155, ERC721 } from '../constants/common';
|
||||||
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
|
||||||
import * as util from './util';
|
import * as util from './util';
|
||||||
import { formatCurrency } from './confirm-tx.util';
|
import { formatCurrency } from './confirm-tx.util';
|
||||||
import { getTransactionData } from './transactions.util';
|
|
||||||
|
|
||||||
const DEFAULT_SYMBOL = '';
|
const DEFAULT_SYMBOL = '';
|
||||||
|
|
||||||
async function getSymbolFromContract(tokenAddress) {
|
async function getSymbolFromContract(tokenAddress) {
|
||||||
const token = util.getContractAtAddress(tokenAddress);
|
const token = util.getContractAtAddress(tokenAddress);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await token.symbol();
|
const result = await token.symbol();
|
||||||
return result[0];
|
return result[0];
|
||||||
@ -136,7 +135,8 @@ export function calcTokenValue(value, decimals) {
|
|||||||
* @returns {string | undefined} A lowercase address string.
|
* @returns {string | undefined} A lowercase address string.
|
||||||
*/
|
*/
|
||||||
export function getTokenAddressParam(tokenData = {}) {
|
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();
|
return value?.toString().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ export async function getAssetDetails(
|
|||||||
transactionData,
|
transactionData,
|
||||||
existingCollectibles,
|
existingCollectibles,
|
||||||
) {
|
) {
|
||||||
const tokenData = getTransactionData(transactionData);
|
const tokenData = parseStandardTokenTransactionData(transactionData);
|
||||||
if (!tokenData) {
|
if (!tokenData) {
|
||||||
throw new Error('Unable to detect valid token data');
|
throw new Error('Unable to detect valid token data');
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { MethodRegistry } from 'eth-method-registry';
|
import { MethodRegistry } from 'eth-method-registry';
|
||||||
import abi from 'human-standard-token-abi';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
|
||||||
import { addHexPrefix } from '../../../app/scripts/lib/util';
|
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 { readAddressAsContract } from '../../../shared/modules/contract-utils';
|
||||||
import fetchWithCache from './fetch-with-cache';
|
import fetchWithCache from './fetch-with-cache';
|
||||||
|
|
||||||
const hstInterface = new ethers.utils.Interface(abi);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef EthersContractCall
|
* @typedef EthersContractCall
|
||||||
* @type object
|
* @type object
|
||||||
@ -29,19 +25,6 @@ const hstInterface = new ethers.utils.Interface(abi);
|
|||||||
* representation of the function.
|
* 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) {
|
async function getMethodFrom4Byte(fourBytePrefix) {
|
||||||
const fourByteResponse = await fetchWithCache(
|
const fourByteResponse = await fetchWithCache(
|
||||||
`https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`,
|
`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_TRANSFER,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
|
TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
|
||||||
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
|
||||||
|
TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
|
||||||
].includes(type);
|
].includes(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +199,9 @@ export function getTransactionTypeTitle(t, type, nativeCurrency = 'ETH') {
|
|||||||
case TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM: {
|
case TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM: {
|
||||||
return t('transferFrom');
|
return t('transferFrom');
|
||||||
}
|
}
|
||||||
|
case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: {
|
||||||
|
return t('safeTransferFrom');
|
||||||
|
}
|
||||||
case TRANSACTION_TYPES.TOKEN_METHOD_APPROVE: {
|
case TRANSACTION_TYPES.TOKEN_METHOD_APPROVE: {
|
||||||
return t('approve');
|
return t('approve');
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
TRANSACTION_TYPES,
|
|
||||||
TRANSACTION_GROUP_STATUSES,
|
TRANSACTION_GROUP_STATUSES,
|
||||||
TRANSACTION_STATUSES,
|
TRANSACTION_STATUSES,
|
||||||
TRANSACTION_ENVELOPE_TYPES,
|
TRANSACTION_ENVELOPE_TYPES,
|
||||||
@ -7,25 +6,6 @@ import {
|
|||||||
import * as utils from './transactions.util';
|
import * as utils from './transactions.util';
|
||||||
|
|
||||||
describe('Transactions utils', () => {
|
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', () => {
|
describe('getStatusKey', () => {
|
||||||
it('should return the correct status', () => {
|
it('should return the correct status', () => {
|
||||||
const tests = [
|
const tests = [
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils';
|
||||||
import { getCollectibles, getTokens } from '../ducks/metamask/metamask';
|
import { getCollectibles, getTokens } from '../ducks/metamask/metamask';
|
||||||
import { ERC1155, ERC721, ERC20 } from '../helpers/constants/common';
|
import { ERC1155, ERC721, ERC20 } from '../helpers/constants/common';
|
||||||
import {
|
import {
|
||||||
@ -8,14 +9,12 @@ import {
|
|||||||
getTokenAddressParam,
|
getTokenAddressParam,
|
||||||
getTokenValueParam,
|
getTokenValueParam,
|
||||||
} from '../helpers/utils/token-util';
|
} from '../helpers/utils/token-util';
|
||||||
import { getTransactionData } from '../helpers/utils/transactions.util';
|
|
||||||
import { getTokenList } from '../selectors';
|
import { getTokenList } from '../selectors';
|
||||||
import { hideLoadingIndication, showLoadingIndication } from '../store/actions';
|
import { hideLoadingIndication, showLoadingIndication } from '../store/actions';
|
||||||
import { usePrevious } from './usePrevious';
|
import { usePrevious } from './usePrevious';
|
||||||
|
|
||||||
export function useAssetDetails(tokenAddress, userAddress, transactionData) {
|
export function useAssetDetails(tokenAddress, userAddress, transactionData) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
// state selectors
|
// state selectors
|
||||||
const tokens = useSelector(getTokens);
|
const tokens = useSelector(getTokens);
|
||||||
const collectibles = useSelector(getCollectibles);
|
const collectibles = useSelector(getCollectibles);
|
||||||
@ -84,7 +83,7 @@ export function useAssetDetails(tokenAddress, userAddress, transactionData) {
|
|||||||
balance,
|
balance,
|
||||||
decimals: currentAssetDecimals,
|
decimals: currentAssetDecimals,
|
||||||
} = currentAsset;
|
} = currentAsset;
|
||||||
const tokenData = getTransactionData(transactionData);
|
const tokenData = parseStandardTokenTransactionData(transactionData);
|
||||||
assetStandard = standard;
|
assetStandard = standard;
|
||||||
assetAddress = tokenAddress;
|
assetAddress = tokenAddress;
|
||||||
tokenSymbol = symbol;
|
tokenSymbol = symbol;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { getTransactionData } from '../helpers/utils/transactions.util';
|
import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* useTokenData
|
* useTokenData
|
||||||
@ -19,6 +19,6 @@ export function useTokenData(transactionData, isTokenTransaction = true) {
|
|||||||
if (!isTokenTransaction || !transactionData) {
|
if (!isTokenTransaction || !transactionData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return getTransactionData(transactionData);
|
return parseStandardTokenTransactionData(transactionData);
|
||||||
}, [isTokenTransaction, transactionData]);
|
}, [isTokenTransaction, transactionData]);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { renderHook } from '@testing-library/react-hooks';
|
import { renderHook } from '@testing-library/react-hooks';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import * as tokenUtil from '../helpers/utils/token-util';
|
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';
|
import { useTokenDisplayValue } from './useTokenDisplayValue';
|
||||||
|
|
||||||
const tests = [
|
const tests = [
|
||||||
@ -122,9 +122,12 @@ describe('useTokenDisplayValue', () => {
|
|||||||
describe(`when input is decimals: ${token.decimals} and value: ${tokenValue}`, () => {
|
describe(`when input is decimals: ${token.decimals} and value: ${tokenValue}`, () => {
|
||||||
it(`should return ${displayValue} as displayValue`, () => {
|
it(`should return ${displayValue} as displayValue`, () => {
|
||||||
const getTokenValueStub = sinon.stub(tokenUtil, 'getTokenValueParam');
|
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);
|
getTokenValueStub.callsFake(() => tokenValue);
|
||||||
|
|
||||||
const { result } = renderHook(() =>
|
const { result } = renderHook(() =>
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
|
import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
|
||||||
import { decimalToHex } from '../../helpers/utils/conversions.util';
|
import { decimalToHex } from '../../helpers/utils/conversions.util';
|
||||||
import {
|
import {
|
||||||
calcTokenValue,
|
calcTokenValue,
|
||||||
getTokenAddressParam,
|
getTokenAddressParam,
|
||||||
} from '../../helpers/utils/token-util';
|
} from '../../helpers/utils/token-util';
|
||||||
import { getTransactionData } from '../../helpers/utils/transactions.util';
|
|
||||||
|
|
||||||
export function getCustomTxParamsData(
|
export function getCustomTxParamsData(
|
||||||
data,
|
data,
|
||||||
{ customPermissionAmount, decimals },
|
{ customPermissionAmount, decimals },
|
||||||
) {
|
) {
|
||||||
const tokenData = getTransactionData(data);
|
const tokenData = parseStandardTokenTransactionData(data);
|
||||||
|
|
||||||
if (!tokenData) {
|
if (!tokenData) {
|
||||||
throw new Error('Invalid data');
|
throw new Error('Invalid data');
|
||||||
|
@ -43,6 +43,7 @@ import {
|
|||||||
} from '../../ducks/metamask/metamask';
|
} from '../../ducks/metamask/metamask';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
parseStandardTokenTransactionData,
|
||||||
transactionMatchesNetwork,
|
transactionMatchesNetwork,
|
||||||
txParamsAreDappSuggested,
|
txParamsAreDappSuggested,
|
||||||
} from '../../../shared/modules/transaction.utils';
|
} from '../../../shared/modules/transaction.utils';
|
||||||
@ -52,6 +53,7 @@ import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
|
|||||||
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
|
import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
|
||||||
import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas';
|
import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas';
|
||||||
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
|
||||||
|
import { getTokenAddressParam } from '../../helpers/utils/token-util';
|
||||||
import ConfirmTransactionBase from './confirm-transaction-base.component';
|
import ConfirmTransactionBase from './confirm-transaction-base.component';
|
||||||
|
|
||||||
let customNonceValue = '';
|
let customNonceValue = '';
|
||||||
@ -104,9 +106,12 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
} = (transaction && transaction.txParams) || txParams;
|
} = (transaction && transaction.txParams) || txParams;
|
||||||
const accounts = getMetaMaskAccounts(state);
|
const accounts = getMetaMaskAccounts(state);
|
||||||
|
|
||||||
|
const transactionData = parseStandardTokenTransactionData(data);
|
||||||
|
const tokenToAddress = getTokenAddressParam(transactionData);
|
||||||
|
|
||||||
const { balance } = accounts[fromAddress];
|
const { balance } = accounts[fromAddress];
|
||||||
const { name: fromName } = identities[fromAddress];
|
const { name: fromName } = identities[fromAddress];
|
||||||
const toAddress = propsToAddress || txParamsToAddress;
|
const toAddress = propsToAddress || tokenToAddress || txParamsToAddress;
|
||||||
|
|
||||||
const tokenList = getTokenList(state);
|
const tokenList = getTokenList(state);
|
||||||
const useTokenDetection = getUseTokenDetection(state);
|
const useTokenDetection = getUseTokenDetection(state);
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
SIGNATURE_REQUEST_PATH,
|
SIGNATURE_REQUEST_PATH,
|
||||||
DECRYPT_MESSAGE_REQUEST_PATH,
|
DECRYPT_MESSAGE_REQUEST_PATH,
|
||||||
ENCRYPTION_PUBLIC_KEY_REQUEST_PATH,
|
ENCRYPTION_PUBLIC_KEY_REQUEST_PATH,
|
||||||
|
CONFIRM_SAFE_TRANSFER_FROM_PATH,
|
||||||
} from '../../helpers/constants/routes';
|
} from '../../helpers/constants/routes';
|
||||||
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
||||||
import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
|
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}`;
|
const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TRANSFER_FROM_PATH}`;
|
||||||
return <Redirect to={{ pathname }} />;
|
return <Redirect to={{ pathname }} />;
|
||||||
}
|
}
|
||||||
|
case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: {
|
||||||
|
const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SAFE_TRANSFER_FROM_PATH}`;
|
||||||
|
return <Redirect to={{ pathname }} />;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TOKEN_METHOD_PATH}`;
|
const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TOKEN_METHOD_PATH}`;
|
||||||
return <Redirect to={{ pathname }} />;
|
return <Redirect to={{ pathname }} />;
|
||||||
|
@ -4,6 +4,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
CONFIRM_APPROVE_PATH,
|
CONFIRM_APPROVE_PATH,
|
||||||
|
CONFIRM_SAFE_TRANSFER_FROM_PATH,
|
||||||
CONFIRM_SEND_TOKEN_PATH,
|
CONFIRM_SEND_TOKEN_PATH,
|
||||||
CONFIRM_TRANSACTION_ROUTE,
|
CONFIRM_TRANSACTION_ROUTE,
|
||||||
CONFIRM_TRANSFER_FROM_PATH,
|
CONFIRM_TRANSFER_FROM_PATH,
|
||||||
@ -88,6 +89,29 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SAFE_TRANSFER_FROM_PATH}`}
|
||||||
|
render={() => (
|
||||||
|
<ConfirmTokenTransactionBase
|
||||||
|
assetStandard={assetStandard}
|
||||||
|
assetName={assetName}
|
||||||
|
userBalance={userBalance}
|
||||||
|
tokenSymbol={tokenSymbol}
|
||||||
|
decimals={decimals}
|
||||||
|
image={tokenImage}
|
||||||
|
tokenAddress={tokenAddress}
|
||||||
|
toAddress={toAddress}
|
||||||
|
tokenAmount={tokenAmount}
|
||||||
|
tokenId={tokenId}
|
||||||
|
userAddress={userAddress}
|
||||||
|
transaction={transaction}
|
||||||
|
ethTransactionTotal={ethTransactionTotal}
|
||||||
|
fiatTransactionTotal={fiatTransactionTotal}
|
||||||
|
hexMaximumTransactionFee={hexMaximumTransactionFee}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SEND_TOKEN_PATH}`}
|
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SEND_TOKEN_PATH}`}
|
||||||
|
@ -75,7 +75,6 @@ import {
|
|||||||
SWAPS_ERROR_ROUTE,
|
SWAPS_ERROR_ROUTE,
|
||||||
AWAITING_SWAP_ROUTE,
|
AWAITING_SWAP_ROUTE,
|
||||||
} from '../../../helpers/constants/routes';
|
} from '../../../helpers/constants/routes';
|
||||||
import { getTransactionData } from '../../../helpers/utils/transactions.util';
|
|
||||||
import {
|
import {
|
||||||
calcTokenAmount,
|
calcTokenAmount,
|
||||||
calcTokenValue,
|
calcTokenValue,
|
||||||
@ -113,6 +112,7 @@ import SwapsFooter from '../swaps-footer';
|
|||||||
import PulseLoader from '../../../components/ui/pulse-loader'; // TODO: Replace this with a different loading component.
|
import PulseLoader from '../../../components/ui/pulse-loader'; // TODO: Replace this with a different loading component.
|
||||||
import Box from '../../../components/ui/box';
|
import Box from '../../../components/ui/box';
|
||||||
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
|
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
|
||||||
|
import { parseStandardTokenTransactionData } from '../../../../shared/modules/transaction.utils';
|
||||||
import ViewQuotePriceDifference from './view-quote-price-difference';
|
import ViewQuotePriceDifference from './view-quote-price-difference';
|
||||||
|
|
||||||
let intervalId;
|
let intervalId;
|
||||||
@ -320,7 +320,7 @@ export default function ViewQuote() {
|
|||||||
const tokenBalanceUnavailable =
|
const tokenBalanceUnavailable =
|
||||||
tokensWithBalances && balanceToken === undefined;
|
tokensWithBalances && balanceToken === undefined;
|
||||||
|
|
||||||
const approveData = getTransactionData(approveTxParams?.data);
|
const approveData = parseStandardTokenTransactionData(approveTxParams?.data);
|
||||||
const approveValue = approveData && getTokenValueParam(approveData);
|
const approveValue = approveData && getTokenValueParam(approveData);
|
||||||
const approveAmount =
|
const approveAmount =
|
||||||
approveValue &&
|
approveValue &&
|
||||||
|
@ -2914,7 +2914,7 @@
|
|||||||
gl-mat4 "1.1.4"
|
gl-mat4 "1.1.4"
|
||||||
gl-vec3 "1.0.3"
|
gl-vec3 "1.0.3"
|
||||||
|
|
||||||
"@metamask/metamask-eth-abis@3.0.0":
|
"@metamask/metamask-eth-abis@3.0.0", "@metamask/metamask-eth-abis@^3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@metamask/metamask-eth-abis/-/metamask-eth-abis-3.0.0.tgz#eccc0746b3ab1ab63000444403819c16e88b5272"
|
resolved "https://registry.yarnpkg.com/@metamask/metamask-eth-abis/-/metamask-eth-abis-3.0.0.tgz#eccc0746b3ab1ab63000444403819c16e88b5272"
|
||||||
integrity sha512-YtIl4e1VzqwwHGafuLIVPqbcWWWqQ0Ezo8/Ci5m5OGllqE2oTTx9iVHdUmXNkgCVD37SBfwn/fm/S1IGkM8BQA==
|
integrity sha512-YtIl4e1VzqwwHGafuLIVPqbcWWWqQ0Ezo8/Ci5m5OGllqE2oTTx9iVHdUmXNkgCVD37SBfwn/fm/S1IGkM8BQA==
|
||||||
|
Loading…
Reference in New Issue
Block a user