From 68aa72b419f9f6b568614660ad08289df0062183 Mon Sep 17 00:00:00 2001 From: Pedro Figueiredo Date: Wed, 4 Sep 2024 16:58:06 +0100 Subject: [PATCH] feat: ERC20 Revoke Allowance --- app/_locales/en/messages.json | 6 + .../erc20-approve-redesign.spec.ts | 4 +- .../increase-token-allowance-redesign.spec.ts | 30 +---- .../revoke-allowance-redesign.spec.ts | 110 ++++++++++++++++++ .../approve-static-simulation.tsx | 44 ++++--- .../confirm/info/approve/approve.test.tsx | 5 +- .../confirm/info/approve/approve.tsx | 25 +++- .../approve/hooks/use-custom-spending-cap.ts | 7 ++ .../approve/revoke-details/revoke-details.tsx | 11 ++ .../revoke-static-simulation.tsx | 53 +++++++++ .../info/approve/spending-cap-context.tsx | 33 ++++++ .../static-simulation/static-simulation.tsx | 16 +-- .../permit-simulation/permit-simulation.tsx | 54 +++++---- .../components/confirm/title/title.test.tsx | 31 +++-- .../components/confirm/title/title.tsx | 37 ++++-- ui/pages/confirmations/confirm/confirm.tsx | 49 ++++---- 16 files changed, 384 insertions(+), 131 deletions(-) create mode 100644 test/e2e/tests/confirmations/transactions/revoke-allowance-redesign.spec.ts create mode 100644 ui/pages/confirmations/components/confirm/info/approve/hooks/use-custom-spending-cap.ts create mode 100644 ui/pages/confirmations/components/confirm/info/approve/revoke-details/revoke-details.tsx create mode 100644 ui/pages/confirmations/components/confirm/info/approve/revoke-static-simulation/revoke-static-simulation.tsx create mode 100644 ui/pages/confirmations/components/confirm/info/approve/spending-cap-context.tsx diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f0a32ba6b755..2ee5abebc47e 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1084,6 +1084,9 @@ "confirmTitlePermitSignature": { "message": "Spending cap request" }, + "confirmTitleRevokeApproveTransaction": { + "message": "Remove permission" + }, "confirmTitleSIWESignature": { "message": "Sign-in request" }, @@ -4527,6 +4530,9 @@ "revokePermission": { "message": "Revoke permission" }, + "revokeSimulationDetailsDesc": { + "message": "You're removing someone's permission to spend tokens from your account." + }, "revokeSpendingCap": { "message": "Revoke spending cap for your $1", "description": "$1 is a token symbol" diff --git a/test/e2e/tests/confirmations/transactions/erc20-approve-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/erc20-approve-redesign.spec.ts index 60a141144833..da92ca4bdb09 100644 --- a/test/e2e/tests/confirmations/transactions/erc20-approve-redesign.spec.ts +++ b/test/e2e/tests/confirmations/transactions/erc20-approve-redesign.spec.ts @@ -87,7 +87,7 @@ describe('Confirmation Redesign ERC20 Approve Component', function () { }); }); -async function mocked4Bytes(mockServer: MockttpServer) { +export async function mocked4BytesApprove(mockServer: MockttpServer) { return await mockServer .forGet('https://www.4byte.directory/api/v1/signatures/') .withQuery({ hex_signature: '0x095ea7b3' }) @@ -111,7 +111,7 @@ async function mocked4Bytes(mockServer: MockttpServer) { } async function mocks(server: MockttpServer) { - return [await mocked4Bytes(server)]; + return [await mocked4BytesApprove(server)]; } export async function importTST(driver: Driver) { diff --git a/test/e2e/tests/confirmations/transactions/increase-token-allowance-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/increase-token-allowance-redesign.spec.ts index a64da891e447..a37d1c78b199 100644 --- a/test/e2e/tests/confirmations/transactions/increase-token-allowance-redesign.spec.ts +++ b/test/e2e/tests/confirmations/transactions/increase-token-allowance-redesign.spec.ts @@ -8,6 +8,7 @@ import { import { Driver } from '../../../webdriver/driver'; import { scrollAndConfirmAndAssertConfirm } from '../helpers'; import { openDAppWithContract, TestSuiteArguments } from './shared'; +import { mocked4BytesApprove } from './erc20-approve-redesign.spec'; const { defaultGanacheOptions, @@ -155,30 +156,7 @@ describe('Confirmation Redesign ERC20 Increase Allowance', function () { }); }); -async function mocked4BytesApprove(mockServer: MockttpServer) { - return await mockServer - .forGet('https://www.4byte.directory/api/v1/signatures/') - .withQuery({ hex_signature: '0x095ea7b3' }) - .thenCallback(() => ({ - statusCode: 200, - json: { - count: 1, - next: null, - previous: null, - results: [ - { - id: 149, - created_at: '2016-07-09T03:58:29.617584Z', - text_signature: 'approve(address,uint256)', - hex_signature: '0x095ea7b3', - bytes_signature: '\t^§³', - }, - ], - }, - })); -} - -async function mocked4BytesIncreaseAllowance(mockServer: MockttpServer) { +export async function mocked4BytesIncreaseAllowance(mockServer: MockttpServer) { return await mockServer .forGet('https://www.4byte.directory/api/v1/signatures/') .withQuery({ hex_signature: '0x39509351' }) @@ -213,7 +191,7 @@ async function createERC20IncreaseAllowanceTransaction(driver: Driver) { await driver.clickElement('#increaseTokenAllowance'); } -async function editSpendingCap(driver: Driver, newSpendingCap: string) { +export async function editSpendingCap(driver: Driver, newSpendingCap: string) { await driver.delay(largeDelayMs); await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await driver.clickElement('[data-testid="edit-spending-cap-icon"'); @@ -231,7 +209,7 @@ async function editSpendingCap(driver: Driver, newSpendingCap: string) { await driver.delay(veryLargeDelayMs * 2); } -async function assertChangedSpendingCap( +export async function assertChangedSpendingCap( driver: Driver, newSpendingCap: string, ) { diff --git a/test/e2e/tests/confirmations/transactions/revoke-allowance-redesign.spec.ts b/test/e2e/tests/confirmations/transactions/revoke-allowance-redesign.spec.ts new file mode 100644 index 000000000000..ba97d9cda4cd --- /dev/null +++ b/test/e2e/tests/confirmations/transactions/revoke-allowance-redesign.spec.ts @@ -0,0 +1,110 @@ +/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */ +import { MockttpServer } from 'mockttp'; +import { WINDOW_TITLES } from '../../../helpers'; +import { Driver } from '../../../webdriver/driver'; +import { scrollAndConfirmAndAssertConfirm } from '../helpers'; +import { mocked4BytesApprove } from './erc20-approve-redesign.spec'; +import { + assertChangedSpendingCap, + editSpendingCap, +} from './increase-token-allowance-redesign.spec'; +import { openDAppWithContract, TestSuiteArguments } from './shared'; + +const { + defaultGanacheOptions, + defaultGanacheOptionsForType2Transactions, + withFixtures, +} = require('../../../helpers'); +const FixtureBuilder = require('../../../fixture-builder'); +const { SMART_CONTRACTS } = require('../../../seeder/smart-contracts'); + +describe('Confirmation Redesign ERC20 Revoke Allowance', function () { + const smartContract = SMART_CONTRACTS.HST; + + describe('Submit an revoke transaction @no-mmi', function () { + it('Sends a type 0 transaction (Legacy)', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + smartContract, + testSpecificMock: mocks, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await openDAppWithContract(driver, contractRegistry, smartContract); + + await createERC20ApproveTransaction(driver); + + const NEW_SPENDING_CAP = '0'; + await editSpendingCap(driver, NEW_SPENDING_CAP); + + await driver.waitForSelector({ + css: 'h2', + text: 'Remove permission', + }); + + await scrollAndConfirmAndAssertConfirm(driver); + + await assertChangedSpendingCap(driver, NEW_SPENDING_CAP); + }, + ); + }); + + it('Sends a type 2 transaction (EIP1559)', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .withPreferencesController({ + preferences: { + redesignedConfirmationsEnabled: true, + isRedesignedConfirmationsDeveloperEnabled: true, + }, + }) + .build(), + ganacheOptions: defaultGanacheOptionsForType2Transactions, + smartContract, + testSpecificMock: mocks, + title: this.test?.fullTitle(), + }, + async ({ driver, contractRegistry }: TestSuiteArguments) => { + await openDAppWithContract(driver, contractRegistry, smartContract); + + await createERC20ApproveTransaction(driver); + + const NEW_SPENDING_CAP = '0'; + await editSpendingCap(driver, NEW_SPENDING_CAP); + + await driver.waitForSelector({ + css: 'h2', + text: 'Remove permission', + }); + + await scrollAndConfirmAndAssertConfirm(driver); + + await assertChangedSpendingCap(driver, NEW_SPENDING_CAP); + }, + ); + }); + }); +}); + +async function mocks(server: MockttpServer) { + return [await mocked4BytesApprove(server)]; +} + +async function createERC20ApproveTransaction(driver: Driver) { + await driver.switchToWindowWithTitle(WINDOW_TITLES.TestDApp); + await driver.clickElement('#approveTokens'); +} diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx index 59bd3350ca24..8fd4c55e353a 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve-static-simulation/approve-static-simulation.tsx @@ -1,6 +1,7 @@ import { NameType } from '@metamask/name-controller'; import { TransactionMeta } from '@metamask/transaction-controller'; import React from 'react'; +import { ConfirmInfoRow } from '../../../../../../../components/app/confirm/info/row'; import Name from '../../../../../../../components/app/name'; import { Box, Text } from '../../../../../../../components/component-library'; import Tooltip from '../../../../../../../components/ui/tooltip'; @@ -65,23 +66,31 @@ export const ApproveStaticSimulation = () => { ); const simulationElements = ( - - - {spendingCap === UNLIMITED_MSG ? ( - {formattedTokenText} - ) : ( - formattedTokenText - )} + + + + + {spendingCap === UNLIMITED_MSG ? ( + + {formattedTokenText} + + ) : ( + formattedTokenText + )} + + + - - + ); return ( @@ -89,9 +98,6 @@ export const ApproveStaticSimulation = () => { title={t('simulationDetailsTitle')} titleTooltip={t('simulationDetailsTitleTooltip')} description={t('simulationDetailsApproveDesc')} - simulationHeading={ - isNFT ? t('simulationApproveHeading') : t('spendingCap') - } simulationElements={simulationElements} /> ); diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx index 9a64326ad029..82e1718c0a4d 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.test.tsx @@ -4,6 +4,7 @@ import thunk from 'redux-thunk'; import { getMockApproveConfirmState } from '../../../../../../../test/data/confirmations/helper'; import { renderWithConfirmContextProvider } from '../../../../../../../test/lib/confirmations/render-helpers'; import ApproveInfo from './approve'; +import { SpendingCapProvider } from './spending-cap-context'; jest.mock('../../../../../../store/actions', () => ({ ...jest.requireActual('../../../../../../store/actions'), @@ -69,7 +70,9 @@ describe('', () => { const mockStore = configureMockStore(middleware)(state); const { container } = renderWithConfirmContextProvider( - , + + + , mockStore, ); diff --git a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx index ddcb015e5b86..a3d447dfa06e 100644 --- a/ui/pages/confirmations/components/confirm/info/approve/approve.tsx +++ b/ui/pages/confirmations/components/confirm/info/approve/approve.tsx @@ -1,4 +1,7 @@ -import { TransactionMeta } from '@metamask/transaction-controller'; +import { + TransactionMeta, + TransactionType, +} from '@metamask/transaction-controller'; import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import { useConfirmContext } from '../../../../context/confirm'; @@ -9,6 +12,9 @@ import { ApproveDetails } from './approve-details/approve-details'; import { ApproveStaticSimulation } from './approve-static-simulation/approve-static-simulation'; import { EditSpendingCapModal } from './edit-spending-cap-modal/edit-spending-cap-modal'; import { useIsNFT } from './hooks/use-is-nft'; +import { RevokeDetails } from './revoke-details/revoke-details'; +import { RevokeStaticSimulation } from './revoke-static-simulation/revoke-static-simulation'; +import { useSpendingCapContext } from './spending-cap-context'; import { SpendingCap } from './spending-cap/spending-cap'; const ApproveInfo = () => { @@ -24,7 +30,8 @@ const ApproveInfo = () => { const [isOpenEditSpendingCapModal, setIsOpenEditSpendingCapModal] = useState(false); - const [customSpendingCap, setCustomSpendingCap] = useState(''); + + const { customSpendingCap, setCustomSpendingCap } = useSpendingCapContext(); const setCustomSpendingCapCandidate = (newValue: string) => { const value = parseInt(newValue, 10); @@ -36,11 +43,19 @@ const ApproveInfo = () => { return null; } + const showRevokeScreen = + customSpendingCap === '0' && + transactionMeta.type === TransactionType.tokenMethodApprove; + return ( <> - - - {!isNFT && ( + {showRevokeScreen ? ( + + ) : ( + + )} + {showRevokeScreen ? : } + {!isNFT && !showRevokeScreen && ( { + const [customSpendingCap, setCustomSpendingCap] = useState(''); + + return { customSpendingCap, setCustomSpendingCap }; +}; diff --git a/ui/pages/confirmations/components/confirm/info/approve/revoke-details/revoke-details.tsx b/ui/pages/confirmations/components/confirm/info/approve/revoke-details/revoke-details.tsx new file mode 100644 index 000000000000..49bf5e7724e1 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/revoke-details/revoke-details.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; +import { OriginRow } from '../../shared/transaction-details/transaction-details'; + +export const RevokeDetails = () => { + return ( + + + + ); +}; diff --git a/ui/pages/confirmations/components/confirm/info/approve/revoke-static-simulation/revoke-static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/approve/revoke-static-simulation/revoke-static-simulation.tsx new file mode 100644 index 000000000000..5e51722a7479 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/revoke-static-simulation/revoke-static-simulation.tsx @@ -0,0 +1,53 @@ +import { NameType } from '@metamask/name-controller'; +import { TransactionMeta } from '@metamask/transaction-controller'; +import React from 'react'; +import { ConfirmInfoRow } from '../../../../../../../components/app/confirm/info/row'; +import Name from '../../../../../../../components/app/name'; +import { Box } from '../../../../../../../components/component-library'; +import { Display } from '../../../../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../../../../hooks/useI18nContext'; +import { useConfirmContext } from '../../../../../context/confirm'; +import StaticSimulation from '../../shared/static-simulation/static-simulation'; + +export const RevokeStaticSimulation = () => { + const t = useI18nContext(); + + const { currentConfirmation: transactionMeta } = useConfirmContext() as { + currentConfirmation: TransactionMeta; + }; + + const simulationElements = ( + <> + + + + + + + + + + + + + + + + + ); + + return ( + + ); +}; diff --git a/ui/pages/confirmations/components/confirm/info/approve/spending-cap-context.tsx b/ui/pages/confirmations/components/confirm/info/approve/spending-cap-context.tsx new file mode 100644 index 000000000000..820d7207c2e7 --- /dev/null +++ b/ui/pages/confirmations/components/confirm/info/approve/spending-cap-context.tsx @@ -0,0 +1,33 @@ +import PropTypes from 'prop-types'; +import React, { createContext, ReactNode, useContext, useState } from 'react'; + +const SpendingCapContext = createContext<{ + customSpendingCap: string; + setCustomSpendingCap: (newCap: string) => void; +} | null>(null); + +export const SpendingCapProvider = ({ children }: { children: ReactNode }) => { + const [customSpendingCap, setCustomSpendingCap] = useState(''); + + return ( + + {children} + + ); +}; + +export const useSpendingCapContext = () => { + const context = useContext(SpendingCapContext); + if (!context) { + throw new Error( + 'useSpendingCapContext must be used within an SpendingCapProvider', + ); + } + return context; +}; + +SpendingCapProvider.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx b/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx index 3414a1aee94a..7901854b91d1 100644 --- a/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/shared/static-simulation/static-simulation.tsx @@ -4,31 +4,19 @@ import { ConfirmInfoRowText, } from '../../../../../../../components/app/confirm/info/row'; import { ConfirmInfoSection } from '../../../../../../../components/app/confirm/info/row/section'; -import { Box } from '../../../../../../../components/component-library'; const StaticSimulation: React.FC<{ title: string; titleTooltip: string; description: string; - simulationHeading: string; simulationElements: React.ReactNode; -}> = ({ - title, - titleTooltip, - description, - simulationHeading, - simulationElements, -}) => { +}> = ({ title, titleTooltip, description, simulationElements }) => { return ( - - - {simulationElements} - - + {simulationElements} ); }; diff --git a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx index 847558e07f6c..35173db46b1b 100644 --- a/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx +++ b/ui/pages/confirmations/components/confirm/info/typed-sign/permit-simulation/permit-simulation.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { PrimaryType } from '../../../../../../../../shared/constants/signatures'; import { parseTypedDataMessage } from '../../../../../../../../shared/modules/transaction.utils'; +import { ConfirmInfoRow } from '../../../../../../../components/app/confirm/info/row'; import { Box } from '../../../../../../../components/component-library'; import { Display, @@ -56,34 +57,37 @@ const PermitSimulation: React.FC = () => { title={t('simulationDetailsTitle')} titleTooltip={t('simulationDetailsTitleTooltip')} description={t('permitSimulationDetailInfo')} - simulationHeading={t('spendingCap')} simulationElements={ - Array.isArray(tokenDetails) ? ( - - {tokenDetails.map( - ( - { token, amount }: { token: string; amount: string }, - i: number, - ) => ( - - ), + + + {Array.isArray(tokenDetails) ? ( + + {tokenDetails.map( + ( + { token, amount }: { token: string; amount: string }, + i: number, + ) => ( + + ), + )} + + ) : ( + )} - ) : ( - - ) + } /> ); diff --git a/ui/pages/confirmations/components/confirm/title/title.test.tsx b/ui/pages/confirmations/components/confirm/title/title.test.tsx index d886eccc7ac6..cbe5028fcae5 100644 --- a/ui/pages/confirmations/components/confirm/title/title.test.tsx +++ b/ui/pages/confirmations/components/confirm/title/title.test.tsx @@ -8,21 +8,24 @@ import { getMockTypedSignConfirmState, getMockTypedSignConfirmStateForRequest, } from '../../../../../../test/data/confirmations/helper'; -import { renderWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; -import { permitSignatureMsg } from '../../../../../../test/data/confirmations/typed_sign'; import { unapprovedPersonalSignMsg } from '../../../../../../test/data/confirmations/personal_sign'; -import { Severity } from '../../../../../helpers/constants/design-system'; +import { permitSignatureMsg } from '../../../../../../test/data/confirmations/typed_sign'; +import { renderWithConfirmContextProvider } from '../../../../../../test/lib/confirmations/render-helpers'; import { Alert, ConfirmAlertsState, } from '../../../../../ducks/confirm-alerts/confirm-alerts'; +import { Severity } from '../../../../../helpers/constants/design-system'; +import { SpendingCapProvider } from '../info/approve/spending-cap-context'; import ConfirmTitle from './title'; describe('ConfirmTitle', () => { it('should render the title and description for a personal signature', () => { const mockStore = configureMockStore([])(getMockPersonalSignConfirmState); const { getByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); @@ -39,7 +42,9 @@ describe('ConfirmTitle', () => { getMockTypedSignConfirmStateForRequest(permitSignatureMsg), ); const { getByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); @@ -52,7 +57,9 @@ describe('ConfirmTitle', () => { it('should render the title and description for typed signature', () => { const mockStore = configureMockStore([])(getMockTypedSignConfirmState()); const { getByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); @@ -69,7 +76,9 @@ describe('ConfirmTitle', () => { getMockContractInteractionConfirmState(), ); const { getByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); @@ -107,7 +116,9 @@ describe('ConfirmTitle', () => { }), ); const { queryByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); @@ -119,7 +130,9 @@ describe('ConfirmTitle', () => { const mockStore = configureMockStore([])(mockAlertState()); const { getByText } = renderWithConfirmContextProvider( - , + + + , mockStore, ); diff --git a/ui/pages/confirmations/components/confirm/title/title.tsx b/ui/pages/confirmations/components/confirm/title/title.tsx index 04370b006e94..5fbb7de1df56 100644 --- a/ui/pages/confirmations/components/confirm/title/title.tsx +++ b/ui/pages/confirmations/components/confirm/title/title.tsx @@ -4,6 +4,8 @@ import { } from '@metamask/transaction-controller'; import React, { memo, useMemo } from 'react'; +import GeneralAlert from '../../../../../components/app/alert-system/general-alert/general-alert'; +import { getHighestSeverity } from '../../../../../components/app/alert-system/utils'; import { Box, Text } from '../../../../../components/component-library'; import { TextAlign, @@ -12,15 +14,14 @@ import { } from '../../../../../helpers/constants/design-system'; import useAlerts from '../../../../../hooks/useAlerts'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; -import { getHighestSeverity } from '../../../../../components/app/alert-system/utils'; -import GeneralAlert from '../../../../../components/app/alert-system/general-alert/general-alert'; +import { useConfirmContext } from '../../../context/confirm'; import { Confirmation, SignatureRequestType } from '../../../types/confirm'; import { isPermitSignatureRequest, isSIWESignatureRequest, } from '../../../utils'; -import { useConfirmContext } from '../../../context/confirm'; import { useIsNFT } from '../info/approve/hooks/use-is-nft'; +import { useSpendingCapContext } from '../info/approve/spending-cap-context'; function ConfirmBannerAlert({ ownerId }: { ownerId: string }) { const t = useI18nContext(); @@ -64,6 +65,7 @@ const getTitle = ( t: IntlFunction, confirmation?: Confirmation, isNFT?: boolean, + customSpendingCap?: string, ) => { switch (confirmation?.type) { case TransactionType.contractInteraction: @@ -72,6 +74,9 @@ const getTitle = ( if (isNFT) { return t('confirmTitleApproveTransaction'); } + if (customSpendingCap === '0') { + return t('confirmTitleRevokeApproveTransaction'); + } return t('confirmTitlePermitSignature'); case TransactionType.deployContract: return t('confirmTitleDeployContract'); @@ -93,6 +98,7 @@ const getDescription = ( t: IntlFunction, confirmation?: Confirmation, isNFT?: boolean, + customSpendingCap?: string, ) => { switch (confirmation?.type) { case TransactionType.contractInteraction: @@ -101,6 +107,9 @@ const getDescription = ( if (isNFT) { return t('confirmTitleDescApproveTransaction'); } + if (customSpendingCap === '0') { + return ''; + } return t('confirmTitleDescERC20ApproveTransaction'); case TransactionType.deployContract: return t('confirmTitleDescDeployContract'); @@ -124,14 +133,28 @@ const ConfirmTitle: React.FC = memo(() => { const { isNFT } = useIsNFT(currentConfirmation as TransactionMeta); + const { customSpendingCap } = useSpendingCapContext(); + const title = useMemo( - () => getTitle(t as IntlFunction, currentConfirmation, isNFT), - [currentConfirmation, isNFT], + () => + getTitle( + t as IntlFunction, + currentConfirmation, + isNFT, + customSpendingCap, + ), + [currentConfirmation, isNFT, customSpendingCap], ); const description = useMemo( - () => getDescription(t as IntlFunction, currentConfirmation, isNFT), - [currentConfirmation, isNFT], + () => + getDescription( + t as IntlFunction, + currentConfirmation, + isNFT, + customSpendingCap, + ), + [currentConfirmation, isNFT, customSpendingCap], ); if (!currentConfirmation) { diff --git a/ui/pages/confirmations/confirm/confirm.tsx b/ui/pages/confirmations/confirm/confirm.tsx index 8f98cef1e9a7..0ab81d2cbce9 100644 --- a/ui/pages/confirmations/confirm/confirm.tsx +++ b/ui/pages/confirmations/confirm/confirm.tsx @@ -11,16 +11,17 @@ import { ConfirmAlerts } from '../components/confirm/confirm-alerts'; import { Footer } from '../components/confirm/footer'; import { Header } from '../components/confirm/header'; import { Info } from '../components/confirm/info'; +import { SpendingCapProvider } from '../components/confirm/info/approve/spending-cap-context'; import { LedgerInfo } from '../components/confirm/ledger-info'; import { Nav } from '../components/confirm/nav'; +import { NetworkChangeToast } from '../components/confirm/network-change-toast'; import { PluggableSection } from '../components/confirm/pluggable-section'; import ScrollToBottom from '../components/confirm/scroll-to-bottom'; import { Title } from '../components/confirm/title'; import EditGasFeePopover from '../components/edit-gas-fee-popover'; -import { NetworkChangeToast } from '../components/confirm/network-change-toast'; +import { ConfirmContextProvider } from '../context/confirm'; import setCurrentConfirmation from '../hooks/setCurrentConfirmation'; import syncConfirmPath from '../hooks/syncConfirmPath'; -import { ConfirmContextProvider } from '../context/confirm'; const EIP1559TransactionGasModal = () => { return ( @@ -40,27 +41,29 @@ const Confirm = () => { {/* This context should be removed once we implement the new edit gas fees popovers */} - - - -