From f12b384e074b878939261bd59341ef55b1887bf4 Mon Sep 17 00:00:00 2001 From: Ted Palmer Date: Fri, 6 Feb 2026 09:37:17 -0500 Subject: [PATCH] Refactor deposit address transaction tracking to use requests API Switch OnrampModal and DepositAddressModalRenderer from polling /intents/status/v2 (via useExecutionStatus) to polling /requests/v2 (via new useDepositAddressStatus hook) using the deposit address as the user param. The new hook wraps useRequests and maps the response to match the existing executionStatus interface, minimizing changes to consuming components. Co-Authored-By: Claude Opus 4.5 --- .../src/hooks/useDepositAddressStatus.ts | 91 +++++++++++++++++++ packages/hooks/src/index.ts | 5 + .../DepositAddressModalRenderer.tsx | 25 +++-- .../OnrampWidget/modals/OnrampModal.tsx | 21 ++--- 4 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 packages/hooks/src/hooks/useDepositAddressStatus.ts diff --git a/packages/hooks/src/hooks/useDepositAddressStatus.ts b/packages/hooks/src/hooks/useDepositAddressStatus.ts new file mode 100644 index 000000000..6cb20593a --- /dev/null +++ b/packages/hooks/src/hooks/useDepositAddressStatus.ts @@ -0,0 +1,91 @@ +import { useMemo } from 'react' +import { + useQuery, + type DefaultError, + type QueryKey +} from '@tanstack/react-query' +import { MAINNET_RELAY_API } from '@relayprotocol/relay-sdk' +import { queryRequests } from './useRequests.js' + +export type DepositAddressStatusResponse = { + status?: 'refund' | 'waiting' | 'failure' | 'pending' | 'success' + details?: string + txHashes?: string[] + inTxHashes?: string[] +} + +type QueryType = typeof useQuery< + DepositAddressStatusResponse | undefined, + DefaultError, + DepositAddressStatusResponse | undefined, + QueryKey +> +type QueryOptions = Parameters['0'] + +type DepositAddressStatusParams = { + depositAddress: string +} + +export const queryDepositAddressStatus = async function ( + baseApiUrl: string = MAINNET_RELAY_API, + options?: DepositAddressStatusParams +): Promise { + if (!options?.depositAddress) { + return undefined + } + + const response = await queryRequests(baseApiUrl, { + user: options.depositAddress + }) + + const request = response?.requests?.[0] + if (!request) { + return undefined + } + + return { + status: request.status, + details: request.data?.failReason, + txHashes: request.data?.outTxs + ?.map((tx) => tx.hash) + .filter((hash): hash is string => Boolean(hash)), + inTxHashes: request.data?.inTxs + ?.map((tx) => tx.hash) + .filter((hash): hash is string => Boolean(hash)) + } +} + +export default function useDepositAddressStatus( + options?: DepositAddressStatusParams, + baseApiUrl?: string, + queryOptions?: Partial +) { + const response = (useQuery as QueryType)({ + queryKey: ['useDepositAddressStatus', options?.depositAddress], + queryFn: () => queryDepositAddressStatus(baseApiUrl, options), + enabled: options?.depositAddress !== undefined, + retry: false, + ...queryOptions + }) + + return useMemo( + () => + ({ + ...response, + data: response.error ? undefined : response.data, + queryKey: ['useDepositAddressStatus', options?.depositAddress] + }) as Omit, 'data'> & { + data?: DepositAddressStatusResponse + queryKey: QueryKey + }, + [ + response.data, + response.error, + response.isLoading, + response.isFetching, + response.isRefetching, + response.dataUpdatedAt, + options?.depositAddress + ] + ) +} diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index dc7cc1401..dafffe7a1 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -17,6 +17,10 @@ export { default as useExecutionStatus, queryExecutionStatus } from './hooks/useExecutionStatus.js' +export { + default as useDepositAddressStatus, + queryDepositAddressStatus +} from './hooks/useDepositAddressStatus.js' export { default as useTokenPrice, queryTokenPrice @@ -29,3 +33,4 @@ export { //types export type { CurrencyList, Currency } from './hooks/useTokenList.js' export type { QuoteResponse } from './hooks/useQuote.js' +export type { DepositAddressStatusResponse } from './hooks/useDepositAddressStatus.js' diff --git a/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx b/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx index 71c5ce88c..ba86998ca 100644 --- a/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx +++ b/packages/ui/src/components/common/TransactionModal/DepositAddressModalRenderer.tsx @@ -21,8 +21,9 @@ import { import type { Token } from '../../../types/index.js' import { useRequests, - useExecutionStatus, - queryQuote + useDepositAddressStatus, + queryQuote, + type DepositAddressStatusResponse } from '@relayprotocol/relay-kit-hooks' import { useRelayClient } from '../../../hooks/index.js' import { EventNames } from '../../../constants/events.js' @@ -62,7 +63,7 @@ export type ChildrenProps = { fillTime: string requestId: string | null depositAddress?: string - executionStatus?: ReturnType['data'] + executionStatus?: DepositAddressStatusResponse isLoadingTransaction: boolean toChain?: RelayChain | null timeEstimate?: { time: number; formattedTime: string } @@ -83,7 +84,7 @@ type Props = { children: (props: ChildrenProps) => ReactNode onSuccess?: ( quote?: Execute | null, - executionStatus?: ReturnType['data'] + executionStatus?: DepositAddressStatusResponse ) => void onAnalyticEvent?: (eventName: string, data?: any) => void onSwapError?: (error: string, data?: Execute) => void @@ -226,22 +227,18 @@ export const DepositAddressModalRenderer: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [open]) - const { data: executionStatus } = useExecutionStatus( - relayClient ? relayClient : undefined, - { - requestId: requestId ?? undefined, - referrer: relayClient?.source - }, - undefined, - undefined, + const { data: executionStatus } = useDepositAddressStatus( + depositAddress ? { depositAddress } : undefined, + relayClient?.baseApiUrl, { - enabled: requestId !== null && open, + enabled: depositAddress !== undefined && open, refetchInterval(query) { const observableStates = ['waiting', 'pending'] if ( !query.state.data?.status || - (requestId && observableStates.includes(query.state.data?.status)) + (depositAddress && + observableStates.includes(query.state.data?.status)) ) { return 1000 } diff --git a/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx b/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx index 54baa639a..b0d8af898 100644 --- a/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx +++ b/packages/ui/src/components/widgets/OnrampWidget/modals/OnrampModal.tsx @@ -10,7 +10,7 @@ import type { FiatCurrency, Token } from '../../../../types/index.js' import useRelayClient from '../../../../hooks/useRelayClient.js' import { EventNames } from '../../../../constants/events.js' import { - useExecutionStatus, + useDepositAddressStatus, useQuote, useRequests, useTokenPrice @@ -223,22 +223,21 @@ export const OnrampModal: FC = ({ [quote] ) - const { data: executionStatus } = useExecutionStatus( - client ? client : undefined, - { - requestId: requestId ?? undefined, - referrer: client?.source - }, - undefined, - undefined, + const { data: executionStatus } = useDepositAddressStatus( + depositAddress ? { depositAddress } : undefined, + client?.baseApiUrl, { - enabled: requestId !== null && step === OnrampStep.Processing && open, + enabled: + depositAddress !== undefined && + step === OnrampStep.Processing && + open, refetchInterval(query) { const observableStates = ['waiting', 'pending'] if ( !query.state.data?.status || - (requestId && observableStates.includes(query.state.data?.status)) + (depositAddress && + observableStates.includes(query.state.data?.status)) ) { return 1000 }