diff --git a/packages/appstore/package.json b/packages/appstore/package.json index 04fe6dee5fd3..77cd8a233135 100644 --- a/packages/appstore/package.json +++ b/packages/appstore/package.json @@ -29,6 +29,7 @@ "@deriv/cashier": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/cfd": "^1.0.0", + "@deriv/hooks": "^1.0.0", "@deriv/shared": "^1.0.0", "@deriv/stores": "^1.0.0", "@deriv/trader": "^3.8.0", diff --git a/packages/appstore/src/assets/svgs/trading-platform/ic-appstore-swap-free.svg b/packages/appstore/src/assets/svgs/trading-platform/ic-appstore-swap-free.svg new file mode 100644 index 000000000000..7b464b0951e6 --- /dev/null +++ b/packages/appstore/src/assets/svgs/trading-platform/ic-appstore-swap-free.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/appstore/src/assets/svgs/trading-platform/index.tsx b/packages/appstore/src/assets/svgs/trading-platform/index.tsx index 10ab73c97b71..560dfa830f9d 100644 --- a/packages/appstore/src/assets/svgs/trading-platform/index.tsx +++ b/packages/appstore/src/assets/svgs/trading-platform/index.tsx @@ -16,6 +16,7 @@ import Financial from 'Assets/svgs/trading-platform/branding/ic-branding-mt5-fin import Options from 'Assets/svgs/trading-platform/ic-appstore-options.svg'; import SmartTrader from 'Assets/svgs/trading-platform/branding/ic-branding-smarttrader-dashboard.svg'; import SmartTraderBlue from 'Assets/svgs/trading-platform/ic-appstore-smarttrader-blue.svg'; +import SwapFree from 'Assets/svgs/trading-platform/ic-appstore-swap-free.svg'; import { IconProps } from '../icon-types'; export const PlatformIcons = { @@ -33,6 +34,7 @@ export const PlatformIcons = { DropDown, DTrader, Financial, + SwapFree, Options, SmartTrader, SmartTraderBlue, diff --git a/packages/appstore/src/components/cfds-listing/cfds-listing.scss b/packages/appstore/src/components/cfds-listing/cfds-listing.scss index 44d35d7421ed..654d41675cab 100644 --- a/packages/appstore/src/components/cfds-listing/cfds-listing.scss +++ b/packages/appstore/src/components/cfds-listing/cfds-listing.scss @@ -1,5 +1,6 @@ .cfd-jurisdiction-card--synthetic, -.cfd-jurisdiction-card--financial { +.cfd-jurisdiction-card--financial, +.cfd-jurisdiction-card--all { border: solid 1px var(--border-normal); border-radius: 0.8rem; display: flex; diff --git a/packages/appstore/src/components/modals/account-type-modal/account-type-modal.tsx b/packages/appstore/src/components/modals/account-type-modal/account-type-modal.tsx index 54afec4e0cef..cf3a99bfc425 100644 --- a/packages/appstore/src/components/modals/account-type-modal/account-type-modal.tsx +++ b/packages/appstore/src/components/modals/account-type-modal/account-type-modal.tsx @@ -5,21 +5,11 @@ import { observer } from 'mobx-react-lite'; import classNames from 'classnames'; import { useStores } from 'Stores/index'; import TradigPlatformIconProps from 'Assets/svgs/trading-platform'; -import { TModalContent, TAccountType, TAccountCard, TTradingPlatformAvailableAccount } from './types'; +import { TModalContent, TAccountCard, TTradingPlatformAvailableAccount } from './types'; import { TIconTypes } from 'Types'; import { CFD_PLATFORMS } from '@deriv/shared'; - -const getDerivedAccount = (): TAccountType => ({ - title_and_type: localize('Derived'), - icon: 'Derived', - description: localize('Trade CFDs on MT5 with Derived indices that simulate real-world market movements.'), -}); - -const getFinancialAccount = (): TAccountType => ({ - title_and_type: localize('Financial'), - icon: 'Financial', - description: localize('Trade CFDs on MT5 with forex, stock indices, commodities, and cryptocurrencies.'), -}); +import { getDerivedAccount, getFinancialAccount, getSwapFreeAccount } from '../../../helpers/account-helper'; +import { useHasSwapFreeAccount } from '@deriv/hooks'; const AccountCard = ({ selectAccountTypeCard, account_type_card, title_and_type, description, icon }: TAccountCard) => { const cardSelection = (cardType: string) => { @@ -54,6 +44,7 @@ const ModalContent = ({ selectAccountTypeCard, is_financial_available, is_synthetic_available, + is_swapfree_available, }: TModalContent) => { return (
@@ -75,6 +66,15 @@ const ModalContent = ({ icon={getFinancialAccount().icon} /> )} + {is_swapfree_available && ( + selectAccountTypeCard(`${getSwapFreeAccount().title_and_type}`)} + description={getSwapFreeAccount().description} + title_and_type={getSwapFreeAccount().title_and_type} + icon={getSwapFreeAccount().icon} + /> + )}
); }; @@ -92,6 +92,13 @@ const MT5AccountTypeModal = () => { const { trading_platform_available_accounts } = client; const { enableApp, disableApp } = ui; const { setAppstorePlatform } = common; + + React.useEffect(() => { + if (!is_account_type_modal_visible) { + selectAccountTypeCard(''); + } + }, [is_account_type_modal_visible, selectAccountTypeCard]); + const is_financial_available = trading_platform_available_accounts.some( (available_account: TTradingPlatformAvailableAccount) => available_account.market_type === 'financial' ); @@ -99,12 +106,22 @@ const MT5AccountTypeModal = () => { const is_synthetic_available = trading_platform_available_accounts.some( (available_account: TTradingPlatformAvailableAccount) => available_account.market_type === 'gaming' ); + const is_swapfree_available = useHasSwapFreeAccount(); - const set_account_type = () => - account_type_card === localize('Derived') - ? setAccountType({ category: 'real', type: 'synthetic' }) - : setAccountType({ category: 'real', type: 'financial' }); - + const set_account_type = () => { + switch (account_type_card) { + case 'Derived': + setAccountType({ category: 'real', type: 'synthetic' }); + break; + case 'Swap-Free': + setAccountType({ category: 'real', type: 'all' }); + break; + case 'Financial': + default: + setAccountType({ category: 'real', type: 'financial' }); + break; + } + }; return (
}> @@ -126,6 +143,7 @@ const MT5AccountTypeModal = () => { selectAccountTypeCard={selectAccountTypeCard} is_financial_available={is_financial_available} is_synthetic_available={is_synthetic_available} + is_swapfree_available={is_swapfree_available} />
)} diff --git a/packages/cfd/src/Components/props.types.ts b/packages/cfd/src/Components/props.types.ts index 3dae634df84c..03d0147dbb6f 100644 --- a/packages/cfd/src/Components/props.types.ts +++ b/packages/cfd/src/Components/props.types.ts @@ -52,7 +52,7 @@ export type TCFDAccountCardActionProps = { }; export type TTradingPlatformAvailableAccount = { - market_type: 'financial' | 'gaming'; + market_type: 'financial' | 'gaming' | 'all'; name: string; requirements: { after_first_deposit: { @@ -79,8 +79,10 @@ export type TCFDAccountCard = { real: string; }; is_hovered?: boolean; - isEligibleForMoreDemoMt5Svg: (market_type: 'synthetic' | 'financial') => boolean; - isEligibleForMoreRealMt5: (market_type: 'synthetic' | 'financial') => boolean; + isEligibleForMoreDemoMt5Svg: ( + market_type: TTradingPlatformAvailableAccount['market_type'] | 'synthetic' + ) => boolean; + isEligibleForMoreRealMt5: (market_type: TTradingPlatformAvailableAccount['market_type'] | 'synthetic') => boolean; existing_accounts_data?: TExistingData; trading_platform_available_accounts: TTradingPlatformAvailableAccount[]; has_banner?: boolean; @@ -158,9 +160,9 @@ export type TTradingPlatformAccounts = { /** * Market type */ - market_type?: 'financial' | 'synthetic'; + market_type?: 'financial' | 'synthetic' | 'all'; /** * Name of trading platform. */ - platform?: 'dxtrade'; + platform?: 'dxtrade' | string; }; diff --git a/packages/cfd/src/Constants/cfd-specifications.ts b/packages/cfd/src/Constants/cfd-specifications.ts index edc80044e78a..5b38704f14dd 100644 --- a/packages/cfd/src/Constants/cfd-specifications.ts +++ b/packages/cfd/src/Constants/cfd-specifications.ts @@ -38,6 +38,12 @@ const mt5 = { 'stop-out-level': { key: () => localize('Stop out level'), value: () => localize('50%') }, 'number-of-assets': { key: () => localize('Number of assets'), value: () => localize('150+') }, }, + real_all_specs: { + leverage: { key: () => localize('Leverage'), value: () => localize('Up to 1:1000') }, + 'margin-call': { key: () => localize('Margin call'), value: () => localize('100%') }, + 'stop-out-level': { key: () => localize('Stop out level'), value: () => localize('50%') }, + 'number-of-assets': { key: () => localize('Number of assets'), value: () => localize('40+') }, + }, eu_real_financial_specs: { leverage: { key: () => localize('Leverage'), value: () => localize('Up to 1:30') }, 'margin-call': { key: () => localize('Margin call'), value: () => localize('100%') }, diff --git a/packages/cfd/src/Constants/jurisdiction-contents.ts b/packages/cfd/src/Constants/jurisdiction-contents.ts index e47a5e95d456..b83e28e125ea 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents.ts @@ -5,6 +5,7 @@ type TJurisdictionCardItems = { over_header?: string; synthetic_contents: Array; financial_contents: Array; + swapfree_contents?: Array; is_over_header_available: boolean; }; type TJurisdictionContent = { @@ -32,6 +33,13 @@ export const getJurisdictionContents = (): TJurisdictionContent => ({ )}`, `${localize('Leverage up to 1:1000')}`, ], + swapfree_contents: [ + `${localize('Selecting this will onboard you through Deriv (SVG) LLC (company no. 273 LLC 2020)')}`, + `${localize( + '40+ assets: forex, synthetics, stocks, stock indices, cryptocurrencies, and ETFs swap-free.' + )}`, + `${localize('Leverage up to 1:1000')}`, + ], }, vanuatu: { is_over_header_available: false, diff --git a/packages/cfd/src/Containers/cfd-dashboard.tsx b/packages/cfd/src/Containers/cfd-dashboard.tsx index f61777085162..bc2e24421588 100644 --- a/packages/cfd/src/Containers/cfd-dashboard.tsx +++ b/packages/cfd/src/Containers/cfd-dashboard.tsx @@ -30,11 +30,12 @@ import { general_messages } from '../Constants/cfd-shared-strings'; import SwitchToRealAccountModal from './switch-to-real-account'; import 'Sass/cfd-dashboard.scss'; import RootStore from '../Stores/index'; -import { DetailsOfEachMT5Loginid, LandingCompany, ResidenceList } from '@deriv/api-types'; +import { LandingCompany, ResidenceList, DetailsOfEachMT5Loginid } from '@deriv/api-types'; // TODO: Change these imports after real released import CFDDxtradeDemoAccountDisplay from '../Components/cfd-dxtrade-demo-account-display'; import CFDMT5DemoAccountDisplay from '../Components/cfd-mt5-demo-account-display'; import { CFDRealAccountDisplay } from '../Components/cfd-real-account-display'; +import { TTradingPlatformAccounts } from 'Components/props.types'; declare module 'react' { interface HTMLAttributes extends React.AriaAttributes, React.DOMAttributes { @@ -310,18 +311,20 @@ const CFDDashboard = (props: TCFDDashboardProps) => { }; const openAccountTransfer = ( - data: DetailsOfEachMT5Loginid & { account_id?: string; platform?: string }, - meta: { category: string; type?: string } + data: DetailsOfEachMT5Loginid | TTradingPlatformAccounts, + meta: TOpenAccountTransferMeta ) => { if (meta.category === 'real') { - if (data.platform === CFD_PLATFORMS.DXTRADE) - sessionStorage.setItem('cfd_transfer_to_login_id', data.account_id as string); - else sessionStorage.setItem('cfd_transfer_to_login_id', data.login as string); - - props.disableCFDPasswordModal(); - props.history.push(routes.cashier_acc_transfer); + if (data) { + if ('platform' in data && data.platform === CFD_PLATFORMS.DXTRADE) + sessionStorage.setItem('cfd_transfer_to_login_id', data.account_id || ''); + else sessionStorage.setItem('cfd_transfer_to_login_id', data.login || ''); + + props.disableCFDPasswordModal(); + props.history.push(routes.cashier_acc_transfer); + } } else { - props.setCurrentAccount(data, meta); + if ('sub_account_type' in data) props.setCurrentAccount(data, meta); props.openTopUpModal(); } }; @@ -402,6 +405,19 @@ const CFDDashboard = (props: TCFDDashboardProps) => { ); }; + const isSwapFreeCardVisible = () => { + const { platform, landing_companies, is_logged_in } = props; + + return ( + !is_logged_in || + isLandingCompanyEnabled({ + landing_companies, + platform, + type: 'all', + }) + ); + }; + const { account_status, beginRealSignupForMt5, @@ -646,6 +662,7 @@ const CFDDashboard = (props: TCFDDashboardProps) => { is_loading={is_loading} isSyntheticCardVisible={isSyntheticCardVisible} isFinancialCardVisible={isFinancialCardVisible} + isSwapFreeCardVisible={isSwapFreeCardVisible} current_list={current_list} onSelectAccount={createCFDAccount} landing_companies={landing_companies} diff --git a/packages/cfd/src/Containers/cfd-password-modal.tsx b/packages/cfd/src/Containers/cfd-password-modal.tsx index f019569dd3e4..6cf7acd3416e 100644 --- a/packages/cfd/src/Containers/cfd-password-modal.tsx +++ b/packages/cfd/src/Containers/cfd-password-modal.tsx @@ -4,7 +4,13 @@ import { RouteComponentProps, withRouter } from 'react-router'; import { SentEmailModal } from '@deriv/account'; import { DetailsOfEachMT5Loginid, GetAccountStatus, LandingCompany, Mt5NewAccount } from '@deriv/api-types'; import RootStore from '../Stores/index'; -import { getMtCompanies, getFormattedJurisdictionCode, TMtCompanies } from '../Stores/Modules/CFD/Helpers/cfd-config'; +import { + getDxCompanies, + getMtCompanies, + getFormattedJurisdictionCode, + TMtCompanies, + TDxCompanies, +} from '../Stores/Modules/CFD/Helpers/cfd-config'; import { FormSubmitButton, Icon, @@ -38,7 +44,7 @@ import TradingPlatformIcon from '../Assets/svgs/trading-platform'; export type TCFDPasswordFormValues = { password: string }; type TExtendedDetailsOfEachMT5Loginid = Omit & { - market_type?: 'synthetic' | 'financial' | 'gaming'; + market_type?: 'synthetic' | 'financial' | 'gaming' | 'all'; }; type TOnSubmitPassword = (values: TCFDPasswordFormValues, actions: FormikHelpers) => void; @@ -147,6 +153,23 @@ type TCFDPasswordModalProps = RouteComponentProps & { updateAccountStatus: () => void; }; +const getAccountTitle = ( + platform: string, + account_type: { + category?: string; + type?: string; + }, + account_title: string +) => { + if (platform === CFD_PLATFORMS.DXTRADE) { + return getDxCompanies()[account_type.category as keyof TDxCompanies][ + account_type.type as keyof TDxCompanies['demo' | 'real'] + ].short_title; + } + + return account_title; +}; + const PasswordModalHeader = ({ should_set_trading_password, is_password_reset_error, @@ -203,6 +226,8 @@ const IconType = React.memo(({ platform, type, show_eu_related_content }: TIconT switch (type) { case 'synthetic': return ; + case 'all': + return ; case 'financial': if (show_eu_related_content) { return ; @@ -215,6 +240,8 @@ const IconType = React.memo(({ platform, type, show_eu_related_content }: TIconT switch (type) { case 'synthetic': return ; + case 'all': + return ; case 'financial': if (show_eu_related_content) { return ; @@ -529,7 +556,9 @@ const CFDPasswordForm = ({ values={{ platform: getCFDPlatformLabel(platform), platform_name: platform === CFD_PLATFORMS.MT5 ? 'MT5' : 'Deriv X', - account: !show_eu_related_content ? account_title : '', + account: !show_eu_related_content + ? getAccountTitle(platform, account_type, account_title) + : '', jurisdiction_shortcode: showJuristiction(), }} /> @@ -540,7 +569,7 @@ const CFDPasswordForm = ({ values={{ platform: getCFDPlatformLabel(platform), platform_name: platform === CFD_PLATFORMS.MT5 ? 'MT5' : 'Deriv X', - account: account_title, + account: getAccountTitle(platform, account_type, account_title), }} /> )} @@ -800,6 +829,8 @@ const CFDPasswordModal = ({ getMtCompanies(show_eu_related_content)[category as keyof TMtCompanies][ type as keyof TMtCompanies['demo' | 'real'] ].short_title; + const deriv_x_type_label = + getDxCompanies()[category as keyof TDxCompanies][type as keyof TDxCompanies['demo' | 'real']].short_title; const jurisdiction_label = jurisdiction_selected_shortcode && getFormattedJurisdictionCode(jurisdiction_selected_shortcode); const mt5_platform_label = jurisdiction_selected_shortcode !== 'maltainvest' ? 'MT5' : ''; @@ -811,7 +842,7 @@ const CFDPasswordModal = ({ i18n_default_text='Congratulations, you have successfully created your {{category}} <0>{{platform}} <1>{{type}} {{jurisdiction_selected_shortcode}} account. ' values={{ // TODO: remove below condition once deriv x changes are completed - type: platform === 'dxtrade' && type_label === 'Derived' ? 'Synthetic' : type_label, + type: platform === CFD_PLATFORMS.DXTRADE ? deriv_x_type_label : type_label, platform: platform === CFD_PLATFORMS.MT5 ? mt5_platform_label : 'Deriv X', category: category_label, jurisdiction_selected_shortcode: @@ -834,9 +865,10 @@ const CFDPasswordModal = ({ return (
- {getHeadingTitle()} + {trade_modal_title} {(mt5_trade_account as TTradingPlatformAccounts)?.display_login && ( diff --git a/packages/cfd/src/Containers/dmt5-trade-modal.tsx b/packages/cfd/src/Containers/dmt5-trade-modal.tsx index 7f5c47e83920..6dc38fa4985d 100644 --- a/packages/cfd/src/Containers/dmt5-trade-modal.tsx +++ b/packages/cfd/src/Containers/dmt5-trade-modal.tsx @@ -17,7 +17,7 @@ import { getPlatformMt5DownloadLink, getMT5WebTerminalLink } from '../Helpers/co import TradingPlatformIcon from '../Assets/svgs/trading-platform'; type TMT5TradeModalProps = { - mt5_trade_account: Required; + mt5_trade_account: DetailsOfEachMT5Loginid; show_eu_related_content: boolean; onPasswordManager: ( arg1: string | undefined, @@ -119,6 +119,7 @@ const DMT5TradeModal = ({ const getAccountTitle = () => { if (show_eu_related_content) return 'CFDs'; else if (mt5_trade_account.market_type === 'synthetic') return 'Derived'; + else if (mt5_trade_account.market_type === 'all') return 'SwapFree'; return 'Financial'; }; return ( @@ -173,8 +174,8 @@ const DMT5TradeModal = ({ }); onPasswordManager( mt5_trade_account?.login, - getTitle(mt5_trade_account.market_type, show_eu_related_content), - mt5_trade_account.account_type, + getTitle(mt5_trade_account.market_type || '', show_eu_related_content), + mt5_trade_account.account_type || '', account_type, (mt5_trade_account as DetailsOfEachMT5Loginid)?.server ); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card-banner.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card-banner.tsx index 0ddd4b06fc90..d604d63fe7eb 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card-banner.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card-banner.tsx @@ -18,6 +18,7 @@ const VerificationStatusBanner = ({ should_restrict_vanuatu_account_creation, real_synthetic_accounts_existing_data, real_financial_accounts_existing_data, + real_swapfree_accounts_existing_data, }: TVerificationStatusBannerProps) => { const { poi_not_submitted_for_vanuatu_maltainvest, @@ -62,10 +63,22 @@ const VerificationStatusBanner = ({ } }; - const isAccountCreated = () => - account_type === 'synthetic' - ? real_synthetic_accounts_existing_data?.some(account => account.landing_company_short === type_of_card) - : real_financial_accounts_existing_data?.some(account => account.landing_company_short === type_of_card); + const isAccountCreated = () => { + switch (account_type) { + case 'synthetic': + return real_synthetic_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + case 'all': + return real_swapfree_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + default: + return real_financial_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + } + }; if (disabled && isAccountCreated()) { // account added @@ -195,4 +208,5 @@ export default connect(({ modules: { cfd }, client }: RootStore) => ({ should_restrict_vanuatu_account_creation: client.should_restrict_vanuatu_account_creation, real_financial_accounts_existing_data: cfd.real_financial_accounts_existing_data, real_synthetic_accounts_existing_data: cfd.real_synthetic_accounts_existing_data, + real_swapfree_accounts_existing_data: cfd.real_swapfree_accounts_existing_data, }))(JurisdictionCardBanner); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card.tsx index bee3284893ce..955c0aaa1936 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-card.tsx @@ -14,20 +14,27 @@ const JurisdictionCard = ({ financial_available_accounts, setJurisdictionSelectedShortcode, synthetic_available_accounts, + swapfree_available_accounts, type_of_card, }: TJurisdictionCardProps) => { const card_classname = `cfd-jurisdiction-card--${account_type}`; const number_of_synthetic_accounts_to_be_shown = synthetic_available_accounts?.length; const number_of_financial_accounts_to_be_shown = financial_available_accounts?.length; + const number_of_swapfree_accounts_to_be_shown = swapfree_available_accounts?.length; const is_synthetic = account_type === 'synthetic'; + const is_swapfree = account_type === 'all'; + const non_synthetic_accounts = is_swapfree + ? number_of_swapfree_accounts_to_be_shown + : number_of_financial_accounts_to_be_shown; const [number_of_cards] = React.useState( - is_synthetic ? number_of_synthetic_accounts_to_be_shown : number_of_financial_accounts_to_be_shown + is_synthetic ? number_of_synthetic_accounts_to_be_shown : non_synthetic_accounts ); const card_values = getJurisdictionContents()[type_of_card as TJurisdictionCardType]; - const card_data = is_synthetic ? card_values.synthetic_contents : card_values.financial_contents; + const non_synthetic_card_data = is_swapfree ? card_values.swapfree_contents : card_values.financial_contents; + const card_data = is_synthetic ? card_values.synthetic_contents : non_synthetic_card_data; const cardSelection = (cardType: string) => { setJurisdictionSelectedShortcode(jurisdiction_selected_shortcode === cardType ? '' : cardType); @@ -57,7 +64,7 @@ const JurisdictionCard = ({ - {card_data.map((item, index) => ( + {card_data?.map((item, index) => (
diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx index 800cfc63a5e9..d5b65a585d25 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-content.tsx @@ -9,24 +9,42 @@ const JurisdictionModalContent = ({ setJurisdictionSelectedShortcode, synthetic_available_accounts, financial_available_accounts, + swapfree_available_accounts, context, real_synthetic_accounts_existing_data, real_financial_accounts_existing_data, + real_swapfree_accounts_existing_data, }: TJurisdictionModalContentProps) => { const card_classname = `cfd-jurisdiction-card--${account_type}`; - const cardsToBeShown = (type_of_card: string) => - account_type === 'synthetic' - ? synthetic_available_accounts?.some(account => account.shortcode === type_of_card) - : financial_available_accounts?.some(account => account.shortcode === type_of_card); - + const cardsToBeShown = (type_of_card: string) => { + switch (account_type) { + case 'synthetic': + return synthetic_available_accounts?.some(account => account.shortcode === type_of_card); + case 'all': + return swapfree_available_accounts?.some(account => account.shortcode === type_of_card); + default: + return financial_available_accounts?.some(account => account.shortcode === type_of_card); + } + }; const disableCard = (type_of_card: string) => { if (is_virtual && type_of_card !== 'svg') { return true; } - return account_type === 'synthetic' - ? real_synthetic_accounts_existing_data?.some(account => account.landing_company_short === type_of_card) - : real_financial_accounts_existing_data?.some(account => account.landing_company_short === type_of_card); + switch (account_type) { + case 'synthetic': + return real_synthetic_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + case 'all': + return real_swapfree_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + default: + return real_financial_accounts_existing_data?.some( + account => account.landing_company_short === type_of_card + ); + } }; const jurisdiction_cards_array = ['svg', 'bvi', 'vanuatu', 'labuan', 'maltainvest']; return ( @@ -43,6 +61,7 @@ const JurisdictionModalContent = ({ jurisdiction_selected_shortcode={jurisdiction_selected_shortcode} synthetic_available_accounts={synthetic_available_accounts} financial_available_accounts={financial_available_accounts} + swapfree_available_accounts={swapfree_available_accounts} account_type={account_type} setJurisdictionSelectedShortcode={setJurisdictionSelectedShortcode} /> diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-foot-note.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-foot-note.tsx index a9f4ddec1090..ed67f6ba1f5e 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-foot-note.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal-foot-note.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Text } from '@deriv/components'; import { Localize } from '@deriv/translations'; -import { getAuthenticationStatusInfo, isMobile } from '@deriv/shared'; +import { getAuthenticationStatusInfo, isMobile, getMT5Title } from '@deriv/shared'; import { TJurisdictionModalFootNoteProps } from '../props.types'; const FooterNote = ({ @@ -13,7 +13,7 @@ const FooterNote = ({ should_restrict_bvi_account_creation, should_restrict_vanuatu_account_creation, }: TJurisdictionModalFootNoteProps) => { - const account_type_name = account_type === 'synthetic' ? 'Derived' : 'Financial'; + const account_type_name = getMT5Title(account_type); const { poa_pending } = getAuthenticationStatusInfo(account_status); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal.tsx b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal.tsx index 60bf787fcf05..d290dfc2c4a9 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/jurisdiction-modal.tsx @@ -4,7 +4,7 @@ import { localize } from '@deriv/translations'; import { connect } from '../../Stores/connect'; import RootStore from '../../Stores/index'; import JurisdictionModalContent from './jurisdiction-modal-content'; -import { getAuthenticationStatusInfo, isMobile } from '@deriv/shared'; +import { getAuthenticationStatusInfo, isMobile, getMT5Title } from '@deriv/shared'; import { TJurisdictionModalProps } from '../props.types'; import JurisdictionCheckBox from './jurisdiction-modal-checkbox'; import JurisdictionModalFootNote from './jurisdiction-modal-foot-note'; @@ -21,6 +21,7 @@ const JurisdictionModal = ({ openPasswordModal, real_synthetic_accounts_existing_data, real_financial_accounts_existing_data, + real_swapfree_accounts_existing_data, trading_platform_available_accounts, toggleJurisdictionModal, setJurisdictionSelectedShortcode, @@ -76,10 +77,18 @@ const JurisdictionModal = ({ : available_account.shortcode !== 'maltainvest') ); + const swapfree_available_accounts = trading_platform_available_accounts.filter( + available_account => + available_account.market_type === 'all' && + (show_eu_related_content + ? available_account.shortcode === 'maltainvest' + : available_account.shortcode !== 'maltainvest') + ); + const modal_title = show_eu_related_content ? localize('Jurisdiction for your Deriv MT5 CFDs account') - : localize('Choose a jurisdiction for your MT5 {{account_type}} account', { - account_type: account_type.type === 'synthetic' ? 'Derived' : 'Financial', + : localize('Choose a jurisdiction for your Deriv MT5 {{account_type}} account', { + account_type: getMT5Title(account_type.type), }); const is_svg_selected = jurisdiction_selected_shortcode === 'svg'; @@ -90,14 +99,21 @@ const JurisdictionModal = ({ const isNextButtonDisabled = () => { if (jurisdiction_selected_shortcode) { - const is_account_created = - account_type.type === 'synthetic' - ? real_synthetic_accounts_existing_data?.some( - account => account.landing_company_short === jurisdiction_selected_shortcode - ) - : real_financial_accounts_existing_data?.some( - account => account.landing_company_short === jurisdiction_selected_shortcode - ); + let is_account_created; + + if (account_type.type === 'synthetic') { + is_account_created = real_synthetic_accounts_existing_data?.some( + account => account.landing_company_short === jurisdiction_selected_shortcode + ); + } else if (account_type.type === 'all') { + is_account_created = real_swapfree_accounts_existing_data?.some( + account => account.landing_company_short === jurisdiction_selected_shortcode + ); + } else { + is_account_created = real_financial_accounts_existing_data?.some( + account => account.landing_company_short === jurisdiction_selected_shortcode + ); + } if (!is_account_created) { if ( @@ -168,10 +184,12 @@ const JurisdictionModal = ({ is_virtual={is_virtual} real_financial_accounts_existing_data={real_financial_accounts_existing_data} real_synthetic_accounts_existing_data={real_synthetic_accounts_existing_data} + real_swapfree_accounts_existing_data={real_swapfree_accounts_existing_data} jurisdiction_selected_shortcode={jurisdiction_selected_shortcode} context={context} setJurisdictionSelectedShortcode={setJurisdictionSelectedShortcode} synthetic_available_accounts={synthetic_available_accounts} + swapfree_available_accounts={swapfree_available_accounts} />
; + mt5_trade_account: Required< + DetailsOfEachMT5Loginid & { market_type?: TTradingPlatformAvailableAccount['market_type'] | 'synthetic' } + >; is_eu_user: boolean; is_open: boolean; onPasswordManager: ( diff --git a/packages/cfd/src/Containers/props.types.ts b/packages/cfd/src/Containers/props.types.ts index 37856780cca8..fc9de6cdb716 100644 --- a/packages/cfd/src/Containers/props.types.ts +++ b/packages/cfd/src/Containers/props.types.ts @@ -166,6 +166,7 @@ export type TJurisdictionCardProps = { context: RootStore; synthetic_available_accounts: TTradingPlatformAvailableAccount[]; financial_available_accounts: TTradingPlatformAvailableAccount[]; + swapfree_available_accounts: TTradingPlatformAvailableAccount[]; setJurisdictionSelectedShortcode: (card_type: string) => void; account_type: string; type_of_card: string; @@ -184,6 +185,7 @@ export type TVerificationStatusBannerProps = { type_of_card: string; real_synthetic_accounts_existing_data: TExistingData; real_financial_accounts_existing_data: TExistingData; + real_swapfree_accounts_existing_data: TExistingData; should_restrict_bvi_account_creation: boolean; should_restrict_vanuatu_account_creation: boolean; }; @@ -225,6 +227,7 @@ export type TJurisdictionModalProps = { toggleCFDVerificationModal: () => void; real_synthetic_accounts_existing_data: TExistingData; real_financial_accounts_existing_data: TExistingData; + real_swapfree_accounts_existing_data: TExistingData; updateMT5Status: () => void; has_submitted_cfd_personal_details: boolean; }; @@ -236,8 +239,10 @@ export type TJurisdictionModalContentProps = { setJurisdictionSelectedShortcode: (card_type: string) => void; synthetic_available_accounts: TTradingPlatformAvailableAccount[]; financial_available_accounts: TTradingPlatformAvailableAccount[]; + swapfree_available_accounts: TTradingPlatformAvailableAccount[]; real_synthetic_accounts_existing_data: TExistingData; real_financial_accounts_existing_data: TExistingData; + real_swapfree_accounts_existing_data: TExistingData; is_virtual: boolean; }; diff --git a/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts b/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts index 0de741e45e0f..6498e216b23a 100644 --- a/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts +++ b/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts @@ -4,6 +4,11 @@ export type TDxCompanies = ReturnType; export type TMtCompanies = ReturnType; export const getDxCompanies = () => { + const all_config = { + account_type: '', + leverage: 500, + short_title: localize('CFDs'), + }; const synthetic_config = { account_type: '', leverage: 500, @@ -16,6 +21,12 @@ export const getDxCompanies = () => { }; return { demo: { + all: { + dxtrade_account_type: all_config.account_type, + leverage: all_config.leverage, + title: localize('Demo'), + short_title: all_config.short_title, + }, synthetic: { dxtrade_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, @@ -30,6 +41,18 @@ export const getDxCompanies = () => { }, }, real: { + all: { + dxtrade_account_type: all_config.account_type, + leverage: all_config.leverage, + title: localize('Real'), + short_title: all_config.short_title, + }, + dxtrade: { + mt5_account_type: all_config.account_type, + leverage: all_config.leverage, + title: localize('Real'), + short_title: all_config.short_title, + }, synthetic: { dxtrade_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, @@ -50,8 +73,8 @@ export const getMtCompanies = (is_eu: boolean) => { // TODO: Move this to the getDxCompanies for real release and when separating MT5 and DerivX components. const all_config = { account_type: '', - leverage: 500, - short_title: localize('CFDs'), + leverage: 100, + short_title: localize('Swap-Free'), }; const synthetic_config = { account_type: '', @@ -74,7 +97,13 @@ export const getMtCompanies = (is_eu: boolean) => { all: { mt5_account_type: all_config.account_type, leverage: all_config.leverage, - title: localize('Demo'), + title: localize('Demo Swap-Free'), + short_title: all_config.short_title, + }, + all_svg: { + mt5_account_type: all_config.account_type, + leverage: all_config.leverage, + title: localize('Demo Swap-Free SVG'), short_title: all_config.short_title, }, synthetic: { @@ -113,7 +142,13 @@ export const getMtCompanies = (is_eu: boolean) => { all: { mt5_account_type: all_config.account_type, leverage: all_config.leverage, - title: localize('Real'), + title: localize('Swap-Free'), + short_title: all_config.short_title, + }, + all_svg: { + mt5_account_type: all_config.account_type, + leverage: all_config.leverage, + title: localize('Swap-Free SVG'), short_title: all_config.short_title, }, dxtrade: { diff --git a/packages/cfd/src/Stores/Modules/CFD/cfd-store.js b/packages/cfd/src/Stores/Modules/CFD/cfd-store.js index 3a7e6e007c41..305500419575 100644 --- a/packages/cfd/src/Stores/Modules/CFD/cfd-store.js +++ b/packages/cfd/src/Stores/Modules/CFD/cfd-store.js @@ -37,6 +37,7 @@ export default class CFDStore extends BaseStore { real_synthetic_accounts_existing_data = []; real_financial_accounts_existing_data = []; + real_swapfree_accounts_existing_data = []; constructor({ root_store }) { super({ root_store }); @@ -94,6 +95,7 @@ export default class CFDStore extends BaseStore { toggleCompareAccountsModal: action.bound, getRealSyntheticAccountsExistingData: action.bound, getRealFinancialAccountsExistingData: action.bound, + getRealSwapfreeAccountsExistingData: action.bound, toggleJurisdictionModal: action.bound, toggleMT5TradeModal: action.bound, disableMt5FinancialStpModal: action.bound, @@ -165,13 +167,20 @@ export default class CFDStore extends BaseStore { return getDxCompanies(); } get has_created_account_for_selected_jurisdiction() { - return this.account_type.type === 'synthetic' - ? this.real_synthetic_accounts_existing_data?.some( - account => account.landing_company_short === this.jurisdiction_selected_shortcode - ) - : this.real_financial_accounts_existing_data?.some( - account => account.landing_company_short === this.jurisdiction_selected_shortcode - ); + switch (this.account_type.type) { + case 'synthetic': + return this.real_synthetic_accounts_existing_data?.some( + account => account.landing_company_short === this.jurisdiction_selected_shortcode + ); + case 'all': + return this.real_swapfree_accounts_existing_data?.some( + account => account.landing_company_short === this.jurisdiction_selected_shortcode + ); + default: + return this.real_financial_accounts_existing_data?.some( + account => account.landing_company_short === this.jurisdiction_selected_shortcode + ); + } } onMount() { @@ -278,6 +287,7 @@ export default class CFDStore extends BaseStore { phone, state: address_state, zipCode: address_postcode, + ...(this.account_type.type === 'all' ? { sub_account_category: 'swap_free' } : {}), ...(values.server ? { server: values.server } : {}), ...(this.jurisdiction_selected_shortcode ? { company: this.jurisdiction_selected_shortcode } : {}), ...(this.jurisdiction_selected_shortcode !== 'labuan' @@ -504,6 +514,10 @@ export default class CFDStore extends BaseStore { this.real_financial_accounts_existing_data = real_financial_accounts_existing_data; } + getRealSwapfreeAccountsExistingData(real_swapfree_accounts_existing_data) { + this.real_swapfree_accounts_existing_data = real_swapfree_accounts_existing_data; + } + toggleJurisdictionModal() { this.is_jurisdiction_modal_visible = !this.is_jurisdiction_modal_visible; } diff --git a/packages/cfd/src/sass/cfd-dashboard.scss b/packages/cfd/src/sass/cfd-dashboard.scss index 937250771fd5..e4f84d45e409 100644 --- a/packages/cfd/src/sass/cfd-dashboard.scss +++ b/packages/cfd/src/sass/cfd-dashboard.scss @@ -488,7 +488,8 @@ } .cfd-jurisdiction-card--synthetic, -.cfd-jurisdiction-card--financial { +.cfd-jurisdiction-card--financial, +.cfd-jurisdiction-card--all { border: solid 1px var(--border-normal); border-radius: 0.8rem; display: flex; diff --git a/packages/cfd/types.ts b/packages/cfd/types.ts new file mode 100644 index 000000000000..6389dd6f436a --- /dev/null +++ b/packages/cfd/types.ts @@ -0,0 +1,3 @@ +import type { TCoreStores } from '@deriv/stores/types'; + +export type TTradingPlatformAvailableAccount = TCoreStores['client']['trading_platform_available_accounts'][number]; diff --git a/packages/components/src/components/icon/icons.js b/packages/components/src/components/icon/icons.js index c51f50ca4e17..1869d85d25bd 100644 --- a/packages/components/src/components/icon/icons.js +++ b/packages/components/src/components/icon/icons.js @@ -645,6 +645,7 @@ import './mt5/ic-mt5-open-markets.svg'; import './mt5/ic-mt5-password-updated.svg'; import './mt5/ic-mt5-responsive.svg'; import './mt5/ic-mt5-support.svg'; +import './mt5/ic-mt5-swap-free-platform.svg'; import './mt5/ic-mt5-synthetic-dashboard.svg'; import './mt5/ic-mt5-synthetic-indices.svg'; import './mt5/ic-mt5-synthetic-platform.svg'; diff --git a/packages/components/src/components/icon/mt5/ic-mt5-swap-free-platform.svg b/packages/components/src/components/icon/mt5/ic-mt5-swap-free-platform.svg new file mode 100644 index 000000000000..7b464b0951e6 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-swap-free-platform.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/stories/icon/icons.js b/packages/components/stories/icon/icons.js index 0424319c40e1..a809e59bc3a9 100644 --- a/packages/components/stories/icon/icons.js +++ b/packages/components/stories/icon/icons.js @@ -665,6 +665,7 @@ export const icons = 'IcMt5PasswordUpdated', 'IcMt5Responsive', 'IcMt5Support', + 'IcMt5SwapFreePlatform', 'IcMt5SyntheticDashboard', 'IcMt5SyntheticIndices', 'IcMt5SyntheticPlatform', diff --git a/packages/core/src/Constants/cfd-text.js b/packages/core/src/Constants/cfd-text.js index 361ae8c8d724..264d84db7f70 100644 --- a/packages/core/src/Constants/cfd-text.js +++ b/packages/core/src/Constants/cfd-text.js @@ -6,13 +6,18 @@ export const CFD_TEXT = { mt5_cfds: () => localize('MT5 CFDs'), cfd: () => localize('CFDs'), synthetic: () => localize('Derived'), + synthetic_demo: () => localize('Derived Demo'), synthetic_bvi: () => localize('Derived BVI'), synthetic_svg: () => localize('Derived SVG'), synthetic_v: () => localize('Derived Vanuatu'), financial: () => localize('Financial'), + financial_demo: () => localize('Financial Demo'), financial_bvi: () => localize('Financial BVI'), financial_fx: () => localize('Financial Labuan'), financial_v: () => localize('Financial Vanuatu'), financial_svg: () => localize('Financial SVG'), derivez: () => localize('Deriv EZ'), + all: () => localize('Swap-Free'), + all_demo: () => localize('Swap-Free Demo'), + all_svg: () => localize('Swap-Free SVG'), }; diff --git a/packages/core/src/Stores/Helpers/client.js b/packages/core/src/Stores/Helpers/client.js index 6bf8bfd76ab2..534d7a76c75c 100644 --- a/packages/core/src/Stores/Helpers/client.js +++ b/packages/core/src/Stores/Helpers/client.js @@ -34,6 +34,13 @@ export const getAccountTitle = loginid => { return types_map[getClientAccountType(loginid)] || types_map.default; }; +export const getAvailableAccount = market_type => { + if (market_type === 'all') { + return 'all'; + } + return 'financial'; +}; + export const getLandingCompanyValue = ({ loginid, landing_company, isAccountOfType }) => { const key = 'changeable_fields'; let landing_company_object; diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js index 8d5c69e4dd4b..79a8e18fb7fd 100644 --- a/packages/core/src/Stores/client-store.js +++ b/packages/core/src/Stores/client-store.js @@ -26,7 +26,7 @@ import { } from '@deriv/shared'; import { WS, requestLogout } from 'Services'; import { action, computed, makeObservable, observable, reaction, runInAction, toJS, when } from 'mobx'; -import { getAccountTitle, getClientAccountType } from './Helpers/client'; +import { getAccountTitle, getClientAccountType, getAvailableAccount } from './Helpers/client'; import { getLanguage, localize } from '@deriv/translations'; import { getRegion, isEuCountry, isMultipliersOnly, isOptionsBlocked } from '_common/utility'; @@ -989,39 +989,50 @@ export default class ClientStore extends BaseStore { } }; - getIsMarketTypeMatching = (account, market_type) => - market_type === 'synthetic' - ? account.market_type === market_type || account.market_type === 'gaming' - : account.market_type === 'financial'; + getIsMarketTypeMatching = (account, market_type) => { + if (market_type === 'synthetic') { + return account.market_type === market_type || account.market_type === 'gaming'; + } else if (market_type === 'all') { + return account.market_type === 'all'; + } + return account.market_type === 'financial'; + }; isEligibleForMoreDemoMt5Svg(market_type) { + const is_synthetic = market_type === 'synthetic'; + const available_account = getAvailableAccount(market_type); const existing_demo_accounts = this.mt5_login_list.filter( account => account.account_type === 'demo' && this.getIsMarketTypeMatching(account, market_type) ); - return ( - this.trading_platform_available_accounts.some( - account => - (market_type === 'synthetic' ? 'gaming' : 'financial') === account.market_type && - account.shortcode === 'svg' - ) && existing_demo_accounts.every(account => !(account.landing_company_short === 'svg')) - ); + const has_matching_account = this.trading_platform_available_accounts.some(account => { + return (is_synthetic ? 'gaming' : available_account) === account.market_type && account.shortcode === 'svg'; + }); + const has_no_svg_account = existing_demo_accounts.every(account => { + return !(account.landing_company_short === 'svg'); + }); + + return has_matching_account && has_no_svg_account; } isEligibleForMoreRealMt5(market_type) { + const is_synthetic = market_type === 'synthetic'; + const available_account = getAvailableAccount(market_type); const existing_real_accounts = this.mt5_login_list.filter( account => account.account_type === 'real' && this.getIsMarketTypeMatching(account, market_type) ); const available_real_accounts_shortcodes = this.trading_platform_available_accounts .filter( account => - (market_type === 'synthetic' ? 'gaming' : 'financial') === account.market_type && + (is_synthetic ? 'gaming' : available_account) === account.market_type && account.shortcode !== 'maltainvest' ) .map(account => account.shortcode); - return !!available_real_accounts_shortcodes.filter(shortcode => - existing_real_accounts.every(account => account.landing_company_short !== shortcode) - ).length; + const has_no_matching_accounts = available_real_accounts_shortcodes.every(shortcode => + existing_real_accounts.some(account => account.landing_company_short !== shortcode) + ); + + return !has_no_matching_accounts; } isMT5Allowed = landing_companies => { diff --git a/packages/core/src/Stores/traders-hub-store.js b/packages/core/src/Stores/traders-hub-store.js index 463498940f38..e3afa1aad9c0 100644 --- a/packages/core/src/Stores/traders-hub-store.js +++ b/packages/core/src/Stores/traders-hub-store.js @@ -341,23 +341,37 @@ export default class TradersHubStore extends BaseStore { } getAvailableCFDAccounts() { - const account_desc = - !this.is_eu_user || this.is_demo_low_risk + const getAccountDesc = () => { + return !this.is_eu_user || this.is_demo_low_risk ? localize('Trade CFDs on MT5 with forex, stock indices, commodities, and cryptocurrencies.') : localize( 'Trade CFDs on MT5 with forex, stocks, stock indices, synthetics, cryptocurrencies, and commodities.' ); + }; + const getSwapFreeAccountDesc = () => { + return localize( + 'Trade swap-free CFDs on MT5 with synthetics, forex, stocks, stock indices, cryptocurrencies and ETFs.' + ); + }; const all_available_accounts = [ ...getCFDAvailableAccount(), { name: !this.is_eu_user || this.is_demo_low_risk ? localize('Financial') : localize('CFDs'), - description: account_desc, + description: getAccountDesc(), platform: CFD_PLATFORMS.MT5, market_type: 'financial', icon: !this.is_eu_user || this.is_demo_low_risk ? 'Financial' : 'CFDs', availability: 'All', }, + { + name: !this.is_eu_user ? localize('Swap-Free') : '', + description: getSwapFreeAccountDesc(), + platform: CFD_PLATFORMS.MT5, + market_type: 'all', + icon: 'SwapFree', + availability: 'Non-EU', + }, ]; this.available_cfd_accounts = all_available_accounts.map(account => { return { @@ -399,7 +413,10 @@ export default class TradersHubStore extends BaseStore { if (this.CFDs_restricted_countries) { this.available_mt5_accounts = this.available_cfd_accounts.filter( - account => account.market_type !== 'financial' && account.platform === CFD_PLATFORMS.MT5 + account => + account.market_type !== 'financial' && + account.market_type !== 'all' && + account.platform === CFD_PLATFORMS.MT5 ); return; } @@ -445,6 +462,9 @@ export default class TradersHubStore extends BaseStore { if (platform === CFD_PLATFORMS.MT5 && !this.is_eu_user && !maltainvest_account) { return key.startsWith(`${platform}.${selected_account_type}.${market_type}`); } + if (platform === CFD_PLATFORMS.MT5 && market_type === 'all') { + return key.startsWith(`${platform}.${selected_account_type}.${platform}@${market_type}`); + } if (platform === CFD_PLATFORMS.DXTRADE && market_type === 'all') { return key.startsWith(`${platform}.${selected_account_type}.${platform}@${market_type}`); } @@ -572,8 +592,11 @@ export default class TradersHubStore extends BaseStore { this.is_real && !this.is_eu_user && (this.hasCFDAccount(CFD_PLATFORMS.MT5, 'real', 'synthetic') || - this.hasCFDAccount(CFD_PLATFORMS.MT5, 'real', 'financial')) && - (isEligibleForMoreRealMt5('synthetic') || isEligibleForMoreRealMt5('financial')) && + this.hasCFDAccount(CFD_PLATFORMS.MT5, 'real', 'financial') || + this.hasCFDAccount(CFD_PLATFORMS.MT5, 'real', 'all')) && + (isEligibleForMoreRealMt5('synthetic') || + isEligibleForMoreRealMt5('financial') || + isEligibleForMoreRealMt5('all')) && !is_high_risk_client_for_mt5 ); } diff --git a/packages/hooks/src/__tests__/useHasSwapFreeAccount.spec.tsx b/packages/hooks/src/__tests__/useHasSwapFreeAccount.spec.tsx new file mode 100644 index 000000000000..f071c8f9d6b5 --- /dev/null +++ b/packages/hooks/src/__tests__/useHasSwapFreeAccount.spec.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { renderHook } from '@testing-library/react-hooks'; +import useHasSwapFreeAccount from '../useHasSwapFreeAccount'; + +describe('useHasSwapFreeAccount', () => { + test('should be true if it has a market type of all', async () => { + const mock = mockStore({ + client: { + trading_platform_available_accounts: [{ market_type: 'all' }], + }, + }); + + const wrapper = ({ children }: { children: JSX.Element }) => ( + {children} + ); + const { result } = renderHook(() => useHasSwapFreeAccount(), { wrapper }); + + expect(result.current).toBe(true); + }); + + test('should be false if it has a market type of financial', async () => { + const mock = mockStore({ + client: { + trading_platform_available_accounts: [{ market_type: 'financial' }], + }, + }); + + const wrapper = ({ children }: { children: JSX.Element }) => ( + {children} + ); + const { result } = renderHook(() => useHasSwapFreeAccount(), { wrapper }); + + expect(result.current).toBe(false); + }); + test('should be false if has a market type of gaming ', async () => { + const mock = mockStore({ + client: { + trading_platform_available_accounts: [{ market_type: 'gaming' }], + }, + }); + + const wrapper = ({ children }: { children: JSX.Element }) => ( + {children} + ); + const { result } = renderHook(() => useHasSwapFreeAccount(), { wrapper }); + + expect(result.current).toBe(false); + }); +}); diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index 1a88d45c814a..ca9e6182e64d 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -12,6 +12,7 @@ export { default as useHasActiveRealAccount } from './useHasActiveRealAccount'; export { default as useHasFiatCurrency } from './useHasFiatCurrency'; export { default as useHasMaltaInvestAccount } from './useHasMaltaInvestAccount'; export { default as useHasSetCurrency } from './useHasSetCurrency'; +export { default as useHasSwapFreeAccount } from './useHasSwapFreeAccount'; export { default as useHasSvgAccount } from './useHasSvgAccount'; export { default as useHasUSDCurrency } from './useHasUSDCurrency'; export { default as useIsP2PEnabled } from './useIsP2PEnabled'; diff --git a/packages/hooks/src/useHasSwapFreeAccount.ts b/packages/hooks/src/useHasSwapFreeAccount.ts new file mode 100644 index 000000000000..642bdd30e24d --- /dev/null +++ b/packages/hooks/src/useHasSwapFreeAccount.ts @@ -0,0 +1,17 @@ +import { useStore } from '@deriv/stores'; + +/** + * This hook is used to check if the client has a Swap-Free account. + * It checks for availability of market_type 'all' in trading_platform_available_accounts API response + */ +const useHasSwapFreeAccount = () => { + const { client } = useStore(); + const { trading_platform_available_accounts } = client; + const has_swap_free_account = trading_platform_available_accounts.some( + available_account => available_account.market_type === 'all' + ); + + return has_swap_free_account; +}; + +export default useHasSwapFreeAccount; diff --git a/packages/shared/src/utils/cfd/cfd.ts b/packages/shared/src/utils/cfd/cfd.ts index 212943728176..f18ac1be205d 100644 --- a/packages/shared/src/utils/cfd/cfd.ts +++ b/packages/shared/src/utils/cfd/cfd.ts @@ -4,23 +4,37 @@ import { localize } from '@deriv/translations'; let CFD_text_translated: { [key: string]: () => void }; -// TODO: add swap_free to this file when ready -const CFD_text: { [key: string]: string } = { +export const CFD_text: { [key: string]: string } = { dxtrade: 'Deriv X', mt5: 'MT5', mt5_cfds: 'MT5 CFDs', cfd: 'CFDs', synthetic: 'Derived', + synthetic_demo: 'Derived Demo', synthetic_bvi: 'Derived BVI', synthetic_svg: 'Derived SVG', synthetic_v: 'Derived Vanuatu', financial: 'Financial', + financial_demo: 'Financial Demo', financial_bvi: 'Financial BVI', financial_fx: 'Financial Labuan', financial_v: 'Financial Vanuatu', financial_svg: 'Financial SVG', + all: 'Swap-Free', + all_demo: 'Swap-Free Demo', + all_svg: 'Swap-Free SVG', } as const; +export const getMT5Title = (account_type: string) => { + if (account_type === 'synthetic') { + return CFD_text.synthetic; + } + if (account_type === 'all') { + return CFD_text.all; + } + return CFD_text.financial; +}; + type TPlatform = 'dxtrade' | 'mt5' | 'derivez'; type TMarketType = 'financial' | 'synthetic' | 'gaming' | 'all' | undefined; type TShortcode = 'svg' | 'bvi' | 'labuan' | 'vanuatu'; @@ -38,9 +52,20 @@ type TGetCFDAccountKey = TGetAccount & { // sub_account_type: "financial" | "financial_stp" | "swap_free" // * // sub_account_type financial_stp only happens in "financial" market_type +// dxrade and swap_free both have market_type "all" so check for platform is neccessary export const getCFDAccountKey = ({ market_type, sub_account_type, platform, shortcode }: TGetCFDAccountKey) => { if (market_type === 'all') { - return platform === CFD_PLATFORMS.DERIVEZ ? 'derivez' : 'dxtrade'; + if (platform === CFD_PLATFORMS.MT5) { + // currently we are only supporting SVG for SwapFree + switch (shortcode) { + case 'svg': + return 'all_svg'; + default: + return 'all_demo'; + } + } else { + return platform === CFD_PLATFORMS.DERIVEZ ? 'derivez' : 'dxtrade'; + } } if (market_type === 'gaming' || market_type === 'synthetic') { @@ -53,7 +78,7 @@ export const getCFDAccountKey = ({ market_type, sub_account_type, platform, shor case 'vanuatu': return 'synthetic_v'; default: - return 'synthetic'; + return 'synthetic_demo'; } } } @@ -73,7 +98,7 @@ export const getCFDAccountKey = ({ market_type, sub_account_type, platform, shor case 'vanuatu': return 'financial_v'; default: - return 'financial'; + return 'financial_demo'; } } } @@ -90,7 +115,7 @@ export const getCFDAccountKey = ({ market_type, sub_account_type, platform, shor type TGetAccountTypeFields = { category: 'real' | 'demo'; - type: 'financial' | 'synthetic'; + type: 'financial' | 'synthetic' | 'all'; }; type TAccountType = { @@ -112,6 +137,9 @@ export const getAccountTypeFields = ({ category, type }: TGetAccountTypeFields) account_type: 'financial', mt5_account_type: 'financial', }, + all: { + account_type: 'all', + }, }, demo: { synthetic: { @@ -121,6 +149,9 @@ export const getAccountTypeFields = ({ category, type }: TGetAccountTypeFields) account_type: 'demo', mt5_account_type: 'financial', }, + all: { + account_type: 'demo', + }, }, }; diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts index 51bbf865470c..4e6d494e11a8 100644 --- a/packages/stores/src/mockStore.ts +++ b/packages/stores/src/mockStore.ts @@ -8,6 +8,7 @@ const mock = (): TStores & { is_mock: boolean } => { account_settings: {}, accounts: {}, active_account_landing_company: '', + trading_platform_available_accounts: [], account_limits: { daily_transfers: { dxtrade: { diff --git a/packages/stores/types.ts b/packages/stores/types.ts index 8a6536f136a3..be904a96ea23 100644 --- a/packages/stores/types.ts +++ b/packages/stores/types.ts @@ -51,6 +51,23 @@ type TActiveAccount = TAccount & { is_virtual: number; }; +type TTradingPlatformAvailableAccount = { + market_type: 'financial' | 'gaming' | 'all'; + name: string; + requirements: { + after_first_deposit: { + financial_assessment: string[]; + }; + compliance: { + mt5: string[]; + tax_information: string[]; + }; + signup: string[]; + }; + shortcode: 'bvi' | 'labuan' | 'svg' | 'vanuatu'; + sub_account_type: string; +}; + type TAuthenticationStatus = { document_status: string; identity_status: string }; type TMenuItem = { @@ -108,6 +125,7 @@ type TClientStore = { accounts: { [k: string]: TActiveAccount }; active_accounts: TActiveAccount[]; active_account_landing_company: string; + trading_platform_available_accounts: TTradingPlatformAvailableAccount[]; account_limits: Partial & { is_loading?: boolean; api_initial_load_error?: string;