Bug/1686 decimal issues with size and price (#1938)
* chore: fix order validation when market is in pending state * chore: fix order validation when market is in pending state * chore: fix order validation when market is in pending state * chore: fix order validation when market is in pending state - add some urgent memo * chore: fix order validation when market is in pending state - add unit test * chore: fix order validation when market is in pending state - add memos in right places Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
parent
e36d571cbf
commit
596c273657
@ -28,3 +28,5 @@ export const DEAL_TICKET_SECTION = {
|
||||
EXPIRY: 'sec-expiry',
|
||||
SUMMARY: 'sec-summary',
|
||||
};
|
||||
|
||||
export const ERROR_SIZE_DECIMAL = 'step';
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
import { toBigNum } from '@vegaprotocol/react-helpers';
|
||||
@ -33,15 +34,20 @@ export const useOrderMarginValidation = ({ market, estMargin }: Props) => {
|
||||
const margin = toBigNum(estMargin?.margin || 0, 0);
|
||||
const { id, symbol, decimals } =
|
||||
market.tradableInstrument.instrument.product.settlementAsset;
|
||||
if (balance.isZero() || balance.isLessThan(margin)) {
|
||||
const balanceString = balance.toString();
|
||||
const marginString = margin.toString();
|
||||
const memoizedValue = useMemo(() => {
|
||||
return {
|
||||
balance: balance.toString(),
|
||||
margin: margin.toString(),
|
||||
balance: balanceString,
|
||||
margin: marginString,
|
||||
id,
|
||||
symbol,
|
||||
decimals,
|
||||
};
|
||||
}
|
||||
}, [balanceString, marginString, id, symbol, decimals]);
|
||||
|
||||
if (balance.isZero() || balance.isLessThan(margin)) {
|
||||
return memoizedValue;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -11,10 +11,10 @@ import {
|
||||
} from '@vegaprotocol/types';
|
||||
import type { ValidationProps } from './use-order-validation';
|
||||
import { marketTranslations, useOrderValidation } from './use-order-validation';
|
||||
import { ERROR_SIZE_DECIMAL } from './validate-size';
|
||||
import type { DealTicketMarketFragment } from '../deal-ticket/__generated___/DealTicket';
|
||||
import * as OrderMarginValidation from './use-order-margin-validation';
|
||||
import { ValidateMargin } from './validate-margin';
|
||||
import { ERROR_SIZE_DECIMAL } from '../constants';
|
||||
|
||||
jest.mock('@vegaprotocol/wallet');
|
||||
|
||||
@ -174,12 +174,17 @@ describe('useOrderValidation', () => {
|
||||
`(
|
||||
'Returns an error message for market state suspended or pending',
|
||||
({ state }) => {
|
||||
jest
|
||||
.spyOn(OrderMarginValidation, 'useOrderMarginValidation')
|
||||
.mockReturnValue(false);
|
||||
const { result } = setup({
|
||||
market: {
|
||||
...defaultOrder.market,
|
||||
state,
|
||||
tradingMode: MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
|
||||
},
|
||||
orderType: Schema.OrderType.TYPE_LIMIT,
|
||||
orderTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTT,
|
||||
});
|
||||
expect(result.current).toStrictEqual({
|
||||
isDisabled: false,
|
||||
@ -302,4 +307,28 @@ describe('useOrderValidation', () => {
|
||||
testElement.type
|
||||
);
|
||||
});
|
||||
|
||||
it.each`
|
||||
state
|
||||
${MarketState.STATE_PENDING}
|
||||
${MarketState.STATE_PROPOSED}
|
||||
`(
|
||||
'Returns error when market state is pending and size is wrong',
|
||||
({ state }) => {
|
||||
const { result } = setup({
|
||||
fieldErrors: {
|
||||
size: { type: `validate`, message: ERROR_SIZE_DECIMAL },
|
||||
},
|
||||
market: {
|
||||
...market,
|
||||
state,
|
||||
},
|
||||
});
|
||||
expect(result.current).toStrictEqual({
|
||||
isDisabled: true,
|
||||
message: ERROR.FIELD_PRICE_STEP_DECIMAL,
|
||||
section: 'sec-size',
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -12,14 +12,13 @@ import {
|
||||
} from '@vegaprotocol/types';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { ERROR_SIZE_DECIMAL } from './validate-size';
|
||||
import { MarketDataGrid } from '../trading-mode-tooltip';
|
||||
import { compileGridData } from '../trading-mode-tooltip/compile-grid-data';
|
||||
import type { DealTicketMarketFragment } from '../deal-ticket/__generated___/DealTicket';
|
||||
import { ValidateMargin } from './validate-margin';
|
||||
import type { OrderMargin } from '../../hooks/use-order-margin';
|
||||
import { useOrderMarginValidation } from './use-order-margin-validation';
|
||||
import { DEAL_TICKET_SECTION } from '../constants';
|
||||
import { DEAL_TICKET_SECTION, ERROR_SIZE_DECIMAL } from '../constants';
|
||||
|
||||
export const isMarketInAuction = (market: DealTicketMarketFragment) => {
|
||||
return [
|
||||
@ -53,7 +52,7 @@ export type DealTicketSection =
|
||||
|
||||
export const useOrderValidation = ({
|
||||
market,
|
||||
fieldErrors = {},
|
||||
fieldErrors,
|
||||
orderType,
|
||||
orderTimeInForce,
|
||||
estMargin,
|
||||
@ -66,6 +65,80 @@ export const useOrderValidation = ({
|
||||
const minSize = toDecimal(market.positionDecimalPlaces);
|
||||
const isInvalidOrderMargin = useOrderMarginValidation({ market, estMargin });
|
||||
|
||||
const fieldErrorChecking = useMemo<{
|
||||
message: ReactNode | string;
|
||||
isDisabled: boolean;
|
||||
section: DealTicketSection;
|
||||
} | null>(() => {
|
||||
if (fieldErrors?.size?.type || fieldErrors?.price?.type) {
|
||||
if (fieldErrors?.size?.type === 'required') {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('You need to provide a size'),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
if (fieldErrors?.size?.type === 'min') {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(`Size cannot be lower than "${minSize}"`),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.price?.type === 'required' &&
|
||||
orderType !== Schema.OrderType.TYPE_MARKET
|
||||
) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('You need to provide a price'),
|
||||
section: DEAL_TICKET_SECTION.PRICE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.price?.type === 'min' &&
|
||||
orderType !== Schema.OrderType.TYPE_MARKET
|
||||
) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(`The price cannot be negative`),
|
||||
section: DEAL_TICKET_SECTION.PRICE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.size?.type === 'validate' &&
|
||||
fieldErrors?.size?.message === ERROR_SIZE_DECIMAL
|
||||
) {
|
||||
if (market.positionDecimalPlaces === 0) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('Order sizes must be in whole numbers for this market'),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(
|
||||
`The size field accepts up to ${market.positionDecimalPlaces} decimal places`
|
||||
),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [
|
||||
fieldErrors?.size?.type,
|
||||
fieldErrors?.size?.message,
|
||||
fieldErrors?.price?.type,
|
||||
minSize,
|
||||
orderType,
|
||||
market.positionDecimalPlaces,
|
||||
]);
|
||||
|
||||
const { message, isDisabled, section } = useMemo<{
|
||||
message: ReactNode | string;
|
||||
isDisabled: boolean;
|
||||
@ -104,6 +177,9 @@ export const useOrderValidation = ({
|
||||
market.state
|
||||
)
|
||||
) {
|
||||
if (fieldErrorChecking) {
|
||||
return fieldErrorChecking;
|
||||
}
|
||||
return {
|
||||
isDisabled: false,
|
||||
message: t(
|
||||
@ -243,62 +319,8 @@ export const useOrderValidation = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldErrors?.size?.type === 'required') {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('You need to provide a size'),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
if (fieldErrors?.size?.type === 'min') {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(`Size cannot be lower than "${minSize}"`),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.price?.type === 'required' &&
|
||||
orderType !== Schema.OrderType.TYPE_MARKET
|
||||
) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('You need to provide a price'),
|
||||
section: DEAL_TICKET_SECTION.PRICE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.price?.type === 'min' &&
|
||||
orderType !== Schema.OrderType.TYPE_MARKET
|
||||
) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(`The price cannot be negative`),
|
||||
section: DEAL_TICKET_SECTION.PRICE,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
fieldErrors?.size?.type === 'validate' &&
|
||||
fieldErrors?.size?.message === ERROR_SIZE_DECIMAL
|
||||
) {
|
||||
if (market.positionDecimalPlaces === 0) {
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t('Order sizes must be in whole numbers for this market'),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
}
|
||||
return {
|
||||
isDisabled: true,
|
||||
message: t(
|
||||
`The size field accepts up to ${market.positionDecimalPlaces} decimal places`
|
||||
),
|
||||
section: DEAL_TICKET_SECTION.SIZE,
|
||||
};
|
||||
if (fieldErrorChecking) {
|
||||
return fieldErrorChecking;
|
||||
}
|
||||
|
||||
if (isInvalidOrderMargin) {
|
||||
@ -331,12 +353,9 @@ export const useOrderValidation = ({
|
||||
section: '',
|
||||
};
|
||||
}, [
|
||||
minSize,
|
||||
pubKey,
|
||||
market,
|
||||
fieldErrors?.size?.type,
|
||||
fieldErrors?.size?.message,
|
||||
fieldErrors?.price?.type,
|
||||
fieldErrorChecking,
|
||||
orderType,
|
||||
orderTimeInForce,
|
||||
isInvalidOrderMargin,
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const ERROR_SIZE_DECIMAL = 'step';
|
||||
import { ERROR_SIZE_DECIMAL } from '../constants';
|
||||
|
||||
export const validateSize = (step: number) => {
|
||||
const [, stepDecimals = ''] = String(step).split('.');
|
||||
|
Loading…
Reference in New Issue
Block a user