Skip to content

Commit e851ec6

Browse files
Merge pull request #1375 from multiversx/mm-add-guardian-change-alert-on-login
Unconfirmed Guardian Change Toast
2 parents 4d0ff99 + b7d9e11 commit e851ec6

File tree

9 files changed

+123
-4
lines changed

9 files changed

+123
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- [Added a warning toast when an unconfirmed guardian change took place](https://github.com/multiversx/mx-sdk-dapp/pull/1375)
1011
- [Fixed metamask addon link to use new Chrome Web Store domain](https://github.com/multiversx/mx-sdk-dapp/pull/1374)
1112

1213
## [[v3.2.3](https://github.com/multiversx/mx-sdk-dapp/pull/1373)] - 2025-02-06
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import { faWarning } from '@fortawesome/free-solid-svg-icons';
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4+
5+
import { WithStylesImportType } from 'hocs/useStyles';
6+
import { withStyles } from 'hocs/withStyles';
7+
8+
export const GuardianWarningToastComponent = ({
9+
styles
10+
}: WithStylesImportType) => (
11+
<div className={styles?.guardianWarningToast}>
12+
<div className={styles?.guardianWarningToastHeading}>
13+
<FontAwesomeIcon
14+
icon={faWarning}
15+
className={styles?.guardianWarningToastHeadingIcon}
16+
/>
17+
18+
<div className={styles?.guardianWarningToastHeadingText}>
19+
Account at risk!
20+
</div>
21+
</div>
22+
23+
<div className={styles?.guardianWarningToastDescription}>
24+
Your account has an unconfirmed guardian change, which could be caused by
25+
a possible breach! Be cautious before making any transaction.
26+
</div>
27+
</div>
28+
);
29+
30+
export const GuardianWarningToast = withStyles(GuardianWarningToastComponent, {
31+
ssrStyles: () =>
32+
import('UI/GuardianWarningToast/guardianWarningToastStyles.scss'),
33+
clientStyles: () =>
34+
require('UI/GuardianWarningToast/guardianWarningToastStyles.scss').default
35+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@use '../../assets/sass/variables/variables.scss';
2+
3+
.guardian-warning-toast {
4+
padding: 8px;
5+
display: flex;
6+
flex-direction: column;
7+
gap: 8px;
8+
9+
.guardian-warning-toast-heading {
10+
display: flex;
11+
gap: 8px;
12+
align-items: center;
13+
14+
.guardian-warning-toast-heading-icon {
15+
color: variables.$danger;
16+
font-size: 20px;
17+
}
18+
19+
.guardian-warning-toast-heading-text {
20+
color: variables.$danger;
21+
font-size: 16px;
22+
}
23+
}
24+
}

src/UI/GuardianWarningToast/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './GuardianWarningToast';

src/UI/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export * from './TransactionsToastList/components';
2121
export * from './Trim';
2222
export * from './Loader';
2323
export * from './Pagination';
24+
export * from './GuardianWarningToast';
2425
export * from './UsdValue';
2526
export * from './walletConnect/WalletConnectLoginButton';
2627
export * from './walletConnect/WalletConnectLoginContainer';

src/components/ProviderInitializer/ProviderInitializer.tsx

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useRef } from 'react';
2+
23
import { getNetworkConfigFromApi, useGetAccountFromApi } from 'apiCalls';
34
import {
45
DEVNET_CHAIN_ID,
@@ -15,6 +16,7 @@ import {
1516
import { loginAction } from 'reduxStore/commonActions';
1617
import { useDispatch, useSelector } from 'reduxStore/DappProviderContext';
1718
import {
19+
accountSelector,
1820
addressSelector,
1921
ledgerAccountSelector
2022
} from 'reduxStore/selectors/accountInfoSelectors';
@@ -52,34 +54,38 @@ import {
5254
refreshAccount
5355
} from 'utils/account';
5456
import { parseNavigationParams } from 'utils/parseNavigationParams';
55-
5657
import { isContract } from 'utils/smartContracts';
58+
5759
import {
5860
getOperaProvider,
5961
getCrossWindowProvider,
6062
getExtensionProvider,
6163
getPasskeyProvider,
6264
processModifiedAccount,
6365
getMetamaskProvider,
64-
getIframeProvider
66+
getIframeProvider,
67+
handleGuardianWarning
6568
} from './helpers';
6669
import { useSetLedgerProvider } from './hooks';
6770

6871
let initalizingLedger = false;
6972

7073
export function ProviderInitializer() {
74+
const { loginMethod, iframeLoginType } = useSelector(loginInfoSelector);
75+
7176
const network = useSelector(networkSelector);
7277
const walletAddress = useSelector(walletAddressSelector);
7378
const walletConnectLogin = useSelector(walletConnectLoginSelector);
74-
const { loginMethod, iframeLoginType } = useSelector(loginInfoSelector);
7579
const walletLogin = useSelector(walletLoginSelector);
7680
const address = useSelector(addressSelector);
7781
const ledgerAccount = useSelector(ledgerAccountSelector);
7882
const ledgerLogin = useSelector(ledgerLoginSelector);
7983
const isLoggedIn = useSelector(isLoggedInSelector);
8084
const chainID = useSelector(chainIDSelector);
8185
const tokenLogin = useSelector(tokenLoginSelector);
86+
const userAccount = useSelector(accountSelector);
8287
const nativeAuthConfig = tokenLogin?.nativeAuthConfig;
88+
8389
const loginService = useLoginService(
8490
nativeAuthConfig ? nativeAuthConfig : false
8591
);
@@ -124,6 +130,12 @@ export function ProviderInitializer() {
124130
setLedgerAccountInfo();
125131
}, [ledgerAccount, isLoggedIn, ledgerData]);
126132

133+
useEffect(() => {
134+
if (isLoggedIn && userAccount.address) {
135+
handleGuardianWarning(userAccount);
136+
}
137+
}, [isLoggedIn, userAccount]);
138+
127139
// We need to get the roundDuration for networks that do not support websocket (e.g. sovereign)
128140
// The round duration is used for polling interval
129141
async function refreshNetworkConfig() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import { faWarning } from '@fortawesome/free-solid-svg-icons';
3+
4+
import { AccountType } from 'types';
5+
import { GuardianWarningToast } from 'UI';
6+
import { addNewCustomToast, storage } from 'utils';
7+
import { localStorageKeys } from 'utils/storage/local';
8+
9+
const DAYS_TO_SHOW_AGAIN_AFTER_DISMISSAL = 3;
10+
const SECONDS_IN_A_DAY = 24 * 60 * 60;
11+
12+
export const handleGuardianWarning = (userAccount: AccountType) => {
13+
const handleToastDismissal = () => {
14+
const daysAsSeconds = SECONDS_IN_A_DAY * DAYS_TO_SHOW_AGAIN_AFTER_DISMISSAL;
15+
const currentTimestamp = Math.floor(Date.now() / 1000);
16+
17+
storage.local.setItem({
18+
key: localStorageKeys.guardianBreachToastDismissTimestamp,
19+
data: currentTimestamp,
20+
expires: currentTimestamp + daysAsSeconds
21+
});
22+
};
23+
24+
const guardianBreachToastDismissTimestamp = storage.local.getItem(
25+
localStorageKeys.guardianBreachToastDismissTimestamp
26+
);
27+
28+
const isGuardedAccountPendingChange =
29+
userAccount.isGuarded &&
30+
userAccount.activeGuardianAddress &&
31+
userAccount.pendingGuardianAddress;
32+
33+
if (isGuardedAccountPendingChange && !guardianBreachToastDismissTimestamp) {
34+
addNewCustomToast({
35+
toastId: 'guardianUnconfirmedChangeWarning',
36+
title: 'Account at risk!',
37+
component: () => <GuardianWarningToast />,
38+
icon: faWarning,
39+
onClose: handleToastDismissal
40+
});
41+
}
42+
};

src/components/ProviderInitializer/helpers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './processModifiedAccount';
66
export * from './getModifiedLoginToken';
77
export * from './getMetamaskProvider';
88
export * from './getPasskeyProvider';
9+
export * from './handleGuardianWarning';

src/utils/storage/local.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { getUnixTimestamp } from 'utils/dateTime';
22

33
export const localStorageKeys = {
44
loginExpiresAt: 'sdk-dapp-login-expires-at',
5-
logoutEvent: 'sdk-dapp-logout-event'
5+
logoutEvent: 'sdk-dapp-logout-event',
6+
guardianBreachToastDismissTimestamp:
7+
'sdk-dapp-guardian-breach-toast-dismiss-timestamp'
68
} as const;
79

810
type LocalValueType = keyof typeof localStorageKeys;

0 commit comments

Comments
 (0)