fix: filter rejected markets, order when suspended, remove market state from header, show continuous trading (#761)
* fix: #603 filter out rejected markets & dialog lg width * fix: #609 show trading mode Continuous Trading and hide market state * fix: #656 modify order validation to trade when suspended * fix: #656 fix use order validation tests * fix: #656 format volume no * fix: format volume with positionDecimalPlaces * fix: tests don't need to be async * fix: md:w-[720px] to prevent dialog overflow * fix: add market state translations * fix: imprt type validation props * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update libs/orders/src/lib/order-hooks/use-order-validation.spec.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * Update apps/trading/pages/markets/__generated__/Market.ts Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * fix: fix warning messages based on feedback * fix: capitalize trading mode * fix: capitalize trading mode * fix: remove line 72 on markets.cy.ts * fix: don't show trigger if unspecified * fix: format last price and shrink 0 on warning icon * fix: order sizes must be whole numbers for this market and input warning size 20 * fix: order sizes must be whole numbers for this market and input warning size 20 * fix: format market list * fix: remove market state check from markets.cy.ts * fix: remove market state check from markets.cy.ts * fix: remove market state check from markets.cy.ts * Revert "fix: remove market state check from markets.cy.ts" This reverts commitc9ab55c98a
. * Revert "fix: remove market state check from markets.cy.ts" This reverts commitbe60e56d8a
. * fix: fix markets.cy.ts failing * fix: fix markets.cy.ts failing * fix: fix markets.cy.ts failing * fix: remove extra test from markets.cy.ts * fix: update extra test from markets.cy.ts * fix: update extra test from markets.cy.ts Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
This commit is contained in:
parent
00e786d596
commit
5c038690c3
@ -25,7 +25,7 @@ describe('markets table', () => {
|
|||||||
const expectedMarketHeaders = [
|
const expectedMarketHeaders = [
|
||||||
'Market',
|
'Market',
|
||||||
'Settlement asset',
|
'Settlement asset',
|
||||||
'State',
|
'Trading mode',
|
||||||
'Best bid',
|
'Best bid',
|
||||||
'Best offer',
|
'Best offer',
|
||||||
'Mark price',
|
'Mark price',
|
||||||
@ -64,43 +64,22 @@ describe('markets table', () => {
|
|||||||
mockTradingPage(req, MarketState.Active);
|
mockTradingPage(req, MarketState.Active);
|
||||||
});
|
});
|
||||||
|
|
||||||
// click on active market
|
// click on market
|
||||||
cy.get('[role="gridcell"][col-id=data]').should('be.visible');
|
cy.get('[role="gridcell"][col-id=data]').should('be.visible');
|
||||||
cy.get('[role="gridcell"][col-id=data]').contains('Active').click();
|
cy.get('[role="gridcell"][col-id=name]').contains('ACTIVE MARKET').click();
|
||||||
|
|
||||||
cy.wait('@Market');
|
cy.wait('@Market');
|
||||||
cy.contains('ACTIVE MARKET');
|
cy.contains('ACTIVE MARKET');
|
||||||
cy.url().should('include', '/markets/market-0');
|
cy.url().should('include', '/markets/market-0');
|
||||||
|
verifyMarketSummaryDisplayed();
|
||||||
verifyMarketSummaryDisplayed('Active');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can select a suspended market', () => {
|
function verifyMarketSummaryDisplayed() {
|
||||||
cy.wait('@Markets');
|
|
||||||
cy.get('.ag-root-wrapper').should('be.visible');
|
|
||||||
|
|
||||||
cy.mockGQL((req) => {
|
|
||||||
mockTradingPage(req, MarketState.Suspended);
|
|
||||||
});
|
|
||||||
|
|
||||||
// click on active market
|
|
||||||
cy.get('[role="gridcell"][col-id=data]').should('be.visible');
|
|
||||||
cy.get('[role="gridcell"][col-id=data]').contains('Suspended').click();
|
|
||||||
|
|
||||||
cy.wait('@Market');
|
|
||||||
cy.contains('SUSPENDED MARKET');
|
|
||||||
cy.url().should('include', '/markets/market-1');
|
|
||||||
|
|
||||||
verifyMarketSummaryDisplayed('Suspended');
|
|
||||||
});
|
|
||||||
|
|
||||||
function verifyMarketSummaryDisplayed(expectedMarketState: string) {
|
|
||||||
const marketSummaryBlock = 'market-summary';
|
const marketSummaryBlock = 'market-summary';
|
||||||
const percentageValue = 'price-change-percentage';
|
const percentageValue = 'price-change-percentage';
|
||||||
const priceChangeValue = 'price-change';
|
const priceChangeValue = 'price-change';
|
||||||
const tradingVolume = 'trading-volume';
|
const tradingVolume = 'trading-volume';
|
||||||
const tradingMode = 'trading-mode';
|
const tradingMode = 'trading-mode';
|
||||||
const marketState = 'market-state';
|
|
||||||
|
|
||||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||||
cy.contains('Change (24h)');
|
cy.contains('Change (24h)');
|
||||||
@ -110,8 +89,6 @@ describe('markets table', () => {
|
|||||||
cy.getByTestId(tradingVolume).should('not.be.empty');
|
cy.getByTestId(tradingVolume).should('not.be.empty');
|
||||||
cy.contains('Trading mode');
|
cy.contains('Trading mode');
|
||||||
cy.getByTestId(tradingMode).should('not.be.empty');
|
cy.getByTestId(tradingMode).should('not.be.empty');
|
||||||
cy.contains('State');
|
|
||||||
cy.getByTestId(marketState).should('have.text', expectedMarketState);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -20,6 +20,7 @@ const MARKET_QUERY = gql`
|
|||||||
tradingMode
|
tradingMode
|
||||||
state
|
state
|
||||||
decimalPlaces
|
decimalPlaces
|
||||||
|
positionDecimalPlaces
|
||||||
data {
|
data {
|
||||||
market {
|
market {
|
||||||
id
|
id
|
||||||
@ -31,6 +32,7 @@ const MARKET_QUERY = gql`
|
|||||||
bestStaticBidVolume
|
bestStaticBidVolume
|
||||||
bestStaticOfferVolume
|
bestStaticOfferVolume
|
||||||
indicativeVolume
|
indicativeVolume
|
||||||
|
trigger
|
||||||
}
|
}
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
|
12
apps/trading/pages/markets/__generated__/Market.ts
generated
12
apps/trading/pages/markets/__generated__/Market.ts
generated
@ -3,7 +3,7 @@
|
|||||||
// @generated
|
// @generated
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { Interval, MarketTradingMode, MarketState } from "@vegaprotocol/types";
|
import { Interval, MarketTradingMode, MarketState, AuctionTrigger } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: Market
|
// GraphQL query operation: Market
|
||||||
@ -47,6 +47,10 @@ export interface Market_market_data {
|
|||||||
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
|
* the aggregated volume being offered at the best static offer price, excluding pegged orders.
|
||||||
*/
|
*/
|
||||||
bestStaticOfferVolume: string;
|
bestStaticOfferVolume: string;
|
||||||
|
/**
|
||||||
|
* what triggered an auction (if an auction was started)
|
||||||
|
*/
|
||||||
|
trigger: AuctionTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Market_market_tradableInstrument_instrument_metadata {
|
export interface Market_market_tradableInstrument_instrument_metadata {
|
||||||
@ -144,6 +148,12 @@ export interface Market_market {
|
|||||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||||
*/
|
*/
|
||||||
decimalPlaces: number;
|
decimalPlaces: number;
|
||||||
|
/**
|
||||||
|
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
|
||||||
|
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
|
||||||
|
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
|
||||||
|
*/
|
||||||
|
positionDecimalPlaces: number;
|
||||||
/**
|
/**
|
||||||
* marketData for the given market
|
* marketData for the given market
|
||||||
*/
|
*/
|
||||||
|
@ -11,7 +11,11 @@ import { TradesContainer } from '@vegaprotocol/trades';
|
|||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||||
import type { Market_market } from './__generated__/Market';
|
import type { Market_market } from './__generated__/Market';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import {
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
formatLabel,
|
||||||
|
t,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||||
@ -23,6 +27,8 @@ import {
|
|||||||
PriceCellChange,
|
PriceCellChange,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import type { CandleClose } from '@vegaprotocol/types';
|
import type { CandleClose } from '@vegaprotocol/types';
|
||||||
|
import { AuctionTrigger } from '@vegaprotocol/types';
|
||||||
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
|
||||||
const TradingViews = {
|
const TradingViews = {
|
||||||
Candles: CandlesChartContainer,
|
Candles: CandlesChartContainer,
|
||||||
@ -55,7 +61,7 @@ export const TradeMarketHeader = ({
|
|||||||
const itemClassName =
|
const itemClassName =
|
||||||
'font-sans font-normal mb-0 text-black-60 dark:text-white-80 text-ui-small';
|
'font-sans font-normal mb-0 text-black-60 dark:text-white-80 text-ui-small';
|
||||||
const itemValueClassName =
|
const itemValueClassName =
|
||||||
'capitalize font-sans tracking-tighter text-black dark:text-white text-ui';
|
'font-sans tracking-tighter text-black dark:text-white text-ui';
|
||||||
const headerClassName = classNames(
|
const headerClassName = classNames(
|
||||||
'w-full p-8 bg-white dark:bg-black',
|
'w-full p-8 bg-white dark:bg-black',
|
||||||
className
|
className
|
||||||
@ -87,20 +93,23 @@ export const TradeMarketHeader = ({
|
|||||||
<span className={itemClassName}>Volume</span>
|
<span className={itemClassName}>Volume</span>
|
||||||
<span data-testid="trading-volume" className={itemValueClassName}>
|
<span data-testid="trading-volume" className={itemValueClassName}>
|
||||||
{market.data && market.data.indicativeVolume !== '0'
|
{market.data && market.data.indicativeVolume !== '0'
|
||||||
? market.data.indicativeVolume
|
? addDecimalsFormatNumber(
|
||||||
|
market.data.indicativeVolume,
|
||||||
|
market.positionDecimalPlaces
|
||||||
|
)
|
||||||
: '-'}
|
: '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={headerItemClassName}>
|
<div className={headerItemClassName}>
|
||||||
<span className={itemClassName}>Trading mode</span>
|
<span className={itemClassName}>Trading mode</span>
|
||||||
<span data-testid="trading-mode" className={itemValueClassName}>
|
<span data-testid="trading-mode" className={itemValueClassName}>
|
||||||
{market.tradingMode}
|
{market.tradingMode === MarketTradingMode.MonitoringAuction &&
|
||||||
</span>
|
market.data?.trigger &&
|
||||||
</div>
|
market.data.trigger !== AuctionTrigger.Unspecified
|
||||||
<div className={headerItemClassName}>
|
? `${formatLabel(
|
||||||
<span className={itemClassName}>State</span>
|
market.tradingMode
|
||||||
<span data-testid="market-state" className={itemValueClassName}>
|
)} - ${market.data?.trigger.toLowerCase()}`
|
||||||
{market.state}
|
: formatLabel(market.tradingMode)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,11 @@ import {
|
|||||||
VegaWalletOrderType,
|
VegaWalletOrderType,
|
||||||
VegaWalletOrderTimeInForce,
|
VegaWalletOrderTimeInForce,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
|
import {
|
||||||
|
t,
|
||||||
|
toDecimal,
|
||||||
|
addDecimalsFormatNumber,
|
||||||
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
|
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
import { TypeSelector } from './type-selector';
|
import { TypeSelector } from './type-selector';
|
||||||
import { SideSelector } from './side-selector';
|
import { SideSelector } from './side-selector';
|
||||||
@ -43,22 +47,22 @@ export const DealTicket = ({
|
|||||||
const step = toDecimal(market.positionDecimalPlaces);
|
const step = toDecimal(market.positionDecimalPlaces);
|
||||||
const orderType = watch('type');
|
const orderType = watch('type');
|
||||||
const orderTimeInForce = watch('timeInForce');
|
const orderTimeInForce = watch('timeInForce');
|
||||||
const invalidText = useOrderValidation({
|
const { message, isDisabled: disabled } = useOrderValidation({
|
||||||
step,
|
step,
|
||||||
market,
|
market,
|
||||||
orderType,
|
orderType,
|
||||||
orderTimeInForce,
|
orderTimeInForce,
|
||||||
fieldErrors: errors,
|
fieldErrors: errors,
|
||||||
});
|
});
|
||||||
const isDisabled = transactionStatus === 'pending' || Boolean(invalidText);
|
const isDisabled = transactionStatus === 'pending' || disabled;
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(order: Order) => {
|
(order: Order) => {
|
||||||
if (!isDisabled && !invalidText) {
|
if (!isDisabled) {
|
||||||
submit(order);
|
submit(order);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isDisabled, invalidText, submit]
|
[isDisabled, submit]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -83,7 +87,10 @@ export const DealTicket = ({
|
|||||||
register={register}
|
register={register}
|
||||||
price={
|
price={
|
||||||
market.depth.lastTrade
|
market.depth.lastTrade
|
||||||
? addDecimal(market.depth.lastTrade.price, market.decimalPlaces)
|
? addDecimalsFormatNumber(
|
||||||
|
market.depth.lastTrade.price,
|
||||||
|
market.decimalPlaces
|
||||||
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
quoteName={market.tradableInstrument.instrument.product.quoteName}
|
quoteName={market.tradableInstrument.instrument.product.quoteName}
|
||||||
@ -118,9 +125,12 @@ export const DealTicket = ({
|
|||||||
>
|
>
|
||||||
{transactionStatus === 'pending' ? t('Pending...') : t('Place order')}
|
{transactionStatus === 'pending' ? t('Pending...') : t('Place order')}
|
||||||
</Button>
|
</Button>
|
||||||
{invalidText && (
|
{message && (
|
||||||
<InputError className="mb-8" data-testid="dealticket-error-message">
|
<InputError
|
||||||
{invalidText}
|
className="mt-12 mb-12"
|
||||||
|
data-testid="dealticket-error-message"
|
||||||
|
>
|
||||||
|
{message}
|
||||||
</InputError>
|
</InputError>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
@ -29,7 +29,10 @@ export const LandingDialog = ({ open, setOpen }: LandingDialogProps) => {
|
|||||||
intent={Intent.Primary}
|
intent={Intent.Primary}
|
||||||
open={open}
|
open={open}
|
||||||
onChange={setClose}
|
onChange={setClose}
|
||||||
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
|
titleClassNames={
|
||||||
|
'font-bold font-sans text-3xl tracking-tight mb-0 pl-8'
|
||||||
|
}
|
||||||
|
contentClassNames={'md:w-[520px] lg:w-[520px] w-full'}
|
||||||
>
|
>
|
||||||
<SelectMarketList data={data} onSelect={setClose} />
|
<SelectMarketList data={data} onSelect={setClose} />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// @generated
|
// @generated
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
|
import { MarketState, MarketTradingMode, AuctionTrigger } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL fragment: MarketDataFields
|
// GraphQL fragment: MarketDataFields
|
||||||
@ -43,4 +43,8 @@ export interface MarketDataFields {
|
|||||||
* the mark price (actually an unsigned int)
|
* the mark price (actually an unsigned int)
|
||||||
*/
|
*/
|
||||||
markPrice: string;
|
markPrice: string;
|
||||||
|
/**
|
||||||
|
* what triggered an auction (if an auction was started)
|
||||||
|
*/
|
||||||
|
trigger: AuctionTrigger;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// @generated
|
// @generated
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
|
import { MarketState, MarketTradingMode, AuctionTrigger } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL subscription operation: MarketDataSub
|
// GraphQL subscription operation: MarketDataSub
|
||||||
@ -43,6 +43,10 @@ export interface MarketDataSub_marketData {
|
|||||||
* the mark price (actually an unsigned int)
|
* the mark price (actually an unsigned int)
|
||||||
*/
|
*/
|
||||||
markPrice: string;
|
markPrice: string;
|
||||||
|
/**
|
||||||
|
* what triggered an auction (if an auction was started)
|
||||||
|
*/
|
||||||
|
trigger: AuctionTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarketDataSub {
|
export interface MarketDataSub {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// @generated
|
// @generated
|
||||||
// This file was automatically generated and should not be edited.
|
// This file was automatically generated and should not be edited.
|
||||||
|
|
||||||
import { MarketState, MarketTradingMode } from "@vegaprotocol/types";
|
import { MarketState, MarketTradingMode, AuctionTrigger } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: Markets
|
// GraphQL query operation: Markets
|
||||||
@ -43,6 +43,10 @@ export interface Markets_markets_data {
|
|||||||
* the mark price (actually an unsigned int)
|
* the mark price (actually an unsigned int)
|
||||||
*/
|
*/
|
||||||
markPrice: string;
|
markPrice: string;
|
||||||
|
/**
|
||||||
|
* what triggered an auction (if an auction was started)
|
||||||
|
*/
|
||||||
|
trigger: AuctionTrigger;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Markets_markets_tradableInstrument_instrument_product_settlementAsset {
|
export interface Markets_markets_tradableInstrument_instrument_product_settlementAsset {
|
||||||
|
@ -4,11 +4,13 @@ import {
|
|||||||
PriceFlashCell,
|
PriceFlashCell,
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
t,
|
t,
|
||||||
|
formatLabel,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||||
import { AgGridColumn } from 'ag-grid-react';
|
import { AgGridColumn } from 'ag-grid-react';
|
||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import type { Markets_markets } from '../__generated__/Markets';
|
import type { Markets_markets } from '../__generated__/Markets';
|
||||||
|
import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types';
|
||||||
|
|
||||||
interface MarketListTableProps {
|
interface MarketListTableProps {
|
||||||
datasource: IDatasource;
|
datasource: IDatasource;
|
||||||
@ -44,13 +46,19 @@ export const MarketListTable = forwardRef<AgGridReact, MarketListTableProps>(
|
|||||||
field="tradableInstrument.instrument.product.settlementAsset.symbol"
|
field="tradableInstrument.instrument.product.settlementAsset.symbol"
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('State')}
|
headerName={t('Trading mode')}
|
||||||
field="data"
|
field="data"
|
||||||
valueFormatter={({ value }: ValueFormatterParams) =>
|
minWidth={200}
|
||||||
value === undefined
|
valueFormatter={({ value }: ValueFormatterParams) => {
|
||||||
? value
|
if (!value) return value;
|
||||||
: `${value.market.state} (${value.market.tradingMode})`
|
const { market, trigger } = value;
|
||||||
}
|
return market &&
|
||||||
|
market.tradingMode === MarketTradingMode.MonitoringAuction &&
|
||||||
|
trigger &&
|
||||||
|
trigger !== AuctionTrigger.Unspecified
|
||||||
|
? `${formatLabel(market.tradingMode)} - ${trigger.toLowerCase()}`
|
||||||
|
: formatLabel(market?.tradingMode);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<AgGridColumn
|
<AgGridColumn
|
||||||
headerName={t('Best bid')}
|
headerName={t('Best bid')}
|
||||||
|
@ -10,6 +10,7 @@ import type {
|
|||||||
Markets_markets_data,
|
Markets_markets_data,
|
||||||
} from '../../components/__generated__/Markets';
|
} from '../../components/__generated__/Markets';
|
||||||
import { marketsDataProvider as dataProvider } from './markets-data-provider';
|
import { marketsDataProvider as dataProvider } from './markets-data-provider';
|
||||||
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export const MarketsContainer = () => {
|
export const MarketsContainer = () => {
|
||||||
const { push } = useRouter();
|
const { push } = useRouter();
|
||||||
@ -28,14 +29,15 @@ export const MarketsContainer = () => {
|
|||||||
Markets_markets_data
|
Markets_markets_data
|
||||||
>({ dataProvider, update });
|
>({ dataProvider, update });
|
||||||
dataRef.current = data;
|
dataRef.current = data;
|
||||||
|
|
||||||
const getRows = async ({
|
const getRows = async ({
|
||||||
successCallback,
|
successCallback,
|
||||||
startRow,
|
startRow,
|
||||||
endRow,
|
endRow,
|
||||||
}: IGetRowsParams) => {
|
}: IGetRowsParams) => {
|
||||||
const rowsThisBlock = dataRef.current
|
const rowsThisBlock = dataRef.current
|
||||||
? dataRef.current.slice(startRow, endRow)
|
? dataRef.current
|
||||||
|
.slice(startRow, endRow)
|
||||||
|
.filter((m) => m.data?.market.state !== MarketState.Rejected)
|
||||||
: [];
|
: [];
|
||||||
const lastRow = dataRef.current?.length ?? -1;
|
const lastRow = dataRef.current?.length ?? -1;
|
||||||
successCallback(rowsThisBlock, lastRow);
|
successCallback(rowsThisBlock, lastRow);
|
||||||
|
@ -21,6 +21,7 @@ const MARKET_DATA_FRAGMENT = gql`
|
|||||||
bestBidPrice
|
bestBidPrice
|
||||||
bestOfferPrice
|
bestOfferPrice
|
||||||
markPrice
|
markPrice
|
||||||
|
trigger
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
import orderBy from 'lodash/orderBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import type {
|
import type {
|
||||||
MarketList,
|
MarketList,
|
||||||
@ -11,22 +12,24 @@ export const lastPrice = ({ candles }: MarketList_markets) =>
|
|||||||
|
|
||||||
export const mapDataToMarketList = ({ markets }: MarketList) =>
|
export const mapDataToMarketList = ({ markets }: MarketList) =>
|
||||||
orderBy(
|
orderBy(
|
||||||
markets?.map((m) => {
|
markets
|
||||||
return {
|
?.filter((m) => m.state !== MarketState.Rejected)
|
||||||
id: m.id,
|
.map((m) => {
|
||||||
decimalPlaces: m.decimalPlaces,
|
return {
|
||||||
marketName: m.tradableInstrument.instrument?.code,
|
id: m.id,
|
||||||
lastPrice: lastPrice(m) ?? m.data?.markPrice,
|
decimalPlaces: m.decimalPlaces,
|
||||||
candles: (m.candles || []).filter((c) => c),
|
marketName: m.tradableInstrument.instrument?.code,
|
||||||
open: m.marketTimestamps.open
|
lastPrice: lastPrice(m) ?? m.data?.markPrice,
|
||||||
? new Date(m.marketTimestamps.open).getTime()
|
candles: (m.candles || []).filter((c) => c),
|
||||||
: null,
|
open: m.marketTimestamps.open
|
||||||
close: m.marketTimestamps.close
|
? new Date(m.marketTimestamps.open).getTime()
|
||||||
? new Date(m.marketTimestamps.close).getTime()
|
: null,
|
||||||
: null,
|
close: m.marketTimestamps.close
|
||||||
state: m.state,
|
? new Date(m.marketTimestamps.close).getTime()
|
||||||
};
|
: null,
|
||||||
}) || [],
|
state: m.state,
|
||||||
|
};
|
||||||
|
}) || [],
|
||||||
['state', 'open', 'id'],
|
['state', 'open', 'id'],
|
||||||
['asc', 'asc', 'asc']
|
['asc', 'asc', 'asc']
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ import type {
|
|||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
import type { ValidationProps } from './use-order-validation';
|
import type { ValidationProps } from './use-order-validation';
|
||||||
|
import { marketTranslations } from './use-order-validation';
|
||||||
import { useOrderValidation } from './use-order-validation';
|
import { useOrderValidation } from './use-order-validation';
|
||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
||||||
import type { Market } from '../market';
|
import type { Market } from '../market';
|
||||||
@ -73,12 +74,12 @@ const ERROR = {
|
|||||||
'Only limit orders are permitted when market is in auction',
|
'Only limit orders are permitted when market is in auction',
|
||||||
MARKET_CONTINUOUS_TIF:
|
MARKET_CONTINUOUS_TIF:
|
||||||
'Only GTT, GTC and GFA are permitted when market is in auction',
|
'Only GTT, GTC and GFA are permitted when market is in auction',
|
||||||
FIELD_SIZE_REQ: 'An amount needs to be provided',
|
FIELD_SIZE_REQ: 'You need to provide an amount',
|
||||||
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
||||||
FIELD_PRICE_REQ: 'A price needs to be provided',
|
FIELD_PRICE_REQ: 'You need to provide a price',
|
||||||
FIELD_PRICE_MIN: 'The price cannot be negative',
|
FIELD_PRICE_MIN: 'The price cannot be negative',
|
||||||
FIELD_PRICE_STEP_NULL: 'No decimal amounts allowed for this order',
|
FIELD_PRICE_STEP_NULL: 'Order sizes must be in whole numbers for this market',
|
||||||
FIELD_PRICE_STEP_DECIMAL: `The amount field only takes up to ${market.positionDecimalPlaces} decimals`,
|
FIELD_PRICE_STEP_DECIMAL: `The amount field accepts up to ${market.positionDecimalPlaces} decimal places`,
|
||||||
};
|
};
|
||||||
|
|
||||||
function setup(
|
function setup(
|
||||||
@ -90,107 +91,148 @@ function setup(
|
|||||||
return renderHook(() => useOrderValidation({ ...defaultOrder, ...props }));
|
return renderHook(() => useOrderValidation({ ...defaultOrder, ...props }));
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Returns empty string when given valid data', () => {
|
describe('useOrderValidation', () => {
|
||||||
const { result } = setup();
|
it('Returns empty string when given valid data', () => {
|
||||||
expect(result.current).toEqual('');
|
const { result } = setup();
|
||||||
});
|
expect(result.current).toStrictEqual({ isDisabled: false, message: `` });
|
||||||
|
|
||||||
it('Returns an error message when no keypair found', async () => {
|
|
||||||
const { result } = setup(defaultOrder, { keypair: null });
|
|
||||||
expect(result.current).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Returns an error message when the keypair is tainted', async () => {
|
|
||||||
const { result } = setup(defaultOrder, {
|
|
||||||
keypair: { ...defaultWalletContext.keypair, tainted: true },
|
|
||||||
});
|
});
|
||||||
expect(result.current).toEqual('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each`
|
it('Returns an error message when no keypair found', () => {
|
||||||
state | errorMessage
|
const { result } = setup(defaultOrder, { keypair: null });
|
||||||
${MarketState.Cancelled} | ${ERROR.MARKET_INACTIVE}
|
expect(result.current).toStrictEqual({ isDisabled: false, message: `` });
|
||||||
${MarketState.Closed} | ${ERROR.MARKET_INACTIVE}
|
|
||||||
${MarketState.Rejected} | ${ERROR.MARKET_INACTIVE}
|
|
||||||
${MarketState.Settled} | ${ERROR.MARKET_INACTIVE}
|
|
||||||
${MarketState.TradingTerminated} | ${ERROR.MARKET_INACTIVE}
|
|
||||||
${MarketState.Suspended} | ${ERROR.MARKET_SUSPENDED}
|
|
||||||
${MarketState.Pending} | ${ERROR.MARKET_WAITING}
|
|
||||||
${MarketState.Proposed} | ${ERROR.MARKET_WAITING}
|
|
||||||
`(
|
|
||||||
'Returns an error message for "$marketState" market',
|
|
||||||
async ({ state, errorMessage }) => {
|
|
||||||
const { result } = setup({ market: { ...defaultOrder.market, state } });
|
|
||||||
expect(result.current).toEqual(errorMessage);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it.each`
|
|
||||||
tradingMode | errorMessage
|
|
||||||
${MarketTradingMode.BatchAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
|
||||||
${MarketTradingMode.MonitoringAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
|
||||||
${MarketTradingMode.OpeningAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
|
||||||
`(
|
|
||||||
'Returns an error message when trying to submit a non-limit order for a "$tradingMode" market',
|
|
||||||
async ({ tradingMode, errorMessage }) => {
|
|
||||||
const { result } = setup({
|
|
||||||
market: { ...defaultOrder.market, tradingMode },
|
|
||||||
orderType: VegaWalletOrderType.Market,
|
|
||||||
});
|
|
||||||
expect(result.current).toEqual(errorMessage);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it.each`
|
|
||||||
tradingMode | orderTimeInForce | errorMessage
|
|
||||||
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
|
||||||
`(
|
|
||||||
'Returns an error message when submitting a limit order with a "$orderTimeInForce" value to a "$tradingMode" market',
|
|
||||||
async ({ tradingMode, orderTimeInForce, errorMessage }) => {
|
|
||||||
const { result } = setup({
|
|
||||||
market: { ...defaultOrder.market, tradingMode },
|
|
||||||
orderType: VegaWalletOrderType.Limit,
|
|
||||||
orderTimeInForce,
|
|
||||||
});
|
|
||||||
expect(result.current).toEqual(errorMessage);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it.each`
|
|
||||||
fieldName | errorType | errorMessage
|
|
||||||
${'size'} | ${'required'} | ${ERROR.FIELD_SIZE_REQ}
|
|
||||||
${'size'} | ${'min'} | ${ERROR.FIELD_SIZE_MIN}
|
|
||||||
${'price'} | ${'required'} | ${ERROR.FIELD_PRICE_REQ}
|
|
||||||
${'price'} | ${'min'} | ${ERROR.FIELD_PRICE_MIN}
|
|
||||||
`(
|
|
||||||
'Returns an error message when the order $fieldName "$errorType" validation fails',
|
|
||||||
async ({ fieldName, errorType, errorMessage }) => {
|
|
||||||
const { result } = setup({
|
|
||||||
fieldErrors: { [fieldName]: { type: errorType } },
|
|
||||||
});
|
|
||||||
expect(result.current).toEqual(errorMessage);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it('Returns an error message when the order size incorrectly has decimal values', async () => {
|
|
||||||
const { result } = setup({
|
|
||||||
market: { ...market, positionDecimalPlaces: 0 },
|
|
||||||
fieldErrors: { size: { type: 'validate', message: ERROR_SIZE_DECIMAL } },
|
|
||||||
});
|
});
|
||||||
expect(result.current).toEqual(ERROR.FIELD_PRICE_STEP_NULL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Returns an error message when the order size has more decimals then allowed', async () => {
|
it('Returns an error message when the keypair is tainted', () => {
|
||||||
const { result } = setup({
|
const { result } = setup(defaultOrder, {
|
||||||
fieldErrors: { size: { type: 'validate', message: ERROR_SIZE_DECIMAL } },
|
keypair: { ...defaultWalletContext.keypair, tainted: true },
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({ isDisabled: false, message: `` });
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
state
|
||||||
|
${MarketState.Settled}
|
||||||
|
${MarketState.Rejected}
|
||||||
|
${MarketState.TradingTerminated}
|
||||||
|
`(
|
||||||
|
'Returns an error message for market state when not accepting orders',
|
||||||
|
({ state }) => {
|
||||||
|
const { result } = setup({ market: { ...defaultOrder.market, state } });
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: `This market is ${marketTranslations(
|
||||||
|
state
|
||||||
|
)} and not accepting orders`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
state
|
||||||
|
${MarketState.Suspended}
|
||||||
|
${MarketState.Pending}
|
||||||
|
${MarketState.Cancelled}
|
||||||
|
${MarketState.Proposed}
|
||||||
|
${MarketState.Closed}
|
||||||
|
`(
|
||||||
|
'Returns an error message for market state suspended or pending',
|
||||||
|
({ state }) => {
|
||||||
|
const { result } = setup({
|
||||||
|
market: {
|
||||||
|
...defaultOrder.market,
|
||||||
|
state,
|
||||||
|
tradingMode: MarketTradingMode.BatchAuction,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: false,
|
||||||
|
message: `This market is ${state.toLowerCase()} and only accepting liquidity commitment orders`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
tradingMode | errorMessage
|
||||||
|
${MarketTradingMode.BatchAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
||||||
|
${MarketTradingMode.MonitoringAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
||||||
|
${MarketTradingMode.OpeningAuction} | ${ERROR.MARKET_CONTINUOUS_LIMIT}
|
||||||
|
`(
|
||||||
|
`Returns an error message when trying to submit a non-limit order for a "$tradingMode" market`,
|
||||||
|
({ tradingMode, errorMessage }) => {
|
||||||
|
const { result } = setup({
|
||||||
|
market: { ...defaultOrder.market, tradingMode },
|
||||||
|
orderType: VegaWalletOrderType.Market,
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
tradingMode | orderTimeInForce | errorMessage
|
||||||
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
|
`(
|
||||||
|
`Returns an error message when submitting a limit order with a "$orderTimeInForce" value to a "$tradingMode" market`,
|
||||||
|
({ tradingMode, orderTimeInForce, errorMessage }) => {
|
||||||
|
const { result } = setup({
|
||||||
|
market: { ...defaultOrder.market, tradingMode },
|
||||||
|
orderType: VegaWalletOrderType.Limit,
|
||||||
|
orderTimeInForce,
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
fieldName | errorType | errorMessage
|
||||||
|
${`size`} | ${`required`} | ${ERROR.FIELD_SIZE_REQ}
|
||||||
|
${`size`} | ${`min`} | ${ERROR.FIELD_SIZE_MIN}
|
||||||
|
${`price`} | ${`required`} | ${ERROR.FIELD_PRICE_REQ}
|
||||||
|
${`price`} | ${`min`} | ${ERROR.FIELD_PRICE_MIN}
|
||||||
|
`(
|
||||||
|
`Returns an error message when the order $fieldName "$errorType" validation fails`,
|
||||||
|
({ fieldName, errorType, errorMessage }) => {
|
||||||
|
const { result } = setup({
|
||||||
|
fieldErrors: { [fieldName]: { type: errorType } },
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Returns an error message when the order size incorrectly has decimal values', () => {
|
||||||
|
const { result } = setup({
|
||||||
|
market: { ...market, positionDecimalPlaces: 0 },
|
||||||
|
fieldErrors: { size: { type: `validate`, message: ERROR_SIZE_DECIMAL } },
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: ERROR.FIELD_PRICE_STEP_NULL,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Returns an error message when the order size has more decimals than allowed', () => {
|
||||||
|
const { result } = setup({
|
||||||
|
fieldErrors: { size: { type: `validate`, message: ERROR_SIZE_DECIMAL } },
|
||||||
|
});
|
||||||
|
expect(result.current).toStrictEqual({
|
||||||
|
isDisabled: true,
|
||||||
|
message: ERROR.FIELD_PRICE_STEP_DECIMAL,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
expect(result.current).toEqual(ERROR.FIELD_PRICE_STEP_DECIMAL);
|
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,15 @@ export type ValidationProps = {
|
|||||||
fieldErrors?: FieldErrors<Order>;
|
fieldErrors?: FieldErrors<Order>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const marketTranslations = (marketState: MarketState) => {
|
||||||
|
switch (marketState) {
|
||||||
|
case MarketState.TradingTerminated:
|
||||||
|
return t('terminated');
|
||||||
|
default:
|
||||||
|
return t(marketState).toLowerCase();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const useOrderValidation = ({
|
export const useOrderValidation = ({
|
||||||
step,
|
step,
|
||||||
market,
|
market,
|
||||||
@ -28,33 +37,120 @@ export const useOrderValidation = ({
|
|||||||
}: ValidationProps) => {
|
}: ValidationProps) => {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
|
|
||||||
const invalidText = useMemo(() => {
|
const { message, isDisabled } = useMemo(() => {
|
||||||
if (!keypair) {
|
if (!keypair) {
|
||||||
return t('No public key selected');
|
return { message: t('No public key selected'), isDisabled: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keypair.tainted) {
|
if (keypair.tainted) {
|
||||||
return t('Selected public key has been tainted');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t('Selected public key has been tainted'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
MarketState.Settled,
|
||||||
|
MarketState.Rejected,
|
||||||
|
MarketState.TradingTerminated,
|
||||||
|
].includes(market.state)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(
|
||||||
|
`This market is ${marketTranslations(
|
||||||
|
market.state
|
||||||
|
)} and not accepting orders`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
MarketState.Suspended,
|
||||||
|
MarketState.Pending,
|
||||||
|
MarketState.Proposed,
|
||||||
|
MarketState.Cancelled,
|
||||||
|
MarketState.Closed,
|
||||||
|
].includes(market.state)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: false,
|
||||||
|
message: t(
|
||||||
|
`This market is ${marketTranslations(
|
||||||
|
market.state
|
||||||
|
)} and only accepting liquidity commitment orders`
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (market.state !== MarketState.Active) {
|
if (market.state !== MarketState.Active) {
|
||||||
if (market.state === MarketState.Suspended) {
|
if (market.state === MarketState.Suspended) {
|
||||||
return t('Market is currently suspended');
|
if (market.tradingMode === MarketTradingMode.Continuous) {
|
||||||
|
if (orderType !== OrderType.Limit) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(
|
||||||
|
'Only limit orders are permitted when market is in auction'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
OrderTimeInForce.FOK,
|
||||||
|
OrderTimeInForce.IOC,
|
||||||
|
OrderTimeInForce.GFN,
|
||||||
|
].includes(orderTimeInForce)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(
|
||||||
|
'Only GTT, GTC and GFA are permitted when market is in auction'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isDisabled: false,
|
||||||
|
message: t(
|
||||||
|
`This market is ${marketTranslations(
|
||||||
|
market.state
|
||||||
|
)} and only accepting liquidity commitment orders`
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
market.state === MarketState.Proposed ||
|
market.state === MarketState.Proposed ||
|
||||||
market.state === MarketState.Pending
|
market.state === MarketState.Pending
|
||||||
) {
|
) {
|
||||||
return t('Market is not active yet');
|
return {
|
||||||
|
isDisabled: false,
|
||||||
|
message: t(
|
||||||
|
`This market is ${marketTranslations(
|
||||||
|
market.state
|
||||||
|
)} and only accepting liquidity commitment orders`
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return t('Market is no longer active');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t('This market is no longer active.'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (market.tradingMode !== MarketTradingMode.Continuous) {
|
if (market.tradingMode !== MarketTradingMode.Continuous) {
|
||||||
if (orderType !== OrderType.Limit) {
|
if (orderType !== OrderType.Limit) {
|
||||||
return t('Only limit orders are permitted when market is in auction');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(
|
||||||
|
'Only limit orders are permitted when market is in auction'
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -64,26 +160,41 @@ export const useOrderValidation = ({
|
|||||||
OrderTimeInForce.GFN,
|
OrderTimeInForce.GFN,
|
||||||
].includes(orderTimeInForce)
|
].includes(orderTimeInForce)
|
||||||
) {
|
) {
|
||||||
return t(
|
return {
|
||||||
'Only GTT, GTC and GFA are permitted when market is in auction'
|
isDisabled: true,
|
||||||
);
|
message: t(
|
||||||
|
'Only GTT, GTC and GFA are permitted when market is in auction'
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.size?.type === 'required') {
|
if (fieldErrors?.size?.type === 'required') {
|
||||||
return t('An amount needs to be provided');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t('You need to provide an amount'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.size?.type === 'min') {
|
if (fieldErrors?.size?.type === 'min') {
|
||||||
return t(`The amount cannot be lower than "${step}"`);
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(`The amount cannot be lower than "${step}"`),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.price?.type === 'required') {
|
if (fieldErrors?.price?.type === 'required') {
|
||||||
return t('A price needs to be provided');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t('You need to provide a price'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.price?.type === 'min') {
|
if (fieldErrors?.price?.type === 'min') {
|
||||||
return t(`The price cannot be negative`);
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t(`The price cannot be negative`),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -91,14 +202,20 @@ export const useOrderValidation = ({
|
|||||||
fieldErrors?.size?.message === ERROR_SIZE_DECIMAL
|
fieldErrors?.size?.message === ERROR_SIZE_DECIMAL
|
||||||
) {
|
) {
|
||||||
if (market.positionDecimalPlaces === 0) {
|
if (market.positionDecimalPlaces === 0) {
|
||||||
return t('No decimal amounts allowed for this order');
|
return {
|
||||||
|
isDisabled: true,
|
||||||
|
message: t('Order sizes must be in whole numbers for this market'),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return t(
|
return {
|
||||||
`The amount field only takes up to ${market.positionDecimalPlaces} decimals`
|
isDisabled: true,
|
||||||
);
|
message: t(
|
||||||
|
`The amount field accepts up to ${market.positionDecimalPlaces} decimal places`
|
||||||
|
),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return { isDisabled: false, message: '' };
|
||||||
}, [
|
}, [
|
||||||
keypair,
|
keypair,
|
||||||
step,
|
step,
|
||||||
@ -110,5 +227,5 @@ export const useOrderValidation = ({
|
|||||||
orderTimeInForce,
|
orderTimeInForce,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return invalidText;
|
return { message, isDisabled };
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
|
import capitalize from 'lodash/capitalize';
|
||||||
|
import startCase from 'lodash/startCase';
|
||||||
|
|
||||||
export const getUserLocale = () => 'default';
|
export const getUserLocale = () => 'default';
|
||||||
|
|
||||||
export const splitAt = (index: number) => (x: string) =>
|
export const splitAt = (index: number) => (x: string) =>
|
||||||
[x.slice(0, index), x.slice(index)];
|
[x.slice(0, index), x.slice(index)];
|
||||||
|
|
||||||
|
export const formatLabel = (str: string) =>
|
||||||
|
capitalize(startCase(str).toLowerCase());
|
||||||
|
@ -26,7 +26,7 @@ export function Dialog({
|
|||||||
}: DialogProps) {
|
}: DialogProps) {
|
||||||
const contentClasses = classNames(
|
const contentClasses = classNames(
|
||||||
// Positions the modal in the center of screen
|
// Positions the modal in the center of screen
|
||||||
'z-20 fixed w-full md:w-[520px] lg:w-[940px] px-28 py-24 top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]',
|
'z-20 fixed w-full md:w-[720px] lg:w-[940px] px-28 py-24 top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]',
|
||||||
// Need to apply background and text colors again as content is rendered in a portal
|
// Need to apply background and text colors again as content is rendered in a portal
|
||||||
'dark:bg-black dark:text-white-95 bg-white text-black-95',
|
'dark:bg-black dark:text-white-95 bg-white text-black-95',
|
||||||
getIntentShadow(intent),
|
getIntentShadow(intent),
|
||||||
|
@ -17,6 +17,7 @@ export const Icon = ({ size = 16, name, className, ariaLabel }: IconProps) => {
|
|||||||
'fill-current',
|
'fill-current',
|
||||||
'align-text-bottom',
|
'align-text-bottom',
|
||||||
'fill-current',
|
'fill-current',
|
||||||
|
'shrink-0',
|
||||||
{
|
{
|
||||||
'w-20': size === 20,
|
'w-20': size === 20,
|
||||||
'h-20': size === 20,
|
'h-20': size === 20,
|
||||||
|
@ -21,7 +21,6 @@ export const InputError = ({
|
|||||||
'flex',
|
'flex',
|
||||||
'items-center',
|
'items-center',
|
||||||
'box-border',
|
'box-border',
|
||||||
'h-28',
|
|
||||||
'border-l-4',
|
'border-l-4',
|
||||||
'text-black-95 dark:text-white-95',
|
'text-black-95 dark:text-white-95',
|
||||||
'text-ui',
|
'text-ui',
|
||||||
@ -44,7 +43,7 @@ export const InputError = ({
|
|||||||
{...props}
|
{...props}
|
||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<Icon name="warning-sign" className={iconClassName} />
|
<Icon name="warning-sign" size={20} className={iconClassName} />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,7 @@ import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { OrderType } from '@vegaprotocol/types';
|
import { OrderType } from '@vegaprotocol/types';
|
||||||
import type { Order } from '../wallet-types';
|
import type { Order } from '../wallet-types';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
|
||||||
export interface VegaTransactionDialogProps {
|
export interface VegaTransactionDialogProps {
|
||||||
orderDialogOpen: boolean;
|
orderDialogOpen: boolean;
|
||||||
@ -117,7 +118,8 @@ export const VegaDialog = ({
|
|||||||
>
|
>
|
||||||
{transaction.error && (
|
{transaction.error && (
|
||||||
<pre className="text-ui break-all whitespace-pre-wrap">
|
<pre className="text-ui break-all whitespace-pre-wrap">
|
||||||
{JSON.stringify(transaction.error, null, 2)}
|
{get(transaction.error, 'error') ??
|
||||||
|
JSON.stringify(transaction.error, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
</OrderDialogWrapper>
|
</OrderDialogWrapper>
|
||||||
|
Loading…
Reference in New Issue
Block a user