diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index def53a578..823c012c5 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -103,9 +103,6 @@
"SIWEWarningTitle": {
"message": "Are you sure?"
},
- "ShowMore": {
- "message": "Show more"
- },
"about": {
"message": "About"
},
@@ -3278,6 +3275,12 @@
"removeAccountDescription": {
"message": "This account will be removed from your wallet. Please make sure you have the original Secret Recovery Phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. "
},
+ "removeJWT": {
+ "message": "Remove custodian token"
+ },
+ "removeJWTDescription": {
+ "message": "Are you sure you want to remove this token? All accounts assigned to this token will be removed from extension as well: "
+ },
"removeNFT": {
"message": "Remove NFT"
},
@@ -3661,6 +3664,9 @@
"message": "This relies on $1 which will have access to your Ethereum address and your IP address. $2",
"description": "$1 is the link to etherscan url and $2 is the link to the privacy policy of consensys APIs"
},
+ "showMore": {
+ "message": "Show more"
+ },
"showPermissions": {
"message": "Show permissions"
},
diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss
index fd7334459..1a4b9eac3 100644
--- a/ui/components/app/app-components.scss
+++ b/ui/components/app/app-components.scss
@@ -106,6 +106,7 @@
@import 'approve-content-card/index';
@import 'transaction-alerts/transaction-alerts';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
+@import '../institutional/confirm-remove-jwt-modal/index';
@import '../institutional/custody-confirm-link-modal/index';
@import '../institutional/transaction-failed-modal/index';
///: END:ONLY_INCLUDE_IN
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/__snapshots__/confirm-remove-jwt-modal.test.js.snap b/ui/components/institutional/confirm-remove-jwt-modal/__snapshots__/confirm-remove-jwt-modal.test.js.snap
new file mode 100644
index 000000000..9b0373da0
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/__snapshots__/confirm-remove-jwt-modal.test.js.snap
@@ -0,0 +1,151 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Confirm Remove JWT should render correctly 1`] = `
+
+
+
+
+
+ ...Dce07538D
+
+
+
+ Show more
+
+
+
+ Are you sure you want to remove this token? All accounts assigned to this token will be removed from extension as well:
+
+
+
+
+
+
+`;
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.js b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.js
new file mode 100644
index 000000000..b59f12538
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.js
@@ -0,0 +1,125 @@
+import React, { memo, useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import { useDispatch } from 'react-redux';
+import Modal from '../../app/modal';
+import CustodyAccountList from '../../../pages/institutional/connect-custody/account-list';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import { removeAccount } from '../../../store/actions';
+import withModalProps from '../../../helpers/higher-order-components/with-modal-props';
+import { Text } from '../../component-library';
+import Box from '../../ui/box';
+import {
+ BorderRadius,
+ DISPLAY,
+ TEXT_ALIGN,
+ TextColor,
+ TextVariant,
+} from '../../../helpers/constants/design-system';
+
+const ConfirmRemoveJWT = ({
+ custodyAccountDetails,
+ accounts,
+ token,
+ hideModal,
+}) => {
+ const t = useI18nContext();
+ const dispatch = useDispatch();
+ const [showMore, setShowMore] = useState(false);
+ const [tokenAccounts, setTokenAccounts] = useState([]);
+
+ useEffect(() => {
+ const lowercasedTokenAddress = token.address.toLowerCase();
+
+ const filteredAccounts = custodyAccountDetails.filter((item) => {
+ const addressLower = item.address.toLowerCase();
+ return accounts.find((acc) => acc.address.toLowerCase() === addressLower);
+ });
+
+ const tokens = filteredAccounts
+ .filter(({ authDetails }) => {
+ const getToken =
+ authDetails?.token ?? authDetails?.jwt ?? authDetails?.refreshToken;
+ return getToken?.toLowerCase() === lowercasedTokenAddress;
+ })
+ .map(({ address, name, labels, authDetails }) => {
+ const lowercasedAddress = address.toLowerCase();
+ const account = accounts.find(
+ ({ address: adressAcc }) =>
+ adressAcc.toLowerCase() === lowercasedAddress,
+ );
+ const balance = account?.balance;
+ const getToken =
+ authDetails?.token ?? authDetails?.jwt ?? authDetails?.refreshToken;
+ return { address, name, labels, balance, token: getToken };
+ });
+
+ setTokenAccounts(tokens);
+ }, [accounts, custodyAccountDetails, token]);
+
+ const handleRemove = async () => {
+ try {
+ for (const account of tokenAccounts) {
+ await dispatch(removeAccount(account.address.toLowerCase()));
+ }
+ hideModal();
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const handleShowMore = () => {
+ setShowMore(true);
+ };
+
+ return (
+
+
+ {showMore && token ? token.address : `...${token.address.slice(-9)}`}
+
+ {!showMore && (
+
+
+ {t('showMore')}
+
+
+ )}
+
+ {t('removeJWTDescription')}
+
+
+
+
+
+ );
+};
+
+ConfirmRemoveJWT.propTypes = {
+ hideModal: PropTypes.func.isRequired,
+ token: PropTypes.object.isRequired,
+ custodyAccountDetails: PropTypes.array.isRequired,
+ accounts: PropTypes.array.isRequired,
+};
+
+export default withModalProps(memo(ConfirmRemoveJWT));
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.stories.js b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.stories.js
new file mode 100644
index 000000000..ae5590c14
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.stories.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import ConfirmRemoveJWT from '.';
+
+export default {
+ title: 'Components/Institutional/ConfirmRemoveJWT',
+ component: ConfirmRemoveJWT,
+ args: {
+ hideModal: () => {
+ /**/
+ },
+ removeAccount: () => {
+ /**/
+ },
+ token: { address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D' },
+ custodyAccountDetails: [
+ {
+ address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
+ name: 'Test name account',
+ labels: [],
+ authDetails: { token: '0xaD6D458402F60fD3Bd25163575031ACDce07538D' },
+ },
+ ],
+ accounts: [
+ { address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D', balance: '0x0' },
+ ],
+ },
+};
+
+export const DefaultStory = (args) => ;
+
+DefaultStory.storyName = 'ConfirmRemoveJWT';
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.test.js b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.test.js
new file mode 100644
index 000000000..4416ede93
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/confirm-remove-jwt-modal.test.js
@@ -0,0 +1,71 @@
+import React from 'react';
+import { fireEvent, waitFor } from '@testing-library/react';
+import configureMockStore from 'redux-mock-store';
+import thunk from 'redux-thunk';
+import { renderWithProvider } from '../../../../test/lib/render-helpers';
+import testData from '../../../../.storybook/test-data';
+import ConfirmRemoveJwt from '.';
+
+const mockedRemoveAccount = jest.fn();
+const mockedHideModal = jest.fn();
+
+jest.mock('../../../store/actions', () => ({
+ removeAccount: () => mockedRemoveAccount,
+ hideModal: () => mockedHideModal,
+}));
+
+const address = '0xaD6D458402F60fD3Bd25163575031ACDce07538D';
+
+const props = {
+ hideModal: mockedHideModal,
+ token: { address },
+ custodyAccountDetails: [
+ {
+ address,
+ name: 'Test name account',
+ labels: [],
+ authDetails: { token: address },
+ },
+ ],
+ accounts: [{ address, balance: '0x0' }],
+};
+
+const mockStore = {
+ ...testData,
+ metamask: {},
+};
+
+const middleware = [thunk];
+const store = configureMockStore(middleware)(mockStore);
+
+const render = () => {
+ return renderWithProvider(, store);
+};
+
+describe('Confirm Remove JWT', function () {
+ it('should render correctly', () => {
+ const { container } = render();
+
+ expect(container).toMatchSnapshot();
+ });
+
+ it('should show full token address when "show more" is clicked', () => {
+ const { getByText } = render();
+
+ const showMoreLink = getByText('Show more');
+ fireEvent.click(showMoreLink);
+
+ const fullTokenAddress = getByText(address);
+ expect(fullTokenAddress).toBeInTheDocument();
+ });
+
+ it('dispatches removeAccount action when user clicks remove button', async () => {
+ const { getByText } = render();
+
+ const removeButton = getByText('Remove');
+ fireEvent.click(removeButton);
+
+ await waitFor(() => expect(mockedRemoveAccount).toHaveBeenCalled());
+ expect(mockedHideModal).toHaveBeenCalled();
+ });
+});
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/index.js b/ui/components/institutional/confirm-remove-jwt-modal/index.js
new file mode 100644
index 000000000..3f27ef6b4
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/index.js
@@ -0,0 +1 @@
+export { default } from './confirm-remove-jwt-modal';
diff --git a/ui/components/institutional/confirm-remove-jwt-modal/index.scss b/ui/components/institutional/confirm-remove-jwt-modal/index.scss
new file mode 100644
index 000000000..1aff21359
--- /dev/null
+++ b/ui/components/institutional/confirm-remove-jwt-modal/index.scss
@@ -0,0 +1,13 @@
+.confirm-action-jwt {
+ &__jwt {
+ border: 1px solid var(--color-border-muted);
+ }
+
+ &__show-more {
+ cursor: pointer;
+ }
+
+ &__accounts-list {
+ margin-left: -5%;
+ }
+}
diff --git a/ui/pages/institutional/confirm-add-custodian-token/confirm-add-custodian-token.js b/ui/pages/institutional/confirm-add-custodian-token/confirm-add-custodian-token.js
index ec57d5781..15c879463 100644
--- a/ui/pages/institutional/confirm-add-custodian-token/confirm-add-custodian-token.js
+++ b/ui/pages/institutional/confirm-add-custodian-token/confirm-add-custodian-token.js
@@ -79,7 +79,7 @@ const ConfirmAddCustodianToken = () => {
setShowMore(true);
}}
>
- {t('ShowMore')}
+ {t('showMore')}
)}
diff --git a/ui/pages/institutional/connect-custody/account-list.js b/ui/pages/institutional/connect-custody/account-list.js
index 676093b33..ca7de18b4 100644
--- a/ui/pages/institutional/connect-custody/account-list.js
+++ b/ui/pages/institutional/connect-custody/account-list.js
@@ -34,7 +34,7 @@ export default function CustodyAccountList({
rawList,
accounts,
onAccountChange,
- selectedAccounts,
+ selectedAccounts = {},
onCancel,
onAddAccounts,
custody,