{({ field }) => (
-
- setFieldValue('citizenship', value ? text : '', true)}
- />
-
-
- {
- setFieldTouched('citizenship', true);
- setFieldValue('citizenship', e.target.value, true);
- }}
- should_hide_disabled_options={false}
- />
-
+ setFieldValue('citizenship', value ? text : '', true)}
+ />
)}
diff --git a/packages/core/src/App/Containers/Modals/ready-to-deposit-modal/ready-to-deposit-modal.tsx b/packages/core/src/App/Containers/Modals/ready-to-deposit-modal/ready-to-deposit-modal.tsx
index a9a3c66a6dcd..4484f8517a5b 100644
--- a/packages/core/src/App/Containers/Modals/ready-to-deposit-modal/ready-to-deposit-modal.tsx
+++ b/packages/core/src/App/Containers/Modals/ready-to-deposit-modal/ready-to-deposit-modal.tsx
@@ -5,7 +5,7 @@ import { useStore, observer } from '@deriv/stores';
import './ready-to-deposit-modal.scss';
const ReadyToDepositModal = observer(() => {
- const { ui, traders_hub } = useStore();
+ const { ui, traders_hub, client } = useStore();
const { is_eu_user } = traders_hub;
const {
is_ready_to_deposit_modal_visible: is_open,
@@ -13,11 +13,18 @@ const ReadyToDepositModal = observer(() => {
disableApp,
enableApp,
openRealAccountSignup,
+ setShouldShowCooldownModal,
} = ui;
+ const { real_account_creation_unlock_date } = client;
const createAccount = () => {
onClose();
- return is_eu_user ? openRealAccountSignup('maltainvest') : openRealAccountSignup();
+ if (real_account_creation_unlock_date) {
+ return setShouldShowCooldownModal(true);
+ } else if (is_eu_user) {
+ return openRealAccountSignup('maltainvest');
+ }
+ return openRealAccountSignup();
};
return (
diff --git a/packages/core/src/App/Containers/SetResidenceModal/set-residence-form.jsx b/packages/core/src/App/Containers/SetResidenceModal/set-residence-form.jsx
index d0418cab274f..e9c52dd1ff81 100644
--- a/packages/core/src/App/Containers/SetResidenceModal/set-residence-form.jsx
+++ b/packages/core/src/App/Containers/SetResidenceModal/set-residence-form.jsx
@@ -1,7 +1,7 @@
import { Field } from 'formik';
import PropTypes from 'prop-types';
import React from 'react';
-import { Autocomplete, DesktopWrapper, MobileWrapper, SelectNative, Text } from '@deriv/components';
+import { Autocomplete, Text } from '@deriv/components';
import { localize } from '@deriv/translations';
const SetResidenceForm = ({
@@ -26,38 +26,19 @@ const SetResidenceForm = ({
{({ field }) => (
-
-
- setFieldValue('residence', value ? text : '', true)
- }
- />
-
-
- {
- setFieldTouched('residence', true);
- setFieldValue('residence', e.target.value, true);
- }}
- />
-
+ setFieldValue('residence', value ? text : '', true)}
+ />
)}
diff --git a/packages/core/src/Services/ws-methods.js b/packages/core/src/Services/ws-methods.js
index 80ceab624bb7..6765de4ca3fc 100644
--- a/packages/core/src/Services/ws-methods.js
+++ b/packages/core/src/Services/ws-methods.js
@@ -1,6 +1,5 @@
import BinarySocket from '_common/base/socket_base';
-import { trackJSNetworkMonitor } from '@deriv/shared';
const WS = BinarySocket;
-export default trackJSNetworkMonitor(WS);
+export default WS;
diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js
index 7171ea4bff47..7f842a59b7f7 100644
--- a/packages/core/src/Stores/client-store.js
+++ b/packages/core/src/Stores/client-store.js
@@ -507,11 +507,11 @@ export default class ClientStore extends BaseStore {
get legal_allowed_currencies() {
const getDefaultAllowedCurrencies = () => {
- if (this.landing_companies.gaming_company) {
- return this.landing_companies.gaming_company.legal_allowed_currencies;
+ if (this.landing_companies?.gaming_company) {
+ return this.landing_companies?.gaming_company?.legal_allowed_currencies;
}
- if (this.landing_companies.financial_company) {
- return this.landing_companies.financial_company.legal_allowed_currencies;
+ if (this.landing_companies?.financial_company) {
+ return this.landing_companies?.financial_company?.legal_allowed_currencies;
}
return [];
};
diff --git a/packages/hooks/src/__tests__/useCFDAccounts.spec.tsx b/packages/hooks/src/__tests__/useCFDAccounts.spec.tsx
new file mode 100644
index 000000000000..840740e75702
--- /dev/null
+++ b/packages/hooks/src/__tests__/useCFDAccounts.spec.tsx
@@ -0,0 +1,95 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useCFDAccounts from '../useCFDAccounts';
+
+describe('useCFDAccounts', () => {
+ test('should return empty array when client has no CFD accounts', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAccounts(), { wrapper });
+
+ expect(result.current.all.length).toBe(0);
+ expect(result.current.demo.length).toBe(0);
+ expect(result.current.real.length).toBe(0);
+ });
+
+ test('should return proper data when client only has CFD demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'demo',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAccounts(), { wrapper });
+
+ expect(result.current.all.length).toBe(1);
+ expect(result.current.demo.length).toBe(1);
+ expect(result.current.real.length).toBe(0);
+ });
+
+ test('should return proper data when client only has CFD real accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAccounts(), { wrapper });
+
+ expect(result.current.all.length).toBe(1);
+ expect(result.current.demo.length).toBe(0);
+ expect(result.current.real.length).toBe(1);
+ });
+
+ test('should return proper data when client only has both CFD real and demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'demo',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAccounts(), { wrapper });
+
+ expect(result.current.all.length).toBe(2);
+ expect(result.current.demo.length).toBe(1);
+ expect(result.current.real.length).toBe(1);
+ });
+});
diff --git a/packages/hooks/src/__tests__/useCFDAllAccounts.spec.tsx b/packages/hooks/src/__tests__/useCFDAllAccounts.spec.tsx
new file mode 100644
index 000000000000..74a602c08adb
--- /dev/null
+++ b/packages/hooks/src/__tests__/useCFDAllAccounts.spec.tsx
@@ -0,0 +1,87 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useCFDAllAccounts from '../useCFDAllAccounts';
+
+describe('useCFDAllAccounts', () => {
+ test('should return empty array when client has no CFD accounts', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAllAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(0);
+ });
+
+ test('should return proper data when client has MT5 accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAllAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(1);
+ });
+
+ test('should return proper data when client has dxtrade accounts', async () => {
+ const mock = mockStore({
+ client: {
+ dxtrade_accounts_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAllAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(1);
+ });
+
+ test('should return proper data when client has both MT5 and dxtrade accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'real',
+ balance: 1000,
+ currency: 'USD',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDAllAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(2);
+ });
+});
diff --git a/packages/hooks/src/__tests__/useCFDDemoAccounts.spec.tsx b/packages/hooks/src/__tests__/useCFDDemoAccounts.spec.tsx
new file mode 100644
index 000000000000..d2015000c2bb
--- /dev/null
+++ b/packages/hooks/src/__tests__/useCFDDemoAccounts.spec.tsx
@@ -0,0 +1,68 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useCFDDemoAccounts from '../useCFDDemoAccounts';
+
+describe('useCFDDemoAccounts', () => {
+ test('should return empty array when user has no CFD accounts', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDDemoAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(0);
+ });
+
+ test('should return empty array when user has no CFD demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'real',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDDemoAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(0);
+ });
+
+ test('should return proper data when user has CFD demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'demo',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'real',
+ },
+ {
+ account_type: 'demo',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDDemoAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(2);
+ });
+});
diff --git a/packages/hooks/src/__tests__/useCFDRealAccounts.spec.tsx b/packages/hooks/src/__tests__/useCFDRealAccounts.spec.tsx
new file mode 100644
index 000000000000..c796f02d09fe
--- /dev/null
+++ b/packages/hooks/src/__tests__/useCFDRealAccounts.spec.tsx
@@ -0,0 +1,68 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useCFDRealAccounts from '../useCFDRealAccounts';
+
+describe('useCFDRealAccounts', () => {
+ test('should return empty array when user has no CFD accounts', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDRealAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(0);
+ });
+
+ test('should return empty array when user has no CFD real accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'demo',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'demo',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDRealAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(0);
+ });
+
+ test('should return proper data when user has CFD real accounts', async () => {
+ const mock = mockStore({
+ client: {
+ mt5_login_list: [
+ {
+ account_type: 'real',
+ },
+ ],
+ dxtrade_accounts_list: [
+ {
+ account_type: 'real',
+ },
+ {
+ account_type: 'demo',
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useCFDRealAccounts(), { wrapper });
+
+ expect(result.current?.length).toBe(2);
+ });
+});
diff --git a/packages/hooks/src/__tests__/useExchangeRate.spec.tsx b/packages/hooks/src/__tests__/useExchangeRate.spec.tsx
new file mode 100644
index 000000000000..19a574ea65eb
--- /dev/null
+++ b/packages/hooks/src/__tests__/useExchangeRate.spec.tsx
@@ -0,0 +1,48 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useExchangeRate from '../useExchangeRate';
+
+describe('useExchangeRate', () => {
+ test('should return 1 if currency is not found', async () => {
+ const mock = mockStore({
+ exchange_rates: {
+ data: {
+ base_currency: 'USD',
+ rates: {
+ EUR: 1.3,
+ GBP: 1.4,
+ ETH: 0.0001,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useExchangeRate(), { wrapper });
+ const rate = result.current.getRate('JPY');
+ expect(rate).toBe(1);
+ });
+
+ test('should return correct rate for the given currency other than USD', async () => {
+ const mock = mockStore({
+ exchange_rates: {
+ data: {
+ rates: {
+ EUR: 1.3,
+ GBP: 1.5,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useExchangeRate(), { wrapper });
+ const rate = result.current.getRate('EUR');
+ expect(rate).toBe(1.3);
+ });
+});
diff --git a/packages/hooks/src/__tests__/usePlatformAccounts.spec.tsx b/packages/hooks/src/__tests__/usePlatformAccounts.spec.tsx
new file mode 100644
index 000000000000..d46a4cc10bac
--- /dev/null
+++ b/packages/hooks/src/__tests__/usePlatformAccounts.spec.tsx
@@ -0,0 +1,92 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import usePlatformAccounts from '../usePlatformAccounts';
+
+describe('usePlatformRealAccounts', () => {
+ test('should return proper data when user has no platform demo and real accounts', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformAccounts(), { wrapper });
+
+ expect(result.current.demo).toBe(undefined);
+ expect(result.current.real.length).toBe(0);
+ });
+
+ test('should return proper data when user only has platform demo account', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ VR1234: {
+ is_virtual: 1,
+ loginid: 'VR1234',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformAccounts(), { wrapper });
+
+ expect(result.current.demo?.loginid).toBe(mock.client.accounts.VR1234.loginid);
+ expect(result.current.real.length).toBe(0);
+ });
+
+ test('should return proper data when user only has platform real account', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ CR1234: {
+ is_virtual: 0,
+ loginid: 'CR1234',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformAccounts(), { wrapper });
+
+ expect(result.current.demo?.loginid).toBe(undefined);
+ expect(result.current.real.length).toBe(1);
+ });
+
+ test('should return proper data when user has both real and demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ CR1234: {
+ is_virtual: 0,
+ loginid: 'VR1234',
+ landing_company_shortcode: 'svg',
+ },
+ MF1234: {
+ is_virtual: 0,
+ loginid: 'VR1235',
+ landing_company_shortcode: 'maltainvest',
+ },
+ VR1235: {
+ is_virtual: 1,
+ loginid: 'VR1236',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformAccounts(), { wrapper });
+
+ expect(result.current.demo?.loginid).toBe(mock.client.accounts.VR1235.loginid);
+ expect(result.current.real.length).toBe(1);
+ expect(result.current.real[0].landing_company_shortcode).toBe('svg');
+ });
+});
diff --git a/packages/hooks/src/__tests__/usePlatformDemoAccount.spec.tsx b/packages/hooks/src/__tests__/usePlatformDemoAccount.spec.tsx
new file mode 100644
index 000000000000..680f32547cd7
--- /dev/null
+++ b/packages/hooks/src/__tests__/usePlatformDemoAccount.spec.tsx
@@ -0,0 +1,45 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import usePlatformDemoAccount from '../usePlatformDemoAccount';
+
+describe('usePlatformDemoAccount', () => {
+ test('should return undefined when user has no platform demo accounts', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ VR1234: {
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformDemoAccount(), { wrapper });
+
+ expect(result.current).toBe(undefined);
+ });
+
+ test('should return proper data when user has platform demo account', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ VR1234: {
+ is_virtual: 1,
+ loginid: 'VR1234',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformDemoAccount(), { wrapper });
+
+ expect(result.current?.loginid).toBe(mock.client.accounts.VR1234.loginid);
+ });
+});
diff --git a/packages/hooks/src/__tests__/usePlatformRealAccounts.spec.tsx b/packages/hooks/src/__tests__/usePlatformRealAccounts.spec.tsx
new file mode 100644
index 000000000000..2388882fae81
--- /dev/null
+++ b/packages/hooks/src/__tests__/usePlatformRealAccounts.spec.tsx
@@ -0,0 +1,93 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import usePlatformRealAccounts from '../usePlatformRealAccounts';
+
+describe('usePlatformRealAccounts', () => {
+ test('should return null when user has no platform real accounts', async () => {
+ const mock = mockStore({
+ client: {
+ accounts: {
+ VR1234: {
+ is_virtual: 1,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformRealAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(0);
+ });
+
+ test('should return svg accounts when user has real account and switch to non-eu accounts', async () => {
+ const mock = mockStore({
+ traders_hub: {
+ is_eu_user: false,
+ },
+ client: {
+ accounts: {
+ CR1234: {
+ is_virtual: 0,
+ loginid: 'VR1234',
+ landing_company_shortcode: 'svg',
+ },
+ MF1234: {
+ is_virtual: 0,
+ loginid: 'VR1234',
+ landing_company_shortcode: 'maltainvest',
+ },
+ VR1235: {
+ is_virtual: 1,
+ loginid: 'VR1234',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformRealAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(1);
+ expect(result.current[0].landing_company_shortcode).toBe('svg');
+ });
+
+ test('should return maltainvest accounts when user has real account and switch to eu accounts', async () => {
+ const mock = mockStore({
+ traders_hub: {
+ is_eu_user: true,
+ },
+ client: {
+ accounts: {
+ CR1234: {
+ is_virtual: 0,
+ loginid: 'VR1234',
+ landing_company_shortcode: 'svg',
+ },
+ MF1234: {
+ is_virtual: 0,
+ loginid: 'VR1234',
+ landing_company_shortcode: 'maltainvest',
+ },
+ VR1235: {
+ is_virtual: 1,
+ loginid: 'VR1234',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => usePlatformRealAccounts(), { wrapper });
+
+ expect(result.current.length).toBe(1);
+ expect(result.current[0].landing_company_shortcode).toBe('maltainvest');
+ });
+});
diff --git a/packages/hooks/src/__tests__/useTotalAccountBalance.spec.tsx b/packages/hooks/src/__tests__/useTotalAccountBalance.spec.tsx
new file mode 100644
index 000000000000..943a2d1e1a47
--- /dev/null
+++ b/packages/hooks/src/__tests__/useTotalAccountBalance.spec.tsx
@@ -0,0 +1,49 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useTotalAccountBalance from '../useTotalAccountBalance';
+
+describe('useTotalAccountBalance', () => {
+ test('should return zero when user has no account', async () => {
+ const mock = mockStore({});
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useTotalAccountBalance(mock.client.active_accounts), { wrapper });
+
+ expect(result.current.balance).toBe(0);
+ });
+
+ test('should return total balance correctly when user has multiple accounts', async () => {
+ const mock = mockStore({
+ exchange_rates: {
+ data: {
+ rates: {
+ EUR: 2,
+ AUD: 3,
+ },
+ },
+ },
+ client: {
+ active_accounts: [
+ {
+ currency: 'AUD',
+ balance: 300,
+ },
+ {
+ currency: 'EUR',
+ balance: 200,
+ },
+ ],
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useTotalAccountBalance(mock.client.active_accounts), { wrapper });
+
+ expect(result.current.balance).toBe(200);
+ });
+});
diff --git a/packages/hooks/src/__tests__/useTotalAssetCurrency.spec.tsx b/packages/hooks/src/__tests__/useTotalAssetCurrency.spec.tsx
new file mode 100644
index 000000000000..1d2ddaf4c034
--- /dev/null
+++ b/packages/hooks/src/__tests__/useTotalAssetCurrency.spec.tsx
@@ -0,0 +1,237 @@
+import * as React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+import { renderHook } from '@testing-library/react-hooks';
+import useRealTotalAssetCurrency from '../useTotalAssetCurrency';
+
+describe('useRealTotalAssetCurrency', () => {
+ test('should return default currency when user has no account', async () => {
+ const mock = mockStore({
+ client: {
+ default_currency: 'EUR',
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+ expect(result.current).toBe(mock.client.default_currency);
+ });
+
+ test('should return default currency when user has no real account', async () => {
+ const mock = mockStore({
+ client: {
+ default_currency: 'EUR',
+ accounts: {
+ acc1: {
+ currency: 'JPY',
+ is_virtual: 1,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+ expect(result.current).toBe(mock.client.default_currency);
+ });
+
+ test('should return proper currency when user has non_crypto account', async () => {
+ const mock = mockStore({
+ client: {
+ is_crypto: (currency: string) => currency === 'BTC',
+ accounts: {
+ crypto_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ },
+ non_crypto_acc: {
+ currency: 'AUD',
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe(mock.client.accounts.non_crypto_acc.currency);
+ });
+
+ test('should return empty string when user has non_crypto account with no currency set', async () => {
+ const mock = mockStore({
+ client: {
+ is_crypto: (currency: string) => currency === 'BTC',
+ accounts: {
+ crypto_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ },
+ non_crypto_acc: {
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe('');
+ });
+
+ test("should return the first account's currency when user only has crypto account", async () => {
+ const mock = mockStore({
+ client: {
+ is_crypto: (currency: string) => ['BTC', 'ETH'].includes(currency) || true,
+ accounts: {
+ eth_acc: {
+ currency: 'ETH',
+ is_virtual: 0,
+ },
+ btc_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ const first_account_currency = mock.client.accounts[Object.keys(mock.client.accounts)[0]].currency;
+ expect(result.current).toBe(first_account_currency);
+ });
+
+ test('should return the current selected currency when user only has crypto account and is_crypto() is false', async () => {
+ const mock = mockStore({
+ client: {
+ is_crypto: (currency: string) => ['BTC', 'ETH'].includes(currency) || false,
+ currency: 'USDC',
+ accounts: {
+ eth_acc: {
+ currency: 'ETH',
+ is_virtual: 0,
+ },
+ usdc_acc: {
+ currency: 'USDC',
+ is_virtual: 0,
+ },
+ btc_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe(mock.client.currency);
+ });
+
+ test('should return undefined when user only has crypto account with no currency set', async () => {
+ const mock = mockStore({
+ client: {
+ is_crypto: (currency: string) => ['BTC', 'ETH'].includes(currency) || true,
+ accounts: {
+ crypto_acc: {
+ is_virtual: 0,
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe(undefined);
+ });
+
+ test("should return MF account's currency when user switch to eu account type", async () => {
+ const mock = mockStore({
+ traders_hub: {
+ is_eu_user: true,
+ },
+ client: {
+ current_fiat_currency: 'EUR',
+ is_crypto: (currency: string) => ['BTC', 'ETH'].includes(currency) || true,
+ accounts: {
+ btc_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ landing_company_shortcode: 'svg',
+ },
+ eth_acc: {
+ currency: 'ETH',
+ is_virtual: 0,
+ landing_company_shortcode: 'svg',
+ },
+ MF1234: {
+ currency: 'EUR',
+ is_virtual: 0,
+ landing_company_shortcode: 'maltainvest',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe(mock.client.accounts.MF1234.currency);
+ });
+
+ test("should return default currency when user switch to eu account type but MF account's currency is not set", async () => {
+ const mock = mockStore({
+ traders_hub: {
+ is_eu_user: true,
+ },
+ client: {
+ default_currency: 'GBP',
+ is_crypto: (currency: string) => ['BTC', 'ETH'].includes(currency) || true,
+ accounts: {
+ btc_acc: {
+ currency: 'BTC',
+ is_virtual: 0,
+ landing_company_shortcode: 'svg',
+ },
+ eth_acc: {
+ currency: 'ETH',
+ is_virtual: 0,
+ landing_company_shortcode: 'svg',
+ },
+ MF1234: {
+ currency: 'EUR',
+ is_virtual: 0,
+ landing_company_shortcode: 'maltainvest',
+ },
+ },
+ },
+ });
+
+ const wrapper = ({ children }: { children: JSX.Element }) => (
+
{children}
+ );
+ const { result } = renderHook(() => useRealTotalAssetCurrency(), { wrapper });
+
+ expect(result.current).toBe(mock.client.default_currency);
+ });
+});
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index 245cd16c5a0b..f4555a7a9cfe 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -1,9 +1,9 @@
export { default as useAccountTransferVisible } from './useAccountTransferVisible';
+export { default as useCFDAccounts } from './useCFDAccounts';
export { default as useCFDAllAccounts } from './useCFDAllAccounts';
export { default as useCFDDemoAccounts } from './useCFDDemoAccounts';
export { default as useCFDRealAccounts } from './useCFDRealAccounts';
export { default as useCashierLocked } from './useCashierLocked';
-export { default as useCfdAccounts } from './useCfdAccounts';
export { default as useCountdown } from './useCountdown';
export { default as useDepositLocked } from './useDepositLocked';
export { default as useExchangeRate } from './useExchangeRate';
diff --git a/packages/hooks/src/useCfdAccounts.ts b/packages/hooks/src/useCFDAccounts.ts
similarity index 74%
rename from packages/hooks/src/useCfdAccounts.ts
rename to packages/hooks/src/useCFDAccounts.ts
index 8641de8ff8dd..5da45e4d102d 100644
--- a/packages/hooks/src/useCfdAccounts.ts
+++ b/packages/hooks/src/useCFDAccounts.ts
@@ -1,4 +1,4 @@
-import useCFDAccounts from './useCFDAllAccounts';
+import useCFDAllAccounts from './useCFDAllAccounts';
import useCFDDemoAccounts from './useCFDDemoAccounts';
import useCFDRealAccounts from './useCFDRealAccounts';
@@ -7,8 +7,8 @@ import useCFDRealAccounts from './useCFDRealAccounts';
* and it returns different cfd account types which are demo, real, and all
*/
-const useGetCfdAccounts = () => {
- const all_cfd_accounts = useCFDAccounts();
+const useCFDAccounts = () => {
+ const all_cfd_accounts = useCFDAllAccounts();
const cfd_demo_accounts = useCFDDemoAccounts();
const cfd_real_accounts = useCFDRealAccounts();
@@ -19,4 +19,4 @@ const useGetCfdAccounts = () => {
};
};
-export default useGetCfdAccounts;
+export default useCFDAccounts;
diff --git a/packages/hooks/src/useTotalAccountBalance.ts b/packages/hooks/src/useTotalAccountBalance.ts
index 741f4e83c600..5ab91f88ecaf 100644
--- a/packages/hooks/src/useTotalAccountBalance.ts
+++ b/packages/hooks/src/useTotalAccountBalance.ts
@@ -15,8 +15,8 @@ const useTotalAccountBalance = (accounts: { balance?: number; currency?: string
if (!accounts.length) return { balance: 0, currency: total_assets_real_currency };
const balance = accounts.reduce((total, account) => {
- const base_rate = getRate(total_assets_real_currency);
- const rate = getRate(account.currency || total_assets_real_currency);
+ const base_rate = getRate(total_assets_real_currency || '');
+ const rate = getRate(account.currency || total_assets_real_currency || '');
const exchange_rate = base_rate / rate;
return total + (account.balance || 0) * exchange_rate;
diff --git a/packages/p2p/package.json b/packages/p2p/package.json
index a85e3b78b412..a0ff0cb0ff75 100644
--- a/packages/p2p/package.json
+++ b/packages/p2p/package.json
@@ -74,9 +74,7 @@
"@types/react-dom": "^18.0.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
- "copy-webpack-plugin": "^9.0.1",
"cross-env": "^5.2.0",
- "css-hot-loader": "^1.4.4",
"css-loader": "^5.0.1",
"css-minimizer-webpack-plugin": "^3.0.1",
"eslint-config-airbnb-base": "^14.2.1",
@@ -91,13 +89,13 @@
"node-sass": "^7.0.1",
"postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.4.3",
- "postcss-scss": "^4.0.3",
+ "postcss-scss": "^4.0.6",
"sass-loader": "^12.6.0",
"sass-resources-loader": "^2.1.1",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^5.1.1",
"typescript": "^4.6.3",
- "webpack": "^5.46.0",
+ "webpack": "^5.81.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2"
}
diff --git a/packages/p2p/src/components/modal-manager/modals/email-link-verified-modal/email-link-verified-modal.jsx b/packages/p2p/src/components/modal-manager/modals/email-link-verified-modal/email-link-verified-modal.jsx
index dc42a2076d57..f55530f72a20 100644
--- a/packages/p2p/src/components/modal-manager/modals/email-link-verified-modal/email-link-verified-modal.jsx
+++ b/packages/p2p/src/components/modal-manager/modals/email-link-verified-modal/email-link-verified-modal.jsx
@@ -33,7 +33,7 @@ const EmailLinkVerifiedModal = () => {
large
primary
onClick={() => {
- hideModal();
+ hideModal({ should_hide_all_modals: true });
order_store.confirmOrder(is_buy_order_for_user);
}}
>
diff --git a/packages/reports/package.json b/packages/reports/package.json
index ebd6169c1ca0..9e2167b2e076 100644
--- a/packages/reports/package.json
+++ b/packages/reports/package.json
@@ -53,14 +53,14 @@
"html-webpack-plugin": "^5.0.0-beta.5",
"html-webpack-tags-plugin": "^2.0.17",
"ignore-styles": "^5.0.1",
- "jsdom": "^16.2.1",
+ "jsdom": "^21.1.1",
"jsdom-global": "^2.1.1",
"mini-css-extract-plugin": "^1.3.4",
"mock-local-storage": "^1.1.8",
"node-sass": "^7.0.1",
"postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.4.3",
- "postcss-scss": "^4.0.3",
+ "postcss-scss": "^4.0.6",
"react-svg-loader": "^3.0.3",
"resolve-url-loader": "^3.1.2",
"sass-loader": "^12.6.0",
@@ -68,7 +68,7 @@
"stylelint-formatter-pretty": "^2.1.1",
"svgo": "^2.8.0",
"terser-webpack-plugin": "^5.1.1",
- "webpack": "^5.46.0",
+ "webpack": "^5.81.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2",
"webpack-manifest-plugin": "^4.0.2",
@@ -80,7 +80,6 @@
"@deriv/deriv-api": "^1.0.11",
"@deriv/shared": "^1.0.0",
"@deriv/translations": "^1.0.0",
- "@types/classnames": "^2.2.11",
"@types/react": "^18.0.7",
"@types/react-dom": "^18.0.0",
"@types/react-loadable": "^5.5.6",
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 6e5da757327c..3fdd2dd09abb 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -35,7 +35,7 @@
"@types/jsdom": "^20.0.0",
"@types/react": "^18.0.7",
"@types/react-dom": "^18.0.0",
- "jsdom": "^16.2.1",
+ "jsdom": "^21.1.1",
"moment": "^2.29.2",
"typescript": "^4.6.3"
},
diff --git a/packages/shared/src/services/index.ts b/packages/shared/src/services/index.ts
index 33c0e02743b6..20f6ca4ceb72 100644
--- a/packages/shared/src/services/index.ts
+++ b/packages/shared/src/services/index.ts
@@ -1,2 +1 @@
export * from './ws-methods';
-export { trackJSNetworkMonitor } from './trackjs';
diff --git a/packages/shared/src/services/trackjs.ts b/packages/shared/src/services/trackjs.ts
deleted file mode 100644
index 6a67bed13523..000000000000
--- a/packages/shared/src/services/trackjs.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- Responses that should be ignored upon receiving errors.
- We still log them, but having an error inside of these
- will not break the queue and send them to trackjs.
- This will look for predefined `ignored_responses_in_trackjs` from GTM, if
- there is none, then it just does not filter out any response.
-*/
-declare global {
- interface Window {
- ignored_responses_in_trackjs: [];
- TrackJS: { console: { log: (arg0: unknown[]) => void }; track: (arg0: object) => void };
- }
-}
-
-const getIgnoredResponseKeywords = () => {
- return window.ignored_responses_in_trackjs || [];
-};
-
-class ResponseQueue {
- list: unknown[];
- size: number;
- constructor() {
- this.list = [];
- this.size = 3;
- }
-
- add(response: unknown) {
- this.list.unshift(response);
- }
-
- remove() {
- this.list.pop();
- }
-
- push(response: unknown) {
- if (this.list.length >= this.size) {
- this.remove();
- }
- this.add(response);
- }
-
- fresh() {
- this.list = [];
- }
-}
-
-const queue = new ResponseQueue();
-
-/**
- * Listen on method calls and inspect the response to see if error is thrown.
- * Handling the response status is NOT this function's responsibility
- */
-export const ApiCallProxyHandler = {
- get(target: object, prop_key: PropertyKey, receiver: string) {
- try {
- const target_value = Reflect.get(target, prop_key, receiver);
- if (typeof target_value === 'function') {
- return (...args: string[]) => {
- const result = target_value.apply(this, args);
- if (result instanceof Promise) {
- return new Promise(resolve => {
- result
- .then(response => {
- if (response.error) {
- queue.push(response);
- if (window.TrackJS) window.TrackJS.console.log(queue.list);
- queue.fresh();
- if (
- window.TrackJS &&
- !getIgnoredResponseKeywords().some(
- (item: string) => item === response.error.code
- )
- ) {
- window.TrackJS.track(response.error.code);
- }
- }
- queue.push(response);
- resolve(response);
- })
- .catch(error => {
- if (window.TrackJS) {
- window.TrackJS.console.log(queue.list);
- window.TrackJS.track(error.getMessage());
- }
- });
- });
- }
- return result;
- };
- }
- return target_value;
- } catch (error: unknown) {
- if (error instanceof Error) {
- throw new Error(error.message);
- }
- }
- return null;
- },
-};
-
-export const trackJSNetworkMonitor = (obj: Record
) => new Proxy(obj, ApiCallProxyHandler);
diff --git a/packages/shared/src/utils/url/__tests__/url.js b/packages/shared/src/utils/url/__tests__/url.js
index edac76d1dd53..8cd2b05e22bb 100644
--- a/packages/shared/src/utils/url/__tests__/url.js
+++ b/packages/shared/src/utils/url/__tests__/url.js
@@ -1,6 +1,5 @@
import { deriv_urls } from '../constants';
import { reset, urlFor, websiteUrl, getPath, getContractPath } from '../url';
-import { expect } from '../../../test_utils/test_common';
// Testable URLs
const urls = [deriv_urls.DERIV_APP_PRODUCTION];
diff --git a/packages/shared/src/utils/validation/declarative-validation-rules.ts b/packages/shared/src/utils/validation/declarative-validation-rules.ts
index af1a9bd1c3a3..01dc95d82938 100644
--- a/packages/shared/src/utils/validation/declarative-validation-rules.ts
+++ b/packages/shared/src/utils/validation/declarative-validation-rules.ts
@@ -48,7 +48,7 @@ export const validLetterSymbol = (value: string) => /^[A-Za-z]+([a-zA-Z.' -])*[a
export const validName = (value: string) => /^(?!.*\s{2,})[\p{L}\s'.-]{2,50}$/u.test(value);
export const validLength = (value = '', options: TOptions) =>
(options.min ? value.length >= options.min : true) && (options.max ? value.length <= options.max : true);
-export const validPassword = (value: string) => /(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]+/.test(value);
+export const validPassword = (value: string) => /^(?=.*[a-z])(?=.*\d)(?=.*[A-Z])[!-~]{8,25}$/.test(value);
export const validEmail = (value: string) => /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$/.test(value);
const validBarrier = (value: string) => /^[+-]?\d+\.?\d*$/.test(value);
const validGeneral = (value: string) => !/[`~!@#$%^&*)(_=+[}{\]\\/";:?><|]+/.test(value);
diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts
index 1e66b0ccb439..e25010324f36 100644
--- a/packages/stores/src/mockStore.ts
+++ b/packages/stores/src/mockStore.ts
@@ -1,20 +1,11 @@
import merge from 'lodash.merge';
import { TStores } from '../types';
-const mock = (): TStores => {
+const mock = (): TStores & { is_mock: boolean } => {
return {
+ is_mock: true,
client: {
- accounts: {
- loginid: {
- account_type: 'trading',
- created_at: 1674633682,
- currency: 'USD',
- is_disabled: 0,
- is_virtual: 0,
- excluded_until: 0,
- landing_company_name: 'svg',
- },
- },
+ accounts: {},
active_account_landing_company: '',
account_limits: {
daily_transfers: {
@@ -198,6 +189,7 @@ const mock = (): TStores => {
setTwoFAStatus: jest.fn(),
has_changed_two_fa: false,
setTwoFAChangedStatus: jest.fn(),
+ real_account_creation_unlock_date: 0,
},
common: {
error: {
@@ -222,6 +214,7 @@ const mock = (): TStores => {
current_language: 'EN',
is_network_online: false,
is_language_changing: false,
+ getExchangeRate: jest.fn(),
},
ui: {
app_contents_scroll_ref: {
@@ -253,6 +246,7 @@ const mock = (): TStores => {
is_ready_to_deposit_modal_visible: false,
is_need_real_account_for_cashier_modal_visible: false,
toggleNeedRealAccountForCashierModal: jest.fn(),
+ setShouldShowCooldownModal: jest.fn(),
},
traders_hub: {
closeModal: jest.fn(),
@@ -267,11 +261,28 @@ const mock = (): TStores => {
is_real: false,
selectRegion: jest.fn(),
is_low_risk_cr_eu_real: false,
+ platform_real_balance: {
+ currency: '',
+ balance: 0,
+ },
+ cfd_demo_balance: {
+ currency: '',
+ balance: 0,
+ },
+ platform_demo_balance: {
+ currency: '',
+ balance: 0,
+ },
+ cfd_real_balance: {
+ currency: '',
+ balance: 0,
+ },
financial_restricted_countries: false,
selected_account_type: 'real',
no_CR_account: false,
no_MF_account: false,
setTogglePlatformType: jest.fn(),
+ is_demo: false,
},
menu: {
attach: jest.fn(),
diff --git a/packages/stores/src/storeProvider.tsx b/packages/stores/src/storeProvider.tsx
index 3c0b18a1b872..e7708e3bb6b2 100644
--- a/packages/stores/src/storeProvider.tsx
+++ b/packages/stores/src/storeProvider.tsx
@@ -5,18 +5,21 @@ import { ExchangeRatesProvider } from './providers';
import type { TCoreStores, TStores } from '../types';
const StoreProvider = ({ children, store }: PropsWithChildren<{ store: TCoreStores }>) => {
- const memoizedValue: TStores = useMemo(
- () => ({
+ const memoizedValue: TStores = useMemo(() => {
+ // If the store is mocked for testing purposes, then return the mocked value.
+ if ('is_mock' in store && store.is_mock) return store as unknown as TStores;
+
+ // Otherwise, instantiate store and return it.
+ return {
...store,
exchange_rates: new ExchangeRatesStore(),
- }),
- [store]
- );
+ };
+ }, [store]);
useEffect(() => {
return () => {
Object.values(memoizedValue).forEach(value => {
- if ('unmount' in value) value.unmount();
+ if (typeof value === 'object' && 'unmount' in value) value.unmount();
});
};
}, [memoizedValue]);
diff --git a/packages/stores/types.ts b/packages/stores/types.ts
index e114d70cce3c..e4a76c2a7940 100644
--- a/packages/stores/types.ts
+++ b/packages/stores/types.ts
@@ -90,7 +90,7 @@ type TNotification =
type TAccountStatus = Omit & Partial>;
type TClientStore = {
- accounts: { [k: string]: TAccount };
+ accounts: { [k: string]: TActiveAccount };
active_accounts: TActiveAccount[];
active_account_landing_company: string;
account_limits: {
@@ -191,6 +191,7 @@ type TClientStore = {
setTwoFAStatus: (status: boolean) => void;
has_changed_two_fa: boolean;
setTwoFAChangedStatus: (status: boolean) => void;
+ real_account_creation_unlock_date: number;
};
type TCommonStoreError = {
@@ -218,6 +219,7 @@ type TCommonStore = {
changeSelectedLanguage: (key: string) => void;
current_language: string;
is_language_changing: boolean;
+ getExchangeRate: (from_currency: string, to_currency: string) => Promise;
};
type TUiStore = {
@@ -248,6 +250,7 @@ type TUiStore = {
is_ready_to_deposit_modal_visible: boolean;
is_need_real_account_for_cashier_modal_visible: boolean;
toggleNeedRealAccountForCashierModal: () => void;
+ setShouldShowCooldownModal: (value: boolean) => void;
};
type TMenuStore = {
@@ -287,10 +290,27 @@ type TTradersHubStore = {
setTogglePlatformType: (platform_type: string) => void;
is_real: boolean;
selectRegion: (region: string) => void;
+ platform_real_balance: {
+ currency: string;
+ balance: number;
+ };
+ cfd_demo_balance: {
+ currency: string;
+ balance: number;
+ };
+ platform_demo_balance: {
+ currency: string;
+ balance: number;
+ };
+ cfd_real_balance: {
+ currency: string;
+ balance: number;
+ };
financial_restricted_countries: boolean;
selected_account_type: string;
no_CR_account: boolean;
no_MF_account: boolean;
+ is_demo: boolean;
};
/**
diff --git a/packages/trader/package.json b/packages/trader/package.json
index 612df0c6b790..8b267f08a53a 100644
--- a/packages/trader/package.json
+++ b/packages/trader/package.json
@@ -56,14 +56,14 @@
"html-webpack-plugin": "^5.0.0-beta.5",
"html-webpack-tags-plugin": "^2.0.17",
"ignore-styles": "^5.0.1",
- "jsdom": "^16.2.1",
+ "jsdom": "^21.1.1",
"jsdom-global": "^2.1.1",
"mini-css-extract-plugin": "^1.3.4",
"mock-local-storage": "^1.1.8",
"node-sass": "^7.0.1",
"postcss-loader": "^6.2.1",
"postcss-preset-env": "^7.4.3",
- "postcss-scss": "^4.0.3",
+ "postcss-scss": "^4.0.6",
"react-svg-loader": "^3.0.3",
"resolve-url-loader": "^3.1.2",
"sass-loader": "^12.6.0",
@@ -72,7 +72,7 @@
"svgo": "^2.8.0",
"terser-webpack-plugin": "^5.1.1",
"typescript": "^4.6.3",
- "webpack": "^5.46.0",
+ "webpack": "^5.81.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.7.2",
"webpack-manifest-plugin": "^4.0.2",
@@ -82,12 +82,11 @@
"@deriv/api-types": "^1.0.94",
"@deriv/components": "^1.0.0",
"@deriv/deriv-api": "^1.0.11",
- "@deriv/deriv-charts": "1.1.9",
+ "@deriv/deriv-charts": "1.2.1",
"@deriv/reports": "^1.0.0",
"@deriv/shared": "^1.0.0",
"@deriv/stores": "^1.0.0",
"@deriv/translations": "^1.0.0",
- "@types/classnames": "^2.2.11",
"@types/react-loadable": "^5.5.6",
"classnames": "^2.2.6",
"extend": "^3.0.2",
diff --git a/packages/trader/src/Modules/SmartChart/Components/toolbar-widgets.jsx b/packages/trader/src/Modules/SmartChart/Components/toolbar-widgets.jsx
index db799d507ccb..f98fa5684f6d 100644
--- a/packages/trader/src/Modules/SmartChart/Components/toolbar-widgets.jsx
+++ b/packages/trader/src/Modules/SmartChart/Components/toolbar-widgets.jsx
@@ -2,13 +2,10 @@ import PropTypes from 'prop-types';
import React from 'react';
import { isDesktop, isMobile } from '@deriv/shared';
import { ChartMode, DrawTools, Share, StudyLegend, Views, ToolbarWidget } from 'Modules/SmartChart';
-import { connect } from 'Stores/connect';
-const ToolbarWidgets = ({ is_accumulator, position, updateChartType, updateGranularity }) => (
+const ToolbarWidgets = ({ position, updateChartType, updateGranularity }) => (
- {!is_accumulator && (
-
- )}
+
{isDesktop() && }
{isDesktop() && }
{isDesktop() && }
@@ -20,11 +17,6 @@ ToolbarWidgets.propTypes = {
position: PropTypes.string,
updateChartType: PropTypes.func,
updateGranularity: PropTypes.func,
- is_accumulator: PropTypes.func,
};
-export default React.memo(
- connect(({ modules }) => ({
- is_accumulator: modules.trade.is_accumulator,
- }))(ToolbarWidgets)
-);
+export default React.memo(ToolbarWidgets);
diff --git a/packages/trader/src/Modules/Trading/Containers/trade.jsx b/packages/trader/src/Modules/Trading/Containers/trade.jsx
index ee477cd765fe..61950b4261a7 100644
--- a/packages/trader/src/Modules/Trading/Containers/trade.jsx
+++ b/packages/trader/src/Modules/Trading/Containers/trade.jsx
@@ -4,7 +4,6 @@ import classNames from 'classnames';
import { DesktopWrapper, Div100vhContainer, MobileWrapper, SwipeableWrapper } from '@deriv/components';
import { isDesktop, isMobile } from '@deriv/shared';
import ChartLoader from 'App/Components/Elements/chart-loader.jsx';
-import { isDigitTradeType } from 'Modules/Trading/Helpers/digits';
import { connect } from 'Stores/connect';
import PositionsDrawer from 'App/Components/Elements/PositionsDrawer';
import MarketIsClosedOverlay from 'App/Components/Elements/market-is-closed-overlay.jsx';
@@ -31,7 +30,6 @@ const BottomWidgetsMobile = ({ tick, digits, setTick, setDigits }) => {
};
const Trade = ({
- contract_type,
form_components,
getFirstOpenMarket,
should_show_active_symbols_loading,
@@ -182,13 +180,7 @@ const Trade = ({