mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 09:23:21 +01:00
Extract out confirm-data and confirm-hex-data components from confirm-transaction-base.component.js (#17822)
This commit is contained in:
parent
2c2505be06
commit
0ac54e40ee
@ -308,11 +308,10 @@ describe('MetaMask', function () {
|
||||
await driver.clickElement({ text: 'Hex', tag: 'button' });
|
||||
await driver.delay(regularDelayMs);
|
||||
|
||||
const functionType = await driver.findElement(
|
||||
'.confirm-page-container-content__function-type',
|
||||
);
|
||||
const functionTypeText = await functionType.getText();
|
||||
assert(functionTypeText.match('Transfer'));
|
||||
await driver.findElement({
|
||||
tag: 'span',
|
||||
text: 'Transfer',
|
||||
});
|
||||
|
||||
const tokenAmount = await driver.findElement(
|
||||
'.confirm-page-container-summary__title-text',
|
||||
@ -320,17 +319,10 @@ describe('MetaMask', function () {
|
||||
const tokenAmountText = await tokenAmount.getText();
|
||||
assert.equal(tokenAmountText, '1 TST');
|
||||
|
||||
const confirmDataDiv = await driver.findElement(
|
||||
'.confirm-page-container-content__data-box',
|
||||
);
|
||||
const confirmDataText = await confirmDataDiv.getText();
|
||||
|
||||
await driver.delay(regularDelayMs);
|
||||
assert(
|
||||
confirmDataText.match(
|
||||
/0xa9059cbb0000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c97/u,
|
||||
),
|
||||
);
|
||||
await driver.waitForSelector({
|
||||
tag: 'p',
|
||||
text: '0xa9059cbb0000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c97',
|
||||
});
|
||||
|
||||
await driver.clickElement({ text: 'Details', tag: 'button' });
|
||||
await driver.delay(regularDelayMs);
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { render } from '@testing-library/react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Router } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import configureStore from '../../ui/store/store';
|
||||
import { I18nContext, LegacyI18nProvider } from '../../ui/contexts/i18n';
|
||||
import { LegacyMetaMetricsProvider } from '../../ui/contexts/metametrics';
|
||||
import { getMessage } from '../../ui/helpers/utils/i18n-helper';
|
||||
@ -35,7 +37,7 @@ I18nProvider.defaultProps = {
|
||||
children: undefined,
|
||||
};
|
||||
|
||||
export function renderWithProvider(component, store, pathname = '/') {
|
||||
const createProviderWrapper = (store, pathname = '/') => {
|
||||
const history = createMemoryHistory({ initialEntries: [pathname] });
|
||||
const Wrapper = ({ children }) =>
|
||||
store ? (
|
||||
@ -59,12 +61,29 @@ export function renderWithProvider(component, store, pathname = '/') {
|
||||
Wrapper.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
return {
|
||||
Wrapper,
|
||||
history,
|
||||
};
|
||||
};
|
||||
|
||||
export function renderWithProvider(component, store, pathname = '/') {
|
||||
const { history, Wrapper } = createProviderWrapper(store, pathname);
|
||||
return {
|
||||
...render(component, { wrapper: Wrapper }),
|
||||
history,
|
||||
};
|
||||
}
|
||||
|
||||
export function renderHookWithProvider(hook, state, pathname = '/') {
|
||||
const store = state ? configureStore(state) : undefined;
|
||||
const { history, Wrapper } = createProviderWrapper(store, pathname);
|
||||
return {
|
||||
...renderHook(hook, { wrapper: Wrapper }),
|
||||
history,
|
||||
};
|
||||
}
|
||||
|
||||
export function renderWithLocalization(component) {
|
||||
const Wrapper = ({ children }) => (
|
||||
<I18nProvider currentLocale="en" current={en} en={en}>
|
||||
|
@ -12,6 +12,7 @@
|
||||
@import 'beta-header/index';
|
||||
@import 'cancel-speedup-popover/index';
|
||||
@import 'confirm-page-container/index';
|
||||
@import 'confirm-data/index';
|
||||
@import 'confirmation-warning-modal/index';
|
||||
@import 'nfts-items/index';
|
||||
@import 'nfts-tab/index';
|
||||
|
10
ui/components/app/confirm-data/README.mdx
Normal file
10
ui/components/app/confirm-data/README.mdx
Normal file
@ -0,0 +1,10 @@
|
||||
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||
import { ConfirmData } from '.';
|
||||
|
||||
# Confirm Data
|
||||
|
||||
Confirm Data is used on confirmation screen to display transaction data.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-app-ConfirmData--default-story" />
|
||||
</Canvas>
|
72
ui/components/app/confirm-data/confirm-data.js
Normal file
72
ui/components/app/confirm-data/confirm-data.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import {
|
||||
Color,
|
||||
TextVariant,
|
||||
TEXT_TRANSFORM,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { getKnownMethodData } from '../../../selectors';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { useTransactionFunctionType } from '../../../hooks/useTransactionFunctionType';
|
||||
|
||||
import Box from '../../ui/box/box';
|
||||
import Disclosure from '../../ui/disclosure';
|
||||
import TransactionDecoding from '../transaction-decoding';
|
||||
import { Text } from '../../component-library';
|
||||
|
||||
const ConfirmData = ({ txData, dataComponent }) => {
|
||||
const t = useI18nContext();
|
||||
const { txParams = {} } = txData;
|
||||
const methodData = useSelector(
|
||||
(state) => getKnownMethodData(state, txParams.data) || {},
|
||||
);
|
||||
const { functionType } = useTransactionFunctionType(txData);
|
||||
|
||||
if (dataComponent) {
|
||||
return dataComponent;
|
||||
}
|
||||
|
||||
if (!txParams.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { params } = methodData;
|
||||
const functionParams = params?.length
|
||||
? `(${params.map(({ type }) => type).join(', ')})`
|
||||
: '';
|
||||
|
||||
return (
|
||||
<Box color={Color.textAlternative} className="confirm-data" padding={4}>
|
||||
<Box paddingBottom={3} paddingTop={2}>
|
||||
<Text
|
||||
as="span"
|
||||
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{`${t('functionType')}:`}
|
||||
</Text>
|
||||
<Text
|
||||
as="span"
|
||||
color={Color.textDefault}
|
||||
paddingLeft={1}
|
||||
textTransform={TEXT_TRANSFORM.CAPITALIZE}
|
||||
variant={TextVariant.bodySmBold}
|
||||
>
|
||||
{`${functionType} ${functionParams}`}
|
||||
</Text>
|
||||
</Box>
|
||||
<Disclosure>
|
||||
<TransactionDecoding to={txParams?.to} inputData={txParams?.data} />
|
||||
</Disclosure>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmData.propTypes = {
|
||||
txData: PropTypes.object,
|
||||
dataComponent: PropTypes.element,
|
||||
};
|
||||
|
||||
export default ConfirmData;
|
46
ui/components/app/confirm-data/confirm-data.stories.js
Normal file
46
ui/components/app/confirm-data/confirm-data.stories.js
Normal file
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import README from './README.mdx';
|
||||
import ConfirmData from './confirm-data';
|
||||
|
||||
export default {
|
||||
title: 'Components/App/ConfirmData',
|
||||
|
||||
component: ConfirmData,
|
||||
parameters: {
|
||||
docs: {
|
||||
page: README,
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
txData: {
|
||||
control: 'object',
|
||||
},
|
||||
dataHexComponent: {
|
||||
control: 'element',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
txData: {
|
||||
txParams: {
|
||||
data: '0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => {
|
||||
return <ConfirmData {...args} />;
|
||||
};
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const DataComponentStory = (args) => {
|
||||
return <ConfirmData {...args} />;
|
||||
};
|
||||
|
||||
DataComponentStory.storyName = 'DataComponent';
|
||||
DataComponentStory.args = {
|
||||
dataComponent: <div>Any custom component passed in props</div>,
|
||||
};
|
58
ui/components/app/confirm-data/confirm-data.test.js
Normal file
58
ui/components/app/confirm-data/confirm-data.test.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import { renderWithProvider } from '../../../../test/jest';
|
||||
|
||||
import configureStore from '../../../store/store';
|
||||
import ConfirmData from './confirm-data';
|
||||
|
||||
jest.mock('../../../../shared/lib/fetch-with-cache');
|
||||
|
||||
describe('ConfirmData', () => {
|
||||
const store = configureStore(mockState);
|
||||
|
||||
it('should render function type', async () => {
|
||||
const { findByText } = renderWithProvider(
|
||||
<ConfirmData
|
||||
txData={{
|
||||
txParams: {
|
||||
data: '0x608060405234801',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(await findByText('Transfer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return null if transaction has no data', () => {
|
||||
const { container } = renderWithProvider(
|
||||
<ConfirmData
|
||||
txData={{
|
||||
txParams: {},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(container.firstChild).toStrictEqual(null);
|
||||
});
|
||||
|
||||
it('should render dataComponent if passed', () => {
|
||||
const { getByText } = renderWithProvider(
|
||||
<ConfirmData
|
||||
txData={{
|
||||
txParams: {},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
dataComponent={<span>Data Component</span>}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(getByText('Data Component')).toBeInTheDocument();
|
||||
});
|
||||
});
|
1
ui/components/app/confirm-data/index.js
Normal file
1
ui/components/app/confirm-data/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default as ConfirmData } from './confirm-data';
|
5
ui/components/app/confirm-data/index.scss
Normal file
5
ui/components/app/confirm-data/index.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.confirm-data {
|
||||
& > .disclosure {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
10
ui/components/app/confirm-hexdata/README.mdx
Normal file
10
ui/components/app/confirm-hexdata/README.mdx
Normal file
@ -0,0 +1,10 @@
|
||||
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||
import { ConfirmHexData } from '.';
|
||||
|
||||
# Confirm Data
|
||||
|
||||
Confirm Hex Data is used on confirmation screen to display transaction data.
|
||||
|
||||
<Canvas>
|
||||
<Story id="components-app-ConfirmHexData--default-story" />
|
||||
</Canvas>
|
106
ui/components/app/confirm-hexdata/confirm-hexdata.js
Normal file
106
ui/components/app/confirm-hexdata/confirm-hexdata.js
Normal file
@ -0,0 +1,106 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { toBuffer } from '../../../../shared/modules/buffer-utils';
|
||||
import { getKnownMethodData } from '../../../selectors';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { useTransactionFunctionType } from '../../../hooks/useTransactionFunctionType';
|
||||
import {
|
||||
Color,
|
||||
OVERFLOW_WRAP,
|
||||
TextVariant,
|
||||
TEXT_TRANSFORM,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import Box from '../../ui/box';
|
||||
import { Text } from '../../component-library';
|
||||
import CopyRawData from '../transaction-decoding/components/ui/copy-raw-data';
|
||||
|
||||
const ConfirmHexData = ({ txData, dataHexComponent }) => {
|
||||
const t = useI18nContext();
|
||||
const { txParams = {} } = txData;
|
||||
const methodData = useSelector(
|
||||
(state) => getKnownMethodData(state, txParams.data) || {},
|
||||
);
|
||||
const { functionType } = useTransactionFunctionType(txData);
|
||||
|
||||
if (dataHexComponent) {
|
||||
return dataHexComponent;
|
||||
}
|
||||
|
||||
if (!txParams.data || !txParams.to) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { params } = methodData;
|
||||
const functionParams = params?.length
|
||||
? `(${params.map(({ type }) => type).join(', ')})`
|
||||
: '';
|
||||
|
||||
return (
|
||||
<Box padding={4}>
|
||||
<Box paddingBottom={3} paddingTop={2}>
|
||||
<Text
|
||||
as="span"
|
||||
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{`${t('functionType')}:`}
|
||||
</Text>
|
||||
<Text
|
||||
as="span"
|
||||
color={Color.textDefault}
|
||||
paddingLeft={1}
|
||||
textTransform={TEXT_TRANSFORM.CAPITALIZE}
|
||||
variant={TextVariant.bodySmBold}
|
||||
>
|
||||
{`${functionType} ${functionParams}`}
|
||||
</Text>
|
||||
</Box>
|
||||
{params && (
|
||||
<Box backgroundColor={Color.backgroundAlternative} padding={4}>
|
||||
<Text
|
||||
as="h3"
|
||||
paddingBottom={3}
|
||||
paddingTop={2}
|
||||
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{`${t('parameters')}:`}
|
||||
</Text>
|
||||
<Text
|
||||
overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
<pre>{JSON.stringify(params, null, 2)}</pre>
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Text
|
||||
as="h3"
|
||||
paddingBottom={3}
|
||||
paddingTop={2}
|
||||
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{`${t('hexData')}: ${toBuffer(txParams?.data).length} bytes`}
|
||||
</Text>
|
||||
<Text
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
|
||||
padding={4}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{txParams?.data}
|
||||
</Text>
|
||||
<CopyRawData data={txParams?.data} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmHexData.propTypes = {
|
||||
txData: PropTypes.object,
|
||||
dataHexComponent: PropTypes.element,
|
||||
};
|
||||
|
||||
export default ConfirmHexData;
|
47
ui/components/app/confirm-hexdata/confirm-hexdata.stories.js
Normal file
47
ui/components/app/confirm-hexdata/confirm-hexdata.stories.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import README from './README.mdx';
|
||||
import ConfirmHexData from './confirm-hexdata';
|
||||
|
||||
export default {
|
||||
title: 'Components/App/ConfirmHexData',
|
||||
|
||||
component: ConfirmHexData,
|
||||
parameters: {
|
||||
docs: {
|
||||
page: README,
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
txData: {
|
||||
control: 'object',
|
||||
},
|
||||
dataHexComponent: {
|
||||
control: 'element',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
txData: {
|
||||
txParams: {
|
||||
data: '0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000',
|
||||
to: '0x0',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => {
|
||||
return <ConfirmHexData {...args} />;
|
||||
};
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const DataHexComponentStory = (args) => {
|
||||
return <ConfirmHexData {...args} />;
|
||||
};
|
||||
|
||||
DataHexComponentStory.storyName = 'DataHexComponent';
|
||||
DataHexComponentStory.args = {
|
||||
dataHexComponent: <div>Any custom component passed in props</div>,
|
||||
};
|
77
ui/components/app/confirm-hexdata/confirm-hexdata.test.js
Normal file
77
ui/components/app/confirm-hexdata/confirm-hexdata.test.js
Normal file
@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import { renderWithProvider } from '../../../../test/jest';
|
||||
|
||||
import configureStore from '../../../store/store';
|
||||
import ConfirmHexData from './confirm-hexdata';
|
||||
|
||||
jest.mock('../../../../shared/lib/fetch-with-cache');
|
||||
|
||||
describe('ConfirmHexData', () => {
|
||||
const store = configureStore(mockState);
|
||||
|
||||
it('should render function type', async () => {
|
||||
const { findByText } = renderWithProvider(
|
||||
<ConfirmHexData
|
||||
txData={{
|
||||
txParams: {
|
||||
to: '0x8eeee1781fd885ff5ddef7789486676961873d12',
|
||||
data: '0x608060405234801',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(await findByText('Transfer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return null if transaction has no data', () => {
|
||||
const { container } = renderWithProvider(
|
||||
<ConfirmHexData
|
||||
txData={{
|
||||
txParams: {
|
||||
data: '0x608060405234801',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(container.firstChild).toStrictEqual(null);
|
||||
});
|
||||
|
||||
it('should return null if transaction has no to address', () => {
|
||||
const { container } = renderWithProvider(
|
||||
<ConfirmHexData
|
||||
txData={{
|
||||
txParams: {
|
||||
data: '0x608060405234801',
|
||||
},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(container.firstChild).toStrictEqual(null);
|
||||
});
|
||||
|
||||
it('should render dataHexComponent if passed', () => {
|
||||
const { getByText } = renderWithProvider(
|
||||
<ConfirmHexData
|
||||
txData={{
|
||||
txParams: {},
|
||||
origin: 'https://metamask.github.io',
|
||||
type: 'transfer',
|
||||
}}
|
||||
dataHexComponent={<span>Data Hex Component</span>}
|
||||
/>,
|
||||
store,
|
||||
);
|
||||
expect(getByText('Data Hex Component')).toBeInTheDocument();
|
||||
});
|
||||
});
|
1
ui/components/app/confirm-hexdata/index.js
Normal file
1
ui/components/app/confirm-hexdata/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default as ConfirmHexData } from './confirm-hexdata';
|
@ -21,45 +21,6 @@
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
&__data {
|
||||
padding: 16px;
|
||||
color: var(--color-text-alternative);
|
||||
|
||||
& > .disclosure {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__data-box {
|
||||
@include H7;
|
||||
|
||||
background-color: var(--color-background-alternative);
|
||||
padding: 12px;
|
||||
word-wrap: break-word;
|
||||
overflow-y: auto;
|
||||
|
||||
&-label {
|
||||
@include H7;
|
||||
|
||||
text-transform: uppercase;
|
||||
padding: 8px 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&__data-field {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&-label {
|
||||
font-weight: 500;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__gas-fee {
|
||||
border-bottom: 1px solid var(--color-border-muted);
|
||||
|
||||
@ -68,15 +29,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__function-type {
|
||||
@include H6;
|
||||
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
color: var(--color-text-default);
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
&__tab {
|
||||
@include H7;
|
||||
|
||||
|
46
ui/hooks/useTransactionFunctionType.js
Normal file
46
ui/hooks/useTransactionFunctionType.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { ORIGIN_METAMASK } from '../../shared/constants/app';
|
||||
import { TransactionType } from '../../shared/constants/transaction';
|
||||
import { getKnownMethodData } from '../selectors';
|
||||
import { getNativeCurrency } from '../ducks/metamask/metamask';
|
||||
import { getTransactionTypeTitle } from '../helpers/utils/transactions.util';
|
||||
import { getMethodName } from '../helpers/utils/metrics';
|
||||
|
||||
import { useI18nContext } from './useI18nContext';
|
||||
|
||||
export const useTransactionFunctionType = (txData = {}) => {
|
||||
const t = useI18nContext();
|
||||
const nativeCurrency = useSelector(getNativeCurrency);
|
||||
const { txParams } = txData;
|
||||
const methodData = useSelector(
|
||||
(state) => getKnownMethodData(state, txParams?.data) || {},
|
||||
);
|
||||
|
||||
if (!txParams) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const isTokenApproval =
|
||||
txData.type === TransactionType.tokenMethodSetApprovalForAll ||
|
||||
txData.type === TransactionType.tokenMethodApprove;
|
||||
|
||||
const isContractInteraction =
|
||||
txData.type === TransactionType.contractInteraction;
|
||||
|
||||
const isTransactionFromDapp =
|
||||
(isTokenApproval || isContractInteraction) &&
|
||||
txData.origin !== ORIGIN_METAMASK;
|
||||
|
||||
let functionType = isTransactionFromDapp
|
||||
? getMethodName(methodData?.name)
|
||||
: undefined;
|
||||
|
||||
if (!functionType) {
|
||||
functionType = txData.type
|
||||
? getTransactionTypeTitle(t, txData.type, nativeCurrency)
|
||||
: t('contractInteraction');
|
||||
}
|
||||
|
||||
return { functionType };
|
||||
};
|
48
ui/hooks/useTransactionFunctionType.test.js
Normal file
48
ui/hooks/useTransactionFunctionType.test.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { TransactionType } from '../../shared/constants/transaction';
|
||||
import { renderHookWithProvider } from '../../test/lib/render-helpers';
|
||||
import mockState from '../../test/data/mock-state.json';
|
||||
import { useTransactionFunctionType } from './useTransactionFunctionType';
|
||||
|
||||
describe('useTransactionFunctionType', () => {
|
||||
it('should return functionType depending on transaction data if present', () => {
|
||||
const { result } = renderHookWithProvider(
|
||||
() =>
|
||||
useTransactionFunctionType({
|
||||
txParams: {
|
||||
data: '0x095ea7b30000000000000000000000002f318c334780961fb129d2a6c30d0763d9a5c9700000000000000000000000000000000000000000000000000000000000011170',
|
||||
},
|
||||
type: TransactionType.tokenMethodApprove,
|
||||
}),
|
||||
mockState,
|
||||
);
|
||||
expect(result.current.functionType).toStrictEqual('Approve spend limit');
|
||||
});
|
||||
it('should return functionType depending on transaction type if method not present in transaction data', () => {
|
||||
const { result } = renderHookWithProvider(
|
||||
() =>
|
||||
useTransactionFunctionType({
|
||||
txParams: {},
|
||||
type: TransactionType.tokenMethodTransfer,
|
||||
}),
|
||||
mockState,
|
||||
);
|
||||
expect(result.current.functionType).toStrictEqual('Transfer');
|
||||
});
|
||||
it('should return functionType Contract interaction by default', () => {
|
||||
const { result } = renderHookWithProvider(
|
||||
() =>
|
||||
useTransactionFunctionType({
|
||||
txParams: {},
|
||||
}),
|
||||
mockState,
|
||||
);
|
||||
expect(result.current.functionType).toStrictEqual('Contract interaction');
|
||||
});
|
||||
it('should return undefined is txData is not present', () => {
|
||||
const { result } = renderHookWithProvider(
|
||||
() => useTransactionFunctionType(),
|
||||
mockState,
|
||||
);
|
||||
expect(result.current.functionType).toBeUndefined();
|
||||
});
|
||||
});
|
@ -2,6 +2,15 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConfirmTransactionBase from '../confirm-transaction-base';
|
||||
import { toBuffer } from '../../../shared/modules/buffer-utils';
|
||||
import Box from '../../components/ui/box';
|
||||
import { Text } from '../../components/component-library';
|
||||
import {
|
||||
Color,
|
||||
DISPLAY,
|
||||
OVERFLOW_WRAP,
|
||||
TextVariant,
|
||||
TEXT_TRANSFORM,
|
||||
} from '../../helpers/constants/design-system';
|
||||
|
||||
export default class ConfirmDeployContract extends Component {
|
||||
static contextTypes = {
|
||||
@ -17,26 +26,55 @@ export default class ConfirmDeployContract extends Component {
|
||||
const { txData: { origin, txParams: { data } = {} } = {} } = this.props;
|
||||
|
||||
return (
|
||||
<div className="confirm-page-container-content__data">
|
||||
<div className="confirm-page-container-content__data-box">
|
||||
<div className="confirm-page-container-content__data-field">
|
||||
<div className="confirm-page-container-content__data-field-label">
|
||||
<Box color={Color.textAlternative} padding={4}>
|
||||
<Box
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
padding={4}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
<Box display={DISPLAY.FLEX}>
|
||||
<Text
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
marginBottom={1}
|
||||
paddingRight={4}
|
||||
variant={TextVariant.bodySmBold}
|
||||
>
|
||||
{`${t('origin')}:`}
|
||||
</div>
|
||||
<div>{origin}</div>
|
||||
</div>
|
||||
<div className="confirm-page-container-content__data-field">
|
||||
<div className="confirm-page-container-content__data-field-label">
|
||||
</Text>
|
||||
<Text
|
||||
overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{origin}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={DISPLAY.FLEX}>
|
||||
<Text
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
paddingRight={4}
|
||||
variant={TextVariant.bodySmBold}
|
||||
>
|
||||
{`${t('bytes')}:`}
|
||||
</div>
|
||||
<div>{toBuffer(data).length}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="confirm-page-container-content__data-box-label">
|
||||
{`${t('hexData')}:`}
|
||||
</div>
|
||||
<div className="confirm-page-container-content__data-box">{data}</div>
|
||||
</div>
|
||||
</Text>
|
||||
<Text variant={TextVariant.bodySm}>{toBuffer(data).length}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Text
|
||||
as="h3"
|
||||
paddingBottom={3}
|
||||
paddingTop={2}
|
||||
textTransform={TEXT_TRANSFORM.UPPERCASE}
|
||||
variant={TextVariant.bodySm}
|
||||
>{`${t('hexData')}:`}</Text>
|
||||
<Text
|
||||
backgroundColor={Color.backgroundAlternative}
|
||||
overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
|
||||
padding={4}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{data}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,6 @@ export default class ConfirmSendEther extends Component {
|
||||
static propTypes = {
|
||||
editTransaction: PropTypes.func,
|
||||
history: PropTypes.object,
|
||||
txParams: PropTypes.object,
|
||||
};
|
||||
|
||||
handleEdit({ txData }) {
|
||||
@ -21,18 +20,10 @@ export default class ConfirmSendEther extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
shouldHideData() {
|
||||
const { txParams = {} } = this.props;
|
||||
return !txParams.data;
|
||||
}
|
||||
|
||||
render() {
|
||||
const hideData = this.shouldHideData();
|
||||
|
||||
return (
|
||||
<ConfirmTransactionBase
|
||||
actionKey="confirm"
|
||||
hideData={hideData}
|
||||
onEdit={(confirmTransactionData) =>
|
||||
this.handleEdit(confirmTransactionData)
|
||||
}
|
||||
|
@ -6,16 +6,6 @@ import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm
|
||||
import { AssetType } from '../../../shared/constants/transaction';
|
||||
import ConfirmSendEther from './confirm-send-ether.component';
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
confirmTransaction: { txData: { txParams } = {} },
|
||||
} = state;
|
||||
|
||||
return {
|
||||
txParams,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
editTransaction: async (txData) => {
|
||||
@ -28,5 +18,5 @@ const mapDispatchToProps = (dispatch) => {
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
connect(undefined, mapDispatchToProps),
|
||||
)(ConfirmSendEther);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConfirmPageContainer from '../../components/app/confirm-page-container';
|
||||
import TransactionDecoding from '../../components/app/transaction-decoding';
|
||||
import { isBalanceSufficient } from '../send/send.utils';
|
||||
import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
|
||||
import {
|
||||
@ -11,12 +10,10 @@ import {
|
||||
GAS_PRICE_FETCH_FAILURE_ERROR_KEY,
|
||||
} from '../../helpers/constants/error-keys';
|
||||
import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display';
|
||||
import CopyRawData from '../../components/app/transaction-decoding/components/ui/copy-raw-data';
|
||||
|
||||
import { PRIMARY, SECONDARY } from '../../helpers/constants/common';
|
||||
import TextField from '../../components/ui/text-field';
|
||||
import SimulationErrorMessage from '../../components/ui/simulation-error-message';
|
||||
import Disclosure from '../../components/ui/disclosure';
|
||||
import { EVENT } from '../../../shared/constants/metametrics';
|
||||
import {
|
||||
TransactionType,
|
||||
@ -27,7 +24,6 @@ import {
|
||||
getTransactionTypeTitle,
|
||||
isLegacyTransaction,
|
||||
} from '../../helpers/utils/transactions.util';
|
||||
import { toBuffer } from '../../../shared/modules/buffer-utils';
|
||||
|
||||
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
|
||||
import TransactionDetail from '../../components/app/transaction-detail/transaction-detail.component';
|
||||
@ -60,6 +56,8 @@ import {
|
||||
hexWEIToDecGWEI,
|
||||
} from '../../../shared/modules/conversion.utils';
|
||||
import TransactionAlerts from '../../components/app/transaction-alerts';
|
||||
import { ConfirmHexData } from '../../components/app/confirm-hexdata';
|
||||
import { ConfirmData } from '../../components/app/confirm-data';
|
||||
|
||||
const renderHeartBeatIfNotInTest = () =>
|
||||
process.env.IN_TEST ? null : <LoadingHeartBeat />;
|
||||
@ -110,7 +108,6 @@ export default class ConfirmTransactionBase extends Component {
|
||||
contentComponent: PropTypes.node,
|
||||
dataComponent: PropTypes.node,
|
||||
dataHexComponent: PropTypes.node,
|
||||
hideData: PropTypes.bool,
|
||||
hideSubtitle: PropTypes.bool,
|
||||
tokenAddress: PropTypes.string,
|
||||
customTokenAmount: PropTypes.string,
|
||||
@ -638,85 +635,27 @@ export default class ConfirmTransactionBase extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderData(functionType) {
|
||||
const { t } = this.context;
|
||||
renderData() {
|
||||
const { txData, dataComponent } = this.props;
|
||||
const {
|
||||
txData: { txParams } = {},
|
||||
methodData: { params } = {},
|
||||
hideData,
|
||||
dataComponent,
|
||||
} = this.props;
|
||||
|
||||
if (hideData) {
|
||||
txParams: { data },
|
||||
} = txData;
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const functionParams = params?.length
|
||||
? `(${params.map(({ type }) => type).join(', ')})`
|
||||
: '';
|
||||
|
||||
return (
|
||||
dataComponent || (
|
||||
<div className="confirm-page-container-content__data">
|
||||
<div className="confirm-page-container-content__data-box-label">
|
||||
{`${t('functionType')}:`}
|
||||
<span className="confirm-page-container-content__function-type">
|
||||
{`${functionType} ${functionParams}`}
|
||||
</span>
|
||||
</div>
|
||||
<Disclosure>
|
||||
<TransactionDecoding to={txParams?.to} inputData={txParams?.data} />
|
||||
</Disclosure>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
return <ConfirmData txData={txData} dataComponent={dataComponent} />;
|
||||
}
|
||||
|
||||
renderDataHex(functionType) {
|
||||
const { t } = this.context;
|
||||
renderDataHex() {
|
||||
const { txData, dataHexComponent } = this.props;
|
||||
const {
|
||||
txData: { txParams } = {},
|
||||
methodData: { params } = {},
|
||||
hideData,
|
||||
dataHexComponent,
|
||||
} = this.props;
|
||||
|
||||
if (hideData || !txParams.to) {
|
||||
txParams: { data, to },
|
||||
} = txData;
|
||||
if (!data || !to) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const functionParams = params?.length
|
||||
? `(${params.map(({ type }) => type).join(', ')})`
|
||||
: '';
|
||||
|
||||
return (
|
||||
dataHexComponent || (
|
||||
<div className="confirm-page-container-content__data">
|
||||
<div className="confirm-page-container-content__data-box-label">
|
||||
{`${t('functionType')}:`}
|
||||
<span className="confirm-page-container-content__function-type">
|
||||
{`${functionType} ${functionParams}`}
|
||||
</span>
|
||||
</div>
|
||||
{params && (
|
||||
<div className="confirm-page-container-content__data-box">
|
||||
<div className="confirm-page-container-content__data-field-label">
|
||||
{`${t('parameters')}:`}
|
||||
</div>
|
||||
<div>
|
||||
<pre>{JSON.stringify(params, null, 2)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="confirm-page-container-content__data-box-label">
|
||||
{`${t('hexData')}: ${toBuffer(txParams?.data).length} bytes`}
|
||||
</div>
|
||||
<div className="confirm-page-container-content__data-box">
|
||||
{txParams?.data}
|
||||
</div>
|
||||
<CopyRawData data={txParams?.data} />
|
||||
</div>
|
||||
)
|
||||
<ConfirmHexData txData={txData} dataHexComponent={dataHexComponent} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -1049,6 +988,7 @@ export default class ConfirmTransactionBase extends Component {
|
||||
// component, which in turn returns this `<ConfirmTransactionBase />` component. We meed to prevent
|
||||
// the user from editing the transaction in those cases.
|
||||
|
||||
// as this component is made functional, useTransactionFunctionType can be used to get functionType
|
||||
const isTokenApproval =
|
||||
txData.type === TransactionType.tokenMethodSetApprovalForAll ||
|
||||
txData.type === TransactionType.tokenMethodApprove;
|
||||
|
Loading…
Reference in New Issue
Block a user