Skip to content

Commit

Permalink
feat(coinmarket): possibility to proceed with unverified address
Browse files Browse the repository at this point in the history
  • Loading branch information
adderpositive authored and komret committed Sep 27, 2024
1 parent 4d41257 commit 4d08a77
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 86 deletions.
1 change: 1 addition & 0 deletions packages/suite-data/files/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,7 @@
"TR_SHOW_MORE_ADDRESSES": "Show more ({count})",
"TR_SHOW_UNVERIFIED_ADDRESS": "Show unverified address",
"TR_SHOW_UNVERIFIED_XPUB": "Show unverified public key",
"TR_PROCEED_UNVERIFIED_ADDRESS": "Proceed with unverified address",
"TR_SIDEBAR_ADD_COIN": "Add a coin",
"TR_SIGN": "Sign",
"TR_SIGNATURE": "Signature",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,8 @@ export const VERIFY_BUY_ADDRESS_FIXTURES = [
action: {
type: MODAL.OPEN_USER_CONTEXT,
payload: {
type: 'unverified-address',
type: 'unverified-address-proceed',
value: XRP_ACCOUNT.descriptor,
addressPath: XRP_ACCOUNT.path,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ export const verifyAddress =
if (!connected || !available) {
dispatch(
modalActions.openModal({
type: 'unverified-address',
type: 'unverified-address-proceed',
value: address,
addressPath: path,
}),
);

Expand Down
2 changes: 1 addition & 1 deletion packages/suite/src/actions/wallet/coinmarketBuyActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type CoinmarketBuyAction =
| { type: typeof COINMARKET_BUY.SET_IS_FROM_REDIRECT; isFromRedirect: boolean }
| { type: typeof COINMARKET_BUY.SAVE_TRANSACTION_DETAIL_ID; transactionId: string }
| { type: typeof COINMARKET_BUY.SAVE_QUOTE_REQUEST; request: BuyTradeQuoteRequest }
| { type: typeof COINMARKET_BUY.VERIFY_ADDRESS; addressVerified: string }
| { type: typeof COINMARKET_BUY.VERIFY_ADDRESS; addressVerified: string | undefined }
| {
type: typeof COINMARKET_BUY.SAVE_CACHED_ACCOUNT_INFO;
symbol: Account['symbol'];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, { useCallback } from 'react';
import { useCallback } from 'react';

import { ModalProps } from '@trezor/components';
import { openAddressModal, showAddress } from 'src/actions/wallet/receiveActions';
import { ConfirmUnverifiedModal } from './ConfirmUnverifiedModal';

interface ConfirmUnverifiedAddressModalProps extends Required<Pick<ModalProps, 'onCancel'>> {
interface ConfirmUnverifiedAddressModalProps {
addressPath: string;
value: string;
}
Expand All @@ -13,15 +12,17 @@ export const ConfirmUnverifiedAddressModal = ({
addressPath,
value,
}: ConfirmUnverifiedAddressModalProps) => {
const verifyAddress = useCallback(() => showAddress(addressPath, value), [addressPath, value]);
const verifyProcess = useCallback(() => showAddress(addressPath, value), [addressPath, value]);
const showUnverifiedAddress = () => openAddressModal({ addressPath, value });

return (
<ConfirmUnverifiedModal
showUnverifiedButtonText="TR_SHOW_UNVERIFIED_ADDRESS"
action={{
event: showUnverifiedAddress,
title: 'TR_SHOW_UNVERIFIED_ADDRESS',
}}
verifyProcess={verifyProcess}
warningText="TR_ADDRESS_PHISHING_WARNING"
verify={verifyAddress}
showUnverified={showUnverifiedAddress}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,101 +1,100 @@
import { useEffect } from 'react';
import styled from 'styled-components';

import { applySettings } from 'src/actions/settings/deviceSettingsActions';
import { Translation, Modal } from 'src/components/suite';
import { Translation } from 'src/components/suite';
import { TranslationKey } from 'src/components/suite/Translation';
import { useDevice, useDispatch, useSelector } from 'src/hooks/suite';
import { ThunkAction } from 'src/types/suite';
import { Button } from '@trezor/components';
import { Dispatch, GetState } from 'src/types/suite';
import { Button, H3, NewModal, Paragraph } from '@trezor/components';
import { onCancel } from 'src/actions/suite/modalActions';
import { selectDeviceLabelOrName } from '@suite-common/wallet-core';

const StyledModal = styled(Modal)`
width: 520px;
`;

// eslint-disable-next-line local-rules/no-override-ds-component
const StyledButton = styled(Button)`
flex-grow: 1;
`;
import { applySettings } from 'src/actions/settings/deviceSettingsActions';
import { useEffect } from 'react';

interface ConfirmUnverifiedModalProps {
showUnverifiedButtonText: TranslationKey;
showUnverified: () => ThunkAction;
verify: () => ThunkAction;
action: {
event: () => (dispatch: Dispatch) => void;
title: TranslationKey;
closeAfterEventTriggered?: boolean;
};
verifyProcess?: () => (dispatch: Dispatch, getState: GetState) => Promise<void>;
warningText: TranslationKey;
}

export const ConfirmUnverifiedModal = ({
showUnverifiedButtonText,
showUnverified,
verify,
action,
warningText,
verifyProcess,
}: ConfirmUnverifiedModalProps) => {
const deviceLabel = useSelector(selectDeviceLabelOrName);
const { device } = useDevice();
const dispatch = useDispatch();
const { device, isLocked } = useDevice();

// Device connected while the modal is open -> switch to verification modal.
useEffect(() => {
if (device?.connected) {
dispatch(verify());
}
}, [device?.connected, dispatch, verify]);

// just to make TS happy
if (!device) return null;
const { isLocked } = useDevice();

const isDeviceLocked = isLocked();
const isPassphraseRequired = device.connected && !device.available;
const isPassphraseRequired = device?.connected && !device.available;
const deviceStatus = isPassphraseRequired
? 'TR_DEVICE_LABEL_IS_UNAVAILABLE'
: 'TR_DEVICE_LABEL_IS_NOT_CONNECTED';
const description = isPassphraseRequired
? 'TR_PLEASE_ENABLE_PASSPHRASE'
: 'TR_PLEASE_CONNECT_YOUR_DEVICE';

const handleClose = () => dispatch(onCancel());
const handleEvent = () => {
dispatch(action.event());

if (action.closeAfterEventTriggered) {
handleClose();
}
};

const enablePassphraseAndContinue = async () => {
if (!device.available) {
if (!device?.available) {
const result = await dispatch(applySettings({ use_passphrase: true }));
if (!result || !result.success) return;
}
dispatch(verify());
};
const continueUnverified = () => dispatch(showUnverified());
const close = () => dispatch(onCancel());

// Device connected while the modal is open -> switch to verification modal.
useEffect(() => {
if (device?.connected && verifyProcess) {
dispatch(verifyProcess());
}
}, [device?.connected, dispatch, verifyProcess]);

return (
<StyledModal
heading={<Translation id={deviceStatus} values={{ deviceLabel }} />}
isCancelable
onCancel={close}
description={
<Translation
id={warningText}
values={{ claim: <Translation id={description} /> }}
/>
}
bottomBarComponents={
<NewModal
variant="warning"
size="small"
icon="shieldWarning"
onCancel={handleClose}
bottomContent={
<>
<Button variant="warning" onClick={continueUnverified}>
<Translation id={showUnverifiedButtonText} />
<Button variant="warning" onClick={handleEvent}>
<Translation id={action.title} />
</Button>

{isPassphraseRequired && (
<StyledButton
<Button
variant="primary"
onClick={enablePassphraseAndContinue}
isDisabled={isDeviceLocked}
>
<Translation id="TR_ACCOUNT_ENABLE_PASSPHRASE" />
</StyledButton>
</Button>
)}
<Button onClick={close} variant="tertiary">
<Button onClick={handleClose} variant="tertiary">
<Translation id="TR_BACK" />
</Button>
</>
}
/>
>
<H3>
<Translation id={deviceStatus} values={{ deviceLabel }} />
</H3>
<Paragraph>
<Translation
id={warningText}
values={{ claim: <Translation id={description} /> }}
/>
</Paragraph>
</NewModal>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ConfirmUnverifiedModal } from './ConfirmUnverifiedModal';
import { COINMARKET_BUY } from 'src/actions/wallet/constants';
import { Dispatch } from 'src/types/suite';

interface ConfirmUnverifiedProceedModalProps {
value: string;
}

export const ConfirmUnverifiedProceedModal = ({ value }: ConfirmUnverifiedProceedModalProps) => {
const proceedWithUnverifiedAddress = () => (dispatch: Dispatch) => {
dispatch({
type: COINMARKET_BUY.VERIFY_ADDRESS,
addressVerified: value,
});
};

return (
<ConfirmUnverifiedModal
action={{
event: proceedWithUnverifiedAddress,
title: 'TR_PROCEED_UNVERIFIED_ADDRESS',
closeAfterEventTriggered: true,
}}
warningText="TR_ADDRESS_PHISHING_WARNING"
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback } from 'react';

import { ConfirmUnverifiedModal } from './ConfirmUnverifiedModal';
import { openXpubModal, showXpub } from 'src/actions/wallet/publicKeyActions';

export const ConfirmUnverifiedXpubModal = () => {
const event = useCallback(() => openXpubModal(), []);
const verifyProcess = useCallback(() => showXpub(), []);

return (
<ConfirmUnverifiedModal
action={{
event,
title: 'TR_SHOW_UNVERIFIED_XPUB',
}}
verifyProcess={verifyProcess}
warningText="TR_XPUB_PHISHING_WARNING"
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import {
CriticalCoinjoinPhaseModal,
CoinjoinSuccessModal,
MoreRoundsNeededModal,
ConfirmUnverifiedModal,
ConfirmUnverifiedAddressModal,
UnecoCoinjoinModal,
AuthenticateDeviceModal,
AuthenticateDeviceFailModal,
Expand All @@ -39,9 +37,11 @@ import {
ClaimModal,
CopyAddressModal,
UnhideTokenModal,
ConfirmUnverifiedAddressModal,
ConfirmUnverifiedXpubModal,
ConfirmUnverifiedProceedModal,
} from 'src/components/suite/modals';
import type { AcquiredDevice } from 'src/types/suite';
import { openXpubModal, showXpub } from 'src/actions/wallet/publicKeyActions';
import type { ReduxModalProps } from '../ReduxModal';
import { CryptoId } from 'invity-api';
import { EverstakeModal } from './UnstakeModal/EverstakeModal';
Expand Down Expand Up @@ -74,18 +74,12 @@ export const UserContextModal = ({
<ConfirmUnverifiedAddressModal
addressPath={payload.addressPath}
value={payload.value}
onCancel={onCancel}
/>
);
case 'unverified-xpub':
return (
<ConfirmUnverifiedModal
showUnverifiedButtonText="TR_SHOW_UNVERIFIED_XPUB"
warningText="TR_XPUB_PHISHING_WARNING"
verify={showXpub}
showUnverified={openXpubModal}
/>
);
return <ConfirmUnverifiedXpubModal />;
case 'unverified-address-proceed':
return <ConfirmUnverifiedProceedModal value={payload.value} />;
case 'address':
return <ConfirmAddressModal {...payload} onCancel={onCancel} />;
case 'xpub':
Expand Down
2 changes: 2 additions & 0 deletions packages/suite/src/components/suite/modals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export { ImportTransactionModal } from './ReduxModal/UserContextModal/ImportTran
export { ConfirmEvmExplanationModal } from './ConfirmEvmExplanationModal';
export { ConfirmUnverifiedModal } from './ReduxModal/UserContextModal/ConfirmUnverifiedModal';
export { ConfirmUnverifiedAddressModal } from './ReduxModal/UserContextModal/ConfirmUnverifiedAddressModal';
export { ConfirmUnverifiedXpubModal } from './ReduxModal/UserContextModal/ConfirmUnverifiedXpubModal';
export { ConfirmUnverifiedProceedModal } from './ReduxModal/UserContextModal/ConfirmUnverifiedProceedModal';
export { AddAccountModal } from './ReduxModal/UserContextModal/AddAccountModal/AddAccountModal';
export { QrScannerModal } from './ReduxModal/UserContextModal/QrScannerModal';
export { BackgroundGalleryModal } from './ReduxModal/UserContextModal/BackgroundGalleryModal';
Expand Down
4 changes: 2 additions & 2 deletions packages/suite/src/reducers/wallet/coinmarketReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ interface Buy extends CoinmarketTradeCommonProps {
symbol?: Account['symbol'];
shouldSubmit?: boolean;
};
addressVerified?: string;
addressVerified: string | undefined;
}

interface Exchange extends CoinmarketTradeCommonProps {
exchangeInfo?: ExchangeInfo;
quotesRequest?: ExchangeTradeQuoteRequest;
quotes: ExchangeTrade[] | undefined;
addressVerified?: string;
addressVerified: string | undefined;
coinmarketAccount?: Account;
selectedQuote: ExchangeTrade | undefined;
}
Expand Down
8 changes: 8 additions & 0 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ export default defineMessages({
id: 'TR_COINMARKET_SWAP_DEX_MODAL_TERMS_6',
dynamic: true,
},
TR_CONFIRM_ADDRESS: {
defaultMessage: 'Confirm address',
id: 'TR_CONFIRM_ADDRESS',
},
TR_EXCHANGE_STATUS_ERROR: {
defaultMessage: 'Rejected',
id: 'TR_EXCHANGE_STATUS_ERROR',
Expand Down Expand Up @@ -3291,6 +3295,10 @@ export default defineMessages({
defaultMessage: 'Show unverified public key',
id: 'TR_SHOW_UNVERIFIED_XPUB',
},
TR_PROCEED_UNVERIFIED_ADDRESS: {
defaultMessage: 'Proceed with unverified address',
id: 'TR_PROCEED_UNVERIFIED_ADDRESS',
},
TR_SIGN: {
defaultMessage: 'Sign',
description: 'Sign button in Sign and Verify form',
Expand Down
Loading

0 comments on commit 4d08a77

Please sign in to comment.