diff --git a/libs/accounts/src/lib/Margins.graphql b/libs/accounts/src/lib/Margins.graphql index 9580c96af..c0a04e1c8 100644 --- a/libs/accounts/src/lib/Margins.graphql +++ b/libs/accounts/src/lib/Margins.graphql @@ -3,6 +3,9 @@ fragment MarginFields on MarginLevels { searchLevel initialLevel collateralReleaseLevel + marginFactor + marginMode + orderMarginLevel asset { id } @@ -33,6 +36,9 @@ subscription MarginsSubscription($partyId: ID!) { searchLevel initialLevel collateralReleaseLevel + marginFactor + marginMode + orderMarginLevel timestamp } } diff --git a/libs/accounts/src/lib/__generated__/Margins.ts b/libs/accounts/src/lib/__generated__/Margins.ts index daf477bc6..fd7f08b83 100644 --- a/libs/accounts/src/lib/__generated__/Margins.ts +++ b/libs/accounts/src/lib/__generated__/Margins.ts @@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } }; +export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } }; export type MarginsQueryVariables = Types.Exact<{ partyId: Types.Scalars['ID']; }>; -export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null }; +export type MarginsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, marginsConnection?: { __typename?: 'MarginConnection', edges?: Array<{ __typename?: 'MarginEdge', node: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } } }> | null } | null } | null }; export type MarginsSubscriptionSubscriptionVariables = Types.Exact<{ partyId: Types.Scalars['ID']; }>; -export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, timestamp: any } }; +export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, marginFactor: string, marginMode: Types.MarginMode, orderMarginLevel: string, timestamp: any } }; export const MarginFieldsFragmentDoc = gql` fragment MarginFields on MarginLevels { @@ -25,6 +25,9 @@ export const MarginFieldsFragmentDoc = gql` searchLevel initialLevel collateralReleaseLevel + marginFactor + marginMode + orderMarginLevel asset { id } @@ -85,6 +88,9 @@ export const MarginsSubscriptionDocument = gql` searchLevel initialLevel collateralReleaseLevel + marginFactor + marginMode + orderMarginLevel timestamp } } diff --git a/libs/accounts/src/lib/margin-data-provider.ts b/libs/accounts/src/lib/margin-data-provider.ts index 54c0c1572..17990cbe1 100644 --- a/libs/accounts/src/lib/margin-data-provider.ts +++ b/libs/accounts/src/lib/margin-data-provider.ts @@ -40,6 +40,9 @@ const update = ( searchLevel: delta.searchLevel, initialLevel: delta.initialLevel, collateralReleaseLevel: delta.collateralReleaseLevel, + marginFactor: delta.marginFactor, + marginMode: delta.marginMode, + orderMarginLevel: delta.orderMarginLevel, asset: { __typename: 'Asset', id: delta.asset, diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-container.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-container.tsx index cc9705166..f4bbf0e49 100644 --- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-container.tsx +++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-container.tsx @@ -13,6 +13,7 @@ import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit'; import { DealTicket } from './deal-ticket'; import { useFeatureFlags } from '@vegaprotocol/environment'; import { useT } from '../../use-t'; +import { MarginModeSelector } from './margin-mode-selector'; interface DealTicketContainerProps { marketId: string; @@ -51,21 +52,26 @@ export const DealTicketContainer = ({ reload={reload} > {market && marketData ? ( - featureFlags.STOP_ORDERS && showStopOrder ? ( - create({ stopOrdersSubmission })} - /> - ) : ( - create({ orderSubmission })} - /> - ) + <> + + {featureFlags.STOP_ORDERS && showStopOrder ? ( + + create({ stopOrdersSubmission }) + } + /> + ) : ( + create({ orderSubmission })} + /> + )} + ) : (

{t('Could not load market')}

)} 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 new file mode 100644 index 000000000..0cf622aeb --- /dev/null +++ b/libs/deal-ticket/src/components/deal-ticket/margin-mode-selector.tsx @@ -0,0 +1,66 @@ +import { useDataProvider } from '@vegaprotocol/data-provider'; +import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit'; +import { marginModeDataProvider } from '@vegaprotocol/positions'; +import { MarginMode, useVegaWallet } from '@vegaprotocol/wallet'; +import * as Types from '@vegaprotocol/types'; +import { useVegaTransactionStore } from '@vegaprotocol/web3'; + +export const MarginModeSelector = ({ marketId }: { marketId: string }) => { + const { pubKey, isReadOnly } = useVegaWallet(); + const { data: marginMode } = useDataProvider({ + dataProvider: marginModeDataProvider, + variables: { + partyId: pubKey || '', + marketId, + }, + skip: !pubKey, + }); + const create = useVegaTransactionStore((state) => state.create); + const disabled = isReadOnly; + + return ( +
+ + create({ + updateMarginMode: { + market_id: marketId, + mode: MarginMode.MARGIN_MODE_CROSS_MARGIN, + }, + }) + } + intent={ + !marginMode || + marginMode.marginMode === Types.MarginMode.MARGIN_MODE_CROSS_MARGIN + ? Intent.Primary + : Intent.None + } + > + Cross + + + create({ + updateMarginMode: { + market_id: marketId, + mode: MarginMode.MARGIN_MODE_ISOLATED_MARGIN, + marginFactor: '0.1', + }, + }) + } + intent={ + marginMode?.marginMode === + Types.MarginMode.MARGIN_MODE_ISOLATED_MARGIN + ? Intent.Primary + : Intent.None + } + > + Isolated {marginMode?.margin_factor || '10'}x + +
+ ); +}; diff --git a/libs/positions/src/index.ts b/libs/positions/src/index.ts index 98f451cd5..489da5247 100644 --- a/libs/positions/src/index.ts +++ b/libs/positions/src/index.ts @@ -1,4 +1,5 @@ export * from './lib/__generated__/Positions'; +export * from './lib/margin-modes-provider'; export * from './lib/positions-data-providers'; export * from './lib/positions-table'; export * from './lib/positions-manager'; diff --git a/libs/positions/src/lib/Positions.graphql b/libs/positions/src/lib/Positions.graphql index 22cf50a68..a92f4cfc3 100644 --- a/libs/positions/src/lib/Positions.graphql +++ b/libs/positions/src/lib/Positions.graphql @@ -83,3 +83,23 @@ query EstimatePosition( } } } + +fragment MarginMode on PartyMarginMode { + marketId + partyId + marginMode + margin_factor + min_theoretical_margin_factor + max_theoretical_leverage + atEpoch +} + +query MarginModes($partyId: ID!) { + partyMarginModes(partyId: $partyId) { + edges { + node { + ...MarginMode + } + } + } +} diff --git a/libs/positions/src/lib/__generated__/Positions.ts b/libs/positions/src/lib/__generated__/Positions.ts index e43a9d2e1..ecbe031f2 100644 --- a/libs/positions/src/lib/__generated__/Positions.ts +++ b/libs/positions/src/lib/__generated__/Positions.ts @@ -29,6 +29,15 @@ export type EstimatePositionQueryVariables = Types.Exact<{ export type EstimatePositionQuery = { __typename?: 'Query', estimatePosition?: { __typename?: 'PositionEstimate', margin: { __typename?: 'MarginEstimate', worstCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string }, bestCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string } }, liquidation?: { __typename?: 'LiquidationEstimate', worstCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string }, bestCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string } } | null } | null }; +export type MarginModeFragment = { __typename?: 'PartyMarginMode', marketId: string, partyId: string, marginMode: Types.MarginMode, margin_factor?: string | null, min_theoretical_margin_factor?: string | null, max_theoretical_leverage?: string | null, atEpoch: number }; + +export type MarginModesQueryVariables = Types.Exact<{ + partyId: Types.Scalars['ID']; +}>; + + +export type MarginModesQuery = { __typename?: 'Query', partyMarginModes?: { __typename?: 'PartyMarginModesConnection', edges?: Array<{ __typename?: 'PartyMarginModeEdge', node: { __typename?: 'PartyMarginMode', marketId: string, partyId: string, marginMode: Types.MarginMode, margin_factor?: string | null, min_theoretical_margin_factor?: string | null, max_theoretical_leverage?: string | null, atEpoch: number } } | null> | null } | null }; + export const PositionFieldsFragmentDoc = gql` fragment PositionFields on Position { realisedPNL @@ -46,6 +55,17 @@ export const PositionFieldsFragmentDoc = gql` } } `; +export const MarginModeFragmentDoc = gql` + fragment MarginMode on PartyMarginMode { + marketId + partyId + marginMode + margin_factor + min_theoretical_margin_factor + max_theoretical_leverage + atEpoch +} + `; export const PositionsDocument = gql` query Positions($partyIds: [ID!]!) { positions(filter: {partyIds: $partyIds}) { @@ -191,4 +211,43 @@ export function useEstimatePositionLazyQuery(baseOptions?: Apollo.LazyQueryHookO } export type EstimatePositionQueryHookResult = ReturnType; export type EstimatePositionLazyQueryHookResult = ReturnType; -export type EstimatePositionQueryResult = Apollo.QueryResult; \ No newline at end of file +export type EstimatePositionQueryResult = Apollo.QueryResult; +export const MarginModesDocument = gql` + query MarginModes($partyId: ID!) { + partyMarginModes(partyId: $partyId) { + edges { + node { + ...MarginMode + } + } + } +} + ${MarginModeFragmentDoc}`; + +/** + * __useMarginModesQuery__ + * + * To run a query within a React component, call `useMarginModesQuery` and pass it any options that fit your needs. + * When your component renders, `useMarginModesQuery` 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 query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMarginModesQuery({ + * variables: { + * partyId: // value for 'partyId' + * }, + * }); + */ +export function useMarginModesQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(MarginModesDocument, options); + } +export function useMarginModesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(MarginModesDocument, options); + } +export type MarginModesQueryHookResult = ReturnType; +export type MarginModesLazyQueryHookResult = ReturnType; +export type MarginModesQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/positions/src/lib/margin-modes-provider.ts b/libs/positions/src/lib/margin-modes-provider.ts new file mode 100644 index 000000000..4bb50d638 --- /dev/null +++ b/libs/positions/src/lib/margin-modes-provider.ts @@ -0,0 +1,38 @@ +import { removePaginationWrapper } from '@vegaprotocol/utils'; +import { + makeDataProvider, + makeDerivedDataProvider, +} from '@vegaprotocol/data-provider'; +import { + MarginModesDocument, + type MarginModesQueryVariables, + MarginModesQuery, + MarginModeFragment, +} from './__generated__/Positions'; + +export const marginModesDataProvider = makeDataProvider< + MarginModesQuery, + MarginModeFragment[], + never, + never, + MarginModesQueryVariables +>({ + query: MarginModesDocument, + getData: (responseData: MarginModesQuery | null) => + removePaginationWrapper(responseData?.partyMarginModes?.edges) || [], +}); + +export const marginModeDataProvider = makeDerivedDataProvider< + MarginModeFragment | undefined, + never, + MarginModesQueryVariables & { marketId: string } +>( + [ + (callback, client, variables) => + marginModesDataProvider(callback, client, { partyId: variables.partyId }), + ], + (data, variables) => + (data as MarginModeFragment[]).find( + (marginMode) => marginMode.marketId === variables.marketId + ) +); diff --git a/libs/types/src/__generated__/types.ts b/libs/types/src/__generated__/types.ts index 9c90d8c21..1b5ea1c29 100644 --- a/libs/types/src/__generated__/types.ts +++ b/libs/types/src/__generated__/types.ts @@ -1986,8 +1986,14 @@ export type MarginLevels = { initialLevel: Scalars['String']; /** Minimal margin for the position to be maintained in the network (unsigned integer) */ maintenanceLevel: Scalars['String']; + /** Margin factor, only relevant for isolated margin mode, else 0 */ + marginFactor: Scalars['String']; + /** Margin mode of the party, cross margin or isolated margin */ + marginMode: MarginMode; /** Market in which the margin is required for this party */ market: Market; + /** When in isolated margin, the required order margin level, otherwise, 0 */ + orderMarginLevel: Scalars['String']; /** The party for this margin */ party: Party; /** If the margin is between maintenance and search, the network will initiate a collateral search, expressed as unsigned integer */ @@ -2010,8 +2016,14 @@ export type MarginLevelsUpdate = { initialLevel: Scalars['String']; /** Minimal margin for the position to be maintained in the network (unsigned integer) */ maintenanceLevel: Scalars['String']; + /** Margin factor, only relevant for isolated margin mode, else 0 */ + marginFactor: Scalars['String']; + /** Margin mode of the party, cross margin or isolated margin */ + marginMode: MarginMode; /** Market in which the margin is required for this party */ marketId: Scalars['ID']; + /** When in isolated margin, the required order margin level, otherwise, 0 */ + orderMarginLevel: Scalars['String']; /** The party for this margin */ partyId: Scalars['ID']; /** If the margin is between maintenance and search, the network will initiate a collateral search (unsigned integer) */ @@ -2020,6 +2032,13 @@ export type MarginLevelsUpdate = { timestamp: Scalars['Timestamp']; }; +export enum MarginMode { + /** Party is in cross margin mode */ + MARGIN_MODE_CROSS_MARGIN = 'MARGIN_MODE_CROSS_MARGIN', + /** Party is in isolated margin mode */ + MARGIN_MODE_ISOLATED_MARGIN = 'MARGIN_MODE_ISOLATED_MARGIN' +} + /** Represents a product & associated parameters that can be traded on Vega, has an associated OrderBook and Trade history */ export type Market = { __typename?: 'Market'; @@ -3118,6 +3137,8 @@ export enum OrderRejectionReason { ORDER_ERROR_INVALID_TIME_IN_FORCE = 'ORDER_ERROR_INVALID_TIME_IN_FORCE', /** Invalid type */ ORDER_ERROR_INVALID_TYPE = 'ORDER_ERROR_INVALID_TYPE', + /** Party has insufficient funds to cover for the order margin for the new or amended order */ + ORDER_ERROR_ISOLATED_MARGIN_CHECK_FAILED = 'ORDER_ERROR_ISOLATED_MARGIN_CHECK_FAILED', /** Margin check failed - not enough available margin */ ORDER_ERROR_MARGIN_CHECK_FAILED = 'ORDER_ERROR_MARGIN_CHECK_FAILED', /** Market is closed */ @@ -3138,6 +3159,8 @@ export enum OrderRejectionReason { ORDER_ERROR_OFFSET_MUST_BE_GREATER_THAN_ZERO = 'ORDER_ERROR_OFFSET_MUST_BE_GREATER_THAN_ZERO', /** Order is out of sequence */ ORDER_ERROR_OUT_OF_SEQUENCE = 'ORDER_ERROR_OUT_OF_SEQUENCE', + /** Pegged orders are not allowed for a party in isolated margin mode */ + ORDER_ERROR_PEGGED_ORDERS_NOT_ALLOWED_IN_ISOLATED_MARGIN_MODE = 'ORDER_ERROR_PEGGED_ORDERS_NOT_ALLOWED_IN_ISOLATED_MARGIN_MODE', /** A post-only order would produce an aggressive trade and thus it has been rejected */ ORDER_ERROR_POST_ONLY_ORDER_WOULD_TRADE = 'ORDER_ERROR_POST_ONLY_ORDER_WOULD_TRADE', /** A reduce-ony order would not reduce the party's position and thus it has been rejected */ @@ -3586,6 +3609,41 @@ export type PartyLockedBalance = { untilEpoch: Scalars['Int']; }; +/** Margin mode selected for the given party and market. */ +export type PartyMarginMode = { + __typename?: 'PartyMarginMode'; + /** Epoch at which the update happened. */ + atEpoch: Scalars['Int']; + /** Selected margin mode. */ + marginMode: MarginMode; + /** Margin factor for the market. Isolated mode only. */ + margin_factor?: Maybe; + /** Unique ID of the market. */ + marketId: Scalars['ID']; + /** Maximum theoretical leverage for the market. Isolated mode only. */ + max_theoretical_leverage?: Maybe; + /** Minimum theoretical margin factor for the market. Isolated mode only. */ + min_theoretical_margin_factor?: Maybe; + /** Unique ID of the party. */ + partyId: Scalars['ID']; +}; + +/** Edge type containing the deposit and cursor information returned by a PartyMarginModeConnection */ +export type PartyMarginModeEdge = { + __typename?: 'PartyMarginModeEdge'; + cursor: Scalars['String']; + node: PartyMarginMode; +}; + +/** Connection type for retrieving cursor-based paginated party margin modes information */ +export type PartyMarginModesConnection = { + __typename?: 'PartyMarginModesConnection'; + /** The party margin modes */ + edges?: Maybe>>; + /** The pagination information */ + pageInfo?: Maybe; +}; + /** * All staking information related to a Party. * Contains the current recognised balance by the network and @@ -4438,6 +4496,12 @@ export type Query = { partiesConnection?: Maybe; /** An entity that is trading on the Vega network */ party?: Maybe; + /** + * List margin modes per party per market + * + * Get a list of all margin modes, or for a specific market ID, or party ID. + */ + partyMarginModes?: Maybe; /** Fetch all positions */ positions?: Maybe; /** A governance proposal located by either its ID or reference. If both are set, ID is used. */ @@ -6211,6 +6275,8 @@ export enum TransferType { TRANSFER_TYPE_INFRASTRUCTURE_FEE_DISTRIBUTE = 'TRANSFER_TYPE_INFRASTRUCTURE_FEE_DISTRIBUTE', /** Infrastructure fee paid from general account */ TRANSFER_TYPE_INFRASTRUCTURE_FEE_PAY = 'TRANSFER_TYPE_INFRASTRUCTURE_FEE_PAY', + /** Funds moved from order margin account to margin account. */ + TRANSFER_TYPE_ISOLATED_MARGIN_LOW = 'TRANSFER_TYPE_ISOLATED_MARGIN_LOW', /** Allocates liquidity fee earnings to each liquidity provider's network controlled liquidity fee account. */ TRANSFER_TYPE_LIQUIDITY_FEE_ALLOCATE = 'TRANSFER_TYPE_LIQUIDITY_FEE_ALLOCATE', /** Liquidity fee received into general account */ @@ -6237,6 +6303,10 @@ export enum TransferType { TRANSFER_TYPE_MTM_LOSS = 'TRANSFER_TYPE_MTM_LOSS', /** Funds added to margin account after mark to market gain */ TRANSFER_TYPE_MTM_WIN = 'TRANSFER_TYPE_MTM_WIN', + /** Funds released from order margin account to general. */ + TRANSFER_TYPE_ORDER_MARGIN_HIGH = 'TRANSFER_TYPE_ORDER_MARGIN_HIGH', + /** Funds moved from general account to order margin account. */ + TRANSFER_TYPE_ORDER_MARGIN_LOW = 'TRANSFER_TYPE_ORDER_MARGIN_LOW', /** Funds deducted from margin account after a perpetuals funding loss. */ TRANSFER_TYPE_PERPETUALS_FUNDING_LOSS = 'TRANSFER_TYPE_PERPETUALS_FUNDING_LOSS', /** Funds added to margin account after a perpetuals funding gain. */ diff --git a/libs/wallet/src/connectors/vega-connector.ts b/libs/wallet/src/connectors/vega-connector.ts index f3a42d498..6fec916e4 100644 --- a/libs/wallet/src/connectors/vega-connector.ts +++ b/libs/wallet/src/connectors/vega-connector.ts @@ -448,7 +448,24 @@ export type CreateReferralSet = { }; }; +export enum MarginMode { + /** Party is in cross margin mode */ + MARGIN_MODE_CROSS_MARGIN = 1, + /** Party is in isolated margin mode */ + MARGIN_MODE_ISOLATED_MARGIN = 'MARGIN_MODE_ISOLATED_MARGIN', +} +export interface UpdateMarginMode { + market_id: string; + mode: MarginMode; + marginFactor?: string; +} + +export interface UpdateMarginModeBody { + updateMarginMode: UpdateMarginMode; +} + export type Transaction = + | UpdateMarginModeBody | StopOrdersSubmissionBody | StopOrdersCancellationBody | OrderSubmissionBody @@ -465,6 +482,10 @@ export type Transaction = | ApplyReferralCode | CreateReferralSet; +export const isMarginModeUpdateTransaction = ( + transaction: Transaction +): transaction is UpdateMarginModeBody => 'updateMarginMode' in transaction; + export const isWithdrawTransaction = ( transaction: Transaction ): transaction is WithdrawSubmissionBody => 'withdrawSubmission' in transaction; diff --git a/libs/web3/src/lib/use-vega-transaction-store.tsx b/libs/web3/src/lib/use-vega-transaction-store.tsx index b39ac17ab..bac4e9160 100644 --- a/libs/web3/src/lib/use-vega-transaction-store.tsx +++ b/libs/web3/src/lib/use-vega-transaction-store.tsx @@ -10,6 +10,7 @@ import { isStopOrdersSubmissionTransaction, isStopOrdersCancellationTransaction, determineId, + isMarginModeUpdateTransaction, } from '@vegaprotocol/wallet'; import { create } from 'zustand'; @@ -58,7 +59,7 @@ export interface VegaTransactionStore { export const useVegaTransactionStore = create()( subscribeWithSelector((set, get) => ({ - transactions: [] as VegaStoredTxState[], + transactions: [] as (VegaStoredTxState | undefined)[], create: (body: Transaction, order?: OrderTxUpdateFieldsFragment) => { const transactions = get().transactions; const now = new Date(); @@ -205,16 +206,23 @@ export const useVegaTransactionStore = create()( isStopOrdersCancellationTransaction(transaction.body); const isConfirmedStopOrderSubmission = isStopOrdersSubmissionTransaction(transaction.body); + const isConfirmedMarginModeTransaction = + isMarginModeUpdateTransaction(transaction.body); if ( - (isConfirmedOrderCancellation || - isConfirmedTransfer || - isConfirmedStopOrderCancellation || - isConfirmedStopOrderSubmission) && - !transactionResult.error && - transactionResult.status + isConfirmedOrderCancellation || + isConfirmedTransfer || + isConfirmedStopOrderCancellation || + isConfirmedStopOrderSubmission || + isConfirmedMarginModeTransaction + //transactionResult.status ) { - transaction.status = VegaTxStatus.Complete; + if (transactionResult.error) { + transaction.status = VegaTxStatus.Error; + transaction.error = new Error(transactionResult.error); + } else { + transaction.status = VegaTxStatus.Complete; + } } transaction.dialogOpen = true; transaction.updatedAt = new Date(); diff --git a/libs/web3/src/lib/use-vega-transaction-toasts.tsx b/libs/web3/src/lib/use-vega-transaction-toasts.tsx index ef73de2e4..a57604162 100644 --- a/libs/web3/src/lib/use-vega-transaction-toasts.tsx +++ b/libs/web3/src/lib/use-vega-transaction-toasts.tsx @@ -7,6 +7,7 @@ import type { OrderSubmission, StopOrdersSubmission, StopOrderSetup, + UpdateMarginMode, } from '@vegaprotocol/wallet'; import type { OrderTxUpdateFieldsFragment, @@ -26,6 +27,8 @@ import { isStopOrdersSubmissionTransaction, isStopOrdersCancellationTransaction, isReferralRelatedTransaction, + isMarginModeUpdateTransaction, + MarginMode, } from '@vegaprotocol/wallet'; import { useVegaTransactionStore } from './use-vega-transaction-store'; import { VegaTxStatus } from './types'; @@ -163,6 +166,7 @@ const isClosePositionTransaction = (tx: VegaStoredTxState) => { }; const isTransactionTypeSupported = (tx: VegaStoredTxState) => { + const marginModeUpdate = isMarginModeUpdateTransaction(tx.body); const withdraw = isWithdrawTransaction(tx.body); const submitOrder = isOrderSubmissionTransaction(tx.body); const cancelOrder = isOrderCancellationTransaction(tx.body); @@ -173,6 +177,7 @@ const isTransactionTypeSupported = (tx: VegaStoredTxState) => { const transfer = isTransferTransaction(tx.body); const referral = isReferralRelatedTransaction(tx.body); return ( + marginModeUpdate || withdraw || submitOrder || cancelOrder || @@ -445,6 +450,27 @@ const CancelOrderDetails = ({ ); }; +const MarginModeDetails = ({ data }: { data: UpdateMarginMode }) => { + const t = useT(); + const { data: markets } = useMarketsMapProvider(); + const marketId = data.market_id; + const market = marketId && markets?.[marketId]; + if (!market) { + return null; + } + return ( + +

{t('Update margin mode')}

+

{market?.tradableInstrument.instrument.code}

+ {data.mode === MarginMode.MARGIN_MODE_CROSS_MARGIN + ? t('Cross margin mode') + : t('Isolated margin mode {{leverage}}x', { + leverage: 1 / Number(data.marginFactor), + })} +
+ ); +}; + const CancelStopOrderDetails = ({ stopOrderId }: { stopOrderId: string }) => { const t = useT(); const formatTrigger = useFormatTrigger(); @@ -598,6 +624,10 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => { ); } + if (isMarginModeUpdateTransaction(tx.body)) { + return ; + } + if (isClosePositionTransaction(tx)) { const transaction = tx.body as BatchMarketInstructionSubmissionBody; const marketId = first(