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 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`
|
||||
fragment LiquidityProvisionFields on LiquidityProvision {
|
||||
party {
|
||||
@ -257,37 +250,3 @@ export function useLiquidityProviderFeeShareLazyQuery(baseOptions?: Apollo.LazyQ
|
||||
export type LiquidityProviderFeeShareQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareQuery>;
|
||||
export type LiquidityProviderFeeShareLazyQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareLazyQuery>;
|
||||
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 {
|
||||
LiquidityProviderFeeShareDocument,
|
||||
LiquidityProviderFeeShareUpdateDocument,
|
||||
LiquidityProvisionsDocument,
|
||||
LiquidityProvisionsUpdateDocument,
|
||||
MarketLpDocument,
|
||||
@ -18,7 +17,6 @@ import type {
|
||||
LiquidityProviderFeeShareFieldsFragment,
|
||||
LiquidityProviderFeeShareQuery,
|
||||
LiquidityProviderFeeShareQueryVariables,
|
||||
LiquidityProviderFeeShareUpdateSubscription,
|
||||
LiquidityProvisionFieldsFragment,
|
||||
LiquidityProvisionsQuery,
|
||||
LiquidityProvisionsQueryVariables,
|
||||
@ -115,42 +113,14 @@ export const marketLiquidityDataProvider = makeDataProvider<
|
||||
export const liquidityFeeShareDataProvider = makeDataProvider<
|
||||
LiquidityProviderFeeShareQuery,
|
||||
LiquidityProviderFeeShareFieldsFragment[],
|
||||
LiquidityProviderFeeShareUpdateSubscription,
|
||||
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare'],
|
||||
never,
|
||||
never,
|
||||
LiquidityProviderFeeShareQueryVariables
|
||||
>({
|
||||
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) => {
|
||||
return data?.market?.data?.liquidityProviderFeeShare || [];
|
||||
},
|
||||
getDelta: (subscriptionData: LiquidityProviderFeeShareUpdateSubscription) => {
|
||||
return subscriptionData.marketsData[0].liquidityProviderFeeShare;
|
||||
},
|
||||
});
|
||||
|
||||
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-calculator';
|
||||
export * from './lib/positions-table';
|
||||
export * from './lib/use-close-position';
|
||||
export * from './lib/use-market-margin';
|
||||
export * from './lib/use-market-position-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