diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx
index b1fa2c6d5..489dce12d 100644
--- a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx
+++ b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx
@@ -1,5 +1,6 @@
import React, { useCallback, useEffect } from "react"
import {
+ useActionFlowPause,
useActionFlowTokenAmount,
useAppDispatch,
useDepositBTCTransaction,
@@ -7,8 +8,8 @@ import {
useStakeFlowContext,
useVerifyDepositAddress,
} from "#/hooks"
-import { logPromiseFailure } from "#/utils"
-import { PROCESS_STATUSES } from "#/types"
+import { isLedgerLiveError, logPromiseFailure } from "#/utils"
+import { PROCESS_STATUSES, LedgerLiveError } from "#/types"
import { Highlight } from "@chakra-ui/react"
import { TextMd } from "#/components/shared/Typography"
import { CardAlert } from "#/components/shared/alerts"
@@ -20,6 +21,7 @@ export default function DepositBTCModal() {
const { btcAddress, depositReceipt, stake } = useStakeFlowContext()
const verifyDepositAddress = useVerifyDepositAddress()
const dispatch = useAppDispatch()
+ const { handlePause } = useActionFlowPause()
const onStakeBTCSuccess = useCallback(() => {
dispatch(setStatus(PROCESS_STATUSES.SUCCEEDED))
@@ -42,9 +44,22 @@ export default function DepositBTCModal() {
}, [dispatch, handleStake])
// TODO: Handle when the function fails
- const showError = useCallback(() => {}, [])
+ const showError = useCallback((error?: LedgerLiveError) => {
+ console.error(error)
+ }, [])
- const onDepositBTCError = useCallback(() => showError(), [showError])
+ const onDepositBTCError = useCallback(
+ (error: unknown) => {
+ if (!isLedgerLiveError(error)) return
+
+ const isInterrupted =
+ error.message && error.message.includes("Signature interrupted by user")
+ if (isInterrupted) handlePause()
+
+ showError(error)
+ },
+ [showError, handlePause],
+ )
const { sendBitcoinTransaction, transactionHash } = useDepositBTCTransaction(
onDepositBTCSuccess,
diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx
index fa76d2b03..7d42dd72e 100644
--- a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx
+++ b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx
@@ -14,6 +14,7 @@ import ErrorModal from "./ErrorModal"
import LoadingModal from "./LoadingModal"
import MissingAccountModal from "./MissingAccountModal"
import SuccessModal from "./SuccessModal"
+import ResumeModal from "./ResumeModal"
export default function ModalContentWrapper({
children,
@@ -43,5 +44,7 @@ export default function ModalContentWrapper({
if (status === PROCESS_STATUSES.FAILED) return
+ if (status === PROCESS_STATUSES.PAUSED) return
+
return children
}
diff --git a/dapp/src/components/TransactionModal/ResumeModal.tsx b/dapp/src/components/TransactionModal/ResumeModal.tsx
new file mode 100644
index 000000000..3d63b6cb9
--- /dev/null
+++ b/dapp/src/components/TransactionModal/ResumeModal.tsx
@@ -0,0 +1,42 @@
+import React from "react"
+import {
+ ModalHeader,
+ ModalBody,
+ ModalFooter,
+ Button,
+ HStack,
+} from "@chakra-ui/react"
+
+import Spinner from "#/components/shared/Spinner"
+import { PauseIcon } from "#/assets/icons"
+import { TextMd } from "#/components/shared/Typography"
+import { useActionFlowPause, useModal } from "#/hooks"
+
+export default function ResumeModal() {
+ const { handleResume } = useActionFlowPause()
+ const { closeModal } = useModal()
+
+ return (
+ <>
+
+ Paused
+
+
+
+
+
+
+
+ Are your sure you want to cancel?
+
+
+
+
+
+ >
+ )
+}
diff --git a/dapp/src/hooks/store/index.ts b/dapp/src/hooks/store/index.ts
index 0bc778125..ce3b7762a 100644
--- a/dapp/src/hooks/store/index.ts
+++ b/dapp/src/hooks/store/index.ts
@@ -11,3 +11,4 @@ export * from "./useActionFlowTxHash"
export * from "./useCompletedActivities"
export * from "./useLatestActivities"
export * from "./useAllActivitiesCount"
+export * from "./useActionFlowPause"
diff --git a/dapp/src/hooks/store/useActionFlowPause.ts b/dapp/src/hooks/store/useActionFlowPause.ts
new file mode 100644
index 000000000..7bb907d88
--- /dev/null
+++ b/dapp/src/hooks/store/useActionFlowPause.ts
@@ -0,0 +1,29 @@
+import { setStatus } from "#/store/action-flow"
+import { PROCESS_STATUSES } from "#/types"
+import { useAppDispatch } from "./useAppDispatch"
+
+/**
+ * Custom hook that provides functions to pause and resume the action flow process.
+ * @returns An object containing the `handlePause` and `handleResume` functions.
+ */
+export function useActionFlowPause() {
+ const dispatch = useAppDispatch()
+
+ /**
+ * Function to pause the action flow process.
+ * It dispatches an action to set the status to "PAUSED" if the current status is "PENDING".
+ */
+ const handlePause = () => {
+ dispatch(setStatus(PROCESS_STATUSES.PAUSED))
+ }
+
+ /**
+ * Function to resume the action flow process.
+ * It dispatches an action to set the status to "PENDING".
+ */
+ const handleResume = () => {
+ dispatch(setStatus(PROCESS_STATUSES.PENDING))
+ }
+
+ return { handlePause, handleResume }
+}
diff --git a/dapp/src/theme/Spinner.ts b/dapp/src/theme/Spinner.ts
index 678482d13..fd5aa47af 100644
--- a/dapp/src/theme/Spinner.ts
+++ b/dapp/src/theme/Spinner.ts
@@ -13,8 +13,14 @@ const sizeXl = defineStyle({
height: 16,
})
+const size2Xl = defineStyle({
+ width: 20,
+ height: 20,
+})
+
const sizes = {
xl: sizeXl,
+ "2xl": size2Xl,
}
export const spinnerTheme = defineStyleConfig({ baseStyle, sizes })
diff --git a/dapp/src/types/action-flow.ts b/dapp/src/types/action-flow.ts
index 6e870647f..20bcb50c7 100644
--- a/dapp/src/types/action-flow.ts
+++ b/dapp/src/types/action-flow.ts
@@ -22,6 +22,8 @@ export const PROCESS_STATUSES = {
LOADING: "LOADING",
FAILED: "FAILED",
SUCCEEDED: "SUCCEEDED",
+ PAUSED: "PAUSED",
+ PENDING: "PENDING",
} as const
export type ProcessStatus =
diff --git a/dapp/src/types/ledger-live-app.ts b/dapp/src/types/ledger-live-app.ts
index c2e452ca0..c0fbf7bd3 100644
--- a/dapp/src/types/ledger-live-app.ts
+++ b/dapp/src/types/ledger-live-app.ts
@@ -8,3 +8,9 @@ export type UseRequestAccountReturn = {
account: Account | null
requestAccount: (...params: RequestAccountParams) => Promise
}
+
+export type LedgerLiveError = {
+ message?: string
+ name?: string
+ stack?: string
+}
diff --git a/dapp/src/utils/index.ts b/dapp/src/utils/index.ts
index 6df508ec4..89d130930 100644
--- a/dapp/src/utils/index.ts
+++ b/dapp/src/utils/index.ts
@@ -10,3 +10,4 @@ export * from "./exchangeApi"
export * from "./verifyDepositAddress"
export * from "./json"
export * from "./activities"
+export * from "./type-check"
diff --git a/dapp/src/utils/type-check.ts b/dapp/src/utils/type-check.ts
new file mode 100644
index 000000000..a8c5b4202
--- /dev/null
+++ b/dapp/src/utils/type-check.ts
@@ -0,0 +1,15 @@
+import { LedgerLiveError } from "#/types"
+
+export function isObject(
+ arg: unknown,
+): arg is Record {
+ return typeof arg === "object" && arg !== null
+}
+
+export function isString(arg: unknown): arg is string {
+ return typeof arg === "string"
+}
+
+export function isLedgerLiveError(arg: unknown): arg is LedgerLiveError {
+ return isObject(arg) && arg.name === "Error" && isString(arg.message)
+}