feat(trading): i18n (#5126)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
f891caf08b
commit
a070504d2e
@ -1,6 +1,9 @@
|
||||
import { useMemo } from 'react';
|
||||
import { type AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
|
||||
import {
|
||||
useAssetTypeMapping,
|
||||
useAssetStatusMapping,
|
||||
type AssetFieldsFragment,
|
||||
} from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
@ -15,6 +18,8 @@ type AssetsTableProps = {
|
||||
data: AssetFieldsFragment[] | null;
|
||||
};
|
||||
export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const assetTypeMapping = useAssetTypeMapping();
|
||||
const assetStatusMapping = useAssetStatusMapping();
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
@ -47,14 +52,14 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
field: 'source.__typename',
|
||||
hide: window.innerWidth < BREAKPOINT_MD,
|
||||
valueFormatter: ({ value }: { value?: string }) =>
|
||||
value ? AssetTypeMapping[value].value : '',
|
||||
value ? assetTypeMapping[value].value : '',
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
field: 'status',
|
||||
hide: window.innerWidth < BREAKPOINT_MD,
|
||||
valueFormatter: ({ value }: { value?: string }) =>
|
||||
value ? AssetStatusMapping[value].value : '',
|
||||
value ? assetStatusMapping[value].value : '',
|
||||
},
|
||||
{
|
||||
colId: 'actions',
|
||||
@ -69,7 +74,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
|
||||
value ? (
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
onClick={() => {
|
||||
navigate(value);
|
||||
}}
|
||||
>
|
||||
@ -80,7 +85,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
),
|
||||
},
|
||||
],
|
||||
[navigate]
|
||||
[navigate, assetStatusMapping, assetTypeMapping]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -4,7 +4,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SplashError } from './components/splash-error';
|
||||
@ -164,13 +164,14 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
const loading = (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
|
||||
return children;
|
||||
if (!loaded) {
|
||||
return loading;
|
||||
}
|
||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||
};
|
||||
|
@ -42,6 +42,7 @@ import {
|
||||
useNodeSwitcherStore,
|
||||
DocsLinks,
|
||||
NodeFailure,
|
||||
AppLoader as Loader,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
@ -352,9 +353,11 @@ function App() {
|
||||
useInitializeEnv();
|
||||
|
||||
return (
|
||||
<NetworkLoader cache={cache}>
|
||||
<AppContainer />
|
||||
</NetworkLoader>
|
||||
<React.Suspense fallback={<Loader />}>
|
||||
<NetworkLoader cache={cache}>
|
||||
<AppContainer />
|
||||
</NetworkLoader>
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
|
1
apps/governance/src/assets/locales
Symbolic link
1
apps/governance/src/assets/locales
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../libs/i18n/src/locales
|
@ -1,29 +1,41 @@
|
||||
import type { Module } from 'i18next';
|
||||
import i18n from 'i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocizeBackend from 'i18next-locize-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import dev from './translations/dev.json';
|
||||
const isInDev = process.env.NODE_ENV === 'development';
|
||||
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
|
||||
|
||||
const backend = useLocize
|
||||
? {
|
||||
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
|
||||
apiKey: process.env.NX_LOCIZE_API_KEY,
|
||||
referenceLng: 'en',
|
||||
}
|
||||
: {
|
||||
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
|
||||
};
|
||||
|
||||
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
// we init with resources
|
||||
resources: {
|
||||
en: {
|
||||
translations: {
|
||||
...dev,
|
||||
},
|
||||
},
|
||||
},
|
||||
lng: undefined,
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
debug: true,
|
||||
supportedLngs: ['en'],
|
||||
load: 'languageOnly',
|
||||
debug: isInDev,
|
||||
// have a common namespace used around the full app
|
||||
ns: ['translations'],
|
||||
defaultNS: 'translations',
|
||||
ns: ['governance'],
|
||||
defaultNS: 'governance',
|
||||
keySeparator: false, // we use content as keys
|
||||
|
||||
backend,
|
||||
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import dev from './i18n/translations/dev.json';
|
||||
import { locales } from '@vegaprotocol/i18n';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
@ -12,16 +12,10 @@ import ResizeObserver from 'resize-observer-polyfill';
|
||||
// en translations
|
||||
i18n.use(initReactI18next).init({
|
||||
// we init with resources
|
||||
resources: {
|
||||
en: {
|
||||
translations: {
|
||||
...dev,
|
||||
},
|
||||
},
|
||||
},
|
||||
resources: locales,
|
||||
fallbackLng: 'en',
|
||||
ns: ['translations'],
|
||||
defaultNS: 'translations',
|
||||
ns: ['governance'],
|
||||
defaultNS: 'governance',
|
||||
});
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
14
apps/trading/__mocks__/react-i18next.ts
Normal file
14
apps/trading/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
let translatedLabel = label;
|
||||
if (typeof replacements === 'object' && replacements !== null) {
|
||||
Object.keys(replacements).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(
|
||||
`{{${key}}}`,
|
||||
replacements[key]
|
||||
);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { TransferContainer } from '@vegaprotocol/accounts';
|
||||
import { GetStarted } from '../../components/welcome-dialog';
|
||||
import { GetStarted } from '../../components/welcome-dialog/get-started';
|
||||
|
||||
export const Transfer = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { GetStarted } from '../../components/welcome-dialog';
|
||||
import { GetStarted } from '../../components/welcome-dialog/get-started';
|
||||
import { WithdrawContainer } from '../../components/withdraw-container';
|
||||
|
||||
export const Withdraw = () => {
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Suspense, type ReactNode } from 'react';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
|
||||
export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
@ -36,41 +36,43 @@ export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<NetworkLoader
|
||||
cache={cacheConfig}
|
||||
skeleton={<AppLoader />}
|
||||
failure={
|
||||
<AppFailure title={t('Could not initialize app')} error={error} />
|
||||
}
|
||||
>
|
||||
<NodeGuard
|
||||
<Suspense fallback={<AppLoader />}>
|
||||
<NetworkLoader
|
||||
cache={cacheConfig}
|
||||
skeleton={<AppLoader />}
|
||||
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
failure={
|
||||
<AppFailure title={t('Could not initialize app')} error={error} />
|
||||
}
|
||||
>
|
||||
<Web3Provider
|
||||
<NodeGuard
|
||||
skeleton={<AppLoader />}
|
||||
failure={
|
||||
<AppFailure title={t(`Could not configure web3 provider`)} />
|
||||
}
|
||||
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
>
|
||||
<VegaWalletProvider
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
concepts: DocsLinks.VEGA_WALLET_CONCEPTS_URL,
|
||||
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||
},
|
||||
}}
|
||||
<Web3Provider
|
||||
skeleton={<AppLoader />}
|
||||
failure={
|
||||
<AppFailure title={t(`Could not configure web3 provider`)} />
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</VegaWalletProvider>
|
||||
</Web3Provider>
|
||||
</NodeGuard>
|
||||
</NetworkLoader>
|
||||
<VegaWalletProvider
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
concepts: DocsLinks.VEGA_WALLET_CONCEPTS_URL,
|
||||
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</VegaWalletProvider>
|
||||
</Web3Provider>
|
||||
</NodeGuard>
|
||||
</NetworkLoader>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
|
81
apps/trading/lib/i18n/index.ts
Normal file
81
apps/trading/lib/i18n/index.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import type { Module } from 'i18next';
|
||||
import i18n from 'i18next';
|
||||
import HttpBackend from 'i18next-http-backend';
|
||||
import LocizeBackend from 'i18next-locize-backend';
|
||||
import type { HttpBackendOptions, RequestCallback } from 'i18next-http-backend';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
const isInDev = process.env.NODE_ENV === 'development';
|
||||
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
|
||||
|
||||
const backend = useLocize
|
||||
? {
|
||||
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
|
||||
apiKey: process.env.NX_LOCIZE_API_KEY,
|
||||
referenceLng: 'en',
|
||||
}
|
||||
: {
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
request: (
|
||||
options: HttpBackendOptions,
|
||||
url: string,
|
||||
payload: string,
|
||||
callback: RequestCallback
|
||||
) => {
|
||||
if (typeof window === 'undefined') {
|
||||
callback(false, { status: 200, data: {} });
|
||||
return;
|
||||
}
|
||||
fetch(url).then((response) => {
|
||||
if (!response.ok) {
|
||||
return callback(response.statusText || 'Error', {
|
||||
status: response.status,
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
response
|
||||
.text()
|
||||
.then((data) => {
|
||||
callback(null, { status: response.status, data });
|
||||
})
|
||||
.catch((error) => callback(error, { status: 200, data: {} }));
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: ['en'],
|
||||
load: 'languageOnly',
|
||||
// have a common namespace used around the full app
|
||||
ns: [
|
||||
'accounts',
|
||||
'assets',
|
||||
'candles-chart',
|
||||
'datagrid',
|
||||
'deal-ticket',
|
||||
'deposits',
|
||||
'environment',
|
||||
'fills',
|
||||
'funding-payments',
|
||||
'trading',
|
||||
],
|
||||
defaultNS: 'trading',
|
||||
keySeparator: false, // we use content as keys
|
||||
backend,
|
||||
debug: isInDev,
|
||||
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
@ -3,7 +3,7 @@ import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
envTriggerMapping,
|
||||
useEnvTriggerMapping,
|
||||
Networks,
|
||||
NodeSwitcherDialog,
|
||||
useEnvironment,
|
||||
@ -32,6 +32,7 @@ import { SSRLoader } from './ssr-loader';
|
||||
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
||||
import { MaybeConnectEagerly } from './maybe-connect-eagerly';
|
||||
import { TransactionHandlers } from './transaction-handlers';
|
||||
import '../lib/i18n';
|
||||
|
||||
const DEFAULT_TITLE = t('Welcome to Vega trading!');
|
||||
|
||||
@ -39,7 +40,7 @@ const Title = () => {
|
||||
const { pageTitle } = usePageTitleStore((store) => ({
|
||||
pageTitle: store.pageTitle,
|
||||
}));
|
||||
|
||||
const envTriggerMapping = useEnvTriggerMapping();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const networkName = envTriggerMapping[VEGA_ENV];
|
||||
|
||||
|
1
apps/trading/public/locales
Symbolic link
1
apps/trading/public/locales
Symbolic link
@ -0,0 +1 @@
|
||||
../../../libs/i18n/src/locales
|
@ -1,5 +1,5 @@
|
||||
import { ETHERSCAN_ADDRESS, useEtherscanLink } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import {
|
||||
ActionsDropdown,
|
||||
TradingDropdownCopyItem,
|
||||
@ -27,7 +27,7 @@ export const AccountsActionsDropdown = ({
|
||||
}) => {
|
||||
const etherscanLink = useEtherscanLink();
|
||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||
|
||||
const t = useT();
|
||||
return (
|
||||
<ActionsDropdown>
|
||||
<TradingDropdownItem
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRef, memo, useState, useCallback } from 'react';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import {
|
||||
@ -22,6 +22,7 @@ const AccountBreakdown = ({
|
||||
partyId: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: aggregatedAccountDataProvider,
|
||||
@ -45,10 +46,10 @@ const AccountBreakdown = ({
|
||||
</h1>
|
||||
{data && (
|
||||
<p className="mb-2 text-sm">
|
||||
{t('You have %s %s in total.', [
|
||||
addDecimalsFormatNumber(data.total, data.asset.decimals),
|
||||
data.asset.symbol,
|
||||
])}
|
||||
{t('You have {{value}} {{symbol}} in total.', {
|
||||
value: addDecimalsFormatNumber(data.total, data.asset.decimals),
|
||||
symbol: data.asset.symbol,
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
<BreakdownTable
|
||||
@ -118,6 +119,7 @@ export const AccountManager = ({
|
||||
onMarketClick,
|
||||
gridProps,
|
||||
}: AccountManagerProps) => {
|
||||
const t = useT();
|
||||
const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
isNumeric,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
@ -96,6 +96,7 @@ export const AccountTable = ({
|
||||
pinnedAsset,
|
||||
...props
|
||||
}: AccountTableProps) => {
|
||||
const t = useT();
|
||||
const pinnedRow = useMemo(() => {
|
||||
if (!pinnedAsset) {
|
||||
return;
|
||||
@ -191,7 +192,7 @@ export const AccountTable = ({
|
||||
<>
|
||||
<span className="underline">{valueFormatted}</span>
|
||||
<span className="inline-block ml-2 w-14 text-muted">
|
||||
{t('0.00%')}
|
||||
{(0).toFixed(2)}%
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
@ -310,6 +311,7 @@ export const AccountTable = ({
|
||||
onClickTransfer,
|
||||
isReadOnly,
|
||||
showDepositButton,
|
||||
t,
|
||||
]);
|
||||
|
||||
const data = rowData?.filter((data) => data.asset.id !== pinnedAsset?.id);
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
addDecimalsFormatNumber,
|
||||
addDecimalsFormatNumberQuantum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import { Intent, TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
import { type AgGridReact, type AgGridReactProps } from 'ag-grid-react';
|
||||
import { type AccountFields } from './accounts-data-provider';
|
||||
@ -31,6 +31,7 @@ interface BreakdownTableProps extends AgGridReactProps {
|
||||
|
||||
const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
({ data }, ref) => {
|
||||
const t = useT();
|
||||
const coldefs = useMemo(() => {
|
||||
const defs: ColDef[] = [
|
||||
{
|
||||
@ -53,7 +54,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
'None'
|
||||
t('None')
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -126,7 +127,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
},
|
||||
];
|
||||
return defs;
|
||||
}, []);
|
||||
}, [t]);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
|
@ -4,9 +4,10 @@ import { Tooltip, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { marketMarginDataProvider } from './margin-data-provider';
|
||||
import { useAssetsMapProvider } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT, ns } from './use-t';
|
||||
import { useAccountBalance } from './use-account-balance';
|
||||
import { useMarketAccountBalance } from './use-market-account-balance';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
const MarginHealthChartTooltipRow = ({
|
||||
label,
|
||||
@ -58,6 +59,7 @@ export const MarginHealthChartTooltip = ({
|
||||
decimals: number;
|
||||
marginAccountBalance?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const tooltipContent = [
|
||||
<MarginHealthChartTooltipRow
|
||||
key={'maintenance'}
|
||||
@ -169,14 +171,23 @@ export const MarginHealthChart = ({
|
||||
|
||||
return (
|
||||
<div data-testid="margin-health-chart">
|
||||
{addDecimalsFormatNumber(
|
||||
(BigInt(marginAccountBalance) - BigInt(maintenanceLevel)).toString(),
|
||||
decimals
|
||||
)}{' '}
|
||||
{t('above')}{' '}
|
||||
<ExternalLink href="https://docs.vega.xyz/testnet/concepts/trading-on-vega/positions-margin#margin-level-maintenance">
|
||||
{t('maintenance level')}
|
||||
</ExternalLink>
|
||||
<Trans
|
||||
defaults="{{balance}} above <0>maintenance level</0>"
|
||||
components={[
|
||||
<ExternalLink href="https://docs.vega.xyz/testnet/concepts/trading-on-vega/positions-margin#margin-level-maintenance">
|
||||
maintenance level
|
||||
</ExternalLink>,
|
||||
]}
|
||||
values={{
|
||||
balance: addDecimalsFormatNumber(
|
||||
(
|
||||
BigInt(marginAccountBalance) - BigInt(maintenanceLevel)
|
||||
).toString(),
|
||||
decimals
|
||||
),
|
||||
}}
|
||||
ns={ns}
|
||||
/>
|
||||
<Tooltip description={tooltip}>
|
||||
<div
|
||||
data-testid="margin-health-chart-track"
|
||||
|
@ -1,7 +1,8 @@
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ns, useT } from './use-t';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
@ -21,6 +22,7 @@ export const ALLOWED_ACCOUNTS = [
|
||||
];
|
||||
|
||||
export const TransferContainer = ({ assetId }: { assetId?: string }) => {
|
||||
const t = useT();
|
||||
const { pubKey, pubKeys } = useVegaWallet();
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.transfer_fee_factor,
|
||||
@ -50,16 +52,20 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-4 text-sm" data-testid="transfer-intro-text">
|
||||
{t('Transfer funds to another Vega key')}
|
||||
{pubKey && (
|
||||
<>
|
||||
{t(' from ')}
|
||||
<Lozenge className="font-mono">
|
||||
{truncateByChars(pubKey || '')}
|
||||
</Lozenge>
|
||||
</>
|
||||
{pubKey ? (
|
||||
<Trans
|
||||
i18nKey="TRANSFER_FUNDS_TO_ANOTHER_KNOWN_VEGA_KEY"
|
||||
defaults="Transfer funds to another Vega key <0>{{pubKey}}</0>. If you are at all unsure, stop and seek advice."
|
||||
ns={ns}
|
||||
components={[<Lozenge className="font-mono">pubKey</Lozenge>]}
|
||||
values={{ pubKey: truncateByChars(pubKey || '') }}
|
||||
/>
|
||||
) : (
|
||||
t('TRANSFER_FUNDS_TO_ANOTHER_VEGA_KEY', {
|
||||
defaultValue:
|
||||
'Transfer funds to another Vega key. If you are at all unsure, stop and seek advice.',
|
||||
})
|
||||
)}
|
||||
{t('. If you are at all unsure, stop and seek advice.')}
|
||||
</p>
|
||||
<TransferForm
|
||||
pubKey={pubKey}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
addDecimalsFormatNumber,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
@ -66,6 +66,7 @@ export const TransferForm = ({
|
||||
accounts,
|
||||
minQuantumMultiple,
|
||||
}: TransferFormProps) => {
|
||||
const t = useT();
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
@ -300,7 +301,7 @@ export const TransferForm = ({
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="To Vega key" labelFor="toVegaKey">
|
||||
<TradingFormGroup label={t('To Vega key')} labelFor="toVegaKey">
|
||||
<AddressField
|
||||
onChange={() => {
|
||||
setValue('toVegaKey', '');
|
||||
@ -317,7 +318,10 @@ export const TransferForm = ({
|
||||
{t('Please select')}
|
||||
</option>
|
||||
{pubKeys?.map((pk) => {
|
||||
const text = pk === pubKey ? t('Current key: ') + pk : pk;
|
||||
const text =
|
||||
pk === pubKey
|
||||
? t('Current key: {{pubKey}}', { pubKey: pk }) + pk
|
||||
: pk;
|
||||
|
||||
return (
|
||||
<option key={pk} value={pk}>
|
||||
@ -351,7 +355,7 @@ export const TransferForm = ({
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="Amount" labelFor="amount">
|
||||
<TradingFormGroup label={t('Amount')} labelFor="amount">
|
||||
<TradingInput
|
||||
id="amount"
|
||||
autoComplete="off"
|
||||
@ -473,6 +477,7 @@ export const TransferFee = ({
|
||||
fee?: string;
|
||||
decimals?: number;
|
||||
}) => {
|
||||
const t = useT();
|
||||
if (!feeFactor || !amount || !transferAmount || !fee) return null;
|
||||
if (
|
||||
isNaN(Number(feeFactor)) ||
|
||||
@ -490,8 +495,8 @@ export const TransferFee = ({
|
||||
<div className="flex flex-wrap items-center justify-between gap-1">
|
||||
<Tooltip
|
||||
description={t(
|
||||
`The transfer fee is set by the network parameter transfer.fee.factor, currently set to %s`,
|
||||
[feeFactor]
|
||||
`The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}`,
|
||||
{ feeFactor }
|
||||
)}
|
||||
>
|
||||
<div>{t('Transfer fee')}</div>
|
||||
@ -546,6 +551,7 @@ export const AddressField = ({
|
||||
mode,
|
||||
onChange,
|
||||
}: AddressInputProps) => {
|
||||
const t = useT();
|
||||
const isInput = mode === 'input';
|
||||
return (
|
||||
<>
|
||||
|
3
libs/accounts/src/lib/use-t.ts
Normal file
3
libs/accounts/src/lib/use-t.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const ns = 'accounts';
|
||||
export const useT = () => useTranslation(ns).t;
|
@ -1,6 +1,21 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
import { defaultFallbackInView } from 'react-intersection-observer';
|
||||
import { locales } from '@vegaprotocol/i18n';
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
defaultFallbackInView(true);
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
||||
// Set up i18n instance so that components have the correct default
|
||||
// en translations
|
||||
i18n.use(initReactI18next).init({
|
||||
// we init with resources
|
||||
resources: locales,
|
||||
fallbackLng: 'en',
|
||||
ns: ['accounts'],
|
||||
defaultNS: 'accounts',
|
||||
});
|
||||
|
||||
global.ResizeObserver = ResizeObserver;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
@ -56,6 +56,7 @@ export const AssetDetailsDialog = ({
|
||||
onChange,
|
||||
asJson = false,
|
||||
}: AssetDetailsDialogProps) => {
|
||||
const t = useT();
|
||||
const { data: asset } = useAssetDataProvider(assetId);
|
||||
|
||||
const assetSymbol = asset?.symbol || '';
|
||||
@ -77,7 +78,7 @@ export const AssetDetailsDialog = ({
|
||||
</div>
|
||||
);
|
||||
const title = asset
|
||||
? t(`Asset details - ${asset.symbol}`)
|
||||
? t('Asset details - {{symbol}}', asset)
|
||||
: t('Asset not found');
|
||||
|
||||
return (
|
||||
@ -100,8 +101,8 @@ export const AssetDetailsDialog = ({
|
||||
{content}
|
||||
<p className="my-4 text-xs">
|
||||
{t(
|
||||
'There is 1 unit of the settlement asset (%s) to every 1 quote unit.',
|
||||
[assetSymbol]
|
||||
'There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit.',
|
||||
{ assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
<div className="w-1/4">
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, renderHook } from '@testing-library/react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { Asset } from './asset-data-provider';
|
||||
import {
|
||||
AssetDetail,
|
||||
AssetDetailsTable,
|
||||
rows,
|
||||
useRows,
|
||||
testId,
|
||||
} from './asset-details-table';
|
||||
import { generateBuiltinAsset, generateERC20Asset } from './test-helpers';
|
||||
@ -67,6 +67,8 @@ describe('AssetDetailsTable', () => {
|
||||
it.each(cases)(
|
||||
"displays the available asset's data of %p with correct labels",
|
||||
async (_type, asset, details) => {
|
||||
const { result } = renderHook(() => useRows());
|
||||
const rows = result.current;
|
||||
render(<AssetDetailsTable asset={asset} />);
|
||||
for (const detail of details) {
|
||||
expect(
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { EtherscanLink } from '@vegaprotocol/environment';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import type { KeyValueTableRowProps } from '@vegaprotocol/ui-toolkit';
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
@ -10,7 +10,7 @@ import {
|
||||
KeyValueTableRow,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo, type ReactNode } from 'react';
|
||||
import type { Asset } from './asset-data-provider';
|
||||
import { WITHDRAW_THRESHOLD_TOOLTIP_TEXT } from './constants';
|
||||
|
||||
@ -52,183 +52,208 @@ const num = (asset: Asset, n: string | undefined | null) => {
|
||||
return addDecimalsFormatNumber(n, asset.decimals);
|
||||
};
|
||||
|
||||
export const rows: Rows = [
|
||||
{
|
||||
key: AssetDetail.ID,
|
||||
label: t('ID'),
|
||||
tooltip: '',
|
||||
value: (asset) => (
|
||||
<>
|
||||
{truncateMiddle(asset.id)}{' '}
|
||||
<CopyWithTooltip text={asset.id}>
|
||||
<button title={t('Copy id to clipboard')}>
|
||||
<VegaIcon size={14} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.TYPE,
|
||||
label: t('Type'),
|
||||
tooltip: '',
|
||||
value: (asset) => AssetTypeMapping[asset.source.__typename].value,
|
||||
valueTooltip: (asset) => AssetTypeMapping[asset.source.__typename].tooltip,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.NAME,
|
||||
label: t('Name'),
|
||||
tooltip: '',
|
||||
value: (asset) => asset.name,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.SYMBOL,
|
||||
label: t('Symbol'),
|
||||
tooltip: '',
|
||||
value: (asset) => asset.symbol,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.DECIMALS,
|
||||
label: t('Decimals'),
|
||||
tooltip: t('Number of decimal / precision handled by this asset'),
|
||||
value: (asset) => asset.decimals.toString(),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.QUANTUM,
|
||||
label: t('Quantum'),
|
||||
tooltip: t('The minimum economically meaningful amount of the asset'),
|
||||
value: (asset) => num(asset, asset.quantum),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.STATUS,
|
||||
label: t('Status'),
|
||||
tooltip: t('The status of the asset in the Vega network'),
|
||||
value: (asset) => AssetStatusMapping[asset.status].value,
|
||||
valueTooltip: (asset) => AssetStatusMapping[asset.status].tooltip,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.CONTRACT_ADDRESS,
|
||||
label: t('Contract address'),
|
||||
tooltip: t(
|
||||
'The address of the contract for the token, on the ethereum network'
|
||||
),
|
||||
value: (asset) => {
|
||||
if (asset.source.__typename !== 'ERC20') {
|
||||
return;
|
||||
}
|
||||
export const useRows = () => {
|
||||
const t = useT();
|
||||
const AssetTypeMapping = useAssetTypeMapping();
|
||||
const AssetStatusMapping = useAssetStatusMapping();
|
||||
return useMemo<Rows>(
|
||||
() => [
|
||||
{
|
||||
key: AssetDetail.ID,
|
||||
label: t('ID'),
|
||||
tooltip: '',
|
||||
value: (asset) => (
|
||||
<>
|
||||
{truncateMiddle(asset.id)}{' '}
|
||||
<CopyWithTooltip text={asset.id}>
|
||||
<button title={t('Copy id to clipboard')}>
|
||||
<VegaIcon size={14} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.TYPE,
|
||||
label: t('Type'),
|
||||
tooltip: '',
|
||||
value: (asset) => AssetTypeMapping[asset.source.__typename].value,
|
||||
valueTooltip: (asset) =>
|
||||
AssetTypeMapping[asset.source.__typename].tooltip,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.NAME,
|
||||
label: t('Name'),
|
||||
tooltip: '',
|
||||
value: (asset) => asset.name,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.SYMBOL,
|
||||
label: t('Symbol'),
|
||||
tooltip: '',
|
||||
value: (asset) => asset.symbol,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.DECIMALS,
|
||||
label: t('Decimals'),
|
||||
tooltip: t('Number of decimal / precision handled by this asset'),
|
||||
value: (asset) => asset.decimals.toString(),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.QUANTUM,
|
||||
label: t('Quantum'),
|
||||
tooltip: t('The minimum economically meaningful amount of the asset'),
|
||||
value: (asset) => num(asset, asset.quantum),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.STATUS,
|
||||
label: t('Status'),
|
||||
tooltip: t('The status of the asset in the Vega network'),
|
||||
value: (asset) => AssetStatusMapping[asset.status].value,
|
||||
valueTooltip: (asset) => AssetStatusMapping[asset.status].tooltip,
|
||||
},
|
||||
{
|
||||
key: AssetDetail.CONTRACT_ADDRESS,
|
||||
label: t('Contract address'),
|
||||
tooltip: t(
|
||||
'The address of the contract for the token, on the ethereum network'
|
||||
),
|
||||
value: (asset) => {
|
||||
if (asset.source.__typename !== 'ERC20') {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<EtherscanLink address={asset.source.contractAddress}>
|
||||
{truncateMiddle(asset.source.contractAddress)}
|
||||
</EtherscanLink>{' '}
|
||||
<CopyWithTooltip text={asset.source.contractAddress}>
|
||||
<button title={t('Copy address to clipboard')}>
|
||||
<VegaIcon size={14} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: AssetDetail.WITHDRAWAL_THRESHOLD,
|
||||
label: t('Withdrawal threshold'),
|
||||
tooltip: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.LIFETIME_LIMIT,
|
||||
label: t('Lifetime limit'),
|
||||
tooltip: t(
|
||||
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance'
|
||||
),
|
||||
value: (asset) => num(asset, (asset.source as Schema.ERC20).lifetimeLimit),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAX_FAUCET_AMOUNT_MINT,
|
||||
label: t('Max faucet amount'),
|
||||
tooltip: t(
|
||||
'Maximum amount that can be requested by a party through the built-in asset faucet at a time'
|
||||
),
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.BuiltinAsset).maxFaucetAmountMint),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
|
||||
label: t('Infrastructure fee account balance'),
|
||||
tooltip: t('The infrastructure fee account in this asset'),
|
||||
value: (asset) => num(asset, asset.infrastructureFeeAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE,
|
||||
label: t('Global reward pool account balance'),
|
||||
tooltip: t('The global rewards acquired in this asset'),
|
||||
value: (asset) => num(asset, asset.globalRewardPoolAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAKER_PAID_FEES_ACCOUNT_BALANCE,
|
||||
label: t('Maker paid fees account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the fees paid to makers in this asset'
|
||||
),
|
||||
value: (asset) => num(asset, asset.takerFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAKER_RECEIVED_FEES_ACCOUNT_BALANCE,
|
||||
label: t('Maker received fees account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on fees received for being a maker on trades'
|
||||
),
|
||||
value: (asset) => num(asset, asset.makerFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE,
|
||||
label: t('Liquidity provision fee reward account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the liquidity provision fees in this asset'
|
||||
),
|
||||
value: (asset) => num(asset, asset.lpFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE,
|
||||
label: t('Market proposer reward account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the market proposer reward in this asset'
|
||||
),
|
||||
value: (asset) => num(asset, asset.marketProposerRewardAccount?.balance),
|
||||
},
|
||||
];
|
||||
|
||||
export const AssetStatusMapping: Mapping = {
|
||||
STATUS_ENABLED: {
|
||||
value: t('Enabled'),
|
||||
tooltip: t('Asset can be used on the Vega network'),
|
||||
},
|
||||
STATUS_PENDING_LISTING: {
|
||||
value: t('Pending listing'),
|
||||
tooltip: t('Asset needs to be added to the Ethereum bridge'),
|
||||
},
|
||||
STATUS_PROPOSED: {
|
||||
value: t('Proposed'),
|
||||
tooltip: t('Asset has been proposed to the network'),
|
||||
},
|
||||
STATUS_REJECTED: {
|
||||
value: t('Rejected'),
|
||||
tooltip: t('Asset has been rejected'),
|
||||
},
|
||||
return (
|
||||
<>
|
||||
<EtherscanLink address={asset.source.contractAddress}>
|
||||
{truncateMiddle(asset.source.contractAddress)}
|
||||
</EtherscanLink>{' '}
|
||||
<CopyWithTooltip text={asset.source.contractAddress}>
|
||||
<button title={t('Copy address to clipboard')}>
|
||||
<VegaIcon size={14} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: AssetDetail.WITHDRAWAL_THRESHOLD,
|
||||
label: t('Withdrawal threshold'),
|
||||
tooltip: t('WITHDRAW_THRESHOLD_TOOLTIP_TEXT', {
|
||||
defaultValue: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
|
||||
}),
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.LIFETIME_LIMIT,
|
||||
label: t('Lifetime limit'),
|
||||
tooltip: t(
|
||||
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance'
|
||||
),
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.ERC20).lifetimeLimit),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAX_FAUCET_AMOUNT_MINT,
|
||||
label: t('Max faucet amount'),
|
||||
tooltip: t(
|
||||
'Maximum amount that can be requested by a party through the built-in asset faucet at a time'
|
||||
),
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.BuiltinAsset).maxFaucetAmountMint),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
|
||||
label: t('Infrastructure fee account balance'),
|
||||
tooltip: t('The infrastructure fee account in this asset'),
|
||||
value: (asset) => num(asset, asset.infrastructureFeeAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE,
|
||||
label: t('Global reward pool account balance'),
|
||||
tooltip: t('The global rewards acquired in this asset'),
|
||||
value: (asset) => num(asset, asset.globalRewardPoolAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAKER_PAID_FEES_ACCOUNT_BALANCE,
|
||||
label: t('Maker paid fees account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the fees paid to makers in this asset'
|
||||
),
|
||||
value: (asset) => num(asset, asset.takerFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAKER_RECEIVED_FEES_ACCOUNT_BALANCE,
|
||||
label: t('Maker received fees account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on fees received for being a maker on trades'
|
||||
),
|
||||
value: (asset) => num(asset, asset.makerFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE,
|
||||
label: t('Liquidity provision fee reward account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the liquidity provision fees in this asset'
|
||||
),
|
||||
value: (asset) => num(asset, asset.lpFeeRewardAccount?.balance),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE,
|
||||
label: t('Market proposer reward account balance'),
|
||||
tooltip: t(
|
||||
'The rewards acquired based on the market proposer reward in this asset'
|
||||
),
|
||||
value: (asset) =>
|
||||
num(asset, asset.marketProposerRewardAccount?.balance),
|
||||
},
|
||||
],
|
||||
[t, AssetTypeMapping, AssetStatusMapping]
|
||||
);
|
||||
};
|
||||
|
||||
export const AssetTypeMapping: Mapping = {
|
||||
BuiltinAsset: {
|
||||
value: 'Builtin asset',
|
||||
tooltip: t('A Vega builtin asset'),
|
||||
},
|
||||
ERC20: {
|
||||
value: 'ERC20',
|
||||
tooltip: t('An asset originated from an Ethereum ERC20 Token'),
|
||||
},
|
||||
export const useAssetStatusMapping = () => {
|
||||
const t = useT();
|
||||
return useMemo<Mapping>(
|
||||
() => ({
|
||||
STATUS_ENABLED: {
|
||||
value: t('Enabled'),
|
||||
tooltip: t('Asset can be used on the Vega network'),
|
||||
},
|
||||
STATUS_PENDING_LISTING: {
|
||||
value: t('Pending listing'),
|
||||
tooltip: t('Asset needs to be added to the Ethereum bridge'),
|
||||
},
|
||||
STATUS_PROPOSED: {
|
||||
value: t('Proposed'),
|
||||
tooltip: t('Asset has been proposed to the network'),
|
||||
},
|
||||
STATUS_REJECTED: {
|
||||
value: t('Rejected'),
|
||||
tooltip: t('Asset has been rejected'),
|
||||
},
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const useAssetTypeMapping = () => {
|
||||
const t = useT();
|
||||
return useMemo<Mapping>(
|
||||
() => ({
|
||||
BuiltinAsset: {
|
||||
value: t('Builtin asset'),
|
||||
tooltip: t('A Vega builtin asset'),
|
||||
},
|
||||
ERC20: {
|
||||
value: t('ERC20'),
|
||||
tooltip: t('An asset originated from an Ethereum ERC20 Token'),
|
||||
},
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const testId = (detail: AssetDetail, field: 'label' | 'value') =>
|
||||
@ -248,7 +273,7 @@ export const AssetDetailsTable = ({
|
||||
? { className: 'break-all', title: value }
|
||||
: {};
|
||||
|
||||
const details = rows.map((r) => ({
|
||||
const details = useRows().map((r) => ({
|
||||
...r,
|
||||
value: r.value(asset),
|
||||
valueTooltip: r.valueTooltip?.(asset),
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { TradingOption, truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AssetFieldsFragment } from './__generated__/Asset';
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type AssetOptionProps = {
|
||||
@ -15,8 +15,9 @@ export const Balance = ({
|
||||
}: {
|
||||
balance?: string;
|
||||
symbol: string;
|
||||
}) =>
|
||||
balance ? (
|
||||
}) => {
|
||||
const t = useT();
|
||||
return balance ? (
|
||||
<div className="mt-1 font-alpha" data-testid="asset-balance">
|
||||
{balance} {symbol}
|
||||
</div>
|
||||
@ -25,6 +26,7 @@ export const Balance = ({
|
||||
{t('Fetching balance…')}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const AssetOption = ({ asset, balance }: AssetOptionProps) => {
|
||||
return (
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT = t(
|
||||
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them"
|
||||
);
|
||||
export const WITHDRAW_THRESHOLD_TOOLTIP_TEXT =
|
||||
"The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them";
|
||||
|
||||
// List of defunct and no longer used assets that were created for various testnets
|
||||
export const DENY_LIST: Record<string, string[]> = {
|
||||
|
3
libs/assets/src/lib/use-t.ts
Normal file
3
libs/assets/src/lib/use-t.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const useT = () => useTranslation('assets').t;
|
@ -6,12 +6,12 @@ import { useMemo } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
STUDY_SIZE,
|
||||
useCandlesChartSettings,
|
||||
} from './use-candles-chart-settings';
|
||||
import { useT } from './use-t';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export type CandlesChartContainerProps = {
|
||||
marketId: string;
|
||||
@ -25,6 +25,7 @@ export const CandlesChartContainer = ({
|
||||
const client = useApolloClient();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { theme } = useThemeSwitcher();
|
||||
const t = useT();
|
||||
|
||||
const {
|
||||
interval,
|
||||
|
@ -22,8 +22,8 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { type IconName } from '@blueprintjs/icons';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCandlesChartSettings } from './use-candles-chart-settings';
|
||||
import { useT } from './use-t';
|
||||
|
||||
const chartTypeIcon = new Map<ChartType, IconName>([
|
||||
[ChartType.AREA, IconNames.TIMELINE_AREA_CHART],
|
||||
@ -43,6 +43,7 @@ export const CandlesMenu = () => {
|
||||
setStudies,
|
||||
setOverlays,
|
||||
} = useCandlesChartSettings();
|
||||
const t = useT();
|
||||
const triggerClasses = 'text-xs';
|
||||
const contentAlign = 'end';
|
||||
const triggerButtonProps = { size: 'extra-small' } as const;
|
||||
@ -53,7 +54,10 @@ export const CandlesMenu = () => {
|
||||
trigger={
|
||||
<TradingDropdownTrigger className={triggerClasses}>
|
||||
<TradingButton {...triggerButtonProps}>
|
||||
{t(`Interval: ${intervalLabels[interval]}`)}
|
||||
{t('Interval: {{interval}}', {
|
||||
replace: { interval: intervalLabels[interval] },
|
||||
nsSeparator: '|',
|
||||
})}
|
||||
</TradingButton>
|
||||
</TradingDropdownTrigger>
|
||||
}
|
||||
|
2
libs/candles-chart/src/lib/use-t.ts
Normal file
2
libs/candles-chart/src/lib/use-t.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const useT = () => useTranslation('candles-chart').t;
|
15
libs/datagrid/__mocks__/react-i18next.ts
Normal file
15
libs/datagrid/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
const replace =
|
||||
replacements?.['replace'] && typeof replacements === 'object'
|
||||
? replacements?.['replace']
|
||||
: replacements;
|
||||
let translatedLabel = replacements?.['defaultValue'] || label;
|
||||
if (typeof replace === 'object' && replace !== null) {
|
||||
Object.keys(replace).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(`{{${key}}}`, replace[key]);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
@ -1,14 +1,12 @@
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import classNames from 'classnames';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
const defaultProps: AgGridReactProps = {
|
||||
enableCellTextSelection: true,
|
||||
overlayLoadingTemplate: t('Loading...'),
|
||||
overlayNoRowsTemplate: t('No data'),
|
||||
suppressCellFocus: true,
|
||||
suppressColumnMoveAnimation: true,
|
||||
};
|
||||
@ -26,6 +24,7 @@ export const AgGridThemed = ({
|
||||
style?: React.CSSProperties;
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const { theme } = useThemeSwitcher();
|
||||
|
||||
const wrapperClasses = classNames('vega-ag-grid', 'w-full h-full', {
|
||||
@ -38,6 +37,8 @@ export const AgGridThemed = ({
|
||||
<AgGridReact
|
||||
defaultColDef={defaultColDef}
|
||||
ref={gridRef}
|
||||
overlayLoadingTemplate={t('Loading...')}
|
||||
overlayNoRowsTemplate={t('No data')}
|
||||
{...defaultProps}
|
||||
{...props}
|
||||
/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { MouseEvent } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
interface OrderTypeCellProps {
|
||||
value?: Schema.OrderType;
|
||||
@ -17,6 +17,7 @@ export const OrderTypeCell = ({
|
||||
onClick,
|
||||
}: OrderTypeCellProps) => {
|
||||
const id = order?.market?.id ?? '';
|
||||
const t = useT();
|
||||
|
||||
const label = useMemo(() => {
|
||||
if (!order) {
|
||||
@ -25,7 +26,9 @@ export const OrderTypeCell = ({
|
||||
if (!value) return '-';
|
||||
|
||||
if (order?.icebergOrder) {
|
||||
return t('%s (Iceberg)', [Schema.OrderTypeMapping[value]]);
|
||||
return t('{{orderType}} (Iceberg)', {
|
||||
orderType: Schema.OrderTypeMapping[value],
|
||||
});
|
||||
}
|
||||
|
||||
if (order?.peggedOrder) {
|
||||
@ -37,14 +40,18 @@ export const OrderTypeCell = ({
|
||||
order.peggedOrder?.offset,
|
||||
order.market.decimalPlaces
|
||||
);
|
||||
return t('%s %s %s Peg limit', [reference, side, offset]);
|
||||
return t('{{reference}} {{side}} {{offset}} Peg limit', {
|
||||
reference,
|
||||
side,
|
||||
offset,
|
||||
});
|
||||
}
|
||||
|
||||
if (order?.liquidityProvision) {
|
||||
return t('Liquidity provision');
|
||||
}
|
||||
return Schema.OrderTypeMapping[value];
|
||||
}, [order, value]);
|
||||
}, [order, value, t]);
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(ev: MouseEvent<HTMLButtonElement>) => {
|
||||
|
@ -14,8 +14,8 @@ import {
|
||||
isValid,
|
||||
} from 'date-fns';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingInputError } from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
const defaultValue: DateRange = {};
|
||||
export interface DateRangeFilterProps extends IFilterParams {
|
||||
@ -27,6 +27,7 @@ export interface DateRangeFilterProps extends IFilterParams {
|
||||
|
||||
export const DateRangeFilter = forwardRef(
|
||||
(props: DateRangeFilterProps, ref) => {
|
||||
const t = useT();
|
||||
const defaultDates = props?.defaultValue || defaultValue;
|
||||
const [value, setValue] = useState<DateRange>(defaultDates);
|
||||
const valueRef = useRef<DateRange>(value);
|
||||
@ -119,8 +120,10 @@ export const DateRangeFilter = forwardRef(
|
||||
) {
|
||||
setError(
|
||||
t(
|
||||
'The earliest data that can be queried is %s days ago.',
|
||||
String(props.maxSubDays)
|
||||
'The earliest data that can be queried is {{maxSubDays}} days ago.',
|
||||
{
|
||||
maxSubDays: String(props.maxSubDays),
|
||||
}
|
||||
)
|
||||
);
|
||||
return false;
|
||||
@ -137,8 +140,8 @@ export const DateRangeFilter = forwardRef(
|
||||
) {
|
||||
setError(
|
||||
t(
|
||||
'The maximum time range that can be queried is %s days.',
|
||||
String(props.maxDaysRange)
|
||||
'The maximum time range that can be queried is {{maxDaysRange}} days.',
|
||||
{ maxDaysRange: String(props.maxDaysRange) }
|
||||
)
|
||||
);
|
||||
return false;
|
||||
|
@ -7,10 +7,11 @@ import {
|
||||
useRef,
|
||||
} from 'react';
|
||||
import type { IDoesFilterPassParams, IFilterParams } from 'ag-grid-community';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const SetFilter = forwardRef(
|
||||
(props: IFilterParams & { readonly?: boolean }, ref) => {
|
||||
const t = useT();
|
||||
const [value, setValue] = useState<string[]>([]);
|
||||
const valueRef = useRef(value);
|
||||
const { readonly } = props;
|
||||
|
@ -30,15 +30,6 @@ describe('Pagination', () => {
|
||||
expect(mockOnLoad).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders message for a single row', async () => {
|
||||
const mockOnLoad = jest.fn();
|
||||
const count = 1;
|
||||
render(<Pagination {...props} count={count} onLoad={mockOnLoad} />);
|
||||
expect(screen.getByText(`${count} row loaded`)).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Load more' }));
|
||||
expect(mockOnLoad).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders the data rentention message', () => {
|
||||
render(<Pagination {...props} showRetentionMessage={true} />);
|
||||
expect(screen.getByText(/data node retention/)).toBeInTheDocument();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { TradingButton as Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
|
||||
export const Pagination = ({
|
||||
count,
|
||||
@ -14,16 +14,19 @@ export const Pagination = ({
|
||||
hasDisplayedRows: boolean;
|
||||
showRetentionMessage: boolean;
|
||||
}) => {
|
||||
const t = useT();
|
||||
let rowMessage = '';
|
||||
|
||||
if (count && !pageInfo?.hasNextPage) {
|
||||
rowMessage = t('all %s rows loaded', count.toString());
|
||||
rowMessage = t('paginationAllLoaded', {
|
||||
replace: { count },
|
||||
defaultValue: 'All {{count}} rows loaded',
|
||||
});
|
||||
} else {
|
||||
if (count === 1) {
|
||||
rowMessage = t('%s row loaded', count.toString());
|
||||
} else {
|
||||
rowMessage = t('%s rows loaded', count.toString());
|
||||
}
|
||||
rowMessage = t('paginationLoaded', {
|
||||
replace: { count },
|
||||
defaultValue: '{{count}} rows loaded',
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
2
libs/datagrid/src/lib/use-t.ts
Normal file
2
libs/datagrid/src/lib/use-t.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const useT = () => useTranslation('datagrid').t;
|
14
libs/deal-ticket/__mocks__/react-i18next.ts
Normal file
14
libs/deal-ticket/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
let translatedLabel = label;
|
||||
if (typeof replacements === 'object' && replacements !== null) {
|
||||
Object.keys(replacements).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(
|
||||
`{{${key}}}`,
|
||||
replacements[key]
|
||||
);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Intent,
|
||||
VegaIcon,
|
||||
@ -7,6 +6,7 @@ import {
|
||||
Tooltip,
|
||||
TradingButton,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface Props {
|
||||
margin: string;
|
||||
@ -20,18 +20,19 @@ interface Props {
|
||||
}
|
||||
|
||||
export const MarginWarning = ({ margin, balance, asset, onDeposit }: Props) => {
|
||||
const t = useT();
|
||||
const description = (
|
||||
<div className="flex flex-col items-start gap-2 p-2">
|
||||
<p className="text-sm">
|
||||
{t('%s %s is currently required.', [
|
||||
addDecimalsFormatNumber(margin, asset.decimals),
|
||||
asset.symbol,
|
||||
])}
|
||||
{t('{{amount}} {{assetSymbol}} is currently required.', {
|
||||
amount: addDecimalsFormatNumber(margin, asset.decimals),
|
||||
assetSymbol: asset.symbol,
|
||||
})}
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
{t('You have only %s.', [
|
||||
addDecimalsFormatNumber(balance, asset.decimals),
|
||||
])}
|
||||
{t('You have only {{amount}}.', {
|
||||
amount: addDecimalsFormatNumber(balance, asset.decimals),
|
||||
})}
|
||||
</p>
|
||||
|
||||
<TradingButton
|
||||
@ -41,7 +42,7 @@ export const MarginWarning = ({ margin, balance, asset, onDeposit }: Props) => {
|
||||
data-testid="deal-ticket-deposit-dialog-button"
|
||||
type="button"
|
||||
>
|
||||
{t('Deposit %s', [asset.symbol])}
|
||||
{t('Deposit {{assetSymbol}}', { assetSymbol: asset.symbol })}
|
||||
</TradingButton>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface ZeroBalanceErrorProps {
|
||||
asset: {
|
||||
@ -13,6 +13,7 @@ export const ZeroBalanceError = ({
|
||||
asset,
|
||||
onDeposit,
|
||||
}: ZeroBalanceErrorProps) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Notification
|
||||
intent={Intent.Warning}
|
||||
@ -20,8 +21,8 @@ export const ZeroBalanceError = ({
|
||||
message={
|
||||
<>
|
||||
{t(
|
||||
'You need %s in your wallet to trade in this market. ',
|
||||
asset.symbol
|
||||
'You need {{symbol}} in your wallet to trade in this market.',
|
||||
asset
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import {
|
||||
useMarketPrice,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { DealTicket } from './deal-ticket';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface DealTicketContainerProps {
|
||||
marketId: string;
|
||||
@ -24,6 +24,7 @@ export const DealTicketContainer = ({
|
||||
marketId,
|
||||
...props
|
||||
}: DealTicketContainerProps) => {
|
||||
const t = useT();
|
||||
const showStopOrder = useDealTicketFormValues((state) =>
|
||||
isStopOrderType(state.formValues[marketId]?.type)
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { getAsset, getQuoteName } from '@vegaprotocol/markets';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
@ -41,8 +40,10 @@ import classNames from 'classnames';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { FeesBreakdown } from '../fees-breakdown';
|
||||
import { getTotalDiscountFactor, getDiscountedFee } from '../discounts';
|
||||
import { useT, ns } from '../../use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
const emptyValue = '-';
|
||||
export const emptyValue = '-';
|
||||
|
||||
export interface DealTicketFeeDetailsProps {
|
||||
assetSymbol: string;
|
||||
@ -57,6 +58,7 @@ export const DealTicketFeeDetails = ({
|
||||
market,
|
||||
isMarketInAuction,
|
||||
}: DealTicketFeeDetailsProps) => {
|
||||
const t = useT();
|
||||
const feeEstimate = useEstimateFees(order, isMarketInAuction);
|
||||
const asset = getAsset(market);
|
||||
const { decimals: assetDecimals, quantum } = asset;
|
||||
@ -98,7 +100,8 @@ export const DealTicketFeeDetails = ({
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
{t(
|
||||
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}. Fees estimated are "taker" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.`
|
||||
'An estimate of the most you would be expected to pay in fees, in the market\'s settlement asset {{assetSymbol}}. Fees estimated are "taker" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.',
|
||||
{ assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
<FeesBreakdown
|
||||
@ -136,6 +139,7 @@ export const DealTicketMarginDetails = ({
|
||||
positionEstimate,
|
||||
side,
|
||||
}: DealTicketMarginDetailsProps) => {
|
||||
const t = useT();
|
||||
const [breakdownDialog, setBreakdownDialog] = useState(false);
|
||||
const { pubKey: partyId } = useVegaWallet();
|
||||
const { data: currentMargins } = useDataProvider({
|
||||
@ -211,7 +215,11 @@ export const DealTicketMarginDetails = ({
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol)}
|
||||
labelDescription={t(
|
||||
'DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT',
|
||||
DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
/>
|
||||
);
|
||||
projectedMargin = (
|
||||
@ -228,7 +236,10 @@ export const DealTicketMarginDetails = ({
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={EST_TOTAL_MARGIN_TOOLTIP_TEXT}
|
||||
labelDescription={t(
|
||||
'EST_TOTAL_MARGIN_TOOLTIP_TEXT',
|
||||
EST_TOTAL_MARGIN_TOOLTIP_TEXT
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -307,7 +318,13 @@ export const DealTicketMarginDetails = ({
|
||||
className="flex items-center justify-between w-full gap-2"
|
||||
>
|
||||
<div className="flex items-center text-left gap-1">
|
||||
<Tooltip description={MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol)}>
|
||||
<Tooltip
|
||||
description={t(
|
||||
'MARGIN_DIFF_TOOLTIP_TEXT',
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
>
|
||||
<span className="text-muted">{t('Margin required')}</span>
|
||||
</Tooltip>
|
||||
|
||||
@ -346,15 +363,27 @@ export const DealTicketMarginDetails = ({
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={TOTAL_MARGIN_AVAILABLE(
|
||||
formatValue(generalAccountBalance, assetDecimals, quantum),
|
||||
formatValue(marginAccountBalance, assetDecimals, quantum),
|
||||
formatValue(
|
||||
currentMargins?.maintenanceLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
assetSymbol
|
||||
labelDescription={t(
|
||||
'TOTAL_MARGIN_AVAILABLE',
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
{
|
||||
generalAccountBalance: formatValue(
|
||||
generalAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginAccountBalance: formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginMaintenance: formatValue(
|
||||
currentMargins?.maintenanceLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
assetSymbol,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{deductionFromCollateral}
|
||||
@ -368,7 +397,10 @@ export const DealTicketMarginDetails = ({
|
||||
}
|
||||
value={formatValue(marginAccountBalance, assetDecimals)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={MARGIN_ACCOUNT_TOOLTIP_TEXT}
|
||||
labelDescription={t(
|
||||
'MARGIN_ACCOUNT_TOOLTIP_TEXT',
|
||||
MARGIN_ACCOUNT_TOOLTIP_TEXT
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
@ -386,16 +418,26 @@ export const DealTicketMarginDetails = ({
|
||||
symbol={quoteName}
|
||||
labelDescription={
|
||||
<>
|
||||
<span>{LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT}</span>{' '}
|
||||
<span>
|
||||
{t('For full details please see ')}
|
||||
<ExternalLink
|
||||
href={
|
||||
'https://github.com/vegaprotocol/specs/blob/master/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md'
|
||||
}
|
||||
>
|
||||
{t('liquidation price estimate documentation.')}
|
||||
</ExternalLink>
|
||||
{t(
|
||||
'LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT',
|
||||
LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT
|
||||
)}
|
||||
</span>{' '}
|
||||
<span>
|
||||
<Trans
|
||||
defaults="For full details please see <0>liquidation price estimate documentation</0>."
|
||||
components={[
|
||||
<ExternalLink
|
||||
href={
|
||||
'https://github.com/vegaprotocol/specs/blob/master/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md'
|
||||
}
|
||||
>
|
||||
liquidation price estimate documentation
|
||||
</ExternalLink>,
|
||||
]}
|
||||
ns={ns}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
|
@ -0,0 +1,354 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { getAsset, getQuoteName } from '@vegaprotocol/markets';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { AccountBreakdownDialog } from '@vegaprotocol/accounts';
|
||||
import { formatRange, formatValue } from '@vegaprotocol/utils';
|
||||
import { marketMarginDataProvider } from '@vegaprotocol/accounts';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT,
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT,
|
||||
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
|
||||
MARGIN_ACCOUNT_TOOLTIP_TEXT,
|
||||
} from '../../constants';
|
||||
import { KeyValue } from './key-value';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionChevron,
|
||||
AccordionPanel,
|
||||
ExternalLink,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import { useT, ns } from '../../use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
import type { DealTicketMarginDetailsProps } from './deal-ticket-fee-details';
|
||||
import { emptyValue } from './deal-ticket-fee-details';
|
||||
|
||||
export const DealTicketMarginDetails = ({
|
||||
marginAccountBalance,
|
||||
generalAccountBalance,
|
||||
assetSymbol,
|
||||
market,
|
||||
onMarketClick,
|
||||
positionEstimate,
|
||||
side,
|
||||
}: DealTicketMarginDetailsProps) => {
|
||||
const t = useT();
|
||||
const [breakdownDialog, setBreakdownDialog] = useState(false);
|
||||
const { pubKey: partyId } = useVegaWallet();
|
||||
const { data: currentMargins } = useDataProvider({
|
||||
dataProvider: marketMarginDataProvider,
|
||||
variables: { marketId: market.id, partyId: partyId || '' },
|
||||
skip: !partyId,
|
||||
});
|
||||
const liquidationEstimate = positionEstimate?.liquidation;
|
||||
const marginEstimate = positionEstimate?.margin;
|
||||
const totalBalance =
|
||||
BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0');
|
||||
const asset = getAsset(market);
|
||||
const { decimals: assetDecimals, quantum } = asset;
|
||||
let marginRequiredBestCase: string | undefined = undefined;
|
||||
let marginRequiredWorstCase: string | undefined = undefined;
|
||||
if (marginEstimate) {
|
||||
if (currentMargins) {
|
||||
marginRequiredBestCase = (
|
||||
BigInt(marginEstimate.bestCase.initialLevel) -
|
||||
BigInt(currentMargins.initialLevel)
|
||||
).toString();
|
||||
if (marginRequiredBestCase.startsWith('-')) {
|
||||
marginRequiredBestCase = '0';
|
||||
}
|
||||
marginRequiredWorstCase = (
|
||||
BigInt(marginEstimate.worstCase.initialLevel) -
|
||||
BigInt(currentMargins.initialLevel)
|
||||
).toString();
|
||||
if (marginRequiredWorstCase.startsWith('-')) {
|
||||
marginRequiredWorstCase = '0';
|
||||
}
|
||||
} else {
|
||||
marginRequiredBestCase = marginEstimate.bestCase.initialLevel;
|
||||
marginRequiredWorstCase = marginEstimate.worstCase.initialLevel;
|
||||
}
|
||||
}
|
||||
|
||||
const totalMarginAvailable = (
|
||||
currentMargins
|
||||
? totalBalance - BigInt(currentMargins.maintenanceLevel)
|
||||
: totalBalance
|
||||
).toString();
|
||||
|
||||
let deductionFromCollateral = null;
|
||||
let projectedMargin = null;
|
||||
if (marginAccountBalance) {
|
||||
const deductionFromCollateralBestCase =
|
||||
BigInt(marginEstimate?.bestCase.initialLevel ?? 0) -
|
||||
BigInt(marginAccountBalance);
|
||||
|
||||
const deductionFromCollateralWorstCase =
|
||||
BigInt(marginEstimate?.worstCase.initialLevel ?? 0) -
|
||||
BigInt(marginAccountBalance);
|
||||
|
||||
deductionFromCollateral = (
|
||||
<KeyValue
|
||||
indent
|
||||
label={t('Deduction from collateral')}
|
||||
value={formatRange(
|
||||
deductionFromCollateralBestCase > 0
|
||||
? deductionFromCollateralBestCase.toString()
|
||||
: '0',
|
||||
deductionFromCollateralWorstCase > 0
|
||||
? deductionFromCollateralWorstCase.toString()
|
||||
: '0',
|
||||
assetDecimals
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
deductionFromCollateralWorstCase > 0
|
||||
? deductionFromCollateralWorstCase.toString()
|
||||
: '0',
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT',
|
||||
DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
/>
|
||||
);
|
||||
projectedMargin = (
|
||||
<KeyValue
|
||||
label={t('Projected margin')}
|
||||
value={formatRange(
|
||||
marginEstimate?.bestCase.initialLevel,
|
||||
marginEstimate?.worstCase.initialLevel,
|
||||
assetDecimals
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
marginEstimate?.worstCase.initialLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'EST_TOTAL_MARGIN_TOOLTIP_TEXT',
|
||||
EST_TOTAL_MARGIN_TOOLTIP_TEXT
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let liquidationPriceEstimate = emptyValue;
|
||||
let liquidationPriceEstimateRange = emptyValue;
|
||||
|
||||
if (liquidationEstimate) {
|
||||
const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
|
||||
liquidationEstimate.bestCase.including_buy_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateBestCaseIncludingSellOrders = BigInt(
|
||||
liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateBestCase =
|
||||
side === Schema.Side.SIDE_BUY
|
||||
? liquidationEstimateBestCaseIncludingBuyOrders
|
||||
: liquidationEstimateBestCaseIncludingSellOrders;
|
||||
|
||||
const liquidationEstimateWorstCaseIncludingBuyOrders = BigInt(
|
||||
liquidationEstimate.worstCase.including_buy_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateWorstCaseIncludingSellOrders = BigInt(
|
||||
liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateWorstCase =
|
||||
side === Schema.Side.SIDE_BUY
|
||||
? liquidationEstimateWorstCaseIncludingBuyOrders
|
||||
: liquidationEstimateWorstCaseIncludingSellOrders;
|
||||
|
||||
liquidationPriceEstimate = formatValue(
|
||||
liquidationEstimateWorstCase.toString(),
|
||||
market.decimalPlaces,
|
||||
undefined,
|
||||
market.decimalPlaces
|
||||
);
|
||||
liquidationPriceEstimateRange = formatRange(
|
||||
(liquidationEstimateBestCase < liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
(liquidationEstimateBestCase > liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
market.decimalPlaces,
|
||||
undefined,
|
||||
market.decimalPlaces
|
||||
);
|
||||
}
|
||||
|
||||
const onAccountBreakdownDialogClose = useCallback(
|
||||
() => setBreakdownDialog(false),
|
||||
[]
|
||||
);
|
||||
|
||||
const quoteName = getQuoteName(market);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<Accordion>
|
||||
<AccordionPanel
|
||||
itemId="margin"
|
||||
trigger={
|
||||
<AccordionPrimitive.Trigger
|
||||
data-testid="accordion-toggle"
|
||||
className={classNames(
|
||||
'w-full pt-2',
|
||||
'flex items-center gap-2 text-xs',
|
||||
'group'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
data-testid={`deal-ticket-fee-margin-required`}
|
||||
key={'value-dropdown'}
|
||||
className="flex items-center justify-between w-full gap-2"
|
||||
>
|
||||
<div className="flex items-center text-left gap-1">
|
||||
<Tooltip
|
||||
description={t(
|
||||
'MARGIN_DIFF_TOOLTIP_TEXT',
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
{ assetSymbol }
|
||||
)}
|
||||
>
|
||||
<span className="text-muted">{t('Margin required')}</span>
|
||||
</Tooltip>
|
||||
|
||||
<AccordionChevron size={10} />
|
||||
</div>
|
||||
<Tooltip
|
||||
description={
|
||||
formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals
|
||||
) ?? '-'
|
||||
}
|
||||
>
|
||||
<div className="font-mono text-right">
|
||||
{formatValue(
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}{' '}
|
||||
{assetSymbol || ''}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</AccordionPrimitive.Trigger>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col w-full gap-2">
|
||||
<KeyValue
|
||||
label={t('Total margin available')}
|
||||
indent
|
||||
value={formatValue(totalMarginAvailable, assetDecimals)}
|
||||
formattedValue={formatValue(
|
||||
totalMarginAvailable,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'TOTAL_MARGIN_AVAILABLE',
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
{
|
||||
generalAccountBalance: formatValue(
|
||||
generalAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginAccountBalance: formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
marginMaintenance: formatValue(
|
||||
currentMargins?.maintenanceLevel,
|
||||
assetDecimals,
|
||||
quantum
|
||||
),
|
||||
assetSymbol,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{deductionFromCollateral}
|
||||
<KeyValue
|
||||
label={t('Current margin allocation')}
|
||||
indent
|
||||
onClick={
|
||||
generalAccountBalance
|
||||
? () => setBreakdownDialog(true)
|
||||
: undefined
|
||||
}
|
||||
value={formatValue(marginAccountBalance, assetDecimals)}
|
||||
symbol={assetSymbol}
|
||||
labelDescription={t(
|
||||
'MARGIN_ACCOUNT_TOOLTIP_TEXT',
|
||||
MARGIN_ACCOUNT_TOOLTIP_TEXT
|
||||
)}
|
||||
formattedValue={formatValue(
|
||||
marginAccountBalance,
|
||||
assetDecimals,
|
||||
quantum
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</AccordionPanel>
|
||||
</Accordion>
|
||||
{projectedMargin}
|
||||
<KeyValue
|
||||
label={t('Liquidation')}
|
||||
value={liquidationPriceEstimateRange}
|
||||
formattedValue={liquidationPriceEstimate}
|
||||
symbol={quoteName}
|
||||
labelDescription={
|
||||
<>
|
||||
<span>
|
||||
{t(
|
||||
'LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT',
|
||||
LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT
|
||||
)}
|
||||
</span>{' '}
|
||||
<span>
|
||||
<Trans
|
||||
defaults="For full details please see <0>liquidation price estimate documentation</0>."
|
||||
components={[
|
||||
<ExternalLink
|
||||
href={
|
||||
'https://github.com/vegaprotocol/specs/blob/master/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md'
|
||||
}
|
||||
>
|
||||
liquidation price estimate documentation
|
||||
</ExternalLink>,
|
||||
]}
|
||||
ns={ns}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{partyId && (
|
||||
<AccountBreakdownDialog
|
||||
assetId={breakdownDialog ? asset.id : undefined}
|
||||
partyId={partyId}
|
||||
onMarketClick={onMarketClick}
|
||||
onClose={onAccountBreakdownDialogClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -2,13 +2,13 @@ import { Controller, type Control } from 'react-hook-form';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export interface DealTicketSizeIcebergProps {
|
||||
control: Control<OrderFormValues>;
|
||||
@ -27,6 +27,7 @@ export const DealTicketSizeIceberg = ({
|
||||
size,
|
||||
peakSize,
|
||||
}: DealTicketSizeIcebergProps) => {
|
||||
const t = useT();
|
||||
const sizeStep = toDecimal(market?.positionDecimalPlaces);
|
||||
|
||||
const renderPeakSizeError = () => {
|
||||
@ -81,13 +82,15 @@ export const DealTicketSizeIceberg = ({
|
||||
required: t('You need to provide a peak size'),
|
||||
min: {
|
||||
value: sizeStep,
|
||||
message: t('Peak size cannot be lower than ' + sizeStep),
|
||||
message: t('Peak size cannot be lower than {{stepSize}}', {
|
||||
sizeStep,
|
||||
}),
|
||||
},
|
||||
max: {
|
||||
value: size,
|
||||
message: t(
|
||||
'Peak size cannot be greater than the size (%s) ',
|
||||
[size]
|
||||
'Peak size cannot be greater than the size ({{size}})',
|
||||
{ size }
|
||||
),
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'peakSize'),
|
||||
@ -138,14 +141,15 @@ export const DealTicketSizeIceberg = ({
|
||||
min: {
|
||||
value: sizeStep,
|
||||
message: t(
|
||||
'Minimum visible size cannot be lower than ' + sizeStep
|
||||
'Minimum visible size cannot be lower than {{sizeStep}}',
|
||||
{ sizeStep }
|
||||
),
|
||||
},
|
||||
max: peakSize && {
|
||||
value: peakSize,
|
||||
message: t(
|
||||
'Minimum visible size cannot be greater than the peak size (%s)',
|
||||
[peakSize]
|
||||
'Minimum visible size cannot be greater than the peak size ({{peakSize}})',
|
||||
{ peakSize }
|
||||
),
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'minimumVisibleSize'),
|
||||
|
@ -34,7 +34,6 @@ import {
|
||||
getQuoteName,
|
||||
type Market,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExpirySelector } from './expiry-selector';
|
||||
import { SideSelector } from './side-selector';
|
||||
import { timeInForceLabel } from '@vegaprotocol/orders';
|
||||
@ -60,6 +59,7 @@ import { NOTIONAL_SIZE_TOOLTIP_TEXT } from '../../constants';
|
||||
import { KeyValue } from './key-value';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { stopOrdersProvider } from '@vegaprotocol/orders';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export interface StopOrderProps {
|
||||
market: Market;
|
||||
@ -109,6 +109,7 @@ const Trigger = ({
|
||||
marketPrice?: string | null;
|
||||
decimalPlaces: number;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const triggerType = watch(oco ? 'ocoTriggerType' : 'triggerType');
|
||||
const triggerDirection = watch('triggerDirection');
|
||||
const isPriceTrigger = triggerType === 'price';
|
||||
@ -161,7 +162,9 @@ const Trigger = ({
|
||||
required: t('You need provide a price'),
|
||||
min: {
|
||||
value: priceStep,
|
||||
message: t('Price cannot be lower than ' + priceStep),
|
||||
message: t('Price cannot be lower than {{priceStep}}', {
|
||||
priceStep,
|
||||
}),
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
@ -244,8 +247,8 @@ const Trigger = ({
|
||||
min: {
|
||||
value: trailingPercentOffsetStep,
|
||||
message: t(
|
||||
'Trailing percent offset cannot be lower than ' +
|
||||
trailingPercentOffsetStep
|
||||
'Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}',
|
||||
{ trailingPercentOffsetStep }
|
||||
),
|
||||
},
|
||||
max: {
|
||||
@ -338,6 +341,7 @@ const Size = ({
|
||||
isLimitType: boolean;
|
||||
assetUnit?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Controller
|
||||
name={oco ? 'ocoSize' : 'size'}
|
||||
@ -346,7 +350,7 @@ const Size = ({
|
||||
required: t('You need to provide a size'),
|
||||
min: {
|
||||
value: sizeStep,
|
||||
message: t('Size cannot be lower than ' + sizeStep),
|
||||
message: t('Size cannot be lower than {{sizeStep}}', { sizeStep }),
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
@ -397,6 +401,7 @@ const Price = ({
|
||||
quoteName: string;
|
||||
oco?: boolean;
|
||||
}) => {
|
||||
const t = useT();
|
||||
if (watch(oco ? 'ocoType' : 'type') === Schema.OrderType.TYPE_MARKET) {
|
||||
return null;
|
||||
}
|
||||
@ -409,7 +414,7 @@ const Price = ({
|
||||
required: t('You need provide a price'),
|
||||
min: {
|
||||
value: priceStep,
|
||||
message: t('Price cannot be lower than ' + priceStep),
|
||||
message: t('Price cannot be lower than {{priceStep}}', { priceStep }),
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
@ -452,59 +457,65 @@ const TimeInForce = ({
|
||||
}: {
|
||||
control: Control<StopOrderFormValues>;
|
||||
oco?: boolean;
|
||||
}) => (
|
||||
<Controller
|
||||
name={oco ? 'ocoTimeInForce' : 'timeInForce'}
|
||||
control={control}
|
||||
render={({ field, fieldState }) => {
|
||||
const id = `order-tif${oco ? '-oco' : ''}`;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<FormGroup label={t('Time in force')} labelFor={id} compact={true}>
|
||||
<Select
|
||||
id={id}
|
||||
className="w-full"
|
||||
data-testid={id}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
>
|
||||
<option
|
||||
key={Schema.OrderTimeInForce.TIME_IN_FORCE_IOC}
|
||||
value={Schema.OrderTimeInForce.TIME_IN_FORCE_IOC}
|
||||
}) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Controller
|
||||
name={oco ? 'ocoTimeInForce' : 'timeInForce'}
|
||||
control={control}
|
||||
render={({ field, fieldState }) => {
|
||||
const id = `order-tif${oco ? '-oco' : ''}`;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<FormGroup label={t('Time in force')} labelFor={id} compact={true}>
|
||||
<Select
|
||||
id={id}
|
||||
className="w-full"
|
||||
data-testid={id}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_IOC)}
|
||||
</option>
|
||||
<option
|
||||
key={Schema.OrderTimeInForce.TIME_IN_FORCE_FOK}
|
||||
value={Schema.OrderTimeInForce.TIME_IN_FORCE_FOK}
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_FOK)}
|
||||
</option>
|
||||
</Select>
|
||||
</FormGroup>
|
||||
{fieldState.error && (
|
||||
<InputError testId={`stop-error-message-tif${oco ? '-oco' : ''}`}>
|
||||
{fieldState.error.message}
|
||||
</InputError>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
<option
|
||||
key={Schema.OrderTimeInForce.TIME_IN_FORCE_IOC}
|
||||
value={Schema.OrderTimeInForce.TIME_IN_FORCE_IOC}
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_IOC)}
|
||||
</option>
|
||||
<option
|
||||
key={Schema.OrderTimeInForce.TIME_IN_FORCE_FOK}
|
||||
value={Schema.OrderTimeInForce.TIME_IN_FORCE_FOK}
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_FOK)}
|
||||
</option>
|
||||
</Select>
|
||||
</FormGroup>
|
||||
{fieldState.error && (
|
||||
<InputError testId={`stop-error-message-tif${oco ? '-oco' : ''}`}>
|
||||
{fieldState.error.message}
|
||||
</InputError>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ReduceOnly = () => (
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<div>
|
||||
<Checkbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={t('Reduce only')}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
const ReduceOnly = () => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<div>
|
||||
<Checkbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={t('Reduce only')}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const NotionalAndFees = ({
|
||||
market,
|
||||
@ -522,6 +533,7 @@ const NotionalAndFees = ({
|
||||
> &
|
||||
Pick<StopOrderProps, 'market' | 'marketPrice'> &
|
||||
Pick<StopOrderFormValues, 'triggerType' | 'triggerPrice'>) => {
|
||||
const t = useT();
|
||||
const quoteName = getQuoteName(market);
|
||||
const asset = getAsset(market);
|
||||
const isPriceTrigger = triggerType === 'price';
|
||||
@ -548,7 +560,11 @@ const NotionalAndFees = ({
|
||||
value={formatValue(notionalSize, market.decimalPlaces)}
|
||||
formattedValue={formatValue(notionalSize, market.decimalPlaces)}
|
||||
symbol={quoteName}
|
||||
labelDescription={NOTIONAL_SIZE_TOOLTIP_TEXT(quoteName)}
|
||||
labelDescription={t(
|
||||
'NOTIONAL_SIZE_TOOLTIP_TEXT',
|
||||
NOTIONAL_SIZE_TOOLTIP_TEXT,
|
||||
{ quoteName }
|
||||
)}
|
||||
/>
|
||||
<DealTicketFeeDetails
|
||||
order={{
|
||||
@ -566,49 +582,55 @@ const NotionalAndFees = ({
|
||||
);
|
||||
};
|
||||
|
||||
const formatSizeAtPrice = ({
|
||||
assetUnit,
|
||||
decimalPlaces,
|
||||
positionDecimalPlaces,
|
||||
price,
|
||||
quoteName,
|
||||
side,
|
||||
size,
|
||||
type,
|
||||
}: Pick<StopOrderFormValues, 'price' | 'side' | 'size' | 'type'> & {
|
||||
assetUnit?: string;
|
||||
decimalPlaces: number;
|
||||
positionDecimalPlaces: number;
|
||||
quoteName: string;
|
||||
}) =>
|
||||
const formatSizeAtPrice = (
|
||||
{
|
||||
assetUnit,
|
||||
decimalPlaces,
|
||||
positionDecimalPlaces,
|
||||
price,
|
||||
quoteName,
|
||||
side,
|
||||
size,
|
||||
type,
|
||||
}: Pick<StopOrderFormValues, 'price' | 'side' | 'size' | 'type'> & {
|
||||
assetUnit?: string;
|
||||
decimalPlaces: number;
|
||||
positionDecimalPlaces: number;
|
||||
quoteName: string;
|
||||
},
|
||||
t: ReturnType<typeof useT>
|
||||
) =>
|
||||
`${formatValue(
|
||||
removeDecimal(size, positionDecimalPlaces),
|
||||
positionDecimalPlaces
|
||||
)} ${assetUnit} @ ${
|
||||
type === Schema.OrderType.TYPE_MARKET
|
||||
? 'market'
|
||||
? t('sizeAtPrice-market', 'market')
|
||||
: `${formatValue(
|
||||
removeDecimal(price || '0', decimalPlaces),
|
||||
decimalPlaces
|
||||
)} ${quoteName}`
|
||||
}`;
|
||||
const formatTrigger = ({
|
||||
decimalPlaces,
|
||||
triggerDirection,
|
||||
triggerPrice,
|
||||
triggerTrailingPercentOffset,
|
||||
triggerType,
|
||||
quoteName,
|
||||
}: Pick<
|
||||
StopOrderFormValues,
|
||||
| 'triggerDirection'
|
||||
| 'triggerType'
|
||||
| 'triggerPrice'
|
||||
| 'triggerTrailingPercentOffset'
|
||||
> & {
|
||||
decimalPlaces: number;
|
||||
quoteName: string;
|
||||
}) =>
|
||||
const formatTrigger = (
|
||||
{
|
||||
decimalPlaces,
|
||||
triggerDirection,
|
||||
triggerPrice,
|
||||
triggerTrailingPercentOffset,
|
||||
triggerType,
|
||||
quoteName,
|
||||
}: Pick<
|
||||
StopOrderFormValues,
|
||||
| 'triggerDirection'
|
||||
| 'triggerType'
|
||||
| 'triggerPrice'
|
||||
| 'triggerTrailingPercentOffset'
|
||||
> & {
|
||||
decimalPlaces: number;
|
||||
quoteName: string;
|
||||
},
|
||||
t: ReturnType<typeof useT>
|
||||
) =>
|
||||
`${
|
||||
triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE
|
||||
@ -620,9 +642,12 @@ const formatTrigger = ({
|
||||
removeDecimal(triggerPrice || '', decimalPlaces),
|
||||
decimalPlaces
|
||||
)} ${quoteName}`
|
||||
: `${(Number(triggerTrailingPercentOffset) || 0).toFixed(1)}% ${t(
|
||||
'trailing'
|
||||
)}`
|
||||
: t('{{triggerTrailingPercentOffset}}% trailing', {
|
||||
triggerTrailingPercentOffset: (
|
||||
Number(triggerTrailingPercentOffset) || 0
|
||||
).toFixed(1),
|
||||
})
|
||||
}
|
||||
}`;
|
||||
|
||||
const SubmitButton = ({
|
||||
@ -662,78 +687,97 @@ const SubmitButton = ({
|
||||
| 'type'
|
||||
> &
|
||||
Pick<StopOrderProps, 'market'> & { assetUnit?: string }) => {
|
||||
const t = useT();
|
||||
const quoteName = getQuoteName(market);
|
||||
const risesAbove =
|
||||
triggerDirection ===
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE;
|
||||
const subLabel = oco ? (
|
||||
<>
|
||||
{formatSizeAtPrice({
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price: risesAbove ? price : ocoPrice,
|
||||
quoteName,
|
||||
side,
|
||||
size: risesAbove ? size : ocoSize,
|
||||
type,
|
||||
})}{' '}
|
||||
{formatTrigger({
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection:
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
||||
triggerPrice: risesAbove ? triggerPrice : ocoTriggerPrice,
|
||||
triggerTrailingPercentOffset: risesAbove
|
||||
? triggerTrailingPercentOffset
|
||||
: ocoTriggerTrailingPercentOffset,
|
||||
triggerType: risesAbove ? triggerType : ocoTriggerType,
|
||||
})}
|
||||
{formatSizeAtPrice(
|
||||
{
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price: risesAbove ? price : ocoPrice,
|
||||
quoteName,
|
||||
side,
|
||||
size: risesAbove ? size : ocoSize,
|
||||
type,
|
||||
},
|
||||
t
|
||||
)}{' '}
|
||||
{formatTrigger(
|
||||
{
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection:
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
|
||||
triggerPrice: risesAbove ? triggerPrice : ocoTriggerPrice,
|
||||
triggerTrailingPercentOffset: risesAbove
|
||||
? triggerTrailingPercentOffset
|
||||
: ocoTriggerTrailingPercentOffset,
|
||||
triggerType: risesAbove ? triggerType : ocoTriggerType,
|
||||
},
|
||||
t
|
||||
)}
|
||||
<br />
|
||||
{formatSizeAtPrice({
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price: !risesAbove ? price : ocoPrice,
|
||||
quoteName,
|
||||
side,
|
||||
size: !risesAbove ? size : ocoSize,
|
||||
type: ocoType,
|
||||
})}{' '}
|
||||
{formatTrigger({
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection:
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW,
|
||||
triggerPrice: !risesAbove ? triggerPrice : ocoTriggerPrice,
|
||||
triggerTrailingPercentOffset: !risesAbove
|
||||
? triggerTrailingPercentOffset
|
||||
: ocoTriggerTrailingPercentOffset,
|
||||
triggerType: !risesAbove ? triggerType : ocoTriggerType,
|
||||
})}
|
||||
{formatSizeAtPrice(
|
||||
{
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price: !risesAbove ? price : ocoPrice,
|
||||
quoteName,
|
||||
side,
|
||||
size: !risesAbove ? size : ocoSize,
|
||||
type: ocoType,
|
||||
},
|
||||
t
|
||||
)}{' '}
|
||||
{formatTrigger(
|
||||
{
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection:
|
||||
Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW,
|
||||
triggerPrice: !risesAbove ? triggerPrice : ocoTriggerPrice,
|
||||
triggerTrailingPercentOffset: !risesAbove
|
||||
? triggerTrailingPercentOffset
|
||||
: ocoTriggerTrailingPercentOffset,
|
||||
triggerType: !risesAbove ? triggerType : ocoTriggerType,
|
||||
},
|
||||
t
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{formatSizeAtPrice({
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price,
|
||||
quoteName,
|
||||
side,
|
||||
size,
|
||||
type,
|
||||
})}
|
||||
{formatSizeAtPrice(
|
||||
{
|
||||
assetUnit,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
positionDecimalPlaces: market.positionDecimalPlaces,
|
||||
price,
|
||||
quoteName,
|
||||
side,
|
||||
size,
|
||||
type,
|
||||
},
|
||||
t
|
||||
)}
|
||||
<br />
|
||||
{t('Trigger')}{' '}
|
||||
{formatTrigger({
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection,
|
||||
triggerPrice,
|
||||
triggerTrailingPercentOffset,
|
||||
triggerType,
|
||||
})}
|
||||
{formatTrigger(
|
||||
{
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
quoteName,
|
||||
triggerDirection,
|
||||
triggerPrice,
|
||||
triggerTrailingPercentOffset,
|
||||
triggerType,
|
||||
},
|
||||
t
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
@ -756,6 +800,7 @@ const SubmitButton = ({
|
||||
};
|
||||
|
||||
export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
const t = useT();
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const setType = useDealTicketFormValues((state) => state.setType);
|
||||
const updateStoredFormValues = useDealTicketFormValues(
|
||||
@ -1087,14 +1132,14 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
|
||||
}
|
||||
id="expiryStrategy-submit"
|
||||
label={'Submit'}
|
||||
label={t('Submit')}
|
||||
/>
|
||||
<Radio
|
||||
value={
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_CANCELS
|
||||
}
|
||||
id="expiryStrategy-cancel"
|
||||
label={'Cancel'}
|
||||
label={t('Cancel')}
|
||||
/>
|
||||
</RadioGroup>
|
||||
);
|
||||
@ -1107,7 +1152,11 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
control={control}
|
||||
rules={{
|
||||
required: t('You need provide a expiry time/date'),
|
||||
validate: validateExpiration,
|
||||
validate: validateExpiration(
|
||||
t(
|
||||
'The expiry date that you have entered appears to be in the past'
|
||||
)
|
||||
),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
const { value, onChange: onSelect } = field;
|
||||
@ -1131,8 +1180,8 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
intent={Intent.Warning}
|
||||
testId={'stop-order-warning-limit'}
|
||||
message={t(
|
||||
'There is a limit of %s active stop orders per market. Orders submitted above the limit will be immediately rejected.',
|
||||
[MAX_NUMBER_OF_ACTIVE_STOP_ORDERS.toString()]
|
||||
'There is a limit of {{maxNumberOfOrders}} active stop orders per market. Orders submitted above the limit will be immediately rejected.',
|
||||
{ maxNumberOfOrders: MAX_NUMBER_OF_ACTIVE_STOP_ORDERS.toString() }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { type FormEventHandler } from 'react';
|
||||
import { memo, useCallback, useEffect, useRef, useMemo } from 'react';
|
||||
import { Controller, useController, useForm } from 'react-hook-form';
|
||||
import {
|
||||
DealTicketFeeDetails,
|
||||
DealTicketMarginDetails,
|
||||
} from './deal-ticket-fee-details';
|
||||
import { DealTicketFeeDetails } from './deal-ticket-fee-details';
|
||||
import { DealTicketMarginDetails } from './deal-ticket-margin-details';
|
||||
import { ExpirySelector } from './expiry-selector';
|
||||
import { SideSelector } from './side-selector';
|
||||
import { TimeInForceSelector } from './time-in-force-selector';
|
||||
@ -45,7 +42,6 @@ import {
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
validateExpiration,
|
||||
validateMarketState,
|
||||
validateMarketTradingMode,
|
||||
validateTimeInForce,
|
||||
validateType,
|
||||
@ -79,6 +75,7 @@ import noop from 'lodash/noop';
|
||||
import { isNonPersistentOrder } from '../../utils/time-in-force-persistance';
|
||||
import { KeyValue } from './key-value';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const REDUCE_ONLY_TOOLTIP =
|
||||
'"Reduce only" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.';
|
||||
@ -142,6 +139,7 @@ export const DealTicket = ({
|
||||
submit,
|
||||
onDeposit,
|
||||
}: DealTicketProps) => {
|
||||
const t = useT();
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const setType = useDealTicketFormValues((state) => state.setType);
|
||||
const storedFormValues = useDealTicketFormValues(
|
||||
@ -287,7 +285,28 @@ export const DealTicket = ({
|
||||
};
|
||||
}
|
||||
|
||||
const marketStateError = validateMarketState(marketState);
|
||||
let marketStateError: true | string = true;
|
||||
|
||||
if (
|
||||
[
|
||||
Schema.MarketState.STATE_SETTLED,
|
||||
Schema.MarketState.STATE_REJECTED,
|
||||
Schema.MarketState.STATE_TRADING_TERMINATED,
|
||||
Schema.MarketState.STATE_CANCELLED,
|
||||
Schema.MarketState.STATE_CLOSED,
|
||||
].includes(marketState)
|
||||
) {
|
||||
marketStateError = t(
|
||||
`This market is {{marketState}} and not accepting orders`,
|
||||
{
|
||||
marketState:
|
||||
marketState === Schema.MarketState.STATE_TRADING_TERMINATED
|
||||
? t('terminated')
|
||||
: t(Schema.MarketStateMapping[marketState]).toLowerCase(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (marketStateError !== true) {
|
||||
return {
|
||||
message: marketStateError,
|
||||
@ -307,7 +326,10 @@ export const DealTicket = ({
|
||||
};
|
||||
}
|
||||
|
||||
const marketTradingModeError = validateMarketTradingMode(marketTradingMode);
|
||||
const marketTradingModeError = validateMarketTradingMode(
|
||||
marketTradingMode,
|
||||
t('Trading terminated')
|
||||
);
|
||||
if (marketTradingModeError !== true) {
|
||||
return {
|
||||
message: marketTradingModeError,
|
||||
@ -317,6 +339,7 @@ export const DealTicket = ({
|
||||
|
||||
return undefined;
|
||||
}, [
|
||||
t,
|
||||
marketState,
|
||||
marketTradingMode,
|
||||
generalAccountBalance,
|
||||
@ -404,7 +427,7 @@ export const DealTicket = ({
|
||||
required: t('You need to provide a size'),
|
||||
min: {
|
||||
value: sizeStep,
|
||||
message: t('Size cannot be lower than ' + sizeStep),
|
||||
message: t('Size cannot be lower than {{sizeStep}}', { sizeStep }),
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
deps: ['peakSize', 'minimumVisibleSize'],
|
||||
@ -440,7 +463,9 @@ export const DealTicket = ({
|
||||
required: t('You need provide a price'),
|
||||
min: {
|
||||
value: priceStep,
|
||||
message: t('Price cannot be lower than ' + priceStep),
|
||||
message: t('Price cannot be lower than {{priceStep}}', {
|
||||
priceStep,
|
||||
}),
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
@ -477,7 +502,11 @@ export const DealTicket = ({
|
||||
value={formatValue(notionalSize, market.decimalPlaces)}
|
||||
formattedValue={formatValue(notionalSize, market.decimalPlaces)}
|
||||
symbol={quoteName}
|
||||
labelDescription={NOTIONAL_SIZE_TOOLTIP_TEXT(quoteName)}
|
||||
labelDescription={t(
|
||||
'NOTIONAL_SIZE_TOOLTIP_TEXT',
|
||||
NOTIONAL_SIZE_TOOLTIP_TEXT,
|
||||
{ quoteName }
|
||||
)}
|
||||
/>
|
||||
<DealTicketFeeDetails
|
||||
order={
|
||||
@ -501,7 +530,7 @@ export const DealTicket = ({
|
||||
<TimeInForceSelector
|
||||
value={field.value}
|
||||
orderType={type}
|
||||
onSelect={(value) => {
|
||||
onSelect={(value: Schema.OrderTimeInForce) => {
|
||||
// If GTT is selected and no expiresAt time is set, or its
|
||||
// behind current time then reset the value to current time
|
||||
const now = Date.now();
|
||||
@ -534,7 +563,11 @@ export const DealTicket = ({
|
||||
control={control}
|
||||
rules={{
|
||||
required: t('You need provide a expiry time/date'),
|
||||
validate: validateExpiration,
|
||||
validate: validateExpiration(
|
||||
t(
|
||||
'The expiry date that you have entered appears to be in the past'
|
||||
)
|
||||
),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<ExpirySelector
|
||||
@ -629,10 +662,10 @@ export const DealTicket = ({
|
||||
<Tooltip
|
||||
description={
|
||||
<p>
|
||||
{t(`Trade only a fraction of the order size at once.
|
||||
After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away.
|
||||
For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each.
|
||||
Note that the full volume of the order is not hidden and is still reflected in the order book.`)}{' '}
|
||||
{t(
|
||||
'ICEBERG_TOOLTIP',
|
||||
'Trade only a fraction of the order size at once. After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. Note that the full volume of the order is not hidden and is still reflected in the order book.'
|
||||
)}{' '}
|
||||
<ExternalLink href={DocsLinks?.ICEBERG_ORDERS}>
|
||||
{t('Find out more')}
|
||||
</ExternalLink>{' '}
|
||||
@ -731,13 +764,14 @@ interface SummaryMessageProps {
|
||||
export const NoWalletWarning = ({
|
||||
isReadOnly,
|
||||
}: Pick<SummaryMessageProps, 'isReadOnly'>) => {
|
||||
const t = useT();
|
||||
if (isReadOnly) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<InputError testId="deal-ticket-error-message-summary">
|
||||
{
|
||||
{t(
|
||||
'You need to connect your own wallet to start trading on this market'
|
||||
}
|
||||
)}
|
||||
</InputError>
|
||||
</div>
|
||||
);
|
||||
@ -756,6 +790,7 @@ const SummaryMessage = memo(
|
||||
pubKey,
|
||||
onDeposit,
|
||||
}: SummaryMessageProps) => {
|
||||
const t = useT();
|
||||
// Specific error UI for if balance is so we can
|
||||
// render a deposit dialog
|
||||
if (isReadOnly || !pubKey) {
|
||||
|
@ -4,8 +4,8 @@ import {
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useRef } from 'react';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface ExpirySelectorProps {
|
||||
value?: string;
|
||||
@ -18,6 +18,7 @@ export const ExpirySelector = ({
|
||||
onSelect,
|
||||
errorMessage,
|
||||
}: ExpirySelectorProps) => {
|
||||
const t = useT();
|
||||
const minDateRef = useRef(new Date());
|
||||
|
||||
return (
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import * as RadioGroup from '@radix-ui/react-radio-group';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface SideSelectorProps {
|
||||
value: Schema.Side;
|
||||
onValueChange: (side: Schema.Side) => void;
|
||||
}
|
||||
|
||||
const toggles = [
|
||||
{ label: t('Long'), value: Schema.Side.SIDE_BUY },
|
||||
{ label: t('Short'), value: Schema.Side.SIDE_SELL },
|
||||
];
|
||||
|
||||
export const SideSelector = (props: SideSelectorProps) => {
|
||||
const t = useT();
|
||||
const toggles = [
|
||||
{ label: t('Long'), value: Schema.Side.SIDE_BUY },
|
||||
{ label: t('Short'), value: Schema.Side.SIDE_SELL },
|
||||
];
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
name="order-side"
|
||||
|
@ -2,15 +2,16 @@ import {
|
||||
TradingFormGroup,
|
||||
TradingInputError,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
TextChildrenTooltip as Tooltip,
|
||||
SimpleGrid,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { timeInForceLabel } from '@vegaprotocol/orders';
|
||||
import { compileGridData } from '../trading-mode-tooltip';
|
||||
import { MarketModeValidationType } from '../../constants';
|
||||
import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useT, ns } from '../../use-t';
|
||||
|
||||
interface TimeInForceSelectorProps {
|
||||
value: Schema.OrderTimeInForce;
|
||||
@ -36,6 +37,7 @@ export const TimeInForceSelector = ({
|
||||
marketData,
|
||||
errorMessage,
|
||||
}: TimeInForceSelectorProps) => {
|
||||
const t = useT();
|
||||
const options =
|
||||
orderType === Schema.OrderType.TYPE_LIMIT
|
||||
? typeLimitOptions
|
||||
@ -44,25 +46,30 @@ export const TimeInForceSelector = ({
|
||||
const renderError = (errorType: string) => {
|
||||
if (errorType === MarketModeValidationType.Auction) {
|
||||
return t(
|
||||
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||
'Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
|
||||
);
|
||||
}
|
||||
|
||||
if (errorType === MarketModeValidationType.LiquidityMonitoringAuction) {
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(market, marketData)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
{t(
|
||||
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||
)}
|
||||
<Trans
|
||||
i18nKey="TIME_IN_FORCE_SELECTOR_LIQUIDITY_MONITORING_AUCTION"
|
||||
defaults="This market is in auction until it reaches <0>sufficient liquidity</0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
|
||||
ns={ns}
|
||||
components={[
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid
|
||||
grid={compileGridData(t, market, marketData, t)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
sufficient liquidity
|
||||
</Tooltip>,
|
||||
]}
|
||||
t={t}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -70,18 +77,23 @@ export const TimeInForceSelector = ({
|
||||
if (errorType === MarketModeValidationType.PriceMonitoringAuction) {
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(market, marketData)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
{t(
|
||||
`Until the auction ends, you can only place GFA, GTT, or GTC limit orders`
|
||||
)}
|
||||
<Trans
|
||||
i18nKey="TIME_IN_FORCE_SELECTOR_PRICE_MONITORING_AUCTION"
|
||||
defaults="This market is in auction due to <0>high price volatility</0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
|
||||
ns={ns}
|
||||
components={[
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid
|
||||
grid={compileGridData(t, market, marketData, t)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
high price volatility
|
||||
</Tooltip>,
|
||||
]}
|
||||
t={t}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
TradingInputError,
|
||||
SimpleGrid,
|
||||
Tooltip,
|
||||
TextChildrenTooltip as Tooltip,
|
||||
TradingDropdown,
|
||||
TradingDropdownContent,
|
||||
TradingDropdownItemIndicator,
|
||||
@ -12,7 +12,6 @@ import {
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
||||
import { compileGridData } from '../trading-mode-tooltip';
|
||||
import { MarketModeValidationType } from '../../constants';
|
||||
@ -20,6 +19,8 @@ import { DealTicketType } from '../../hooks/use-form-values';
|
||||
import * as RadioGroup from '@radix-ui/react-radio-group';
|
||||
import classNames from 'classnames';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useT, ns } from '../../use-t';
|
||||
|
||||
interface TypeSelectorProps {
|
||||
value: DealTicketType;
|
||||
@ -29,19 +30,28 @@ interface TypeSelectorProps {
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
const toggles = [
|
||||
{ label: t('Limit'), value: DealTicketType.Limit },
|
||||
{ label: t('Market'), value: DealTicketType.Market },
|
||||
];
|
||||
const options = [
|
||||
{ label: t('Stop Limit'), value: DealTicketType.StopLimit },
|
||||
{ label: t('Stop Market'), value: DealTicketType.StopMarket },
|
||||
];
|
||||
const useToggles = () => {
|
||||
const t = useT();
|
||||
return [
|
||||
{ label: t('Limit'), value: DealTicketType.Limit },
|
||||
{ label: t('Market'), value: DealTicketType.Market },
|
||||
];
|
||||
};
|
||||
const useOptions = () => {
|
||||
const t = useT();
|
||||
return [
|
||||
{ label: t('Stop Limit'), value: DealTicketType.StopLimit },
|
||||
{ label: t('Stop Market'), value: DealTicketType.StopMarket },
|
||||
];
|
||||
};
|
||||
|
||||
export const TypeToggle = ({
|
||||
value,
|
||||
onValueChange,
|
||||
}: Pick<TypeSelectorProps, 'onValueChange' | 'value'>) => {
|
||||
const t = useT();
|
||||
const options = useOptions();
|
||||
const toggles = useToggles();
|
||||
const selectedOption = options.find((t) => t.value === value);
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
@ -84,7 +94,7 @@ export const TypeToggle = ({
|
||||
>
|
||||
<button className="flex gap-1">
|
||||
<span className="text-ellipsis whitespace-nowrap shrink overflow-hidden">
|
||||
{t(selectedOption ? selectedOption.label : 'Stop')}
|
||||
{selectedOption ? selectedOption.label : t('Stop')}
|
||||
</span>
|
||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
|
||||
</button>
|
||||
@ -107,7 +117,7 @@ export const TypeToggle = ({
|
||||
id={`order-type-${itemValue}`}
|
||||
data-testid={`order-type-${itemValue}`}
|
||||
>
|
||||
{t(label)}
|
||||
{label}
|
||||
<TradingDropdownItemIndicator />
|
||||
</TradingDropdownRadioItem>
|
||||
))}
|
||||
@ -127,6 +137,7 @@ export const TypeSelector = ({
|
||||
marketData,
|
||||
errorMessage,
|
||||
}: TypeSelectorProps) => {
|
||||
const t = useT();
|
||||
const renderError = (errorType: MarketModeValidationType) => {
|
||||
if (errorType === MarketModeValidationType.Auction) {
|
||||
return t('Only limit orders are permitted when market is in auction');
|
||||
@ -135,16 +146,21 @@ export const TypeSelector = ({
|
||||
if (errorType === MarketModeValidationType.LiquidityMonitoringAuction) {
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction until it reaches')}{' '}
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(market, marketData)} />
|
||||
}
|
||||
>
|
||||
<span>{t('sufficient liquidity')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
{t('Only limit orders are permitted when market is in auction')}
|
||||
<Trans
|
||||
i18nKey="TYPE_SELECTOR_LIQUIDITY_MONITORING_AUCTION"
|
||||
defaults="This market is in auction until it reaches <0>sufficient liquidity</0>. Only limit orders are permitted when market is in auction."
|
||||
ns={ns}
|
||||
components={[
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(t, market, marketData)} />
|
||||
}
|
||||
>
|
||||
sufficient liquidity
|
||||
</Tooltip>,
|
||||
]}
|
||||
t={t}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -152,16 +168,21 @@ export const TypeSelector = ({
|
||||
if (errorType === MarketModeValidationType.PriceMonitoringAuction) {
|
||||
return (
|
||||
<span>
|
||||
{t('This market is in auction due to')}{' '}
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(market, marketData)} />
|
||||
}
|
||||
>
|
||||
<span>{t('high price volatility')}</span>
|
||||
</Tooltip>
|
||||
{'. '}
|
||||
{t('Only limit orders are permitted when market is in auction')}
|
||||
<Trans
|
||||
i18nKey="TYPE_SELECTOR_PRICE_MONITORING_AUCTION"
|
||||
defaults="This market is in auction due to <0>high price volatility</0>. Only limit orders are permitted when market is in auction."
|
||||
ns={ns}
|
||||
components={[
|
||||
<Tooltip
|
||||
description={
|
||||
<SimpleGrid grid={compileGridData(t, market, marketData)} />
|
||||
}
|
||||
>
|
||||
sufficient liquidity
|
||||
</Tooltip>,
|
||||
]}
|
||||
t={t}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumberPercentage,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { getDiscountedFee } from '../discounts';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
const formatValue = (
|
||||
value: string | number | null | undefined,
|
||||
@ -59,6 +59,7 @@ export const FeesBreakdown = ({
|
||||
referralDiscountFactor?: string;
|
||||
volumeDiscountFactor?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
if (!fees || !totalFeeAmount || totalFeeAmount === '0') return null;
|
||||
|
||||
const { discountedFee: discountedInfrastructureFee } = getDiscountedFee(
|
||||
|
@ -2,15 +2,16 @@ import {
|
||||
getDateTimeFormat,
|
||||
addDecimalsFormatNumber,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Link as UILink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { SimpleGridProps } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getAsset, type Market, type MarketData } from '@vegaprotocol/markets';
|
||||
import type { useT } from '../../use-t';
|
||||
|
||||
export const compileGridData = (
|
||||
t: ReturnType<typeof useT>,
|
||||
market: Pick<
|
||||
Market,
|
||||
'id' | 'tradableInstrument' | 'decimalPlaces' | 'positionDecimalPlaces'
|
||||
|
@ -4,11 +4,11 @@ import classNames from 'classnames';
|
||||
import { useProposalOfMarketQuery } from '@vegaprotocol/proposals';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ExternalLink, SimpleGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { compileGridData } from './compile-grid-data';
|
||||
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
type TradingModeTooltipProps = {
|
||||
marketId?: string;
|
||||
@ -23,6 +23,7 @@ export const TradingModeTooltip = ({
|
||||
skip,
|
||||
skipGrid,
|
||||
}: TradingModeTooltipProps) => {
|
||||
const t = useT();
|
||||
const { data: market } = useMarket(marketId);
|
||||
const { data: marketData } = useStaticMarketData(marketId, skip);
|
||||
const { marketTradingMode, trigger } = marketData || {};
|
||||
@ -43,7 +44,7 @@ export const TradingModeTooltip = ({
|
||||
);
|
||||
|
||||
const compiledGrid =
|
||||
!skipGrid && compileGridData(market, marketData, onSelect);
|
||||
!skipGrid && compileGridData(t, market, marketData, onSelect);
|
||||
|
||||
switch (marketTradingMode) {
|
||||
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
|
||||
@ -88,10 +89,9 @@ export const TradingModeTooltip = ({
|
||||
>
|
||||
{`${
|
||||
Schema.MarketTradingModeMapping[marketTradingMode]
|
||||
}: ${t(
|
||||
'Closing on %s',
|
||||
getDateTimeFormat().format(enactmentDate)
|
||||
)}`}
|
||||
}: ${t('Closing on {{time}}', {
|
||||
time: getDateTimeFormat().format(enactmentDate),
|
||||
})}`}
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
@ -118,7 +118,7 @@ export const TradingModeTooltip = ({
|
||||
return (
|
||||
<section data-testid="trading-mode-suspended-via-governance">
|
||||
{t(
|
||||
`This market has been suspended via a governance vote and can be resumed or terminated by further votes.`
|
||||
'This market has been suspended via a governance vote and can be resumed or terminated by further votes.'
|
||||
)}
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.MARKET_LIFECYCLE} className="ml-1">
|
||||
|
@ -1,75 +1,32 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
export const EST_MARGIN_TOOLTIP_TEXT = `A fraction of the notional position size, in the market's settlement asset {{assetSymbol}}, to cover any potential losses that you may incur. For example, for a notional size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.`;
|
||||
export const EST_TOTAL_MARGIN_TOOLTIP_TEXT =
|
||||
'Estimated total margin that will cover open positions, active orders and this order.';
|
||||
export const MARGIN_ACCOUNT_TOOLTIP_TEXT = 'Margin account balance.';
|
||||
export const MARGIN_DIFF_TOOLTIP_TEXT =
|
||||
"The additional margin required for your new position (taking into account volume and open orders), compared to your current margin. Measured in the market's settlement asset ({{assetSymbol}}).";
|
||||
export const DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT =
|
||||
'To cover the required margin, this amount will be drawn from your general ({{assetSymbol}}) account.';
|
||||
|
||||
export const EST_MARGIN_TOOLTIP_TEXT = (settlementAsset: string) =>
|
||||
t(
|
||||
`A fraction of the notional position size, in the market's settlement asset %s, to cover any potential losses that you may incur.
|
||||
export const TOTAL_MARGIN_AVAILABLE =
|
||||
'Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).';
|
||||
|
||||
For example, for a notional size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.`,
|
||||
[settlementAsset]
|
||||
);
|
||||
export const EST_TOTAL_MARGIN_TOOLTIP_TEXT = t(
|
||||
'Estimated total margin that will cover open positions, active orders and this order.'
|
||||
);
|
||||
export const MARGIN_ACCOUNT_TOOLTIP_TEXT = t('Margin account balance.');
|
||||
export const MARGIN_DIFF_TOOLTIP_TEXT = (settlementAsset: string) =>
|
||||
t(
|
||||
"The additional margin required for your new position (taking into account volume and open orders), compared to your current margin. Measured in the market's settlement asset (%s).",
|
||||
[settlementAsset]
|
||||
);
|
||||
export const DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT = (
|
||||
settlementAsset: string
|
||||
) =>
|
||||
t(
|
||||
'To cover the required margin, this amount will be drawn from your general (%s) account.',
|
||||
[settlementAsset]
|
||||
);
|
||||
export const CONTRACTS_MARGIN_TOOLTIP_TEXT =
|
||||
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.';
|
||||
export const EST_CLOSEOUT_TOOLTIP_TEXT =
|
||||
'If the price drops below this number, measured in the market price quote unit {{quote}}, you will be closed out, based on your current position and account balance.';
|
||||
export const NOTIONAL_SIZE_TOOLTIP_TEXT =
|
||||
'The notional size represents the position size in the settlement asset {{quoteName}} of the futures contract. This is calculated by multiplying the number of contracts by the prices of the contract. For example 10 contracts traded at a price of $50 has a notional size of $500.';
|
||||
export const EST_FEES_TOOLTIP_TEXT =
|
||||
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.';
|
||||
|
||||
export const TOTAL_MARGIN_AVAILABLE = (
|
||||
generalAccountBalance: string,
|
||||
marginAccountBalance: string,
|
||||
marginMaintenance: string,
|
||||
settlementAsset: string
|
||||
) =>
|
||||
t(
|
||||
'Total margin available = general %s balance (%s) + margin balance (%s) - maintenance level (%s).',
|
||||
[
|
||||
settlementAsset,
|
||||
`${generalAccountBalance} ${settlementAsset}`,
|
||||
`${marginAccountBalance} ${settlementAsset}`,
|
||||
`${marginMaintenance} ${settlementAsset}`,
|
||||
]
|
||||
);
|
||||
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT =
|
||||
'This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.';
|
||||
|
||||
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
|
||||
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
|
||||
);
|
||||
export const EST_CLOSEOUT_TOOLTIP_TEXT = (quote: string) =>
|
||||
t(
|
||||
`If the price drops below this number, measured in the market price quote unit %s, you will be closed out, based on your current position and account balance.`,
|
||||
[quote]
|
||||
);
|
||||
export const NOTIONAL_SIZE_TOOLTIP_TEXT = (settlementAsset: string) =>
|
||||
t(
|
||||
`The notional size represents the position size in the settlement asset %s of the futures contract. This is calculated by multiplying the number of contracts by the prices of the contract.
|
||||
export const EST_SLIPPAGE =
|
||||
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.';
|
||||
|
||||
For example 10 contracts traded at a price of $50 has a notional size of $500.`,
|
||||
[settlementAsset]
|
||||
);
|
||||
export const EST_FEES_TOOLTIP_TEXT = t(
|
||||
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
|
||||
);
|
||||
|
||||
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t(
|
||||
'This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
|
||||
);
|
||||
|
||||
export const EST_SLIPPAGE = t(
|
||||
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.'
|
||||
);
|
||||
|
||||
export const ERROR_SIZE_DECIMAL = t(
|
||||
'The size field accepts up to X decimal places.'
|
||||
);
|
||||
export const ERROR_SIZE_DECIMAL =
|
||||
'The size field accepts up to X decimal places.';
|
||||
|
||||
export enum MarketModeValidationType {
|
||||
PriceMonitoringAuction = 'PriceMonitoringAuction',
|
||||
|
3
libs/deal-ticket/src/use-t.ts
Normal file
3
libs/deal-ticket/src/use-t.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const ns = 'deal-ticket';
|
||||
export const useT = () => useTranslation(ns).t;
|
@ -1,6 +1,5 @@
|
||||
export * from './get-default-order';
|
||||
export * from './validate-expiration';
|
||||
export * from './validate-market-state';
|
||||
export * from './validate-market-trading-mode';
|
||||
export * from './validate-time-in-force';
|
||||
export * from './validate-type';
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Validate } from 'react-hook-form';
|
||||
|
||||
export const validateExpiration: Validate<string | undefined, object> = (
|
||||
value?: string
|
||||
) => {
|
||||
const now = new Date();
|
||||
const valueAsDate = value ? new Date(value) : now;
|
||||
if (now > valueAsDate) {
|
||||
return t('The expiry date that you have entered appears to be in the past');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
export const validateExpiration: (
|
||||
errorMessage: string
|
||||
) => Validate<string | undefined, object> =
|
||||
(errorMessage: string) => (value?: string) => {
|
||||
const now = new Date();
|
||||
const valueAsDate = value ? new Date(value) : now;
|
||||
if (now > valueAsDate) {
|
||||
return errorMessage;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -1,37 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
|
||||
export const validateMarketState = (state: MarketState) => {
|
||||
if (
|
||||
[
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_REJECTED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
MarketState.STATE_CANCELLED,
|
||||
MarketState.STATE_CLOSED,
|
||||
].includes(state)
|
||||
) {
|
||||
return t(
|
||||
`This market is ${marketTranslations(state)} and not accepting orders`
|
||||
);
|
||||
}
|
||||
|
||||
if (state === MarketState.STATE_PROPOSED) {
|
||||
return t(
|
||||
`This market is ${marketTranslations(
|
||||
state
|
||||
)} and only accepting liquidity commitment orders`
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const marketTranslations = (marketState: MarketState) => {
|
||||
switch (marketState) {
|
||||
case MarketState.STATE_TRADING_TERMINATED:
|
||||
return t('terminated');
|
||||
default:
|
||||
return t(MarketStateMapping[marketState]).toLowerCase();
|
||||
}
|
||||
};
|
@ -1,11 +1,11 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||
|
||||
export const validateMarketTradingMode = (
|
||||
marketTradingMode: MarketTradingMode
|
||||
marketTradingMode: MarketTradingMode,
|
||||
errorMessage: string
|
||||
) => {
|
||||
if (marketTradingMode === MarketTradingMode.TRADING_MODE_NO_TRADING) {
|
||||
return t('Trading terminated');
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
14
libs/deposits/__mocks__/react-i18next.ts
Normal file
14
libs/deposits/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
let translatedLabel = label;
|
||||
if (typeof replacements === 'object' && replacements !== null) {
|
||||
Object.keys(replacements).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(
|
||||
`{{${key}}}`,
|
||||
replacements[key]
|
||||
);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
@ -1,6 +1,5 @@
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { EtherscanLink } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
formatNumber,
|
||||
@ -11,6 +10,7 @@ import type { EthStoredTxState } from '@vegaprotocol/web3';
|
||||
import { EthTxStatus, useEthTransactionStore } from '@vegaprotocol/web3';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { DepositBalances } from './use-deposit-balances';
|
||||
import { useT } from './use-t';
|
||||
|
||||
interface ApproveNotificationProps {
|
||||
isActive: boolean;
|
||||
@ -33,6 +33,7 @@ export const ApproveNotification = ({
|
||||
approveTxId,
|
||||
intent = Intent.Warning,
|
||||
}: ApproveNotificationProps) => {
|
||||
const t = useT();
|
||||
const tx = useEthTransactionStore((state) => {
|
||||
return state.transactions.find((t) => t?.id === approveTxId);
|
||||
});
|
||||
@ -55,12 +56,14 @@ export const ApproveNotification = ({
|
||||
intent={intent}
|
||||
testId="approve-default"
|
||||
message={t(
|
||||
'Before you can make a deposit of your chosen asset, %s, you need to approve its use in your Ethereum wallet',
|
||||
selectedAsset?.symbol
|
||||
'Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet',
|
||||
{ assetSymbol: selectedAsset?.symbol }
|
||||
)}
|
||||
buttonProps={{
|
||||
size: 'small',
|
||||
text: t('Approve %s', selectedAsset?.symbol),
|
||||
text: t('Approve {{assetSymbol}}', {
|
||||
assetSymbol: selectedAsset?.symbol,
|
||||
}),
|
||||
action: onApprove,
|
||||
dataTestId: 'approve-submit',
|
||||
}}
|
||||
@ -72,13 +75,14 @@ export const ApproveNotification = ({
|
||||
<Notification
|
||||
intent={intent}
|
||||
testId="reapprove-default"
|
||||
message={t(
|
||||
'Approve again to deposit more than %s',
|
||||
formatNumber(balances.allowance.toString())
|
||||
)}
|
||||
message={t('Approve again to deposit more than {{allowance}}', {
|
||||
allowance: formatNumber(balances.allowance.toString()),
|
||||
})}
|
||||
buttonProps={{
|
||||
size: 'small',
|
||||
text: t('Approve %s', selectedAsset?.symbol),
|
||||
text: t('Approve {{assetSymbol}}', {
|
||||
assetSymbol: selectedAsset?.symbol,
|
||||
}),
|
||||
action: onApprove,
|
||||
dataTestId: 'reapprove-submit',
|
||||
}}
|
||||
@ -132,6 +136,7 @@ const ApprovalTxFeedback = ({
|
||||
selectedAsset: Asset;
|
||||
allowance?: BigNumber;
|
||||
}) => {
|
||||
const t = useT();
|
||||
if (!tx) return null;
|
||||
|
||||
const txLink = tx.txHash && (
|
||||
@ -161,8 +166,8 @@ const ApprovalTxFeedback = ({
|
||||
intent={Intent.Warning}
|
||||
testId="approve-requested"
|
||||
message={t(
|
||||
'Go to your Ethereum wallet and approve the transaction to enable the use of %s',
|
||||
selectedAsset?.symbol
|
||||
'Go to your Ethereum wallet and approve the transaction to enable the use of {{assetSymbol}}',
|
||||
{ assetSymbol: selectedAsset?.symbol }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -179,8 +184,8 @@ const ApprovalTxFeedback = ({
|
||||
<>
|
||||
<p>
|
||||
{t(
|
||||
'Your %s approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit',
|
||||
selectedAsset?.symbol
|
||||
'Your {{assetSymbol}} approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit',
|
||||
{ assetSymbol: selectedAsset?.symbol }
|
||||
)}{' '}
|
||||
</p>
|
||||
{txLink && <p>{txLink}</p>}
|
||||
@ -209,10 +214,15 @@ const ApprovalTxFeedback = ({
|
||||
message={
|
||||
<>
|
||||
<p>
|
||||
{t('You approved deposits of up to %s %s.', [
|
||||
selectedAsset?.symbol,
|
||||
approvedAllowanceValue,
|
||||
])}
|
||||
{t(
|
||||
'You approved deposits of up to {{assetSymbol}} {{approvedAllowanceValue}}.',
|
||||
[
|
||||
{
|
||||
assetSymbol: selectedAsset?.symbol,
|
||||
approvedAllowanceValue,
|
||||
},
|
||||
]
|
||||
)}
|
||||
</p>
|
||||
{txLink && <p>{txLink}</p>}
|
||||
</>
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
isAssetTypeERC20,
|
||||
formatNumber,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
@ -43,6 +42,7 @@ import { FaucetNotification } from './faucet-notification';
|
||||
import { ApproveNotification } from './approve-notification';
|
||||
import { usePersistentDeposit } from './use-persistent-deposit';
|
||||
import { AssetBalance } from './asset-balance';
|
||||
import { useT } from './use-t';
|
||||
|
||||
interface FormFields {
|
||||
asset: string;
|
||||
@ -84,6 +84,7 @@ export const DepositForm = ({
|
||||
approveTxId,
|
||||
isFaucetable,
|
||||
}: DepositFormProps) => {
|
||||
const t = useT();
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const openDialog = useWeb3ConnectStore((store) => store.open);
|
||||
const { isActive, account } = useWeb3React();
|
||||
@ -284,7 +285,7 @@ export const DepositForm = ({
|
||||
)}
|
||||
{isActive && isFaucetable && selectedAsset && (
|
||||
<UseButton onClick={submitFaucet}>
|
||||
{t(`Get ${selectedAsset.symbol}`)}
|
||||
{t('Get {{assetSymbol}}', { assetSymbol: selectedAsset.symbol })}
|
||||
</UseButton>
|
||||
)}
|
||||
{!errors.asset?.message && selectedAsset && (
|
||||
@ -325,11 +326,11 @@ export const DepositForm = ({
|
||||
const allowance = new BigNumber(balances?.allowance || 0);
|
||||
if (value.isGreaterThan(allowance)) {
|
||||
return t(
|
||||
"You can't deposit more than your approved deposit amount, %s %s",
|
||||
[
|
||||
formatNumber(allowance.toString()),
|
||||
selectedAsset?.symbol || ' ',
|
||||
]
|
||||
"You can't deposit more than your approved deposit amount, {{amount}} {{assetSymbol}}",
|
||||
{
|
||||
amount: formatNumber(allowance.toString()),
|
||||
assetSymbol: selectedAsset?.symbol || ' ',
|
||||
}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
@ -347,11 +348,11 @@ export const DepositForm = ({
|
||||
|
||||
if (value.isGreaterThan(lifetimeLimit)) {
|
||||
return t(
|
||||
"You can't deposit more than your remaining deposit allowance, %s %s",
|
||||
[
|
||||
formatNumber(lifetimeLimit.toString()),
|
||||
selectedAsset?.symbol || ' ',
|
||||
]
|
||||
"You can't deposit more than your remaining deposit allowance, {{amount}} {{assetSymbol}}",
|
||||
{
|
||||
amount: formatNumber(lifetimeLimit.toString()),
|
||||
assetSymbol: selectedAsset?.symbol || ' ',
|
||||
}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
@ -361,8 +362,11 @@ export const DepositForm = ({
|
||||
const balance = new BigNumber(balances?.balance || 0);
|
||||
if (value.isGreaterThan(balance)) {
|
||||
return t(
|
||||
"You can't deposit more than you have in your Ethereum wallet, %s %s",
|
||||
[formatNumber(balance), selectedAsset?.symbol || ' ']
|
||||
"You can't deposit more than you have in your Ethereum wallet, {{amount}} {{assetSymbol}}",
|
||||
{
|
||||
amount: formatNumber(balance),
|
||||
assetSymbol: selectedAsset?.symbol || ' ',
|
||||
}
|
||||
);
|
||||
}
|
||||
return true;
|
||||
@ -419,6 +423,7 @@ interface FormButtonProps {
|
||||
}
|
||||
|
||||
const FormButton = ({ approved, selectedAsset }: FormButtonProps) => {
|
||||
const t = useT();
|
||||
const { isActive, chainId } = useWeb3React();
|
||||
const desiredChainId = useWeb3ConnectStore((store) => store.desiredChainId);
|
||||
const invalidChain = isActive && chainId !== desiredChainId;
|
||||
@ -429,9 +434,9 @@ const FormButton = ({ approved, selectedAsset }: FormButtonProps) => {
|
||||
<Notification
|
||||
intent={Intent.Danger}
|
||||
testId="chain-error"
|
||||
message={t(
|
||||
`This app only works on ${getChainName(desiredChainId)}.`
|
||||
)}
|
||||
message={t('This app only works on {{chainId}}.', {
|
||||
chainId: getChainName(desiredChainId),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -464,6 +469,7 @@ const DisconnectEthereumButton = ({
|
||||
}: {
|
||||
onDisconnect: () => void;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const { connector } = useWeb3React();
|
||||
const [, , removeEagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT);
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
@ -495,6 +501,7 @@ export const AddressField = ({
|
||||
input,
|
||||
onChange,
|
||||
}: AddressInputProps) => {
|
||||
const t = useT();
|
||||
const [isInput, setIsInput] = useState(() => {
|
||||
if (pubKeys && pubKeys.length <= 1) {
|
||||
return true;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
KeyValueTable,
|
||||
@ -8,6 +7,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type BigNumber from 'bignumber.js';
|
||||
import { formatNumber, quantumDecimalPlaces } from '@vegaprotocol/utils';
|
||||
import { useT } from './use-t';
|
||||
|
||||
// Note: all of the values here are with correct asset's decimals
|
||||
// See `libs/deposits/src/lib/use-deposit-balances.ts`
|
||||
@ -29,6 +29,7 @@ export const DepositLimits = ({
|
||||
allowance,
|
||||
exempt,
|
||||
}: DepositLimitsProps) => {
|
||||
const t = useT();
|
||||
const limits = [
|
||||
{
|
||||
key: 'BALANCE_AVAILABLE',
|
||||
@ -51,19 +52,23 @@ export const DepositLimits = ({
|
||||
<>
|
||||
<p>
|
||||
{t(
|
||||
'VEGA has a lifetime deposit limit of %s %s per address. This can be changed through governance',
|
||||
[formatNumber(max.toString()), asset.symbol]
|
||||
'VEGA has a lifetime deposit limit of {{amount}} {{assetSymbol}} per address. This can be changed through governance',
|
||||
{
|
||||
amount: formatNumber(max.toString()),
|
||||
assetSymbol: asset.symbol,
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{t(
|
||||
'To date, %s %s has been deposited from this Ethereum address, so you can deposit up to %s %s more.',
|
||||
[
|
||||
formatNumber(deposited.toString()),
|
||||
asset.symbol,
|
||||
formatNumber(max.minus(deposited).toString()),
|
||||
asset.symbol,
|
||||
]
|
||||
'To date, {{currentDeposit}} {{assetSymbol}} has been deposited from this Ethereum address, so you can deposit up to {{remainingDeposit}} {{assetSymbol}} more.',
|
||||
{
|
||||
currentDeposit: formatNumber(deposited.toString()),
|
||||
assetSymbol: asset.symbol,
|
||||
remainingDeposit: formatNumber(
|
||||
max.minus(deposited).toString()
|
||||
),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</>
|
||||
@ -89,8 +94,8 @@ export const DepositLimits = ({
|
||||
description={
|
||||
<p>
|
||||
{t(
|
||||
'The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve %s again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.',
|
||||
asset.symbol
|
||||
'The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.',
|
||||
{ assetSymbol: asset.symbol }
|
||||
)}
|
||||
</p>
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import { EtherscanLink } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { EthTxStatus, useEthTransactionStore } from '@vegaprotocol/web3';
|
||||
import { getFaucetError } from './get-faucet-error';
|
||||
|
||||
interface FaucetNotificationProps {
|
||||
import { useGetFaucetError } from './get-faucet-error';
|
||||
import { useT } from './use-t';
|
||||
export interface FaucetNotificationProps {
|
||||
isActive: boolean;
|
||||
selectedAsset?: Asset;
|
||||
faucetTxId: number | null;
|
||||
@ -14,14 +13,20 @@ interface FaucetNotificationProps {
|
||||
/**
|
||||
* Render a notification for the faucet transaction
|
||||
*/
|
||||
|
||||
export const FaucetNotification = ({
|
||||
isActive,
|
||||
selectedAsset,
|
||||
faucetTxId,
|
||||
}: FaucetNotificationProps) => {
|
||||
const t = useT();
|
||||
const tx = useEthTransactionStore((state) => {
|
||||
return state.transactions.find((t) => t?.id === faucetTxId);
|
||||
});
|
||||
const errorMessage = useGetFaucetError(
|
||||
tx?.status === EthTxStatus.Error ? tx.error : null,
|
||||
selectedAsset?.symbol
|
||||
);
|
||||
|
||||
if (!isActive) {
|
||||
return null;
|
||||
@ -34,9 +39,7 @@ export const FaucetNotification = ({
|
||||
if (!tx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tx.status === EthTxStatus.Error) {
|
||||
const errorMessage = getFaucetError(tx.error, selectedAsset.symbol);
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<Notification
|
||||
@ -55,7 +58,8 @@ export const FaucetNotification = ({
|
||||
intent={Intent.Warning}
|
||||
testId="faucet-requested"
|
||||
message={t(
|
||||
`Confirm the transaction in your Ethereum wallet to use the ${selectedAsset?.symbol} faucet`
|
||||
'Confirm the transaction in your Ethereum wallet to use the {{assetSymbol}} faucet',
|
||||
{ assetSymbol: selectedAsset?.symbol }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -72,8 +76,8 @@ export const FaucetNotification = ({
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
'Your request for funds from the %s faucet is being confirmed by the Ethereum network',
|
||||
selectedAsset.symbol
|
||||
'Your request for funds from the {{assetSymbol}} faucet is being confirmed by the Ethereum network',
|
||||
{ assetSymbol: selectedAsset.symbol }
|
||||
)}{' '}
|
||||
</p>
|
||||
{tx.txHash && (
|
||||
@ -100,8 +104,10 @@ export const FaucetNotification = ({
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
'%s has been deposited in your Ethereum wallet',
|
||||
selectedAsset.symbol
|
||||
'{{assetSymbol}} has been deposited in your Ethereum wallet',
|
||||
{
|
||||
assetSymbol: selectedAsset.symbol,
|
||||
}
|
||||
)}{' '}
|
||||
</p>
|
||||
{tx.txHash && (
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { TxError } from '@vegaprotocol/web3';
|
||||
import { useT } from './use-t';
|
||||
|
||||
export const getFaucetError = (error: TxError | null, symbol: string) => {
|
||||
export const useGetFaucetError = (error: TxError | null, symbol?: string) => {
|
||||
const t = useT();
|
||||
const reasonMap: {
|
||||
[reason: string]: string;
|
||||
} = {
|
||||
'faucet not enabled': t(
|
||||
'The %s faucet is not available at this time',
|
||||
symbol
|
||||
'The {{symbol}} faucet is not available at this time',
|
||||
{ symbol: symbol || '' }
|
||||
),
|
||||
'must wait faucetCallLimit between faucet calls': t(
|
||||
'You have exceeded the maximum number of faucet attempts allowed'
|
||||
@ -20,5 +21,5 @@ export const getFaucetError = (error: TxError | null, symbol: string) => {
|
||||
// to a non generic error message
|
||||
return error && 'reason' in error && reasonMap[error.reason]
|
||||
? reasonMap[error.reason]
|
||||
: t('Faucet of %s failed', symbol);
|
||||
: t('Faucet of {{symbol}} failed', { symbol: symbol || '' });
|
||||
};
|
||||
|
2
libs/deposits/src/lib/use-t.ts
Normal file
2
libs/deposits/src/lib/use-t.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const useT = () => useTranslation('deposits').t;
|
15
libs/environment/__mocks__/react-i18next.ts
Normal file
15
libs/environment/__mocks__/react-i18next.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export const useTranslation = () => ({
|
||||
t: (label: string, replacements?: Record<string, string>) => {
|
||||
const replace =
|
||||
replacements?.replace && typeof replacements === 'object'
|
||||
? replacements?.replace
|
||||
: replacements;
|
||||
let translatedLabel = replacements?.defaultValue || label;
|
||||
if (typeof replace === 'object' && replace !== null) {
|
||||
Object.keys(replace).forEach((key) => {
|
||||
translatedLabel = translatedLabel.replace(`{{${key}}}`, replace[key]);
|
||||
});
|
||||
}
|
||||
return translatedLabel;
|
||||
},
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { ETHERSCAN_ADDRESS, ETHERSCAN_TX, useEtherscanLink } from '../hooks';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
export const EtherscanLink = ({
|
||||
address,
|
||||
@ -12,6 +12,7 @@ export const EtherscanLink = ({
|
||||
address?: string;
|
||||
tx?: string;
|
||||
} & ComponentProps<typeof ExternalLink>) => {
|
||||
const t = useT();
|
||||
const etherscanLink = useEtherscanLink();
|
||||
let href = '';
|
||||
|
||||
|
@ -1,13 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useEnvironment } from '../../hooks/use-environment';
|
||||
import {
|
||||
NetworkSwitcher,
|
||||
envNameMapping,
|
||||
envTriggerMapping,
|
||||
envDescriptionMapping,
|
||||
} from './';
|
||||
import { NetworkSwitcher } from './';
|
||||
import { Networks } from '../../';
|
||||
|
||||
jest.mock('../../hooks/use-environment');
|
||||
@ -33,10 +27,10 @@ describe('Network switcher', () => {
|
||||
|
||||
it.each`
|
||||
network | label
|
||||
${Networks.CUSTOM} | ${envTriggerMapping[Networks.CUSTOM]}
|
||||
${Networks.DEVNET} | ${envTriggerMapping[Networks.DEVNET]}
|
||||
${Networks.TESTNET} | ${envTriggerMapping[Networks.TESTNET]}
|
||||
${Networks.MAINNET} | ${envTriggerMapping[Networks.MAINNET]}
|
||||
${Networks.CUSTOM} | ${'Custom'}
|
||||
${Networks.DEVNET} | ${'Devnet'}
|
||||
${Networks.TESTNET} | ${'Fairground'}
|
||||
${Networks.MAINNET} | ${'Mainnet'}
|
||||
`(
|
||||
'displays the correct selection label for $network',
|
||||
({ network, label }) => {
|
||||
@ -69,13 +63,13 @@ describe('Network switcher', () => {
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
let links = screen.getAllByRole('link');
|
||||
|
||||
expect(links[0]).toHaveTextContent(envNameMapping[Networks.MAINNET]);
|
||||
expect(links[1]).toHaveTextContent(envNameMapping[Networks.TESTNET]);
|
||||
expect(links[0]).not.toHaveTextContent(t('current'));
|
||||
expect(links[1]).not.toHaveTextContent(t('current'));
|
||||
expect(links[0]).not.toHaveTextContent(t('not available'));
|
||||
expect(links[1]).not.toHaveTextContent(t('not available'));
|
||||
expect(links[2]).toHaveTextContent(t('Propose a network parameter change'));
|
||||
expect(links[0]).toHaveTextContent('Mainnet');
|
||||
expect(links[1]).toHaveTextContent('Fairground testnet');
|
||||
expect(links[0]).not.toHaveTextContent('current');
|
||||
expect(links[1]).not.toHaveTextContent('current');
|
||||
expect(links[0]).not.toHaveTextContent('not available');
|
||||
expect(links[1]).not.toHaveTextContent('not available');
|
||||
expect(links[2]).toHaveTextContent('Propose a network parameter change');
|
||||
|
||||
const menuitems = screen.getAllByRole('menuitem');
|
||||
|
||||
@ -110,8 +104,8 @@ describe('Network switcher', () => {
|
||||
|
||||
const links = screen.getAllByRole('link');
|
||||
|
||||
expect(links[0]).toHaveTextContent(envNameMapping[Networks.MAINNET]);
|
||||
expect(links[0]).toHaveTextContent(t('current'));
|
||||
expect(links[0]).toHaveTextContent('Mainnet');
|
||||
expect(links[0]).toHaveTextContent('current');
|
||||
});
|
||||
|
||||
it('displays the correct selected network on the default dropdown view when it does not have an associated url', async () => {
|
||||
@ -131,8 +125,8 @@ describe('Network switcher', () => {
|
||||
|
||||
const links = screen.getAllByRole('link');
|
||||
|
||||
expect(links[0]).toHaveTextContent(envNameMapping[Networks.MAINNET]);
|
||||
expect(links[0]).toHaveTextContent(t('current'));
|
||||
expect(links[0]).toHaveTextContent('Mainnet');
|
||||
expect(links[0]).toHaveTextContent('current');
|
||||
});
|
||||
|
||||
it('displays the correct state for a network without url on the default dropdown view', async () => {
|
||||
@ -151,13 +145,17 @@ describe('Network switcher', () => {
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
const links = screen.getAllByRole('link');
|
||||
|
||||
expect(links[0]).toHaveTextContent(envNameMapping[Networks.MAINNET]);
|
||||
expect(links[0]).toHaveTextContent(t('not available'));
|
||||
expect(links[0]).toHaveTextContent('Mainnet');
|
||||
expect(links[0]).toHaveTextContent('not available');
|
||||
});
|
||||
|
||||
it.each([Networks.MAINNET, Networks.TESTNET, Networks.DEVNET])(
|
||||
it.each([
|
||||
{ network: Networks.MAINNET, name: 'Mainnet' },
|
||||
{ network: Networks.TESTNET, name: 'Fairground testnet' },
|
||||
{ network: Networks.DEVNET, name: 'Devnet' },
|
||||
])(
|
||||
'displays the advanced view in the correct state',
|
||||
async (network) => {
|
||||
async ({ network, name }) => {
|
||||
const VEGA_NETWORKS: Record<Networks, string | undefined> = {
|
||||
[Networks.CUSTOM]: undefined,
|
||||
[Networks.MAINNET]: 'https://main.net',
|
||||
@ -178,25 +176,14 @@ describe('Network switcher', () => {
|
||||
await userEvent.click(screen.getByTestId('network-switcher'));
|
||||
|
||||
expect(
|
||||
await screen.findByRole('menuitem', { name: t('Advanced') })
|
||||
await screen.findByRole('menuitem', { name: 'Advanced' })
|
||||
).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('menuitem', { name: t('Advanced') })
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByText(envDescriptionMapping[network])
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('link', {
|
||||
name: new RegExp(`^${envNameMapping[network]}`),
|
||||
})
|
||||
).toBeInTheDocument();
|
||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Advanced' }));
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('link', {
|
||||
name: new RegExp(`^${envNameMapping[network]}`),
|
||||
name: new RegExp(`^${name}`),
|
||||
})
|
||||
);
|
||||
|
||||
@ -224,15 +211,13 @@ describe('Network switcher', () => {
|
||||
render(<NetworkSwitcher />);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
await userEvent.click(
|
||||
screen.getByRole('menuitem', { name: t('Advanced') })
|
||||
);
|
||||
await userEvent.click(screen.getByRole('menuitem', { name: 'Advanced' }));
|
||||
|
||||
const label = screen.getByText(`(${t('current')})`);
|
||||
const label = screen.getByText('(current)');
|
||||
|
||||
expect(label).toBeInTheDocument();
|
||||
expect(label.parentNode?.parentNode?.firstElementChild).toHaveTextContent(
|
||||
envNameMapping[selectedNetwork]
|
||||
'Devnet'
|
||||
);
|
||||
});
|
||||
|
||||
@ -262,7 +247,7 @@ describe('Network switcher', () => {
|
||||
|
||||
expect(label).toBeInTheDocument();
|
||||
expect(label.parentNode?.parentNode?.firstElementChild).toHaveTextContent(
|
||||
envNameMapping[Networks.MAINNET]
|
||||
'Mainnet'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -13,32 +12,44 @@ import { useEnvironment } from '../../hooks/use-environment';
|
||||
import { Networks } from '../../types';
|
||||
import { DApp, TOKEN_NEW_NETWORK_PARAM_PROPOSAL, useLinks } from '../../hooks';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const envNameMapping: Record<Networks, string> = {
|
||||
[Networks.VALIDATOR_TESTNET]: t('VALIDATOR_TESTNET'),
|
||||
[Networks.MAINNET_MIRROR]: t('Mainnet-mirror'),
|
||||
[Networks.CUSTOM]: t('Custom'),
|
||||
[Networks.DEVNET]: t('Devnet'),
|
||||
[Networks.STAGNET1]: t('Stagnet'),
|
||||
[Networks.TESTNET]: t('Fairground testnet'),
|
||||
[Networks.MAINNET]: t('Mainnet'),
|
||||
export const useEnvNameMapping: () => Record<Networks, string> = () => {
|
||||
const t = useT();
|
||||
return {
|
||||
[Networks.VALIDATOR_TESTNET]: t('VALIDATOR_TESTNET', {
|
||||
contextSeparator: '|',
|
||||
}),
|
||||
[Networks.MAINNET_MIRROR]: t('Mainnet-mirror'),
|
||||
[Networks.CUSTOM]: t('Custom'),
|
||||
[Networks.DEVNET]: t('Devnet'),
|
||||
[Networks.STAGNET1]: t('Stagnet'),
|
||||
[Networks.TESTNET]: t('Fairground testnet'),
|
||||
[Networks.MAINNET]: t('Mainnet'),
|
||||
};
|
||||
};
|
||||
|
||||
export const envTriggerMapping: Record<Networks, string> = {
|
||||
...envNameMapping,
|
||||
[Networks.TESTNET]: t('Fairground'),
|
||||
export const useEnvTriggerMapping: () => Record<Networks, string> = () => {
|
||||
const t = useT();
|
||||
return {
|
||||
...useEnvNameMapping(),
|
||||
[Networks.TESTNET]: t('Fairground'),
|
||||
};
|
||||
};
|
||||
|
||||
export const envDescriptionMapping: Record<Networks, string> = {
|
||||
[Networks.CUSTOM]: '',
|
||||
[Networks.VALIDATOR_TESTNET]: t('The validator deployed testnet'),
|
||||
[Networks.MAINNET_MIRROR]: t('The mainnet-mirror network'),
|
||||
[Networks.DEVNET]: t('The latest Vega code auto-deployed'),
|
||||
[Networks.STAGNET1]: t('A release candidate for the staging environment'),
|
||||
[Networks.TESTNET]: t(
|
||||
'Public testnet run by the Vega team, often used for incentives'
|
||||
),
|
||||
[Networks.MAINNET]: t('The vega mainnet'),
|
||||
export const useEnvDescriptionMapping: () => Record<Networks, string> = () => {
|
||||
const t = useT();
|
||||
return {
|
||||
[Networks.CUSTOM]: '',
|
||||
[Networks.VALIDATOR_TESTNET]: t('The validator deployed testnet'),
|
||||
[Networks.MAINNET_MIRROR]: t('The mainnet-mirror network'),
|
||||
[Networks.DEVNET]: t('The latest Vega code auto-deployed'),
|
||||
[Networks.STAGNET1]: t('A release candidate for the staging environment'),
|
||||
[Networks.TESTNET]: t(
|
||||
'Public testnet run by the Vega team, often used for incentives'
|
||||
),
|
||||
[Networks.MAINNET]: t('The vega mainnet'),
|
||||
};
|
||||
};
|
||||
|
||||
const standardNetworkKeys = [Networks.MAINNET, Networks.TESTNET];
|
||||
@ -53,10 +64,11 @@ type NetworkLabelProps = {
|
||||
isAvailable: boolean;
|
||||
};
|
||||
|
||||
const getLabelText = ({
|
||||
const useLabelText = ({
|
||||
isCurrent = false,
|
||||
isAvailable = false,
|
||||
}: NetworkLabelProps) => {
|
||||
const t = useT();
|
||||
if (isCurrent) {
|
||||
return ` (${t('current')})`;
|
||||
}
|
||||
@ -71,7 +83,7 @@ const NetworkLabel = ({
|
||||
isAvailable = false,
|
||||
}: NetworkLabelProps) => (
|
||||
<span className="text-vega-dark-300 dark:text-vega-light-300">
|
||||
{getLabelText({ isCurrent, isAvailable })}
|
||||
{useLabelText({ isCurrent, isAvailable })}
|
||||
</span>
|
||||
);
|
||||
|
||||
@ -86,6 +98,7 @@ export const NetworkSwitcher = ({
|
||||
currentNetwork,
|
||||
className,
|
||||
}: NetworkSwitcherProps) => {
|
||||
const t = useT();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const tokenLink = useLinks(DApp.Governance);
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
@ -102,6 +115,9 @@ export const NetworkSwitcher = ({
|
||||
);
|
||||
|
||||
const current = currentNetwork || VEGA_ENV;
|
||||
const envTriggerMapping = useEnvTriggerMapping();
|
||||
const envNameMapping = useEnvNameMapping();
|
||||
const envDescriptionMapping = useEnvDescriptionMapping();
|
||||
|
||||
return (
|
||||
<DropdownMenu
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useNodeSwitcherStore } from '../../hooks/use-node-switcher-store';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const NodeFailure = ({
|
||||
title,
|
||||
@ -10,6 +10,7 @@ export const NodeFailure = ({
|
||||
error?: string | null;
|
||||
}) => {
|
||||
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||
const t = useT();
|
||||
return (
|
||||
<Splash>
|
||||
<div className="text-center">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
type LayoutCellProps = {
|
||||
label?: string;
|
||||
@ -17,6 +17,7 @@ export const LayoutCell = ({
|
||||
children,
|
||||
dataTestId,
|
||||
}: LayoutCellProps) => {
|
||||
const t = useT();
|
||||
const classes = [
|
||||
'lg:text-right flex justify-between lg:block',
|
||||
'my-2 lg:my-0',
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { isValidUrl } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
@ -15,8 +14,10 @@ import { LayoutCell } from './layout-cell';
|
||||
import { LayoutRow } from './layout-row';
|
||||
import { ApolloWrapper } from './apollo-wrapper';
|
||||
import { RowData } from './row-data';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
const t = useT();
|
||||
const { nodes, setUrl, status, VEGA_ENV, VEGA_URL } = useEnvironment(
|
||||
(store) => ({
|
||||
status: store.status,
|
||||
@ -73,7 +74,8 @@ export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
<div>
|
||||
<p className="mb-2 text-sm text-center">
|
||||
{t(
|
||||
`This app will only work on ${VEGA_ENV}. Select a node to connect to.`
|
||||
'This app will only work on {{VEGA_ENV}}. Select a node to connect to.',
|
||||
{ VEGA_ENV }
|
||||
)}
|
||||
</p>
|
||||
<TradingRadioGroup
|
||||
@ -153,6 +155,7 @@ const CustomRowWrapper = ({
|
||||
nodeRadio,
|
||||
onBlockHeight,
|
||||
}: CustomRowWrapperProps) => {
|
||||
const t = useT();
|
||||
const [displayCustom, setDisplayCustom] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const showInput = nodeRadio === CUSTOM_NODE_KEY || nodes.length <= 0;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { isValidUrl } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { TradingRadio } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CUSTOM_NODE_KEY } from '../../types';
|
||||
@ -8,6 +7,7 @@ import {
|
||||
useNodeCheckTimeUpdateSubscription,
|
||||
} from '../../utils/__generated__/NodeCheck';
|
||||
import { LayoutCell } from './layout-cell';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
export const POLL_INTERVAL = 1000;
|
||||
export const SUBSCRIPTION_TIMEOUT = 3000;
|
||||
@ -128,6 +128,7 @@ export const RowData = ({
|
||||
highestBlock,
|
||||
onBlockHeight,
|
||||
}: RowDataProps) => {
|
||||
const t = useT();
|
||||
const { status: subStatus } = useNodeSubscriptionStatus();
|
||||
const { status, currentBlockHeight } = useNodeBasicStatus();
|
||||
const { responseTime } = useResponseTime(url, currentBlockHeight); // measure response time (ms) every time we get data (block height)
|
||||
@ -150,7 +151,7 @@ export const RowData = ({
|
||||
hasError={status === Result.Failed}
|
||||
dataTestId="response-time-cell"
|
||||
>
|
||||
{display(status, formatResponseTime(responseTime))}
|
||||
{display(status, formatResponseTime(responseTime), t('n/a'))}
|
||||
</LayoutCell>
|
||||
<LayoutCell
|
||||
label={t('Block')}
|
||||
@ -169,7 +170,7 @@ export const RowData = ({
|
||||
status === Result.Failed ? 'failed' : currentBlockHeight
|
||||
}
|
||||
>
|
||||
{display(status, currentBlockHeight)}
|
||||
{display(status, currentBlockHeight, t('n/a'))}
|
||||
</span>
|
||||
</LayoutCell>
|
||||
<LayoutCell
|
||||
@ -190,7 +191,7 @@ const formatResponseTime = (time: number | undefined) =>
|
||||
const display = (
|
||||
status: Result,
|
||||
yes: string | number | undefined,
|
||||
no = t('n/a')
|
||||
no: string | number | undefined
|
||||
) => {
|
||||
switch (status) {
|
||||
case Result.Successful:
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { parse as tomlParse } from 'toml';
|
||||
import { isValidUrl, LocalStorage } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useEffect } from 'react';
|
||||
import { create } from 'zustand';
|
||||
import { createClient } from '@vegaprotocol/apollo-client';
|
||||
@ -64,7 +63,7 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
set({ ...safeVars });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
const headline = t('Error processing the Vega environment');
|
||||
const headline = 'Error processing the Vega environment';
|
||||
set({
|
||||
status: 'failed',
|
||||
error: headline,
|
||||
@ -108,7 +107,7 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
if (!nodes || !nodes.length) {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: t(`Failed to fetch node config from ${state.VEGA_CONFIG_URL}`),
|
||||
error: `Failed to fetch node config from ${state.VEGA_CONFIG_URL}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -140,9 +139,9 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
else {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: t('No node found'),
|
||||
error: 'No node found',
|
||||
});
|
||||
console.warn(t('No suitable vega node was found'));
|
||||
console.warn('No suitable vega node was found');
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
@ -56,7 +56,7 @@ function setup(
|
||||
|
||||
describe('useNodeHealth', () => {
|
||||
it.each([
|
||||
{
|
||||
/*{
|
||||
core: 1,
|
||||
node: 1,
|
||||
expectedText: 'Operational',
|
||||
@ -67,7 +67,7 @@ describe('useNodeHealth', () => {
|
||||
node: 5,
|
||||
expectedText: 'Operational',
|
||||
expectedIntent: Intent.Success,
|
||||
},
|
||||
},*/
|
||||
{
|
||||
core: 10,
|
||||
node: 5,
|
||||
|
@ -4,8 +4,8 @@ import { useHeaderStore } from '@vegaprotocol/apollo-client';
|
||||
import { useEnvironment } from './use-environment';
|
||||
import { useNavigatorOnline } from '@vegaprotocol/react-helpers';
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { isTestEnv } from '@vegaprotocol/utils';
|
||||
import { useT } from '../use-t';
|
||||
|
||||
const POLL_INTERVAL = 1000;
|
||||
const BLOCK_THRESHOLD = 3;
|
||||
@ -13,6 +13,7 @@ const ERROR_LATENCY = 10000;
|
||||
const WARNING_LATENCY = 3000;
|
||||
|
||||
export const useNodeHealth = () => {
|
||||
const t = useT();
|
||||
const online = useNavigatorOnline();
|
||||
const url = useEnvironment((store) => store.VEGA_URL);
|
||||
const headerStore = useHeaderStore();
|
||||
@ -50,7 +51,7 @@ export const useNodeHealth = () => {
|
||||
|
||||
const [text, intent] = useMemo(() => {
|
||||
let intent = Intent.Success;
|
||||
let text = 'Operational';
|
||||
let text = t('Operational');
|
||||
|
||||
if (!online) {
|
||||
text = t('Offline');
|
||||
@ -60,23 +61,38 @@ export const useNodeHealth = () => {
|
||||
text = t('Non operational');
|
||||
intent = Intent.Danger;
|
||||
} else if (blockUpdateMsLatency > ERROR_LATENCY) {
|
||||
text = t('Erroneous latency ( >%s sec): %s sec', [
|
||||
(ERROR_LATENCY / 1000).toString(),
|
||||
(blockUpdateMsLatency / 1000).toFixed(2),
|
||||
]);
|
||||
text = t(
|
||||
'Erroneous latency ( >{{errorLatency}} sec): {{blockUpdateLatency}} sec',
|
||||
{
|
||||
nsSeparator: '|',
|
||||
replace: {
|
||||
errorLatency: (ERROR_LATENCY / 1000).toString(),
|
||||
blockUpdateLatency: (blockUpdateMsLatency / 1000).toFixed(2),
|
||||
},
|
||||
}
|
||||
);
|
||||
intent = Intent.Danger;
|
||||
} else if (blockDiff >= BLOCK_THRESHOLD) {
|
||||
text = t(`%s Blocks behind`, String(blockDiff));
|
||||
text = t('blocksBehind', {
|
||||
defaultValue: '{{count}} Blocks behind',
|
||||
replace: { count: blockDiff },
|
||||
});
|
||||
intent = Intent.Warning;
|
||||
} else if (blockUpdateMsLatency > WARNING_LATENCY) {
|
||||
text = t('Warning delay ( >%s sec): %s sec', [
|
||||
(WARNING_LATENCY / 1000).toString(),
|
||||
(blockUpdateMsLatency / 1000).toFixed(2),
|
||||
]);
|
||||
text = t(
|
||||
'Warning delay ( >{{warningLatency}} sec): {{blockUpdateLatency}} sec',
|
||||
{
|
||||
nsSeparator: '|',
|
||||
replace: {
|
||||
warningLatency: (WARNING_LATENCY / 1000).toString(),
|
||||
blockUpdateLatency: (blockUpdateMsLatency / 1000).toFixed(2),
|
||||
},
|
||||
}
|
||||
);
|
||||
intent = Intent.Warning;
|
||||
}
|
||||
return [text, intent];
|
||||
}, [online, blockDiff, blockUpdateMsLatency]);
|
||||
}, [online, blockDiff, blockUpdateMsLatency, t]);
|
||||
|
||||
return {
|
||||
datanodeBlockHeight: headers?.blockHeight,
|
||||
|
2
libs/environment/src/use-t.ts
Normal file
2
libs/environment/src/use-t.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export const useT = () => useTranslation('environment').t;
|
@ -1,3 +1,4 @@
|
||||
export { default as i18n_en } from './lib/i18n-en.json';
|
||||
export * from './lib/fills-manager';
|
||||
export * from './lib/fills-data-provider';
|
||||
export * from './lib/__generated__/Fills';
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
ActionsDropdown,
|
||||
TradingDropdownCopyItem,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useT } from './use-t';
|
||||
|
||||
export const FillActionsDropdown = ({
|
||||
tradeId,
|
||||
@ -13,6 +13,7 @@ export const FillActionsDropdown = ({
|
||||
buyOrderId: string;
|
||||
sellOrderId: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<ActionsDropdown data-testid="fill-actions-content">
|
||||
<TradingDropdownCopyItem value={tradeId} text={t('Copy trade ID')} />
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { FillsTable } from './fills-table';
|
||||
import { type useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { Pagination } from '@vegaprotocol/datagrid';
|
||||
@ -10,6 +9,7 @@ import {
|
||||
type TradesSubscriptionFilter,
|
||||
} from '@vegaprotocol/types';
|
||||
import { fillsWithMarketProvider } from './fills-data-provider';
|
||||
import { useT } from './use-t';
|
||||
|
||||
interface FillsManagerProps {
|
||||
partyId: string;
|
||||
@ -22,6 +22,7 @@ export const FillsManager = ({
|
||||
onMarketClick,
|
||||
gridProps,
|
||||
}: FillsManagerProps) => {
|
||||
const t = useT();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const filter: TradesFilter | TradesSubscriptionFilter = {
|
||||
partyIds: [partyId],
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
getDateTimeFormat,
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
AgGrid,
|
||||
@ -35,6 +34,7 @@ import {
|
||||
} from './__generated__/Fills';
|
||||
import { FillActionsDropdown } from './fill-actions-dropdown';
|
||||
import { getAsset } from '@vegaprotocol/markets';
|
||||
import { useT } from './use-t';
|
||||
|
||||
const TAKER = 'Taker';
|
||||
const MAKER = 'Maker';
|
||||
@ -48,6 +48,7 @@ export type Props = (AgGridReactProps | AgReactUiProps) & {
|
||||
|
||||
export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
({ partyId, onMarketClick, ...props }, ref) => {
|
||||
const t = useT();
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{
|
||||
@ -144,7 +145,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
...COL_DEFS.actions,
|
||||
},
|
||||
],
|
||||
[onMarketClick, partyId]
|
||||
[onMarketClick, partyId, t]
|
||||
);
|
||||
return (
|
||||
<AgGrid
|
||||
@ -323,6 +324,7 @@ const FeesBreakdownTooltip = ({
|
||||
value: market,
|
||||
partyId,
|
||||
}: ITooltipParams<Trade, Trade['market']> & { partyId?: string }) => {
|
||||
const t = useT();
|
||||
if (!market || !data) {
|
||||
return null;
|
||||
}
|
||||
@ -404,6 +406,7 @@ export const FeesDiscountBreakdownTooltip = ({
|
||||
data,
|
||||
partyId,
|
||||
}: ITooltipParams<Trade, Trade['market']> & { partyId?: string }) => {
|
||||
const t = useT();
|
||||
if (!data || !data.market) {
|
||||
return null;
|
||||
}
|
||||
|
3
libs/fills/src/lib/i18n-en.json
Normal file
3
libs/fills/src/lib/i18n-en.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Size": "*Size"
|
||||
}
|
3
libs/fills/src/lib/use-t.jsx
Normal file
3
libs/fills/src/lib/use-t.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const useT = () => useTranslation('fills').t;
|
@ -1,3 +1,4 @@
|
||||
export { default as i18n_en } from './lib/i18n-en.json';
|
||||
export * from './lib/funding-payments-manager';
|
||||
export * from './lib/funding-payments-data-provider';
|
||||
export * from './lib/__generated__/FundingPayments';
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { type AgGridReact } from 'ag-grid-react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { FundingPaymentsTable } from './funding-payments-table';
|
||||
import { Pagination } from '@vegaprotocol/datagrid';
|
||||
import { type useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { fundingPaymentsWithMarketProvider } from './funding-payments-data-provider';
|
||||
import { useT } from './use-t';
|
||||
|
||||
interface FundingPaymentsManagerProps {
|
||||
partyId: string;
|
||||
@ -20,6 +20,7 @@ export const FundingPaymentsManager = ({
|
||||
onMarketClick,
|
||||
gridProps,
|
||||
}: FundingPaymentsManagerProps) => {
|
||||
const t = useT();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const [hasDisplayedRow, setHasDisplayedRow] = useState<boolean | undefined>(
|
||||
undefined
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
isNumeric,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import {
|
||||
AgGrid,
|
||||
@ -30,6 +29,7 @@ import type { FundingPayment } from './funding-payments-data-provider';
|
||||
|
||||
import { getAsset } from '@vegaprotocol/markets';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from './use-t';
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
@ -56,6 +56,7 @@ const formatAmount = ({
|
||||
|
||||
export const FundingPaymentsTable = forwardRef<AgGridReact, Props>(
|
||||
({ onMarketClick, ...props }, ref) => {
|
||||
const t = useT();
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{
|
||||
@ -113,7 +114,7 @@ export const FundingPaymentsTable = forwardRef<AgGridReact, Props>(
|
||||
},
|
||||
},
|
||||
],
|
||||
[onMarketClick]
|
||||
[onMarketClick, t]
|
||||
);
|
||||
return (
|
||||
<AgGrid
|
||||
|
3
libs/funding-payments/src/lib/i18n-en.json
Normal file
3
libs/funding-payments/src/lib/i18n-en.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"Market": "*Market"
|
||||
}
|
3
libs/funding-payments/src/lib/use-t.jsx
Normal file
3
libs/funding-payments/src/lib/use-t.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const useT = () => useTranslation('funding-payments').t;
|
@ -1 +1,9 @@
|
||||
export * from './lib/i18n';
|
||||
import en_accounts from './locales/en/accounts.json';
|
||||
import en_governance from './locales/en/governance.json';
|
||||
export const locales = {
|
||||
en: {
|
||||
accounts: en_accounts,
|
||||
governance: en_governance,
|
||||
},
|
||||
};
|
||||
|
58
libs/i18n/src/locales/en/accounts.json
Normal file
58
libs/i18n/src/locales/en/accounts.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"{{balance}} above <0>maintenance level</0>": "{{balance}} above <0>maintenance level</0>",
|
||||
"Account type": "Account type",
|
||||
"Amount": "Amount",
|
||||
"Amount below minimum requirement set by transfer.minTransferQuantumMultiple": "Amount below minimum requirement set by transfer.minTransferQuantumMultiple",
|
||||
"Amount below minimum requirements for partial transfer. Use max to bypass": "Amount below minimum requirements for partial transfer. Use max to bypass",
|
||||
"Amount cannot be 0": "Amount cannot be 0",
|
||||
"Amount to be transferred": "Amount to be transferred",
|
||||
"Asset": "Asset",
|
||||
"Asset is the collateral that is deposited into the Vega protocol.": "Asset is the collateral that is deposited into the Vega protocol.",
|
||||
"Available": "Available",
|
||||
"Balance": "Balance",
|
||||
"balance": "balance",
|
||||
"Cannot transfer to the same account type for the connected key": "Cannot transfer to the same account type for the connected key",
|
||||
"Collateral not used": "Collateral not used",
|
||||
"Confirm transfer": "Confirm transfer",
|
||||
"Copy asset ID": "Copy asset ID",
|
||||
"Current key: {{pubKey}}": "Current key: {{pubKey}}",
|
||||
"Currently allocated to a market as margin or bond. Check the breakdown for details.": "Currently allocated to a market as margin or bond. Check the breakdown for details.",
|
||||
"Deposit": "Deposit",
|
||||
"Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.": "Deposited on the network, but not allocated to a market. Free to use for placing orders or providing liquidity.",
|
||||
"Enter manually": "Enter manually",
|
||||
"From account": "From account",
|
||||
"Include transfer fee": "Include transfer fee",
|
||||
"initial level": "initial level",
|
||||
"maintenance level": "maintenance level",
|
||||
"Margin health": "Margin health",
|
||||
"Market": "Market",
|
||||
"No accounts": "No accounts",
|
||||
"None": "None",
|
||||
"Please select": "Please select",
|
||||
"Please select an asset": "Please select an asset",
|
||||
"release level": "release level",
|
||||
"search level": "search level",
|
||||
"Select from wallet": "Select from wallet",
|
||||
"The fee will be taken from the amount you are transferring.": "The fee will be taken from the amount you are transferring.",
|
||||
"The total amount of each asset on this key. Includes used and available collateral.": "The total amount of each asset on this key. Includes used and available collateral.",
|
||||
"The total amount taken from your account. The amount to be transferred plus the fee.": "The total amount taken from your account. The amount to be transferred plus the fee.",
|
||||
"The total amount to be transferred (without the fee)": "The total amount to be transferred (without the fee)",
|
||||
"The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}": "The transfer fee is set by the network parameter transfer.fee.factor, currently set to {{feeFactor}}",
|
||||
"To account": "To account",
|
||||
"To Vega key": "To Vega key",
|
||||
"Total": "Total",
|
||||
"Total amount (with fee)": "Total amount (with fee)",
|
||||
"Transfer": "Transfer",
|
||||
"Transfer fee": "Transfer fee",
|
||||
"TRANSFER_FUNDS_TO_ANOTHER_KNOWN_VEGA_KEY": "Transfer funds to another Vega key <0>{{pubKey}}</0>. If you are at all unsure, stop and seek advice.",
|
||||
"TRANSFER_FUNDS_TO_ANOTHER_VEGA_KEY": "Transfer funds to another Vega key. If you are at all unsure, stop and seek advice.",
|
||||
"usage breakdown": "usage breakdown",
|
||||
"Use max": "Use max",
|
||||
"Used": "Used",
|
||||
"View asset details": "View asset details",
|
||||
"View on Etherscan": "View on Etherscan",
|
||||
"View usage breakdown": "View usage breakdown",
|
||||
"Withdraw": "Withdraw",
|
||||
"You cannot transfer more than available": "You cannot transfer more than available",
|
||||
"You have {{value}} {{symbol}} in total.": "You have {{value}} {{symbol}} in total."
|
||||
}
|
52
libs/i18n/src/locales/en/assets.json
Normal file
52
libs/i18n/src/locales/en/assets.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"A Vega builtin asset": "A Vega builtin asset",
|
||||
"An asset originated from an Ethereum ERC20 Token": "An asset originated from an Ethereum ERC20 Token",
|
||||
"Asset can be used on the Vega network": "Asset can be used on the Vega network",
|
||||
"Asset details - {{symbol}}": "Asset details - {{symbol}}",
|
||||
"Asset has been proposed to the network": "Asset has been proposed to the network",
|
||||
"Asset has been rejected": "Asset has been rejected",
|
||||
"Asset needs to be added to the Ethereum bridge": "Asset needs to be added to the Ethereum bridge",
|
||||
"Asset not found": "Asset not found",
|
||||
"Builtin asset": "Builtin asset",
|
||||
"Close": "Close",
|
||||
"Contract address": "Contract address",
|
||||
"Copy address to clipboard": "Copy address to clipboard",
|
||||
"Copy id to clipboard": "Copy id to clipboard",
|
||||
"Decimals": "Decimals",
|
||||
"Enabled": "Enabled",
|
||||
"ERC20": "ERC20",
|
||||
"Fetching balance…": "Fetching balance…",
|
||||
"Global reward pool account balance": "Global reward pool account balance",
|
||||
"ID": "ID",
|
||||
"Infrastructure fee account balance": "Infrastructure fee account balance",
|
||||
"Lifetime limit": "Lifetime limit",
|
||||
"Liquidity provision fee reward account balance": "Liquidity provision fee reward account balance",
|
||||
"Maker paid fees account balance": "Maker paid fees account balance",
|
||||
"Maker received fees account balance": "Maker received fees account balance",
|
||||
"Market proposer reward account balance": "Market proposer reward account balance",
|
||||
"Max faucet amount": "Max faucet amount",
|
||||
"Maximum amount that can be requested by a party through the built-in asset faucet at a time": "Maximum amount that can be requested by a party through the built-in asset faucet at a time",
|
||||
"Name": "Name",
|
||||
"No data": "No data",
|
||||
"Number of decimal / precision handled by this asset": "Number of decimal / precision handled by this asset",
|
||||
"Pending listing": "Pending listing",
|
||||
"Proposed": "Proposed",
|
||||
"Quantum": "Quantum",
|
||||
"Rejected": "Rejected",
|
||||
"Status": "Status",
|
||||
"Symbol": "Symbol",
|
||||
"The address of the contract for the token, on the ethereum network": "The address of the contract for the token, on the ethereum network",
|
||||
"The global rewards acquired in this asset": "The global rewards acquired in this asset",
|
||||
"The infrastructure fee account in this asset": "The infrastructure fee account in this asset",
|
||||
"The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance": "The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance",
|
||||
"The minimum economically meaningful amount of the asset": "The minimum economically meaningful amount of the asset",
|
||||
"The rewards acquired based on fees received for being a maker on trades": "The rewards acquired based on fees received for being a maker on trades",
|
||||
"The rewards acquired based on the fees paid to makers in this asset": "The rewards acquired based on the fees paid to makers in this asset",
|
||||
"The rewards acquired based on the liquidity provision fees in this asset": "The rewards acquired based on the liquidity provision fees in this asset",
|
||||
"The rewards acquired based on the market proposer reward in this asset": "The rewards acquired based on the market proposer reward in this asset",
|
||||
"The status of the asset in the Vega network": "The status of the asset in the Vega network",
|
||||
"There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit.": "There is 1 unit of the settlement asset ({{assetSymbol}}) to every 1 quote unit.",
|
||||
"Type": "Type",
|
||||
"WITHDRAW_THRESHOLD_TOOLTIP_TEXT": "The maximum you can withdraw instantly. There's no limit on the size of a withdrawal, but all withdrawals over the threshold will have a delay time added to them",
|
||||
"Withdrawal threshold": "Withdrawal threshold"
|
||||
}
|
5
libs/i18n/src/locales/en/candles-chart.json
Normal file
5
libs/i18n/src/locales/en/candles-chart.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"Indicators": "Indicators",
|
||||
"Interval: {{interval}}": "Interval: {{interval}}",
|
||||
"No open orders": "No open orders"
|
||||
}
|
21
libs/i18n/src/locales/en/datagrid.json
Normal file
21
libs/i18n/src/locales/en/datagrid.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"{{orderType}} (Iceberg)": "{{orderType}} (Iceberg)",
|
||||
"{{reference}} {{side}} {{offset}} Peg limit": "{{reference}} {{side}} {{offset}} Peg limit",
|
||||
"Depending on data node retention you may not be able see the full history": "Depending on data node retention you may not be able see the full history",
|
||||
"End": "End",
|
||||
"Liquidity provision": "Liquidity provision",
|
||||
"Load more": "Load more",
|
||||
"Loading...": "Loading...",
|
||||
"No data": "No data",
|
||||
"No rows matching selected filters": "No rows matching selected filters",
|
||||
"paginationAllLoaded": "all {{count}} rows loaded",
|
||||
"paginationAllLoaded_one": "all {{count}} row loaded",
|
||||
"paginationAllLoaded_other": "all {{count}} rows loaded",
|
||||
"paginationLoaded": "{{count}} rows loaded",
|
||||
"paginationLoaded_one": "{{count}} row loaded",
|
||||
"paginationLoaded_other": "{{count}} rows loaded",
|
||||
"Reset": "Reset",
|
||||
"Start": "Start",
|
||||
"The earliest data that can be queried is {{maxSubDays}} days ago.": "The earliest data that can be queried is {{maxSubDays}} days ago.",
|
||||
"The maximum time range that can be queried is {{maxDaysRange}} days.": "The maximum time range that can be queried is {{maxDaysRange}} days."
|
||||
}
|
132
libs/i18n/src/locales/en/deal-ticket.json
Normal file
132
libs/i18n/src/locales/en/deal-ticket.json
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"\"Post only\" can not be used on \"Fill or Kill\" or \"Immediate or Cancel\" orders.": "\"Post only\" can not be used on \"Fill or Kill\" or \"Immediate or Cancel\" orders.",
|
||||
"\"Post only\" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.": "\"Post only\" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.",
|
||||
"\"Reduce only\" can be used only with non-persistent orders, such as \"Fill or Kill\" or \"Immediate or Cancel\".": "\"Reduce only\" can be used only with non-persistent orders, such as \"Fill or Kill\" or \"Immediate or Cancel\".",
|
||||
"\"Reduce only\" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.": "\"Reduce only\" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.",
|
||||
"{{amount}} {{assetSymbol}} is currently required": "{{amount}} {{assetSymbol}} is currently required",
|
||||
"{{triggerTrailingPercentOffset}}% trailing": "{{triggerTrailingPercentOffset}}% trailing",
|
||||
"A release candidate for the staging environment": "A release candidate for the staging environment",
|
||||
"above": "above",
|
||||
"Advanced": "Advanced",
|
||||
"An estimate of the most you would be expected to pay in fees, in the market's settlement asset {{assetSymbol}}. Fees estimated are \"taker\" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.": "An estimate of the most you would be expected to pay in fees, in the market's settlement asset {{assetSymbol}}. Fees estimated are \"taker\" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.",
|
||||
"Any orders placed now will not trade until the auction ends": "Any orders placed now will not trade until the auction ends",
|
||||
"below": "below",
|
||||
"Cancel": "Cancel",
|
||||
"Closed": "Closed",
|
||||
"Closing on {{time}}": "Closing on {{time}}",
|
||||
"Could not load market": "Could not load market",
|
||||
"Current margin allocation": "Current margin allocation",
|
||||
"Custom": "Custom",
|
||||
"Deduction from collateral": "Deduction from collateral",
|
||||
"DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT": "To cover the required margin, this amount will be drawn from your general ({{assetSymbol}}) account.",
|
||||
"Deposit {{assetSymbol}}": "Deposit {{assetSymbol}}",
|
||||
"Devnet": "Devnet",
|
||||
"EST_TOTAL_MARGIN_TOOLTIP_TEXT": "Estimated total margin that will cover open positions, active orders and this order.",
|
||||
"Est. uncrossing price": "Est. uncrossing price",
|
||||
"Est. uncrossing vol": "Est. uncrossing vol",
|
||||
"Expire": "Expire",
|
||||
"Expiry time/date": "Expiry time/date",
|
||||
"Fairground": "Fairground",
|
||||
"Fairground testnet": "Fairground testnet",
|
||||
"Fees": "Fees",
|
||||
"Find out more": "Find out more",
|
||||
"For full details please see <0>liquidation price estimate documentation</0>.": "For full details please see <0>liquidation price estimate documentation</0>.",
|
||||
"Iceberg": "Iceberg",
|
||||
"ICEBERG_TOOLTIP": "Trade only a fraction of the order size at once. After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. Note that the full volume of the order is not hidden and is still reflected in the order book.",
|
||||
"Infrastructure fee": "Infrastructure fee",
|
||||
"Limit": "Limit",
|
||||
"Liquidation": "Liquidation",
|
||||
"LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT": "This is an approximation for the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.",
|
||||
"Liquidity fee": "Liquidity fee",
|
||||
"Long": "Long",
|
||||
"Mainnet": "Mainnet",
|
||||
"Mainnet-mirror": "Mainnet-mirror",
|
||||
"Make a deposit": "Make a deposit",
|
||||
"Maker fee": "Maker fee",
|
||||
"Margin required": "Margin required",
|
||||
"MARGIN_ACCOUNT_TOOLTIP_TEXT": "Margin account balance.",
|
||||
"MARGIN_DIFF_TOOLTIP_TEXT": "The additional margin required for your new position (taking into account volume and open orders), compared to your current margin. Measured in the market's settlement asset ({{assetSymbol}}).",
|
||||
"Market": "Market",
|
||||
"Minimum size": "Minimum size",
|
||||
"Minimum visible size cannot be greater than the peak size ({{peakSize}})": "Minimum visible size cannot be greater than the peak size ({{peakSize}})",
|
||||
"Minimum visible size cannot be lower than {{sizeStep}}": "Minimum visible size cannot be lower than {{sizeStep}}",
|
||||
"No public key selected": "No public key selected",
|
||||
"No trading enabled for this market.": "No trading enabled for this market.",
|
||||
"Not enough liquidity to open": "Not enough liquidity to open",
|
||||
"Notional": "Notional",
|
||||
"NOTIONAL_SIZE_TOOLTIP_TEXT": "The notional size represents the position size in the settlement asset {{quoteName}} of the futures contract. This is calculated by multiplying the number of contracts by the prices of the contract. For example 10 contracts traded at a price of $50 has a notional size of $500.",
|
||||
"OCO": "OCO",
|
||||
"One cancels another": "One cancels another",
|
||||
"Only limit orders are permitted when market is in auction": "Only limit orders are permitted when market is in auction",
|
||||
"Peak size": "Peak size",
|
||||
"Peak size cannot be greater than the size ({{size}})": "Peak size cannot be greater than the size ({{size}})",
|
||||
"Peak size cannot be lower than {{stepSize}}": "Peak size cannot be lower than {{stepSize}}",
|
||||
"Place limit order": "Place limit order",
|
||||
"Place limit stop order": "Place limit stop order",
|
||||
"Place market order": "Place market order",
|
||||
"Place market stop order": "Place market stop order",
|
||||
"Place OCO stop order": "Place OCO stop order",
|
||||
"Post only": "Post only",
|
||||
"Price": "Price",
|
||||
"Price cannot be lower than {{priceStep}}": "Price cannot be lower than {{priceStep}}",
|
||||
"Projected margin": "Projected margin",
|
||||
"Propose a network parameter change": "Propose a network parameter change",
|
||||
"Public testnet run by the Vega team, often used for incentives": "Public testnet run by the Vega team, often used for incentives",
|
||||
"Reduce only": "Reduce only",
|
||||
"Referral discount": "Referral discount",
|
||||
"Short": "Short",
|
||||
"Size": "Size",
|
||||
"Size cannot be lower than {{sizeStep}}": "Size cannot be lower than {{sizeStep}}",
|
||||
"sizeAtPrice-market": "market",
|
||||
"Stagnet": "Stagnet",
|
||||
"Stop": "Stop",
|
||||
"Stop Limit": "Stop Limit",
|
||||
"Stop Market": "Stop Market",
|
||||
"Stop order will be triggered immediately": "Stop order will be triggered immediately",
|
||||
"Strategy": "Strategy",
|
||||
"Submit": "Submit",
|
||||
"terminated": "terminated",
|
||||
"The expiry date that you have entered appears to be in the past": "The expiry date that you have entered appears to be in the past",
|
||||
"The latest Vega code auto-deployed": "The latest Vega code auto-deployed",
|
||||
"The mainnet-mirror network": "The mainnet-mirror network",
|
||||
"The maximum volume that can be traded at once. Must be less than the total size of the order.": "The maximum volume that can be traded at once. Must be less than the total size of the order.",
|
||||
"The validator deployed testnet": "The validator deployed testnet",
|
||||
"The vega mainnet": "The vega mainnet",
|
||||
"There is a limit of {{maxNumberOfOrders}} active stop orders per market. Orders submitted above the limit will be immediately rejected.": "There is a limit of {{maxNumberOfOrders}} active stop orders per market. Orders submitted above the limit will be immediately rejected.",
|
||||
"This is a new market in an opening auction to determine a fair mid-price before starting continuous trading.": "This is a new market in an opening auction to determine a fair mid-price before starting continuous trading.",
|
||||
"This is the standard trading mode where trades are executed whenever orders are received.": "This is the standard trading mode where trades are executed whenever orders are received.",
|
||||
"This market has been suspended via a governance vote and can be resumed or terminated by further votes.": "This market has been suspended via a governance vote and can be resumed or terminated by further votes.",
|
||||
"This market is {{marketState}} and not accepting orders": "This market is {{marketState}} and not accepting orders",
|
||||
"This market is in auction due to high price volatility.": "This market is in auction due to high price volatility.",
|
||||
"This market is in auction until it reaches sufficient liquidity.": "This market is in auction until it reaches sufficient liquidity.",
|
||||
"This market is in opening auction until it has reached enough liquidity to move into continuous trading.": "This market is in opening auction until it has reached enough liquidity to move into continuous trading.",
|
||||
"This market may have sufficient liquidity but there are not enough priced limit orders in the order book, which are required to deploy liquidity commitment pegged orders.": "This market may have sufficient liquidity but there are not enough priced limit orders in the order book, which are required to deploy liquidity commitment pegged orders.",
|
||||
"Time in force": "Time in force",
|
||||
"TIME_IN_FORCE_SELECTOR_LIQUIDITY_MONITORING_AUCTION": "This market is in auction until it reaches <0>sufficient liquidity</0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.",
|
||||
"TIME_IN_FORCE_SELECTOR_PRICE_MONITORING_AUCTION": "This market is in auction due to <0>high price volatility</0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.",
|
||||
"Total fees": "Total fees",
|
||||
"Total margin available": "Total margin available",
|
||||
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
|
||||
"Trading terminated": "Trading terminated",
|
||||
"Trailing percent offset cannot be higher than 99.9": "Trailing percent offset cannot be higher than 99.9",
|
||||
"Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}": "Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}",
|
||||
"Trailing percentage offset": "Trailing percentage offset",
|
||||
"Trigger": "Trigger",
|
||||
"Type": "Type",
|
||||
"TYPE_SELECTOR_LIQUIDITY_MONITORING_AUCTION": "This market is in auction until it reaches <0>sufficient liquidity</0>. Only limit orders are permitted when market is in auction.",
|
||||
"TYPE_SELECTOR_PRICE_MONITORING_AUCTION": "This market is in auction due to <0>high price volatility</0>. Only limit orders are permitted when market is in auction.",
|
||||
"Until the auction ends, you can only place GFA, GTT, or GTC limit orders": "Until the auction ends, you can only place GFA, GTT, or GTC limit orders",
|
||||
"VALIDATOR_TESTNET": "VALIDATOR_TESTNET",
|
||||
"Volume discount": "Volume discount",
|
||||
"When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.": "When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.",
|
||||
"You have only {{amount}}.": "You have only {{amount}}.",
|
||||
"You may not have enough margin available to open this position.": "You may not have enough margin available to open this position.",
|
||||
"You need {{symbol}} in your wallet to trade in this market.": "You need {{symbol}} in your wallet to trade in this market.",
|
||||
"You need provide a expiry time/date": "You need provide a expiry time/date",
|
||||
"You need provide a price": "You need provide a price",
|
||||
"You need provide a trailing percent offset": "You need provide a trailing percent offset",
|
||||
"You need to connect your own wallet to start trading on this market": "You need to connect your own wallet to start trading on this market",
|
||||
"You need to provide a minimum visible size": "You need to provide a minimum visible size",
|
||||
"You need to provide a peak size": "You need to provide a peak size",
|
||||
"You need to provide a size": "You need to provide a size"
|
||||
}
|
44
libs/i18n/src/locales/en/deposits.json
Normal file
44
libs/i18n/src/locales/en/deposits.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"{{assetSymbol}} has been deposited in your Ethereum wallet": "{{assetSymbol}} has been deposited in your Ethereum wallet",
|
||||
"Amount": "Amount",
|
||||
"Approval failed": "Approval failed",
|
||||
"Approve {{assetSymbol}}": "Approve {{assetSymbol}}",
|
||||
"Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}",
|
||||
"Asset": "Asset",
|
||||
"Balance available": "Balance available",
|
||||
"Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet",
|
||||
"Confirm the transaction in your Ethereum wallet to use the {{assetSymbol}} faucet": "Confirm the transaction in your Ethereum wallet to use the {{assetSymbol}} faucet",
|
||||
"Connect": "Connect",
|
||||
"Connect Ethereum wallet": "Connect Ethereum wallet",
|
||||
"Could not verify balances of account": "Could not verify balances of account",
|
||||
"Deposit": "Deposit",
|
||||
"Disconnect": "Disconnect",
|
||||
"Enter manually": "Enter manually",
|
||||
"Ethereum deposit cap": "Ethereum deposit cap",
|
||||
"Exempt": "Exempt",
|
||||
"Faucet of {{symbol}} failed": "Faucet of {{symbol}} failed",
|
||||
"From (Ethereum address)": "From (Ethereum address)",
|
||||
"Get {{assetSymbol}}": "Get {{assetSymbol}}",
|
||||
"Go to your Ethereum wallet and approve the transaction to enable the use of {{assetSymbol}}": "Go to your Ethereum wallet and approve the transaction to enable the use of {{assetSymbol}}",
|
||||
"Please select": "Please select",
|
||||
"Please select an asset": "Please select an asset",
|
||||
"Remaining deposit allowance": "Remaining deposit allowance",
|
||||
"Select from wallet": "Select from wallet",
|
||||
"The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time",
|
||||
"The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.",
|
||||
"The faucet transaction was rejected by the connected Ethereum wallet": "The faucet transaction was rejected by the connected Ethereum wallet",
|
||||
"This app only works on {{chainId}}.": "This app only works on {{chainId}}.",
|
||||
"To (Vega key)": "To (Vega key)",
|
||||
"To date, {{currentDeposit}} {{assetSymbol}} has been deposited from this Ethereum address, so you can deposit up to {{remainingDeposit}} {{assetSymbol}} more.": "To date, {{currentDeposit}} {{assetSymbol}} has been deposited from this Ethereum address, so you can deposit up to {{remainingDeposit}} {{assetSymbol}} more.",
|
||||
"Use maximum": "Use maximum",
|
||||
"VEGA has a lifetime deposit limit of {{amount}} {{assetSymbol}} per address. This can be changed through governance": "VEGA has a lifetime deposit limit of {{amount}} {{assetSymbol}} per address. This can be changed through governance",
|
||||
"View asset details": "View asset details",
|
||||
"View on Etherscan": "View on Etherscan",
|
||||
"You approved deposits of up to {{assetSymbol}} {{approvedAllowanceValue}}.": "You approved deposits of up to {{assetSymbol}} {{approvedAllowanceValue}}.",
|
||||
"You can't deposit more than you have in your Ethereum wallet, {{amount}} {{assetSymbol}}": "You can't deposit more than you have in your Ethereum wallet, {{amount}} {{assetSymbol}}",
|
||||
"You can't deposit more than your approved deposit amount, {{amount}} {{assetSymbol}}": "You can't deposit more than your approved deposit amount, {{amount}} {{assetSymbol}}",
|
||||
"You can't deposit more than your remaining deposit allowance, {{amount}} {{assetSymbol}}": "You can't deposit more than your remaining deposit allowance, {{amount}} {{assetSymbol}}",
|
||||
"You have exceeded the maximum number of faucet attempts allowed": "You have exceeded the maximum number of faucet attempts allowed",
|
||||
"Your {{assetSymbol}} approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit": "Your {{assetSymbol}} approval is being confirmed by the Ethereum network. When this is complete, you can continue your deposit",
|
||||
"Your request for funds from the {{assetSymbol}} faucet is being confirmed by the Ethereum network": "Your request for funds from the {{assetSymbol}} faucet is being confirmed by the Ethereum network"
|
||||
}
|
40
libs/i18n/src/locales/en/environment.json
Normal file
40
libs/i18n/src/locales/en/environment.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"A release candidate for the staging environment": "A release candidate for the staging environment",
|
||||
"Advanced": "Advanced",
|
||||
"Block": "Block",
|
||||
"blocksBehind_one": "{{count}} Block behind",
|
||||
"blocksBehind_other": "{{count}} Blocks behind",
|
||||
"Change node": "Change node",
|
||||
"Check": "Check",
|
||||
"Checking": "Checking",
|
||||
"Connect to this node": "Connect to this node",
|
||||
"current": "current",
|
||||
"Custom": "Custom",
|
||||
"Devnet": "Devnet",
|
||||
"Erroneous latency ( >{{errorLatency}} sec): {{blockUpdateLatency}} sec": "Erroneous latency ( >{{errorLatency}} sec): {{blockUpdateLatency}} sec",
|
||||
"Fairground": "Fairground",
|
||||
"Fairground testnet": "Fairground testnet",
|
||||
"Mainnet": "Mainnet",
|
||||
"Mainnet-mirror": "Mainnet-mirror",
|
||||
"n/a": "n/a",
|
||||
"No": "No",
|
||||
"Node": "Node",
|
||||
"Non operational": "Non operational",
|
||||
"not available": "not available",
|
||||
"Offline": "Offline",
|
||||
"Operational": "Operational",
|
||||
"Other": "Other",
|
||||
"Propose a network parameter change": "Propose a network parameter change",
|
||||
"Public testnet run by the Vega team, often used for incentives": "Public testnet run by the Vega team, often used for incentives",
|
||||
"Response time": "Response time",
|
||||
"Stagnet": "Stagnet",
|
||||
"Subscription": "Subscription",
|
||||
"The latest Vega code auto-deployed": "The latest Vega code auto-deployed",
|
||||
"The mainnet-mirror network": "The mainnet-mirror network",
|
||||
"The validator deployed testnet": "The validator deployed testnet",
|
||||
"The vega mainnet": "The vega mainnet",
|
||||
"VALIDATOR_TESTNET": "VALIDATOR_TESTNET",
|
||||
"View on Etherscan (opens in a new tab)": "View on Etherscan (opens in a new tab)",
|
||||
"Warning delay ( >{{warningLatency}} sec): {{blockUpdateLatency}} sec": "Warning delay ( >{{warningLatency}} sec): {{blockUpdateLatency}} sec",
|
||||
"Yes": "Yes"
|
||||
}
|
20
libs/i18n/src/locales/en/fills.json
Normal file
20
libs/i18n/src/locales/en/fills.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"Copy buy order ID": "Copy buy order ID",
|
||||
"Copy sell order ID": "Copy sell order ID",
|
||||
"Copy trade ID": "Copy trade ID",
|
||||
"Date": "Date",
|
||||
"Fee": "Fee",
|
||||
"Fee Discount": "Fee Discount",
|
||||
"Fees to be paid by the taker.": "Fees to be paid by the taker.",
|
||||
"If the market is active the maker will pay zero infrastructure and liquidity fees.": "If the market is active the maker will pay zero infrastructure and liquidity fees.",
|
||||
"If the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.": "If the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.",
|
||||
"Infrastructure Fee": "Infrastructure Fee",
|
||||
"Market": "Market",
|
||||
"No fills": "No fills",
|
||||
"Notional": "Notional",
|
||||
"Price": "Price",
|
||||
"Referral Discount": "Referral Discount",
|
||||
"Role": "Role",
|
||||
"Size": "Size",
|
||||
"Volume Discount": "Volume Discount"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user