diff --git a/app/scripts/lib/transaction/metrics.test.ts b/app/scripts/lib/transaction/metrics.test.ts
index 1ff509da3c09..f3dfa87799fc 100644
--- a/app/scripts/lib/transaction/metrics.test.ts
+++ b/app/scripts/lib/transaction/metrics.test.ts
@@ -147,6 +147,7 @@ describe('Transaction metrics', () => {
eip_1559_version: '0',
gas_edit_attempted: 'none',
gas_estimation_failed: false,
+ is_smart_transaction: undefined,
gas_edit_type: 'none',
network: mockNetworkId,
referrer: ORIGIN_METAMASK,
@@ -155,8 +156,8 @@ describe('Transaction metrics', () => {
token_standard: TokenStandard.none,
transaction_speed_up: false,
transaction_type: TransactionType.simpleSend,
- ui_customizations: null,
- transaction_advanced_view: null,
+ ui_customizations: ['redesigned_confirmation'],
+ transaction_advanced_view: undefined,
transaction_contract_method: undefined,
};
@@ -233,7 +234,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['gas_estimation_failed'],
+ ui_customizations: [
+ 'gas_estimation_failed',
+ 'redesigned_confirmation',
+ ],
gas_estimation_failed: true,
},
sensitiveProperties: expectedSensitiveProperties,
@@ -263,7 +267,10 @@ describe('Transaction metrics', () => {
...expectedProperties,
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
ppom_eth_call_count: 5,
ppom_eth_getCode_count: 3,
},
@@ -353,7 +360,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -370,7 +380,10 @@ describe('Transaction metrics', () => {
{
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -490,7 +503,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -510,7 +526,10 @@ describe('Transaction metrics', () => {
{
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -687,7 +706,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -709,7 +731,10 @@ describe('Transaction metrics', () => {
{
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -820,7 +845,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -841,7 +869,10 @@ describe('Transaction metrics', () => {
{
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -947,7 +978,10 @@ describe('Transaction metrics', () => {
persist: true,
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
@@ -964,7 +998,10 @@ describe('Transaction metrics', () => {
{
properties: {
...expectedProperties,
- ui_customizations: ['flagged_as_malicious'],
+ ui_customizations: [
+ 'flagged_as_malicious',
+ 'redesigned_confirmation',
+ ],
security_alert_reason: BlockaidReason.maliciousDomain,
security_alert_response: 'Malicious',
ppom_eth_call_count: 5,
diff --git a/test/e2e/page-objects/pages/test-dapp.ts b/test/e2e/page-objects/pages/test-dapp.ts
index 9da41bcb22d5..afff2f37e57e 100644
--- a/test/e2e/page-objects/pages/test-dapp.ts
+++ b/test/e2e/page-objects/pages/test-dapp.ts
@@ -34,6 +34,8 @@ class TestDapp {
tag: 'button',
};
+ private readonly simpleSendButton = '#sendButton';
+
private readonly erc721MintButton = '#mintButton';
private readonly erc721TransferFromButton = '#transferFromButton';
@@ -186,6 +188,10 @@ class TestDapp {
});
}
+ async clickSimpleSendButton() {
+ await this.driver.clickElement(this.simpleSendButton);
+ }
+
async clickERC721MintButton() {
await this.driver.clickElement(this.erc721MintButton);
}
diff --git a/test/e2e/tests/confirmations/helpers.ts b/test/e2e/tests/confirmations/helpers.ts
index ff467f42c320..355f664ec61c 100644
--- a/test/e2e/tests/confirmations/helpers.ts
+++ b/test/e2e/tests/confirmations/helpers.ts
@@ -46,8 +46,8 @@ export function withRedesignConfirmationFixtures(
transactionEnvelopeType === TransactionEnvelopeType.legacy
? defaultGanacheOptions
: defaultGanacheOptionsForType2Transactions,
- smartContract,
- testSpecificMock: mocks,
+ ...(smartContract && { smartContract }),
+ ...(mocks && { testSpecificMock: mocks }),
title,
},
testFunction,
diff --git a/test/e2e/tests/confirmations/navigation.spec.ts b/test/e2e/tests/confirmations/navigation.spec.ts
index 747ba15872b3..38d29ad3ad77 100644
--- a/test/e2e/tests/confirmations/navigation.spec.ts
+++ b/test/e2e/tests/confirmations/navigation.spec.ts
@@ -66,10 +66,15 @@ describe('Navigation Signature - Different signature types', function (this: Sui
'[data-testid="confirm-nav__next-confirmation"]',
);
- // Verify Transaction Sending ETH is displayed
- await verifyTransaction(driver, 'Sending ETH');
+ // Verify simple send transaction is displayed
+ await driver.waitForSelector({
+ tag: 'h3',
+ text: 'Transfer request',
+ });
- await driver.clickElement('[data-testid="next-page"]');
+ await driver.clickElement(
+ '[data-testid="confirm-nav__next-confirmation"]',
+ );
// Verify Sign Typed Data v3 confirmation is displayed
await verifySignedTypeV3Confirmation(driver);
@@ -78,10 +83,15 @@ describe('Navigation Signature - Different signature types', function (this: Sui
'[data-testid="confirm-nav__previous-confirmation"]',
);
- // Verify Sign Typed Data v3 confirmation is displayed
- await verifyTransaction(driver, 'Sending ETH');
+ // Verify simple send transaction is displayed
+ await driver.waitForSelector({
+ tag: 'h3',
+ text: 'Transfer request',
+ });
- await driver.clickElement('[data-testid="previous-page"]');
+ await driver.clickElement(
+ '[data-testid="confirm-nav__previous-confirmation"]',
+ );
// Verify Sign Typed Data v3 confirmation is displayed
await verifySignTypedData(driver);
@@ -179,13 +189,3 @@ async function queueSignaturesAndTransactions(driver: Driver) {
await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog);
await driver.waitForSelector(By.xpath("//div[normalize-space(.)='1 of 3']"));
}
-
-async function verifyTransaction(
- driver: Driver,
- expectedTransactionType: string,
-) {
- await driver.waitForSelector({
- tag: 'span',
- text: expectedTransactionType,
- });
-}
diff --git a/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts
new file mode 100644
index 000000000000..e8226977d019
--- /dev/null
+++ b/test/e2e/tests/confirmations/transactions/native-send-redesign.spec.ts
@@ -0,0 +1,113 @@
+/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
+import { TransactionEnvelopeType } from '@metamask/transaction-controller';
+import { DAPP_URL } from '../../../constants';
+import {
+ unlockWallet,
+ veryLargeDelayMs,
+ WINDOW_TITLES,
+} from '../../../helpers';
+import TokenTransferTransactionConfirmation from '../../../page-objects/pages/confirmations/redesign/token-transfer-confirmation';
+import HomePage from '../../../page-objects/pages/homepage';
+import SendTokenPage from '../../../page-objects/pages/send/send-token-page';
+import TestDapp from '../../../page-objects/pages/test-dapp';
+import { Driver } from '../../../webdriver/driver';
+import { withRedesignConfirmationFixtures } from '../helpers';
+import { TestSuiteArguments } from './shared';
+
+const TOKEN_RECIPIENT_ADDRESS = '0x2f318C334780961FB129D2a6c30D0763d9a5C970';
+
+describe('Confirmation Redesign Native Send @no-mmi', function () {
+ describe('Wallet initiated', async function () {
+ it('Sends a type 0 transaction (Legacy)', async function () {
+ await withRedesignConfirmationFixtures(
+ this.test?.fullTitle(),
+ TransactionEnvelopeType.legacy,
+ async ({ driver }: TestSuiteArguments) => {
+ await createWalletInitiatedTransactionAndAssertDetails(driver);
+ },
+ );
+ });
+
+ it('Sends a type 2 transaction (EIP1559)', async function () {
+ await withRedesignConfirmationFixtures(
+ this.test?.fullTitle(),
+ TransactionEnvelopeType.feeMarket,
+ async ({ driver }: TestSuiteArguments) => {
+ await createWalletInitiatedTransactionAndAssertDetails(driver);
+ },
+ );
+ });
+ });
+
+ describe('dApp initiated', async function () {
+ it('Sends a type 0 transaction (Legacy)', async function () {
+ await withRedesignConfirmationFixtures(
+ this.test?.fullTitle(),
+ TransactionEnvelopeType.legacy,
+ async ({ driver }: TestSuiteArguments) => {
+ await createDAppInitiatedTransactionAndAssertDetails(driver);
+ },
+ );
+ });
+
+ it('Sends a type 2 transaction (EIP1559)', async function () {
+ await withRedesignConfirmationFixtures(
+ this.test?.fullTitle(),
+ TransactionEnvelopeType.feeMarket,
+ async ({ driver }: TestSuiteArguments) => {
+ await createDAppInitiatedTransactionAndAssertDetails(driver);
+ },
+ );
+ });
+ });
+});
+
+async function createWalletInitiatedTransactionAndAssertDetails(
+ driver: Driver,
+) {
+ await unlockWallet(driver);
+
+ const testDapp = new TestDapp(driver);
+
+ await testDapp.openTestDappPage({ contractAddress: null, url: DAPP_URL });
+
+ await driver.switchToWindowWithTitle(WINDOW_TITLES.ExtensionInFullScreenView);
+ const homePage = new HomePage(driver);
+ await homePage.startSendFlow();
+ const sendToPage = new SendTokenPage(driver);
+ await sendToPage.check_pageIsLoaded();
+ await sendToPage.fillRecipient(TOKEN_RECIPIENT_ADDRESS);
+ await sendToPage.fillAmount('1');
+ await sendToPage.goToNextScreen();
+
+ const tokenTransferTransactionConfirmation =
+ new TokenTransferTransactionConfirmation(driver);
+ await tokenTransferTransactionConfirmation.check_walletInitiatedHeadingTitle();
+ await tokenTransferTransactionConfirmation.check_networkParagraph();
+ await tokenTransferTransactionConfirmation.check_networkFeeParagraph();
+
+ await tokenTransferTransactionConfirmation.clickFooterConfirmButton();
+}
+
+async function createDAppInitiatedTransactionAndAssertDetails(driver: Driver) {
+ await unlockWallet(driver);
+
+ const testDapp = new TestDapp(driver);
+
+ await testDapp.openTestDappPage({ contractAddress: null, url: DAPP_URL });
+
+ await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp);
+
+ await testDapp.clickSimpleSendButton();
+
+ await driver.delay(veryLargeDelayMs);
+ await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog);
+ const tokenTransferTransactionConfirmation =
+ new TokenTransferTransactionConfirmation(driver);
+ await tokenTransferTransactionConfirmation.check_dappInitiatedHeadingTitle();
+ await tokenTransferTransactionConfirmation.check_networkParagraph();
+ await tokenTransferTransactionConfirmation.check_networkFeeParagraph();
+
+ await tokenTransferTransactionConfirmation.clickScrollToBottomButton();
+ await tokenTransferTransactionConfirmation.clickFooterConfirmButton();
+}
diff --git a/ui/pages/confirmations/components/confirm/header/header.tsx b/ui/pages/confirmations/components/confirm/header/header.tsx
index 6c7c0e1cdd7f..278b21f85cbb 100644
--- a/ui/pages/confirmations/components/confirm/header/header.tsx
+++ b/ui/pages/confirmations/components/confirm/header/header.tsx
@@ -3,6 +3,7 @@ import {
TransactionType,
} from '@metamask/transaction-controller';
import React from 'react';
+import { ORIGIN_METAMASK } from '../../../../../../shared/constants/app';
import {
AvatarNetwork,
AvatarNetworkSize,
@@ -30,6 +31,7 @@ const CONFIRMATIONS_WITH_NEW_HEADER = [
TransactionType.tokenMethodTransfer,
TransactionType.tokenMethodTransferFrom,
TransactionType.tokenMethodSafeTransferFrom,
+ TransactionType.simpleSend,
];
const Header = () => {
@@ -88,7 +90,7 @@ const Header = () => {
currentConfirmation?.type &&
CONFIRMATIONS_WITH_NEW_HEADER.includes(currentConfirmation.type);
const isWalletInitiated =
- (currentConfirmation as TransactionMeta)?.origin === 'metamask';
+ (currentConfirmation as TransactionMeta)?.origin === ORIGIN_METAMASK;
if (isConfirmationWithNewHeader && isWalletInitiated) {
return ;
} else if (isConfirmationWithNewHeader && !isWalletInitiated) {
diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.test.ts b/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.test.ts
index efdf2b66ac56..9011569bac3e 100644
--- a/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.test.ts
+++ b/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.test.ts
@@ -1,11 +1,39 @@
import { TransactionMeta } from '@metamask/transaction-controller';
+import { useSelector } from 'react-redux';
import { genUnapprovedTokenTransferConfirmation } from '../../../../../../../test/data/confirmations/token-transfer';
import mockState from '../../../../../../../test/data/mock-state.json';
import { renderHookWithProvider } from '../../../../../../../test/lib/render-helpers';
+import { getTokenList } from '../../../../../../selectors';
import { useTokenDetails } from './useTokenDetails';
+jest.mock('react-redux', () => ({
+ ...jest.requireActual('react-redux'),
+ useSelector: jest.fn(),
+}));
+
+const ICON_SYMBOL = 'FROG';
+const ICON_URL =
+ 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x0a2c375553e6965b42c135bb8b15a8914b08de0c.png';
+const MOCK_TOKEN_LIST = (transactionMeta: TransactionMeta) => ({
+ [transactionMeta.txParams.to as string]: {
+ address: transactionMeta.txParams.to,
+ aggregators: ['CoinGecko', 'Socket', 'Coinmarketcap'],
+ decimals: 9,
+ iconUrl: ICON_URL,
+ name: 'Frog on ETH',
+ occurrences: 3,
+ symbol: ICON_SYMBOL,
+ },
+});
+
describe('useTokenDetails', () => {
- it('returns iconUrl from selected token if it exists', () => {
+ const useSelectorMock = useSelector as jest.Mock;
+
+ beforeEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('returns token details from selected token if it exists', () => {
const transactionMeta = genUnapprovedTokenTransferConfirmation(
{},
) as TransactionMeta;
@@ -18,8 +46,17 @@ describe('useTokenDetails', () => {
image: 'image',
};
+ useSelectorMock.mockImplementation((selector) => {
+ if (selector === getTokenList) {
+ return MOCK_TOKEN_LIST(transactionMeta);
+ } else if (selector?.toString().includes('getWatchedToken')) {
+ return TEST_SELECTED_TOKEN;
+ }
+ return undefined;
+ });
+
const { result } = renderHookWithProvider(
- () => useTokenDetails(transactionMeta, TEST_SELECTED_TOKEN),
+ () => useTokenDetails(transactionMeta),
mockState,
);
@@ -29,6 +66,37 @@ describe('useTokenDetails', () => {
});
});
+ it('returns token details from the token list if it exists', () => {
+ const transactionMeta = genUnapprovedTokenTransferConfirmation(
+ {},
+ ) as TransactionMeta;
+
+ const TEST_SELECTED_TOKEN = {
+ address: 'address',
+ decimals: 18,
+ };
+
+ useSelectorMock.mockImplementation((selector) => {
+ if (selector === getTokenList) {
+ return MOCK_TOKEN_LIST(transactionMeta);
+ } else if (selector?.toString().includes('getWatchedToken')) {
+ return TEST_SELECTED_TOKEN;
+ }
+
+ return undefined;
+ });
+
+ const { result } = renderHookWithProvider(
+ () => useTokenDetails(transactionMeta),
+ mockState,
+ );
+
+ expect(result.current).toEqual({
+ tokenImage: ICON_URL,
+ tokenSymbol: ICON_SYMBOL,
+ });
+ });
+
it('returns selected token image if no iconUrl is included', () => {
const transactionMeta = genUnapprovedTokenTransferConfirmation(
{},
@@ -41,8 +109,17 @@ describe('useTokenDetails', () => {
image: 'image',
};
+ useSelectorMock.mockImplementation((selector) => {
+ if (selector === getTokenList) {
+ return MOCK_TOKEN_LIST(transactionMeta);
+ } else if (selector?.toString().includes('getWatchedToken')) {
+ return TEST_SELECTED_TOKEN;
+ }
+ return undefined;
+ });
+
const { result } = renderHookWithProvider(
- () => useTokenDetails(transactionMeta, TEST_SELECTED_TOKEN),
+ () => useTokenDetails(transactionMeta),
mockState,
);
@@ -63,23 +140,22 @@ describe('useTokenDetails', () => {
symbol: 'symbol',
};
+ useSelectorMock.mockImplementation((selector) => {
+ if (selector === getTokenList) {
+ return MOCK_TOKEN_LIST(transactionMeta);
+ } else if (selector?.toString().includes('getWatchedToken')) {
+ return TEST_SELECTED_TOKEN;
+ }
+ return undefined;
+ });
+
const { result } = renderHookWithProvider(
- () => useTokenDetails(transactionMeta, TEST_SELECTED_TOKEN),
- {
- ...mockState,
- metamask: {
- ...mockState.metamask,
- tokenList: {
- '0x076146c765189d51be3160a2140cf80bfc73ad68': {
- iconUrl: 'tokenListIconUrl',
- },
- },
- },
- },
+ () => useTokenDetails(transactionMeta),
+ mockState,
);
expect(result.current).toEqual({
- tokenImage: 'tokenListIconUrl',
+ tokenImage: ICON_URL,
tokenSymbol: 'symbol',
});
});
@@ -95,8 +171,17 @@ describe('useTokenDetails', () => {
symbol: 'symbol',
};
+ useSelectorMock.mockImplementation((selector) => {
+ if (selector === getTokenList) {
+ return {};
+ } else if (selector?.toString().includes('getWatchedToken')) {
+ return TEST_SELECTED_TOKEN;
+ }
+ return undefined;
+ });
+
const { result } = renderHookWithProvider(
- () => useTokenDetails(transactionMeta, TEST_SELECTED_TOKEN),
+ () => useTokenDetails(transactionMeta),
mockState,
);
diff --git a/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.ts b/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.ts
index be9578496205..da2faacaeb5f 100644
--- a/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.ts
+++ b/ui/pages/confirmations/components/confirm/info/hooks/useTokenDetails.ts
@@ -2,15 +2,14 @@ import { TokenListMap } from '@metamask/assets-controllers';
import { TransactionMeta } from '@metamask/transaction-controller';
import { useSelector } from 'react-redux';
import { useI18nContext } from '../../../../../../hooks/useI18nContext';
-import { getTokenList } from '../../../../../../selectors';
-import { SelectedToken } from '../shared/selected-token';
+import { getTokenList, getWatchedToken } from '../../../../../../selectors';
+import { MultichainState } from '../../../../../../selectors/multichain';
-export const useTokenDetails = (
- transactionMeta: TransactionMeta,
- selectedToken: SelectedToken,
-) => {
+export const useTokenDetails = (transactionMeta: TransactionMeta) => {
const t = useI18nContext();
-
+ const selectedToken = useSelector((state: MultichainState) =>
+ getWatchedToken(transactionMeta)(state),
+ );
const tokenList = useSelector(getTokenList) as TokenListMap;
const tokenImage =
diff --git a/ui/pages/confirmations/components/confirm/info/info.tsx b/ui/pages/confirmations/components/confirm/info/info.tsx
index 67bac40d7e61..f283cbfc2e61 100644
--- a/ui/pages/confirmations/components/confirm/info/info.tsx
+++ b/ui/pages/confirmations/components/confirm/info/info.tsx
@@ -4,19 +4,23 @@ import { useConfirmContext } from '../../../context/confirm';
import { SignatureRequestType } from '../../../types/confirm';
import ApproveInfo from './approve/approve';
import BaseTransactionInfo from './base-transaction-info/base-transaction-info';
+import NativeTransferInfo from './native-transfer/native-transfer';
+import NFTTokenTransferInfo from './nft-token-transfer/nft-token-transfer';
import PersonalSignInfo from './personal-sign/personal-sign';
import SetApprovalForAllInfo from './set-approval-for-all-info/set-approval-for-all-info';
import TokenTransferInfo from './token-transfer/token-transfer';
import TypedSignV1Info from './typed-sign-v1/typed-sign-v1';
import TypedSignInfo from './typed-sign/typed-sign';
-import NFTTokenTransferInfo from './nft-token-transfer/nft-token-transfer';
const Info = () => {
const { currentConfirmation } = useConfirmContext();
const ConfirmationInfoComponentMap = useMemo(
() => ({
+ [TransactionType.contractInteraction]: () => BaseTransactionInfo,
+ [TransactionType.deployContract]: () => BaseTransactionInfo,
[TransactionType.personalSign]: () => PersonalSignInfo,
+ [TransactionType.simpleSend]: () => NativeTransferInfo,
[TransactionType.signTypedData]: () => {
const { version } =
(currentConfirmation as SignatureRequestType)?.msgParams ?? {};
@@ -25,15 +29,13 @@ const Info = () => {
}
return TypedSignInfo;
},
- [TransactionType.contractInteraction]: () => BaseTransactionInfo,
- [TransactionType.deployContract]: () => BaseTransactionInfo,
[TransactionType.tokenMethodApprove]: () => ApproveInfo,
[TransactionType.tokenMethodIncreaseAllowance]: () => ApproveInfo,
+ [TransactionType.tokenMethodSafeTransferFrom]: () => NFTTokenTransferInfo,
[TransactionType.tokenMethodSetApprovalForAll]: () =>
SetApprovalForAllInfo,
[TransactionType.tokenMethodTransfer]: () => TokenTransferInfo,
[TransactionType.tokenMethodTransferFrom]: () => NFTTokenTransferInfo,
- [TransactionType.tokenMethodSafeTransferFrom]: () => NFTTokenTransferInfo,
}),
[currentConfirmation],
);
diff --git a/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap b/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap
new file mode 100644
index 000000000000..234c0b704d5c
--- /dev/null
+++ b/ui/pages/confirmations/components/confirm/info/native-transfer/__snapshots__/native-transfer.test.tsx.snap
@@ -0,0 +1,402 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NativeTransferInfo renders correctly 1`] = `
+
+
+
+ G
+
+
+ 0 ETH
+
+
+ 0
+
+
+
+
+
+
+
+
+ 0x2e0D7...5d09B
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Estimated changes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0x07614...3ad68
+
+
+
+
+
+
+
+
+
+
+ 0.0001 ETH
+
+
+ $0.04
+
+
+
+
+
+
+
+
+
+ 🦊 Market
+
+
+
+ ~
+ 0 sec
+
+
+
+
+
+
+
+`;
diff --git a/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.stories.tsx b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.stories.tsx
new file mode 100644
index 000000000000..1de48b2bb2a0
--- /dev/null
+++ b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.stories.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { Provider } from 'react-redux';
+import { getMockTokenTransferConfirmState } from '../../../../../../../test/data/confirmations/helper';
+import { Box } from '../../../../../../components/component-library';
+import {
+ AlignItems,
+ Display,
+ FlexDirection,
+ JustifyContent,
+} from '../../../../../../helpers/constants/design-system';
+import configureStore from '../../../../../../store/store';
+import { ConfirmContextProvider } from '../../../../context/confirm';
+import NativeTransferInfo from './native-transfer';
+
+const store = configureStore(getMockTokenTransferConfirmState({}));
+
+const Story = {
+ title: 'Components/App/Confirm/info/NativeTransferInfo',
+ component: NativeTransferInfo,
+ decorators: [
+ (story: () => any) => (
+
+
+
+ {story()}
+
+
+
+ ),
+ ],
+};
+
+export default Story;
+
+export const DefaultStory = () => ;
+
+DefaultStory.storyName = 'Default';
diff --git a/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.test.tsx b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.test.tsx
new file mode 100644
index 000000000000..f4b2b4afab50
--- /dev/null
+++ b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.test.tsx
@@ -0,0 +1,41 @@
+import { screen, waitFor } from '@testing-library/react';
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+import { getMockTokenTransferConfirmState } from '../../../../../../../test/data/confirmations/helper';
+import { renderWithConfirmContextProvider } from '../../../../../../../test/lib/confirmations/render-helpers';
+import { tEn } from '../../../../../../../test/lib/i18n-helpers';
+import NativeTransferInfo from './native-transfer';
+
+jest.mock(
+ '../../../../../../components/app/alert-system/contexts/alertMetricsContext',
+ () => ({
+ useAlertMetrics: jest.fn(() => ({
+ trackAlertMetrics: jest.fn(),
+ })),
+ }),
+);
+
+jest.mock('../../../../../../store/actions', () => ({
+ ...jest.requireActual('../../../../../../store/actions'),
+ getGasFeeTimeEstimate: jest.fn().mockResolvedValue({
+ lowerTimeBound: 0,
+ upperTimeBound: 60000,
+ }),
+}));
+
+describe('NativeTransferInfo', () => {
+ it('renders correctly', async () => {
+ const state = getMockTokenTransferConfirmState({});
+ const mockStore = configureMockStore([])(state);
+ const { container } = renderWithConfirmContextProvider(
+ ,
+ mockStore,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText(tEn('networkFee') as string)).toBeInTheDocument();
+ });
+
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.tsx b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.tsx
new file mode 100644
index 000000000000..a2dd3ceaaa05
--- /dev/null
+++ b/ui/pages/confirmations/components/confirm/info/native-transfer/native-transfer.tsx
@@ -0,0 +1,37 @@
+import { TransactionMeta } from '@metamask/transaction-controller';
+import React from 'react';
+import { ConfirmInfoSection } from '../../../../../../components/app/confirm/info/row/section';
+import { useConfirmContext } from '../../../../context/confirm';
+import { SimulationDetails } from '../../../simulation-details';
+import { AdvancedDetails } from '../shared/advanced-details/advanced-details';
+import { GasFeesSection } from '../shared/gas-fees-section/gas-fees-section';
+import NativeSendHeading from '../shared/native-send-heading/native-send-heading';
+import { TokenDetailsSection } from '../token-transfer/token-details-section';
+import { TransactionFlowSection } from '../token-transfer/transaction-flow-section';
+
+const NativeTransferInfo = () => {
+ const { currentConfirmation: transactionMeta } =
+ useConfirmContext();
+
+ const isWalletInitiated = transactionMeta.origin === 'metamask';
+
+ return (
+ <>
+
+
+ {!isWalletInitiated && (
+
+
+
+ )}
+
+
+
+ >
+ );
+};
+
+export default NativeTransferInfo;
diff --git a/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx b/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx
new file mode 100644
index 000000000000..a3c2b91c9f8e
--- /dev/null
+++ b/ui/pages/confirmations/components/confirm/info/shared/native-send-heading/native-send-heading.tsx
@@ -0,0 +1,118 @@
+import { TransactionMeta } from '@metamask/transaction-controller';
+import { BigNumber } from 'bignumber.js';
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../../../../shared/constants/network';
+import {
+ AvatarToken,
+ AvatarTokenSize,
+ Box,
+ Text,
+} from '../../../../../../../components/component-library';
+import Tooltip from '../../../../../../../components/ui/tooltip';
+import { getIntlLocale } from '../../../../../../../ducks/locale/locale';
+import { getConversionRate } from '../../../../../../../ducks/metamask/metamask';
+import {
+ AlignItems,
+ Display,
+ FlexDirection,
+ JustifyContent,
+ TextColor,
+ TextVariant,
+} from '../../../../../../../helpers/constants/design-system';
+import { MIN_AMOUNT } from '../../../../../../../hooks/useCurrencyDisplay';
+import { useFiatFormatter } from '../../../../../../../hooks/useFiatFormatter';
+import { getMultichainNetwork } from '../../../../../../../selectors/multichain';
+import { useConfirmContext } from '../../../../../context/confirm';
+import {
+ formatAmount,
+ formatAmountMaxPrecision,
+} from '../../../../simulation-details/formatAmount';
+import { toNonScientificString } from '../../hooks/use-token-values';
+
+const NativeSendHeading = () => {
+ const { currentConfirmation: transactionMeta } =
+ useConfirmContext();
+
+ const nativeAssetTransferValue = new BigNumber(
+ transactionMeta.txParams.value as string,
+ ).dividedBy(new BigNumber(10).pow(18));
+
+ const conversionRate = useSelector(getConversionRate);
+ const fiatValue =
+ conversionRate &&
+ nativeAssetTransferValue &&
+ new BigNumber(conversionRate)
+ .times(nativeAssetTransferValue, 10)
+ .toNumber();
+ const fiatFormatter = useFiatFormatter();
+ const fiatDisplayValue =
+ fiatValue && fiatFormatter(fiatValue, { shorten: true });
+
+ const multichainNetwork = useSelector(getMultichainNetwork);
+ const ticker = multichainNetwork?.network?.ticker;
+
+ const locale = useSelector(getIntlLocale);
+ const roundedTransferValue = formatAmount(locale, nativeAssetTransferValue);
+
+ const transferValue = toNonScientificString(
+ nativeAssetTransferValue.toNumber(),
+ );
+
+ const NetworkImage = (
+
+ );
+
+ const NativeAssetAmount =
+ roundedTransferValue ===
+ `<${formatAmountMaxPrecision(locale, MIN_AMOUNT)}` ? (
+
+
+ {`${roundedTransferValue} ${ticker}`}
+
+
+ ) : (
+
+ {`${roundedTransferValue} ${ticker}`}
+
+ );
+
+ const NativeAssetFiatConversion = (
+
+ {fiatDisplayValue}
+
+ );
+
+ return (
+
+ {NetworkImage}
+ {NativeAssetAmount}
+ {NativeAssetFiatConversion}
+
+ );
+};
+
+export default NativeSendHeading;
diff --git a/ui/pages/confirmations/components/confirm/info/shared/send-heading/send-heading.tsx b/ui/pages/confirmations/components/confirm/info/shared/send-heading/send-heading.tsx
index b6aff206a26a..3f6bd429d20b 100644
--- a/ui/pages/confirmations/components/confirm/info/shared/send-heading/send-heading.tsx
+++ b/ui/pages/confirmations/components/confirm/info/shared/send-heading/send-heading.tsx
@@ -19,8 +19,7 @@ import {
TextVariant,
} from '../../../../../../../helpers/constants/design-system';
import { MIN_AMOUNT } from '../../../../../../../hooks/useCurrencyDisplay';
-import { getWatchedToken } from '../../../../../../../selectors';
-import { MultichainState } from '../../../../../../../selectors/multichain';
+import { useI18nContext } from '../../../../../../../hooks/useI18nContext';
import { useConfirmContext } from '../../../../../context/confirm';
import { formatAmountMaxPrecision } from '../../../../simulation-details/formatAmount';
import { useTokenValues } from '../../hooks/use-token-values';
@@ -28,16 +27,11 @@ import { useTokenDetails } from '../../hooks/useTokenDetails';
import { ConfirmLoader } from '../confirm-loader/confirm-loader';
const SendHeading = () => {
+ const t = useI18nContext();
const { currentConfirmation: transactionMeta } =
useConfirmContext();
const locale = useSelector(getIntlLocale);
- const selectedToken = useSelector((state: MultichainState) =>
- getWatchedToken(transactionMeta)(state),
- );
- const { tokenImage, tokenSymbol } = useTokenDetails(
- transactionMeta,
- selectedToken,
- );
+ const { tokenImage, tokenSymbol } = useTokenDetails(transactionMeta);
const {
decodedTransferValue,
displayTransferValue,
@@ -48,15 +42,17 @@ const SendHeading = () => {
const TokenImage = (
);
diff --git a/ui/pages/confirmations/components/confirm/info/token-transfer/token-details-section.tsx b/ui/pages/confirmations/components/confirm/info/token-transfer/token-details-section.tsx
index 6d686873ea1c..629d7d64df3a 100644
--- a/ui/pages/confirmations/components/confirm/info/token-transfer/token-details-section.tsx
+++ b/ui/pages/confirmations/components/confirm/info/token-transfer/token-details-section.tsx
@@ -1,4 +1,7 @@
-import { TransactionMeta } from '@metamask/transaction-controller';
+import {
+ TransactionMeta,
+ TransactionType,
+} from '@metamask/transaction-controller';
import React from 'react';
import { useSelector } from 'react-redux';
import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../../../../shared/constants/network';
@@ -61,7 +64,7 @@ export const TokenDetailsSection = () => {
);
- const tokenRow = (
+ const tokenRow = transactionMeta.type !== TransactionType.simpleSend && (
{
const addresses = value?.data[0].params.filter(
(param) => param.type === 'address',
);
- // sometimes there's more than one address, in which case we want the last one
- const recipientAddress = addresses?.[addresses.length - 1].value;
+ const recipientAddress =
+ transactionMeta.type === TransactionType.simpleSend
+ ? transactionMeta.txParams.to
+ : // sometimes there's more than one address, in which case we want the last one
+ addresses?.[addresses.length - 1].value;
if (pending) {
return ;
diff --git a/ui/pages/confirmations/utils/confirm.ts b/ui/pages/confirmations/utils/confirm.ts
index e33ff79d6f01..a007ca0aa0b2 100644
--- a/ui/pages/confirmations/utils/confirm.ts
+++ b/ui/pages/confirmations/utils/confirm.ts
@@ -25,6 +25,7 @@ export const REDESIGN_USER_TRANSACTION_TYPES = [
TransactionType.tokenMethodTransfer,
TransactionType.tokenMethodTransferFrom,
TransactionType.tokenMethodSafeTransferFrom,
+ TransactionType.simpleSend,
];
export const REDESIGN_DEV_TRANSACTION_TYPES = [