Skip to content

Commit

Permalink
[WALL] aum / WALL-4049/ wallet-crypto-withdrawal-age-verification-err…
Browse files Browse the repository at this point in the history
…or (deriv-com#15278)

* feat: added CryptoLimitAgeVerified error screen for WithdrawalCrypto

* fix: fix TS error

* feat: added countLimitMessageFn to handle daily count transfer limit

* feat: added daily count message for demo wallets
  • Loading branch information
aum-deriv authored May 24, 2024
1 parent c446c6c commit 1ce1de3
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/wallets/src/constants/errorCodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum CryptoDepositErrorCodes {
export enum CryptoWithdrawalErrorCodes {
CryptoConnectionError = 'CryptoConnectionError',
CryptoInvalidAddress = 'CryptoInvalidAddress',
CryptoLimitAgeVerified = 'CryptoLimitAgeVerified',
InvalidToken = 'InvalidToken',
SuspendedCurrency = 'CryptoSuspendedCurrency',
SuspendedWithdrawal = 'CryptoDisabledCurrencyWithdrawal',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { displayMoney as displayMoney_ } from '@deriv/api-v2/src/utils';
import { THooks } from '../../../../../../types';
import { TAccount, TInitialTransferFormValues, TMessageFnProps, TTransferMessage } from '../../types';
import {
countLimitMessageFn,
cumulativeAccountLimitsMessageFn,
insufficientBalanceMessageFn,
lifetimeAccountLimitsBetweenWalletsMessageFn,
Expand Down Expand Up @@ -56,6 +57,7 @@ const useTransferMessages = ({
const messages: TTransferMessage[] = [];

messageFns.push(insufficientBalanceMessageFn);
messageFns.push(countLimitMessageFn);

if (!isAccountVerified && isTransferBetweenWallets) {
messageFns.push(lifetimeAccountLimitsBetweenWalletsMessageFn);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { TMessageFnProps, TTransferMessage } from '../../../types';

let text: TTransferMessage['message']['text'],
type: TTransferMessage['type'],
values: TTransferMessage['message']['values'];

const countLimitMessageFn = ({ activeWallet, limits, sourceAccount, targetAccount }: TMessageFnProps) => {
if (!targetAccount) return null;

const isTransferBetweenWallets =
sourceAccount.account_category === 'wallet' && targetAccount.account_category === 'wallet';

const isDemoTransfer = activeWallet?.is_virtual;

const keyAccountType =
[sourceAccount, targetAccount].find(acc => acc.account_category !== 'wallet')?.account_type ?? 'internal';

const platformKey = keyAccountType === 'standard' ? 'dtrade' : keyAccountType;

const allowedCount = isDemoTransfer
? //@ts-expect-error needs backend type
(limits?.daily_transfers?.virtual?.allowed as number)
: //@ts-expect-error needs backend type
(limits?.daily_transfers?.[platformKey]?.allowed as number);

const availableCount = isDemoTransfer
? //@ts-expect-error needs backend type
(limits?.daily_transfers?.virtual?.available as number)
: //@ts-expect-error needs backend type
(limits?.daily_transfers?.[platformKey]?.available as number);

if (allowedCount === undefined || availableCount === undefined) return null;

if (availableCount === 0 && isDemoTransfer) {
text =
'You have reached your daily transfer limit of {{allowedCount}} transfers for your virtual funds. The limit will reset at 00:00 GMT.';
values = {
allowedCount,
};
type = 'error' as const;

return {
message: { text, values },
type,
};
}

if (availableCount === 0) {
text = isTransferBetweenWallets
? 'You have reached your daily transfer limit of {{allowedCount}} transfers between your Wallets. The limit will reset at 00:00 GMT.'
: 'You have reached your daily transfer limit of {{allowedCount}} transfers between your {{sourceAccountName}} and {{targetAccountName}}. The limit will reset at 00:00 GMT.';
values = {
allowedCount,
sourceAccountName: sourceAccount.accountName,
targetAccountName: targetAccount.accountName,
};
type = 'error' as const;

return {
message: { text, values },
type,
};
}

return null;
};

export default countLimitMessageFn;
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import countLimitMessageFn from './countLimitsMessageFn';
import cumulativeAccountLimitsMessageFn from './cumulativeAccountLimitsMessageFn';
import insufficientBalanceMessageFn from './insufficientBalanceMessageFn';
import lifetimeAccountLimitsBetweenWalletsMessageFn from './lifetimeAccountLimitsBetweenWalletsMessageFn';
import transferFeesBetweenWalletsMessageFn from './transferFeesBetweenWalletsMessageFn';

export {
countLimitMessageFn,
cumulativeAccountLimitsMessageFn,
insufficientBalanceMessageFn,
lifetimeAccountLimitsBetweenWalletsMessageFn,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { ComponentProps } from 'react';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
import { useActiveWalletAccount } from '@deriv/api-v2';
import { TSocketError } from '@deriv/api-v2/types';
import { WalletButton, WalletsErrorScreen } from '../../../../components';
Expand All @@ -23,6 +24,7 @@ type TErrorContent = {
type TErrorCodeHandlers = Record<string, TErrorContent>;

const WithdrawalErrorScreen: React.FC<TProps> = ({ error, resetError, setResendEmail }) => {
const history = useHistory();
const { data } = useActiveWalletAccount();
const currency = data?.currency;

Expand Down Expand Up @@ -51,6 +53,16 @@ const WithdrawalErrorScreen: React.FC<TProps> = ({ error, resetError, setResendE
onClick: resetError,
title: 'Error',
},
[CryptoWithdrawalErrorCodes.CryptoLimitAgeVerified]: {
...defaultContent,
buttonText: 'Verify identity',
buttonVariant: 'contained',
onClick: () => {
// @ts-expect-error the following link is not part of wallets routes config
history.push('/account/proof-of-identity');
},
title: 'Error',
},
[CryptoWithdrawalErrorCodes.SuspendedCurrency]: {
...defaultContent,
buttonText: undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useActiveWalletAccount } from '@deriv/api-v2';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import WithdrawalErrorScreen from '../WithdrawalErrorScreen';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: jest.fn(),
}));

jest.mock('@deriv/api-v2', () => ({
useActiveWalletAccount: jest.fn(),
}));
Expand Down Expand Up @@ -108,6 +114,37 @@ describe('WithdrawalErrorScreen', () => {
expect(screen.queryByText('Try again')).not.toBeInTheDocument();
});

it('should show correct withdrawal error screen for crypto age limit verified error', () => {
const error = {
code: 'CryptoLimitAgeVerified',
message: 'Crypto Limit Age Verified Error',
};

render(<WithdrawalErrorScreen error={error} resetError={resetError} setResendEmail={setResendEmail} />);

expect(screen.getByText('Error')).toBeInTheDocument();
expect(screen.getByText('Crypto Limit Age Verified Error')).toBeInTheDocument();
expect(screen.queryByText('Verify identity')).toBeInTheDocument();
});

it('should show redirect the user to the account/proof-of-identity when the user clicks on `Verify identity` after receiving crypto age limit verified error', () => {
const mockHistoryPush = jest.fn();
(useHistory as jest.Mock).mockReturnValueOnce({
push: mockHistoryPush,
});
const error = {
code: 'CryptoLimitAgeVerified',
message: 'Crypto Limit Age Verified Error',
};

render(<WithdrawalErrorScreen error={error} resetError={resetError} setResendEmail={setResendEmail} />);

const verifyIdentityButton = screen.getByText('Verify identity');
userEvent.click(verifyIdentityButton);

expect(mockHistoryPush).toBeCalledWith('/account/proof-of-identity');
});

it('should reload page when the user clicks on `Try again` button', () => {
const reloadMock = jest.fn();
Object.defineProperty(window, 'location', {
Expand Down

0 comments on commit 1ce1de3

Please sign in to comment.