diff --git a/apps/trading/pages/party-active-orders-handler.tsx b/apps/trading/pages/party-active-orders-handler.tsx
index 8e60ead80..2a5db1af3 100644
--- a/apps/trading/pages/party-active-orders-handler.tsx
+++ b/apps/trading/pages/party-active-orders-handler.tsx
@@ -1,15 +1,8 @@
-import { useDataProvider } from '@vegaprotocol/data-provider';
-import { activeOrdersProvider } from '@vegaprotocol/orders';
+import { useActiveOrders } from '@vegaprotocol/orders';
import { useVegaWallet } from '@vegaprotocol/wallet-react';
export const PartyActiveOrdersHandler = () => {
const { pubKey } = useVegaWallet();
- const variables = { partyId: pubKey || '' };
- const skip = !pubKey;
- useDataProvider({
- dataProvider: activeOrdersProvider,
- variables,
- skip,
- });
+ useActiveOrders(pubKey);
return null;
};
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx
index 444bd81fb..a3089d677 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.spec.tsx
@@ -2,7 +2,7 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { generateMarket } from '../../test-helpers';
-import { StopOrder } from './deal-ticket-stop-order';
+import { NoOpenVolumeWarning, StopOrder } from './deal-ticket-stop-order';
import * as Schema from '@vegaprotocol/types';
import { MockedProvider } from '@apollo/client/testing';
import {
@@ -84,6 +84,22 @@ jest.mock('@vegaprotocol/data-provider', () => ({
useDataProvider: jest.fn((...args) => mockDataProvider(...args)),
}));
+const mockUseOpenVolume = jest.fn(() => ({
+ openVolume: '0',
+}));
+
+jest.mock('@vegaprotocol/positions', () => ({
+ ...jest.requireActual('@vegaprotocol/positions'),
+ useOpenVolume: jest.fn(() => mockUseOpenVolume()),
+}));
+
+const mockActiveOrders = jest.fn(() => ({}));
+
+jest.mock('@vegaprotocol/orders', () => ({
+ ...jest.requireActual('@vegaprotocol/orders'),
+ useActiveOrders: jest.fn(() => mockActiveOrders()),
+}));
+
describe('StopOrder', () => {
beforeEach(() => {
localStorage.clear();
@@ -576,3 +592,57 @@ describe('StopOrder', () => {
expect(screen.getByTestId(numberOfActiveOrdersLimit)).toBeInTheDocument();
});
});
+
+describe('NoOpenVolumeWarning', () => {
+ const testId = 'stop-order-warning-position';
+
+ it('shows warning if there is no possible position to reduce', () => {
+ const activeOrders = [
+ {
+ side: Schema.Side.SIDE_BUY,
+ remaining: '8',
+ },
+ {
+ side: Schema.Side.SIDE_BUY,
+ remaining: '2',
+ },
+ {
+ side: Schema.Side.SIDE_SELL,
+ remaining: '7',
+ },
+ {
+ side: Schema.Side.SIDE_SELL,
+ remaining: '3',
+ },
+ ];
+ mockActiveOrders.mockReturnValue({ data: activeOrders });
+
+ mockUseOpenVolume.mockReturnValue({ openVolume: '10' });
+ const result = render(
+
+ );
+ // side buy, volume + remaining 20
+ expect(screen.getByTestId(testId)).toBeInTheDocument();
+
+ result.rerender(
+
+ );
+ // side sell, volume - remaining = 0
+ expect(screen.queryByTestId(testId)).not.toBeInTheDocument();
+
+ result.rerender(
+
+ );
+ // side sell, volume - remaining = 0
+ expect(screen.queryByTestId(testId)).toBeInTheDocument();
+
+ mockUseOpenVolume.mockReturnValue({ openVolume: '2' });
+ render();
+ // side buy, volume + remaining = 12
+ expect(screen.queryByTestId(testId)).toBeInTheDocument();
+
+ render();
+ // side sell, volume - remaining = -8
+ expect(screen.getByTestId(testId)).toBeInTheDocument();
+ });
+});
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx
index 87a253e71..7800c4562 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-stop-order.tsx
@@ -56,9 +56,10 @@ import { validateExpiration } from '../../utils';
import { NOTIONAL_SIZE_TOOLTIP_TEXT } from '../../constants';
import { KeyValue } from './key-value';
import { useDataProvider } from '@vegaprotocol/data-provider';
-import { stopOrdersProvider } from '@vegaprotocol/orders';
+import { useActiveOrders, stopOrdersProvider } from '@vegaprotocol/orders';
import { useT } from '../../use-t';
import { determinePriceStep, determineSizeStep } from '@vegaprotocol/utils';
+import { useOpenVolume } from '@vegaprotocol/positions';
export interface StopOrderProps {
market: Market;
@@ -453,6 +454,47 @@ const Price = ({
);
};
+export const NoOpenVolumeWarning = ({
+ side,
+ partyId,
+ marketId,
+}: {
+ side: Schema.Side;
+ partyId?: string;
+ marketId: string;
+}) => {
+ const { data: activeOrders } = useActiveOrders(partyId, marketId);
+ const t = useT();
+ const { openVolume } = useOpenVolume(partyId, marketId) || {};
+ const volume = BigInt(openVolume || 0);
+ const remaining = activeOrders
+ ? activeOrders.reduce((size, order) => {
+ if (side !== order.side) {
+ size += BigInt(order.remaining);
+ }
+ return size;
+ }, BigInt(0))
+ : BigInt(0);
+
+ if (
+ (side === Schema.Side.SIDE_BUY && volume - remaining < BigInt(0)) ||
+ (side === Schema.Side.SIDE_SELL && volume + remaining > BigInt(0))
+ ) {
+ return null;
+ }
+ return (
+
+
+
+ );
+};
+
const TimeInForce = ({
control,
oco,
@@ -1188,8 +1230,13 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
)}
/>
- ) : null}
-
+ ) : (
+
+ )}
({
DealTicketFeeDetails: () => ,
+}));
+jest.mock('./deal-ticket-margin-details', () => ({
DealTicketMarginDetails: () => (
),
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
index 6e1accfef..a8c4b59ea 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
@@ -36,7 +36,7 @@ import {
formatForInput,
formatValue,
} from '@vegaprotocol/utils';
-import { activeOrdersProvider } from '@vegaprotocol/orders';
+import { useActiveOrders } from '@vegaprotocol/orders';
import {
getAsset,
getDerivedPrice,
@@ -252,11 +252,7 @@ export const DealTicket = ({
market.positionDecimalPlaces
);
- const { data: activeOrders } = useDataProvider({
- dataProvider: activeOrdersProvider,
- variables: { partyId: pubKey || '', marketId: market.id },
- skip: !pubKey,
- });
+ const { data: activeOrders } = useActiveOrders(pubKey, market.id);
const { data: margin } = useDataProvider({
dataProvider: marginModeDataProvider,
variables: { partyId: pubKey || '', marketId: market.id },
diff --git a/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx b/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx
index 5eedd1e8f..da8c19d9f 100644
--- a/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx
@@ -25,7 +25,7 @@ import {
useMarginAccountBalance,
} from '@vegaprotocol/accounts';
import { useMaxLeverage, useOpenVolume } from '@vegaprotocol/positions';
-import { activeOrdersProvider } from '@vegaprotocol/orders';
+import { useActiveOrders } from '@vegaprotocol/orders';
import { usePositionEstimate } from '../../hooks/use-position-estimate';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { getAsset, useMarket } from '@vegaprotocol/markets';
@@ -64,10 +64,7 @@ export const MarginChange = ({
openVolume: '0',
averageEntryPrice: '0',
};
- const { data: activeOrders } = useDataProvider({
- dataProvider: activeOrdersProvider,
- variables: { partyId: partyId || '', marketId },
- });
+ const { data: activeOrders } = useActiveOrders(partyId, marketId);
const orders = activeOrders
? activeOrders.map((order) => ({
isMarketOrder: order.type === Schema.OrderType.TYPE_MARKET,
diff --git a/libs/i18n/src/locales/en/deal-ticket.json b/libs/i18n/src/locales/en/deal-ticket.json
index d3d599b43..203697f8d 100644
--- a/libs/i18n/src/locales/en/deal-ticket.json
+++ b/libs/i18n/src/locales/en/deal-ticket.json
@@ -106,6 +106,7 @@
"Stop Limit": "Stop Limit",
"Stop Market": "Stop Market",
"Stop order will be triggered immediately": "Stop order will be triggered immediately",
+ "Stop orders are reduce only and this order would increase your position.": "Stop orders are reduce only and this order would increase your position.",
"Strategy": "Strategy",
"Submit": "Submit",
"Subtotal": "Subtotal",
diff --git a/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts b/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts
index 4cb2747a0..a77e870ad 100644
--- a/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts
+++ b/libs/orders/src/lib/components/order-data-provider/order-data-provider.ts
@@ -4,6 +4,7 @@ import {
makeDataProvider,
makeDerivedDataProvider,
defaultAppend as append,
+ useDataProvider,
} from '@vegaprotocol/data-provider';
import { type Market } from '@vegaprotocol/markets';
import { marketsMapProvider } from '@vegaprotocol/markets';
@@ -208,6 +209,18 @@ export const activeOrdersProvider = makeDerivedDataProvider<
}
);
+export const useActiveOrders = (
+ partyId: string | undefined,
+ marketId?: string
+) =>
+ useDataProvider({
+ dataProvider: activeOrdersProvider,
+ variables: marketId
+ ? { partyId: partyId || '', marketId }
+ : { partyId: partyId || '' },
+ skip: !partyId,
+ });
+
export const ordersWithMarketProvider = makeDerivedDataProvider<
(Order & Cursor)[],
never,
diff --git a/libs/types/src/global-types-mappings.ts b/libs/types/src/global-types-mappings.ts
index 5fbc370c0..4ddd2b573 100644
--- a/libs/types/src/global-types-mappings.ts
+++ b/libs/types/src/global-types-mappings.ts
@@ -10,7 +10,10 @@ import {
type TransferStatus,
MarketUpdateType,
} from './__generated__/types';
-import type { AccountType } from './__generated__/types';
+import type {
+ AccountType,
+ StopOrderRejectionReason,
+} from './__generated__/types';
import type {
AuctionTrigger,
DataSourceSpecStatus,
@@ -285,6 +288,29 @@ export const StopOrderStatusMapping: {
STATUS_UNSPECIFIED: 'Unspecified',
};
+/**
+ * Stop order rejection reason mappings.
+ */
+export const StopOrderRejectionReasonMapping: {
+ [T in StopOrderRejectionReason]: string;
+} = {
+ REJECTION_REASON_TRADING_NOT_ALLOWED: 'Trading is not allowed yet',
+ REJECTION_REASON_EXPIRY_IN_THE_PAST:
+ 'Expiry of the stop order is in the past',
+ REJECTION_REASON_MUST_BE_REDUCE_ONLY:
+ 'Stop orders submission must be reduce only',
+ REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED:
+ 'Party has reached the maximum stop orders allowed for this market',
+ REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION:
+ 'Stop orders are not allowed without a position',
+ REJECTION_REASON_STOP_ORDER_NOT_CLOSING_THE_POSITION:
+ 'This stop order does not close the position',
+ REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION:
+ 'Stop orders are not allowed during the opening auction',
+ REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES:
+ 'Stop order cannot have matching OCO expiry times',
+};
+
/**
* Valid order types, these determine what happens when an order is added to the book
*/
diff --git a/libs/web3/src/lib/Orders.graphql b/libs/web3/src/lib/Orders.graphql
index ea41306f5..c8515d2e2 100644
--- a/libs/web3/src/lib/Orders.graphql
+++ b/libs/web3/src/lib/Orders.graphql
@@ -42,6 +42,7 @@ query StopOrderById($stopOrderId: ID!) {
expiryStrategy
triggerDirection
status
+ rejectionReason
createdAt
updatedAt
partyId
diff --git a/libs/web3/src/lib/__generated__/Orders.ts b/libs/web3/src/lib/__generated__/Orders.ts
index e53c1f02c..628b2f892 100644
--- a/libs/web3/src/lib/__generated__/Orders.ts
+++ b/libs/web3/src/lib/__generated__/Orders.ts
@@ -15,7 +15,7 @@ export type StopOrderByIdQueryVariables = Types.Exact<{
}>;
-export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
+export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, rejectionReason?: Types.StopOrderRejectionReason | null, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
export const OrderByIdDocument = gql`
@@ -92,6 +92,7 @@ export const StopOrderByIdDocument = gql`
expiryStrategy
triggerDirection
status
+ rejectionReason
createdAt
updatedAt
partyId
diff --git a/libs/web3/src/lib/use-vega-transaction-store.tsx b/libs/web3/src/lib/use-vega-transaction-store.tsx
index 0ae4a2a0e..21da971a1 100644
--- a/libs/web3/src/lib/use-vega-transaction-store.tsx
+++ b/libs/web3/src/lib/use-vega-transaction-store.tsx
@@ -47,6 +47,7 @@ export interface VegaTransactionStore {
) => void;
dismiss: (index: number) => void;
delete: (index: number) => void;
+ getTransaction: (txHash: string) => VegaStoredTxState | undefined;
updateWithdrawal: (
withdrawal: NonNullable,
withdrawalApproval: NonNullable
@@ -60,6 +61,12 @@ export interface VegaTransactionStore {
export const useVegaTransactionStore = create()(
subscribeWithSelector((set, get) => ({
transactions: [] as (VegaStoredTxState | undefined)[],
+ getTransaction: (txHash: string) => {
+ return get().transactions.find(
+ (transaction) =>
+ transaction?.txHash && transaction.txHash.toLowerCase() === txHash
+ );
+ },
create: (body: Transaction, order?: OrderTxUpdateFieldsFragment) => {
const transactions = get().transactions;
const now = new Date();
diff --git a/libs/web3/src/lib/use-vega-transaction-updater.spec.tsx b/libs/web3/src/lib/use-vega-transaction-updater.spec.tsx
index fb5c9df79..6238e3b0c 100644
--- a/libs/web3/src/lib/use-vega-transaction-updater.spec.tsx
+++ b/libs/web3/src/lib/use-vega-transaction-updater.spec.tsx
@@ -23,6 +23,9 @@ import {
OrderTimeInForce,
OrderType,
Side,
+ StopOrderRejectionReason,
+ StopOrderRejectionReasonMapping,
+ StopOrderStatus,
WithdrawalStatus,
} from '@vegaprotocol/types';
@@ -48,14 +51,22 @@ jest.mock('./wait-for-withdrawal-approval', () => ({
waitForWithdrawalApproval: () => mockWaitForWithdrawalApproval(),
}));
+const mockWaitForStopOrder = jest.fn();
+
+jest.mock('./wait-for-stop-order', () => ({
+ waitForStopOrder: () => mockWaitForStopOrder(),
+}));
+
const updateWithdrawal = jest.fn();
const updateOrder = jest.fn();
const updateTransactionResult = jest.fn();
+const getTransaction = jest.fn();
const defaultState: Partial = {
updateWithdrawal,
updateOrder,
updateTransactionResult,
+ getTransaction,
};
const mockTransactionStoreState = jest.fn, []>();
@@ -180,6 +191,29 @@ describe('useVegaTransactionManager', () => {
});
});
+ it('waits for stop order and sets error if rejected', async () => {
+ getTransaction.mockReturnValueOnce({
+ signature: 'signature',
+ body: {
+ stopOrdersSubmission: {},
+ },
+ });
+ const rejectionReason =
+ StopOrderRejectionReason.REJECTION_REASON_STOP_ORDER_NOT_CLOSING_THE_POSITION;
+ mockTransactionStoreState.mockReturnValue(defaultState);
+ mockWaitForStopOrder.mockResolvedValueOnce({
+ status: StopOrderStatus.STATUS_REJECTED,
+ rejectionReason,
+ });
+ render([mockedTransactionResultBusEvent]);
+ await waitFor(() => {
+ expect(updateTransactionResult).toHaveBeenCalledWith({
+ ...transactionResultBusEvent,
+ error: StopOrderRejectionReasonMapping[rejectionReason],
+ });
+ });
+ });
+
it('updates withdrawal on WithdrawalBusEvents', async () => {
mockTransactionStoreState.mockReturnValue(defaultState);
const erc20WithdrawalApproval = {};
diff --git a/libs/web3/src/lib/use-vega-transaction-updater.tsx b/libs/web3/src/lib/use-vega-transaction-updater.tsx
index 6319317f0..553d132dd 100644
--- a/libs/web3/src/lib/use-vega-transaction-updater.tsx
+++ b/libs/web3/src/lib/use-vega-transaction-updater.tsx
@@ -7,6 +7,16 @@ import {
} from './__generated__/TransactionResult';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import { waitForWithdrawalApproval } from './wait-for-withdrawal-approval';
+import {
+ determineId,
+ isStopOrdersSubmissionTransaction,
+} from '@vegaprotocol/wallet';
+import { waitForStopOrder } from './wait-for-stop-order';
+import {
+ StopOrderRejectionReasonMapping,
+ StopOrderStatus,
+ StopOrderStatusMapping,
+} from '@vegaprotocol/types';
export const useVegaTransactionUpdater = () => {
const client = useApolloClient();
@@ -17,6 +27,9 @@ export const useVegaTransactionUpdater = () => {
const updateTransaction = useVegaTransactionStore(
(state) => state.updateTransactionResult
);
+ const getTransaction = useVegaTransactionStore(
+ (state) => state.getTransaction
+ );
const { pubKey } = useVegaWallet();
const variables = { partyId: pubKey || '' };
@@ -51,11 +64,43 @@ export const useVegaTransactionUpdater = () => {
variables,
skip,
fetchPolicy: 'no-cache',
- onData: ({ data: result }) =>
- result.data?.busEvents?.forEach((event) => {
- if (event.event.__typename === 'TransactionResult') {
- updateTransaction(event.event);
+ onData: ({ data: result }) => {
+ result.data?.busEvents?.forEach(({ event }) => {
+ if (event.__typename === 'TransactionResult') {
+ let updateImmediately = true;
+ if (event.status && !event.error) {
+ const transaction = getTransaction(event.hash.toLocaleLowerCase());
+ if (
+ transaction &&
+ transaction.signature &&
+ isStopOrdersSubmissionTransaction(transaction.body)
+ ) {
+ waitForStopOrder(determineId(transaction.signature), client).then(
+ (stopOrder) => {
+ updateTransaction(
+ stopOrder &&
+ stopOrder.status === StopOrderStatus.STATUS_REJECTED
+ ? {
+ ...event,
+ error:
+ (stopOrder.rejectionReason &&
+ StopOrderRejectionReasonMapping[
+ stopOrder.rejectionReason
+ ]) ||
+ StopOrderStatusMapping[stopOrder.status],
+ }
+ : event
+ );
+ }
+ );
+ updateImmediately = false;
+ }
+ }
+ if (updateImmediately) {
+ updateTransaction(event);
+ }
}
- }),
+ });
+ },
});
};
diff --git a/libs/web3/src/lib/wait-for-stop-order.spec.tsx b/libs/web3/src/lib/wait-for-stop-order.spec.tsx
new file mode 100644
index 000000000..a4672d1eb
--- /dev/null
+++ b/libs/web3/src/lib/wait-for-stop-order.spec.tsx
@@ -0,0 +1,72 @@
+import { ApolloClient, InMemoryCache } from '@apollo/client';
+import { MockLink } from '@apollo/client/testing';
+import type { StopOrderByIdQuery } from './__generated__/Orders';
+import { StopOrderByIdDocument } from './__generated__/Orders';
+import type { MockedResponse } from '@apollo/client/testing';
+import { waitForStopOrder } from './wait-for-stop-order';
+import {
+ OrderTimeInForce,
+ OrderType,
+ Side,
+ StopOrderStatus,
+ StopOrderTriggerDirection,
+} from '@vegaprotocol/types';
+
+const stopOrderId =
+ 'ad427c4f5cb599e73ffb6f0ae371d1e0fcba89b6be2401a06e61cab982668d63';
+
+const stopOrder: StopOrderByIdQuery['stopOrder'] = {
+ __typename: 'StopOrder',
+ id: stopOrderId,
+ ocoLinkId: null,
+ expiresAt: null,
+ expiryStrategy: null,
+ triggerDirection: StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE,
+ status: StopOrderStatus.STATUS_PENDING,
+ rejectionReason: null,
+ createdAt: '2024-03-25T10:18:48.946943Z',
+ updatedAt: null,
+ partyId: '02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65',
+ marketId: '00788b763b999ef555ac5da17de155ff4237dd14aa6671a303d1285f27f094f0',
+ trigger: {
+ __typename: 'StopOrderPrice',
+ price: '700000',
+ },
+ submission: {
+ __typename: 'OrderSubmission',
+ marketId:
+ '00788b763b999ef555ac5da17de155ff4237dd14aa6671a303d1285f27f094f0',
+ price: '0',
+ size: '1',
+ side: Side.SIDE_BUY,
+ timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
+ expiresAt: null,
+ type: OrderType.TYPE_MARKET,
+ reference: '',
+ peggedOrder: null,
+ postOnly: false,
+ reduceOnly: true,
+ },
+};
+
+const mockedStopOrderById: MockedResponse = {
+ request: {
+ query: StopOrderByIdDocument,
+ variables: { stopOrderId },
+ },
+ result: {
+ data: {
+ stopOrder,
+ },
+ },
+};
+
+describe('waitForStopOrder', () => {
+ it('resolves with matching stopOrder', async () => {
+ const client = new ApolloClient({
+ cache: new InMemoryCache(),
+ link: new MockLink([mockedStopOrderById]),
+ });
+ expect(await waitForStopOrder(stopOrderId, client)).toEqual(stopOrder);
+ });
+});
diff --git a/libs/web3/src/lib/wait-for-stop-order.tsx b/libs/web3/src/lib/wait-for-stop-order.tsx
new file mode 100644
index 000000000..596356b81
--- /dev/null
+++ b/libs/web3/src/lib/wait-for-stop-order.tsx
@@ -0,0 +1,32 @@
+import { type ApolloClient } from '@apollo/client';
+import {
+ StopOrderByIdDocument,
+ type StopOrderByIdQuery,
+ type StopOrderByIdQueryVariables,
+} from './__generated__/Orders';
+
+export const waitForStopOrder = (
+ stopOrderId: string,
+ client: ApolloClient