diff --git a/apps/explorer/.env b/apps/explorer/.env index 964167bd5..d79b3a9a5 100644 --- a/apps/explorer/.env +++ b/apps/explorer/.env @@ -1,14 +1,14 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 NX_ETHERSCAN_URL=https://sepolia.etherscan.io -NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz +NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.rocks NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml NX_VEGA_ENV=STAGNET1 NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks NX_VEGA_WALLET_URL=http://localhost:1789 -NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz +NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket -NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest +NX_BLOCK_EXPLORER=https://be.stagnet1.vega.rocks/rest NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks diff --git a/apps/explorer/src/app/components/order-details/Order.graphql b/apps/explorer/src/app/components/order-details/Order.graphql index c5565fcb3..6d0cae1d9 100644 --- a/apps/explorer/src/app/components/order-details/Order.graphql +++ b/apps/explorer/src/app/components/order-details/Order.graphql @@ -13,6 +13,10 @@ fragment ExplorerDeterministicOrderFields on Order { remaining size rejectionReason + peggedOrder { + reference + offset + } party { id } diff --git a/apps/explorer/src/app/components/order-details/__generated__/Order.ts b/apps/explorer/src/app/components/order-details/__generated__/Order.ts index ceaa842f8..9fcd9cccf 100644 --- a/apps/explorer/src/app/components/order-details/__generated__/Order.ts +++ b/apps/explorer/src/app/components/order-details/__generated__/Order.ts @@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } }; +export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } }; export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{ orderId: Types.Scalars['ID']; @@ -11,7 +11,7 @@ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{ }>; -export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } }; +export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } }; export const ExplorerDeterministicOrderFieldsFragmentDoc = gql` fragment ExplorerDeterministicOrderFields on Order { @@ -29,6 +29,10 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql` remaining size rejectionReason + peggedOrder { + reference + offset + } party { id } diff --git a/apps/explorer/src/app/components/order-details/amend-order-details.spec.tsx b/apps/explorer/src/app/components/order-details/amend-order-details.spec.tsx index 36de96374..cc093f455 100644 --- a/apps/explorer/src/app/components/order-details/amend-order-details.spec.tsx +++ b/apps/explorer/src/app/components/order-details/amend-order-details.spec.tsx @@ -53,6 +53,7 @@ function renderExistingAmend( timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC, price: '200', side: 'BUY', + peggedOrder: null, remaining: '99', rejectionReason: 'rejection', reference: '123', @@ -100,6 +101,7 @@ function renderExistingAmend( updatedAt: '456', expiresAt: '789', timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC, + peggedOrder: null, price: '200', side: 'BUY', remaining: '99', diff --git a/apps/explorer/src/app/components/order-details/deterministic-order-details.tsx b/apps/explorer/src/app/components/order-details/deterministic-order-details.tsx index 2e3b51458..3c0fcf110 100644 --- a/apps/explorer/src/app/components/order-details/deterministic-order-details.tsx +++ b/apps/explorer/src/app/components/order-details/deterministic-order-details.tsx @@ -5,6 +5,7 @@ import PriceInMarket from '../price-in-market/price-in-market'; import { Time } from '../time'; import { sideText, statusText, tifFull, tifShort } from './lib/order-labels'; import SizeInMarket from '../size-in-market/size-in-market'; +import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg'; export interface DeterministicOrderDetailsProps { id: string; @@ -68,25 +69,35 @@ const DeterministicOrderDetails = ({ @ -

+

In at

+ {o.peggedOrder ? ( +

+ {t('Price peg')}:{' '} + +

+ ) : null} + {o.reference ? ( -

+

{t('Reference')}: {o.reference}

) : null} -
- {version !== 0 ? null : ( -
-

- {t('Status')} -

-
- {statusText[o.status]} -
-
- )} +
+
+

+ {t('Status')} +

+
+ {statusText[o.status]} +
+

{t('Size')}

