Skip to content

Commit

Permalink
Update screens for deposit and withdrawal flows (#821)
Browse files Browse the repository at this point in the history
Ref #811
Ref #277
Closes #814
Closes #813

This PR updates screens for deposit and withdrawal flows. The "Skeleton"
loading screen from both Deposit and Withdraw flows is no longer needed.
The new flow adds two steps:

- "Opening your wallet for signature" Screen
- "Awaiting Transaction" Screen

Flow:
1. User initiates deposit: After pressing the Deposit/Withdraw CTAs, the
user first sees the “Opening your wallet for signature” screen
2. Display wallet modal
3. Transition to ‘Awaiting Transaction’: Once the wallet modal is
prompted, switch to the “Awaiting Transaction” screen while the user
completes the signature.
4. Return to dapp: After signing with the wallet, the user returns to
your dapp, where the “Awaiting Transaction” screen remains visible as
needed.
5. Confirm deposit/withdraw: Once transaction is complete, display the
“Deposit received” or "Withdrawal inititated" screens to confirm
completion.


https://github.com/user-attachments/assets/899263a7-0895-48ba-881e-c61b70e4c32d
  • Loading branch information
r-czajkowski authored Nov 7, 2024
2 parents b3310fa + d20300f commit 36b0fe8
Show file tree
Hide file tree
Showing 15 changed files with 252 additions and 243 deletions.
3 changes: 3 additions & 0 deletions dapp/src/acre-react/hooks/useInitializeWithdraw.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback } from "react"
import {
DataBuiltStepCallback,
MessageSignedStepCallback,
OnSignMessageStepCallback,
} from "@acre-btc/sdk/dist/src/lib/redeemer-proxy"
Expand All @@ -11,13 +12,15 @@ export default function useInitializeWithdraw() {
return useCallback(
async (
amount: bigint,
dataBuiltStepCallback?: DataBuiltStepCallback,
onSignMessageStep?: OnSignMessageStepCallback,
messageSignedStep?: MessageSignedStepCallback,
) => {
if (!acre || !isConnected) throw new Error("Account not connected")

return acre.account.initializeWithdrawal(
amount,
dataBuiltStepCallback,
onSignMessageStep,
messageSignedStep,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React, { useCallback, useEffect } from "react"
import React, { useCallback } from "react"
import {
useActionFlowPause,
useActionFlowTokenAmount,
useAppDispatch,
useDepositBTCTransaction,
useExecuteFunction,
useInvalidateQueries,
useStakeFlowContext,
useVerifyDepositAddress,
} from "#/hooks"
import { eip1193, logPromiseFailure } from "#/utils"
import { PROCESS_STATUSES } from "#/types"
import { Highlight, ModalCloseButton } from "@chakra-ui/react"
import { TextMd } from "#/components/shared/Typography"
import { setStatus, setTxHash } from "#/store/action-flow"
import { queryKeysFactory } from "#/constants"
import { Alert, AlertIcon, AlertDescription } from "#/components/shared/Alert"
import TriggerTransactionModal from "../TriggerTransactionModal"
import { ONE_SEC_IN_MILLISECONDS, queryKeysFactory } from "#/constants"
import { useTimeout } from "@chakra-ui/react"
import { useMutation } from "@tanstack/react-query"
import WalletInteractionModal from "../WalletInteractionModal"

const { userKeys } = queryKeysFactory

Expand All @@ -36,20 +34,27 @@ export default function DepositBTCModal() {
}, [dispatch, handleBitcoinBalanceInvalidation])

const onError = useCallback(
(error?: unknown) => {
(error: unknown) => {
console.error(error)
dispatch(setStatus(PROCESS_STATUSES.FAILED))
},
[dispatch],
)

const handleStake = useExecuteFunction(stake, onStakeBTCSuccess, onError)

const onDepositBTCSuccess = useCallback(() => {
dispatch(setStatus(PROCESS_STATUSES.LOADING))
const { mutate: handleStake } = useMutation({
mutationKey: ["stake"],
mutationFn: stake,
onSuccess: onStakeBTCSuccess,
onError,
})

logPromiseFailure(handleStake())
}, [dispatch, handleStake])
const onDepositBTCSuccess = useCallback(
(transactionHash: string) => {
dispatch(setTxHash(transactionHash))
handleStake()
},
[dispatch, handleStake],
)

const onDepositBTCError = useCallback(
(error: unknown) => {
Expand All @@ -62,55 +67,43 @@ export default function DepositBTCModal() {
[onError, handlePause],
)

const { sendBitcoinTransaction, transactionHash } = useDepositBTCTransaction(
onDepositBTCSuccess,
onDepositBTCError,
)

useEffect(() => {
if (transactionHash) {
dispatch(setTxHash(transactionHash))
}
}, [dispatch, transactionHash])
const { mutate: sendBitcoinTransaction, status } = useDepositBTCTransaction({
onSuccess: onDepositBTCSuccess,
onError: onDepositBTCError,
})

const handledDepositBTC = useCallback(async () => {
if (!tokenAmount?.amount || !btcAddress || !depositReceipt) return
const status = await verifyDepositAddress(depositReceipt, btcAddress)
const verificationStatus = await verifyDepositAddress(
depositReceipt,
btcAddress,
)

if (status === "valid") {
await sendBitcoinTransaction(btcAddress, tokenAmount?.amount)
if (verificationStatus === "valid") {
sendBitcoinTransaction({
recipient: btcAddress,
amount: tokenAmount?.amount,
})
} else {
onError()
onError("Invalid deposit address")
}
}, [
tokenAmount?.amount,
btcAddress,
depositReceipt,
onError,
verifyDepositAddress,
sendBitcoinTransaction,
tokenAmount?.amount,
onError,
])

const handledDepositBTCWrapper = useCallback(() => {
logPromiseFailure(handledDepositBTC())
}, [handledDepositBTC])

return (
<>
<ModalCloseButton />
<TriggerTransactionModal callback={handledDepositBTCWrapper}>
<Alert variant="elevated">
<AlertIcon />
<AlertDescription>
<TextMd>
<Highlight query="Rewards" styles={{ fontWeight: "bold" }}>
You will receive your Rewards once the deposit transaction is
completed.
</Highlight>
</TextMd>
</AlertDescription>
</Alert>
</TriggerTransactionModal>
</>
)
useTimeout(handledDepositBTCWrapper, ONE_SEC_IN_MILLISECONDS)

if (status === "pending" || status === "success")
return <WalletInteractionModal step="awaiting-transaction" />

return <WalletInteractionModal step="opening-wallet" />
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import React, { useCallback, useState } from "react"
import {
useActionFlowTxHash,
useAppDispatch,
useExecuteFunction,
useStakeFlowContext,
} from "#/hooks"
import { PROCESS_STATUSES } from "#/types"
import { logPromiseFailure } from "#/utils"
import { setStatus } from "#/store/action-flow"
import { UnexpectedErrorModalBase } from "#/components/UnexpectedErrorModal"
import { useMutation } from "@tanstack/react-query"
import ServerErrorModal from "./ServerErrorModal"
import RetryModal from "./RetryModal"
import LoadingModal from "../../LoadingModal"
Expand All @@ -22,7 +21,6 @@ export default function StakingErrorModal({
const dispatch = useAppDispatch()
const txHash = useActionFlowTxHash()

const [isLoading, setIsLoading] = useState(false)
const [isServerError, setIsServerError] = useState(false)

const onStakeBTCSuccess = useCallback(() => {
Expand All @@ -31,29 +29,21 @@ export default function StakingErrorModal({

const onStakeBTCError = useCallback(() => setIsServerError(true), [])

const handleStake = useExecuteFunction(
stake,
onStakeBTCSuccess,
onStakeBTCError,
)
const { mutate: handleStake, status } = useMutation({
mutationKey: ["stake"],
mutationFn: stake,
onSuccess: onStakeBTCSuccess,
onError: onStakeBTCError,
})

const handleRetry = useCallback(async () => {
setIsLoading(true)
await handleStake()
setIsLoading(false)
}, [handleStake])

const handleRetryWrapper = useCallback(
() => logPromiseFailure(handleRetry()),
[handleRetry],
)
const isLoading = status === "pending"

if (isServerError)
return <ServerErrorModal retry={handleRetryWrapper} isLoading={isLoading} />
return <ServerErrorModal retry={handleStake} isLoading={isLoading} />

if (isLoading) return <LoadingModal />

if (txHash) return <RetryModal retry={handleRetryWrapper} />
if (txHash) return <RetryModal retry={handleStake} />

return <UnexpectedErrorModalBase closeModal={closeModal} withCloseButton />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react"
import {
Button,
ModalBody,
ModalCloseButton,
ModalHeader,
} from "@chakra-ui/react"
import Spinner from "#/components/shared/Spinner"
import { TextMd } from "#/components/shared/Typography"

export default function BuildTransactionModal({
onClose,
}: {
onClose: () => void
}) {
return (
<>
<ModalCloseButton onClick={onClose} />
<ModalHeader>Building transaction data...</ModalHeader>
<ModalBody>
<Spinner size="xl" variant="filled" />
<TextMd>We are building your withdrawal data.</TextMd>
<Button size="lg" width="100%" variant="outline" onClick={onClose}>
Cancel
</Button>
</ModalBody>
</>
)
}
Loading

0 comments on commit 36b0fe8

Please sign in to comment.