From 1504689cd3747390097030a4ca7fce25953a112a Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Tue, 18 Aug 2026 14:32:35 +0700 Subject: [PATCH 1/2] [SDK] Fix Universal Bridge onramp reporting success when it did not complete On retry after a failed onramp, onrampStatus is left non-pending so the onramp step is skipped; for quotes with no follow-up transactions the completion check only verified execution was not aborted before reporting success. Gate success on the onramp having completed. Co-Authored-By: Claude Opus 4.8 --- .changeset/onramp-success-guard.md | 5 +++++ .../thirdweb/src/react/core/hooks/useStepExecutor.ts | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 .changeset/onramp-success-guard.md diff --git a/.changeset/onramp-success-guard.md b/.changeset/onramp-success-guard.md new file mode 100644 index 00000000000..51fd8da313c --- /dev/null +++ b/.changeset/onramp-success-guard.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Fixed Universal Bridge onramp checkout incorrectly reporting success when the onramp did not complete. diff --git a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts index e0a8f024200..1a6951157da 100644 --- a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts +++ b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts @@ -517,12 +517,15 @@ export function useStepExecutor( } // Execute onramp first if configured and not already completed + let onrampCompleted = + preparedQuote.type !== "onramp" || onrampStatus === "completed"; if (preparedQuote.type === "onramp" && onrampStatus === "pending") { await executeOnramp( preparedQuote, completedStatusResults, abortController.signal, ); + onrampCompleted = true; } if (flatTxs.length > 0) { @@ -637,6 +640,15 @@ export function useStepExecutor( } // All done - check if we actually completed everything if (!abortController.signal.aborted) { + // Only report success for an onramp once it has actually completed. + if (!onrampCompleted) { + throw new ApiError({ + code: "INTERNAL_SERVER_ERROR", + message: "Onramp did not complete", + statusCode: 500, + }); + } + setCurrentTxIndex(undefined); // Call completion callback with all completed status results From 2786b4d45ea4ee9bf46ce6ef414c4e595f752158 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Tue, 18 Aug 2026 14:51:40 +0700 Subject: [PATCH 2/2] [SDK] Fail fast on an incomplete onramp before follow-up transactions A terminal FAILED onramp status now throws so the poller stops and the existing error path runs, and onramp completion is validated before the follow-up transaction loop rather than only at the final success check. Co-Authored-By: Claude Opus 4.8 --- .../src/react/core/hooks/useStepExecutor.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts index 1a6951157da..1f96be33dab 100644 --- a/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts +++ b/packages/thirdweb/src/react/core/hooks/useStepExecutor.ts @@ -466,6 +466,7 @@ export function useStepExecutor( return { completed: true }; } else if (status === "FAILED") { setOnrampStatus("failed"); + throw new Error("Payment failed"); } return { completed: false }; @@ -528,6 +529,16 @@ export function useStepExecutor( onrampCompleted = true; } + // An onramp must complete before any follow-up transactions run or + // success is reported. + if (!onrampCompleted) { + throw new ApiError({ + code: "INTERNAL_SERVER_ERROR", + message: "Onramp did not complete", + statusCode: 500, + }); + } + if (flatTxs.length > 0) { // Then execute transactions if (!wallet) { @@ -640,15 +651,6 @@ export function useStepExecutor( } // All done - check if we actually completed everything if (!abortController.signal.aborted) { - // Only report success for an onramp once it has actually completed. - if (!onrampCompleted) { - throw new ApiError({ - code: "INTERNAL_SERVER_ERROR", - message: "Onramp did not complete", - statusCode: 500, - }); - } - setCurrentTxIndex(undefined); // Call completion callback with all completed status results