diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx index 005acea43..e51d8056f 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/DepositBTCModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react" +import React, { useCallback, useEffect } from "react" import { useActionFlowTokenAmount, useAppDispatch, @@ -18,7 +18,7 @@ import Spinner from "#/components/shared/Spinner" import { TextMd } from "#/components/shared/Typography" import { CardAlert } from "#/components/shared/alerts" import { ONE_SEC_IN_MILLISECONDS } from "#/constants" -import { setStatus } from "#/store/action-flow" +import { setStatus, setTxHash } from "#/store/action-flow" const DELAY = ONE_SEC_IN_MILLISECONDS const TOAST_ID = TOAST_IDS.DEPOSIT_TRANSACTION_ERROR @@ -64,11 +64,17 @@ export default function DepositBTCModal() { const onDepositBTCError = useCallback(() => showError(), [showError]) - const { sendBitcoinTransaction } = useDepositBTCTransaction( + const { sendBitcoinTransaction, transactionHash } = useDepositBTCTransaction( onDepositBTCSuccess, onDepositBTCError, ) + useEffect(() => { + if (transactionHash) { + dispatch(setTxHash(transactionHash)) + } + }, [dispatch, transactionHash]) + const handledDepositBTC = useCallback(async () => { if (!tokenAmount?.amount || !btcAddress || !depositReceipt || !ethAccount) return diff --git a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx index cfe99aea7..c9e78a3a5 100644 --- a/dapp/src/components/TransactionModal/ModalContentWrapper.tsx +++ b/dapp/src/components/TransactionModal/ModalContentWrapper.tsx @@ -2,6 +2,7 @@ import React from "react" import { useActionFlowStatus, useActionFlowTokenAmount, + useActionFlowTxHash, useActionFlowType, useRequestBitcoinAccount, useRequestEthereumAccount, @@ -27,6 +28,7 @@ export default function ModalContentWrapper({ const status = useActionFlowStatus() const type = useActionFlowType() const tokenAmount = useActionFlowTokenAmount() + const txHash = useActionFlowTxHash() if (!btcAccount || !isSupportedBTCAddressType(btcAccount.address)) return ( @@ -50,8 +52,10 @@ export default function ModalContentWrapper({ if (status === PROCESS_STATUSES.LOADING) return - if (status === PROCESS_STATUSES.SUCCEEDED) - return + if (status === PROCESS_STATUSES.SUCCEEDED && txHash) + return ( + + ) if (status === PROCESS_STATUSES.FAILED) return diff --git a/dapp/src/components/TransactionModal/SuccessModal.tsx b/dapp/src/components/TransactionModal/SuccessModal.tsx index 0a2d84b6d..6a666a575 100644 --- a/dapp/src/components/TransactionModal/SuccessModal.tsx +++ b/dapp/src/components/TransactionModal/SuccessModal.tsx @@ -2,6 +2,7 @@ import React from "react" import { Button, HStack, + Icon, ModalBody, ModalFooter, ModalHeader, @@ -11,6 +12,9 @@ import { LoadingSpinnerSuccessIcon } from "#/assets/icons" import { useModal } from "#/hooks" import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion" import { ACTION_FLOW_TYPES, ActionFlowType, TokenAmount } from "#/types" +import { useNavigate } from "react-router-dom" +import { routerPath } from "#/router/path" +import { IconArrowUpRight } from "@tabler/icons-react" import { TextMd } from "../shared/Typography" import Spinner from "../shared/Spinner" import BlockExplorerLink from "../shared/BlockExplorerLink" @@ -19,13 +23,13 @@ const CONTENT: Record< ActionFlowType, { header: string - renderBody: (tokenAmount: TokenAmount) => React.ReactNode + renderBody: (tokenAmount: TokenAmount, txHash: string) => React.ReactNode footer: string } > = { [ACTION_FLOW_TYPES.STAKE]: { header: "Deposit received", - renderBody: (tokenAmount) => ( + renderBody: (tokenAmount, txHash) => ( <> - {/* TODO: Use correct tx hash and update styles */} - + {/* TODO: Update styles */} + + + View on Mempool + + + ), footer: "The staking will continue in the background", @@ -63,10 +77,16 @@ const CONTENT: Record< type SuccessModalProps = { type: ActionFlowType tokenAmount: TokenAmount + txHash: string } -export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) { +export default function SuccessModal({ + type, + tokenAmount, + txHash, +}: SuccessModalProps) { const { closeModal } = useModal() + const navigate = useNavigate() const { header, footer, renderBody } = CONTENT[type] @@ -76,11 +96,19 @@ export default function SuccessModal({ type, tokenAmount }: SuccessModalProps) { - {renderBody(tokenAmount)} + {renderBody(tokenAmount, txHash)} - diff --git a/dapp/src/constants/chains.ts b/dapp/src/constants/chains.ts index 43d408d31..7574621b9 100644 --- a/dapp/src/constants/chains.ts +++ b/dapp/src/constants/chains.ts @@ -3,12 +3,12 @@ import { EthereumNetwork, BitcoinNetwork } from "@acre-btc/sdk" const BLOCK_EXPLORER_TESTNET = { ethereum: { title: "Etherscan", url: "https://sepolia.etherscan.io" }, - bitcoin: { title: "Mempool Space", url: "https://mempool.space/testnet" }, + bitcoin: { title: "Mempool", url: "https://mempool.space/testnet" }, } const BLOCK_EXPLORER_MAINNET = { ethereum: { title: "Etherscan", url: "https://etherscan.io" }, - bitcoin: { title: "Mempool Space", url: "https://mempool.space" }, + bitcoin: { title: "Mempool", url: "https://mempool.space" }, } export const BLOCK_EXPLORER: Record = diff --git a/dapp/src/hooks/store/index.ts b/dapp/src/hooks/store/index.ts index 34a696f73..1cf08a580 100644 --- a/dapp/src/hooks/store/index.ts +++ b/dapp/src/hooks/store/index.ts @@ -7,5 +7,6 @@ export * from "./useActionFlowType" export * from "./useActionFlowStatus" export * from "./useActionFlowActiveStep" export * from "./useActionFlowTokenAmount" +export * from "./useActionFlowTxHash" export * from "./useCompletedActivities" export * from "./useLatestActivities" diff --git a/dapp/src/hooks/store/useActionFlowTxHash.ts b/dapp/src/hooks/store/useActionFlowTxHash.ts new file mode 100644 index 000000000..0dce7063e --- /dev/null +++ b/dapp/src/hooks/store/useActionFlowTxHash.ts @@ -0,0 +1,6 @@ +import { selectActionFlowTxHash } from "#/store/action-flow" +import { useAppSelector } from "./useAppSelector" + +export function useActionFlowTxHash() { + return useAppSelector(selectActionFlowTxHash) +} diff --git a/dapp/src/store/action-flow/actionFlowSelectors.ts b/dapp/src/store/action-flow/actionFlowSelectors.ts index a814d6290..dd83e7517 100644 --- a/dapp/src/store/action-flow/actionFlowSelectors.ts +++ b/dapp/src/store/action-flow/actionFlowSelectors.ts @@ -13,3 +13,6 @@ export const selectActionFlowStatus = (state: RootState): ProcessStatus => export const selectActionFlowTokenAmount = ( state: RootState, ): TokenAmount | undefined => state.actionFlow.tokenAmount + +export const selectActionFlowTxHash = (state: RootState): string | undefined => + state.actionFlow.txHash diff --git a/dapp/src/store/action-flow/actionFlowSlice.ts b/dapp/src/store/action-flow/actionFlowSlice.ts index cf650d70c..31b670484 100644 --- a/dapp/src/store/action-flow/actionFlowSlice.ts +++ b/dapp/src/store/action-flow/actionFlowSlice.ts @@ -12,6 +12,7 @@ type ActionFlowState = { activeStep: number status: ProcessStatus tokenAmount?: TokenAmount + txHash?: string } const initialState: ActionFlowState = { @@ -19,6 +20,7 @@ const initialState: ActionFlowState = { activeStep: 1, status: PROCESS_STATUSES.IDLE, tokenAmount: undefined, + txHash: undefined, } export const actionFlowSlice = createSlice({ @@ -37,6 +39,9 @@ export const actionFlowSlice = createSlice({ setTokenAmount(state, action: PayloadAction) { state.tokenAmount = action.payload }, + setTxHash(state, action: PayloadAction) { + state.txHash = action.payload + }, goNextStep(state) { state.activeStep += 1 }, @@ -45,9 +50,16 @@ export const actionFlowSlice = createSlice({ state.activeStep = initialState.activeStep state.status = initialState.status state.tokenAmount = initialState.tokenAmount + state.txHash = initialState.txHash }, }, }) -export const { setType, setStatus, setTokenAmount, goNextStep, resetState } = - actionFlowSlice.actions +export const { + setType, + setStatus, + setTokenAmount, + setTxHash, + goNextStep, + resetState, +} = actionFlowSlice.actions diff --git a/dapp/src/store/wallet/walletSelector.ts b/dapp/src/store/wallet/walletSelector.ts index cf361c089..f77592b02 100644 --- a/dapp/src/store/wallet/walletSelector.ts +++ b/dapp/src/store/wallet/walletSelector.ts @@ -1,14 +1,17 @@ import { createSelector } from "@reduxjs/toolkit" -import { isActivityCompleted } from "#/utils" +import { isActivityCompleted, sortActivitiesByTimestamp } from "#/utils" import { RootState } from ".." export const selectLatestActivities = createSelector( (state: RootState) => state.wallet.latestActivities, - (latestActivities) => Object.values(latestActivities), + (latestActivities) => + sortActivitiesByTimestamp(Object.values(latestActivities)), ) export const selectCompletedActivities = createSelector( (state: RootState) => state.wallet.activities, (activities) => - activities.filter((activity) => isActivityCompleted(activity)), + sortActivitiesByTimestamp( + activities.filter((activity) => isActivityCompleted(activity)), + ), ) diff --git a/dapp/src/utils/activities.ts b/dapp/src/utils/activities.ts index d498dbf2a..cabe474c2 100644 --- a/dapp/src/utils/activities.ts +++ b/dapp/src/utils/activities.ts @@ -2,3 +2,8 @@ import { Activity } from "#/types" export const isActivityCompleted = (activity: Activity): boolean => activity.status === "completed" + +export const sortActivitiesByTimestamp = (activities: Activity[]): Activity[] => + activities.sort( + (activity1, activity2) => activity1.timestamp - activity2.timestamp, + )