diff --git a/packages/backend/src/Application.ts b/packages/backend/src/Application.ts index 105612d16..30be6776b 100644 --- a/packages/backend/src/Application.ts +++ b/packages/backend/src/Application.ts @@ -670,7 +670,8 @@ export class Application { stateUpdater, stateUpdateRepository, config.starkex.contracts.perpetual, - config.starkex.contracts.escapeVerifier + config.starkex.contracts.escapeVerifier, + userTransactionRepository ) const transactionValidator = new TransactionValidator(ethereumClient) diff --git a/packages/backend/src/api/controllers/EscapeHatchController.ts b/packages/backend/src/api/controllers/EscapeHatchController.ts index eb2488e6f..b7e72b23e 100644 --- a/packages/backend/src/api/controllers/EscapeHatchController.ts +++ b/packages/backend/src/api/controllers/EscapeHatchController.ts @@ -10,14 +10,19 @@ import { } from '@explorer/shared' import { MerkleProof, PositionLeaf } from '@explorer/state' import { EthereumAddress } from '@explorer/types' +import isEmpty from 'lodash/isEmpty' import { FreezeCheckService } from '../../core/FreezeCheckService' import { PageContextService } from '../../core/PageContextService' import { StateUpdater } from '../../core/StateUpdater' import { StateUpdateRepository } from '../../peripherals/database/StateUpdateRepository' -import { UserTransactionRecord } from '../../peripherals/database/transactions/UserTransactionRepository' +import { + UserTransactionRecord, + UserTransactionRepository, +} from '../../peripherals/database/transactions/UserTransactionRepository' import { calculatePositionValue } from '../../utils/calculatePositionValue' import { ControllerResult } from './ControllerResult' +import { getPerpetualEscapables } from './getEscapableAssets' import { serializeMerkleProofForEscape } from './serializeMerkleProofForEscape' export class EscapeHatchController { @@ -27,7 +32,8 @@ export class EscapeHatchController { private readonly stateUpdater: StateUpdater, private readonly stateUpdateRepository: StateUpdateRepository, private readonly starkExAddress: EthereumAddress, - private readonly escapeVerifierAddress: EthereumAddress + private readonly escapeVerifierAddress: EthereumAddress, + private readonly userTransactionRepository: UserTransactionRepository ) {} async getFreezeRequestActionPage( @@ -159,6 +165,22 @@ export class EscapeHatchController { const serializedState = encodeStateAsInt256Array( latestStateUpdate.perpetualState ) + + if (context.tradingMode === 'perpetual') { + const escapableMap = await getPerpetualEscapables( + this.userTransactionRepository, + merkleProof.starkKey, + context.collateralAsset + ) + // If escape process has started on perpetuals, don't allow to start it again + if (!isEmpty(escapableMap)) { + return { + type: 'not found', + message: 'Escape process has already started', + } + } + } + const positionValues = context.tradingMode === 'perpetual' ? calculatePositionValue( @@ -166,6 +188,7 @@ export class EscapeHatchController { latestStateUpdate.perpetualState ) : undefined + let content: string switch (context.tradingMode) { case 'perpetual': diff --git a/packages/frontend/src/view/pages/forced-actions/EscapeHatchActionPage.tsx b/packages/frontend/src/view/pages/forced-actions/EscapeHatchActionPage.tsx index f05c539d2..19ac127e1 100644 --- a/packages/frontend/src/view/pages/forced-actions/EscapeHatchActionPage.tsx +++ b/packages/frontend/src/view/pages/forced-actions/EscapeHatchActionPage.tsx @@ -10,6 +10,7 @@ import { z } from 'zod' import { assetToInfo } from '../../../utils/assets' import { formatWithDecimals } from '../../../utils/formatting/formatAmount' +import { InfoIcon } from '../../assets/icons/InfoIcon' import { AssetWithLogo } from '../../components/AssetWithLogo' import { Button } from '../../components/Button' import { Card } from '../../components/Card' @@ -18,6 +19,7 @@ import { OrderedList } from '../../components/OrderedList' import { ContentWrapper } from '../../components/page/ContentWrapper' import { Page } from '../../components/page/Page' import { TermsOfServiceAck } from '../../components/TermsOfServiceAck' +import { TooltipWrapper } from '../../components/Tooltip' import { reactToHtml } from '../../reactToHtml' import { PerformUserActionsPanel } from '../user/components/PerformUserActionsPanel' import { ForcedActionCard } from './components/ForcedActionCard' @@ -134,8 +136,11 @@ function EscapeHatchActionPage(props: Props) {
- + Estimated value + + + {formatWithDecimals(props.positionValue, 2)}