@@ -95,17 +106,6 @@ const DeterministicOrderDetails = ({
- {version !== 0 ? null : ( -
-

- {t('Remaining')} -

-
- -
-
- )} -

{t('Version')} diff --git a/apps/explorer/src/app/components/order-summary/order-summary.spec.tsx b/apps/explorer/src/app/components/order-summary/order-summary.spec.tsx index 2343f53a2..9aecd0647 100644 --- a/apps/explorer/src/app/components/order-summary/order-summary.spec.tsx +++ b/apps/explorer/src/app/components/order-summary/order-summary.spec.tsx @@ -31,6 +31,7 @@ const mock = { side: 'SIDE_BUY', remaining: '100', size: '100', + peggedOrder: null, party: { id: '456', }, diff --git a/apps/explorer/src/app/components/txs/details/order/tx-order-peg.test.tsx b/apps/explorer/src/app/components/txs/details/order/tx-order-peg.test.tsx new file mode 100644 index 000000000..b17a469ae --- /dev/null +++ b/apps/explorer/src/app/components/txs/details/order/tx-order-peg.test.tsx @@ -0,0 +1,113 @@ +import { render } from '@testing-library/react'; +import type { TxDetailsOrderProps } from './tx-order-peg'; +import { TxOrderPeggedReference, getMarketDecimals } from './tx-order-peg'; +import { useExplorerMarketQuery } from '../../../links/market-link/__generated__/Market'; +import type { ExplorerMarketQuery } from '../../../links/market-link/__generated__/Market'; +import { PeggedReference, Side } from '@vegaprotocol/types'; + +// Mock the useExplorerMarketQuery hook +jest.mock('../../../links/market-link/__generated__/Market', () => ({ + useExplorerMarketQuery: jest.fn().mockReturnValue({ + data: { + market: { decimalPlaces: 0 }, + }, + loading: false, + }), +})); + +describe('getSettlementAsset', () => { + it('should return the decimal places if data is defined', () => { + const data = { + market: { + __typename: 'Market', + id: '123', + decimalPlaces: 8, + }, + }; + + const result = getMarketDecimals(data as Partial); + + expect(result).toEqual(8); + }); + + it('should return 0 if data is undefined', () => { + const result = getMarketDecimals(undefined); + + expect(result).toEqual(0); + }); +}); + +describe('TxOrderPeggedReference', () => { + beforeEach(() => { + // Mock the useExplorerMarketQuery hook return value + (useExplorerMarketQuery as jest.Mock).mockReturnValue({ + data: { + settlementAsset: 'some-settlement-asset', + }, + loading: false, + }); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should render the offset and reference correctly', () => { + const props: TxDetailsOrderProps = { + side: Side.SIDE_BUY, + offset: '10', + reference: PeggedReference.PEGGED_REFERENCE_MID, + marketId: 'some-market-id', + }; + + const { getByTestId } = render(); + + expect(getByTestId('pegged-reference')).toHaveTextContent('Mid + 10'); + }); + + it('should return null if the reference is "PEGGED_REFERENCE_UNSPECIFIED"', () => { + const props: TxDetailsOrderProps = { + side: Side.SIDE_BUY, + offset: '10', + reference: 'PEGGED_REFERENCE_UNSPECIFIED', + marketId: 'some-market-id', + }; + + const { container } = render(); + + expect(container.firstChild).toBeNull(); + }); + + it('should render the offset without formatting initially, then render the formatted version', () => { + const props: TxDetailsOrderProps = { + side: Side.SIDE_BUY, + offset: '10', + reference: PeggedReference.PEGGED_REFERENCE_BEST_ASK, + marketId: 'some-market-id', + }; + + (useExplorerMarketQuery as jest.Mock).mockReturnValue({ + data: null, + loading: true, + }); + + const screen = render(); + expect(screen.getByTestId('pegged-reference')).toHaveTextContent( + 'Ask + 10' + ); + + (useExplorerMarketQuery as jest.Mock).mockReturnValue({ + data: { + market: { + decimalPlaces: 10, + }, + }, + loading: false, + }); + + screen.rerender(); + expect(screen.getByTestId('pegged-reference')).toHaveTextContent( + 'Ask + 0.000000001' + ); + }); +}); diff --git a/apps/explorer/src/app/components/txs/details/order/tx-order-peg.tsx b/apps/explorer/src/app/components/txs/details/order/tx-order-peg.tsx new file mode 100644 index 000000000..26fd4a7d7 --- /dev/null +++ b/apps/explorer/src/app/components/txs/details/order/tx-order-peg.tsx @@ -0,0 +1,72 @@ +import { t } from '@vegaprotocol/i18n'; +import { TableCell, TableRow } from '../../../table'; +import type { VegaPeggedReference } from '../liquidity-provision/liquidity-provision-details'; +import { Side, PeggedReferenceMapping } from '@vegaprotocol/types'; +import { useExplorerMarketQuery } from '../../../links/market-link/__generated__/Market'; +import type { ExplorerMarketQuery } from '../../../links/market-link/__generated__/Market'; +import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; + +export interface TxDetailsOrderProps { + offset: string; + reference: VegaPeggedReference; + marketId: string; + side: Side; +} + +export function getMarketDecimals( + data: ExplorerMarketQuery | undefined +): number { + return data?.market?.decimalPlaces || 0; +} + +/** + * Summarises an order's peg + */ +export const TxOrderPeggedReferenceRow = ({ + offset, + reference, + marketId, + side, +}: TxDetailsOrderProps) => { + return ( + + {t('Pegged order')} + + + + + ); +}; + +export const TxOrderPeggedReference = ({ + offset, + reference, + marketId, + side, +}: TxDetailsOrderProps) => { + const { data, loading } = useExplorerMarketQuery({ + variables: { id: marketId }, + }); + + const direction = side === Side.SIDE_BUY ? '+' : '-'; + const decimalPlaces = getMarketDecimals(data); + + if (reference === 'PEGGED_REFERENCE_UNSPECIFIED') { + return null; + } + + return ( + + {PeggedReferenceMapping[reference]}  + {direction}  + {!loading && data + ? addDecimalsFormatNumber(offset, decimalPlaces) + : offset} + + ); +}; diff --git a/apps/explorer/src/app/components/txs/details/tx-order.tsx b/apps/explorer/src/app/components/txs/details/tx-order.tsx index 57faafa85..577fb34dd 100644 --- a/apps/explorer/src/app/components/txs/details/tx-order.tsx +++ b/apps/explorer/src/app/components/txs/details/tx-order.tsx @@ -7,6 +7,7 @@ import { TableCell, TableRow, TableWithTbody } from '../../table'; import { txSignatureToDeterministicId } from '../lib/deterministic-ids'; import DeterministicOrderDetails from '../../order-details/deterministic-order-details'; import Hash from '../../links/hash'; +import { TxOrderPeggedReferenceRow } from './order/tx-order-peg'; interface TxDetailsOrderProps { txData: BlockExplorerTransactionResult | undefined; @@ -29,6 +30,8 @@ export const TxDetailsOrder = ({ return <>{t('Awaiting Block Explorer transaction details')}; } const marketId = txData.command.orderSubmission.marketId || '-'; + const reference = txData.command.orderSubmission.peggedOrder; + const side = txData.command.orderSubmission.side; let deterministicId = ''; @@ -63,6 +66,14 @@ export const TxDetailsOrder = ({ + {reference ? ( + + ) : null} {deterministicId.length > 0 ? (