From 94e398dd1c56e3c85ca38e59c7a565225d358683 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Mon, 17 Jul 2023 17:24:51 +0300 Subject: [PATCH] feat(trading): submit iceberg orders (#4230) --- .../proposal/__generated__/Proposal.ts | 4 +- .../src/integration/trading-portfolio.cy.ts | 2 +- .../deal-ticket/deal-ticket-size-iceberg.tsx | 195 ++++++++++++++++++ .../deal-ticket/deal-ticket.spec.tsx | 103 +++++++++ .../components/deal-ticket/deal-ticket.tsx | 62 ++++++ libs/deal-ticket/src/hooks/use-order-form.ts | 9 +- .../src/lib/__generated__/MarketLiquidity.ts | 2 +- .../__generated__/Orders.ts | 5 +- .../order-data-provider.ts | 1 - .../lib/components/order-list/order-list.tsx | 14 +- .../__generated__/OrdersSubscription.ts | 2 +- .../src/lib/order-hooks/use-order-store.ts | 6 + libs/types/src/__generated__/types.ts | 2 + libs/types/src/global-types-mappings.ts | 1 + .../src/components/form-group/form-group.tsx | 2 +- libs/utils/src/lib/format/range.ts | 2 +- .../src/__generated__/TransactionResult.ts | 5 +- libs/wallet/src/connectors/vega-connector.ts | 4 + libs/wallet/src/utils.ts | 7 + 19 files changed, 403 insertions(+), 25 deletions(-) create mode 100644 libs/deal-ticket/src/components/deal-ticket/deal-ticket-size-iceberg.tsx diff --git a/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts b/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts index 2674364a2..c734b6605 100644 --- a/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts +++ b/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts @@ -8,7 +8,7 @@ export type ProposalQueryVariables = Types.Exact<{ }>; -export type ProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, positionDecimalPlaces: number, linearSlippageFactor: string, quadraticSlippageFactor: string, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename?: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | null } | { __typename?: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } | null }; +export type ProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', decimalPlaces: number, metadata?: Array | null, lpPriceRange: string, positionDecimalPlaces: number, linearSlippageFactor: string, quadraticSlippageFactor: string, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename?: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | null } | { __typename?: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } | null }; export const ProposalDocument = gql` @@ -294,4 +294,4 @@ export function useProposalLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions

; export type ProposalLazyQueryHookResult = ReturnType; -export type ProposalQueryResult = Apollo.QueryResult; +export type ProposalQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/apps/trading-e2e/src/integration/trading-portfolio.cy.ts b/apps/trading-e2e/src/integration/trading-portfolio.cy.ts index 6a46f35c4..6fe9208c9 100644 --- a/apps/trading-e2e/src/integration/trading-portfolio.cy.ts +++ b/apps/trading-e2e/src/integration/trading-portfolio.cy.ts @@ -50,7 +50,7 @@ describe('Portfolio page', { tags: '@smoke' }, () => { cy.get('fieldset.ag-simple-filter-body-wrapper') .should('be.visible') .within((fields) => { - cy.wrap(fields).find('label').should('have.length', 17); + cy.wrap(fields).find('label').should('have.length', 18); }); cy.getByTestId('"Ledger entries"').click(); cy.get('fieldset.ag-simple-filter-body-wrapper').should('not.exist'); diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-size-iceberg.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-size-iceberg.tsx new file mode 100644 index 000000000..a271eb105 --- /dev/null +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-size-iceberg.tsx @@ -0,0 +1,195 @@ +import { Controller, type Control } from 'react-hook-form'; +import type { Market } from '@vegaprotocol/markets'; +import type { OrderObj } from '@vegaprotocol/orders'; +import type { OrderFormFields } from '../../hooks/use-order-form'; +import { toDecimal, validateAmount } from '@vegaprotocol/utils'; +import { t } from '@vegaprotocol/i18n'; +import { + FormGroup, + Input, + InputError, + Tooltip, +} from '@vegaprotocol/ui-toolkit'; + +export interface DealTicketSizeIcebergProps { + control: Control; + market: Market; + peakSizeError?: string; + minimumVisibleSizeError?: string; + update: (obj: Partial) => void; + peakSize: string; + minimumVisibleSize: string; + size: string; +} + +export const DealTicketSizeIceberg = ({ + control, + market, + update, + peakSizeError, + minimumVisibleSizeError, + peakSize, + minimumVisibleSize, + size, +}: DealTicketSizeIcebergProps) => { + const sizeStep = toDecimal(market?.positionDecimalPlaces); + + const renderPeakSizeError = () => { + if (peakSizeError) { + return ( + + {peakSizeError} + + ); + } + + return null; + }; + + const renderMinimumSizeError = () => { + if (minimumVisibleSizeError) { + return ( + + {minimumVisibleSizeError} + + ); + } + + return null; + }; + + return ( +

+
+
+ + {t( + 'The maximum volume that can be traded at once. Must be less than the total size of the order.' + )} +
+ } + > + {t('Peak size')} + + } + labelFor="input-order-peak-size" + className="!mb-1" + > + ( + + update({ + icebergOpts: { + peakSize: e.target.value, + minimumVisibleSize, + }, + }) + } + step={sizeStep} + min={sizeStep} + max={size} + data-testid="order-peak-size" + onWheel={(e) => e.currentTarget.blur()} + /> + )} + /> + +
+
+
+
+
+
+ + {t( + 'When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.' + )} +
+ } + > + {t('Minimum size')} + + } + labelFor="input-order-minimum-size" + className="!mb-1" + > + ( + + update({ + icebergOpts: { + peakSize, + minimumVisibleSize: e.target.value, + }, + }) + } + step={sizeStep} + min={sizeStep} + max={peakSize} + data-testid="order-minimum-size" + onWheel={(e) => e.currentTarget.blur()} + /> + )} + /> + +
+ + {renderPeakSizeError()} + {renderMinimumSizeError()} + + ); +}; diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx index 0c91e4592..3afdd2385 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx @@ -224,6 +224,109 @@ describe('DealTicket', () => { expect(screen.getByTestId('reduce-only')).not.toBeChecked(); }); + it('should set values for a persistent post only iceberg order and disable reduce only checkbox', () => { + const expectedOrder = { + marketId: market.id, + type: Schema.OrderType.TYPE_LIMIT, + side: Schema.Side.SIDE_SELL, + size: '10', + price: '300.22', + timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC, + persist: true, + reduceOnly: false, + postOnly: true, + iceberg: true, + icebergOpts: { + peakSize: '5', + minimumVisibleSize: '7', + }, + }; + + useOrderStore.setState({ + orders: { + [expectedOrder.marketId]: expectedOrder, + }, + }); + + render(generateJsx()); + + // Assert correct defaults are used from store + expect( + screen + .getByTestId(`order-type-${Schema.OrderType.TYPE_LIMIT}`) + .querySelector('input') + ).toBeChecked(); + expect( + screen.queryByTestId('order-side-SIDE_SELL')?.querySelector('input') + ).toBeChecked(); + expect( + screen.queryByTestId('order-side-SIDE_BUY')?.querySelector('input') + ).not.toBeChecked(); + expect(screen.getByTestId('order-size')).toHaveDisplayValue( + expectedOrder.size + ); + expect(screen.getByTestId('order-tif')).toHaveValue( + expectedOrder.timeInForce + ); + expect(screen.getByTestId('order-price')).toHaveDisplayValue( + expectedOrder.price + ); + expect(screen.getByTestId('post-only')).toBeEnabled(); + expect(screen.getByTestId('reduce-only')).toBeDisabled(); + expect(screen.getByTestId('post-only')).toBeChecked(); + expect(screen.getByTestId('reduce-only')).not.toBeChecked(); + expect(screen.getByTestId('iceberg')).toBeEnabled(); + expect(screen.getByTestId('iceberg')).toBeChecked(); + }); + + it('should set values for a non-persistent iceberg order and disable post only checkbox', () => { + const expectedOrder = { + marketId: market.id, + type: Schema.OrderType.TYPE_LIMIT, + side: Schema.Side.SIDE_SELL, + size: '0.1', + price: '300.22', + timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC, + persist: false, + reduceOnly: false, + postOnly: false, + }; + useOrderStore.setState({ + orders: { + [expectedOrder.marketId]: expectedOrder, + }, + }); + + render(generateJsx()); + + // Assert correct defaults are used from store + expect( + screen + .getByTestId(`order-type-${Schema.OrderType.TYPE_LIMIT}`) + .querySelector('input') + ).toBeChecked(); + expect( + screen.queryByTestId('order-side-SIDE_SELL')?.querySelector('input') + ).toBeChecked(); + expect( + screen.queryByTestId('order-side-SIDE_BUY')?.querySelector('input') + ).not.toBeChecked(); + expect(screen.getByTestId('order-size')).toHaveDisplayValue( + expectedOrder.size + ); + expect(screen.getByTestId('order-tif')).toHaveValue( + expectedOrder.timeInForce + ); + expect(screen.getByTestId('order-price')).toHaveDisplayValue( + expectedOrder.price + ); + expect(screen.getByTestId('post-only')).toBeDisabled(); + expect(screen.getByTestId('reduce-only')).toBeEnabled(); + expect(screen.getByTestId('reduce-only')).not.toBeChecked(); + expect(screen.getByTestId('post-only')).not.toBeChecked(); + expect(screen.getByTestId('iceberg')).not.toBeChecked(); + }); + it('handles TIF select box dependent on order type', async () => { render(generateJsx()); 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 708b11ed6..e8310c260 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx @@ -54,6 +54,7 @@ import { import { OrderTimeInForce, OrderType } from '@vegaprotocol/types'; import { useOrderForm } from '../../hooks/use-order-form'; import { useDataProvider } from '@vegaprotocol/data-provider'; +import { DealTicketSizeIceberg } from './deal-ticket-size-iceberg'; export interface DealTicketProps { market: Market; @@ -292,6 +293,22 @@ export const DealTicket = ({ timeInForce: lastTIF[type] || order.timeInForce, postOnly: type === OrderType.TYPE_MARKET ? false : order.postOnly, + iceberg: + type === OrderType.TYPE_MARKET || + [ + OrderTimeInForce.TIME_IN_FORCE_FOK, + OrderTimeInForce.TIME_IN_FORCE_IOC, + ].includes(lastTIF[type] || order.timeInForce) + ? false + : order.iceberg, + icebergOpts: + type === OrderType.TYPE_MARKET || + [ + OrderTimeInForce.TIME_IN_FORCE_FOK, + OrderTimeInForce.TIME_IN_FORCE_IOC, + ].includes(lastTIF[type] || order.timeInForce) + ? undefined + : order.icebergOpts, reduceOnly: type === OrderType.TYPE_LIMIT && ![ @@ -463,6 +480,51 @@ export const DealTicket = ({ )} /> +
+ {order.type === Schema.OrderType.TYPE_LIMIT && ( + ( + { + update({ iceberg: !order.iceberg, icebergOpts: undefined }); + }} + label={ + + {t(`Trade only a fraction of the order size at once. + After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. + For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. + Note that the full volume of the order is not hidden and is still reflected in the order book.`)} +

+ } + > + {t('Iceberg')} +
+ } + /> + )} + /> + )} +
+ {order.iceberg && ( + + )} { } }, [order, isSubmitted, getValues, setValue]); - const handleSubmitWrapper = ( - cb: (o: Exact) => void - ) => { + const handleSubmitWrapper = (cb: (o: OrderSubmission) => void) => { return handleSubmit(() => { - // remove the persist key from the order in the store, the wallet will reject + // remove the persist and iceberg key from the order in the store, the wallet will reject // an order that contains unrecognized additional keys - cb(omit(order, 'persist')); + cb(omit(order, 'persist', 'iceberg')); }); }; diff --git a/libs/liquidity/src/lib/__generated__/MarketLiquidity.ts b/libs/liquidity/src/lib/__generated__/MarketLiquidity.ts index 93b777618..efc57fba3 100644 --- a/libs/liquidity/src/lib/__generated__/MarketLiquidity.ts +++ b/libs/liquidity/src/lib/__generated__/MarketLiquidity.ts @@ -180,4 +180,4 @@ export function useLiquidityProviderFeeShareLazyQuery(baseOptions?: Apollo.LazyQ } export type LiquidityProviderFeeShareQueryHookResult = ReturnType; export type LiquidityProviderFeeShareLazyQueryHookResult = ReturnType; -export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult; +export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/orders/src/lib/components/order-data-provider/__generated__/Orders.ts b/libs/orders/src/lib/components/order-data-provider/__generated__/Orders.ts index 290dbdbb6..4c3071990 100644 --- a/libs/orders/src/lib/components/order-data-provider/__generated__/Orders.ts +++ b/libs/orders/src/lib/components/order-data-provider/__generated__/Orders.ts @@ -22,7 +22,7 @@ export type OrdersQueryVariables = Types.Exact<{ export type OrdersQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, ordersConnection?: { __typename?: 'OrderConnection', edges?: Array<{ __typename?: 'OrderEdge', cursor?: string | null, node: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } }> | null, pageInfo?: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } | null } | null } | null }; -export type OrderUpdateFieldsFragment = { __typename?: 'OrderUpdate', id: string, marketId: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, liquidityProvisionId?: string | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename?: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null }; +export type OrderUpdateFieldsFragment = { __typename?: 'OrderUpdate', id: string, marketId: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, liquidityProvisionId?: string | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null }; export type OrdersUpdateSubscriptionVariables = Types.Exact<{ partyId: Types.Scalars['ID']; @@ -30,7 +30,7 @@ export type OrdersUpdateSubscriptionVariables = Types.Exact<{ }>; -export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', id: string, marketId: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, liquidityProvisionId?: string | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename?: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null }> | null }; +export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', id: string, marketId: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, liquidityProvisionId?: string | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null }> | null }; export const OrderFieldsFragmentDoc = gql` fragment OrderFields on Order { @@ -89,6 +89,7 @@ export const OrderUpdateFieldsFragmentDoc = gql` offset } icebergOrder { + __typename peakSize minimumVisibleSize reservedRemaining 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 846a6aef0..d5f68bdb1 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 @@ -65,7 +65,6 @@ export const mapOrderUpdateToOrder = ( liquidityProvision: liquidityProvision, icebergOrder: order.icebergOrder ? { - __typename: 'IcebergOrder', ...order.icebergOrder, } : undefined, diff --git a/libs/orders/src/lib/components/order-list/order-list.tsx b/libs/orders/src/lib/components/order-list/order-list.tsx index b92bee2d0..4e221537b 100644 --- a/libs/orders/src/lib/components/order-list/order-list.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.tsx @@ -267,12 +267,14 @@ export const OrderListTable = memo<
{isOrderAmendable(data) && !props.isReadOnly && ( <> - onEdit(data)} - > - {t('Edit')} - + {!data.icebergOrder && ( + onEdit(data)} + > + {t('Edit')} + + )} onCancel(data)} diff --git a/libs/orders/src/lib/order-hooks/__generated__/OrdersSubscription.ts b/libs/orders/src/lib/order-hooks/__generated__/OrdersSubscription.ts index 98caee3c5..b5d7885c3 100644 --- a/libs/orders/src/lib/order-hooks/__generated__/OrdersSubscription.ts +++ b/libs/orders/src/lib/order-hooks/__generated__/OrdersSubscription.ts @@ -56,4 +56,4 @@ export function useOrderSubSubscription(baseOptions: Apollo.SubscriptionHookOpti return Apollo.useSubscription(OrderSubDocument, options); } export type OrderSubSubscriptionHookResult = ReturnType; -export type OrderSubSubscriptionResult = Apollo.SubscriptionResult; +export type OrderSubSubscriptionResult = Apollo.SubscriptionResult; \ No newline at end of file diff --git a/libs/orders/src/lib/order-hooks/use-order-store.ts b/libs/orders/src/lib/order-hooks/use-order-store.ts index 7f5a20d01..dd8ffaba8 100644 --- a/libs/orders/src/lib/order-hooks/use-order-store.ts +++ b/libs/orders/src/lib/order-hooks/use-order-store.ts @@ -16,7 +16,13 @@ export type OrderObj = { persist: boolean; // key used to determine if order should be kept in localStorage postOnly?: boolean; reduceOnly?: boolean; + iceberg?: boolean; + icebergOpts?: { + peakSize: string; + minimumVisibleSize: string; + }; }; + type OrderMap = { [marketId: string]: OrderObj | undefined }; type UpdateOrder = ( diff --git a/libs/types/src/__generated__/types.ts b/libs/types/src/__generated__/types.ts index 0fa801619..18605c0bd 100644 --- a/libs/types/src/__generated__/types.ts +++ b/libs/types/src/__generated__/types.ts @@ -96,6 +96,8 @@ export enum AccountType { ACCOUNT_TYPE_HOLDING = 'ACCOUNT_TYPE_HOLDING', /** Insurance pool account - only for 'system' party */ ACCOUNT_TYPE_INSURANCE = 'ACCOUNT_TYPE_INSURANCE', + /** Per liquidity provider, per market account for holding LPs' fees before distribution */ + ACCOUNT_TYPE_LP_LIQUIDITY_FEES = 'ACCOUNT_TYPE_LP_LIQUIDITY_FEES', /** * Margin - The leverage account for parties, contains funds set aside for the margin needed to support * a party's open positions. Each party will have a margin account for each market they have traded in. diff --git a/libs/types/src/global-types-mappings.ts b/libs/types/src/global-types-mappings.ts index 1c3187549..78c033d34 100644 --- a/libs/types/src/global-types-mappings.ts +++ b/libs/types/src/global-types-mappings.ts @@ -45,6 +45,7 @@ export const AccountTypeMapping: { ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: 'Reward Maker paid fees', ACCOUNT_TYPE_SETTLEMENT: 'Settlement', ACCOUNT_TYPE_HOLDING: 'Holding', + ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees', }; /** diff --git a/libs/ui-toolkit/src/components/form-group/form-group.tsx b/libs/ui-toolkit/src/components/form-group/form-group.tsx index 91f9d6389..b8864fe01 100644 --- a/libs/ui-toolkit/src/components/form-group/form-group.tsx +++ b/libs/ui-toolkit/src/components/form-group/form-group.tsx @@ -4,7 +4,7 @@ import type { ReactNode } from 'react'; export interface FormGroupProps { children: ReactNode; className?: string; - label: string; // For accessibility reasons this must always be set for screen readers. If you want it to not show, then use the hideLabel prop" + label: string | ReactNode; // For accessibility reasons this must always be set for screen readers. If you want it to not show, then use the hideLabel prop" labelFor: string; // Same as above hideLabel?: boolean; labelDescription?: string; diff --git a/libs/utils/src/lib/format/range.ts b/libs/utils/src/lib/format/range.ts index d5f466aef..f0e033081 100644 --- a/libs/utils/src/lib/format/range.ts +++ b/libs/utils/src/lib/format/range.ts @@ -7,7 +7,7 @@ import { export const formatValue = ( value: string | number | null | undefined, decimalPlaces: number, - quantum?: string, + quantum?: string | number, formatDecimals?: number, emptyValue = '-' ): string => { diff --git a/libs/wallet/src/__generated__/TransactionResult.ts b/libs/wallet/src/__generated__/TransactionResult.ts index 32187ad9c..97d4b9449 100644 --- a/libs/wallet/src/__generated__/TransactionResult.ts +++ b/libs/wallet/src/__generated__/TransactionResult.ts @@ -28,7 +28,7 @@ export type OrderTxUpdateSubscriptionVariables = Types.Exact<{ }>; -export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string }> | null }; +export type OrderTxUpdateSubscription = { __typename?: 'Subscription', orders?: Array<{ __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string }> | null }; export type DepositBusEventFieldsFragment = { __typename?: 'Deposit', id: string, status: Types.DepositStatus, amount: string, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number } }; @@ -88,7 +88,6 @@ export const OrderTxUpdateFieldsFragmentDoc = gql` expiresAt side marketId - remaining } `; export const DepositBusEventFieldsFragmentDoc = gql` @@ -238,4 +237,4 @@ export function useDepositBusEventSubscription(baseOptions: Apollo.SubscriptionH return Apollo.useSubscription(DepositBusEventDocument, options); } export type DepositBusEventSubscriptionHookResult = ReturnType; -export type DepositBusEventSubscriptionResult = Apollo.SubscriptionResult; +export type DepositBusEventSubscriptionResult = Apollo.SubscriptionResult; \ No newline at end of file diff --git a/libs/wallet/src/connectors/vega-connector.ts b/libs/wallet/src/connectors/vega-connector.ts index 87e34babb..2d8978cd8 100644 --- a/libs/wallet/src/connectors/vega-connector.ts +++ b/libs/wallet/src/connectors/vega-connector.ts @@ -47,6 +47,10 @@ export interface OrderSubmission { expiresAt?: string; postOnly?: boolean; reduceOnly?: boolean; + icebergOpts?: { + peakSize: string; + minimumVisibleSize: string; + }; } export interface OrderCancellation { diff --git a/libs/wallet/src/utils.ts b/libs/wallet/src/utils.ts index 292a211c1..034741606 100644 --- a/libs/wallet/src/utils.ts +++ b/libs/wallet/src/utils.ts @@ -50,6 +50,13 @@ export const normalizeOrderSubmission = ( : undefined, postOnly: order.postOnly, reduceOnly: order.reduceOnly, + icebergOpts: order.icebergOpts && { + peakSize: removeDecimal(order.icebergOpts.peakSize, positionDecimalPlaces), + minimumVisibleSize: removeDecimal( + order.icebergOpts.minimumVisibleSize, + positionDecimalPlaces + ), + }, }); export const normalizeOrderAmendment = >(