@@ -11,7 +11,13 @@ import type { ThirdwebClient } from "../../../client/client.js";
1111import { waitForReceipt } from "../../../transaction/actions/wait-for-tx-receipt.js" ;
1212import type { Account , Wallet } from "../../../wallets/interfaces/wallet.js" ;
1313import type { WindowAdapter } from "../adapters/WindowAdapter.js" ;
14- import type { BridgePrepareResult } from "./useBridgePrepare.js" ;
14+ import {
15+ useBridgePrepare ,
16+ type BridgePrepareRequest ,
17+ type BridgePrepareResult ,
18+ } from "./useBridgePrepare.js" ;
19+ import { useQuery } from "@tanstack/react-query" ;
20+ import { stringify } from "../../../utils/json.js" ;
1521
1622/**
1723 * Type for completed status results from Bridge.status and Onramp.status
@@ -21,16 +27,16 @@ export type CompletedStatusResult =
2127 | ( { type : "sell" } & Extract < Status , { status : "COMPLETED" } > )
2228 | ( { type : "transfer" } & Extract < Status , { status : "COMPLETED" } > )
2329 | ( { type : "onramp" } & Extract <
24- OnrampStatus . Result ,
25- { status : "COMPLETED" }
26- > ) ;
30+ OnrampStatus . Result ,
31+ { status : "COMPLETED" }
32+ > ) ;
2733
2834/**
2935 * Options for the step executor hook
3036 */
3137export interface StepExecutorOptions {
3238 /** Prepared quote returned by Bridge.prepare */
33- preparedQuote : BridgePrepareResult ;
39+ request : BridgePrepareRequest ;
3440 /** Wallet instance providing getAccount() & sendTransaction */
3541 wallet : Wallet ;
3642 /** Window adapter for opening on-ramp URLs (web / RN) */
@@ -61,7 +67,8 @@ export interface StepExecutorResult {
6167 currentTxIndex ?: number ;
6268 progress : number ; // 0–100
6369 onrampStatus ?: "pending" | "executing" | "completed" | "failed" ;
64- executionState : "idle" | "executing" | "auto-starting" ;
70+ executionState : "fetching" | "idle" | "executing" | "auto-starting" ;
71+ steps ?: RouteStep [ ] ;
6572 error ?: ApiError ;
6673 start : ( ) => void ;
6774 cancel : ( ) => void ;
@@ -93,47 +100,67 @@ export function useStepExecutor(
93100 options : StepExecutorOptions ,
94101) : StepExecutorResult {
95102 const {
96- preparedQuote ,
103+ request ,
97104 wallet,
98105 windowAdapter,
99106 client,
100107 autoStart = false ,
101108 onComplete,
102109 } = options ;
103110
111+ const { data : preparedQuote , isLoading } = useBridgePrepare ( request ) ;
112+
104113 // Flatten all transactions upfront
105114 const flatTxs = useMemo (
106- ( ) => flattenRouteSteps ( preparedQuote . steps ) ,
107- [ preparedQuote . steps ] ,
115+ ( ) => ( preparedQuote ?. steps ? flattenRouteSteps ( preparedQuote . steps ) : [ ] ) ,
116+ [ preparedQuote ? .steps ] ,
108117 ) ;
109118
110119 // State management
111120 const [ currentTxIndex , setCurrentTxIndex ] = useState < number | undefined > (
112121 undefined ,
113122 ) ;
114123 const [ executionState , setExecutionState ] = useState <
115- "idle" | "executing" | "auto-starting"
124+ "fetching" | " idle" | "executing" | "auto-starting"
116125 > ( "idle" ) ;
117126 const [ error , setError ] = useState < ApiError | undefined > ( undefined ) ;
118127 const [ completedTxs , setCompletedTxs ] = useState < Set < number > > ( new Set ( ) ) ;
119128 const [ onrampStatus , setOnrampStatus ] = useState <
120129 "pending" | "executing" | "completed" | "failed" | undefined
121- > ( preparedQuote . type === "onramp" ? "pending" : undefined ) ;
130+ > ( preparedQuote ?. type === "onramp" ? "pending" : undefined ) ;
131+
132+ useQuery ( {
133+ queryKey : [
134+ "bridge-quote-execution-state" ,
135+ stringify ( preparedQuote ?. steps ) ,
136+ isLoading ,
137+ ] ,
138+ queryFn : async ( ) => {
139+ if ( ! isLoading ) {
140+ setExecutionState ( "idle" ) ;
141+ } else {
142+ setExecutionState ( "fetching" ) ;
143+ }
144+ return executionState ;
145+ } ,
146+ } ) ;
122147
123148 // Cancellation tracking
124149 const abortControllerRef = useRef < AbortController | null > ( null ) ;
125150
126151 // Get current step based on current tx index
127152 const currentStep = useMemo ( ( ) => {
153+ if ( typeof preparedQuote ?. steps === "undefined" ) return undefined ;
128154 if ( currentTxIndex === undefined ) {
129155 return undefined ;
130156 }
131157 const tx = flatTxs [ currentTxIndex ] ;
132158 return tx ? preparedQuote . steps [ tx . _stepIndex ] : undefined ;
133- } , [ currentTxIndex , flatTxs , preparedQuote . steps ] ) ;
159+ } , [ currentTxIndex , flatTxs , preparedQuote ? .steps ] ) ;
134160
135161 // Calculate progress including onramp step
136162 const progress = useMemo ( ( ) => {
163+ if ( typeof preparedQuote ?. type === "undefined" ) return 0 ;
137164 const totalSteps =
138165 flatTxs . length + ( preparedQuote . type === "onramp" ? 1 : 0 ) ;
139166 if ( totalSteps === 0 ) {
@@ -142,7 +169,7 @@ export function useStepExecutor(
142169 const completedSteps =
143170 completedTxs . size + ( onrampStatus === "completed" ? 1 : 0 ) ;
144171 return Math . round ( ( completedSteps / totalSteps ) * 100 ) ;
145- } , [ completedTxs . size , flatTxs . length , preparedQuote . type , onrampStatus ] ) ;
172+ } , [ completedTxs . size , flatTxs . length , preparedQuote ? .type , onrampStatus ] ) ;
146173
147174 // Exponential backoff polling utility
148175 const poller = useCallback (
@@ -181,6 +208,9 @@ export function useStepExecutor(
181208 completedStatusResults : CompletedStatusResult [ ] ,
182209 abortSignal : AbortSignal ,
183210 ) => {
211+ if ( typeof preparedQuote ?. type === "undefined" ) {
212+ throw new Error ( "No quote generated. This is unexpected." ) ;
213+ }
184214 const { prepareTransaction } = await import (
185215 "../../../transaction/prepare-transaction.js"
186216 ) ;
@@ -229,10 +259,14 @@ export function useStepExecutor(
229259 return { completed : true } ;
230260 }
231261
262+ if ( statusResult . status === "FAILED" ) {
263+ throw new Error ( "Payment failed" ) ;
264+ }
265+
232266 return { completed : false } ;
233267 } , abortSignal ) ;
234268 } ,
235- [ poller , preparedQuote . type ] ,
269+ [ poller , preparedQuote ? .type ] ,
236270 ) ;
237271
238272 // Execute batch transactions
@@ -243,6 +277,9 @@ export function useStepExecutor(
243277 completedStatusResults : CompletedStatusResult [ ] ,
244278 abortSignal : AbortSignal ,
245279 ) => {
280+ if ( typeof preparedQuote ?. type === "undefined" ) {
281+ throw new Error ( "No quote generated. This is unexpected." ) ;
282+ }
246283 if ( ! account . sendBatchTransaction ) {
247284 throw new Error ( "Account does not support batch transactions" ) ;
248285 }
@@ -303,10 +340,14 @@ export function useStepExecutor(
303340 return { completed : true } ;
304341 }
305342
343+ if ( statusResult . status === "FAILED" ) {
344+ throw new Error ( "Payment failed" ) ;
345+ }
346+
306347 return { completed : false } ;
307348 } , abortSignal ) ;
308349 } ,
309- [ poller , preparedQuote . type ] ,
350+ [ poller , preparedQuote ? .type ] ,
310351 ) ;
311352
312353 // Execute onramp step
@@ -350,6 +391,9 @@ export function useStepExecutor(
350391
351392 // Main execution function
352393 const execute = useCallback ( async ( ) => {
394+ if ( typeof preparedQuote ?. type === "undefined" ) {
395+ throw new Error ( "No quote generated. This is unexpected." ) ;
396+ }
353397 if ( executionState !== "idle" ) {
354398 return ;
355399 }
@@ -552,6 +596,7 @@ export function useStepExecutor(
552596 currentTxIndex,
553597 progress,
554598 executionState,
599+ steps : preparedQuote ?. steps ,
555600 onrampStatus,
556601 error,
557602 start,
0 commit comments