Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 5 additions & 30 deletions packages/thirdweb/src/react/core/hooks/useStepExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useQuery } from "@tanstack/react-query";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { status as OnrampStatus } from "../../../bridge/OnrampStatus.js";
import { ApiError } from "../../../bridge/types/Errors.js";
Expand All @@ -10,14 +9,9 @@
import { getCachedChain } from "../../../chains/utils.js";
import type { ThirdwebClient } from "../../../client/client.js";
import { waitForReceipt } from "../../../transaction/actions/wait-for-tx-receipt.js";
import { stringify } from "../../../utils/json.js";
import type { Account, Wallet } from "../../../wallets/interfaces/wallet.js";
import type { WindowAdapter } from "../adapters/WindowAdapter.js";
import {
type BridgePrepareRequest,
type BridgePrepareResult,
useBridgePrepare,
} from "./useBridgePrepare.js";
import type { BridgePrepareResult } from "./useBridgePrepare.js";

/**
* Type for completed status results from Bridge.status and Onramp.status
Expand All @@ -35,8 +29,7 @@
* Options for the step executor hook
*/
interface StepExecutorOptions {
/** Prepared quote returned by Bridge.prepare */
request: BridgePrepareRequest;
preparedQuote: BridgePrepareResult;
/** Wallet instance providing getAccount() & sendTransaction */
wallet?: Wallet;
/** Window adapter for opening on-ramp URLs (web / RN) */
Expand Down Expand Up @@ -67,7 +60,7 @@
currentTxIndex?: number;
progress: number; // 0–100
onrampStatus?: "pending" | "executing" | "completed" | "failed";
executionState: "fetching" | "idle" | "executing" | "auto-starting";
executionState: "idle" | "executing" | "auto-starting";
steps?: RouteStep[];
error?: ApiError;
start: () => void;
Expand Down Expand Up @@ -100,16 +93,14 @@
options: StepExecutorOptions,
): StepExecutorResult {
const {
request,
wallet,
windowAdapter,
client,
autoStart = false,
onComplete,
preparedQuote,

Check warning on line 101 in packages/thirdweb/src/react/core/hooks/useStepExecutor.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/core/hooks/useStepExecutor.ts#L101

Added line #L101 was not covered by tests
} = options;

const { data: preparedQuote, isLoading } = useBridgePrepare(request);

// Flatten all transactions upfront
const flatTxs = useMemo(
() => (preparedQuote?.steps ? flattenRouteSteps(preparedQuote.steps) : []),
Expand All @@ -121,30 +112,14 @@
undefined,
);
const [executionState, setExecutionState] = useState<
"fetching" | "idle" | "executing" | "auto-starting"
"idle" | "executing" | "auto-starting"
>("idle");
const [error, setError] = useState<ApiError | undefined>(undefined);
const [completedTxs, setCompletedTxs] = useState<Set<number>>(new Set());
const [onrampStatus, setOnrampStatus] = useState<
"pending" | "executing" | "completed" | "failed" | undefined
>(preparedQuote?.type === "onramp" ? "pending" : undefined);

useQuery({
queryFn: async () => {
if (!isLoading) {
setExecutionState("idle");
} else {
setExecutionState("fetching");
}
return executionState;
},
queryKey: [
"bridge-quote-execution-state",
stringify(preparedQuote?.steps),
isLoading,
],
});

// Cancellation tracking
const abortControllerRef = useRef<AbortController | null>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@

{state.value === "execute" && quote && state.context.request && (
<StepRunner
preparedQuote={quote}

Check warning on line 382 in packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx#L382

Added line #L382 was not covered by tests
autoStart={true}
client={client}
onBack={() => {
Expand Down
13 changes: 11 additions & 2 deletions packages/thirdweb/src/react/web/ui/Bridge/StepRunner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
radius,
spacing,
} from "../../../core/design-system/index.js";
import type { BridgePrepareRequest } from "../../../core/hooks/useBridgePrepare.js";
import type {
BridgePrepareRequest,
BridgePrepareResult,
} from "../../../core/hooks/useBridgePrepare.js";
import {
type CompletedStatusResult,
useStepExecutor,
Expand Down Expand Up @@ -62,6 +65,11 @@
* Called when user clicks the back button
*/
onBack?: () => void;

/**
* Prepared quote to use
*/
preparedQuote: BridgePrepareResult;
}

export function StepRunner({
Expand All @@ -74,6 +82,7 @@
onCancel,
onBack,
autoStart,
preparedQuote,

Check warning on line 85 in packages/thirdweb/src/react/web/ui/Bridge/StepRunner.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/Bridge/StepRunner.tsx#L85

Added line #L85 was not covered by tests
}: StepRunnerProps) {
const theme = useCustomTheme();

Expand All @@ -94,8 +103,8 @@
onComplete: (completedStatuses: CompletedStatusResult[]) => {
onComplete(completedStatuses);
},
request,
wallet,
preparedQuote,

Check warning on line 107 in packages/thirdweb/src/react/web/ui/Bridge/StepRunner.tsx

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/react/web/ui/Bridge/StepRunner.tsx#L107

Added line #L107 was not covered by tests
windowAdapter,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ function SwapWidgetContent(props: SwapWidgetProps) {
<StepRunner
title="Processing Swap"
autoStart={true}
preparedQuote={screen.preparedQuote}
client={props.client}
onBack={() => {
setScreen({
Expand Down
29 changes: 6 additions & 23 deletions packages/thirdweb/src/stories/Bridge/StepRunner.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import type { CompletedStatusResult } from "../../react/core/hooks/useStepExecut
import { StepRunner } from "../../react/web/ui/Bridge/StepRunner.js";
import type { Wallet } from "../../wallets/interfaces/wallet.js";
import { ModalThemeWrapper, storyClient } from "../utils.js";
import { STORY_MOCK_WALLET, simpleBuyRequest } from "./fixtures.js";
import {
STORY_MOCK_WALLET,
simpleBuyQuote,
simpleBuyRequest,
} from "./fixtures.js";

// Mock window adapter
const mockWindowAdapter: WindowAdapter = {
Expand All @@ -32,7 +36,7 @@ const StepRunnerWithTheme = (props: StepRunnerWithThemeProps) => {
const { theme, ...componentProps } = props;
return (
<ModalThemeWrapper theme={theme}>
<StepRunner {...componentProps} />
<StepRunner {...componentProps} preparedQuote={simpleBuyQuote} />
</ModalThemeWrapper>
);
};
Expand All @@ -59,29 +63,8 @@ const meta = {
},
component: StepRunnerWithTheme,
parameters: {
docs: {
description: {
component:
"**StepRunner** executes prepared route steps sequentially, showing real-time progress and transaction status.\n\n" +
"## Features\n" +
"- **Real Execution**: Uses useStepExecutor hook for actual transaction processing\n" +
"- **Progress Tracking**: Visual progress bar and step-by-step status updates\n" +
"- **Error Handling**: Retry functionality for failed transactions\n" +
"- **Transaction Batching**: Optimizes multiple transactions when possible\n" +
"- **Onramp Support**: Handles fiat-to-crypto onramp flows\n\n" +
"## Props\n" +
"- `steps`: Array of RouteStep objects from Bridge.prepare()\n" +
"- `wallet`: Connected wallet for transaction signing\n" +
"- `client`: ThirdwebClient instance\n" +
"- `windowAdapter`: Platform-specific window/URL handler\n" +
"- `onramp`: Optional onramp configuration\n\n" +
"## Integration\n" +
"This component is typically used within the BridgeOrchestrator after route preparation.",
},
},
layout: "centered",
},
tags: ["autodocs"],
title: "Bridge/StepRunner",
} satisfies Meta<typeof StepRunnerWithTheme>;

Expand Down
Loading