From 60ecc4ed09ca2b2d9ea2f0e076f690126d4d3e4f Mon Sep 17 00:00:00 2001 From: Kathy Luo Date: Tue, 4 Feb 2025 10:12:18 +0100 Subject: [PATCH 01/13] feat: prevent actions that require private key in demo mode (#6469) ### Description Unfortunately not all flows that access the private key go through the same functions, so this PR adds a new blocking bottom sheet that is a screen and navigates to it from: 1. `ensurePincode` - this is used from the flows that launch CAB / reset pin / view recovery phrase 2. `sendPreparedTransaction` - this is used from all the transaction flows, except for walletconnect 3. walletconnect saga, where we display the action request. There is often UI that awaits the outcome of the transaction send flows (spinners, navigation etc.) so I am returning the same result as if a user dismissed the pincode screen during the flow. ### Test plan Tested all flows to ensure the bottom sheet is correctly triggered, and no extra errors are displayed. https://github.com/user-attachments/assets/f376d1a7-a62c-43fa-b8a3-c598f81711d8 https://github.com/user-attachments/assets/eb817506-1cc1-4a8f-91c0-f9f3560e1455 https://github.com/user-attachments/assets/7bcdf0ee-00d9-465c-89c7-41f613669440 https://github.com/user-attachments/assets/5fbedfcd-7e2d-4b00-b61f-cec38788eb75 https://github.com/user-attachments/assets/55d26718-fa74-4406-a02f-257fdbc293c9 ### Related issues - Fixes RET-1304 ### Backwards compatibility Y ### Network scalability If a new NetworkId and/or Network are added in the future, the changes in this PR will: - [ ] Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added) --- locales/base/translation.json | 5 ++ src/navigator/DemoModeAuthBlock.test.tsx | 60 +++++++++++++++++++++++ src/navigator/DemoModeAuthBlock.tsx | 61 ++++++++++++++++++++++++ src/navigator/NavigationService.ts | 9 +++- src/navigator/Navigator.tsx | 2 + src/navigator/NavigatorWrapper.tsx | 9 +++- src/navigator/Screens.tsx | 1 + src/navigator/types.tsx | 1 + src/send/EnterAmountOptions.tsx | 5 +- src/styles/variables.tsx | 1 + src/swap/saga.ts | 10 +--- src/viem/saga.ts | 10 ++++ src/walletConnect/saga.ts | 9 +++- test/RootStateSchema.json | 2 + 14 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 src/navigator/DemoModeAuthBlock.test.tsx create mode 100644 src/navigator/DemoModeAuthBlock.tsx diff --git a/locales/base/translation.json b/locales/base/translation.json index fb66bde90d7..f2008ef57ea 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -2869,6 +2869,11 @@ "info": "You are currently viewing the app in demo mode", "cta": "Exit Demo" }, + "restrictedAccess": { + "title": "In Demo Mode", + "info": "You can't complete this action because you are viewing the app in demo mode", + "cta": "Exit Demo" + }, "inAppIndicatorLabel": "Demo Mode" } } diff --git a/src/navigator/DemoModeAuthBlock.test.tsx b/src/navigator/DemoModeAuthBlock.test.tsx new file mode 100644 index 00000000000..1e199353eb3 --- /dev/null +++ b/src/navigator/DemoModeAuthBlock.test.tsx @@ -0,0 +1,60 @@ +import { fireEvent, render } from '@testing-library/react-native' +import React from 'react' +import { Provider } from 'react-redux' +import DemoModeAuthBlock from 'src/navigator/DemoModeAuthBlock' +import { navigateBack, navigateClearingStack } from 'src/navigator/NavigationService' +import { demoModeToggled } from 'src/web3/actions' +import { createMockStore } from 'test/utils' +import { mockAccount } from 'test/values' + +jest.mock('src/statsig') + +describe('DemoModeAuthBlock', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('renders correctly and executes the expected actions on button press', () => { + const store = createMockStore({ + web3: { + account: mockAccount, + }, + }) + const { getByText } = render( + + + + ) + + expect(getByText('demoMode.restrictedAccess.title')).toBeTruthy() + expect(getByText('demoMode.restrictedAccess.info')).toBeTruthy() + + fireEvent.press(getByText('demoMode.restrictedAccess.cta')) + + expect(store.getActions()).toEqual([demoModeToggled(false)]) + expect(navigateBack).toHaveBeenCalledTimes(1) + expect(navigateClearingStack).not.toHaveBeenCalled() + + fireEvent.press(getByText('dismiss')) + expect(navigateBack).toHaveBeenCalledTimes(2) + }) + + it('navigates to onboarding when exiting demo mode', () => { + const store = createMockStore({ + web3: { + account: null, // no wallet set up + }, + }) + const { getByText } = render( + + + + ) + + fireEvent.press(getByText('demoMode.restrictedAccess.cta')) + + expect(store.getActions()).toEqual([demoModeToggled(false)]) + expect(navigateBack).toHaveBeenCalled() + expect(navigateClearingStack).toHaveBeenCalled() + }) +}) diff --git a/src/navigator/DemoModeAuthBlock.tsx b/src/navigator/DemoModeAuthBlock.tsx new file mode 100644 index 00000000000..30c1674174c --- /dev/null +++ b/src/navigator/DemoModeAuthBlock.tsx @@ -0,0 +1,61 @@ +import React from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet, Text } from 'react-native' +import BottomSheetScrollView from 'src/components/BottomSheetScrollView' +import Button, { BtnSizes, BtnTypes } from 'src/components/Button' +import { navigateBack, navigateClearingStack } from 'src/navigator/NavigationService' +import { Screens } from 'src/navigator/Screens' +import { useDispatch, useSelector } from 'src/redux/hooks' +import { typeScale } from 'src/styles/fonts' +import { Spacing } from 'src/styles/styles' +import { demoModeToggled } from 'src/web3/actions' +import { rawWalletAddressSelector } from 'src/web3/selectors' + +export default function DemoModeAuthBlock() { + const { t } = useTranslation() + const dispatch = useDispatch() + const originalWalletAddress = useSelector(rawWalletAddressSelector) + + const handleExitDemoMode = () => { + dispatch(demoModeToggled(false)) + navigateBack() // dismiss the bottom sheet + + if (!originalWalletAddress) { + navigateClearingStack(Screens.Welcome) + } + } + + return ( + + {t('demoMode.restrictedAccess.title')} + {t('demoMode.restrictedAccess.info')} +