Chore/657 refactor wallet and orders libs (#709)
* feat: 470 edit orders hook and @vegaprotocol/vegawallet-service-api-client@0.4.14 * fix: 470 add methods for dialog intent and title * fix: #657 rename order-list lib to orders * chore: #657 move hooks to orders lib * chore: #657 vega tx dialog used for order cancellation and order submission * chore: #657 use client subscribe and unsubscribe on reset, refactor vegatxdialog * fix: #657 revert script src=./env-config.js ending * fix: #657 format project.json * Update project.json * fix: #657 cancel all subs and async tasks in useffect cleanup function * feat: #657 styling updates on vega order dialog * fix: #657 rename set dialog open and awaiting confirmation dialog update * fix: #657 updates on cancel order id check * fix: #657 fix vega tx dialog test * fix: #657 fix cypress trading-deal-tciket test * fix: #657 fix data-testid market test * Update libs/orders/README.md Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Update libs/wallet/src/vega-transaction-dialog/vega-transaction-dialog.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Update libs/wallet/src/vega-order-transaction-dialog/vega-order-transaction-dialog.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * fix: #657 remove the magic string and use the ordertype enum from types package * fix: #657 guarantee that order.id is present at this point or we need to determine the id of the order * fix: #657 fix translation in dialog * fix: #657 rename wallet types, delete ticket query, set finalized order null in submit * fix: #657 fix deal ticket steps test * fix: #657 remove settings.json * fix: #657 use order submit in orders lib * fix: #463 final modal links to block explorer * fix: #745 long/short instead of buy/sell * fix: #657 use only one vega tx dialog * fix: #657 keep ref of subscription and unsubscribe * fix: #657 hide cancelled orders * fix: #657 sub only when id set * fix: WIP: trying to unsub when order updated * fix: #745 long/short instead of buy/sell * fix: ensure observable defined * fix: #657 remove redundant test * Update libs/orders/src/lib/order-hooks/use-order-submit.ts * fix: failing test due to resizeobserver loop limit exceeded * fix: lint * fix: #657 fix test resize observer loop limit exceeded Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> Co-authored-by: Matthew Russell <mattrussell36@gmail.com> Co-authored-by: Joe <joe@vega.xyz>
This commit is contained in:
parent
0291c37cfb
commit
07abc2b1eb
@ -2,25 +2,28 @@ import * as React from 'react';
|
|||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { Stepper } from '../stepper';
|
import { Stepper } from '../stepper';
|
||||||
import type { DealTicketQuery_market, Order } from '@vegaprotocol/deal-ticket';
|
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
|
||||||
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
|
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
|
||||||
import {
|
import {
|
||||||
ExpirySelector,
|
ExpirySelector,
|
||||||
SideSelector,
|
SideSelector,
|
||||||
TimeInForceSelector,
|
TimeInForceSelector,
|
||||||
TypeSelector,
|
TypeSelector,
|
||||||
getDefaultOrder,
|
|
||||||
useOrderValidation,
|
|
||||||
useOrderSubmit,
|
|
||||||
DealTicketAmount,
|
DealTicketAmount,
|
||||||
MarketSelector,
|
MarketSelector,
|
||||||
} from '@vegaprotocol/deal-ticket';
|
} from '@vegaprotocol/deal-ticket';
|
||||||
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import {
|
import {
|
||||||
OrderTimeInForce,
|
VegaWalletOrderTimeInForce as OrderTimeInForce,
|
||||||
OrderType,
|
VegaWalletOrderType as OrderType,
|
||||||
VegaTxStatus,
|
VegaTxStatus,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
|
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
|
import {
|
||||||
|
getDefaultOrder,
|
||||||
|
useOrderValidation,
|
||||||
|
useOrderSubmit,
|
||||||
|
} from '@vegaprotocol/orders';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
|
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
|
||||||
|
@ -18,21 +18,25 @@ describe('accounts', () => {
|
|||||||
|
|
||||||
cy.getByTestId('tab-accounts').should('be.visible');
|
cy.getByTestId('tab-accounts').should('be.visible');
|
||||||
cy.getByTestId('tab-accounts')
|
cy.getByTestId('tab-accounts')
|
||||||
|
.should('be.visible')
|
||||||
.get(`[row-id='General-tEURO-null']`)
|
.get(`[row-id='General-tEURO-null']`)
|
||||||
.find('[col-id="asset.symbol"]')
|
.find('[col-id="asset.symbol"]')
|
||||||
.should('have.text', 'tEURO');
|
.should('have.text', 'tEURO');
|
||||||
|
|
||||||
cy.getByTestId('tab-accounts')
|
cy.getByTestId('tab-accounts')
|
||||||
|
.should('be.visible')
|
||||||
.get(`[row-id='General-tEURO-null']`)
|
.get(`[row-id='General-tEURO-null']`)
|
||||||
.find('[col-id="type"]')
|
.find('[col-id="type"]')
|
||||||
.should('have.text', 'General');
|
.should('have.text', 'General');
|
||||||
|
|
||||||
cy.getByTestId('tab-accounts')
|
cy.getByTestId('tab-accounts')
|
||||||
|
.should('be.visible')
|
||||||
.get(`[row-id='General-tEURO-null']`)
|
.get(`[row-id='General-tEURO-null']`)
|
||||||
.find('[col-id="market.name"]')
|
.find('[col-id="market.name"]')
|
||||||
.should('have.text', '—');
|
.should('have.text', '—');
|
||||||
|
|
||||||
cy.getByTestId('tab-accounts')
|
cy.getByTestId('tab-accounts')
|
||||||
|
.should('be.visible')
|
||||||
.get(`[row-id='General-tEURO-null']`)
|
.get(`[row-id='General-tEURO-null']`)
|
||||||
.find('[col-id="balance"]')
|
.find('[col-id="balance"]')
|
||||||
.should('have.text', '1,000.00000');
|
.should('have.text', '1,000.00000');
|
||||||
|
@ -33,7 +33,7 @@ describe('deal ticket orders', () => {
|
|||||||
const orderTIFDropDown = 'order-tif';
|
const orderTIFDropDown = 'order-tif';
|
||||||
const placeOrderBtn = 'place-order';
|
const placeOrderBtn = 'place-order';
|
||||||
const orderStatusHeader = 'order-status-header';
|
const orderStatusHeader = 'order-status-header';
|
||||||
const orderTransactionHash = 'tx-hash';
|
const orderTransactionHash = 'tx-block-explorer';
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
|
@ -19,6 +19,7 @@ describe('positions', () => {
|
|||||||
cy.getByTestId('tab-positions').should('be.visible');
|
cy.getByTestId('tab-positions').should('be.visible');
|
||||||
cy.getByTestId('tab-positions')
|
cy.getByTestId('tab-positions')
|
||||||
.get('[col-id="market.tradableInstrument.instrument.code"]')
|
.get('[col-id="market.tradableInstrument.instrument.code"]')
|
||||||
|
.should('be.visible')
|
||||||
.each(($marketSymbol) => {
|
.each(($marketSymbol) => {
|
||||||
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { Orders, Orders_party_orders } from '@vegaprotocol/order-list';
|
import type { Orders, Orders_party_orders } from '@vegaprotocol/orders';
|
||||||
import {
|
import {
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
OrderTimeInForce,
|
OrderTimeInForce,
|
||||||
|
@ -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 { MarketTradingMode } from "@vegaprotocol/types";
|
import { MarketTradingMode, MarketState } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: MarketsLanding
|
// GraphQL query operation: MarketsLanding
|
||||||
@ -27,6 +27,10 @@ export interface MarketsLanding_markets {
|
|||||||
* Current mode of execution of the market
|
* Current mode of execution of the market
|
||||||
*/
|
*/
|
||||||
tradingMode: MarketTradingMode;
|
tradingMode: MarketTradingMode;
|
||||||
|
/**
|
||||||
|
* Current state of the market
|
||||||
|
*/
|
||||||
|
state: MarketState;
|
||||||
/**
|
/**
|
||||||
* timestamps for state changes in the market
|
* timestamps for state changes in the market
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { gql, useQuery } from '@apollo/client';
|
import { gql, useQuery } from '@apollo/client';
|
||||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import sortBy from 'lodash/sortBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useGlobalStore } from '../stores';
|
import { useGlobalStore } from '../stores';
|
||||||
@ -12,6 +12,7 @@ const MARKETS_QUERY = gql`
|
|||||||
markets {
|
markets {
|
||||||
id
|
id
|
||||||
tradingMode
|
tradingMode
|
||||||
|
state
|
||||||
marketTimestamps {
|
marketTimestamps {
|
||||||
open
|
open
|
||||||
}
|
}
|
||||||
@ -20,13 +21,13 @@ const MARKETS_QUERY = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const marketList = ({ markets }: MarketsLanding) =>
|
const marketList = ({ markets }: MarketsLanding) =>
|
||||||
sortBy(
|
orderBy(
|
||||||
markets?.filter(
|
markets?.filter(
|
||||||
({ marketTimestamps, tradingMode }) =>
|
({ marketTimestamps, tradingMode }) =>
|
||||||
marketTimestamps.open && tradingMode === MarketTradingMode.Continuous
|
marketTimestamps.open && tradingMode === MarketTradingMode.Continuous
|
||||||
) || [],
|
) || [],
|
||||||
'marketTimestamps.open',
|
['state', 'marketTimestamps.open', 'id'],
|
||||||
'id'
|
['asc', 'asc', 'asc']
|
||||||
);
|
);
|
||||||
|
|
||||||
export function Index() {
|
export function Index() {
|
||||||
|
@ -81,6 +81,7 @@ const MarketPage = ({ id }: { id?: string }) => {
|
|||||||
return (
|
return (
|
||||||
<PageQueryContainer<Market, MarketVariables>
|
<PageQueryContainer<Market, MarketVariables>
|
||||||
query={MARKET_QUERY}
|
query={MARKET_QUERY}
|
||||||
|
data-testid="market"
|
||||||
options={{
|
options={{
|
||||||
variables: {
|
variables: {
|
||||||
marketId,
|
marketId,
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
DealTicketContainer,
|
DealTicketContainer,
|
||||||
MarketInfoContainer,
|
MarketInfoContainer,
|
||||||
} from '@vegaprotocol/deal-ticket';
|
} from '@vegaprotocol/deal-ticket';
|
||||||
import { OrderListContainer } from '@vegaprotocol/order-list';
|
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||||
import { TradesContainer } from '@vegaprotocol/trades';
|
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';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Web3Container } from '../../components/web3-container';
|
import { Web3Container } from '../../components/web3-container';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||||
import { OrderListContainer } from '@vegaprotocol/order-list';
|
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||||
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
|
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
|
||||||
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
|
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import type { UseFormRegister } from 'react-hook-form';
|
import type { UseFormRegister } from 'react-hook-form';
|
||||||
import { OrderType } from '@vegaprotocol/wallet';
|
import { VegaWalletOrderType } from '@vegaprotocol/wallet';
|
||||||
import type { Order } from '../utils/get-default-order';
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
import { DealTicketMarketAmount } from './deal-ticket-market-amount';
|
import { DealTicketMarketAmount } from './deal-ticket-market-amount';
|
||||||
import { DealTicketLimitAmount } from './deal-ticket-limit-amount';
|
import { DealTicketLimitAmount } from './deal-ticket-limit-amount';
|
||||||
|
|
||||||
export interface DealTicketAmountProps {
|
export interface DealTicketAmountProps {
|
||||||
orderType: OrderType;
|
orderType: VegaWalletOrderType;
|
||||||
step: number;
|
step: number;
|
||||||
register: UseFormRegister<Order>;
|
register: UseFormRegister<Order>;
|
||||||
quoteName: string;
|
quoteName: string;
|
||||||
@ -17,9 +17,9 @@ export const DealTicketAmount = ({
|
|||||||
...props
|
...props
|
||||||
}: DealTicketAmountProps) => {
|
}: DealTicketAmountProps) => {
|
||||||
switch (orderType) {
|
switch (orderType) {
|
||||||
case OrderType.Market:
|
case VegaWalletOrderType.Market:
|
||||||
return <DealTicketMarketAmount {...props} />;
|
return <DealTicketMarketAmount {...props} />;
|
||||||
case OrderType.Limit:
|
case VegaWalletOrderType.Limit:
|
||||||
return <DealTicketLimitAmount {...props} />;
|
return <DealTicketLimitAmount {...props} />;
|
||||||
default: {
|
default: {
|
||||||
throw new Error('Invalid ticket type');
|
throw new Error('Invalid ticket type');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { validateSize } from '../utils/validate-size';
|
import { validateSize } from '@vegaprotocol/orders';
|
||||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||||
|
|
||||||
export type DealTicketLimitAmountProps = Omit<
|
export type DealTicketLimitAmountProps = Omit<
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
import { VegaTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
|
||||||
import { OrderStatus } from '@vegaprotocol/types';
|
|
||||||
import { VegaOrderTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
|
|
||||||
import { DealTicket } from './deal-ticket';
|
import { DealTicket } from './deal-ticket';
|
||||||
import { useOrderSubmit } from '../hooks/use-order-submit';
|
|
||||||
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
|
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
|
||||||
|
import { useOrderSubmit } from '@vegaprotocol/orders';
|
||||||
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export interface DealTicketManagerProps {
|
export interface DealTicketManagerProps {
|
||||||
market: DealTicketQuery_market;
|
market: DealTicketQuery_market;
|
||||||
@ -18,43 +17,20 @@ export const DealTicketManager = ({
|
|||||||
}: DealTicketManagerProps) => {
|
}: DealTicketManagerProps) => {
|
||||||
const [orderDialogOpen, setOrderDialogOpen] = useState(false);
|
const [orderDialogOpen, setOrderDialogOpen] = useState(false);
|
||||||
const { submit, transaction, finalizedOrder, reset } = useOrderSubmit(market);
|
const { submit, transaction, finalizedOrder, reset } = useOrderSubmit(market);
|
||||||
|
const getDialogTitle = (status?: string) => {
|
||||||
const getDialogIntent = (status: VegaTxStatus) => {
|
switch (status) {
|
||||||
if (finalizedOrder) {
|
case OrderStatus.Active:
|
||||||
if (
|
return 'Order submitted';
|
||||||
finalizedOrder.status === OrderStatus.Active ||
|
case OrderStatus.Filled:
|
||||||
finalizedOrder.status === OrderStatus.Filled ||
|
return 'Order filled';
|
||||||
finalizedOrder.status === OrderStatus.PartiallyFilled
|
case OrderStatus.PartiallyFilled:
|
||||||
) {
|
return 'Order partially filled';
|
||||||
return Intent.Success;
|
case OrderStatus.Parked:
|
||||||
}
|
return 'Order parked';
|
||||||
|
default:
|
||||||
if (finalizedOrder.status === OrderStatus.Parked) {
|
return 'Submission failed';
|
||||||
return Intent.Warning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Intent.Danger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === VegaTxStatus.Requested) {
|
|
||||||
return Intent.Warning;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === VegaTxStatus.Error) {
|
|
||||||
return Intent.Danger;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Intent.None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (transaction.status !== VegaTxStatus.Default || finalizedOrder) {
|
|
||||||
setOrderDialogOpen(true);
|
|
||||||
} else {
|
|
||||||
setOrderDialogOpen(false);
|
|
||||||
}
|
|
||||||
}, [finalizedOrder, transaction.status]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{children || (
|
{children || (
|
||||||
@ -69,23 +45,15 @@ export const DealTicketManager = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Dialog
|
<VegaTransactionDialog
|
||||||
open={orderDialogOpen}
|
key={`submit-order-dialog-${transaction.txHash}`}
|
||||||
onChange={(isOpen) => {
|
orderDialogOpen={orderDialogOpen}
|
||||||
setOrderDialogOpen(isOpen);
|
setOrderDialogOpen={setOrderDialogOpen}
|
||||||
|
finalizedOrder={finalizedOrder}
|
||||||
// If closing reset
|
transaction={transaction}
|
||||||
if (!isOpen) {
|
reset={reset}
|
||||||
reset();
|
title={getDialogTitle(finalizedOrder?.status)}
|
||||||
}
|
/>
|
||||||
}}
|
|
||||||
intent={getDialogIntent(transaction.status)}
|
|
||||||
>
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
transaction={transaction}
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { validateSize } from '../utils/validate-size';
|
import { validateSize } from '@vegaprotocol/orders';
|
||||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||||
|
|
||||||
export type DealTicketMarketAmountProps = Omit<
|
export type DealTicketMarketAmountProps = Omit<
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
VegaWalletContext,
|
VegaWalletContext,
|
||||||
OrderTimeInForce,
|
VegaWalletOrderTimeInForce,
|
||||||
OrderType,
|
VegaWalletOrderType,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { addDecimal } from '@vegaprotocol/react-helpers';
|
import { addDecimal } from '@vegaprotocol/react-helpers';
|
||||||
import { fireEvent, render, screen, act } from '@testing-library/react';
|
import { fireEvent, render, screen, act } from '@testing-library/react';
|
||||||
@ -64,7 +64,7 @@ it('Displays ticket defaults', () => {
|
|||||||
|
|
||||||
// Assert defaults are used
|
// Assert defaults are used
|
||||||
expect(
|
expect(
|
||||||
screen.getByTestId(`order-type-${OrderType.Market}-selected`)
|
screen.getByTestId(`order-type-${VegaWalletOrderType.Market}-selected`)
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('order-side-SIDE_BUY-selected')
|
screen.queryByTestId('order-side-SIDE_BUY-selected')
|
||||||
@ -75,7 +75,9 @@ it('Displays ticket defaults', () => {
|
|||||||
expect(screen.getByTestId('order-size')).toHaveDisplayValue(
|
expect(screen.getByTestId('order-size')).toHaveDisplayValue(
|
||||||
String(1 / Math.pow(10, market.positionDecimalPlaces))
|
String(1 / Math.pow(10, market.positionDecimalPlaces))
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.IOC
|
||||||
|
);
|
||||||
|
|
||||||
// Assert last price is shown
|
// Assert last price is shown
|
||||||
expect(screen.getByTestId('last-price')).toHaveTextContent(
|
expect(screen.getByTestId('last-price')).toHaveTextContent(
|
||||||
@ -101,9 +103,11 @@ it('Can edit deal ticket', async () => {
|
|||||||
expect(screen.getByTestId('order-size')).toHaveDisplayValue('200');
|
expect(screen.getByTestId('order-size')).toHaveDisplayValue('200');
|
||||||
|
|
||||||
fireEvent.change(screen.getByTestId('order-tif'), {
|
fireEvent.change(screen.getByTestId('order-tif'), {
|
||||||
target: { value: OrderTimeInForce.IOC },
|
target: { value: VegaWalletOrderTimeInForce.IOC },
|
||||||
});
|
});
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.IOC
|
||||||
|
);
|
||||||
|
|
||||||
// Switch to limit order
|
// Switch to limit order
|
||||||
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
||||||
@ -113,7 +117,7 @@ it('Can edit deal ticket', async () => {
|
|||||||
|
|
||||||
// Check all TIF options shown
|
// Check all TIF options shown
|
||||||
expect(screen.getByTestId('order-tif').children).toHaveLength(
|
expect(screen.getByTestId('order-tif').children).toHaveLength(
|
||||||
Object.keys(OrderTimeInForce).length
|
Object.keys(VegaWalletOrderTimeInForce).length
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -130,26 +134,34 @@ it('Handles TIF select box dependent on order type', () => {
|
|||||||
// Switch to limit order and check all TIF options shown
|
// Switch to limit order and check all TIF options shown
|
||||||
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
||||||
expect(screen.getByTestId('order-tif').children).toHaveLength(
|
expect(screen.getByTestId('order-tif').children).toHaveLength(
|
||||||
Object.keys(OrderTimeInForce).length
|
Object.keys(VegaWalletOrderTimeInForce).length
|
||||||
);
|
);
|
||||||
|
|
||||||
// Change to GTC
|
// Change to GTC
|
||||||
fireEvent.change(screen.getByTestId('order-tif'), {
|
fireEvent.change(screen.getByTestId('order-tif'), {
|
||||||
target: { value: OrderTimeInForce.GTC },
|
target: { value: VegaWalletOrderTimeInForce.GTC },
|
||||||
});
|
});
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.GTC);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.GTC
|
||||||
|
);
|
||||||
|
|
||||||
// Switch back to market order and TIF should now be IOC
|
// Switch back to market order and TIF should now be IOC
|
||||||
fireEvent.click(screen.getByTestId('order-type-TYPE_MARKET'));
|
fireEvent.click(screen.getByTestId('order-type-TYPE_MARKET'));
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.IOC
|
||||||
|
);
|
||||||
|
|
||||||
// Switch tif to FOK
|
// Switch tif to FOK
|
||||||
fireEvent.change(screen.getByTestId('order-tif'), {
|
fireEvent.change(screen.getByTestId('order-tif'), {
|
||||||
target: { value: OrderTimeInForce.FOK },
|
target: { value: VegaWalletOrderTimeInForce.FOK },
|
||||||
});
|
});
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.FOK);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.FOK
|
||||||
|
);
|
||||||
|
|
||||||
// Change back to limit and check we are still on FOK
|
// Change back to limit and check we are still on FOK
|
||||||
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
|
||||||
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.FOK);
|
expect(screen.getByTestId('order-tif')).toHaveValue(
|
||||||
|
VegaWalletOrderTimeInForce.FOK
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useForm, Controller } from 'react-hook-form';
|
import { useForm, Controller } from 'react-hook-form';
|
||||||
import { OrderType, OrderTimeInForce } from '@vegaprotocol/wallet';
|
import {
|
||||||
|
VegaWalletOrderType,
|
||||||
|
VegaWalletOrderTimeInForce,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
|
import { t, addDecimal, toDecimal } 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';
|
||||||
import { DealTicketAmount } from './deal-ticket-amount';
|
import { DealTicketAmount } from './deal-ticket-amount';
|
||||||
import { TimeInForceSelector } from './time-in-force-selector';
|
import { TimeInForceSelector } from './time-in-force-selector';
|
||||||
import { useOrderValidation } from '../hooks/use-order-validation';
|
|
||||||
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
|
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
|
||||||
import type { Order } from '../utils/get-default-order';
|
|
||||||
import { getDefaultOrder } from '../utils/get-default-order';
|
|
||||||
import { ExpirySelector } from './expiry-selector';
|
import { ExpirySelector } from './expiry-selector';
|
||||||
|
import type { Order } from '@vegaprotocol/orders';
|
||||||
|
import { getDefaultOrder, useOrderValidation } from '@vegaprotocol/orders';
|
||||||
|
|
||||||
export type TransactionStatus = 'default' | 'pending';
|
export type TransactionStatus = 'default' | 'pending';
|
||||||
|
|
||||||
@ -97,8 +99,8 @@ export const DealTicket = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{orderType === OrderType.Limit &&
|
{orderType === VegaWalletOrderType.Limit &&
|
||||||
orderTimeInForce === OrderTimeInForce.GTT && (
|
orderTimeInForce === VegaWalletOrderTimeInForce.GTT && (
|
||||||
<Controller
|
<Controller
|
||||||
name="expiration"
|
name="expiration"
|
||||||
control={control}
|
control={control}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { FormGroup } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup } from '@vegaprotocol/ui-toolkit';
|
||||||
import { OrderSide } from '@vegaprotocol/wallet';
|
import { VegaWalletOrderSide } from '@vegaprotocol/wallet';
|
||||||
import { Toggle } from '@vegaprotocol/ui-toolkit';
|
import { Toggle } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
interface SideSelectorProps {
|
interface SideSelectorProps {
|
||||||
value: OrderSide;
|
value: VegaWalletOrderSide;
|
||||||
onSelect: (side: OrderSide) => void;
|
onSelect: (side: VegaWalletOrderSide) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SideSelector = ({ value, onSelect }: SideSelectorProps) => {
|
export const SideSelector = ({ value, onSelect }: SideSelectorProps) => {
|
||||||
const toggles = Object.entries(OrderSide).map(([label, value]) => ({
|
const toggles = Object.entries(VegaWalletOrderSide).map(([label, value]) => ({
|
||||||
label,
|
label: label === 'Buy' ? 'Long' : 'Short',
|
||||||
value,
|
value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export const SideSelector = ({ value, onSelect }: SideSelectorProps) => {
|
|||||||
name="order-side"
|
name="order-side"
|
||||||
toggles={toggles}
|
toggles={toggles}
|
||||||
checkedValue={value}
|
checkedValue={value}
|
||||||
onChange={(e) => onSelect(e.target.value as OrderSide)}
|
onChange={(e) => onSelect(e.target.value as VegaWalletOrderSide)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { FormGroup, Select } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup, Select } from '@vegaprotocol/ui-toolkit';
|
||||||
import { OrderTimeInForce, OrderType } from '@vegaprotocol/wallet';
|
import {
|
||||||
|
VegaWalletOrderTimeInForce,
|
||||||
|
VegaWalletOrderType,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
interface TimeInForceSelectorProps {
|
interface TimeInForceSelectorProps {
|
||||||
value: OrderTimeInForce;
|
value: VegaWalletOrderTimeInForce;
|
||||||
orderType: OrderType;
|
orderType: VegaWalletOrderType;
|
||||||
onSelect: (tif: OrderTimeInForce) => void;
|
onSelect: (tif: VegaWalletOrderTimeInForce) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TimeInForceSelector = ({
|
export const TimeInForceSelector = ({
|
||||||
@ -14,12 +17,12 @@ export const TimeInForceSelector = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
}: TimeInForceSelectorProps) => {
|
}: TimeInForceSelectorProps) => {
|
||||||
const options =
|
const options =
|
||||||
orderType === OrderType.Limit
|
orderType === VegaWalletOrderType.Limit
|
||||||
? Object.entries(OrderTimeInForce)
|
? Object.entries(VegaWalletOrderTimeInForce)
|
||||||
: Object.entries(OrderTimeInForce).filter(
|
: Object.entries(VegaWalletOrderTimeInForce).filter(
|
||||||
([_, timeInForce]) =>
|
([_, timeInForce]) =>
|
||||||
timeInForce === OrderTimeInForce.FOK ||
|
timeInForce === VegaWalletOrderTimeInForce.FOK ||
|
||||||
timeInForce === OrderTimeInForce.IOC
|
timeInForce === VegaWalletOrderTimeInForce.IOC
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -27,7 +30,7 @@ export const TimeInForceSelector = ({
|
|||||||
<Select
|
<Select
|
||||||
id="select-time-in-force"
|
id="select-time-in-force"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onSelect(e.target.value as OrderTimeInForce)}
|
onChange={(e) => onSelect(e.target.value as VegaWalletOrderTimeInForce)}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
data-testid="order-tif"
|
data-testid="order-tif"
|
||||||
>
|
>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { FormGroup } from '@vegaprotocol/ui-toolkit';
|
import { FormGroup } from '@vegaprotocol/ui-toolkit';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
import { OrderType } from '@vegaprotocol/wallet';
|
import { VegaWalletOrderType } from '@vegaprotocol/wallet';
|
||||||
import { Toggle } from '@vegaprotocol/ui-toolkit';
|
import { Toggle } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface TypeSelectorProps {
|
interface TypeSelectorProps {
|
||||||
value: OrderType;
|
value: VegaWalletOrderType;
|
||||||
onSelect: (type: OrderType) => void;
|
onSelect: (type: VegaWalletOrderType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggles = Object.entries(OrderType).map(([label, value]) => ({
|
const toggles = Object.entries(VegaWalletOrderType).map(([label, value]) => ({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
}));
|
}));
|
||||||
@ -21,7 +21,7 @@ export const TypeSelector = ({ value, onSelect }: TypeSelectorProps) => {
|
|||||||
name="order-type"
|
name="order-type"
|
||||||
toggles={toggles}
|
toggles={toggles}
|
||||||
checkedValue={value}
|
checkedValue={value}
|
||||||
onChange={(e) => onSelect(e.target.value as OrderType)}
|
onChange={(e) => onSelect(e.target.value as VegaWalletOrderType)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
|
114
libs/deal-ticket/src/hooks/__generated__/OrderEvent.ts
generated
114
libs/deal-ticket/src/hooks/__generated__/OrderEvent.ts
generated
@ -1,114 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @generated
|
|
||||||
// This file was automatically generated and should not be edited.
|
|
||||||
|
|
||||||
import { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
|
|
||||||
|
|
||||||
// ====================================================
|
|
||||||
// GraphQL subscription operation: OrderEvent
|
|
||||||
// ====================================================
|
|
||||||
|
|
||||||
export interface OrderEvent_busEvents_event_TimeUpdate {
|
|
||||||
__typename: "TimeUpdate" | "MarketEvent" | "TransferResponses" | "PositionResolution" | "Trade" | "Account" | "Party" | "MarginLevels" | "Proposal" | "Vote" | "MarketData" | "NodeSignature" | "LossSocialization" | "SettlePosition" | "Market" | "Asset" | "MarketTick" | "SettleDistressed" | "AuctionEvent" | "RiskFactor" | "Deposit" | "Withdrawal" | "OracleSpec" | "LiquidityProvision";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OrderEvent_busEvents_event_Order_market {
|
|
||||||
__typename: "Market";
|
|
||||||
/**
|
|
||||||
* Market full name
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
|
||||||
* number denominated in the currency of the Market. (uint64)
|
|
||||||
*
|
|
||||||
* Examples:
|
|
||||||
* Currency Balance decimalPlaces Real Balance
|
|
||||||
* GBP 100 0 GBP 100
|
|
||||||
* GBP 100 2 GBP 1.00
|
|
||||||
* GBP 100 4 GBP 0.01
|
|
||||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
|
||||||
*
|
|
||||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
|
||||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
|
||||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
|
||||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OrderEvent_busEvents_event_Order {
|
|
||||||
__typename: "Order";
|
|
||||||
/**
|
|
||||||
* Type the order type (defaults to PARTY)
|
|
||||||
*/
|
|
||||||
type: OrderType | null;
|
|
||||||
/**
|
|
||||||
* Hash of the order data
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* The status of an order, for example 'Active'
|
|
||||||
*/
|
|
||||||
status: OrderStatus;
|
|
||||||
/**
|
|
||||||
* Reason for the order to be rejected
|
|
||||||
*/
|
|
||||||
rejectionReason: OrderRejectionReason | null;
|
|
||||||
/**
|
|
||||||
* RFC3339Nano formatted date and time for when the order was created (timestamp)
|
|
||||||
*/
|
|
||||||
createdAt: string;
|
|
||||||
/**
|
|
||||||
* Total number of contracts that may be bought or sold (immutable) (uint64)
|
|
||||||
*/
|
|
||||||
size: string;
|
|
||||||
/**
|
|
||||||
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
|
||||||
*/
|
|
||||||
price: string;
|
|
||||||
/**
|
|
||||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
|
||||||
*/
|
|
||||||
market: OrderEvent_busEvents_event_Order_market | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OrderEvent_busEvents_event = OrderEvent_busEvents_event_TimeUpdate | OrderEvent_busEvents_event_Order;
|
|
||||||
|
|
||||||
export interface OrderEvent_busEvents {
|
|
||||||
__typename: "BusEvent";
|
|
||||||
/**
|
|
||||||
* the id for this event
|
|
||||||
*/
|
|
||||||
eventId: string;
|
|
||||||
/**
|
|
||||||
* the block hash
|
|
||||||
*/
|
|
||||||
block: string;
|
|
||||||
/**
|
|
||||||
* the type of event we're dealing with
|
|
||||||
*/
|
|
||||||
type: BusEventType;
|
|
||||||
/**
|
|
||||||
* the payload - the wrapped event
|
|
||||||
*/
|
|
||||||
event: OrderEvent_busEvents_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OrderEvent {
|
|
||||||
/**
|
|
||||||
* Subscribe to event data from the event bus
|
|
||||||
*/
|
|
||||||
busEvents: OrderEvent_busEvents[] | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OrderEventVariables {
|
|
||||||
partyId: string;
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { act, renderHook } from '@testing-library/react-hooks';
|
|
||||||
import type { Order } from '../utils/get-default-order';
|
|
||||||
import type {
|
|
||||||
VegaKeyExtended,
|
|
||||||
VegaWalletContextShape,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
|
|
||||||
import { OrderSide, OrderTimeInForce, OrderType } from '@vegaprotocol/wallet';
|
|
||||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import { useOrderSubmit } from './use-order-submit';
|
|
||||||
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
|
|
||||||
|
|
||||||
const defaultMarket: DealTicketQuery_market = {
|
|
||||||
__typename: 'Market',
|
|
||||||
id: 'market-id',
|
|
||||||
decimalPlaces: 2,
|
|
||||||
positionDecimalPlaces: 1,
|
|
||||||
tradingMode: MarketTradingMode.Continuous,
|
|
||||||
state: MarketState.Active,
|
|
||||||
tradableInstrument: {
|
|
||||||
__typename: 'TradableInstrument',
|
|
||||||
instrument: {
|
|
||||||
__typename: 'Instrument',
|
|
||||||
product: {
|
|
||||||
__typename: 'Future',
|
|
||||||
quoteName: 'quote-name',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
depth: {
|
|
||||||
__typename: 'MarketDepth',
|
|
||||||
lastTrade: {
|
|
||||||
__typename: 'Trade',
|
|
||||||
price: '100',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultWalletContext = {
|
|
||||||
keypair: null,
|
|
||||||
keypairs: [],
|
|
||||||
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
|
||||||
connect: jest.fn(),
|
|
||||||
disconnect: jest.fn(),
|
|
||||||
selectPublicKey: jest.fn(),
|
|
||||||
connector: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
function setup(
|
|
||||||
context?: Partial<VegaWalletContextShape>,
|
|
||||||
market = defaultMarket
|
|
||||||
) {
|
|
||||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
||||||
<MockedProvider>
|
|
||||||
<VegaWalletContext.Provider
|
|
||||||
value={{ ...defaultWalletContext, ...context }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</VegaWalletContext.Provider>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
return renderHook(() => useOrderSubmit(market), { wrapper });
|
|
||||||
}
|
|
||||||
|
|
||||||
it('Has the correct default state', () => {
|
|
||||||
const { result } = setup();
|
|
||||||
expect(typeof result.current.submit).toEqual('function');
|
|
||||||
expect(typeof result.current.reset).toEqual('function');
|
|
||||||
expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
|
|
||||||
expect(result.current.transaction.txHash).toEqual(null);
|
|
||||||
expect(result.current.transaction.error).toEqual(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not sendTx if no keypair', async () => {
|
|
||||||
const mockSendTx = jest.fn();
|
|
||||||
const { result } = setup({ sendTx: mockSendTx, keypairs: [], keypair: null });
|
|
||||||
await act(async () => {
|
|
||||||
result.current.submit({} as Order);
|
|
||||||
});
|
|
||||||
expect(mockSendTx).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not sendTx side is not specified', async () => {
|
|
||||||
const mockSendTx = jest.fn();
|
|
||||||
const keypair = {
|
|
||||||
pub: '0x123',
|
|
||||||
} as VegaKeyExtended;
|
|
||||||
const { result } = setup({
|
|
||||||
sendTx: mockSendTx,
|
|
||||||
keypairs: [keypair],
|
|
||||||
keypair,
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
result.current.submit({} as Order);
|
|
||||||
});
|
|
||||||
expect(mockSendTx).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Create an Id if a signature is returned', async () => {
|
|
||||||
const signature =
|
|
||||||
'597a7706491e6523c091bab1e4d655b62c45a224e80f6cd92ac366aa5dd9a070cc7dd3c6919cb07b81334b876c662dd43bdbe5e827c8baa17a089feb654fab0b';
|
|
||||||
const expectedId =
|
|
||||||
'2fe09b0e2e6ed35f8883802629c7d609d3cc2fc9ce3cec0b7824a0d581bd3747';
|
|
||||||
const successObj = {
|
|
||||||
tx: {
|
|
||||||
inputData: 'input-data',
|
|
||||||
signature: {
|
|
||||||
algo: 'algo',
|
|
||||||
version: 1,
|
|
||||||
value: signature,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
txHash: '0x123',
|
|
||||||
};
|
|
||||||
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve(successObj));
|
|
||||||
const keypair = {
|
|
||||||
pub: '0x123',
|
|
||||||
} as VegaKeyExtended;
|
|
||||||
const { result } = setup({
|
|
||||||
sendTx: mockSendTx,
|
|
||||||
keypairs: [keypair],
|
|
||||||
keypair,
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
result.current.submit({
|
|
||||||
type: OrderType.Market,
|
|
||||||
side: OrderSide.Buy,
|
|
||||||
size: '1',
|
|
||||||
timeInForce: OrderTimeInForce.FOK,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
expect(result.current.id).toEqual(expectedId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should submit a correctly formatted order', async () => {
|
|
||||||
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve({}));
|
|
||||||
const keypair = {
|
|
||||||
pub: '0x123',
|
|
||||||
} as VegaKeyExtended;
|
|
||||||
const { result } = setup(
|
|
||||||
{
|
|
||||||
sendTx: mockSendTx,
|
|
||||||
keypairs: [keypair],
|
|
||||||
keypair,
|
|
||||||
},
|
|
||||||
defaultMarket
|
|
||||||
);
|
|
||||||
|
|
||||||
const order: Order = {
|
|
||||||
type: OrderType.Limit,
|
|
||||||
size: '10',
|
|
||||||
timeInForce: OrderTimeInForce.GTT,
|
|
||||||
side: OrderSide.Buy,
|
|
||||||
price: '1234567.89',
|
|
||||||
expiration: new Date('2022-01-01'),
|
|
||||||
};
|
|
||||||
await act(async () => {
|
|
||||||
result.current.submit(order);
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockSendTx).toHaveBeenCalledWith({
|
|
||||||
pubKey: keypair.pub,
|
|
||||||
propagate: true,
|
|
||||||
orderSubmission: {
|
|
||||||
type: OrderType.Limit,
|
|
||||||
marketId: defaultMarket.id, // Market provided from hook arugment
|
|
||||||
size: '100', // size adjusted based on positionDecimalPlaces
|
|
||||||
side: OrderSide.Buy,
|
|
||||||
timeInForce: OrderTimeInForce.GTT,
|
|
||||||
price: '123456789', // Decimal removed
|
|
||||||
expiresAt: order.expiration?.getTime() + '000000', // Nanoseconds appened
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,102 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { useSubscription } from '@apollo/client';
|
|
||||||
import type { Order } from '../utils/get-default-order';
|
|
||||||
import type {
|
|
||||||
OrderEvent,
|
|
||||||
OrderEventVariables,
|
|
||||||
OrderEvent_busEvents_event_Order,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
import {
|
|
||||||
OrderType,
|
|
||||||
useVegaWallet,
|
|
||||||
ORDER_EVENT_SUB,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
|
|
||||||
import { useVegaTransaction } from '@vegaprotocol/wallet';
|
|
||||||
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
|
|
||||||
|
|
||||||
export const useOrderSubmit = (market: DealTicketQuery_market) => {
|
|
||||||
const { keypair } = useVegaWallet();
|
|
||||||
const { send, transaction, reset: resetTransaction } = useVegaTransaction();
|
|
||||||
const [id, setId] = useState('');
|
|
||||||
const [finalizedOrder, setFinalizedOrder] =
|
|
||||||
useState<OrderEvent_busEvents_event_Order | null>(null);
|
|
||||||
|
|
||||||
// Start a subscription looking for the newly created order
|
|
||||||
useSubscription<OrderEvent, OrderEventVariables>(ORDER_EVENT_SUB, {
|
|
||||||
variables: { partyId: keypair?.pub || '' },
|
|
||||||
skip: !id,
|
|
||||||
onSubscriptionData: ({ subscriptionData }) => {
|
|
||||||
if (!subscriptionData.data?.busEvents?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No types available for the subscription result
|
|
||||||
const matchingOrderEvent = subscriptionData.data.busEvents.find((e) => {
|
|
||||||
if (e.event.__typename !== 'Order') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.event.id === id;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
matchingOrderEvent &&
|
|
||||||
matchingOrderEvent.event.__typename === 'Order'
|
|
||||||
) {
|
|
||||||
setFinalizedOrder(matchingOrderEvent.event);
|
|
||||||
resetTransaction();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const submit = useCallback(
|
|
||||||
async (order: Order) => {
|
|
||||||
if (!keypair || !order.side) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setFinalizedOrder(null);
|
|
||||||
|
|
||||||
const res = await send({
|
|
||||||
pubKey: keypair.pub,
|
|
||||||
propagate: true,
|
|
||||||
orderSubmission: {
|
|
||||||
marketId: market.id,
|
|
||||||
price:
|
|
||||||
order.type === OrderType.Limit && order.price
|
|
||||||
? removeDecimal(order.price, market.decimalPlaces)
|
|
||||||
: undefined,
|
|
||||||
size: removeDecimal(order.size, market.positionDecimalPlaces),
|
|
||||||
type: order.type,
|
|
||||||
side: order.side,
|
|
||||||
timeInForce: order.timeInForce,
|
|
||||||
expiresAt: order.expiration
|
|
||||||
? // Wallet expects timestamp in nanoseconds, we don't have that level of accuracy so
|
|
||||||
// just append 6 zeroes
|
|
||||||
order.expiration.getTime().toString() + '000000'
|
|
||||||
: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res?.signature) {
|
|
||||||
setId(determineId(res.signature));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[market, keypair, send]
|
|
||||||
);
|
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
|
||||||
resetTransaction();
|
|
||||||
setFinalizedOrder(null);
|
|
||||||
setId('');
|
|
||||||
}, [resetTransaction]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
transaction,
|
|
||||||
finalizedOrder,
|
|
||||||
id,
|
|
||||||
submit,
|
|
||||||
reset,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,4 +1 @@
|
|||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './utils/get-default-order';
|
|
||||||
export * from './hooks/use-order-submit';
|
|
||||||
export * from './hooks/use-order-validation';
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { OrderTimeInForce, OrderType, OrderSide } from '@vegaprotocol/wallet';
|
|
||||||
import { toDecimal } from '@vegaprotocol/react-helpers';
|
|
||||||
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
|
|
||||||
|
|
||||||
export type Order =
|
|
||||||
| {
|
|
||||||
size: string;
|
|
||||||
type: OrderType.Market;
|
|
||||||
timeInForce: OrderTimeInForce;
|
|
||||||
side: OrderSide;
|
|
||||||
price?: never;
|
|
||||||
expiration?: never;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
size: string;
|
|
||||||
type: OrderType.Limit;
|
|
||||||
timeInForce: OrderTimeInForce;
|
|
||||||
side: OrderSide;
|
|
||||||
price?: string;
|
|
||||||
expiration?: Date;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDefaultOrder = (market: DealTicketQuery_market): Order => ({
|
|
||||||
type: OrderType.Market,
|
|
||||||
side: OrderSide.Buy,
|
|
||||||
timeInForce: OrderTimeInForce.IOC,
|
|
||||||
size: String(toDecimal(market.positionDecimalPlaces)),
|
|
||||||
});
|
|
@ -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 } from "@vegaprotocol/types";
|
import { Interval, MarketState } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL query operation: MarketList
|
// GraphQL query operation: MarketList
|
||||||
@ -108,6 +108,10 @@ export interface MarketList_markets {
|
|||||||
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
|
||||||
*/
|
*/
|
||||||
decimalPlaces: number;
|
decimalPlaces: number;
|
||||||
|
/**
|
||||||
|
* Current state of the market
|
||||||
|
*/
|
||||||
|
state: MarketState;
|
||||||
/**
|
/**
|
||||||
* marketData for the given market
|
* marketData for the given market
|
||||||
*/
|
*/
|
||||||
|
@ -55,6 +55,7 @@ export const MARKET_LIST_QUERY = gql`
|
|||||||
markets {
|
markets {
|
||||||
id
|
id
|
||||||
decimalPlaces
|
decimalPlaces
|
||||||
|
state
|
||||||
data {
|
data {
|
||||||
market {
|
market {
|
||||||
id
|
id
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import sortBy from 'lodash/sortBy';
|
import orderBy from 'lodash/orderBy';
|
||||||
import type {
|
import type {
|
||||||
MarketList,
|
MarketList,
|
||||||
MarketList_markets,
|
MarketList_markets,
|
||||||
@ -10,7 +10,7 @@ export const lastPrice = ({ candles }: MarketList_markets) =>
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
export const mapDataToMarketList = ({ markets }: MarketList) =>
|
export const mapDataToMarketList = ({ markets }: MarketList) =>
|
||||||
sortBy(
|
orderBy(
|
||||||
markets?.map((m) => {
|
markets?.map((m) => {
|
||||||
return {
|
return {
|
||||||
id: m.id,
|
id: m.id,
|
||||||
@ -24,8 +24,9 @@ export const mapDataToMarketList = ({ markets }: MarketList) =>
|
|||||||
close: m.marketTimestamps.close
|
close: m.marketTimestamps.close
|
||||||
? new Date(m.marketTimestamps.close).getTime()
|
? new Date(m.marketTimestamps.close).getTime()
|
||||||
: null,
|
: null,
|
||||||
|
state: m.state,
|
||||||
};
|
};
|
||||||
}) || [],
|
}) || [],
|
||||||
'open',
|
['state', 'open', 'id'],
|
||||||
'id'
|
['asc', 'asc', 'asc']
|
||||||
);
|
);
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
# order-list
|
|
||||||
|
|
||||||
This library was generated with [Nx](https://nx.dev).
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `nx test order-list` to execute the unit tests via [Jest](https://jestjs.io).
|
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@vegaprotocol/order-list",
|
|
||||||
"version": "0.0.1"
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import { OrderStatus, OrderType } from '@vegaprotocol/types';
|
|
||||||
import type { VegaTxState, Order } from '@vegaprotocol/wallet';
|
|
||||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
|
||||||
import type { CancelDialogProps } from './cancel-dialog';
|
|
||||||
import { CancelDialog } from './cancel-dialog';
|
|
||||||
|
|
||||||
describe('CancelDialog', () => {
|
|
||||||
let defaultProps: CancelDialogProps;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
defaultProps = {
|
|
||||||
orderDialogOpen: true,
|
|
||||||
setOrderDialogOpen: () => false,
|
|
||||||
transaction: {
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
},
|
|
||||||
finalizedOrder: {
|
|
||||||
status: OrderStatus.Cancelled,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
},
|
|
||||||
reset: jest.fn(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when an order is successfully cancelled', () => {
|
|
||||||
render(<CancelDialog {...defaultProps} />);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Order cancelled'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when an order is not successfully cancelled', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
const finalizedOrder: Order = {
|
|
||||||
status: OrderStatus.Active,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
};
|
|
||||||
const propsForTest = {
|
|
||||||
transaction,
|
|
||||||
finalizedOrder,
|
|
||||||
};
|
|
||||||
|
|
||||||
render(<CancelDialog {...defaultProps} {...propsForTest} />);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Cancellation failed'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,65 +0,0 @@
|
|||||||
import { OrderStatus } from '@vegaprotocol/types';
|
|
||||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import type { Order, VegaTxState } from '@vegaprotocol/wallet';
|
|
||||||
import { VegaTxStatus, VegaOrderTransactionDialog } from '@vegaprotocol/wallet';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
export interface CancelDialogProps {
|
|
||||||
orderDialogOpen: boolean;
|
|
||||||
setOrderDialogOpen: (isOpen: boolean) => void;
|
|
||||||
finalizedOrder: Order | null;
|
|
||||||
transaction: VegaTxState;
|
|
||||||
reset: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CancelDialog = ({
|
|
||||||
orderDialogOpen,
|
|
||||||
setOrderDialogOpen,
|
|
||||||
finalizedOrder,
|
|
||||||
transaction,
|
|
||||||
reset,
|
|
||||||
}: CancelDialogProps) => {
|
|
||||||
const getDialogIntent = () => {
|
|
||||||
if (finalizedOrder) {
|
|
||||||
if (finalizedOrder.status === OrderStatus.Cancelled) {
|
|
||||||
return Intent.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Intent.Danger;
|
|
||||||
}
|
|
||||||
return Intent.None;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (transaction.status !== VegaTxStatus.Default || finalizedOrder) {
|
|
||||||
setOrderDialogOpen(true);
|
|
||||||
} else {
|
|
||||||
setOrderDialogOpen(false);
|
|
||||||
}
|
|
||||||
}, [finalizedOrder, setOrderDialogOpen, transaction.status]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
open={orderDialogOpen}
|
|
||||||
onChange={(isOpen) => {
|
|
||||||
setOrderDialogOpen(isOpen);
|
|
||||||
|
|
||||||
// If closing reset
|
|
||||||
if (!isOpen) {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
intent={getDialogIntent()}
|
|
||||||
>
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
transaction={transaction}
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
title={
|
|
||||||
finalizedOrder?.status === OrderStatus.Cancelled
|
|
||||||
? 'Order cancelled'
|
|
||||||
: 'Cancellation failed'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export * from './cancel-dialog';
|
|
@ -1 +0,0 @@
|
|||||||
export * from './components';
|
|
7
libs/orders/README.md
Normal file
7
libs/orders/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Orders
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test orders` to execute the unit tests via [Jest](https://jestjs.io).
|
@ -1,10 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
displayName: 'order-list',
|
displayName: 'orders',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
coverageDirectory: '../../coverage/libs/order-list',
|
coverageDirectory: '../../coverage/libs/orders',
|
||||||
setupFilesAfterEnv: ['./src/setup-tests.ts'],
|
setupFilesAfterEnv: ['./src/setup-tests.ts'],
|
||||||
};
|
};
|
4
libs/orders/package.json
Normal file
4
libs/orders/package.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "@vegaprotocol/orders",
|
||||||
|
"version": "0.0.1"
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"root": "libs/order-list",
|
"root": "libs/orders",
|
||||||
"sourceRoot": "libs/order-list/src",
|
"sourceRoot": "libs/orders/src",
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"tags": [],
|
"tags": [],
|
||||||
"targets": {
|
"targets": {
|
||||||
@ -8,16 +8,16 @@
|
|||||||
"executor": "@nrwl/web:rollup",
|
"executor": "@nrwl/web:rollup",
|
||||||
"outputs": ["{options.outputPath}"],
|
"outputs": ["{options.outputPath}"],
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/libs/order-list",
|
"outputPath": "dist/libs/orders",
|
||||||
"tsConfig": "libs/order-list/tsconfig.lib.json",
|
"tsConfig": "libs/orders/tsconfig.lib.json",
|
||||||
"project": "libs/order-list/package.json",
|
"project": "libs/orders/package.json",
|
||||||
"entryFile": "libs/order-list/src/index.ts",
|
"entryFile": "libs/orders/src/index.ts",
|
||||||
"external": ["react/jsx-runtime"],
|
"external": ["react/jsx-runtime"],
|
||||||
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
||||||
"compiler": "babel",
|
"compiler": "babel",
|
||||||
"assets": [
|
"assets": [
|
||||||
{
|
{
|
||||||
"glob": "libs/order-list/README.md",
|
"glob": "libs/orders/README.md",
|
||||||
"input": ".",
|
"input": ".",
|
||||||
"output": "."
|
"output": "."
|
||||||
}
|
}
|
||||||
@ -28,14 +28,14 @@
|
|||||||
"executor": "@nrwl/linter:eslint",
|
"executor": "@nrwl/linter:eslint",
|
||||||
"outputs": ["{options.outputFile}"],
|
"outputs": ["{options.outputFile}"],
|
||||||
"options": {
|
"options": {
|
||||||
"lintFilePatterns": ["libs/order-list/**/*.{ts,tsx,js,jsx}"]
|
"lintFilePatterns": ["libs/orders/**/*.{ts,tsx,js,jsx}"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"executor": "@nrwl/jest:jest",
|
"executor": "@nrwl/jest:jest",
|
||||||
"outputs": ["coverage/libs/order-list"],
|
"outputs": ["coverage/libs/orders"],
|
||||||
"options": {
|
"options": {
|
||||||
"jestConfig": "libs/order-list/jest.config.js",
|
"jestConfig": "libs/orders/jest.config.js",
|
||||||
"passWithNoTests": true
|
"passWithNoTests": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"uiFramework": "@storybook/react",
|
"uiFramework": "@storybook/react",
|
||||||
"port": 4400,
|
"port": 4400,
|
||||||
"config": {
|
"config": {
|
||||||
"configFolder": "libs/order-list/.storybook"
|
"configFolder": "libs/orders/.storybook"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
@ -59,9 +59,9 @@
|
|||||||
"outputs": ["{options.outputPath}"],
|
"outputs": ["{options.outputPath}"],
|
||||||
"options": {
|
"options": {
|
||||||
"uiFramework": "@storybook/react",
|
"uiFramework": "@storybook/react",
|
||||||
"outputPath": "dist/storybook/order-list",
|
"outputPath": "dist/storybook/orders",
|
||||||
"config": {
|
"config": {
|
||||||
"configFolder": "libs/order-list/.storybook"
|
"configFolder": "libs/orders/.storybook"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
@ -38,14 +38,14 @@ export interface Orders_party_orders_market {
|
|||||||
/**
|
/**
|
||||||
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
|
||||||
* number denominated in the currency of the Market. (uint64)
|
* number denominated in the currency of the Market. (uint64)
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* Currency Balance decimalPlaces Real Balance
|
* Currency Balance decimalPlaces Real Balance
|
||||||
* GBP 100 0 GBP 100
|
* GBP 100 0 GBP 100
|
||||||
* GBP 100 2 GBP 1.00
|
* GBP 100 2 GBP 1.00
|
||||||
* GBP 100 4 GBP 0.01
|
* GBP 100 4 GBP 0.01
|
||||||
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
* GBP 1 4 GBP 0.0001 ( 0.01p )
|
||||||
*
|
*
|
||||||
* GBX (pence) 100 0 GBP 1.00 (100p )
|
* GBX (pence) 100 0 GBP 1.00 (100p )
|
||||||
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
* GBX (pence) 100 2 GBP 0.01 ( 1p )
|
||||||
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
|
@ -1,5 +1,4 @@
|
|||||||
export * from './__generated__';
|
export * from './__generated__';
|
||||||
export * from './cancel-order-dialog';
|
|
||||||
export * from './mocks';
|
export * from './mocks';
|
||||||
export * from './order-data-provider';
|
export * from './order-data-provider';
|
||||||
export * from './order-list';
|
export * from './order-list';
|
@ -22,7 +22,7 @@ export const generateOrders = (override?: PartialDeep<Orders>): Orders => {
|
|||||||
return merge(defaultResult, override);
|
return merge(defaultResult, override);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateOrder = (partialOrder: Partial<Orders_party_orders>) =>
|
export const generateOrder = (partialOrder?: Partial<Orders_party_orders>) =>
|
||||||
merge(
|
merge(
|
||||||
{
|
{
|
||||||
__typename: 'Order',
|
__typename: 'Order',
|
@ -70,6 +70,7 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
|
|||||||
return sortOrders(data);
|
return sortOrders(data);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
// We can set <OrderList showCancelled={false} to hide cancelled orders
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={orders}>
|
<AsyncRenderer loading={loading} error={error} data={orders}>
|
||||||
<OrderList ref={gridRef} data={orders} />
|
<OrderList ref={gridRef} data={orders} />
|
@ -2,7 +2,7 @@ import { act, render, screen } from '@testing-library/react';
|
|||||||
import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||||
import type { Orders_party_orders } from '../__generated__/Orders';
|
import type { Orders_party_orders } from '../__generated__/Orders';
|
||||||
import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
|
import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
|
||||||
import { OrderList } from './order-list';
|
import { OrderListTable } from './order-list';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
@ -16,13 +16,13 @@ const generateJsx = (
|
|||||||
return (
|
return (
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||||
<OrderList data={orders} />
|
<OrderListTable data={orders} cancel={jest.fn()} />
|
||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('OrderList', () => {
|
describe('OrderListTable', () => {
|
||||||
it('should show no orders message', async () => {
|
it('should show no orders message', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(generateJsx([]));
|
render(generateJsx([]));
|
@ -1,10 +1,9 @@
|
|||||||
import type { Story, Meta } from '@storybook/react';
|
import type { Story, Meta } from '@storybook/react';
|
||||||
import { OrderType, OrderStatus } from '@vegaprotocol/types';
|
import { OrderType, OrderStatus } from '@vegaprotocol/types';
|
||||||
import { OrderList, OrderListTable } from './order-list';
|
import { OrderList, OrderListTable } from './order-list';
|
||||||
import { CancelDialog } from '../cancel-order-dialog';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import type { VegaTxState, Order } from '@vegaprotocol/wallet';
|
import type { Order, VegaTxState } from '@vegaprotocol/wallet';
|
||||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
import { VegaTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
|
||||||
import { generateOrdersArray } from '../mocks';
|
import { generateOrdersArray } from '../mocks';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -38,7 +37,7 @@ const Template2: Story = (args) => {
|
|||||||
rejectionReason: null,
|
rejectionReason: null,
|
||||||
size: '10',
|
size: '10',
|
||||||
price: '1000',
|
price: '1000',
|
||||||
market: null,
|
market: { name: 'ETH/DAI (30 Jun 2022)', decimalPlaces: 5 },
|
||||||
type: OrderType.Limit,
|
type: OrderType.Limit,
|
||||||
};
|
};
|
||||||
const reset = () => null;
|
const reset = () => null;
|
||||||
@ -47,12 +46,13 @@ const Template2: Story = (args) => {
|
|||||||
<div style={{ height: 1000 }}>
|
<div style={{ height: 1000 }}>
|
||||||
<OrderListTable data={args.data} cancel={cancel} />
|
<OrderListTable data={args.data} cancel={cancel} />
|
||||||
</div>
|
</div>
|
||||||
<CancelDialog
|
<VegaTransactionDialog
|
||||||
orderDialogOpen={open}
|
orderDialogOpen={open}
|
||||||
setOrderDialogOpen={setOpen}
|
setOrderDialogOpen={setOpen}
|
||||||
finalizedOrder={finalizedOrder}
|
finalizedOrder={finalizedOrder}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
|
title={'Order cancelled'}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
@ -9,27 +9,45 @@ import type {
|
|||||||
import type { AgGridReact } from 'ag-grid-react';
|
import type { AgGridReact } from 'ag-grid-react';
|
||||||
import { AgGridColumn } from 'ag-grid-react';
|
import { AgGridColumn } from 'ag-grid-react';
|
||||||
import { forwardRef, useState } from 'react';
|
import { forwardRef, useState } from 'react';
|
||||||
import { useOrderCancel } from '@vegaprotocol/wallet';
|
|
||||||
import { CancelDialog } from '../cancel-order-dialog/cancel-dialog';
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { useOrderCancel } from '../../order-hooks/use-order-cancel';
|
||||||
|
import { VegaTransactionDialog } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
interface OrderListProps {
|
interface OrderListProps {
|
||||||
data: Orders_party_orders[] | null;
|
data: Orders_party_orders[] | null;
|
||||||
|
showCancelled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
||||||
({ data }, ref) => {
|
({ data, showCancelled = true }, ref) => {
|
||||||
const [orderDialogOpen, setOrderDialogOpen] = useState(false);
|
const [cancelOrderDialogOpen, setCancelOrderDialogOpen] = useState(false);
|
||||||
const { transaction, finalizedOrder, reset, cancel } = useOrderCancel();
|
const { transaction, updatedOrder, reset, cancel } = useOrderCancel();
|
||||||
|
const ordersData = showCancelled
|
||||||
|
? data
|
||||||
|
: data?.filter((o) => o.status !== OrderStatus.Cancelled) || null;
|
||||||
|
const getDialogTitle = (status?: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case OrderStatus.Cancelled:
|
||||||
|
return 'Order cancelled';
|
||||||
|
case OrderStatus.Rejected:
|
||||||
|
return 'Order rejected';
|
||||||
|
case OrderStatus.Expired:
|
||||||
|
return 'Order expired';
|
||||||
|
default:
|
||||||
|
return 'Cancellation failed';
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<OrderListTable data={data} cancel={cancel} ref={ref} />
|
<OrderListTable data={ordersData} cancel={cancel} ref={ref} />
|
||||||
<CancelDialog
|
<VegaTransactionDialog
|
||||||
orderDialogOpen={orderDialogOpen}
|
key={`cancel-order-dialog-${transaction.txHash}`}
|
||||||
setOrderDialogOpen={setOrderDialogOpen}
|
orderDialogOpen={cancelOrderDialogOpen}
|
||||||
finalizedOrder={finalizedOrder}
|
setOrderDialogOpen={setCancelOrderDialogOpen}
|
||||||
|
finalizedOrder={updatedOrder}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
|
title={getDialogTitle(updatedOrder?.status)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
4
libs/orders/src/lib/index.ts
Normal file
4
libs/orders/src/lib/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './components';
|
||||||
|
export * from './order-hooks';
|
||||||
|
export * from './utils';
|
||||||
|
export * from './market';
|
13
libs/orders/src/lib/market.ts
Normal file
13
libs/orders/src/lib/market.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import type { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export interface Market {
|
||||||
|
__typename?: string;
|
||||||
|
id: string;
|
||||||
|
positionDecimalPlaces: number;
|
||||||
|
state: MarketState;
|
||||||
|
decimalPlaces: number;
|
||||||
|
tradingMode: MarketTradingMode;
|
||||||
|
tradableInstrument?: any;
|
||||||
|
depth?: any;
|
||||||
|
}
|
@ -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 { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
|
import { BusEventType, OrderType, OrderStatus, OrderRejectionReason, OrderTimeInForce, Side } from "@vegaprotocol/types";
|
||||||
|
|
||||||
// ====================================================
|
// ====================================================
|
||||||
// GraphQL subscription operation: OrderEvent
|
// GraphQL subscription operation: OrderEvent
|
||||||
@ -68,6 +68,14 @@ export interface OrderEvent_busEvents_event_Order {
|
|||||||
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
|
||||||
*/
|
*/
|
||||||
price: string;
|
price: string;
|
||||||
|
/**
|
||||||
|
* The timeInForce of order (determines how and if it executes, and whether it persists on the book)
|
||||||
|
*/
|
||||||
|
timeInForce: OrderTimeInForce;
|
||||||
|
/**
|
||||||
|
* Whether the order is to buy or sell
|
||||||
|
*/
|
||||||
|
side: Side;
|
||||||
/**
|
/**
|
||||||
* The market the order is trading on (probably stored internally as a hash of the market details)
|
* The market the order is trading on (probably stored internally as a hash of the market details)
|
||||||
*/
|
*/
|
@ -1,3 +1,5 @@
|
|||||||
export * from './__generated__';
|
export * from './__generated__';
|
||||||
export * from './order-event-query';
|
export * from './order-event-query';
|
||||||
export * from './use-order-cancel';
|
export * from './use-order-cancel';
|
||||||
|
export * from './use-order-submit';
|
||||||
|
export * from './use-order-validation';
|
@ -13,6 +13,8 @@ export const ORDER_EVENT_SUB = gql`
|
|||||||
createdAt
|
createdAt
|
||||||
size
|
size
|
||||||
price
|
price
|
||||||
|
timeInForce
|
||||||
|
side
|
||||||
market {
|
market {
|
||||||
name
|
name
|
||||||
decimalPlaces
|
decimalPlaces
|
@ -1,12 +1,19 @@
|
|||||||
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { act, renderHook } from '@testing-library/react-hooks';
|
import { act, renderHook } from '@testing-library/react-hooks';
|
||||||
import { MarketState, MarketTradingMode, OrderType } from '@vegaprotocol/types';
|
import { MarketState, MarketTradingMode, OrderType } from '@vegaprotocol/types';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { VegaKeyExtended, VegaWalletContextShape } from '../context';
|
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
import { VegaWalletContext } from '../context';
|
import type {
|
||||||
import { VegaTxStatus } from '../use-vega-transaction';
|
VegaKeyExtended,
|
||||||
import type { Order } from '../vega-order-transaction-dialog';
|
VegaWalletContextShape,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
import { useOrderCancel } from './use-order-cancel';
|
import { useOrderCancel } from './use-order-cancel';
|
||||||
|
import type {
|
||||||
|
OrderEvent,
|
||||||
|
OrderEvent_busEvents,
|
||||||
|
} from './__generated__/OrderEvent';
|
||||||
|
import { ORDER_EVENT_SUB } from './order-event-query';
|
||||||
|
|
||||||
const defaultMarket = {
|
const defaultMarket = {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
@ -46,8 +53,79 @@ const defaultWalletContext = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function setup(context?: Partial<VegaWalletContextShape>) {
|
function setup(context?: Partial<VegaWalletContextShape>) {
|
||||||
|
const mocks: MockedResponse<OrderEvent> = {
|
||||||
|
request: {
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: {
|
||||||
|
partyId: context?.keypair?.pub || '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
busEvents: [
|
||||||
|
{
|
||||||
|
type: 'Order',
|
||||||
|
event: {
|
||||||
|
type: 'Limit',
|
||||||
|
id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62',
|
||||||
|
status: 'Active',
|
||||||
|
rejectionReason: null,
|
||||||
|
createdAt: '2022-07-05T14:25:47.815283706Z',
|
||||||
|
size: '10',
|
||||||
|
price: '300000',
|
||||||
|
timeInForce: 'GTC',
|
||||||
|
side: 'Buy',
|
||||||
|
market: {
|
||||||
|
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||||
|
decimalPlaces: 5,
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
__typename: 'Order',
|
||||||
|
},
|
||||||
|
__typename: 'BusEvent',
|
||||||
|
} as OrderEvent_busEvents,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterMocks: MockedResponse<OrderEvent> = {
|
||||||
|
request: {
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: {
|
||||||
|
partyId: context?.keypair?.pub || '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
busEvents: [
|
||||||
|
{
|
||||||
|
type: 'Order',
|
||||||
|
event: {
|
||||||
|
type: 'Limit',
|
||||||
|
id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62',
|
||||||
|
status: 'Active',
|
||||||
|
rejectionReason: null,
|
||||||
|
createdAt: '2022-07-05T14:25:47.815283706Z',
|
||||||
|
size: '10',
|
||||||
|
price: '300000',
|
||||||
|
timeInForce: 'GTC',
|
||||||
|
side: 'Buy',
|
||||||
|
market: {
|
||||||
|
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||||
|
decimalPlaces: 5,
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
__typename: 'Order',
|
||||||
|
},
|
||||||
|
__typename: 'BusEvent',
|
||||||
|
} as OrderEvent_busEvents,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
<MockedProvider>
|
<MockedProvider mocks={[mocks, filterMocks]}>
|
||||||
<VegaWalletContext.Provider
|
<VegaWalletContext.Provider
|
||||||
value={{ ...defaultWalletContext, ...context }}
|
value={{ ...defaultWalletContext, ...context }}
|
||||||
>
|
>
|
||||||
@ -55,6 +133,7 @@ function setup(context?: Partial<VegaWalletContextShape>) {
|
|||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
return renderHook(() => useOrderCancel(), { wrapper });
|
return renderHook(() => useOrderCancel(), { wrapper });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +149,7 @@ describe('useOrderCancel', () => {
|
|||||||
|
|
||||||
it('should not sendTx if no keypair', async () => {
|
it('should not sendTx if no keypair', async () => {
|
||||||
const mockSendTx = jest.fn();
|
const mockSendTx = jest.fn();
|
||||||
const order: Order = {
|
const order = {
|
||||||
type: OrderType.Market,
|
type: OrderType.Market,
|
||||||
size: '10',
|
size: '10',
|
||||||
price: '1234567.89',
|
price: '1234567.89',
|
||||||
@ -94,7 +173,7 @@ describe('useOrderCancel', () => {
|
|||||||
const keypair = {
|
const keypair = {
|
||||||
pub: '0x123',
|
pub: '0x123',
|
||||||
} as VegaKeyExtended;
|
} as VegaKeyExtended;
|
||||||
const order: Order = {
|
const order = {
|
||||||
type: OrderType.Limit,
|
type: OrderType.Limit,
|
||||||
size: '10',
|
size: '10',
|
||||||
price: '1234567.89',
|
price: '1234567.89',
|
117
libs/orders/src/lib/order-hooks/use-order-cancel.tsx
Normal file
117
libs/orders/src/lib/order-hooks/use-order-cancel.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useVegaWallet, useVegaTransaction } from '@vegaprotocol/wallet';
|
||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import type {
|
||||||
|
OrderEvent,
|
||||||
|
OrderEventVariables,
|
||||||
|
OrderEvent_busEvents_event_Order,
|
||||||
|
} from './__generated__/OrderEvent';
|
||||||
|
import { ORDER_EVENT_SUB } from './order-event-query';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { OrderStatus } from '@vegaprotocol/types';
|
||||||
|
import { determineId } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { Subscription } from 'zen-observable-ts';
|
||||||
|
|
||||||
|
export const useOrderCancel = () => {
|
||||||
|
const { keypair } = useVegaWallet();
|
||||||
|
const { send, transaction, reset: resetTransaction } = useVegaTransaction();
|
||||||
|
const [updatedOrder, setUpdatedOrder] =
|
||||||
|
useState<OrderEvent_busEvents_event_Order | null>(null);
|
||||||
|
const client = useApolloClient();
|
||||||
|
const subRef = useRef<Subscription | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
setUpdatedOrder(null);
|
||||||
|
resetTransaction();
|
||||||
|
};
|
||||||
|
}, [resetTransaction]);
|
||||||
|
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
resetTransaction();
|
||||||
|
setUpdatedOrder(null);
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
}, [resetTransaction]);
|
||||||
|
|
||||||
|
const cancel = useCallback(
|
||||||
|
async (order) => {
|
||||||
|
if (!keypair) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
OrderStatus.Cancelled,
|
||||||
|
OrderStatus.Rejected,
|
||||||
|
OrderStatus.Expired,
|
||||||
|
OrderStatus.Filled,
|
||||||
|
OrderStatus.Stopped,
|
||||||
|
].includes(order.status)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdatedOrder(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await send({
|
||||||
|
pubKey: keypair.pub,
|
||||||
|
propagate: true,
|
||||||
|
orderCancellation: {
|
||||||
|
orderId: order.id,
|
||||||
|
marketId: order.market.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.signature) {
|
||||||
|
const resId = order.id ?? determineId(res.signature);
|
||||||
|
setUpdatedOrder(null);
|
||||||
|
// setId(resId);
|
||||||
|
if (resId) {
|
||||||
|
// Start a subscription looking for the newly created order
|
||||||
|
subRef.current = client
|
||||||
|
.subscribe<OrderEvent, OrderEventVariables>({
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: { partyId: keypair?.pub || '' },
|
||||||
|
})
|
||||||
|
.subscribe(({ data }) => {
|
||||||
|
if (!data?.busEvents?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No types available for the subscription result
|
||||||
|
const matchingOrderEvent = data.busEvents.find((e) => {
|
||||||
|
if (e.event.__typename !== 'Order') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.event.id === resId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
matchingOrderEvent &&
|
||||||
|
matchingOrderEvent.event.__typename === 'Order'
|
||||||
|
) {
|
||||||
|
setUpdatedOrder(matchingOrderEvent.event);
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
Sentry.captureException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[client, keypair, send]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
transaction,
|
||||||
|
updatedOrder,
|
||||||
|
cancel,
|
||||||
|
reset,
|
||||||
|
};
|
||||||
|
};
|
224
libs/orders/src/lib/order-hooks/use-order-submit.spec.tsx
Normal file
224
libs/orders/src/lib/order-hooks/use-order-submit.spec.tsx
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import { act, renderHook } from '@testing-library/react-hooks';
|
||||||
|
import type { Order } from '../utils';
|
||||||
|
import type {
|
||||||
|
VegaKeyExtended,
|
||||||
|
VegaWalletContextShape,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
|
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
|
import {
|
||||||
|
VegaWalletOrderSide,
|
||||||
|
VegaWalletOrderTimeInForce,
|
||||||
|
VegaWalletOrderType,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
import { useOrderSubmit } from './use-order-submit';
|
||||||
|
import type {
|
||||||
|
OrderEvent,
|
||||||
|
OrderEvent_busEvents,
|
||||||
|
} from './__generated__/OrderEvent';
|
||||||
|
import { ORDER_EVENT_SUB } from './order-event-query';
|
||||||
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import type { Market } from '../market';
|
||||||
|
|
||||||
|
const defaultMarket = {
|
||||||
|
__typename: 'Market',
|
||||||
|
id: 'market-id',
|
||||||
|
decimalPlaces: 2,
|
||||||
|
positionDecimalPlaces: 1,
|
||||||
|
tradingMode: MarketTradingMode.Continuous,
|
||||||
|
state: MarketState.Active,
|
||||||
|
tradableInstrument: {
|
||||||
|
__typename: 'TradableInstrument',
|
||||||
|
instrument: {
|
||||||
|
__typename: 'Instrument',
|
||||||
|
product: {
|
||||||
|
__typename: 'Future',
|
||||||
|
quoteName: 'quote-name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
depth: {
|
||||||
|
__typename: 'MarketDepth',
|
||||||
|
lastTrade: {
|
||||||
|
__typename: 'Trade',
|
||||||
|
price: '100',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as Market;
|
||||||
|
|
||||||
|
const defaultWalletContext = {
|
||||||
|
keypair: null,
|
||||||
|
keypairs: [],
|
||||||
|
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
selectPublicKey: jest.fn(),
|
||||||
|
connector: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
function setup(
|
||||||
|
context?: Partial<VegaWalletContextShape>,
|
||||||
|
market = defaultMarket
|
||||||
|
) {
|
||||||
|
const mocks: MockedResponse<OrderEvent> = {
|
||||||
|
request: {
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: {
|
||||||
|
partyId: context?.keypair?.pub || '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
busEvents: [
|
||||||
|
{
|
||||||
|
type: 'Order',
|
||||||
|
event: {
|
||||||
|
type: 'Limit',
|
||||||
|
id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62',
|
||||||
|
status: 'Active',
|
||||||
|
rejectionReason: null,
|
||||||
|
createdAt: '2022-07-05T14:25:47.815283706Z',
|
||||||
|
size: '10',
|
||||||
|
price: '300000',
|
||||||
|
timeInForce: 'GTC',
|
||||||
|
side: 'Buy',
|
||||||
|
market: {
|
||||||
|
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||||
|
decimalPlaces: 5,
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
__typename: 'Order',
|
||||||
|
},
|
||||||
|
__typename: 'BusEvent',
|
||||||
|
} as OrderEvent_busEvents,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filterMocks: MockedResponse<OrderEvent> = {
|
||||||
|
request: {
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: {
|
||||||
|
partyId: context?.keypair?.pub || '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
busEvents: [
|
||||||
|
{
|
||||||
|
type: 'Order',
|
||||||
|
event: {
|
||||||
|
type: 'Limit',
|
||||||
|
id: '9c70716f6c3698ac7bbcddc97176025b985a6bb9a0c4507ec09c9960b3216b62',
|
||||||
|
status: 'Active',
|
||||||
|
rejectionReason: null,
|
||||||
|
createdAt: '2022-07-05T14:25:47.815283706Z',
|
||||||
|
size: '10',
|
||||||
|
price: '300000',
|
||||||
|
timeInForce: 'GTC',
|
||||||
|
side: 'Buy',
|
||||||
|
market: {
|
||||||
|
name: 'UNIDAI Monthly (30 Jun 2022)',
|
||||||
|
decimalPlaces: 5,
|
||||||
|
__typename: 'Market',
|
||||||
|
},
|
||||||
|
__typename: 'Order',
|
||||||
|
},
|
||||||
|
__typename: 'BusEvent',
|
||||||
|
} as OrderEvent_busEvents,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||||
|
<MockedProvider mocks={[mocks, filterMocks]}>
|
||||||
|
<VegaWalletContext.Provider
|
||||||
|
value={{ ...defaultWalletContext, ...context }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
return renderHook(() => useOrderSubmit(market), { wrapper });
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useOrderSubmit', () => {
|
||||||
|
it('should submit a correctly formatted order', async () => {
|
||||||
|
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve({}));
|
||||||
|
const keypair = {
|
||||||
|
pub: '0x123',
|
||||||
|
} as VegaKeyExtended;
|
||||||
|
const { result } = setup({
|
||||||
|
sendTx: mockSendTx,
|
||||||
|
keypairs: [keypair],
|
||||||
|
keypair,
|
||||||
|
});
|
||||||
|
|
||||||
|
const order: Order = {
|
||||||
|
type: VegaWalletOrderType.Limit,
|
||||||
|
size: '10',
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce.GTT,
|
||||||
|
side: VegaWalletOrderSide.Buy,
|
||||||
|
price: '1234567.89',
|
||||||
|
expiration: new Date('2022-01-01'),
|
||||||
|
};
|
||||||
|
await act(async () => {
|
||||||
|
result.current.submit(order);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockSendTx).toHaveBeenCalledWith({
|
||||||
|
pubKey: keypair.pub,
|
||||||
|
propagate: true,
|
||||||
|
orderSubmission: {
|
||||||
|
type: VegaWalletOrderType.Limit,
|
||||||
|
marketId: defaultMarket.id, // Market provided from hook argument
|
||||||
|
size: '100', // size adjusted based on positionDecimalPlaces
|
||||||
|
side: VegaWalletOrderSide.Buy,
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce.GTT,
|
||||||
|
price: '123456789', // Decimal removed
|
||||||
|
expiresAt: order.expiration?.getTime() + '000000', // Nanoseconds append
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has the correct default state', () => {
|
||||||
|
const { result } = setup();
|
||||||
|
expect(typeof result.current.submit).toEqual('function');
|
||||||
|
expect(typeof result.current.reset).toEqual('function');
|
||||||
|
expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
|
||||||
|
expect(result.current.transaction.txHash).toEqual(null);
|
||||||
|
expect(result.current.transaction.error).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sendTx if no keypair', async () => {
|
||||||
|
const mockSendTx = jest.fn();
|
||||||
|
const { result } = setup({
|
||||||
|
sendTx: mockSendTx,
|
||||||
|
keypairs: [],
|
||||||
|
keypair: null,
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
result.current.submit({} as Order);
|
||||||
|
});
|
||||||
|
expect(mockSendTx).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sendTx side is not specified', async () => {
|
||||||
|
const mockSendTx = jest.fn();
|
||||||
|
const keypair = {
|
||||||
|
pub: '0x123',
|
||||||
|
} as VegaKeyExtended;
|
||||||
|
const { result } = setup({
|
||||||
|
sendTx: mockSendTx,
|
||||||
|
keypairs: [keypair],
|
||||||
|
keypair,
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
result.current.submit({} as Order);
|
||||||
|
});
|
||||||
|
expect(mockSendTx).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
123
libs/orders/src/lib/order-hooks/use-order-submit.ts
Normal file
123
libs/orders/src/lib/order-hooks/use-order-submit.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useApolloClient } from '@apollo/client';
|
||||||
|
import type { Order } from '../utils/get-default-order';
|
||||||
|
import { ORDER_EVENT_SUB } from './order-event-query';
|
||||||
|
import type {
|
||||||
|
OrderEvent,
|
||||||
|
OrderEventVariables,
|
||||||
|
OrderEvent_busEvents_event_Order,
|
||||||
|
} from './__generated__';
|
||||||
|
import { VegaWalletOrderType, useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
|
import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
|
||||||
|
import { useVegaTransaction } from '@vegaprotocol/wallet';
|
||||||
|
import * as Sentry from '@sentry/react';
|
||||||
|
import type { Market } from '../market';
|
||||||
|
import type { Subscription } from 'zen-observable-ts';
|
||||||
|
|
||||||
|
export const useOrderSubmit = (market: Market) => {
|
||||||
|
const { keypair } = useVegaWallet();
|
||||||
|
const { send, transaction, reset: resetTransaction } = useVegaTransaction();
|
||||||
|
const [finalizedOrder, setFinalizedOrder] =
|
||||||
|
useState<OrderEvent_busEvents_event_Order | null>(null);
|
||||||
|
const client = useApolloClient();
|
||||||
|
const subRef = useRef<Subscription | null>(null);
|
||||||
|
|
||||||
|
const reset = useCallback(() => {
|
||||||
|
resetTransaction();
|
||||||
|
setFinalizedOrder(null);
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
}, [resetTransaction]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
resetTransaction();
|
||||||
|
setFinalizedOrder(null);
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
};
|
||||||
|
}, [resetTransaction]);
|
||||||
|
|
||||||
|
const submit = useCallback(
|
||||||
|
async (order: Order) => {
|
||||||
|
if (!keypair || !order.side) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFinalizedOrder(null);
|
||||||
|
try {
|
||||||
|
const res = await send({
|
||||||
|
pubKey: keypair.pub,
|
||||||
|
propagate: true,
|
||||||
|
orderSubmission: {
|
||||||
|
marketId: market.id,
|
||||||
|
price:
|
||||||
|
order.type === VegaWalletOrderType.Limit && order.price
|
||||||
|
? removeDecimal(order.price, market.decimalPlaces)
|
||||||
|
: undefined,
|
||||||
|
size: removeDecimal(order.size, market.positionDecimalPlaces),
|
||||||
|
type: order.type,
|
||||||
|
side: order.side,
|
||||||
|
timeInForce: order.timeInForce,
|
||||||
|
expiresAt: order.expiration
|
||||||
|
? // Wallet expects timestamp in nanoseconds, we don't have that level of accuracy so
|
||||||
|
// just append 6 zeroes
|
||||||
|
order.expiration.getTime().toString() + '000000'
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.signature) {
|
||||||
|
const resId = determineId(res.signature);
|
||||||
|
if (resId) {
|
||||||
|
// Start a subscription looking for the newly created order
|
||||||
|
subRef.current = client
|
||||||
|
.subscribe<OrderEvent, OrderEventVariables>({
|
||||||
|
query: ORDER_EVENT_SUB,
|
||||||
|
variables: { partyId: keypair?.pub || '' },
|
||||||
|
})
|
||||||
|
.subscribe(({ data }) => {
|
||||||
|
if (!data?.busEvents?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No types available for the subscription result
|
||||||
|
const matchingOrderEvent = data.busEvents.find((e) => {
|
||||||
|
if (e.event.__typename !== 'Order') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.event.id === resId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
matchingOrderEvent &&
|
||||||
|
matchingOrderEvent.event.__typename === 'Order'
|
||||||
|
) {
|
||||||
|
setFinalizedOrder(matchingOrderEvent.event);
|
||||||
|
subRef.current?.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
Sentry.captureException(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
client,
|
||||||
|
keypair,
|
||||||
|
send,
|
||||||
|
market.id,
|
||||||
|
market.decimalPlaces,
|
||||||
|
market.positionDecimalPlaces,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
transaction,
|
||||||
|
finalizedOrder,
|
||||||
|
submit,
|
||||||
|
reset,
|
||||||
|
};
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { renderHook } from '@testing-library/react-hooks';
|
import { renderHook } from '@testing-library/react-hooks';
|
||||||
import {
|
import {
|
||||||
OrderTimeInForce,
|
VegaWalletOrderTimeInForce,
|
||||||
OrderType,
|
VegaWalletOrderType,
|
||||||
useVegaWallet,
|
useVegaWallet,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import type {
|
import type {
|
||||||
@ -11,12 +11,12 @@ import type {
|
|||||||
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 { useOrderValidation } from './use-order-validation';
|
import { useOrderValidation } from './use-order-validation';
|
||||||
import type { DealTicketQuery_market } from '../__generated__/DealTicketQuery';
|
|
||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
||||||
|
import type { Market } from '../market';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/wallet');
|
jest.mock('@vegaprotocol/wallet');
|
||||||
|
|
||||||
const market: DealTicketQuery_market = {
|
const market: Market = {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: 'market-id',
|
id: 'market-id',
|
||||||
decimalPlaces: 2,
|
decimalPlaces: 2,
|
||||||
@ -59,8 +59,8 @@ const defaultWalletContext = {
|
|||||||
const defaultOrder = {
|
const defaultOrder = {
|
||||||
market,
|
market,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
orderType: OrderType.Market,
|
orderType: VegaWalletOrderType.Market,
|
||||||
orderTimeInForce: OrderTimeInForce.FOK,
|
orderTimeInForce: VegaWalletOrderTimeInForce.FOK,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ERROR = {
|
const ERROR = {
|
||||||
@ -71,7 +71,7 @@ const ERROR = {
|
|||||||
MARKET_WAITING: 'Market is not active yet',
|
MARKET_WAITING: 'Market is not active yet',
|
||||||
MARKET_CONTINUOUS_LIMIT:
|
MARKET_CONTINUOUS_LIMIT:
|
||||||
'Only limit orders are permitted when market is in auction',
|
'Only limit orders are permitted when market is in auction',
|
||||||
MARKET_COUNTINUOUS_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: 'An amount needs to be provided',
|
||||||
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`,
|
||||||
@ -135,29 +135,29 @@ it.each`
|
|||||||
async ({ tradingMode, errorMessage }) => {
|
async ({ tradingMode, errorMessage }) => {
|
||||||
const { result } = setup({
|
const { result } = setup({
|
||||||
market: { ...defaultOrder.market, tradingMode },
|
market: { ...defaultOrder.market, tradingMode },
|
||||||
orderType: OrderType.Market,
|
orderType: VegaWalletOrderType.Market,
|
||||||
});
|
});
|
||||||
expect(result.current).toEqual(errorMessage);
|
expect(result.current).toEqual(errorMessage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
tradingMode | orderTimeInForce | errorMessage
|
tradingMode | orderTimeInForce | errorMessage
|
||||||
${MarketTradingMode.BatchAuction} | ${OrderTimeInForce.FOK} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.MonitoringAuction} | ${OrderTimeInForce.FOK} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.OpeningAuction} | ${OrderTimeInForce.FOK} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.FOK} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.BatchAuction} | ${OrderTimeInForce.IOC} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.MonitoringAuction} | ${OrderTimeInForce.IOC} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.OpeningAuction} | ${OrderTimeInForce.IOC} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.OpeningAuction} | ${VegaWalletOrderTimeInForce.IOC} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.BatchAuction} | ${OrderTimeInForce.GFN} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.BatchAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.MonitoringAuction} | ${OrderTimeInForce.GFN} | ${ERROR.MARKET_COUNTINUOUS_TIF}
|
${MarketTradingMode.MonitoringAuction} | ${VegaWalletOrderTimeInForce.GFN} | ${ERROR.MARKET_CONTINUOUS_TIF}
|
||||||
${MarketTradingMode.OpeningAuction} | ${OrderTimeInForce.GFN} | ${ERROR.MARKET_COUNTINUOUS_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',
|
'Returns an error message when submitting a limit order with a "$orderTimeInForce" value to a "$tradingMode" market',
|
||||||
async ({ tradingMode, orderTimeInForce, errorMessage }) => {
|
async ({ tradingMode, orderTimeInForce, errorMessage }) => {
|
||||||
const { result } = setup({
|
const { result } = setup({
|
||||||
market: { ...defaultOrder.market, tradingMode },
|
market: { ...defaultOrder.market, tradingMode },
|
||||||
orderType: OrderType.Limit,
|
orderType: VegaWalletOrderType.Limit,
|
||||||
orderTimeInForce,
|
orderTimeInForce,
|
||||||
});
|
});
|
||||||
expect(result.current).toEqual(errorMessage);
|
expect(result.current).toEqual(errorMessage);
|
@ -1,19 +1,19 @@
|
|||||||
import type { FieldErrors } from 'react-hook-form';
|
import type { FieldErrors } from 'react-hook-form';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { t } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { Order } from '@vegaprotocol/wallet';
|
||||||
import {
|
import {
|
||||||
useVegaWallet,
|
useVegaWallet,
|
||||||
OrderTimeInForce,
|
VegaWalletOrderTimeInForce as OrderTimeInForce,
|
||||||
OrderType,
|
VegaWalletOrderType as OrderType,
|
||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
import type { Order } from '../utils/get-default-order';
|
import type { Market } from '../market';
|
||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
||||||
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
|
|
||||||
|
|
||||||
export type ValidationProps = {
|
export type ValidationProps = {
|
||||||
step: number;
|
step: number;
|
||||||
market: DealTicketQuery_market;
|
market: Market;
|
||||||
orderType: OrderType;
|
orderType: OrderType;
|
||||||
orderTimeInForce: OrderTimeInForce;
|
orderTimeInForce: OrderTimeInForce;
|
||||||
fieldErrors?: FieldErrors<Order>;
|
fieldErrors?: FieldErrors<Order>;
|
32
libs/orders/src/lib/utils/get-default-order.ts
Normal file
32
libs/orders/src/lib/utils/get-default-order.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
VegaWalletOrderTimeInForce,
|
||||||
|
VegaWalletOrderType,
|
||||||
|
VegaWalletOrderSide,
|
||||||
|
} from '@vegaprotocol/wallet';
|
||||||
|
import { toDecimal } from '@vegaprotocol/react-helpers';
|
||||||
|
import type { Market } from '../market';
|
||||||
|
|
||||||
|
export type Order =
|
||||||
|
| {
|
||||||
|
size: string;
|
||||||
|
type: VegaWalletOrderType.Market;
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce;
|
||||||
|
side: VegaWalletOrderSide;
|
||||||
|
price?: never;
|
||||||
|
expiration?: never;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
size: string;
|
||||||
|
type: VegaWalletOrderType.Limit;
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce;
|
||||||
|
side: VegaWalletOrderSide;
|
||||||
|
price?: string;
|
||||||
|
expiration?: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultOrder = (market: Market): Order => ({
|
||||||
|
type: VegaWalletOrderType.Market,
|
||||||
|
side: VegaWalletOrderSide.Buy,
|
||||||
|
timeInForce: VegaWalletOrderTimeInForce.IOC,
|
||||||
|
size: String(toDecimal(market.positionDecimalPlaces)),
|
||||||
|
});
|
2
libs/orders/src/lib/utils/index.ts
Normal file
2
libs/orders/src/lib/utils/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './get-default-order';
|
||||||
|
export * from './validate-size';
|
@ -7,7 +7,7 @@ import {
|
|||||||
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
import { LocalStorage } from '@vegaprotocol/react-helpers';
|
||||||
import { WALLET_CONFIG } from '../storage-keys';
|
import { WALLET_CONFIG } from '../storage-keys';
|
||||||
import type { VegaConnector } from './vega-connector';
|
import type { VegaConnector } from './vega-connector';
|
||||||
import type { TransactionSubmission } from '../types';
|
import type { TransactionSubmission } from '../wallet-types';
|
||||||
|
|
||||||
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
|
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
|
||||||
// need to use local storage, I don't think so...
|
// need to use local storage, I don't think so...
|
||||||
|
@ -2,7 +2,7 @@ import type {
|
|||||||
VegaKey,
|
VegaKey,
|
||||||
TransactionResponse,
|
TransactionResponse,
|
||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
import type { TransactionSubmission } from '../types';
|
import type { TransactionSubmission } from '../wallet-types';
|
||||||
|
|
||||||
type ErrorResponse =
|
type ErrorResponse =
|
||||||
| {
|
| {
|
||||||
|
@ -4,7 +4,7 @@ import type {
|
|||||||
} from '@vegaprotocol/vegawallet-service-api-client';
|
} from '@vegaprotocol/vegawallet-service-api-client';
|
||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
import type { VegaConnector } from './connectors';
|
import type { VegaConnector } from './connectors';
|
||||||
import type { TransactionSubmission } from './types';
|
import type { TransactionSubmission } from './wallet-types';
|
||||||
|
|
||||||
export type SendTxError =
|
export type SendTxError =
|
||||||
| {
|
| {
|
||||||
|
@ -2,11 +2,10 @@ export * from './context';
|
|||||||
export * from './use-vega-wallet';
|
export * from './use-vega-wallet';
|
||||||
export * from './connectors';
|
export * from './connectors';
|
||||||
export * from './storage-keys';
|
export * from './storage-keys';
|
||||||
export * from './types';
|
export * from './wallet-types';
|
||||||
export * from './use-vega-transaction';
|
export * from './use-vega-transaction';
|
||||||
export * from './use-eager-connect';
|
export * from './use-eager-connect';
|
||||||
export * from './manage-dialog';
|
export * from './manage-dialog';
|
||||||
export * from './vega-order-transaction-dialog';
|
export * from './vega-transaction-dialog';
|
||||||
export * from './provider';
|
export * from './provider';
|
||||||
export * from './order-hooks';
|
|
||||||
export * from './connect-dialog';
|
export * from './connect-dialog';
|
||||||
|
1
libs/wallet/src/manage-dialog/index.ts
Normal file
1
libs/wallet/src/manage-dialog/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './manage-dialog';
|
@ -1,9 +1,9 @@
|
|||||||
/* eslint-disable jest/no-conditional-expect */
|
/* eslint-disable jest/no-conditional-expect */
|
||||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||||
import { VegaWalletContext } from './context';
|
import { VegaWalletContext } from '../context';
|
||||||
import type { VegaWalletContextShape, VegaKeyExtended } from './context';
|
import type { VegaWalletContextShape, VegaKeyExtended } from '../context';
|
||||||
import type { VegaManageDialogProps } from './manage-dialog';
|
import type { VegaManageDialogProps } from '.';
|
||||||
import { VegaManageDialog } from './manage-dialog';
|
import { VegaManageDialog } from '.';
|
||||||
|
|
||||||
let props: VegaManageDialogProps;
|
let props: VegaManageDialogProps;
|
||||||
let context: Partial<VegaWalletContextShape>;
|
let context: Partial<VegaWalletContextShape>;
|
@ -6,7 +6,7 @@ import {
|
|||||||
Intent,
|
Intent,
|
||||||
Icon,
|
Icon,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '.';
|
import { useVegaWallet } from '..';
|
||||||
|
|
||||||
export interface VegaManageDialogProps {
|
export interface VegaManageDialogProps {
|
||||||
dialogOpen: boolean;
|
dialogOpen: boolean;
|
@ -1,83 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
import { determineId } from '@vegaprotocol/react-helpers';
|
|
||||||
|
|
||||||
import { useVegaTransaction } from '../use-vega-transaction';
|
|
||||||
import { useVegaWallet } from '../use-vega-wallet';
|
|
||||||
import { useSubscription } from '@apollo/client';
|
|
||||||
import type {
|
|
||||||
OrderEvent,
|
|
||||||
OrderEventVariables,
|
|
||||||
OrderEvent_busEvents_event_Order,
|
|
||||||
} from './__generated__/OrderEvent';
|
|
||||||
import { ORDER_EVENT_SUB } from './order-event-query';
|
|
||||||
import * as Sentry from '@sentry/react';
|
|
||||||
|
|
||||||
export const useOrderCancel = () => {
|
|
||||||
const { keypair } = useVegaWallet();
|
|
||||||
const { send, transaction, reset: resetTransaction } = useVegaTransaction();
|
|
||||||
const [updatedOrder, setUpdatedOrder] =
|
|
||||||
useState<OrderEvent_busEvents_event_Order | null>(null);
|
|
||||||
const [id, setId] = useState('');
|
|
||||||
|
|
||||||
// Start a subscription looking for the newly created order
|
|
||||||
useSubscription<OrderEvent, OrderEventVariables>(ORDER_EVENT_SUB, {
|
|
||||||
variables: { partyId: keypair?.pub || '' },
|
|
||||||
skip: !id,
|
|
||||||
onSubscriptionData: ({ subscriptionData }) => {
|
|
||||||
if (!subscriptionData.data?.busEvents?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// No types available for the subscription result
|
|
||||||
const matchingOrderEvent = subscriptionData.data.busEvents[0].event;
|
|
||||||
|
|
||||||
if (matchingOrderEvent && matchingOrderEvent.__typename === 'Order') {
|
|
||||||
setUpdatedOrder(matchingOrderEvent);
|
|
||||||
resetTransaction();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const cancel = useCallback(
|
|
||||||
async (order) => {
|
|
||||||
if (!keypair) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setUpdatedOrder(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await send({
|
|
||||||
pubKey: keypair.pub,
|
|
||||||
propagate: true,
|
|
||||||
orderCancellation: {
|
|
||||||
orderId: order.id,
|
|
||||||
marketId: order.market.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res?.signature) {
|
|
||||||
setId(determineId(res.signature));
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
Sentry.captureException(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[keypair, send]
|
|
||||||
);
|
|
||||||
|
|
||||||
const reset = useCallback(() => {
|
|
||||||
resetTransaction();
|
|
||||||
setUpdatedOrder(null);
|
|
||||||
setId('');
|
|
||||||
}, [resetTransaction]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
transaction,
|
|
||||||
finalizedOrder: updatedOrder,
|
|
||||||
id,
|
|
||||||
cancel,
|
|
||||||
reset,
|
|
||||||
};
|
|
||||||
};
|
|
@ -5,7 +5,7 @@ import type { VegaKeyExtended, VegaWalletContextShape } from '.';
|
|||||||
import type { VegaConnector } from './connectors/vega-connector';
|
import type { VegaConnector } from './connectors/vega-connector';
|
||||||
import { VegaWalletContext } from './context';
|
import { VegaWalletContext } from './context';
|
||||||
import { WALLET_KEY } from './storage-keys';
|
import { WALLET_KEY } from './storage-keys';
|
||||||
import type { TransactionSubmission } from './types';
|
import type { TransactionSubmission } from './wallet-types';
|
||||||
|
|
||||||
interface VegaWalletProviderProps {
|
interface VegaWalletProviderProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import type { TransactionSubmission } from './types';
|
import type { TransactionSubmission } from './wallet-types';
|
||||||
import { useVegaWallet } from './use-vega-wallet';
|
import { useVegaWallet } from './use-vega-wallet';
|
||||||
import type { SendTxError } from './context';
|
import type { SendTxError } from './context';
|
||||||
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from './vega-order-transaction-dialog';
|
|
@ -1,159 +0,0 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import { OrderStatus, OrderType } from '@vegaprotocol/types';
|
|
||||||
import type { VegaTxState } from '../use-vega-transaction';
|
|
||||||
import { VegaTxStatus } from '../use-vega-transaction';
|
|
||||||
import type { Order } from './vega-order-transaction-dialog';
|
|
||||||
import { VegaOrderTransactionDialog } from './vega-order-transaction-dialog';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/environment', () => ({
|
|
||||||
useEnvironment: () => ({
|
|
||||||
VEGA_EXPLORER_URL: 'https://test.explorer.vega.network',
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('VegaOrderTransactionDialog', () => {
|
|
||||||
it('should render when an order is successful', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
const finalizedOrder: Order = {
|
|
||||||
status: OrderStatus.Active,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Order placed'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when transaction is requested', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Requested,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
const finalizedOrder: Order = {
|
|
||||||
status: OrderStatus.Active,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Confirm transaction in wallet'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when transaction has error', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Error,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
const finalizedOrder: Order = {
|
|
||||||
status: OrderStatus.Active,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Order rejected by wallet'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when an order is rejected', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
const finalizedOrder: Order = {
|
|
||||||
status: OrderStatus.Rejected,
|
|
||||||
rejectionReason: null,
|
|
||||||
size: '10',
|
|
||||||
price: '1000',
|
|
||||||
market: null,
|
|
||||||
type: OrderType.Limit,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={finalizedOrder}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Order failed'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render when pending consensus', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Error,
|
|
||||||
error: null,
|
|
||||||
txHash: null,
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={null}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Order rejected by wallet'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render awaiting network confirmation and add link to tx in block explorer', () => {
|
|
||||||
const transaction: VegaTxState = {
|
|
||||||
status: VegaTxStatus.Default,
|
|
||||||
error: null,
|
|
||||||
txHash: 'TxHash',
|
|
||||||
signature: null,
|
|
||||||
};
|
|
||||||
render(
|
|
||||||
<VegaOrderTransactionDialog
|
|
||||||
finalizedOrder={null}
|
|
||||||
transaction={transaction}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('order-status-header')).toHaveTextContent(
|
|
||||||
'Awaiting network confirmation'
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('tx-hash')).toHaveTextContent('TxHash');
|
|
||||||
expect(screen.getByTestId('tx-hash')).toHaveAttribute(
|
|
||||||
'href',
|
|
||||||
'https://test.explorer.vega.network/txs/0xTxHash'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,160 +0,0 @@
|
|||||||
import { Icon, Loader } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import {
|
|
||||||
addDecimal,
|
|
||||||
addDecimalsFormatNumber,
|
|
||||||
t,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import type { VegaTxState } from '../use-vega-transaction';
|
|
||||||
import { VegaTxStatus } from '../use-vega-transaction';
|
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
|
|
||||||
export interface Market {
|
|
||||||
name: string;
|
|
||||||
positionDecimalPlaces?: number;
|
|
||||||
decimalPlaces: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Order {
|
|
||||||
status: string;
|
|
||||||
rejectionReason: string | null;
|
|
||||||
size: string;
|
|
||||||
price: string;
|
|
||||||
market: Market | null;
|
|
||||||
type: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VegaOrderTransactionDialogProps {
|
|
||||||
transaction: VegaTxState;
|
|
||||||
finalizedOrder: Order | null;
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const VegaOrderTransactionDialog = ({
|
|
||||||
transaction,
|
|
||||||
finalizedOrder,
|
|
||||||
title = 'Order placed',
|
|
||||||
}: VegaOrderTransactionDialogProps) => {
|
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
|
||||||
// Rejected by wallet
|
|
||||||
if (transaction.status === VegaTxStatus.Requested) {
|
|
||||||
return (
|
|
||||||
<OrderDialogWrapper
|
|
||||||
title="Confirm transaction in wallet"
|
|
||||||
icon={<Icon name="hand-up" size={20} />}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
Please open your wallet application and confirm or reject the
|
|
||||||
transaction
|
|
||||||
</p>
|
|
||||||
</OrderDialogWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transaction error
|
|
||||||
if (transaction.status === VegaTxStatus.Error) {
|
|
||||||
return (
|
|
||||||
<OrderDialogWrapper
|
|
||||||
title="Order rejected by wallet"
|
|
||||||
icon={<Icon name="warning-sign" size={20} />}
|
|
||||||
>
|
|
||||||
{transaction.error && (
|
|
||||||
<pre className="text-ui break-all whitespace-pre-wrap">
|
|
||||||
{JSON.stringify(transaction.error, null, 2)}
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</OrderDialogWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pending consensus
|
|
||||||
if (!finalizedOrder) {
|
|
||||||
return (
|
|
||||||
<OrderDialogWrapper
|
|
||||||
title="Awaiting network confirmation"
|
|
||||||
icon={<Loader size="small" />}
|
|
||||||
>
|
|
||||||
{transaction.txHash && (
|
|
||||||
<p className="break-all">
|
|
||||||
Tx hash:
|
|
||||||
<a
|
|
||||||
className="underline"
|
|
||||||
data-testid="tx-hash"
|
|
||||||
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{transaction.txHash}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</OrderDialogWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order on network but was rejected
|
|
||||||
if (finalizedOrder.status === 'Rejected') {
|
|
||||||
return (
|
|
||||||
<OrderDialogWrapper
|
|
||||||
title="Order failed"
|
|
||||||
icon={<Icon name="warning-sign" size={20} />}
|
|
||||||
>
|
|
||||||
<p data-testid="error-reason">
|
|
||||||
{t(`Reason: ${finalizedOrder.rejectionReason}`)}
|
|
||||||
</p>
|
|
||||||
</OrderDialogWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<OrderDialogWrapper title={title} icon={<Icon name="tick" size={20} />}>
|
|
||||||
<p>{t(`Status: ${finalizedOrder.status}`)}</p>
|
|
||||||
{finalizedOrder.market && (
|
|
||||||
<p>{t(`Market: ${finalizedOrder.market.name}`)}</p>
|
|
||||||
)}
|
|
||||||
<p>{t(`Type: ${finalizedOrder.type}`)}</p>
|
|
||||||
<p>
|
|
||||||
{t(
|
|
||||||
`Amount: ${addDecimal(
|
|
||||||
finalizedOrder.size,
|
|
||||||
finalizedOrder.market?.positionDecimalPlaces || 0
|
|
||||||
)}`
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
{finalizedOrder.type === 'Limit' && finalizedOrder.market && (
|
|
||||||
<p>
|
|
||||||
{t(
|
|
||||||
`Price: ${addDecimalsFormatNumber(
|
|
||||||
finalizedOrder.price,
|
|
||||||
finalizedOrder.market.decimalPlaces
|
|
||||||
)}`
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</OrderDialogWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface OrderDialogWrapperProps {
|
|
||||||
children: ReactNode;
|
|
||||||
icon: ReactNode;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const OrderDialogWrapper = ({
|
|
||||||
children,
|
|
||||||
icon,
|
|
||||||
title,
|
|
||||||
}: OrderDialogWrapperProps) => {
|
|
||||||
return (
|
|
||||||
<div className="flex gap-12 max-w-full">
|
|
||||||
<div className="pt-8 fill-current">{icon}</div>
|
|
||||||
<div data-testid="order-wrapper" className="flex-1">
|
|
||||||
<h1 data-testid="order-status-header" className="text-h4">
|
|
||||||
{title}
|
|
||||||
</h1>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user