\ No newline at end of file
diff --git a/packages/components/src/components/icon/wallet/ic-wallet-info-message-with-three-dots.svg b/packages/components/src/components/icon/wallet/ic-wallet-info-message-with-three-dots.svg
new file mode 100644
index 000000000000..48023c82e0e7
--- /dev/null
+++ b/packages/components/src/components/icon/wallet/ic-wallet-info-message-with-three-dots.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/icon/wallet/ic-wallet-success-message.svg b/packages/components/src/components/icon/wallet/ic-wallet-success-message.svg
new file mode 100644
index 000000000000..03491c4831f3
--- /dev/null
+++ b/packages/components/src/components/icon/wallet/ic-wallet-success-message.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/components/src/components/message-list/__tests__/alert-message.spec.tsx b/packages/components/src/components/message-list/__tests__/alert-message.spec.tsx
new file mode 100644
index 000000000000..e8ef334dcd8d
--- /dev/null
+++ b/packages/components/src/components/message-list/__tests__/alert-message.spec.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import AlertMessage from '../alert-message';
+
+describe('AlertMessage', () => {
+ it('Should render proper icon type', () => {
+ const { rerender } = render(
);
+
+ expect(screen.getByTestId('dt_IcWalletErrorMessageWithCross')).toBeInTheDocument();
+
+ rerender(
);
+
+ expect(screen.getByTestId('dt_IcWalletInfoMessageWithThreeDots')).toBeInTheDocument();
+
+ rerender(
);
+
+ expect(screen.getByTestId('dt_IcWalletSuccessMessage')).toBeInTheDocument();
+ });
+
+ it('Should render proper message', () => {
+ render(
);
+
+ expect(screen.getByText('Error message')).toBeInTheDocument();
+ });
+
+ it('Should render proper button', () => {
+ render(
+
+ );
+
+ expect(screen.getByRole('button', { name: 'Error button' })).toBeInTheDocument();
+ });
+
+ it('Should trigger onClick handler when the user is clicking on the button', () => {
+ const onClickHandler = jest.fn();
+
+ render(
+
+ );
+
+ const el_btn = screen.getByRole('button', { name: 'Error button' });
+ userEvent.click(el_btn);
+
+ expect(onClickHandler).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/packages/components/src/components/message-list/__tests__/message-list.spec.tsx b/packages/components/src/components/message-list/__tests__/message-list.spec.tsx
new file mode 100644
index 000000000000..80a591e753fa
--- /dev/null
+++ b/packages/components/src/components/message-list/__tests__/message-list.spec.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import MessageList from '../message-list';
+
+describe('MessageList', () => {
+ it('Should render the list of messages', () => {
+ const list: React.ComponentProps
['list'] = [
+ { variant: 'base', key: '1', message: 'Success message', type: 'success' },
+ { variant: 'base', key: '2', message: 'Error message', type: 'error' },
+ { variant: 'base', key: '3', message: 'Info message', type: 'info' },
+ ];
+
+ render();
+
+ expect(screen.getAllByTestId('dt_alert_message').length).toBe(3);
+ });
+});
diff --git a/packages/components/src/components/message-list/alert-message.tsx b/packages/components/src/components/message-list/alert-message.tsx
new file mode 100644
index 000000000000..151df49a1607
--- /dev/null
+++ b/packages/components/src/components/message-list/alert-message.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import { isMobile } from '@deriv/shared';
+import Button from '../button';
+import Icon from '../icon';
+import Text from '../text';
+
+const type_mapper = {
+ info: {
+ icon: 'IcWalletInfoMessageWithThreeDots',
+ color: 'status-info-blue',
+ },
+ error: {
+ icon: 'IcWalletErrorMessageWithCross',
+ color: 'loss-danger',
+ },
+ success: {
+ icon: 'IcWalletSuccessMessage',
+ color: 'profit-success',
+ },
+};
+
+type TAlertMessageProps =
+ | {
+ variant: 'with-action-button';
+ button_label: string;
+ message: string | JSX.Element;
+ onClickHandler: VoidFunction;
+ type: 'info' | 'error' | 'success';
+ }
+ | {
+ variant: 'base';
+ message: string | JSX.Element;
+ type: 'info' | 'error' | 'success';
+ };
+
+const AlertMessage = (props: TAlertMessageProps) => {
+ const { type, message, variant } = props;
+
+ const icon = type_mapper[type].icon;
+ const color = type_mapper[type].color;
+
+ return (
+
+
+
+
+ {message}
+
+
+ {variant === 'with-action-button' && props.button_label && (
+
+
+
+ )}
+
+ );
+};
+
+export default AlertMessage;
diff --git a/packages/components/src/components/message-list/index.ts b/packages/components/src/components/message-list/index.ts
new file mode 100644
index 000000000000..bd4c491a159a
--- /dev/null
+++ b/packages/components/src/components/message-list/index.ts
@@ -0,0 +1 @@
+export { default as MessageList } from './message-list';
diff --git a/packages/components/src/components/message-list/message-list.scss b/packages/components/src/components/message-list/message-list.scss
new file mode 100644
index 000000000000..f5954f4ba4cd
--- /dev/null
+++ b/packages/components/src/components/message-list/message-list.scss
@@ -0,0 +1,53 @@
+.message-list {
+ width: 100%;
+ max-width: 63.4rem;
+ margin: 0 auto;
+ min-height: 3.2rem;
+
+ .alert-message {
+ display: flex;
+ flex-direction: row;
+ column-gap: 0.8rem;
+ justify-content: flex-start;
+ min-height: 3.2rem;
+
+ @include mobile {
+ margin: 0;
+ }
+
+ &__icon-container {
+ position: relative;
+
+ .icon-container {
+ &__line {
+ border: 1px solid var(--border-normal);
+ position: absolute;
+ height: 100%;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ &__icon {
+ display: flex;
+ position: relative;
+ margin-top: 0.8rem;
+ }
+ }
+ }
+
+ &__message-container,
+ &__button-container {
+ display: flex;
+ align-items: center;
+ }
+
+ &__message-container {
+ padding: 0.7rem 0;
+ }
+
+ &__button-container {
+ margin-left: auto;
+ }
+ }
+}
diff --git a/packages/components/src/components/message-list/message-list.tsx b/packages/components/src/components/message-list/message-list.tsx
new file mode 100644
index 000000000000..705fdba43438
--- /dev/null
+++ b/packages/components/src/components/message-list/message-list.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import AlertMessage from './alert-message';
+import './message-list.scss';
+
+type TMessageListProps = { list: (React.ComponentProps & { key: string })[] };
+
+const animations = {
+ initial: {
+ height: 0,
+ opacity: 0,
+ },
+ animate: {
+ height: 'auto',
+ opacity: 1,
+ transition: {
+ height: {
+ duration: 0.2,
+ },
+ opacity: {
+ duration: 0.15,
+ delay: 0.05,
+ },
+ },
+ },
+ exit: {
+ height: 0,
+ opacity: 0,
+ transition: {
+ height: {
+ duration: 0.2,
+ },
+ opacity: { duration: 0.1 },
+ },
+ },
+};
+
+const MessageList = ({ list }: TMessageListProps) => {
+ return (
+
+
+ {list.map(item => {
+ return (
+
+
+
+ );
+ })}
+
+
+ );
+};
+
+export default MessageList;
diff --git a/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-list.spec.tsx b/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-list.spec.tsx
deleted file mode 100644
index 82cdef92052f..000000000000
--- a/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-list.spec.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import Badge from '../../badge';
-import TransferAccountList from '../transfer-account-list';
-import { render, screen } from '@testing-library/react';
-
-jest.mock('../../wallet-tile/wallet-tile', () => jest.fn(() => Wallet Tile
));
-
-describe('TransferAccountList', () => {
- let mocked_props: React.ComponentProps;
-
- beforeEach(() => {
- mocked_props = {
- is_mobile: false,
- selected_account: {
- balance: '100',
- currency: 'USD',
- icon: 'Icon',
- jurisdiction: ,
- label: 'Account Label',
- loginid: '12345678',
- type: 'fiat',
- wallet_icon: 'Wallet Icon',
- wallet_name: 'USD Wallet',
- },
- setIsListModalOpen: jest.fn(),
- setSelectedAccount: jest.fn(),
- transfer_accounts: {
- accounts: [
- {
- loginid: '1',
- label: 'Deriv Apps',
- currency: 'USD',
- balance: '10.00',
- wallet_icon: 'IcCurrencyUsd',
- icon: 'IcDerivApps',
- jurisdiction: ,
- type: 'fiat',
- },
- {
- loginid: '2',
- label: 'MT5 Derived',
- currency: 'USD',
- balance: '10.00',
- wallet_icon: 'IcCurrencyUsd',
- icon: 'IcMT5Derived',
- jurisdiction: ,
- type: 'fiat',
- },
- ],
- wallets: [
- {
- loginid: '3',
- label: 'USD Wallet',
- currency: 'USD',
- balance: '10,000.00',
- wallet_icon: 'IcCurrencyUsd',
- jurisdiction: ,
- type: 'fiat',
- },
- ],
- },
- transfer_hint: 'Transfer hint',
- wallet_name: 'USD Wallet',
- };
- });
-
- it('Should render proper titles of transfer accounts', () => {
- render();
-
- expect(screen.getByText('Trading accounts linked with USD Wallet')).toBeInTheDocument();
- expect(screen.getByText('Wallets')).toBeInTheDocument();
- });
-
- it('Should render proper amount of transfer accounts', () => {
- render();
-
- expect(screen.getAllByText('Wallet Tile').length).toBe(3);
- });
-
- it('Should render transfer hint for Wallets account list', () => {
- mocked_props.transfer_accounts = { ...mocked_props.transfer_accounts, accounts: [] };
- render();
-
- expect(screen.getByText('Transfer hint')).toBeInTheDocument();
- });
-});
diff --git a/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-selector.spec.tsx b/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-selector.spec.tsx
deleted file mode 100644
index b655830e02ee..000000000000
--- a/packages/components/src/components/transfer-account-selector/__tests__/transfer-account-selector.spec.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import TransferAccountSelector from '../transfer-account-selector';
-import userEvent from '@testing-library/user-event';
-import { render, screen } from '@testing-library/react';
-
-jest.mock('../../transfer-account-selector/transfer-tile', () => jest.fn(() => Transfer Tile
));
-jest.mock('../../transfer-account-selector/transfer-account-list', () =>
- jest.fn(() => Transfer Account List
)
-);
-
-describe('TransferAccountSelector', () => {
- let modal_root_el: HTMLDivElement, mocked_props: React.ComponentProps;
-
- beforeAll(() => {
- modal_root_el = document.createElement('div');
- modal_root_el.setAttribute('id', 'modal_root');
- document.body.appendChild(modal_root_el);
- });
-
- beforeEach(() => {
- mocked_props = {
- is_mobile: false,
- is_wallet_name_visible: false,
- label: 'Transfer from',
- onSelectAccount: jest.fn(),
- placeholder: 'Placeholder',
- portal_id: 'modal_root',
- setIsWalletNameVisible: jest.fn(),
- transfer_accounts: {
- accounts: [],
- wallets: [],
- },
- transfer_hint: 'Transfer hint',
- value: undefined,
- wallet_name: 'USD Wallet',
- };
- });
-
- it('Should render transfer tile by default', () => {
- render();
-
- expect(screen.getByText('Transfer Tile')).toBeInTheDocument();
- });
-
- it('Should render TransferAccountList when the user is clicking on Transfer selector', () => {
- render();
-
- const el_transfer_tile = screen.getByTestId('dt_transfer_account_selector');
- userEvent.click(el_transfer_tile);
-
- expect(screen.getByText('Transfer Account List')).toBeInTheDocument();
- });
-});
diff --git a/packages/components/src/components/transfer-account-selector/__tests__/transfer-tile.spec.tsx b/packages/components/src/components/transfer-account-selector/__tests__/transfer-tile.spec.tsx
deleted file mode 100644
index 7e5cb2ae2281..000000000000
--- a/packages/components/src/components/transfer-account-selector/__tests__/transfer-tile.spec.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import React from 'react';
-import Badge from '../../badge';
-import TransferTile from '../transfer-tile';
-import { render, screen } from '@testing-library/react';
-
-jest.mock('../../wallet-tile/wallet-tile', () => jest.fn(() => Wallet Tile
));
-
-describe('TransferTile', () => {
- let mocked_props: React.ComponentProps;
-
- beforeEach(() => {
- mocked_props = {
- is_mobile: false,
- label: 'Transfer from',
- selected_account: {
- balance: '100',
- currency: 'USD',
- icon: 'IconSrc',
- jurisdiction: ,
- label: 'Account Label',
- loginid: '12345678',
- type: 'fiat',
- wallet_icon: 'Wallet Icon',
- wallet_name: 'USD Wallet',
- },
- placeholder: 'Placeholder',
- };
- });
-
- it('Should render proper label', () => {
- render();
-
- expect(screen.getByText('Transfer from')).toBeInTheDocument();
- });
-
- it('Should render wallet tile if selected account exists', () => {
- render();
-
- expect(screen.getByText('Wallet Tile')).toBeInTheDocument();
- });
-
- it("Should render placeholder if selected account doesn't exist", () => {
- mocked_props.selected_account = undefined;
- render();
-
- expect(screen.getByText('Placeholder')).toBeInTheDocument();
- });
-
- it('Should render proper jurisdiction if selected account exists', () => {
- render();
-
- expect(screen.getByText('SVG')).toBeInTheDocument();
- });
-
- it('Should render chevron icon', () => {
- render();
-
- expect(screen.getByTestId('dt_chevron_icon')).toBeInTheDocument();
- });
-});
diff --git a/packages/components/src/components/transfer-account-selector/transfer-tile.tsx b/packages/components/src/components/transfer-account-selector/transfer-tile.tsx
deleted file mode 100644
index 8f8a4f89bdbf..000000000000
--- a/packages/components/src/components/transfer-account-selector/transfer-tile.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import React from 'react';
-import Icon from '../icon';
-import Text from '../text';
-import { WalletTile } from '../wallet-tile';
-import type { TTransferAccount } from './transfer-account-selector';
-
-type TTransferTile = {
- is_mobile?: boolean;
- label?: string;
- selected_account?: TTransferAccount;
- placeholder?: string;
-};
-
-const ChevronIcon = () => {
- return (
-
-
-
- );
-};
-
-const TransferTile = ({ is_mobile, label, placeholder, selected_account }: TTransferTile) => {
- return (
-
-
-
-
- {label}
-
-
- {is_mobile &&
}
-
-
- {selected_account ? (
-
- ) : (
-
- {placeholder}
-
- )}
-
-
- {!is_mobile && (
-
- {selected_account?.jurisdiction}
-
-
- )}
-
- );
-};
-
-export default React.memo(TransferTile);
diff --git a/packages/components/src/components/wallet-icon/wallet-icon.tsx b/packages/components/src/components/wallet-icon/wallet-icon.tsx
index 0cd618c6af04..37616338e99c 100644
--- a/packages/components/src/components/wallet-icon/wallet-icon.tsx
+++ b/packages/components/src/components/wallet-icon/wallet-icon.tsx
@@ -21,6 +21,7 @@ const sizes = {
medium: 24,
large: 32,
xlarge: 48,
+ xxlarge: 64,
},
// The crypto and demo sizes are the same
box: {
@@ -44,6 +45,10 @@ const sizes = {
width: 96,
height: 60,
},
+ xxlarge: {
+ width: 120,
+ height: 80,
+ },
},
} as const;
@@ -59,6 +64,7 @@ const WalletIcon = ({ gradient_class, icon, size = 'medium', type, has_bg, hide_
(!!gradient_class && type !== 'app') || has_bg,
'wallet-card--hide-watermark': hide_watermark,
})}
+ data-testid='dt_wallet_icon'
>
{(type === 'fiat' || type === 'app') && }
{(type === 'demo' || type === 'crypto') && (
diff --git a/packages/components/src/components/wallet-tile/__tests__/wallet-tile.spec.tsx b/packages/components/src/components/wallet-tile/__tests__/wallet-tile.spec.tsx
deleted file mode 100644
index 1dd579a9e04f..000000000000
--- a/packages/components/src/components/wallet-tile/__tests__/wallet-tile.spec.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import React from 'react';
-import Badge from '../../badge';
-import WalletTile from '../wallet-tile';
-import userEvent from '@testing-library/user-event';
-import { render, screen } from '@testing-library/react';
-
-jest.mock('../../app-linked-with-wallet-icon/app-linked-with-wallet-icon', () =>
- jest.fn(() => AppLinkedWithWalletIcon
)
-);
-jest.mock('../../wallet-icon/wallet-icon', () => jest.fn(() => WalletIcon
));
-
-describe('WalletTile', () => {
- let mocked_props: React.ComponentProps;
-
- beforeEach(() => {
- mocked_props = {
- account: {
- balance: '100',
- currency: 'USD',
- icon: 'Icon',
- jurisdiction: ,
- label: 'Account Label',
- loginid: '12345678',
- type: 'fiat',
- wallet_icon: 'Wallet Icon',
- wallet_name: 'USD Wallet',
- },
- className: 'classname',
- has_hover: false,
- icon_size: 'small',
- is_active: false,
- is_mobile: false,
- is_value: false,
- onClick: jest.fn(),
- };
- });
-
- it('Should render merged icon (App with Wallet)', () => {
- render();
-
- expect(screen.getByText('AppLinkedWithWalletIcon')).toBeInTheDocument();
- });
-
- it('Should render single wallet icon, if there is no app icon', () => {
- mocked_props.account = { ...mocked_props.account, icon: '' };
- render();
-
- expect(screen.getByText('WalletIcon')).toBeInTheDocument();
- });
-
- it('Should render jurisdiction in mobile view', () => {
- mocked_props.is_value = true;
- mocked_props.is_mobile = true;
- render();
-
- expect(screen.getByText('SVG')).toBeInTheDocument();
- });
-
- it('Should render jurisdiction in desktop view', () => {
- mocked_props.is_value = false;
- render();
-
- expect(screen.getByText('SVG')).toBeInTheDocument();
- });
-
- it('Should render proper account label', () => {
- render();
-
- expect(screen.getByText('Account Label')).toBeInTheDocument();
- });
-
- it('Should render proper account balance', () => {
- render();
-
- expect(screen.getByText('Balance: 100 USD')).toBeInTheDocument();
- });
-
- it('Should trigger onClick callback when the user is clicking on Wallet tile', () => {
- render();
-
- const el_wallet_tile = screen.getByTestId('dt_wallet_tile');
- userEvent.click(el_wallet_tile);
-
- expect(mocked_props.onClick).toHaveBeenCalledTimes(1);
- });
-});
diff --git a/packages/components/src/components/wallet-tile/index.ts b/packages/components/src/components/wallet-tile/index.ts
deleted file mode 100644
index 5838b63b8187..000000000000
--- a/packages/components/src/components/wallet-tile/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import WalletTile from './wallet-tile';
-
-export { WalletTile };
diff --git a/packages/components/src/components/wallet-tile/wallet-tile.tsx b/packages/components/src/components/wallet-tile/wallet-tile.tsx
deleted file mode 100644
index 813d80869989..000000000000
--- a/packages/components/src/components/wallet-tile/wallet-tile.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import React from 'react';
-import classNames from 'classnames';
-import Text from '../text';
-import { getCurrencyDisplayCode } from '@deriv/shared';
-import { localize } from '@deriv/translations';
-import { AppLinkedWithWalletIcon } from '../app-linked-with-wallet-icon';
-import { WalletIcon } from '../wallet-icon';
-import './wallet-tile.scss';
-
-type TAccount = {
- balance?: string;
- currency: string;
- gradient_class: string;
- icon?: string;
- jurisdiction?: JSX.Element;
- label?: string;
- loginid: string;
- type: 'fiat' | 'crypto';
- wallet_icon?: string;
- wallet_name?: string;
-};
-
-type TIconSize =
- | React.ComponentProps['size']
- | React.ComponentProps['size'];
-
-type TWalletTileProps = {
- account: TAccount;
- className?: string;
- has_hover?: boolean;
- icon_size?: TIconSize;
- is_active?: boolean;
- is_mobile?: boolean;
- is_value?: boolean;
- onClick?: () => void;
-};
-
-const WalletTile = ({
- account,
- className,
- has_hover,
- icon_size = 'small',
- is_active,
- is_mobile,
- is_value,
- onClick,
-}: TWalletTileProps) => {
- const IconComponent = () => {
- if (account.icon && account.wallet_icon) {
- return (
- ['size']}
- type={account.type}
- wallet_icon={account.wallet_icon}
- />
- );
- } else if (account.wallet_icon) {
- return (
- ['size']}
- type={account.type}
- />
- );
- }
-
- return null;
- };
-
- const Label = () => {
- if (account.label) {
- let size;
- if (is_value) size = is_mobile ? 'xxxxs' : 'xxxs';
- else size = is_mobile ? 'xxs' : 'xs';
-
- return (
-
- {account.label}
-
- );
- }
-
- return null;
- };
-
- const Balance = () => {
- if (account.balance) {
- let size;
- if (is_value) size = is_mobile ? 'xxxxs' : 'xxxs';
- else size = is_mobile ? 'xxxs' : 'xxs';
-
- return (
-
- {localize('Balance')}: {account.balance} {getCurrencyDisplayCode(account.currency)}
-
- );
- }
-
- return null;
- };
-
- return (
- onClick?.()}
- >
-
-
-
-
-
- {is_value && is_mobile && account.jurisdiction}
-
-
-
-
-
-
-
- {!is_value && account.jurisdiction}
-
- );
-};
-
-export default React.memo(WalletTile);
diff --git a/packages/components/src/index.js b/packages/components/src/index.js
index 0ae2fba6b5bd..cae57cc29e8e 100644
--- a/packages/components/src/index.js
+++ b/packages/components/src/index.js
@@ -108,7 +108,7 @@ export { default as Text } from './components/text';
export { default as Toast } from './components/toast';
export { default as ThemedScrollbars } from './components/themed-scrollbars';
export { default as ToggleSwitch } from './components/toggle-switch';
-export { default as TransferAccountSelector } from './components/transfer-account-selector';
+export * from './components/message-list';
export { default as TickPicker } from './components/tick-picker';
export { default as TickProgress } from './components/tick-progress';
export { default as Timeline } from './components/timeline';
@@ -119,7 +119,5 @@ export { default as VerticalTab } from './components/vertical-tab';
export { default as Wizard } from './components/wizard';
export * from './components/wallet-card';
export * from './components/wallet-icon';
-export * from './components/wallet-tile';
export * from './components/app-linked-with-wallet-icon';
-export * from './components/transfer-account-selector';
export * from './hooks';
diff --git a/packages/components/stories/icon/icons.js b/packages/components/stories/icon/icons.js
index 44fc0b2c497e..8875e4b71248 100644
--- a/packages/components/stories/icon/icons.js
+++ b/packages/components/stories/icon/icons.js
@@ -57,7 +57,7 @@ export const icons =
'IcAppstoreWalletUsdcLight',
'IcAppstoreWalletUsdtLight',
'IcAppstoreWalletsLink',
- 'IcAppstoreWarning'
+ 'IcAppstoreWarning',
],
'brand': [
'IcBrandDerivEzWordmark',
@@ -67,7 +67,7 @@ export const icons =
'IcBrandDmt5FinancialStp',
'IcBrandDmt5Financial',
'IcBrandDmt5Synthetics',
- 'IcBrandDxtradeWordmark'
+ 'IcBrandDxtradeWordmark',
],
'cashier': [
'IcCashierAdd',
@@ -243,7 +243,7 @@ export const icons =
'IcCashierWyreLight',
'IcCashierZenithbankDark',
'IcCashierZenithbankLight',
- 'IcCashier'
+ 'IcCashier',
],
'common': [
'IcAccountCross',
@@ -618,7 +618,7 @@ export const icons =
'IcWithdrawRequestVerification',
'IcZingpay',
'IcZoomIn',
- 'IcZoomOut'
+ 'IcZoomOut',
],
'contract': [
'IcContractBarrier',
@@ -636,7 +636,7 @@ export const icons =
'IcContractStartTimeCircle',
'IcContractStartTime',
'IcContractStrike',
- 'IcContractTarget'
+ 'IcContractTarget',
],
'currency': [
'IcCurrencyAud',
@@ -660,10 +660,10 @@ export const icons =
'IcCurrencyUsdc',
'IcCurrencyUsdk',
'IcCurrencyUst',
- 'IcCurrencyVirtual'
+ 'IcCurrencyVirtual',
],
'derivez': [
- 'IcDerivez'
+ 'IcDerivez',
],
'dxtrade': [
'IcDxtradeDerivX',
@@ -680,7 +680,7 @@ export const icons =
'IcDxtradeFinancialPlatform',
'IcDxtradeFinancial',
'IcDxtradeOnePassword',
- 'IcDxtradeSyntheticPlatform'
+ 'IcDxtradeSyntheticPlatform',
],
'flag': [
'IcFlagDe',
@@ -696,7 +696,7 @@ export const icons =
'IcFlagUk',
'IcFlagVi',
'IcFlagZhCn',
- 'IcFlagZhTw'
+ 'IcFlagZhTw',
],
'mt5': [
'IcMt5Acuity',
@@ -725,7 +725,7 @@ export const icons =
'IcMt5SyntheticDashboard',
'IcMt5SyntheticIndices',
'IcMt5SyntheticPlatform',
- 'IcMt5TradeTypes'
+ 'IcMt5TradeTypes',
],
'option': [
'IcOptionAccumulators',
@@ -743,7 +743,7 @@ export const icons =
'IcOptionOverUnder',
'IcOptionRaiseFall',
'IcOptionTouchNotouch',
- 'IcOptionUpDownAsian'
+ 'IcOptionUpDownAsian',
],
'rebranding': [
'IcRebrandingBinaryBot',
@@ -768,7 +768,7 @@ export const icons =
'IcRebrandingMt5Logo',
'IcRebrandingMt5SwapFree',
'IcRebrandingSmarttraderDashboard',
- 'IcRebrandingSmarttrader'
+ 'IcRebrandingSmarttrader',
],
'stock': [
'IcStockAdidasSalomon',
@@ -818,7 +818,7 @@ export const icons =
'IcStockVisa',
'IcStockWallMart',
'IcStockWaltDisney',
- 'IcStockZoom'
+ 'IcStockZoom',
],
'tradetype': [
'IcTradetypeAccu',
@@ -856,7 +856,7 @@ export const icons =
'IcTradetypeTicklow',
'IcTradetypeUpordown',
'IcTradetypeVanillaLongCall',
- 'IcTradetypeVanillaLongPut'
+ 'IcTradetypeVanillaLongPut',
],
'underlying': [
'IcUnderlying1HZ100V',
@@ -1015,7 +1015,7 @@ export const icons =
'IcUnderlyingWLDEUR',
'IcUnderlyingWLDGBP',
'IcUnderlyingWLDUSD',
- 'IcUnderlyingWLDXAU'
+ 'IcUnderlyingWLDXAU',
],
'wallet': [
'IcWalletBitcoinDark',
@@ -1029,8 +1029,10 @@ export const icons =
'IcWalletDerivDemoLight',
'IcWalletDerivP2pDark',
'IcWalletDerivP2pLight',
+ 'IcWalletErrorMessageWithCross',
'IcWalletEthereumDark',
'IcWalletEthereumLight',
+ 'IcWalletInfoMessageWithThreeDots',
'IcWalletLiteCoinDark',
'IcWalletLiteCoinLight',
'IcWalletModalTetherDark',
@@ -1040,9 +1042,10 @@ export const icons =
'IcWalletOptionsLight',
'IcWalletPaymentAgentDark',
'IcWalletPaymentAgentLight',
+ 'IcWalletSuccessMessage',
'IcWalletTetherDark',
'IcWalletTetherLight',
'IcWalletUsdCoinDark',
- 'IcWalletUsdCoinLight'
- ]
-}
\ No newline at end of file
+ 'IcWalletUsdCoinLight',
+ ],
+}
diff --git a/packages/hooks/src/__tests__/useExistingCFDAccounts.spec.tsx b/packages/hooks/src/__tests__/useExistingCFDAccounts.spec.tsx
new file mode 100644
index 000000000000..d03eb6ec6d7d
--- /dev/null
+++ b/packages/hooks/src/__tests__/useExistingCFDAccounts.spec.tsx
@@ -0,0 +1,173 @@
+import React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import { StoreProvider, mockStore } from '@deriv/stores';
+import { APIProvider } from '@deriv/api';
+import useExistingCFDAccounts from '../useExistingCFDAccounts';
+
+jest.mock('@deriv/api', () => ({
+ ...jest.requireActual('@deriv/api'),
+ useFetch: jest.fn(name => {
+ if (name === 'authorize') {
+ return {
+ data: {
+ account_list: [
+ {
+ account_category: 'wallet',
+ currency: 'USD',
+ is_virtual: 1,
+ linked_to: [
+ {
+ loginid: 'CRW909900',
+ platform: 'mt5',
+ },
+ ],
+ },
+ ],
+ },
+ };
+ }
+
+ if (name === 'mt5_login_list') {
+ return {
+ data: {
+ mt5_login_list: [
+ {
+ display_login: 'CRW909900',
+ email: '',
+ leverage: '10012123123',
+ login: 'CRW909900',
+ server: 'Deriv-Server',
+ server_description: 'Deriv-Server',
+ type: 'demo',
+ },
+ ],
+ },
+ };
+ }
+
+ if (name === 'trading_platform_accounts') {
+ return {
+ data: {
+ trading_platform_accounts: [
+ {
+ account_id: 'DXR1646584',
+ account_type: 'real',
+ balance: 0,
+ currency: 'USD',
+ display_balance: '0.00',
+ enabled: 1,
+ landing_company_short: 'svg',
+ login: '8807230',
+ market_type: 'all',
+ platform: 'dxtrade',
+ },
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'za',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'mei+za1@binary.com',
+ group: 'real\\p02_ts01\\all\\svg_ez_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'EZR80001086',
+ market_type: 'all',
+ name: 'Baily Pan',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ },
+ ],
+ },
+ };
+ }
+
+ return { data: undefined };
+ }),
+}));
+
+describe('useExistingCFDAccounts', () => {
+ it('should return the existing cfd accounts', () => {
+ const mock = mockStore({
+ client: { accounts: { CRW909900: { token: '12345' } }, loginid: 'CRW909900' },
+ traders_hub: {
+ combined_cfd_mt5_accounts: [
+ {
+ platform: 'mt5',
+ description: 'Deriv-Server',
+ icon: 'Derived',
+ sub_title: 'sub_name',
+ name: 'Derived',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
+ {children}
+
+ );
+ const { result } = renderHook(() => useExistingCFDAccounts(), { wrapper });
+
+ expect(result.current.data.dxtrade_accounts).toEqual(
+ expect.arrayContaining([
+ {
+ account_id: 'DXR1646584',
+ account_type: 'real',
+ balance: 0,
+ currency: 'USD',
+ display_balance: '0.00',
+ enabled: 1,
+ landing_company_short: 'svg',
+ login: '8807230',
+ loginid: 'DXR1646584',
+ market_type: 'all',
+ platform: 'dxtrade',
+ transfer_icon: 'IcRebrandingDerivX',
+ },
+ ])
+ );
+
+ expect(result.current.data.derivez_accounts).toEqual(
+ expect.arrayContaining([
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'za',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'mei+za1@binary.com',
+ group: 'real\\p02_ts01\\all\\svg_ez_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'EZR80001086',
+ loginid: 'EZR80001086',
+ market_type: 'all',
+ name: 'Baily Pan',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ transfer_icon: 'IcRebrandingDerivEz',
+ },
+ ])
+ );
+ });
+});
diff --git a/packages/hooks/src/__tests__/useTransferBetweenAccounts.spec.tsx b/packages/hooks/src/__tests__/useTransferBetweenAccounts.spec.tsx
new file mode 100644
index 000000000000..90eb81b411a9
--- /dev/null
+++ b/packages/hooks/src/__tests__/useTransferBetweenAccounts.spec.tsx
@@ -0,0 +1,294 @@
+import React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import useTransferBetweenAccounts from '../useTransferBetweenAccounts';
+import { StoreProvider, mockStore } from '@deriv/stores';
+import { APIProvider } from '@deriv/api';
+
+jest.mock('@deriv/api', () => ({
+ ...jest.requireActual('@deriv/api'),
+ useFetch: jest.fn((name: string) => {
+ if (name === 'authorize') {
+ return {
+ data: {
+ authorize: {
+ account_list: [
+ {
+ account_category: 'wallet',
+ account_type: 'doughflow',
+ created_at: 1688642811,
+ currency: 'USD',
+ is_disabled: 0,
+ is_selected: true,
+ is_virtual: 0,
+ landing_company_name: 'svg',
+ linked_to: [
+ {
+ loginid: 'MTR100967300',
+ platform: 'mt5',
+ },
+ {
+ loginid: 'MTR80057067',
+ platform: 'mt5',
+ },
+ {
+ loginid: 'DXR1646584',
+ platform: 'dxtrade',
+ },
+ {
+ loginid: 'EZR80001086',
+ platform: 'derivez',
+ },
+ ],
+ loginid: 'CRW1030',
+ },
+ ],
+ },
+ },
+ };
+ }
+ if (name === 'mt5_login_list') {
+ return {
+ data: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'ng',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'email@gmail.com',
+ group: 'real\\p02_ts02\\synthetic\\svg_std_usd\\04',
+ landing_company_short: 'svg',
+ leverage: 500,
+ login: 'MTR100967300',
+ market_type: 'synthetic',
+ name: 'Name',
+ server: 'p02_ts02',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_synthetic',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts02',
+ },
+ status: null,
+ sub_account_category: '',
+ sub_account_type: 'financial',
+ },
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'ng',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'email@gmail.com',
+ group: 'real\\p02_ts01\\all\\svg_std-sf_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'MTR80057067',
+ market_type: 'all',
+ name: 'Name',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ status: null,
+ sub_account_category: 'swap_free',
+ sub_account_type: 'standard',
+ },
+ ],
+ },
+ };
+ }
+ if (name === 'trading_platform_accounts') {
+ return {
+ data: {
+ trading_platform_accounts: [
+ {
+ account_id: 'DXR1646584',
+ account_type: 'real',
+ balance: 0,
+ currency: 'USD',
+ display_balance: '0.00',
+ enabled: 1,
+ landing_company_short: 'svg',
+ login: '8807230',
+ market_type: 'all',
+ platform: 'dxtrade',
+ },
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'za',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'mei+za1@binary.com',
+ group: 'real\\p02_ts01\\all\\svg_ez_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'EZR80001086',
+ market_type: 'all',
+ name: 'Baily Pan',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ },
+ ],
+ },
+ };
+ }
+ if (name === 'website_status') {
+ return {
+ data: {
+ website_status: {
+ currencies_config: {
+ USD: {
+ fractional_digits: 2,
+ name: 'US Dollar',
+ type: 'fiat',
+ },
+ },
+ },
+ },
+ };
+ }
+ if (name === 'transfer_between_accounts') {
+ return {
+ data: {
+ accounts: [
+ {
+ account_type: 'wallet',
+ balance: '100.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'CRW1030',
+ },
+ {
+ account_type: 'mt5',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'MTR100967300',
+ },
+ {
+ account_type: 'mt5',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'MTR80057067',
+ },
+ {
+ account_type: 'derivez',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'EZR80001086',
+ },
+ {
+ account_type: 'dxtrade',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'DXR1646584',
+ },
+ ],
+ },
+ };
+ }
+
+ return { data: undefined };
+ }),
+}));
+
+describe('useTransferBetweenAccounts', () => {
+ let mock_store: ReturnType, wrapper: ({ children }: { children: JSX.Element }) => JSX.Element;
+
+ beforeEach(() => {
+ mock_store = mockStore({
+ client: {
+ loginid: 'CRW1030',
+ accounts: {
+ CRW1030: {
+ token: 'token',
+ },
+ },
+ },
+ });
+
+ wrapper = ({ children }: { children: JSX.Element }) => (
+
+ {children}
+
+ );
+ });
+
+ it('should be correct amount of transfer accounts', () => {
+ const {
+ result: {
+ current: { trading_accounts, wallet_accounts },
+ },
+ } = renderHook(() => useTransferBetweenAccounts(), { wrapper });
+
+ expect(Object.keys(trading_accounts).length).toBe(4);
+ expect(Object.keys(wallet_accounts).length).toBe(1);
+ });
+
+ it('all transfer accounts should have extended properties', () => {
+ const {
+ result: {
+ current: { trading_accounts, wallet_accounts },
+ },
+ } = renderHook(() => useTransferBetweenAccounts(), { wrapper });
+
+ Object.values({ ...trading_accounts, ...wallet_accounts }).forEach(account => {
+ expect(account).toHaveProperty('active_wallet_icon');
+ expect(account).toHaveProperty('display_currency_code');
+ expect(account).toHaveProperty('gradient_class');
+ expect(account).toHaveProperty('icon');
+ expect(account).toHaveProperty('is_demo');
+ expect(account).toHaveProperty('shortcode');
+ expect(account).toHaveProperty('type');
+ });
+ });
+
+ it('should return proper active account with extended properties', () => {
+ const {
+ result: {
+ current: { active_wallet },
+ },
+ } = renderHook(() => useTransferBetweenAccounts(), { wrapper });
+
+ expect(active_wallet).toEqual({
+ account_type: 'wallet',
+ active_wallet_icon: 'IcWalletCurrencyUsd',
+ balance: 100,
+ currency: 'USD',
+ demo_account: 0,
+ display_currency_code: 'USD',
+ gradient_class: 'wallet-card__usd-bg',
+ icon: 'IcWalletCurrencyUsd',
+ is_demo: false,
+ loginid: 'CRW1030',
+ shortcode: 'svg',
+ type: 'fiat',
+ });
+ });
+});
diff --git a/packages/hooks/src/__tests__/useWalletTransactions.spec.tsx b/packages/hooks/src/__tests__/useWalletTransactions.spec.tsx
index d20c813f4685..c87c966fd916 100644
--- a/packages/hooks/src/__tests__/useWalletTransactions.spec.tsx
+++ b/packages/hooks/src/__tests__/useWalletTransactions.spec.tsx
@@ -91,6 +91,19 @@ describe('useWalletsList', () => {
},
],
},
+ website_status: {
+ currencies_config: {
+ USD: {
+ fractional_digits: 2,
+ is_deposit_suspended: 0,
+ is_suspended: 0,
+ is_withdrawal_suspended: 0,
+ name: 'US Dollar',
+ stake_default: 10,
+ type: 'fiat',
+ },
+ },
+ },
},
} as unknown as ReturnType);
diff --git a/packages/hooks/src/__tests__/useWalletTransfer.spec.tsx b/packages/hooks/src/__tests__/useWalletTransfer.spec.tsx
new file mode 100644
index 000000000000..4ecf2da6538e
--- /dev/null
+++ b/packages/hooks/src/__tests__/useWalletTransfer.spec.tsx
@@ -0,0 +1,252 @@
+import React from 'react';
+import { renderHook } from '@testing-library/react-hooks';
+import useWalletTransfer from '../useWalletTransfer';
+import { StoreProvider, mockStore } from '@deriv/stores';
+import { APIProvider } from '@deriv/api';
+
+jest.mock('@deriv/api', () => ({
+ ...jest.requireActual('@deriv/api'),
+ useFetch: jest.fn((name: string) => {
+ if (name === 'authorize') {
+ return {
+ data: {
+ authorize: {
+ account_list: [
+ {
+ account_category: 'wallet',
+ account_type: 'doughflow',
+ created_at: 1688642811,
+ currency: 'USD',
+ is_disabled: 0,
+ is_selected: true,
+ is_virtual: 0,
+ landing_company_name: 'svg',
+ linked_to: [
+ {
+ loginid: 'MTR100967300',
+ platform: 'mt5',
+ },
+ {
+ loginid: 'MTR80057067',
+ platform: 'mt5',
+ },
+ {
+ loginid: 'DXR1646584',
+ platform: 'dxtrade',
+ },
+ {
+ loginid: 'EZR80001086',
+ platform: 'derivez',
+ },
+ ],
+ loginid: 'CRW1030',
+ },
+ ],
+ },
+ },
+ };
+ }
+ if (name === 'mt5_login_list') {
+ return {
+ data: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'ng',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'email@gmail.com',
+ group: 'real\\p02_ts02\\synthetic\\svg_std_usd\\04',
+ landing_company_short: 'svg',
+ leverage: 500,
+ login: 'MTR100967300',
+ market_type: 'synthetic',
+ name: 'Name',
+ server: 'p02_ts02',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_synthetic',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts02',
+ },
+ status: null,
+ sub_account_category: '',
+ sub_account_type: 'financial',
+ },
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'ng',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'email@gmail.com',
+ group: 'real\\p02_ts01\\all\\svg_std-sf_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'MTR80057067',
+ market_type: 'all',
+ name: 'Name',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ status: null,
+ sub_account_category: 'swap_free',
+ sub_account_type: 'standard',
+ },
+ ],
+ },
+ };
+ }
+ if (name === 'trading_platform_accounts') {
+ return {
+ data: {
+ trading_platform_accounts: [
+ {
+ account_id: 'DXR1646584',
+ account_type: 'real',
+ balance: 0,
+ currency: 'USD',
+ display_balance: '0.00',
+ enabled: 1,
+ landing_company_short: 'svg',
+ login: '8807230',
+ market_type: 'all',
+ platform: 'dxtrade',
+ },
+ {
+ account_type: 'real',
+ balance: 0,
+ country: 'za',
+ currency: 'USD',
+ display_balance: '0.00',
+ email: 'mei+za1@binary.com',
+ group: 'real\\p02_ts01\\all\\svg_ez_usd',
+ landing_company_short: 'svg',
+ leverage: 1000,
+ login: 'EZR80001086',
+ market_type: 'all',
+ name: 'Baily Pan',
+ server: 'p02_ts01',
+ server_info: {
+ environment: 'Deriv-Server-02',
+ geolocation: {
+ group: 'africa_derivez',
+ location: 'South Africa',
+ region: 'Africa',
+ sequence: 2,
+ },
+ id: 'p02_ts01',
+ },
+ },
+ ],
+ },
+ };
+ }
+ if (name === 'website_status') {
+ return {
+ data: {
+ website_status: {
+ currencies_config: {
+ USD: {
+ fractional_digits: 2,
+ name: 'US Dollar',
+ type: 'fiat',
+ },
+ },
+ },
+ },
+ };
+ }
+ if (name === 'transfer_between_accounts') {
+ return {
+ data: {
+ accounts: [
+ {
+ account_type: 'wallet',
+ balance: '100.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'CRW1030',
+ },
+ {
+ account_type: 'mt5',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'MTR100967300',
+ },
+ {
+ account_type: 'mt5',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'MTR80057067',
+ },
+ {
+ account_type: 'derivez',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'EZR80001086',
+ },
+ {
+ account_type: 'dxtrade',
+ balance: '0.00',
+ currency: 'USD',
+ demo_account: 0,
+ loginid: 'DXR1646584',
+ },
+ ],
+ },
+ };
+ }
+
+ return { data: undefined };
+ }),
+}));
+
+describe('useWalletTransfer', () => {
+ let mock_store: ReturnType, wrapper: ({ children }: { children: JSX.Element }) => JSX.Element;
+
+ beforeEach(() => {
+ mock_store = mockStore({
+ client: {
+ loginid: 'CRW1030',
+ accounts: {
+ CRW1030: {
+ token: 'token',
+ },
+ },
+ },
+ });
+
+ wrapper = ({ children }: { children: JSX.Element }) => (
+
+ {children}
+
+ );
+ });
+
+ it('from_account should be undefined by default', () => {
+ const {
+ result: {
+ current: { from_account },
+ },
+ } = renderHook(() => useWalletTransfer(), { wrapper });
+
+ expect(from_account).toBeUndefined();
+ });
+});
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index 1e6144cbf18b..274d245c637b 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -1,5 +1,7 @@
export { default as useAccountTransferVisible } from './useAccountTransferVisible';
export { default as useActiveWallet } from './useActiveWallet';
+export { default as useAuthorize } from './useAuthorize';
+export { default as useAvailableWallets } from './useAvailableWallets';
export { default as useCFDAccounts } from './useCFDAccounts';
export { default as useCFDAllAccounts } from './useCFDAllAccounts';
export { default as useCFDDemoAccounts } from './useCFDDemoAccounts';
@@ -14,6 +16,7 @@ export { default as useDepositCryptoAddress } from './useDepositCryptoAddress';
export { default as useDepositFiatAddress } from './useDepositFiatAddress';
export { default as useDepositLocked } from './useDepositLocked';
export { default as useExchangeRate } from './useExchangeRate';
+export { default as useExistingCFDAccounts } from './useExistingCFDAccounts';
export { default as useFeatureFlags } from './useFeatureFlags';
export { default as useFiatAccountList } from './useFiatAccountList';
export { default as useHasActiveRealAccount } from './useHasActiveRealAccount';
@@ -44,10 +47,10 @@ export { default as usePlatformDemoAccount } from './usePlatformDemoAccount';
export { default as usePlatformRealAccounts } from './usePlatformRealAccounts';
export { default as useRealSTPAccount } from './useRealSTPAccount';
export { default as useTotalAccountBalance } from './useTotalAccountBalance';
+export { default as useTransferBetweenAccounts } from './useTransferBetweenAccounts';
export { default as useVerifyEmail } from './useVerifyEmail';
export { default as useWalletsList } from './useWalletsList';
-export { default as useAvailableWallets } from './useAvailableWallets';
-export { default as useAuthorize } from './useAuthorize';
export { default as useWalletTransactions } from './useWalletTransactions';
+export { default as useWalletTransfer } from './useWalletTransfer';
export { default as useWalletMigration } from './useWalletMigration';
export { useIsAccountStatusPresent } from './useIsAccountStatusPresent';
diff --git a/packages/hooks/src/useExistingCFDAccounts.ts b/packages/hooks/src/useExistingCFDAccounts.ts
new file mode 100644
index 000000000000..2a1961a494cf
--- /dev/null
+++ b/packages/hooks/src/useExistingCFDAccounts.ts
@@ -0,0 +1,106 @@
+import { useMemo } from 'react';
+import { useFetch } from '@deriv/api';
+import useActiveWallet from './useActiveWallet';
+import { useStore } from '@deriv/stores';
+
+type TAccount = {
+ cfd_type?: 'mt5' | 'derivez' | 'dxtrade';
+ market_type?: 'financial' | 'synthetic' | 'all';
+};
+
+const getAccountIcon = ({ cfd_type, market_type }: TAccount) => {
+ switch (cfd_type) {
+ case 'mt5': {
+ switch (market_type) {
+ case 'financial':
+ return 'IcRebrandingMt5FinancialDashboard';
+ case 'synthetic':
+ return 'IcRebrandingMt5DerivedDashboard';
+ case 'all':
+ return 'IcRebrandingMt5SwapFree';
+ default:
+ return 'IcRebrandingDmt5Dashboard';
+ }
+ }
+ case 'derivez':
+ return 'IcRebrandingDerivEz';
+ case 'dxtrade':
+ return 'IcRebrandingDerivX';
+ default:
+ return '';
+ }
+};
+
+/**
+ * @description This hook is used to get the created CFD accounts of the user.
+ */
+const useExistingCFDAccounts = () => {
+ const { traders_hub } = useStore();
+ const { combined_cfd_mt5_accounts } = traders_hub;
+ const wallet = useActiveWallet();
+ const { data: mt5, ...mt5_rest } = useFetch('mt5_login_list');
+ const { data: derivez, ...derivez_rest } = useFetch('trading_platform_accounts', {
+ payload: { platform: 'derivez' },
+ });
+ const { data: dxtrade, ...dxtrade_rest } = useFetch('trading_platform_accounts', {
+ payload: { platform: 'dxtrade' },
+ });
+
+ /**
+ *
+ * @description This is the modified MT5 accounts that will be used in the CFD account creation.
+ */
+ const modified_mt5_accounts = useMemo(() => {
+ const getAccountInfo = (login?: string) => {
+ return {
+ platform: wallet?.linked_to?.find(linked => linked.loginid === login)?.platform,
+ icon: combined_cfd_mt5_accounts?.find(cfd => cfd.login === login)?.icon,
+ description: combined_cfd_mt5_accounts?.find(cfd => cfd.login === login)?.description,
+ name: combined_cfd_mt5_accounts?.find(cfd => cfd.login === login)?.name,
+ sub_title: combined_cfd_mt5_accounts?.find(cfd => cfd.login === login)?.sub_title,
+ action_type: 'multi-action',
+ };
+ };
+
+ return mt5?.mt5_login_list?.map(account => ({
+ ...account,
+ ...getAccountInfo(account.login),
+ loginid: account.login,
+ transfer_icon: getAccountIcon({ cfd_type: 'mt5', ...account }),
+ }));
+ }, [mt5?.mt5_login_list, wallet?.linked_to, combined_cfd_mt5_accounts]);
+
+ const modified_derivez_accounts = useMemo(
+ () =>
+ derivez?.trading_platform_accounts?.map(account => ({
+ ...account,
+ loginid: account.login,
+ transfer_icon: getAccountIcon({ cfd_type: 'derivez' }),
+ })),
+ [derivez?.trading_platform_accounts]
+ );
+ const modified_dxtrade_accounts = useMemo(
+ () =>
+ dxtrade?.trading_platform_accounts?.map(account => ({
+ ...account,
+ loginid: account.account_id,
+ transfer_icon: getAccountIcon({ cfd_type: 'dxtrade' }),
+ })),
+ [dxtrade?.trading_platform_accounts]
+ );
+ const data = useMemo(
+ () => ({
+ mt5_accounts: modified_mt5_accounts || [],
+ dxtrade_accounts: modified_dxtrade_accounts || [],
+ derivez_accounts: modified_derivez_accounts || [],
+ }),
+ [modified_mt5_accounts, modified_dxtrade_accounts, modified_derivez_accounts]
+ );
+
+ return {
+ data,
+ isSuccess: [mt5_rest.isSuccess, dxtrade_rest.isSuccess, derivez_rest.isSuccess].every(Boolean),
+ };
+};
+
+export default useExistingCFDAccounts;
diff --git a/packages/hooks/src/useTransferBetweenAccounts.ts b/packages/hooks/src/useTransferBetweenAccounts.ts
new file mode 100644
index 000000000000..35f232ab805c
--- /dev/null
+++ b/packages/hooks/src/useTransferBetweenAccounts.ts
@@ -0,0 +1,144 @@
+import { useMemo } from 'react';
+import { useStore } from '@deriv/stores';
+import { useFetch } from '@deriv/api';
+import useActiveWallet from './useActiveWallet';
+import useCurrencyConfig from './useCurrencyConfig';
+import useExistingCFDAccounts from './useExistingCFDAccounts';
+import useWalletsList from './useWalletsList';
+
+const useTransferBetweenAccounts = () => {
+ const { ui } = useStore();
+ const { is_dark_mode_on } = ui;
+
+ const active_wallet = useActiveWallet();
+
+ const { data: wallets } = useWalletsList();
+
+ const { getConfig } = useCurrencyConfig();
+
+ const trading_apps_icon = is_dark_mode_on ? 'IcWalletOptionsDark' : 'IcWalletOptionsLight';
+
+ const {
+ data: { derivez_accounts, dxtrade_accounts, mt5_accounts },
+ isSuccess: is_cfd_accounts_loaded,
+ } = useExistingCFDAccounts();
+
+ const { data, ...rest } = useFetch('transfer_between_accounts', {
+ payload: { accounts: 'all' },
+ options: { enabled: is_cfd_accounts_loaded },
+ });
+
+ const modified_transfer_accounts = useMemo(() => {
+ const all_linked_cfd_accounts = [...derivez_accounts, ...dxtrade_accounts, ...mt5_accounts];
+
+ const getAccountType = (is_demo?: number, currency?: string): 'fiat' | 'crypto' | 'demo' => {
+ if (is_demo) return 'demo';
+ return getConfig(currency || '')?.is_crypto ? 'crypto' : 'fiat';
+ };
+
+ const accounts = data?.accounts?.map(account => {
+ return {
+ ...account,
+ active_wallet_icon: active_wallet?.icon,
+ balance: parseFloat(
+ Number(account.balance).toFixed(getConfig(account.currency || '')?.fractional_digits)
+ ),
+ display_currency_code: getConfig(account.currency || '')?.display_code,
+ is_demo: Boolean(account?.demo_account),
+ shortcode: active_wallet?.landing_company_name,
+ type: getAccountType(account.demo_account, account.currency),
+ };
+ });
+
+ return {
+ trading_accounts:
+ accounts?.reduce(
+ (trading_accounts, account) => {
+ if (account.account_type === 'wallet') return trading_accounts;
+ if (!account.loginid) return trading_accounts;
+
+ const cfd_icon = all_linked_cfd_accounts.find(
+ cfd_account => account.loginid && cfd_account.loginid?.includes(account.loginid)
+ )?.transfer_icon;
+
+ trading_accounts[account.loginid] = {
+ ...account,
+ gradient_class: active_wallet?.gradient_card_class,
+ icon: account.account_type === 'trading' ? trading_apps_icon : cfd_icon,
+ ...(account.account_type === 'mt5' && {
+ mt5_market_type: mt5_accounts?.find(
+ mt5_account => account.loginid && mt5_account.loginid?.includes(account.loginid)
+ )?.market_type,
+ }),
+ };
+
+ return trading_accounts;
+ },
+ {} as Record<
+ string,
+ NonNullable<
+ typeof accounts[number] & {
+ gradient_class?: `wallet-card__${string}`;
+ icon?: string;
+ mt5_market_type?: 'all' | 'financial' | 'synthetic';
+ }
+ >
+ >
+ ) || {},
+ wallet_accounts:
+ accounts?.reduce(
+ (wallet_accounts, wallet) => {
+ if (wallet.account_type !== 'wallet') return wallet_accounts;
+ if (!wallet.loginid) return wallet_accounts;
+
+ const available_wallet = wallets?.find(acc => acc.loginid === wallet.loginid);
+
+ wallet_accounts[wallet.loginid] = {
+ ...wallet,
+ icon: available_wallet?.icon,
+ gradient_class: available_wallet?.gradient_card_class,
+ };
+
+ return wallet_accounts;
+ },
+ {} as Record<
+ string,
+ NonNullable<
+ typeof accounts[number] & {
+ gradient_class?: `wallet-card__${string}`;
+ icon?: string;
+ }
+ >
+ >
+ ) || {},
+ };
+ }, [
+ active_wallet?.gradient_card_class,
+ active_wallet?.icon,
+ active_wallet?.landing_company_name,
+ data?.accounts,
+ derivez_accounts,
+ dxtrade_accounts,
+ getConfig,
+ mt5_accounts,
+ trading_apps_icon,
+ wallets,
+ ]);
+
+ const modified_active_wallet = useMemo(() => {
+ return active_wallet?.loginid
+ ? {
+ ...modified_transfer_accounts.wallet_accounts[active_wallet?.loginid],
+ }
+ : undefined;
+ }, [active_wallet?.loginid, modified_transfer_accounts.wallet_accounts]);
+
+ return {
+ ...rest,
+ active_wallet: modified_active_wallet,
+ trading_accounts: modified_transfer_accounts.trading_accounts,
+ wallet_accounts: modified_transfer_accounts.wallet_accounts,
+ };
+};
+
+export default useTransferBetweenAccounts;
diff --git a/packages/hooks/src/useWalletTransfer.ts b/packages/hooks/src/useWalletTransfer.ts
new file mode 100644
index 000000000000..4f769ee57b43
--- /dev/null
+++ b/packages/hooks/src/useWalletTransfer.ts
@@ -0,0 +1,54 @@
+import { useMemo, useState, useEffect } from 'react';
+import useTransferBetweenAccounts from './useTransferBetweenAccounts';
+
+const useWalletTransfer = () => {
+ const {
+ active_wallet,
+ trading_accounts,
+ wallet_accounts,
+ isLoading: is_accounts_loading,
+ } = useTransferBetweenAccounts();
+
+ const [from_account, setFromAccount] = useState();
+ const [to_account, setToAccount] = useState();
+
+ const to_account_list = useMemo(() => {
+ if (!from_account?.loginid) return { trading_accounts: {}, wallet_accounts: {} };
+ if (!active_wallet?.loginid) return { trading_accounts: {}, wallet_accounts: {} };
+
+ if (from_account?.loginid === active_wallet?.loginid) {
+ return {
+ trading_accounts,
+ wallet_accounts: Object.fromEntries(
+ Object.entries(wallet_accounts).filter(
+ ([key]) => active_wallet?.loginid && !key.includes(active_wallet?.loginid)
+ )
+ ),
+ };
+ }
+ return { trading_accounts: {}, wallet_accounts: { [active_wallet?.loginid]: active_wallet } };
+ }, [active_wallet, from_account?.loginid, trading_accounts, wallet_accounts]);
+
+ //this useEffect populates from/to accounts with updated values, if they were updated in the background
+ useEffect(() => {
+ setFromAccount(acc => {
+ return acc?.loginid ? { ...trading_accounts, ...wallet_accounts }[acc?.loginid] : undefined;
+ });
+ setToAccount(acc => {
+ return acc?.loginid ? { ...trading_accounts, ...wallet_accounts }[acc?.loginid] : undefined;
+ });
+ }, [setFromAccount, setToAccount, trading_accounts, wallet_accounts]);
+
+ return {
+ active_wallet,
+ is_accounts_loading,
+ from_account,
+ to_account,
+ to_account_list,
+ transfer_accounts: { trading_accounts, wallet_accounts },
+ setFromAccount,
+ setToAccount,
+ };
+};
+
+export default useWalletTransfer;
diff --git a/packages/shared/src/styles/themes.scss b/packages/shared/src/styles/themes.scss
index 8a29f21f5e32..73c8777627aa 100644
--- a/packages/shared/src/styles/themes.scss
+++ b/packages/shared/src/styles/themes.scss
@@ -85,6 +85,7 @@
--text-less-prominent: #{$color-grey-1};
--text-prominent: #{$color-black-1};
--text-disabled: #{$color-grey-1};
+ --text-disabled-1: #{$color-grey-6};
--text-loss-danger: #{$color-red-1};
--text-profit-success: #{$color-green-1};
--text-warning: #{$color-yellow};
@@ -235,6 +236,7 @@
--text-less-prominent: #{$color-grey-7};
--text-primary: #{$color-grey-1};
--text-disabled: #{$color-black-6};
+ --text-disabled-1: #{$color-black-6};
--text-profit-success: #{$color-green-3};
--text-loss-danger: #{$color-red-2};
--text-red: #{$color-red};
diff --git a/packages/utils/src/__tests__/getWalletCurrencyIcon.spec.tsx b/packages/utils/src/__tests__/getWalletCurrencyIcon.spec.ts
similarity index 100%
rename from packages/utils/src/__tests__/getWalletCurrencyIcon.spec.tsx
rename to packages/utils/src/__tests__/getWalletCurrencyIcon.spec.ts
diff --git a/packages/utils/src/__tests__/groupTransactionsByDay.spec.tsx b/packages/utils/src/__tests__/groupTransactionsByDay.spec.ts
similarity index 100%
rename from packages/utils/src/__tests__/groupTransactionsByDay.spec.tsx
rename to packages/utils/src/__tests__/groupTransactionsByDay.spec.ts
diff --git a/packages/utils/src/__tests__/unFormatLocaleString.spec.tsx b/packages/utils/src/__tests__/unFormatLocaleString.spec.ts
similarity index 100%
rename from packages/utils/src/__tests__/unFormatLocaleString.spec.tsx
rename to packages/utils/src/__tests__/unFormatLocaleString.spec.ts
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 56d7c5788ec8..3f0b1026c243 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -1,4 +1,4 @@
-export { default as getWalletCurrencyIcon } from './getWalletCurrencyIcon';
export { default as groupTransactionsByDay } from './groupTransactionsByDay';
+export { default as getWalletCurrencyIcon } from './getWalletCurrencyIcon';
export { default as unFormatLocaleString } from './unFormatLocaleString';
export { getLocalStorage } from './getLocalStorage';