Compare commits

..
Author SHA1 Message Date
zramsay 3830ddd2dc use available node version 2024-03-11 13:05:56 +00:00
zramsay c4c8b2ee97 fix 2024-03-11 13:04:44 +00:00
Matthew Russell 29bcbd06fb fix(trading,types): fix default build and type generation commands (#5959) 2024-03-11 10:06:59 +00:00
48 changed files with 342 additions and 344 deletions
+1 -1
View File
@@ -136,7 +136,7 @@ The [`docker`](./docker) subfolder has some docker configurations for easily set
Using multistage dockerfile dist is compiled using [node](https://hub.docker.com/_/node) image and later packed to nginx as in [dist build](#dist-build). The multistage builds ensures consistent CPU architecture and build toolchains are used so that the result will be identical.
```bash
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.9.1 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
docker build --build-arg APP=[YOUR APP] --build-arg NODE_VERSION=20.11 --build-arg ENV_NAME=mainnet -t [TAG] -f docker/node-inside-docker.Dockerfile .
```
### Computing ipfs-hash of the build
@@ -10,7 +10,7 @@ import {
addDecimalsFormatNumber,
formatNumber,
removePaginationWrapper,
validForSyntaxHighlighter,
suitableForSyntaxHighlighter,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../components/route-title';
@@ -134,7 +134,7 @@ export const NetworkParameterRow = ({
}: {
row: { key: string; value: string };
}) => {
const isSyntaxRow = validForSyntaxHighlighter(value);
const isSyntaxRow = suitableForSyntaxHighlighter(value);
useDocumentTitle(['Network Parameters']);
return (
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { validForSyntaxHighlighter } from '@vegaprotocol/utils';
import { suitableForSyntaxHighlighter } from '@vegaprotocol/utils';
import { useNetworkParams } from '@vegaprotocol/network-parameters';
import {
getClosingTimestamp,
@@ -46,7 +46,7 @@ const SelectedNetworkParamCurrentValue = ({
<div className="mb-4">
<p className="text-sm text-white">{t('CurrentValue')}</p>
{validForSyntaxHighlighter(value) ? (
{suitableForSyntaxHighlighter(value) ? (
<SyntaxHighlighter data={JSON.parse(value)} />
) : (
<Input
@@ -8,7 +8,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { useValidateJson } from '@vegaprotocol/react-helpers';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -7,7 +7,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { useValidateJson } from '@vegaprotocol/react-helpers';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -14,7 +14,7 @@ import {
RoundedWrapper,
TextArea,
} from '@vegaprotocol/ui-toolkit';
import { useValidateJson } from '@vegaprotocol/react-helpers';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -7,7 +7,7 @@ import {
doesValueEquateToParam,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { useValidateJson } from '@vegaprotocol/react-helpers';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -8,7 +8,7 @@ import {
useProposalSubmit,
} from '@vegaprotocol/proposals';
import { useEnvironment, DocsLinks } from '@vegaprotocol/environment';
import { useValidateJson } from '@vegaprotocol/react-helpers';
import { useValidateJson } from '@vegaprotocol/utils';
import {
NetworkParams,
useNetworkParams,
@@ -9,7 +9,7 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { URL_REGEX, validVegaPublicKey } from '@vegaprotocol/utils';
import { URL_REGEX, isValidVegaPublicKey } from '@vegaprotocol/utils';
import { type useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
import { useT } from '../../lib/use-t';
@@ -217,7 +217,9 @@ export const TeamForm = ({
validate: {
allowList: (value) => {
const publicKeys = parseAllowListText(value);
if (publicKeys.every((pk) => validVegaPublicKey(pk))) {
if (
publicKeys.every((pk) => isValidVegaPublicKey(pk))
) {
return true;
}
return t('Invalid public key found in allow list');
@@ -2,11 +2,12 @@ import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { DocsLinks, useEnvironment } from '@vegaprotocol/environment';
import { ButtonLink, ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import type { Market } from '@vegaprotocol/markets';
import { addDecimalsFormatNumber, fromNanoSeconds } from '@vegaprotocol/utils';
import {
useMarketExpiryDate,
addDecimalsFormatNumber,
fromNanoSeconds,
getExpiryDate,
getMarketExpiryDate,
} from '@vegaprotocol/react-helpers';
} from '@vegaprotocol/utils';
import {
Last24hPriceChange,
Last24hVolume,
@@ -263,13 +264,12 @@ export const FundingCountdown = ({ marketId }: { marketId: string }) => {
};
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
const expiryDate = useMarketExpiryDate(
market.tradableInstrument.instrument.metadata.tags,
market.marketTimestamps.close,
market.state
);
const content = market.tradableInstrument.instrument.metadata.tags
? expiryDate
? getExpiryDate(
market.tradableInstrument.instrument.metadata.tags,
market.marketTimestamps.close,
market.state
)
: '-';
return <div data-testid="trading-expiry">{content}</div>;
};
+4 -2
View File
@@ -11,8 +11,10 @@ import { useMemo } from 'react';
import type { Asset } from '@vegaprotocol/types';
import type { ProductType } from '@vegaprotocol/types';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { getMarketExpiryDate } from '@vegaprotocol/react-helpers';
import {
addDecimalsFormatNumber,
getMarketExpiryDate,
} from '@vegaprotocol/utils';
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
@@ -7,8 +7,11 @@ import {
useSuccessorMarket,
type Market,
} from '@vegaprotocol/markets';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import { getMarketExpiryDate } from '@vegaprotocol/react-helpers';
import {
addDecimalsFormatNumber,
getMarketExpiryDate,
isNumeric,
} from '@vegaprotocol/utils';
import { useT, ns } from '../../lib/use-t';
import { Links } from '../../lib/links';
@@ -9,7 +9,7 @@ import {
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
import { useForm } from 'react-hook-form';
import { useT } from '../../lib/use-t';
import { useRequired } from '@vegaprotocol/react-helpers';
import { useRequired } from '@vegaprotocol/utils';
import {
useSimpleTransaction,
type Status,
+5 -4
View File
@@ -1,14 +1,15 @@
# Build container
ARG NODE_VERSION
FROM --platform=amd64 node:${NODE_VERSION}-alpine3.16 as build
FROM --platform=amd64 node:${NODE_VERSION}-alpine3.18 as build
WORKDIR /app
# Argument to allow building of different apps
ARG APP
ARG ENV_NAME=""
RUN apk add --update --no-cache \
make==4.3-r0 \
gcc==11.2.1_git20220219-r2 \
g++==11.2.1_git20220219-r2
git \
make==4.4.1-r1 \
gcc==12.2.1_git20220924-r10 \
g++==12.2.1_git20220924-r10
COPY . ./
RUN yarn --pure-lockfile
# work around for different build process in trading
+3 -5
View File
@@ -1,15 +1,13 @@
import sortBy from 'lodash/sortBy';
import {
useMaxSafe,
useRequired,
useVegaPublicKey,
addDecimal,
toBigNum,
removeDecimal,
addDecimalsFormatNumber,
} from '@vegaprotocol/utils';
import {
useMaxSafe,
useRequired,
useVegaPublicKey,
} from '@vegaprotocol/react-helpers';
import { useT } from './use-t';
import {
TradingFormGroup,
@@ -1,8 +1,7 @@
import { Controller, type Control } from 'react-hook-form';
import type { Market } from '@vegaprotocol/markets';
import type { OrderFormValues } from '../../hooks/use-form-values';
import { determinePriceStep } from '@vegaprotocol/utils';
import { useValidateAmount } from '@vegaprotocol/react-helpers';
import { determinePriceStep, useValidateAmount } from '@vegaprotocol/utils';
import {
TradingFormGroup,
Tooltip,
@@ -1,7 +1,7 @@
import { Controller, type Control } from 'react-hook-form';
import type { Market } from '@vegaprotocol/markets';
import type { OrderFormValues } from '../../hooks/use-form-values';
import { useValidateAmount } from '@vegaprotocol/react-helpers';
import { useValidateAmount } from '@vegaprotocol/utils';
import {
TradingFormGroup,
TradingInput,
@@ -8,8 +8,8 @@ import {
formatForInput,
formatValue,
removeDecimal,
useValidateAmount,
} from '@vegaprotocol/utils';
import { useValidateAmount } from '@vegaprotocol/react-helpers';
import { type Control, type UseFormWatch } from 'react-hook-form';
import { useForm, Controller, useController } from 'react-hook-form';
import * as Schema from '@vegaprotocol/types';
@@ -31,10 +31,10 @@ import { useOpenVolume } from '@vegaprotocol/positions';
import {
toBigNum,
removeDecimal,
useValidateAmount,
formatForInput,
formatValue,
} from '@vegaprotocol/utils';
import { useValidateAmount } from '@vegaprotocol/react-helpers';
import { activeOrdersProvider } from '@vegaprotocol/orders';
import {
getAsset,
+5 -7
View File
@@ -1,18 +1,16 @@
import type { Asset, AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetOption } from '@vegaprotocol/assets';
import {
addDecimal,
isAssetTypeERC20,
formatNumber,
} from '@vegaprotocol/utils';
import {
useLocalStorage,
useEthereumAddress,
useRequired,
useVegaPublicKey,
useMinSafe,
useMaxSafe,
} from '@vegaprotocol/react-helpers';
addDecimal,
isAssetTypeERC20,
formatNumber,
} from '@vegaprotocol/utils';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import {
TradingFormGroup,
TradingInput,
-2
View File
@@ -16,7 +16,6 @@ import en_markets from './locales/en/markets.json';
import en_web3 from './locales/en/web3.json';
import en_proposals from './locales/en/proposals.json';
import en_positions from './locales/en/positions.json';
import en_react_helpers from './locales/en/react-helpers.json';
import en_trades from './locales/en/trading.json';
import en_ui_toolkit from './locales/en/ui-toolkit.json';
import en_wallet from './locales/en/wallet.json';
@@ -40,7 +39,6 @@ export const locales = {
web3: en_web3,
positions: en_positions,
proposals: en_proposals,
react_helpers: en_react_helpers,
trades: en_trades,
'ui-toolkit': en_ui_toolkit,
wallet: en_wallet,
@@ -27,8 +27,8 @@ import {
formatNumber,
formatNumberPercentage,
getDateTimeFormat,
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import { getMarketExpiryDateFormatted } from '@vegaprotocol/react-helpers';
import type { Get } from 'type-fest';
import { MarketInfoTable } from './info-key-value-table';
import type {
@@ -3,10 +3,10 @@ import {
getDateTimeFormat,
addDecimal,
addDecimalsFormatNumber,
useValidateAmount,
determinePriceStep,
determineSizeStep,
} from '@vegaprotocol/utils';
import { useValidateAmount } from '@vegaprotocol/react-helpers';
import { Size } from '@vegaprotocol/datagrid';
import * as Schema from '@vegaprotocol/types';
import {
@@ -3,8 +3,8 @@ import {
getDateTimeFormat,
isNumeric,
toBigNum,
useFormatTrigger,
} from '@vegaprotocol/utils';
import { useFormatTrigger } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import {
ActionsDropdown,
-3
View File
@@ -12,8 +12,5 @@ export * from './use-theme-switcher';
export * from './use-storybook-theme-observer';
export * from './use-yesterday';
export * from './use-previous';
export * from './use-validate';
export * from './use-market-expiry-date';
export { useScript } from './use-script';
export { useUserAgent } from './use-user-agent';
export { useFormatTrigger } from './use-format-trigger';
@@ -1,121 +0,0 @@
import { useCallback } from 'react';
import BigNumber from 'bignumber.js';
import * as utils from '@vegaprotocol/utils';
import { useT } from './use-t';
export const useRequired = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!utils.validRequired(value)) {
return t('Required');
}
return true;
},
[t]
);
};
export const useEthereumAddress = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!utils.validEthAddress(value)) {
return t('Invalid Ethereum address');
}
return true;
},
[t]
);
};
export const useVegaPublicKey = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!utils.validVegaPublicKey(value)) {
return t('Invalid Vega key');
}
return true;
},
[t]
);
};
export const useMinSafe = () => {
const t = useT();
return useCallback(
(min: BigNumber) => (value: string) => {
if (utils.validMinSafe(value, min)) {
return t('Value is below minimum');
}
return true;
},
[t]
);
};
export const useMaxSafe = () => {
const t = useT();
return useCallback(
(max: BigNumber) => (value: string) => {
if (utils.validMaxSafe(value, max)) {
return t('Value is above maximum');
}
return true;
},
[t]
);
};
export const useValidateJson = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!utils.validJSON(value)) {
return t('Must be valid JSON');
}
return true;
},
[t]
);
};
export const useValidateUrl = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!utils.validUrl(value)) {
return t('Invalid URL');
}
return true;
},
[t]
);
};
/** Used in deal ticket price/size amounts */
export const useValidateAmount = () => {
const t = useT();
return useCallback(
(step: number | string, field: string) => {
return (value?: string) => {
if (!utils.validStep(step, value)) {
if (new BigNumber(step).isEqualTo(1)) {
return t('{{field}} must be whole numbers for this market', {
field,
step,
});
}
return t('{{field}} must be a multiple of {{step}} for this market', {
field,
step,
});
}
return true;
};
},
[t]
);
};
+1 -2
View File
@@ -17,8 +17,7 @@ import {
type SymbolQuery,
type SymbolQueryVariables,
} from './__generated__/Symbol';
import { toBigNum } from '@vegaprotocol/utils';
import { getMarketExpiryDate } from '@vegaprotocol/react-helpers';
import { getMarketExpiryDate, toBigNum } from '@vegaprotocol/utils';
import {
type IBasicDataFeed,
type DatafeedConfiguration,
+15
View File
@@ -34,6 +34,21 @@
"options": {
"jestConfig": "libs/types/jest.config.ts"
}
},
"generate": {
"executor": "nx:run-commands",
"options": {
"commands": ["npx graphql-codegen --config=libs/types/codegen.yml"],
"parallel": false
}
},
"local-registry": {
"executor": "@nx/js:verdaccio",
"options": {
"port": 4873,
"config": ".verdaccio/config.yml",
"storage": "tmp/local-registry/storage"
}
}
},
"tags": []
+4 -6
View File
@@ -1,6 +1,6 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"ignorePatterns": ["!**/*", "__generated__"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -9,18 +9,16 @@
"error",
{
"paths": [
"error",
"@apollo/client",
"@ethersproject",
"@vegaprotocol/data-provider",
"ag-grid-react",
"ag-grid-community",
"ethers",
"graphql",
"graphql-tag",
"graphql-ws",
"react",
"react-dom",
"react-i18next"
"ethers",
"@ethersproject"
],
"patterns": ["@sentry/*"]
}
+4
View File
@@ -7,6 +7,10 @@
"date-fns": "^2.28.0",
"lodash": "^4.17.21"
},
"peerDependencies": {
"react": "18.2.0",
"react-i18next": "13.5.0"
},
"type": "module",
"module": "./index.js"
}
+8
View File
@@ -35,6 +35,14 @@
"options": {
"jestConfig": "libs/utils/jest.config.ts"
}
},
"local-registry": {
"executor": "@nx/js:verdaccio",
"options": {
"port": 4873,
"config": ".verdaccio/config.yml",
"storage": "tmp/local-registry/storage"
}
}
},
"tags": []
+1
View File
@@ -7,6 +7,7 @@ export * from './lib/helpers';
export * from './lib/is-asset-erc20';
export * from './lib/is-valid-url';
export * from './lib/local-storage';
export * from './lib/markets';
export * from './lib/price-change';
export * from './lib/remove-0x';
export * from './lib/remove-pagination-wrapper';
+1
View File
@@ -3,4 +3,5 @@ export * from './number';
export * from './range';
export * from './size';
export * from './strings';
export * from './trigger';
export * from './ether';
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { type StopOrder, StopOrderTriggerDirection } from '@vegaprotocol/types';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { useT } from './use-t';
import { addDecimalsFormatNumber } from './number';
import { useCallback } from 'react';
import { useT } from '../use-t';
export const useFormatTrigger = () => {
const t = useT();
@@ -1,10 +1,42 @@
import { MarketState } from '@vegaprotocol/types';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { isValid, parseISO } from 'date-fns';
import { getDateTimeFormat } from './format';
import { useT } from './use-t';
export const useMarketExpiryDate = (
tags: ReadonlyArray<string> | null | undefined,
export const getMarketExpiryDate = (
tags?: ReadonlyArray<string> | null
): Date | null => {
if (tags) {
const dateFound = tags.reduce<Date | null>((agg, tag) => {
const parsed = parseISO(
(tag.match(/^settlement.*:/) &&
tag
.split(':')
.filter((item, i) => i)
.join(':')) as string
);
if (isValid(parsed)) {
agg = parsed;
}
return agg;
}, null);
return dateFound;
}
return null;
};
export const getMarketExpiryDateFormatted = (
tags?: ReadonlyArray<string> | null
): string | null => {
if (tags) {
const dateFound = getMarketExpiryDate(tags);
return dateFound ? getDateTimeFormat().format(dateFound) : null;
}
return null;
};
export const getExpiryDate = (
tags: ReadonlyArray<string> | null,
close: string | null,
state: MarketState
): string => {
@@ -35,35 +67,3 @@ export const useMarketExpiryDate = (
}
return content;
};
export const getMarketExpiryDateFormatted = (
tags?: ReadonlyArray<string> | null
): string | null => {
if (tags) {
const dateFound = getMarketExpiryDate(tags);
return dateFound ? getDateTimeFormat().format(dateFound) : null;
}
return null;
};
export const getMarketExpiryDate = (
tags?: ReadonlyArray<string> | null
): Date | null => {
if (tags) {
const dateFound = tags.reduce<Date | null>((agg, tag) => {
const parsed = parseISO(
(tag.match(/^settlement.*:/) &&
tag
.split(':')
.filter((item, i) => i)
.join(':')) as string
);
if (isValid(parsed)) {
agg = parsed;
}
return agg;
}, null);
return dateFound;
}
return null;
};
@@ -1,3 +1,3 @@
import { useTranslation } from 'react-i18next';
export const ns = 'react-helpers';
export const ns = 'utils';
export const useT = () => useTranslation(ns).t;
+21 -47
View File
@@ -1,66 +1,40 @@
import { validEthAddress, validVegaPublicKey, validStep } from './common';
import { renderHook } from '@testing-library/react';
import { useEthereumAddress, useVegaPublicKey } from './common';
it('ethereumAddress', () => {
const result = renderHook(useEthereumAddress);
const ethereumAddress = result.result.current;
const errorMessage = 'Invalid Ethereum address';
it('validEthAddress', () => {
const validAddress = '0x72c22822A19D20DE7e426fB84aa047399Ddd8853';
expect(validEthAddress(validAddress)).toEqual(true);
expect(ethereumAddress(validAddress)).toEqual(true);
const invalidChars = '0xzzc22822A19D20DE7e426fB84aa047399Ddd8853';
expect(validEthAddress(invalidChars)).toEqual(false);
expect(ethereumAddress(invalidChars)).toEqual(errorMessage);
const tooManyChars = '0x72c22822A19D20DE7e426fB84aa047399Ddd88531111111';
expect(validEthAddress(tooManyChars)).toEqual(false);
expect(ethereumAddress(tooManyChars)).toEqual(errorMessage);
const no0x = '1x72c22822A19D20DE7e426fB84aa047399Ddd8853';
expect(validEthAddress(no0x)).toEqual(false);
expect(ethereumAddress(no0x)).toEqual(errorMessage);
});
it('validVegaPublicKey', () => {
it('vegaPublicKey', () => {
const result = renderHook(useVegaPublicKey);
const vegaPublicKey = result.result.current;
const errorMessage = 'Invalid Vega key';
const validKey =
'70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680';
expect(validVegaPublicKey(validKey)).toEqual(true);
expect(vegaPublicKey(validKey)).toEqual(true);
const invalidChars =
'zzz14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680';
expect(validVegaPublicKey(invalidChars)).toEqual(false);
expect(vegaPublicKey(invalidChars)).toEqual(errorMessage);
const tooManyChars =
'70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680111111';
expect(validVegaPublicKey(tooManyChars)).toEqual(false);
});
describe('validateAgainstStep', () => {
it('fails when step is an empty string', () => {
expect(validStep('', '1234')).toEqual(false);
});
it.each([
[0, 0],
[1234567890, 0],
[0.03, 0.03],
[0.09, 0.03],
[0.27, 0.03],
[1, 1],
[123, 1],
[4, 2],
[8, 2],
])(
'checks whether given value (%s) IS a multiple of given step (%s)',
(value, step) => {
expect(validStep(step, value)).toEqual(true);
}
);
it.each([
[1, 2],
[0.1, 0.003],
[1.11, 0.1],
[123.1, 1],
[222, 221],
[NaN, 1],
])(
'checks whether given value (%s) IS NOT a multiple of given step (%s)',
(value, step) => {
expect(validStep(step, value)).toEqual(false);
}
);
expect(vegaPublicKey(tooManyChars)).toEqual(errorMessage);
});
+91 -48
View File
@@ -1,52 +1,77 @@
import BigNumber from 'bignumber.js';
import { useT } from '../use-t';
import { useCallback } from 'react';
export const validRequired = (value: string | number | undefined | null) => {
if (value === null || value === undefined || value === '') {
return false;
}
return true;
export const useRequired = () => {
const t = useT();
return useCallback(
(value: string) => {
if (value === null || value === undefined || value === '') {
return t('Required');
}
return true;
},
[t]
);
};
export const useEthereumAddress = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!/^0x[0-9a-fA-F]{40}$/i.test(value)) {
return t('Invalid Ethereum address');
}
return true;
},
[t]
);
};
export const VEGA_ID_REGEX = /^[A-Fa-f0-9]{64}$/i;
export const validVegaPublicKey = (value: string) => {
export const isValidVegaPublicKey = (value: string) => {
return VEGA_ID_REGEX.test(value);
};
export const URL_REGEX =
/^(https?:\/\/)?([a-zA-Z0-9.-]+(\.[a-zA-Z]{2,})+)(:[0-9]{1,5})?(\/[^\s]*)?$/;
export const validUrl = (value: string) => {
return URL_REGEX.test(value);
export const useVegaPublicKey = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!isValidVegaPublicKey(value)) {
return t('Invalid Vega key');
}
return true;
},
[t]
);
};
export const ETH_ADDRESS = /^0x[0-9a-fA-F]{40}$/i;
export const validEthAddress = (value: string) => {
return ETH_ADDRESS.test(value);
export const useMinSafe = () => {
const t = useT();
return useCallback(
(min: BigNumber) => (value: string) => {
if (new BigNumber(value).isLessThan(min)) {
return t('Value is below minimum');
}
return true;
},
[t]
);
};
export const validMinSafe = (
value: string | number | BigNumber,
min: string | number | BigNumber
) => {
return new BigNumber(value).isLessThan(min);
export const useMaxSafe = () => {
const t = useT();
return useCallback(
(max: BigNumber) => (value: string) => {
if (new BigNumber(value).isGreaterThan(max)) {
return t('Value is above maximum');
}
return true;
},
[t]
);
};
export const validMaxSafe = (
value: string | number | BigNumber,
max: string | number | BigNumber
) => {
return new BigNumber(value).isGreaterThan(max);
};
export const validJSON = (value: string) => {
try {
JSON.parse(value);
return true;
} catch (e) {
return false;
}
};
export const validForSyntaxHighlighter = (str: string) => {
export const suitableForSyntaxHighlighter = (str: string) => {
try {
const test = JSON.parse(str);
return test && Object.keys(test).length > 0;
@@ -55,17 +80,35 @@ export const validForSyntaxHighlighter = (str: string) => {
}
};
export const validStep = (step: string | number, input?: string | number) => {
const stepValue = new BigNumber(step);
if (stepValue.isNaN()) {
// unable to check if step is not a number
return false;
}
if (stepValue.isZero()) {
// every number is valid when step is zero
return true;
}
export const useValidateJson = () => {
const t = useT();
return useCallback(
(value: string) => {
try {
JSON.parse(value);
return true;
} catch (e) {
return t('Must be valid JSON');
}
},
[t]
);
};
const value = new BigNumber(input || '');
return value.modulo(stepValue).isZero();
export const URL_REGEX =
/^(https?:\/\/)?([a-zA-Z0-9.-]+(\.[a-zA-Z]{2,})+)(:[0-9]{1,5})?(\/[^\s]*)?$/;
const isValidUrl = (value: string) => {
return URL_REGEX.test(value);
};
export const useValidateUrl = () => {
const t = useT();
return useCallback(
(value: string) => {
if (!isValidUrl(value)) {
return t('Invalid URL');
}
return true;
},
[t]
);
};
+1
View File
@@ -1 +1,2 @@
export * from './common';
export * from './validate-amount';
@@ -0,0 +1,38 @@
import { validateAgainstStep } from './validate-amount';
describe('validateAgainstStep', () => {
it('fails when step is an empty string', () => {
expect(validateAgainstStep('', '1234')).toEqual(false);
});
it.each([
[0, 0],
[1234567890, 0],
[0.03, 0.03],
[0.09, 0.03],
[0.27, 0.03],
[1, 1],
[123, 1],
[4, 2],
[8, 2],
])(
'checks whether given value (%s) IS a multiple of given step (%s)',
(value, step) => {
expect(validateAgainstStep(step, value)).toEqual(true);
}
);
it.each([
[1, 2],
[0.1, 0.003],
[1.11, 0.1],
[123.1, 1],
[222, 221],
[NaN, 1],
])(
'checks whether given value (%s) IS NOT a multiple of given step (%s)',
(value, step) => {
expect(validateAgainstStep(step, value)).toEqual(false);
}
);
});
@@ -0,0 +1,50 @@
import { useCallback } from 'react';
import { useT } from '../use-t';
import BigNumber from 'bignumber.js';
export const useValidateAmount = () => {
const t = useT();
return useCallback(
(step: number | string, field: string) => {
return (value?: string) => {
const isValid = validateAgainstStep(step, value);
if (!isValid) {
if (new BigNumber(step).isEqualTo(1)) {
return t('{{field}} must be whole numbers for this market', {
field,
step,
});
}
return t('{{field}} must be a multiple of {{step}} for this market', {
field,
step,
});
}
return true;
};
},
[t]
);
};
const isMultipleOf = (value: BigNumber, multipleOf: BigNumber) =>
value.modulo(multipleOf).isZero();
export const validateAgainstStep = (
step: string | number,
input?: string | number
) => {
const stepValue = new BigNumber(step);
if (stepValue.isNaN()) {
// unable to check if step is not a number
return false;
}
if (stepValue.isZero()) {
// every number is valid when step is zero
return true;
}
const value = new BigNumber(input || '');
return isMultipleOf(value, stepValue);
};
+8
View File
@@ -35,6 +35,14 @@
"options": {
"jestConfig": "libs/wallet/jest.config.ts"
}
},
"local-registry": {
"executor": "@nx/js:verdaccio",
"options": {
"port": 4873,
"config": ".verdaccio/config.yml",
"storage": "tmp/local-registry/storage"
}
}
},
"tags": []
@@ -1,6 +1,6 @@
import { type StoreApi } from 'zustand';
import { type Store, type Connector } from '../types';
import { validVegaPublicKey } from '@vegaprotocol/utils';
import { isValidVegaPublicKey } from '@vegaprotocol/utils';
import {
ConnectorError,
chainIdError,
@@ -40,7 +40,7 @@ export class ViewPartyConnector implements Connector {
throw userRejectedError();
}
if (!validVegaPublicKey(value)) {
if (!isValidVegaPublicKey(value)) {
throw connectError('invalid public key');
}
@@ -40,9 +40,9 @@ import {
formatNumber,
toBigNum,
truncateByChars,
useFormatTrigger,
HALFMAXGOINT64,
} from '@vegaprotocol/utils';
import { useFormatTrigger } from '@vegaprotocol/react-helpers';
import { useAssetsMapProvider } from '@vegaprotocol/assets';
import { useEthWithdrawApprovalsStore } from './use-ethereum-withdraw-approvals-store';
import { DApp, EXPLORER_TX, useLinks } from '@vegaprotocol/environment';
+4 -6
View File
@@ -1,15 +1,13 @@
import type { Asset } from '@vegaprotocol/assets';
import { AssetOption } from '@vegaprotocol/assets';
import {
removeDecimal,
isAssetTypeERC20,
formatNumber,
} from '@vegaprotocol/utils';
import {
useEthereumAddress,
useRequired,
useMinSafe,
} from '@vegaprotocol/react-helpers';
removeDecimal,
isAssetTypeERC20,
formatNumber,
} from '@vegaprotocol/utils';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import {
TradingFormGroup,
-3
View File
@@ -241,8 +241,5 @@
"graphql": "15.8.0",
"//": "workaround storybook issue: https://github.com/storybookjs/storybook/issues/21642",
"@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.cd77847.0"
},
"nx": {
"includedScripts": []
}
}
-14
View File
@@ -1,14 +0,0 @@
{
"name": "nx-monorepo",
"$schema": "node_modules/nx/schemas/project-schema.json",
"targets": {
"local-registry": {
"executor": "@nx/js:verdaccio",
"options": {
"port": 4873,
"config": ".verdaccio/config.yml",
"storage": "tmp/local-registry/storage"
}
}
}
}