chore(trading): remove not used close position code and liquidity subscription (#3350)
This commit is contained in:
parent
381d9011a0
commit
351a5aaf96
@ -103,13 +103,3 @@ query LiquidityProviderFeeShare($marketId: ID!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription LiquidityProviderFeeShareUpdate($marketId: ID!) {
|
|
||||||
marketsData(marketIds: [$marketId]) {
|
|
||||||
liquidityProviderFeeShare {
|
|
||||||
partyId
|
|
||||||
equityLikeShare
|
|
||||||
averageEntryValuation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -36,13 +36,6 @@ export type LiquidityProviderFeeShareQueryVariables = Types.Exact<{
|
|||||||
|
|
||||||
export type LiquidityProviderFeeShareQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, data?: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string }, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } | null };
|
export type LiquidityProviderFeeShareQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, data?: { __typename?: 'MarketData', market: { __typename?: 'Market', id: string }, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } | null };
|
||||||
|
|
||||||
export type LiquidityProviderFeeShareUpdateSubscriptionVariables = Types.Exact<{
|
|
||||||
marketId: Types.Scalars['ID'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type LiquidityProviderFeeShareUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', liquidityProviderFeeShare?: Array<{ __typename?: 'ObservableLiquidityProviderFeeShare', partyId: string, equityLikeShare: string, averageEntryValuation: string }> | null }> };
|
|
||||||
|
|
||||||
export const LiquidityProvisionFieldsFragmentDoc = gql`
|
export const LiquidityProvisionFieldsFragmentDoc = gql`
|
||||||
fragment LiquidityProvisionFields on LiquidityProvision {
|
fragment LiquidityProvisionFields on LiquidityProvision {
|
||||||
party {
|
party {
|
||||||
@ -257,37 +250,3 @@ export function useLiquidityProviderFeeShareLazyQuery(baseOptions?: Apollo.LazyQ
|
|||||||
export type LiquidityProviderFeeShareQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareQuery>;
|
export type LiquidityProviderFeeShareQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareQuery>;
|
||||||
export type LiquidityProviderFeeShareLazyQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareLazyQuery>;
|
export type LiquidityProviderFeeShareLazyQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareLazyQuery>;
|
||||||
export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult<LiquidityProviderFeeShareQuery, LiquidityProviderFeeShareQueryVariables>;
|
export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult<LiquidityProviderFeeShareQuery, LiquidityProviderFeeShareQueryVariables>;
|
||||||
export const LiquidityProviderFeeShareUpdateDocument = gql`
|
|
||||||
subscription LiquidityProviderFeeShareUpdate($marketId: ID!) {
|
|
||||||
marketsData(marketIds: [$marketId]) {
|
|
||||||
liquidityProviderFeeShare {
|
|
||||||
partyId
|
|
||||||
equityLikeShare
|
|
||||||
averageEntryValuation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useLiquidityProviderFeeShareUpdateSubscription__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useLiquidityProviderFeeShareUpdateSubscription` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useLiquidityProviderFeeShareUpdateSubscription` returns an object from Apollo Client that contains loading, error, and data properties
|
|
||||||
* you can use to render your UI.
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const { data, loading, error } = useLiquidityProviderFeeShareUpdateSubscription({
|
|
||||||
* variables: {
|
|
||||||
* marketId: // value for 'marketId'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useLiquidityProviderFeeShareUpdateSubscription(baseOptions: Apollo.SubscriptionHookOptions<LiquidityProviderFeeShareUpdateSubscription, LiquidityProviderFeeShareUpdateSubscriptionVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useSubscription<LiquidityProviderFeeShareUpdateSubscription, LiquidityProviderFeeShareUpdateSubscriptionVariables>(LiquidityProviderFeeShareUpdateDocument, options);
|
|
||||||
}
|
|
||||||
export type LiquidityProviderFeeShareUpdateSubscriptionHookResult = ReturnType<typeof useLiquidityProviderFeeShareUpdateSubscription>;
|
|
||||||
export type LiquidityProviderFeeShareUpdateSubscriptionResult = Apollo.SubscriptionResult<LiquidityProviderFeeShareUpdateSubscription>;
|
|
||||||
|
@ -6,7 +6,6 @@ import produce from 'immer';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
LiquidityProviderFeeShareDocument,
|
LiquidityProviderFeeShareDocument,
|
||||||
LiquidityProviderFeeShareUpdateDocument,
|
|
||||||
LiquidityProvisionsDocument,
|
LiquidityProvisionsDocument,
|
||||||
LiquidityProvisionsUpdateDocument,
|
LiquidityProvisionsUpdateDocument,
|
||||||
MarketLpDocument,
|
MarketLpDocument,
|
||||||
@ -18,7 +17,6 @@ import type {
|
|||||||
LiquidityProviderFeeShareFieldsFragment,
|
LiquidityProviderFeeShareFieldsFragment,
|
||||||
LiquidityProviderFeeShareQuery,
|
LiquidityProviderFeeShareQuery,
|
||||||
LiquidityProviderFeeShareQueryVariables,
|
LiquidityProviderFeeShareQueryVariables,
|
||||||
LiquidityProviderFeeShareUpdateSubscription,
|
|
||||||
LiquidityProvisionFieldsFragment,
|
LiquidityProvisionFieldsFragment,
|
||||||
LiquidityProvisionsQuery,
|
LiquidityProvisionsQuery,
|
||||||
LiquidityProvisionsQueryVariables,
|
LiquidityProvisionsQueryVariables,
|
||||||
@ -115,42 +113,14 @@ export const marketLiquidityDataProvider = makeDataProvider<
|
|||||||
export const liquidityFeeShareDataProvider = makeDataProvider<
|
export const liquidityFeeShareDataProvider = makeDataProvider<
|
||||||
LiquidityProviderFeeShareQuery,
|
LiquidityProviderFeeShareQuery,
|
||||||
LiquidityProviderFeeShareFieldsFragment[],
|
LiquidityProviderFeeShareFieldsFragment[],
|
||||||
LiquidityProviderFeeShareUpdateSubscription,
|
never,
|
||||||
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare'],
|
never,
|
||||||
LiquidityProviderFeeShareQueryVariables
|
LiquidityProviderFeeShareQueryVariables
|
||||||
>({
|
>({
|
||||||
query: LiquidityProviderFeeShareDocument,
|
query: LiquidityProviderFeeShareDocument,
|
||||||
subscriptionQuery: LiquidityProviderFeeShareUpdateDocument,
|
|
||||||
update: (
|
|
||||||
data: LiquidityProviderFeeShareFieldsFragment[] | null,
|
|
||||||
deltas: LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare']
|
|
||||||
) => {
|
|
||||||
return produce(data || [], (draft) => {
|
|
||||||
deltas?.forEach((delta) => {
|
|
||||||
const id = delta.partyId;
|
|
||||||
const index = draft.findIndex((a) => a.party.id === id);
|
|
||||||
if (index !== -1) {
|
|
||||||
draft[index].equityLikeShare = delta.equityLikeShare;
|
|
||||||
draft[index].averageEntryValuation = delta.averageEntryValuation;
|
|
||||||
} else {
|
|
||||||
draft.unshift({
|
|
||||||
equityLikeShare: delta.equityLikeShare,
|
|
||||||
averageEntryValuation: delta.averageEntryValuation,
|
|
||||||
party: {
|
|
||||||
id: delta.partyId,
|
|
||||||
},
|
|
||||||
// TODO add accounts connection to the subscription
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getData: (data) => {
|
getData: (data) => {
|
||||||
return data?.market?.data?.liquidityProviderFeeShare || [];
|
return data?.market?.data?.liquidityProviderFeeShare || [];
|
||||||
},
|
},
|
||||||
getDelta: (subscriptionData: LiquidityProviderFeeShareUpdateSubscription) => {
|
|
||||||
return subscriptionData.marketsData[0].liquidityProviderFeeShare;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Filter = { partyId?: string; active?: boolean };
|
export type Filter = { partyId?: string; active?: boolean };
|
||||||
|
@ -4,7 +4,6 @@ export * from './lib/positions-data-providers';
|
|||||||
export * from './lib/margin-data-provider';
|
export * from './lib/margin-data-provider';
|
||||||
export * from './lib/margin-calculator';
|
export * from './lib/margin-calculator';
|
||||||
export * from './lib/positions-table';
|
export * from './lib/positions-table';
|
||||||
export * from './lib/use-close-position';
|
|
||||||
export * from './lib/use-market-margin';
|
export * from './lib/use-market-margin';
|
||||||
export * from './lib/use-market-position-open-volume';
|
export * from './lib/use-market-position-open-volume';
|
||||||
export * from './lib/use-open-volume';
|
export * from './lib/use-open-volume';
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
|
||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import type { TransactionResult, VegaTxState } from '@vegaprotocol/wallet';
|
|
||||||
import type { ClosingOrder as IClosingOrder } from '../use-close-position';
|
|
||||||
import { useRequestClosePositionData } from '../use-request-close-position-data';
|
|
||||||
import { ClosingOrder } from './shared';
|
|
||||||
|
|
||||||
interface CompleteProps {
|
|
||||||
partyId: string;
|
|
||||||
transaction: VegaTxState;
|
|
||||||
transactionResult?: TransactionResult;
|
|
||||||
closingOrder?: IClosingOrder;
|
|
||||||
closingOrderResult?: OrderFieldsFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Complete = ({
|
|
||||||
partyId,
|
|
||||||
transaction,
|
|
||||||
transactionResult,
|
|
||||||
closingOrder,
|
|
||||||
closingOrderResult,
|
|
||||||
}: CompleteProps) => {
|
|
||||||
const { VEGA_EXPLORER_URL } = useEnvironment();
|
|
||||||
|
|
||||||
if (!transactionResult || !closingOrderResult) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{closingOrderResult.status === Schema.OrderStatus.STATUS_FILLED &&
|
|
||||||
transactionResult.status ? (
|
|
||||||
<Success partyId={partyId} order={closingOrder} />
|
|
||||||
) : (
|
|
||||||
<Error
|
|
||||||
transactionResult={transactionResult}
|
|
||||||
closingOrderResult={closingOrderResult}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{transaction.txHash && (
|
|
||||||
<>
|
|
||||||
<p className="font-semibold mt-4">{t('Transaction')}</p>
|
|
||||||
<p>
|
|
||||||
<Link
|
|
||||||
href={`${VEGA_EXPLORER_URL}/txs/${transaction.txHash}`}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{truncateByChars(transaction.txHash)}
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Success = ({
|
|
||||||
partyId,
|
|
||||||
order,
|
|
||||||
}: {
|
|
||||||
partyId: string;
|
|
||||||
order?: IClosingOrder;
|
|
||||||
}) => {
|
|
||||||
const { market, marketData, orders } = useRequestClosePositionData(
|
|
||||||
order?.marketId,
|
|
||||||
partyId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!market || !marketData || !orders) {
|
|
||||||
return <div>{t('Loading...')}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!order) {
|
|
||||||
return (
|
|
||||||
<div className="text-vega-pink">{t('Could retrieve closing order')}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2 className="font-bold">{t('Position closed')}</h2>
|
|
||||||
<ClosingOrder order={order} market={market} marketData={marketData} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Error = ({
|
|
||||||
transactionResult,
|
|
||||||
closingOrderResult,
|
|
||||||
}: {
|
|
||||||
transactionResult: TransactionResult;
|
|
||||||
closingOrderResult: OrderFieldsFragment;
|
|
||||||
}) => {
|
|
||||||
const reason =
|
|
||||||
closingOrderResult.rejectionReason &&
|
|
||||||
Schema.OrderRejectionReasonMapping[closingOrderResult.rejectionReason];
|
|
||||||
return (
|
|
||||||
<div className="text-vega-pink">
|
|
||||||
{reason ? (
|
|
||||||
<p>{reason}</p>
|
|
||||||
) : (
|
|
||||||
<p>
|
|
||||||
{t('Transaction failed')}: {transactionResult.error}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,102 +0,0 @@
|
|||||||
import { render, screen, within } from '@testing-library/react';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import * as dataHook from '../use-request-close-position-data';
|
|
||||||
import { Requested } from './requested';
|
|
||||||
|
|
||||||
jest.mock('./use-request-close-position-data');
|
|
||||||
|
|
||||||
describe('Close position dialog - Request', () => {
|
|
||||||
const props = {
|
|
||||||
partyId: 'party-id',
|
|
||||||
order: {
|
|
||||||
marketId: 'market-id',
|
|
||||||
type: Schema.OrderType.TYPE_MARKET as const,
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK as const,
|
|
||||||
side: Schema.Side.SIDE_BUY,
|
|
||||||
size: '10',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
it('loading state', async () => {
|
|
||||||
jest.spyOn(dataHook, 'useRequestClosePositionData').mockReturnValue({
|
|
||||||
loading: false,
|
|
||||||
market: null,
|
|
||||||
marketData: null,
|
|
||||||
orders: [],
|
|
||||||
});
|
|
||||||
render(<Requested {...props} />);
|
|
||||||
expect(screen.getByText('Loading...')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders message if no closing order found', async () => {
|
|
||||||
const orders = [
|
|
||||||
{
|
|
||||||
size: '200',
|
|
||||||
price: '999',
|
|
||||||
side: Schema.Side.SIDE_BUY,
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
size: '300',
|
|
||||||
price: '888',
|
|
||||||
side: Schema.Side.SIDE_SELL,
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
jest.spyOn(dataHook, 'useRequestClosePositionData').mockReturnValue({
|
|
||||||
market: {
|
|
||||||
decimalPlaces: 2,
|
|
||||||
positionDecimalPlaces: 2,
|
|
||||||
tradableInstrument: {
|
|
||||||
instrument: {
|
|
||||||
name: 'test market',
|
|
||||||
product: {
|
|
||||||
// @ts-ignore avoiding having to add every property on the type
|
|
||||||
settlementAsset: {
|
|
||||||
symbol: 'SYM',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// @ts-ignore avoid all fields
|
|
||||||
marketData: {
|
|
||||||
markPrice: '100',
|
|
||||||
},
|
|
||||||
// @ts-ignore avoid all fields
|
|
||||||
orders,
|
|
||||||
});
|
|
||||||
render(<Requested {...props} />);
|
|
||||||
|
|
||||||
// closing order
|
|
||||||
const closingOrderHeader = screen.getByText('Position to be closed');
|
|
||||||
const closingOrderTable = within(
|
|
||||||
closingOrderHeader.nextElementSibling?.querySelector(
|
|
||||||
'tbody'
|
|
||||||
) as HTMLElement
|
|
||||||
);
|
|
||||||
const closingOrderRow = closingOrderTable.getAllByRole('row');
|
|
||||||
expect(closingOrderRow[0].children[0]).toHaveTextContent('test market');
|
|
||||||
expect(closingOrderRow[0].children[1]).toHaveTextContent('+0.1');
|
|
||||||
expect(closingOrderRow[0].children[2]).toHaveTextContent('~1.00 SYM');
|
|
||||||
|
|
||||||
// orders
|
|
||||||
const ordersHeading = screen.getByText('Orders to be closed');
|
|
||||||
const ordersTable = within(
|
|
||||||
ordersHeading.nextElementSibling?.querySelector('tbody') as HTMLElement
|
|
||||||
);
|
|
||||||
const orderRows = ordersTable.getAllByRole('row');
|
|
||||||
expect(orderRows).toHaveLength(orders.length);
|
|
||||||
expect(orderRows[0].children[0]).toHaveTextContent('+2');
|
|
||||||
expect(orderRows[0].children[1]).toHaveTextContent('9.99 SYM');
|
|
||||||
expect(orderRows[0].children[2]).toHaveTextContent(
|
|
||||||
"Good 'til Cancelled (GTC)"
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(orderRows[1].children[0]).toHaveTextContent('-3');
|
|
||||||
expect(orderRows[1].children[1]).toHaveTextContent('8.88 SYM');
|
|
||||||
expect(orderRows[1].children[2]).toHaveTextContent(
|
|
||||||
"Good 'til Cancelled (GTC)"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,37 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import type { ClosingOrder as IClosingOrder } from '../use-close-position';
|
|
||||||
import { useRequestClosePositionData } from '../use-request-close-position-data';
|
|
||||||
import { ActiveOrders, ClosingOrder } from './shared';
|
|
||||||
|
|
||||||
export const Requested = ({
|
|
||||||
order,
|
|
||||||
partyId,
|
|
||||||
}: {
|
|
||||||
order?: IClosingOrder;
|
|
||||||
partyId: string;
|
|
||||||
}) => {
|
|
||||||
const { market, marketData, orders, loading } = useRequestClosePositionData(
|
|
||||||
order?.marketId,
|
|
||||||
partyId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading || !market || !marketData || !orders) {
|
|
||||||
return <div>{t('Loading...')}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!order) {
|
|
||||||
return (
|
|
||||||
<div className="text-vega-pink">
|
|
||||||
{t('Could not create closing order')}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2 className="font-bold">{t('Position to be closed')}</h2>
|
|
||||||
<ClosingOrder order={order} market={market} marketData={marketData} />
|
|
||||||
<ActiveOrders market={market} orders={orders} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,114 +0,0 @@
|
|||||||
import type { MarketData, Market } from '@vegaprotocol/market-list';
|
|
||||||
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
|
||||||
import { timeInForceLabel } from '@vegaprotocol/orders';
|
|
||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Size } from '@vegaprotocol/react-helpers';
|
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import type { ClosingOrder as IClosingOrder } from '../use-close-position';
|
|
||||||
|
|
||||||
export const ClosingOrder = ({
|
|
||||||
order,
|
|
||||||
market,
|
|
||||||
marketData,
|
|
||||||
}: {
|
|
||||||
order: IClosingOrder;
|
|
||||||
market: Market;
|
|
||||||
marketData: MarketData;
|
|
||||||
}) => {
|
|
||||||
const asset = market.tradableInstrument.instrument.product.settlementAsset;
|
|
||||||
const estimatedPrice =
|
|
||||||
marketData && market
|
|
||||||
? addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
|
|
||||||
: '-';
|
|
||||||
const size = market ? (
|
|
||||||
<Size
|
|
||||||
value={order.size}
|
|
||||||
side={order.side}
|
|
||||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'-'
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BasicTable
|
|
||||||
headers={[t('Market'), t('Amount'), t('Est price')]}
|
|
||||||
rows={[
|
|
||||||
[
|
|
||||||
market.tradableInstrument.instrument.name,
|
|
||||||
size,
|
|
||||||
`~${estimatedPrice} ${asset?.symbol}`,
|
|
||||||
],
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ActiveOrders = ({
|
|
||||||
market,
|
|
||||||
orders,
|
|
||||||
}: {
|
|
||||||
market: Market;
|
|
||||||
orders: OrderFieldsFragment[];
|
|
||||||
}) => {
|
|
||||||
const asset = market.tradableInstrument.instrument.product.settlementAsset;
|
|
||||||
|
|
||||||
if (!orders.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mt-4">
|
|
||||||
<h2 className="font-bold">{t('Orders to be closed')}</h2>
|
|
||||||
<BasicTable
|
|
||||||
headers={[t('Amount'), t('Target price'), t('Time in force')]}
|
|
||||||
rows={orders.map((o) => {
|
|
||||||
return [
|
|
||||||
<Size
|
|
||||||
value={o.size}
|
|
||||||
side={o.side}
|
|
||||||
positionDecimalPlaces={market.positionDecimalPlaces}
|
|
||||||
/>,
|
|
||||||
`${addDecimalsFormatNumber(o.price, market.decimalPlaces)} ${
|
|
||||||
asset.symbol
|
|
||||||
}`,
|
|
||||||
timeInForceLabel(o.timeInForce),
|
|
||||||
];
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface BasicTableProps {
|
|
||||||
headers: ReactNode[];
|
|
||||||
rows: ReactNode[][];
|
|
||||||
}
|
|
||||||
|
|
||||||
const BasicTable = ({ headers, rows }: BasicTableProps) => {
|
|
||||||
return (
|
|
||||||
<table className="w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{headers.map((h, i) => (
|
|
||||||
<th key={i} className="text-left font-medium">
|
|
||||||
{h}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{rows.map((cells, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
{cells.map((c, i) => (
|
|
||||||
<td key={i} className="align-top">
|
|
||||||
{c}
|
|
||||||
</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,211 +0,0 @@
|
|||||||
import type { ReactNode } from 'react';
|
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { renderHook, waitFor } from '@testing-library/react';
|
|
||||||
import * as Types from '@vegaprotocol/types';
|
|
||||||
import { useClosePosition } from './use-close-position';
|
|
||||||
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
|
|
||||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
|
||||||
import { initialState } from '@vegaprotocol/wallet';
|
|
||||||
import type { TransactionEventSubscription } from '@vegaprotocol/wallet';
|
|
||||||
import { TransactionEventDocument } from '@vegaprotocol/wallet';
|
|
||||||
import { act } from 'react-dom/test-utils';
|
|
||||||
import type { OrderSubSubscription } from '@vegaprotocol/orders';
|
|
||||||
import { OrderSubDocument } from '@vegaprotocol/orders';
|
|
||||||
|
|
||||||
const pubKey = 'test-pubkey';
|
|
||||||
const defaultWalletContext = {
|
|
||||||
pubKey,
|
|
||||||
pubKeys: [{ publicKey: pubKey, name: 'test pubkey' }],
|
|
||||||
isReadOnly: false,
|
|
||||||
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
|
||||||
connect: jest.fn(),
|
|
||||||
disconnect: jest.fn(),
|
|
||||||
selectPubKey: jest.fn(),
|
|
||||||
connector: null,
|
|
||||||
};
|
|
||||||
const txResult = {
|
|
||||||
__typename: 'TransactionResult',
|
|
||||||
partyId: pubKey,
|
|
||||||
hash: '0x123',
|
|
||||||
status: true,
|
|
||||||
error: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
function setup(context?: Partial<VegaWalletContextShape>) {
|
|
||||||
const mockTransactionResult: MockedResponse<TransactionEventSubscription> = {
|
|
||||||
request: {
|
|
||||||
query: TransactionEventDocument,
|
|
||||||
variables: {
|
|
||||||
partyId: context?.pubKey || '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: {
|
|
||||||
busEvents: [
|
|
||||||
{
|
|
||||||
type: Types.BusEventType.TransactionResult,
|
|
||||||
event: txResult,
|
|
||||||
__typename: 'BusEvent',
|
|
||||||
},
|
|
||||||
] as TransactionEventSubscription['busEvents'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const mockOrderResult: MockedResponse<OrderSubSubscription> = {
|
|
||||||
request: {
|
|
||||||
query: OrderSubDocument,
|
|
||||||
variables: {
|
|
||||||
partyId: context?.pubKey || '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: {
|
|
||||||
orders: [
|
|
||||||
{
|
|
||||||
type: Types.OrderType.TYPE_LIMIT,
|
|
||||||
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
|
|
||||||
status: Types.OrderStatus.STATUS_ACTIVE,
|
|
||||||
rejectionReason: null,
|
|
||||||
createdAt: '2022-07-05T14:25:47.815283706Z',
|
|
||||||
expiresAt: '2022-07-05T14:25:47.815283706Z',
|
|
||||||
size: '10',
|
|
||||||
price: '300000',
|
|
||||||
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
side: Types.Side.SIDE_BUY,
|
|
||||||
marketId: 'market-id',
|
|
||||||
__typename: 'OrderUpdate',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
||||||
<MockedProvider mocks={[mockTransactionResult, mockOrderResult]}>
|
|
||||||
<VegaWalletContext.Provider
|
|
||||||
value={{ ...defaultWalletContext, ...context }}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</VegaWalletContext.Provider>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
return renderHook(() => useClosePosition(), { wrapper });
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('useClosePosition', () => {
|
|
||||||
const txResponse = {
|
|
||||||
signature:
|
|
||||||
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909',
|
|
||||||
transactionHash: '0x123',
|
|
||||||
};
|
|
||||||
|
|
||||||
it('doesnt send the tx if there is no open volume', () => {
|
|
||||||
const mockSend = jest.fn();
|
|
||||||
const { result } = setup({ sendTx: mockSend });
|
|
||||||
expect(result.current).toEqual({
|
|
||||||
submit: expect.any(Function),
|
|
||||||
transaction: initialState,
|
|
||||||
Dialog: expect.any(Function),
|
|
||||||
});
|
|
||||||
result.current.submit({ marketId: 'test-market', openVolume: '0' });
|
|
||||||
expect(mockSend).not.toBeCalled();
|
|
||||||
expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('doesnt send the tx if there is no pubkey', () => {
|
|
||||||
const mockSend = jest.fn();
|
|
||||||
const { result } = setup({ sendTx: mockSend, pubKey: null });
|
|
||||||
result.current.submit({ marketId: 'test-market', openVolume: '1000' });
|
|
||||||
expect(mockSend).not.toBeCalled();
|
|
||||||
expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('closes long positions', async () => {
|
|
||||||
const marketId = 'test-market';
|
|
||||||
const openVolume = '1000';
|
|
||||||
const mockSend = jest.fn().mockResolvedValue(txResponse);
|
|
||||||
const { result } = setup({ sendTx: mockSend, pubKey });
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.submit({ marketId, openVolume });
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockSend).toBeCalledWith(defaultWalletContext.pubKey, {
|
|
||||||
batchMarketInstructions: {
|
|
||||||
cancellations: [
|
|
||||||
{
|
|
||||||
marketId,
|
|
||||||
orderId: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
submissions: [
|
|
||||||
{
|
|
||||||
marketId,
|
|
||||||
type: Types.OrderType.TYPE_MARKET,
|
|
||||||
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
side: Types.Side.SIDE_SELL,
|
|
||||||
size: openVolume,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.transaction.status).toEqual(VegaTxStatus.Requested);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(result.current.transaction).toEqual({
|
|
||||||
status: VegaTxStatus.Complete,
|
|
||||||
signature: txResponse.signature,
|
|
||||||
txHash: txResponse.transactionHash,
|
|
||||||
dialogOpen: true,
|
|
||||||
error: null,
|
|
||||||
});
|
|
||||||
expect(result.current.transactionResult).toEqual(txResult);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('closes short positions', async () => {
|
|
||||||
const marketId = 'test-market';
|
|
||||||
const openVolume = '-1000';
|
|
||||||
const mockSend = jest.fn().mockResolvedValue(txResponse);
|
|
||||||
const { result } = setup({ sendTx: mockSend, pubKey });
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
result.current.submit({ marketId, openVolume });
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockSend).toBeCalledWith(defaultWalletContext.pubKey, {
|
|
||||||
batchMarketInstructions: {
|
|
||||||
cancellations: [
|
|
||||||
{
|
|
||||||
marketId,
|
|
||||||
orderId: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
submissions: [
|
|
||||||
{
|
|
||||||
marketId,
|
|
||||||
type: Types.OrderType.TYPE_MARKET,
|
|
||||||
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
|
||||||
side: Types.Side.SIDE_BUY,
|
|
||||||
size: openVolume.replace('-', ''),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result.current.transaction.status).toEqual(VegaTxStatus.Requested);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(result.current.transaction).toEqual({
|
|
||||||
status: VegaTxStatus.Complete,
|
|
||||||
signature: txResponse.signature,
|
|
||||||
txHash: txResponse.transactionHash,
|
|
||||||
dialogOpen: true,
|
|
||||||
error: null,
|
|
||||||
});
|
|
||||||
expect(result.current.transactionResult).toEqual(txResult);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,105 +0,0 @@
|
|||||||
import { useCallback, useState } from 'react';
|
|
||||||
import type { TransactionResult } from '@vegaprotocol/wallet';
|
|
||||||
import { determineId } from '@vegaprotocol/wallet';
|
|
||||||
import { useVegaWallet, useTransactionResult } from '@vegaprotocol/wallet';
|
|
||||||
import { useVegaTransaction } from '@vegaprotocol/wallet';
|
|
||||||
import * as Sentry from '@sentry/react';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import { useOrderUpdate } from '@vegaprotocol/orders';
|
|
||||||
import type { OrderSubFieldsFragment } from '@vegaprotocol/orders';
|
|
||||||
|
|
||||||
export interface ClosingOrder {
|
|
||||||
marketId: string;
|
|
||||||
type: Schema.OrderType.TYPE_MARKET;
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK;
|
|
||||||
side: Schema.Side;
|
|
||||||
size: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useClosePosition = () => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
|
||||||
const { send, transaction, setComplete, Dialog } = useVegaTransaction();
|
|
||||||
const [closingOrder, setClosingOrder] = useState<ClosingOrder>();
|
|
||||||
const [closingOrderResult, setClosingOrderResult] =
|
|
||||||
useState<OrderSubFieldsFragment>();
|
|
||||||
const [transactionResult, setTransactionResult] =
|
|
||||||
useState<TransactionResult>();
|
|
||||||
const waitForTransactionResult = useTransactionResult();
|
|
||||||
const waitForOrder = useOrderUpdate(transaction);
|
|
||||||
|
|
||||||
const submit = useCallback(
|
|
||||||
async ({
|
|
||||||
marketId,
|
|
||||||
openVolume,
|
|
||||||
}: {
|
|
||||||
marketId: string;
|
|
||||||
openVolume: string;
|
|
||||||
}) => {
|
|
||||||
if (!pubKey || openVolume === '0') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTransactionResult(undefined);
|
|
||||||
setClosingOrder(undefined);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// figure out if position is long or short and make side the opposite
|
|
||||||
const side = openVolume.startsWith('-')
|
|
||||||
? Schema.Side.SIDE_BUY
|
|
||||||
: Schema.Side.SIDE_SELL;
|
|
||||||
|
|
||||||
// volume could be prefixed with '-' if position is short, remove it
|
|
||||||
const size = openVolume.replace('-', '');
|
|
||||||
const closingOrder = {
|
|
||||||
marketId: marketId,
|
|
||||||
type: Schema.OrderType.TYPE_MARKET as const,
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK as const,
|
|
||||||
side,
|
|
||||||
size,
|
|
||||||
};
|
|
||||||
|
|
||||||
setClosingOrder(closingOrder);
|
|
||||||
|
|
||||||
const command = {
|
|
||||||
batchMarketInstructions: {
|
|
||||||
cancellations: [
|
|
||||||
{
|
|
||||||
marketId,
|
|
||||||
orderId: '', // omit order id to cancel all active orders
|
|
||||||
},
|
|
||||||
],
|
|
||||||
submissions: [closingOrder],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await send(pubKey, command);
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
const orderId = determineId(res.signature);
|
|
||||||
const [txResult, orderResult] = await Promise.all([
|
|
||||||
waitForTransactionResult(res.transactionHash, pubKey),
|
|
||||||
waitForOrder(orderId, pubKey),
|
|
||||||
]);
|
|
||||||
setTransactionResult(txResult);
|
|
||||||
setClosingOrderResult(orderResult);
|
|
||||||
setComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
Sentry.captureException(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[pubKey, send, setComplete, waitForTransactionResult, waitForOrder]
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
transaction,
|
|
||||||
transactionResult,
|
|
||||||
submit,
|
|
||||||
closingOrder,
|
|
||||||
closingOrderResult,
|
|
||||||
Dialog,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,53 +0,0 @@
|
|||||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
|
||||||
import { isOrderActive, ordersProvider } from '@vegaprotocol/orders';
|
|
||||||
import type { OrdersQueryVariables } from '@vegaprotocol/orders';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
export const useRequestClosePositionData = (
|
|
||||||
marketId?: string,
|
|
||||||
partyId?: string
|
|
||||||
) => {
|
|
||||||
const marketVariables = useMemo(
|
|
||||||
() => ({ marketId: marketId || '' }),
|
|
||||||
[marketId]
|
|
||||||
);
|
|
||||||
const orderVariables = useMemo<OrdersQueryVariables>(
|
|
||||||
() => ({
|
|
||||||
partyId: partyId || '',
|
|
||||||
marketIds: marketId ? [marketId] : undefined,
|
|
||||||
}),
|
|
||||||
[partyId, marketId]
|
|
||||||
);
|
|
||||||
const { data: market, loading: marketLoading } = useDataProvider({
|
|
||||||
dataProvider: marketProvider,
|
|
||||||
variables: marketVariables,
|
|
||||||
skip: !marketId,
|
|
||||||
});
|
|
||||||
const { data: marketData, loading: marketDataLoading } = useDataProvider({
|
|
||||||
dataProvider: marketDataProvider,
|
|
||||||
variables: marketVariables,
|
|
||||||
});
|
|
||||||
const { data: orderData, loading: orderDataLoading } = useDataProvider({
|
|
||||||
dataProvider: ordersProvider,
|
|
||||||
variables: orderVariables,
|
|
||||||
skip: !partyId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const orders = useMemo(() => {
|
|
||||||
if (!orderData || !market) return [];
|
|
||||||
return (
|
|
||||||
orderData
|
|
||||||
.filter((o) => o?.node && isOrderActive(o.node.status))
|
|
||||||
// @ts-ignore o is never null as its been filtered out above
|
|
||||||
.map((o) => o.node)
|
|
||||||
);
|
|
||||||
}, [orderData, market]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
market,
|
|
||||||
marketData,
|
|
||||||
orders,
|
|
||||||
loading: marketLoading || marketDataLoading || orderDataLoading,
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user