-
Notifications
You must be signed in to change notification settings - Fork 627
[SDK] Add useFetchWithPayment React hook for x402 payment handling #8444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SDK] Add useFetchWithPayment React hook for x402 payment handling #8444
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 32a243c The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
WalkthroughAdds a platform-agnostic fetch-with-payment feature: core hook (useFetchWithPaymentCore), web/native hooks (useFetchWithPayment), web UI modals (PaymentErrorModal, SignInRequiredModal), updated wrapFetchWithPayment options signature, export barrel updates, documentation/examples, and small UI/tsdoc tweaks. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant App as React App
participant Hook as useFetchWithPayment (web)
participant Core as useFetchWithPaymentCore
participant Wrap as wrapFetchWithPayment
participant Wallet as ConnectFlow
participant API as Server
participant Modal as PaymentErrorModal
User->>App: trigger fetchWithPayment(url)
App->>Hook: fetchWithPayment(url)
Hook->>Core: delegate to core
Core->>Wrap: perform fetch(url)
Wrap->>API: HTTP request
API-->>Wrap: 402 Payment Required
alt wallet not connected
Core->>Hook: request showConnectModal
Hook->>Wallet: show SignInRequiredModal -> ConnectWallet
User->>Wallet: connect wallet
Wallet-->>Core: wallet available
end
Core->>Hook: showErrorModal with errorData
Hook->>Modal: show PaymentErrorModal
alt insufficient funds
User->>Modal: Top up (BuyWidget)
Modal-->>Core: onRetry
else Try again
User->>Modal: Try again
Modal-->>Core: onRetry
end
Core->>Wrap: retry with payment headers
Wrap->>API: retry request
API-->>Wrap: 200 OK + data
Wrap-->>Core: return parsed response
Core-->>Hook: return data/state
Hook-->>App: update UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (9)
.changeset/wet-maps-play.md (1)
20-43: Clarify whetherfetchWithPaymentreturns aResponseor parsed data.The basic usage example treats
fetchWithPaymentas returning aResponse(await response.json()), but the core hook exposes aparseAsoption and the playground uses thedatafield as already‑parsed content. Please confirm the intended contract and update this snippet (and optionally mentionparseAs) so docs and behavior stay in sync.apps/playground-web/src/app/x402/components/X402RightSection.tsx (1)
55-74: Align client code sample with actualfetchWithPaymentcontract.In this snippet you treat
fetchWithPaymentas returning aResponse(await response.json()), while the UI below relies on the hook’sdatafield for parsed content. Once you confirm whetherfetchWithPaymentreturns aResponseor parsed body, it’d be good to update this sample (and the changeset docs) to use the same pattern.packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (1)
34-67: Verify nativefetchWithPaymentreturn type and update examples accordingly.The examples here also assume
fetchWithPaymentreturns aResponsethat you then.json(). If the core hook instead returns parsed data based onparseAs, the examples should showconst data = await fetchWithPayment(...)(or just rely on thedatafield) rather than chainingresponse.json().packages/thirdweb/src/x402/fetchWithPayment.ts (1)
23-27: Update JSDoc to match the newoptionsparam and max-value behavior.The documentation still refers to a positional
maxValueargument with a default of 1 USDC, but the implementation now takes anoptionsobject and only enforcesoptions.maxValuewhen provided. Please:
- Update the
@paramdocs to describeoptions.maxValueandoptions.paymentRequirementsSelector.- Decide explicitly whether a default max should still exist and, if so, enforce it either here or in the core hook.
Also applies to: 55-60, 101-107
packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2)
22-49: Consider exporting the full config type for consumers.
UseFetchWithPaymentConfigadds UI-focused options (theme,fundWalletOptions,connectOptions,showErrorModal) on top ofUseFetchWithPaymentOptions, but only the latter is exported. If you expect callers to share this shape (e.g. typed config objects), exportingUseFetchWithPaymentConfig(or a similarly named public type) would avoid local redefinitions.
142-216: Confirm modal-toggle semantics and harden the connect flow.Two small points:
showErrorModalcurrently gates both the error modal and the sign‑in/connect modal. If the intent is “no UI at all when false,” that’s fine but worth making explicit in docs; otherwise consider a separate flag for the connect modal.- If
useConnectModal().connectcan resolve without a wallet (e.g.null),data.onConnect(connectedWallet)would get an invalid value. A quick guard (if (!connectedWallet) { data.onCancel(); return; }) would make this more robust.packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (2)
67-107: Validateasset→tokenAddressassumptions for BuyWidget
tokenAddressis derived viaselectedRequirement.asset as \0x${string}` without any runtime check. This is fine ifRequestedPaymentRequirements["asset"]is guaranteed to always be an EVM0xtoken address for the supported"exact"scheme, but will break if:
assetcan be"native"or another sentinel, or- non‑EVM networks / other asset formats are later introduced.
If those cases are possible now or in the near future, consider guarding here (e.g. only returning a config when
assetmatches an EVM address pattern and the chain is EVM, otherwisereturn nulland fall back to the plain error UI).
228-257: Avoid duplicating payment requirement selection logic
defaultPaymentRequirementsSelectorintentionally mirrors the logic infetchWithPayment.ts. Keeping two copies in sync is easy to forget and could cause inconsistent behavior between the core payment flow and the UI flow if one side evolves (e.g. supporting additional schemes or selection heuristics).Consider extracting this selector into a shared helper in the x402 module (or exporting the existing implementation) so both
wrapFetchWithPaymentandPaymentErrorModalreuse the same function.packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
133-159: Consider tighteningparseAstyping for better DXCurrently
parseResponsereturnsunknown | string | Responsewhile the hook’s mutation result is typed asunknown, which forces callers to manually narrow the type even whenparseAsis known at call‑site.In a follow‑up, you could:
- Make
useFetchWithPaymentCoregeneric on the response type (e.g.<T = unknown>whenparseAs: "json"), and/or- Use a discriminated union on
parseAssofetchWithPaymentcan inferPromise<any> | Promise<string> | Promise<Response>accordingly.Not urgent, but it would make the hook more ergonomic for downstream consumers.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
.changeset/wet-maps-play.md(1 hunks)apps/playground-web/src/app/x402/components/X402RightSection.tsx(4 hunks)packages/thirdweb/src/exports/react.native.ts(1 hunks)packages/thirdweb/src/exports/react.ts(1 hunks)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts(1 hunks)packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts(1 hunks)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx(1 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/thirdweb/src/exports/react.native.tspackages/thirdweb/src/exports/react.ts.changeset/wet-maps-play.md
📚 Learning: 2025-10-03T23:36:00.631Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8181
File: packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx:27-27
Timestamp: 2025-10-03T23:36:00.631Z
Learning: In packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx, the component intentionally uses a hardcoded English locale (connectLocaleEn) rather than reading from the provider, as BuyWidget is designed to be English-only and does not require internationalization support.
Applied to files:
packages/thirdweb/src/exports/react.native.tspackages/thirdweb/src/exports/react.tsapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Applied to files:
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/playground-web/src/app/x402/components/X402RightSection.tsx.changeset/wet-maps-play.mdpackages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
📚 Learning: 2025-09-17T11:02:13.528Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/use-tokens.ts:15-17
Timestamp: 2025-09-17T11:02:13.528Z
Learning: The thirdweb `client` object is serializable and can safely be used in React Query keys, similar to the `contract` object.
Applied to files:
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-05-27T19:54:55.885Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7177
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx:15-17
Timestamp: 2025-05-27T19:54:55.885Z
Learning: The `fetchDashboardContractMetadata` function from "3rdweb-sdk/react/hooks/useDashboardContractMetadata" has internal error handlers for all promises and cannot throw errors, so external error handling is not needed when calling this function.
Applied to files:
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts.changeset/wet-maps-play.md
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-09-23T19:56:43.668Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx:482-485
Timestamp: 2025-09-23T19:56:43.668Z
Learning: In packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx, the EmbedContainer uses width: "100vw" intentionally rather than "100%" - this is by design for the bridge widget embedding use case.
Applied to files:
packages/thirdweb/src/exports/react.tsapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx.changeset/wet-maps-play.mdpackages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-09-17T11:14:35.659Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx:919-930
Timestamp: 2025-09-17T11:14:35.659Z
Learning: In the thirdweb codebase, useCustomTheme() hook can be used inside styled-components callbacks, contrary to the general React Rules of Hooks. This is a valid pattern in their implementation.
Applied to files:
.changeset/wet-maps-play.md
📚 Learning: 2025-09-24T11:08:43.783Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx:34-41
Timestamp: 2025-09-24T11:08:43.783Z
Learning: In BridgeWidgetProps for packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx, the Swap onError callback signature requires a non-undefined SwapPreparedQuote parameter (unlike Buy's onError which allows undefined quote). This is intentional - SwapWidget's onError is only called when a quote is available.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
🧬 Code graph analysis (4)
packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx (4)
packages/thirdweb/src/react/web/ui/components/Modal.tsx (1)
Modal(32-173)packages/thirdweb/src/react/web/ui/components/basic.tsx (2)
Container(80-193)ScreenBottomContainer(14-23)packages/thirdweb/src/react/core/design-system/index.ts (1)
spacing(142-154)packages/thirdweb/src/react/web/ui/components/text.tsx (1)
Text(18-34)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
packages/thirdweb/src/x402/schemas.ts (1)
RequestedPaymentRequirements(37-39)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
wrapFetchWithPayment(51-156)
packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (4)
packages/thirdweb/src/exports/react.ts (3)
BuyWidgetProps(144-144)UseConnectModalOptions(179-179)useFetchWithPayment(137-137)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
useFetchWithPaymentCore(35-148)packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (1)
PaymentErrorModal(53-226)packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx (1)
SignInRequiredModal(23-75)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Unit Tests
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Lint Packages
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Size
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
packages/thirdweb/src/exports/react.native.ts (1)
117-121: RN x402 exports look consistent and correct.Re-exporting
useFetchWithPaymentandUseFetchWithPaymentOptionsfrom the native hook matches the web pattern and cleanly surfaces the API onthirdweb/react.native.packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx (1)
14-75: Sign-in modal wiring and behavior look solid.Theme handling, header/title, and close behavior (including overlay/close icon via
setOpen→onCancel) all look correct; actions cleanly delegate toonSignIn/onCancel.packages/thirdweb/src/exports/react.ts (1)
134-138: Web x402 exports are wired correctly.The new export block cleanly surfaces
useFetchWithPaymentand its options type from the web hook and aligns with the existing export structure.
size-limit report 📦
|
8403241 to
3038bb7
Compare
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
23-32: DefaultmaxValueis no longer enforced and docs are now misleadingThe implementation now only enforces a limit when
options?.maxValueis explicitly provided:
- The previous
maxValueparameter (documented as “defaults to 1 USDC”) is gone.options?.maxValueis optional and, if omitted, theif (options?.maxValue && …)guard simply never runs, so there’s effectively no client-side cap.- The JSDoc still documents
@param maxValueand a default of 1 USDC, but the function no longer has such a parameter nor a default.This is both a behavioral change (potentially allowing much larger payments than before) and a doc/contract drift.
I’d strongly recommend either:
- Restoring an actual default cap in code, e.g. by computing an
effectiveMaxValue(const maxValue = options?.maxValue ?? DEFAULT_MAX_VALUE;) and checking against that, or- Explicitly deciding that “no cap by default” is the new behavior and updating the JSDoc (and any higher‑level docs like the React hooks) to remove references to a default 1 USDC limit and describe
options.maxValueinstead of a standalonemaxValueparameter.Right now, callers relying on the documented default safeguard may be surprised by the new behavior.
Also applies to: 49-49, 55-60, 101-107
apps/portal/src/app/wallets/server/send-transactions/page.mdx (1)
77-83: Endpoint reference text and example path are inconsistentThe narrative text still links to:
/reference#tag/contracts/post/v1/contracts/{chainId}/{address}/writeBut the
OpenApiEndpointdirectly below now uses:
path="/v1/contracts/write"These should describe the same endpoint. Please update the text link (or the example path if that’s the one that’s wrong) so they match and avoid confusing readers about the correct HTTP path.
apps/playground-web/src/app/x402/components/X402RightSection.tsx (1)
67-111: Probable typo in server code sample:waitUtilvswaitUntilIn the
serverCodetemplate you have:const thirdwebFacilitator = facilitator({ client, serverWalletAddress: "0xYourServerWalletAddress", waitUtil: "${props.options.waitUntil}", });Everywhere else in this file (e.g. search params) the field is named
waitUntil, so this is likely a typo in the example and could confuse users copying the snippet.Consider updating the sample:
- waitUtil: "${props.options.waitUntil}", + waitUntil: "${props.options.waitUntil}",
♻️ Duplicate comments (1)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
89-123: 402 error handling still downgrades detailed errors to a generic messageBecause the
try { ... } catch (_parseError)around the 402 branch currently wraps bothresponse.json()and the subsequent “no modal”throw new Error(...), any thrown error gets caught and replaced with the generic"Payment failed with status 402":
- In the no‑modal path, the detailed error (
errorBody.errorMessage || \Payment failed: ${errorBody.error}``) is never surfaced.- Any synchronous error thrown while preparing the modal data would also be masked.
Refactor to only catch JSON‑parse failures, and let the detailed error propagate, e.g.:
- if (response.status === 402) { - try { - const errorBody = - (await response.json()) as PaymentRequiredResult["responseBody"]; - - // If a modal handler is provided, show the modal and handle retry/cancel - if (showErrorModal) { - return new Promise<unknown>((resolve, reject) => { - showErrorModal({ - errorData: errorBody, - onRetry: async () => { - // Retry the entire fetch+error handling logic recursively - try { - const result = await executeFetch(); - resolve(result); - } catch (error) { - reject(error); - } - }, - onCancel: () => { - reject(new Error("Payment cancelled by user")); - }, - }); - }); - } - - // If no modal handler, throw the error with details - throw new Error( - errorBody.errorMessage || `Payment failed: ${errorBody.error}`, - ); - } catch (_parseError) { - // If we can't parse the error body, throw a generic error - throw new Error("Payment failed with status 402"); - } - } + if (response.status === 402) { + let errorBody: PaymentRequiredResult["responseBody"]; + try { + errorBody = (await response.json()) as PaymentRequiredResult["responseBody"]; + } catch { + // If we can't parse the error body, throw a generic error + throw new Error("Payment failed with status 402"); + } + + // If a modal handler is provided, show the modal and handle retry/cancel + if (showErrorModal) { + return new Promise<unknown>((resolve, reject) => { + showErrorModal({ + errorData: errorBody, + onRetry: async () => { + // Retry the entire fetch+error handling logic recursively + try { + const result = await executeFetch(); + resolve(result); + } catch (error) { + reject(error); + } + }, + onCancel: () => { + reject(new Error("Payment cancelled by user")); + }, + }); + }); + } + + // If no modal handler, throw the error with details + throw new Error( + errorBody.errorMessage || `Payment failed: ${errorBody.error}`, + ); + }This preserves informative server feedback while still handling malformed JSON responses gracefully.
🧹 Nitpick comments (3)
packages/thirdweb/tsdoc.json (1)
8-8: Move "@x402" to maintain alphabetical ordering in supportForTags.The "@x402" tag is inserted after "@bridge" but should be placed near the end of the supportForTags list (after "@walletUtils") to maintain alphabetical order, since tags starting with 'x' come after those starting with 'w'.
Apply this diff to reorder the supportForTags:
"supportForTags": { "@account": true, "@auth": true, "@beta": true, "@bridge": true, - "@x402": true, "@chain": true, "@client": true, "@component": true, "@connectWallet": true, "@contract": true, "@engine": true, "@extension": true, "@insight": true, "@locale": true, "@modules": true, "@nebula": true, "@nft": true, "@rpc": true, "@social": true, "@storage": true, "@theme": true, "@transaction": true, "@utils": true, "@wallet": true, "@walletConfig": true, "@walletConnection": true, "@walletUtils": true, + "@x402": true },.changeset/wet-maps-play.md (1)
1-111: Examples match the hook API; consider minor clarity tweaksThe option names and usage in these snippets (
parseAs,maxValue,paymentRequirementsSelector,fundWalletOptions,connectOptions,showErrorModal) line up with theuseFetchWithPaymentconfig type. To make the snippets more copy‑paste friendly, you could optionally add the missing imports in the customized connect example (e.g.inAppWallet,createWallet) or a brief note that they’re assumed to be imported elsewhere.packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (1)
81-88: Consider removing unused error parameter.The
errorData.errorvalue is passed todefaultPaymentRequirementsSelector(line 87), but the function parameter is prefixed with_indicating it's unused (line 233). If there's no plan to use this parameter, consider removing it to simplify the function signature.const selectedRequirement = paymentRequirementsSelector ? paymentRequirementsSelector(parsedPaymentRequirements) : defaultPaymentRequirementsSelector( parsedPaymentRequirements, currentChainId, "exact", - errorData.error, );function defaultPaymentRequirementsSelector( paymentRequirements: RequestedPaymentRequirements[], chainId: number | undefined, scheme: "exact", - _error?: string, ): RequestedPaymentRequirements | undefined {Also applies to: 228-234
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (18)
.changeset/wet-maps-play.md(1 hunks)apps/playground-web/src/app/x402/components/X402RightSection.tsx(4 hunks)apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts(2 hunks)apps/portal/src/app/wallets/server/send-transactions/page.mdx(1 hunks)apps/portal/src/app/x402/client/page.mdx(3 hunks)apps/portal/src/app/x402/page.mdx(2 hunks)packages/thirdweb/src/exports/react.native.ts(1 hunks)packages/thirdweb/src/exports/react.ts(1 hunks)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts(1 hunks)packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts(1 hunks)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx(1 hunks)packages/thirdweb/src/x402/facilitator.ts(1 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts(3 hunks)packages/thirdweb/src/x402/settle-payment.ts(1 hunks)packages/thirdweb/src/x402/verify-payment.ts(1 hunks)packages/thirdweb/tsdoc.json(2 hunks)
✅ Files skipped from review due to trivial changes (2)
- packages/thirdweb/src/x402/facilitator.ts
- packages/thirdweb/src/x402/settle-payment.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/thirdweb/src/exports/react.ts
- packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
📚 Learning: 2025-10-01T22:32:18.080Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8169
File: packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx:95-107
Timestamp: 2025-10-01T22:32:18.080Z
Learning: In the thirdweb-dev/js codebase, specifically for React components in packages/thirdweb/src/react/**/*.{ts,tsx} files, do not suggest adding TSDoc blocks to function components. The project maintainer MananTank has explicitly declined these suggestions.
Applied to files:
packages/thirdweb/tsdoc.json
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/thirdweb/src/exports/react.native.ts.changeset/wet-maps-play.mdapps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/page.mdxpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-10-03T23:36:00.631Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8181
File: packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx:27-27
Timestamp: 2025-10-03T23:36:00.631Z
Learning: In packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx, the component intentionally uses a hardcoded English locale (connectLocaleEn) rather than reading from the provider, as BuyWidget is designed to be English-only and does not require internationalization support.
Applied to files:
packages/thirdweb/src/exports/react.native.tspackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-09-17T11:14:35.659Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx:919-930
Timestamp: 2025-09-17T11:14:35.659Z
Learning: In the thirdweb codebase, useCustomTheme() hook can be used inside styled-components callbacks, contrary to the general React Rules of Hooks. This is a valid pattern in their implementation.
Applied to files:
.changeset/wet-maps-play.md
📚 Learning: 2025-05-27T19:54:55.885Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7177
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx:15-17
Timestamp: 2025-05-27T19:54:55.885Z
Learning: The `fetchDashboardContractMetadata` function from "3rdweb-sdk/react/hooks/useDashboardContractMetadata" has internal error handlers for all promises and cannot throw errors, so external error handling is not needed when calling this function.
Applied to files:
.changeset/wet-maps-play.mdpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
Applied to files:
.changeset/wet-maps-play.mdapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.tsapps/portal/src/app/x402/page.mdx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Applied to files:
.changeset/wet-maps-play.mdapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsxpackages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.tsapps/portal/src/app/x402/page.mdxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-09-17T11:02:13.528Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/use-tokens.ts:15-17
Timestamp: 2025-09-17T11:02:13.528Z
Learning: The thirdweb `client` object is serializable and can safely be used in React Query keys, similar to the `contract` object.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/page.mdxpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-09-23T19:56:43.668Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx:482-485
Timestamp: 2025-09-23T19:56:43.668Z
Learning: In packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx, the EmbedContainer uses width: "100vw" intentionally rather than "100%" - this is by design for the bridge widget embedding use case.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-09-24T11:08:43.783Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx:34-41
Timestamp: 2025-09-24T11:08:43.783Z
Learning: In BridgeWidgetProps for packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx, the Swap onError callback signature requires a non-undefined SwapPreparedQuote parameter (unlike Buy's onError which allows undefined quote). This is intentional - SwapWidget's onError is only called when a quote is available.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/portal/src/app/x402/page.mdx
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
Repo: thirdweb-dev/js PR: 7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.
Applied to files:
apps/portal/src/app/x402/client/page.mdx
🧬 Code graph analysis (3)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (2)
packages/thirdweb/src/exports/thirdweb.ts (1)
ThirdwebClient(25-25)packages/thirdweb/src/x402/fetchWithPayment.ts (1)
wrapFetchWithPayment(51-156)
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (7)
packages/thirdweb/src/exports/react.native.ts (1)
Theme(6-6)packages/thirdweb/src/exports/react.ts (2)
Theme(6-6)BuyWidget(143-143)packages/thirdweb/src/x402/schemas.ts (2)
networkToCaip2ChainId(183-188)extractEvmChainId(104-111)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain(79-89)packages/thirdweb/src/react/web/ui/components/Modal.tsx (1)
Modal(32-173)packages/thirdweb/src/react/web/ui/components/basic.tsx (3)
Container(80-193)ModalHeader(36-68)ScreenBottomContainer(14-23)packages/thirdweb/src/react/web/ui/components/text.tsx (1)
Text(18-34)
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (5)
packages/thirdweb/src/exports/react.native.ts (2)
useFetchWithPayment(120-120)UseFetchWithPaymentOptions(119-119)packages/thirdweb/src/exports/react.ts (2)
useFetchWithPayment(137-137)UseFetchWithPaymentOptions(136-136)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2)
useFetchWithPayment(163-238)UseFetchWithPaymentOptions(20-20)packages/thirdweb/src/exports/thirdweb.ts (1)
ThirdwebClient(25-25)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (2)
UseFetchWithPaymentOptions(11-17)useFetchWithPaymentCore(35-148)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Unit Tests
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (12)
packages/thirdweb/src/x402/verify-payment.ts (1)
73-73: LGTM! Documentation tag updated for consistency across all x402 APIs.The JSDoc tag change from
@bridge x402to@x402is part of a complete standardization effort. Verification confirms all x402-related files use the consistent@x402tag, with no remaining old tags.packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
35-87: Core hook structure and mutation wiring look solidThe recursive
executeFetchflow, connect‑modal integration, delegation towrapFetchWithPayment, andparseAs‑based response handling are coherent and align well with React Query’s mutation API. Aside from the 402 error‑handling concern above, the rest of the hook reads clean and maintainable.Also applies to: 126-160
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (1)
90-96: Native wrapper correctly delegates to the core hookThe hook signature and return shape are thin, accurate wrappers over
useFetchWithPaymentCore, which is exactly what you want on the native side (no modal wiring here, errors bubble up). This looks good.apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts (1)
10-17:@x402tag wiring into tagsToGroup + sidebarGroupOrder looks correctYou’ve added
@x402to bothtagsToGroupandsidebarGroupOrder, so x402 docs will group correctly and won’t trip the safety check that throws when a tag lacks an ordering. Placement before@bridgealso makes sense for discoverability.Also applies to: 53-69
packages/thirdweb/src/exports/react.native.ts (1)
117-121: Native x402 hook export matches the new web surfaceThe new
useFetchWithPayment+UseFetchWithPaymentOptionsexport looks consistent with the rest of the barrel and keeps native in sync with web. No issues from an API-surface perspective.apps/portal/src/app/x402/page.mdx (1)
1-98: Tabbed client-side docs align with the new hook APIThe new Tabs-based split between TypeScript (
wrapFetchWithPayment) and React (useFetchWithPayment) reads clearly and matches the exported hook signature (useFetchWithPayment(client)). The examples are coherent and consistent with the server-side section below.apps/playground-web/src/app/x402/components/X402RightSection.tsx (1)
28-45: Hook wiring and playground UI state handling look solid
useFetchWithPayment(THIRDWEB_CLIENT)is integrated cleanly:handlePayClickbuilds the paywall URL, the main CTA is disabled viaisPending, and the response card shows loading, error, and pretty‑printed JSON fromdata. This is a good end‑to‑end playground for the new flow.Also applies to: 184-208
apps/portal/src/app/x402/client/page.mdx (2)
2-2: LGTM!The React tab integration follows the established pattern for multi-language documentation.
Also applies to: 30-33
71-73: Documentation accurately matches implementation—no changes needed.The
wrapFetchWithPaymentfunction signature correctly implements the documentedoptionsobject structure withmaxValueandpaymentRequirementsSelectoras optional properties. Verification confirms the implementation atpackages/thirdweb/src/x402/fetchWithPayment.ts:51-60aligns with the documentation.packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (3)
1-46: LGTM!The type definitions are well-structured. The
fundWalletOptionstype properly excludes the props that will be controlled by the component while allowing customization of other BuyWidget options.
109-153: LGTM!The buy-widget modal flow is well-implemented with proper guards (
isInsufficientFunds && buyWidgetConfig), clear user messaging, and appropriate callback handling for success and cancel actions.
156-226: LGTM!The error screen provides clear user feedback with contextual messaging for insufficient funds vs. generic payment errors, and appropriate action buttons for each scenario.
3038bb7 to
c5c1789
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx (2)
232-261:defaultPaymentRequirementsSelectorcomment overstates equivalence with core selector.This helper claims “same logic as in fetchWithPayment.ts”, but it:
- Accepts
chainId: number | undefinedinstead of requirednumber- Returns
undefinedon emptypaymentRequirementsinstead of throwing- Skips chain matching entirely and falls back to the first matching
schemewhenchainIdis undefinedEither:
- Update the comment to describe these UI‑specific, more defensive differences, or
- Share a single implementation with the core selector (same signature and behavior) and reuse it here.
#!/bin/bash # Compare selector behavior between core and UI implementations rg -n "defaultPaymentRequirementsSelector" packages/thirdweb/src/x402/fetchWithPayment.ts packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx -A10 -B5
67-105: ValidateselectedRequirement.assetbefore casting to an EVM address.
selectedRequirement.assetis blindly asserted to0x${string}and passed toBuyWidget. If the asset is a non‑EVM identifier, native token marker, or malformed string, this can surface as a runtime issue in the buy flow.Add a small validator before the cast and bail out when the asset is not a proper 20‑byte hex address so the BuyWidget isn't misconfigured:
- const chain = getCachedChain(chainId); - const tokenAddress = selectedRequirement.asset as `0x${string}`; - - return { - chain, - tokenAddress, - amount: undefined, - }; + const chain = getCachedChain(chainId); + + const asset = selectedRequirement.asset; + if ( + typeof asset !== "string" || + !/^0x[a-fA-F0-9]{40}$/.test(asset) + ) { + // Non-EVM or invalid asset; skip BuyWidget config + return null; + } + + const tokenAddress = asset as `0x${string}`; + + return { + chain, + tokenAddress, + amount: undefined, + };#!/bin/bash # Inspect the RequestedPaymentRequirements schema to confirm asset typing rg -n "asset" packages/thirdweb/src/x402/schemas.ts -A3 -B3 # Check BuyWidgetProps tokenAddress expectations rg -n "tokenAddress" packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx -A3 -B3apps/portal/src/app/x402/client/page.mdx (1)
71-74:maxValuedocs still claim a default that the implementation does not apply.The React
useFetchWithPaymentparameters still say:
maxValue- Maximum allowed payment amount in base units (defaults to 1 USDC = 1,000,000)but
wrapFetchWithPaymentand the core options only gate onoptions?.maxValueand never set a default value. This will mislead users into assuming a cap exists when it does not.Please update the description to state that
maxValueis optional and has no default (just an upper bound when explicitly provided). The rest of the React section (example, features, other options, reference link) looks consistent with the hook behavior.#!/bin/bash # Confirm that maxValue is only honored when explicitly provided rg -n "maxValue" packages/thirdweb/src/x402/fetchWithPayment.ts packages/thirdweb/src/react -A3 -B3 # Check that the reference page for useFetchWithPayment exists in the portal fd -t f "useFetchWithPayment" apps/portal/src/app/references || echo "No reference file found"Also applies to: 81-133
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
90-123: 402 handling still downgrades detailed errors to a generic message.Because the detailed
throw new Error(errorBody.errorMessage || \Payment failed: ${errorBody.error}`)is inside the sametryasresponse.json(), it is immediately caught by thecatch (_parseError)and replaced with"Payment failed with status 402". This means callers never see server‑provided details whenshowErrorModal` is not used.Refactor so the
try/catchonly wraps JSON parsing, and keep the detailed throw outside:- if (response.status === 402) { - try { - const errorBody = - (await response.json()) as PaymentRequiredResult["responseBody"]; - - // If a modal handler is provided, show the modal and handle retry/cancel - if (showErrorModal) { - return new Promise<unknown>((resolve, reject) => { - showErrorModal({ - errorData: errorBody, - onRetry: async () => { - // Retry the entire fetch+error handling logic recursively - try { - const result = await executeFetch(); - resolve(result); - } catch (error) { - reject(error); - } - }, - onCancel: () => { - reject(new Error("Payment cancelled by user")); - }, - }); - }); - } - - // If no modal handler, throw the error with details - throw new Error( - errorBody.errorMessage || `Payment failed: ${errorBody.error}`, - ); - } catch (_parseError) { - // If we can't parse the error body, throw a generic error - throw new Error("Payment failed with status 402"); - } - } + if (response.status === 402) { + let errorBody: PaymentRequiredResult["responseBody"]; + try { + errorBody = + (await response.json()) as PaymentRequiredResult["responseBody"]; + } catch { + // If we can't parse the error body, throw a generic error + throw new Error("Payment failed with status 402"); + } + + // If a modal handler is provided, show the modal and handle retry/cancel + if (showErrorModal) { + return new Promise<unknown>((resolve, reject) => { + showErrorModal({ + errorData: errorBody, + onRetry: async () => { + // Retry the entire fetch+error handling logic recursively + try { + const result = await executeFetch(); + resolve(result); + } catch (error) { + reject(error); + } + }, + onCancel: () => { + reject(new Error("Payment cancelled by user")); + }, + }); + }); + } + + // If no modal handler, throw the error with details + throw new Error( + errorBody.errorMessage || `Payment failed: ${errorBody.error}`, + ); + }In JavaScript/TypeScript, does a `throw` inside a `try` block always route to the corresponding `catch` (unless rethrown), thereby preventing that error from escaping past the `catch`?
🧹 Nitpick comments (2)
packages/thirdweb/tsdoc.json (1)
8-8: Move @x402 to maintain alphabetical order in supportForTags.The new "@x402" tag is inserted after "@bridge" (line 8), but based on the alphabetical ordering of other tags in this array, it should be placed at the end (after "@walletUtils") to maintain consistency.
Apply this change:
"supportForTags": { "@account": true, "@auth": true, "@beta": true, "@bridge": true, - "@x402": true, "@chain": true, "@client": true, "@component": true, "@connectWallet": true, "@contract": true, "@engine": true, "@extension": true, "@insight": true, "@locale": true, "@modules": true, "@nebula": true, "@nft": true, "@rpc": true, "@social": true, "@storage": true, "@theme": true, "@transaction": true, "@utils": true, "@wallet": true, "@walletConfig": true, "@walletConnection": true, "@walletUtils": true, + "@x402": true },packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx (1)
23-74: Well-scoped, theme-aware sign-in modal; consider future-proofing copyThe component is wired cleanly: theme is correctly scoped via
CustomThemeProvider, callbacks (onSignIn,onCancel, overlay/escape viasetOpen) behave as expected, and layout matches existing modal patterns.If you expect reuse or localization later, consider accepting the title/body copy as props (or at least hoisting
"Sign in required"into a shared constant) to avoid duplicated, hard-coded strings in the UI surface.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (19)
.changeset/wet-maps-play.md(1 hunks)apps/playground-web/src/app/x402/components/X402RightSection.tsx(4 hunks)apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts(2 hunks)apps/portal/src/app/wallets/server/send-transactions/page.mdx(1 hunks)apps/portal/src/app/x402/client/page.mdx(3 hunks)apps/portal/src/app/x402/page.mdx(2 hunks)packages/thirdweb/src/exports/react.native.ts(1 hunks)packages/thirdweb/src/exports/react.ts(1 hunks)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts(1 hunks)packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts(1 hunks)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx(1 hunks)packages/thirdweb/src/react/web/ui/components/basic.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx(1 hunks)packages/thirdweb/src/x402/facilitator.ts(1 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts(3 hunks)packages/thirdweb/src/x402/settle-payment.ts(1 hunks)packages/thirdweb/src/x402/verify-payment.ts(1 hunks)packages/thirdweb/tsdoc.json(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/thirdweb/src/x402/settle-payment.ts
🚧 Files skipped from review as they are similar to previous changes (10)
- apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts
- packages/thirdweb/src/x402/facilitator.ts
- apps/portal/src/app/wallets/server/send-transactions/page.mdx
- packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
- packages/thirdweb/src/x402/fetchWithPayment.ts
- .changeset/wet-maps-play.md
- packages/thirdweb/src/exports/react.native.ts
- packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
- packages/thirdweb/src/x402/verify-payment.ts
- apps/portal/src/app/x402/page.mdx
🧰 Additional context used
🧠 Learnings (19)
📓 Common learnings
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
packages/thirdweb/src/exports/react.tsapps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-10-03T23:36:00.631Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8181
File: packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx:27-27
Timestamp: 2025-10-03T23:36:00.631Z
Learning: In packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx, the component intentionally uses a hardcoded English locale (connectLocaleEn) rather than reading from the provider, as BuyWidget is designed to be English-only and does not require internationalization support.
Applied to files:
packages/thirdweb/src/exports/react.tspackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-09-23T19:56:43.668Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx:482-485
Timestamp: 2025-09-23T19:56:43.668Z
Learning: In packages/thirdweb/src/react/web/ui/ConnectWallet/Modal/ConnectEmbed.tsx, the EmbedContainer uses width: "100vw" intentionally rather than "100%" - this is by design for the bridge widget embedding use case.
Applied to files:
packages/thirdweb/src/exports/react.tspackages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsxpackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsxpackages/thirdweb/src/react/web/ui/components/basic.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsxapps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-09-17T11:02:13.528Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/use-tokens.ts:15-17
Timestamp: 2025-09-17T11:02:13.528Z
Learning: The thirdweb `client` object is serializable and can safely be used in React Query keys, similar to the `contract` object.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-09-24T11:08:43.783Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8106
File: packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx:34-41
Timestamp: 2025-09-24T11:08:43.783Z
Learning: In BridgeWidgetProps for packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx, the Swap onError callback signature requires a non-undefined SwapPreparedQuote parameter (unlike Buy's onError which allows undefined quote). This is intentional - SwapWidget's onError is only called when a quote is available.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-07-10T10:18:33.238Z
Learnt from: arcoraven
Repo: thirdweb-dev/js PR: 7505
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/analytics/components/WebhookAnalyticsCharts.tsx:186-204
Timestamp: 2025-07-10T10:18:33.238Z
Learning: The ThirdwebBarChart component in apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/analytics/components/WebhookAnalyticsCharts.tsx does not accept standard accessibility props like `aria-label` and `role` in its TypeScript interface, causing compilation errors when added.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-08-28T12:24:37.171Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7933
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/distribution/token-sale.tsx:0-0
Timestamp: 2025-08-28T12:24:37.171Z
Learning: In the token creation flow, the tokenAddress field in erc20Asset_poolMode is always initialized with nativeTokenAddress and is never undefined, so conditional checks for undefined tokenAddress are not needed.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-05-27T19:55:25.056Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7177
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/_hooks/useTokenPriceData.ts:49-49
Timestamp: 2025-05-27T19:55:25.056Z
Learning: In the ERC20 public pages token price data hook (`useTokenPriceData.ts`), direct array access on `json.data[0]` without optional chaining is intentionally correct and should not be changed to use safety checks.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-10-17T22:59:11.867Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8278
File: apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx:144-144
Timestamp: 2025-10-17T22:59:11.867Z
Learning: The BuyAndSwapEmbed component in apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx cannot be used in testnets, so the hardcoded `is_testnet: false` in asset reporting calls is intentional and correct.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-10-16T19:00:34.707Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 8267
File: packages/thirdweb/src/extensions/erc20/read/getCurrencyMetadata.ts:47-52
Timestamp: 2025-10-16T19:00:34.707Z
Learning: In the thirdweb SDK's getCurrencyMetadata function (packages/thirdweb/src/extensions/erc20/read/getCurrencyMetadata.ts), zero decimals is not a valid value for native currency. If `options.contract.chain.nativeCurrency.decimals` is 0, it should be treated as missing/invalid data and trigger an API fetch to get the correct native currency metadata.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsxapps/portal/src/app/x402/client/page.mdx
📚 Learning: 2025-06-10T00:50:20.795Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7315
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx:153-226
Timestamp: 2025-06-10T00:50:20.795Z
Learning: In apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/nft/launch-nft.tsx, the updateStatus function correctly expects a complete MultiStepState["status"] object. For pending states, { type: "pending" } is the entire status object. For error states, { type: "error", message: React.ReactNode } is the entire status object. The current code incorrectly spreads the entire step object instead of passing just the status object.
Applied to files:
packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
📚 Learning: 2025-08-29T23:44:47.512Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7951
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx:38-38
Timestamp: 2025-08-29T23:44:47.512Z
Learning: The ContractPageLayout component in apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx is not the root layout - it's nested within the dashboard layout which already handles footer positioning with min-h-dvh and AppFooter placement. The ContractPageLayout needs flex flex-col grow to properly participate in the parent's flex layout.
Applied to files:
packages/thirdweb/src/react/web/ui/components/basic.tsx
📚 Learning: 2025-10-01T22:32:18.080Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8169
File: packages/thirdweb/src/react/web/ui/Bridge/FundWallet.tsx:95-107
Timestamp: 2025-10-01T22:32:18.080Z
Learning: In the thirdweb-dev/js codebase, specifically for React components in packages/thirdweb/src/react/**/*.{ts,tsx} files, do not suggest adding TSDoc blocks to function components. The project maintainer MananTank has explicitly declined these suggestions.
Applied to files:
packages/thirdweb/src/react/web/ui/components/basic.tsxpackages/thirdweb/tsdoc.json
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
Repo: thirdweb-dev/js PR: 7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.
Applied to files:
apps/portal/src/app/x402/client/page.mdx
🧬 Code graph analysis (3)
packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx (4)
packages/thirdweb/src/react/web/ui/components/Modal.tsx (1)
Modal(32-173)packages/thirdweb/src/react/web/ui/components/basic.tsx (2)
Container(80-193)ModalHeader(36-68)packages/thirdweb/src/react/core/design-system/index.ts (1)
spacing(142-154)packages/thirdweb/src/react/web/ui/components/text.tsx (1)
Text(18-34)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
wrapFetchWithPayment(51-156)
packages/thirdweb/src/react/web/ui/components/basic.tsx (1)
packages/thirdweb/src/react/core/design-system/index.ts (1)
spacing(142-154)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Build Packages
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (11)
packages/thirdweb/tsdoc.json (1)
137-140: Tag definition structure is correct.The new "@x402" tag definition follows the proper structure and is appropriately placed at the end of the tagDefinitions array.
packages/thirdweb/src/react/web/ui/components/basic.tsx (1)
14-23: Verify visual impact on existing wallet connection flows.The spacing reduction from
spacing.lg(24px) tospacing.md(16px) affects existing components beyond the new x402 UI. While the new x402 modals (PaymentErrorModal,SignInRequiredModal) are designed for this spacing, two existing wallet connection flows will be visually impacted:
- ScanScreen.tsx (line 158): Optional "Get Started" button section
- WalletSelector.tsx (lines 293, 478): Guest option and "new to wallets" sections
Manually verify these components in the development environment to ensure the 8px spacing reduction doesn't negatively impact the wallet connection UX.
apps/playground-web/src/app/x402/components/X402RightSection.tsx (6)
8-8: LGTM! Clean migration to the new payment hook.The import change correctly reflects the new
useFetchWithPaymentAPI, replacing the previouswrapFetchWithPaymentapproach.
28-29: Well-structured hook usage.The
useFetchWithPaymenthook initialization correctly provides both the fetch function and state management, streamlining the payment flow.
47-65: Excellent documentation example.The client code example demonstrates the dual usage pattern effectively: capturing the return value directly from
fetchWithPayment(lines 56-57) while also showing proper loading state management withisPending. This flexibility gives developers options for different use cases.
188-188: Proper loading state handling.Disabling the button during payment processing prevents duplicate submissions and provides good user feedback.
202-208: Comprehensive response state handling.The rendering logic properly covers all states (loading, error, success) and provides clear feedback for each, enhancing the user experience.
31-45: Verified:/api/paywallendpoint correctly handles all payment parameters.The endpoint exists and properly extracts, validates, and uses all six query parameters being sent from the component:
chainId,payTo,amount,tokenAddress,decimals, andwaitUntil. Parameter types and defaults align with the implementation.packages/thirdweb/src/exports/react.ts (1)
134-138: Expose x402 React hook from the main React barrel (LGTM).Re-exporting
UseFetchWithPaymentOptionsanduseFetchWithPaymenthere is consistent with other web hooks and matches the documented"thirdweb/react"import path.apps/portal/src/app/x402/client/page.mdx (1)
2-2: React tab wiring and icons look consistent.The new React tab trigger and
ReactIconimport integrate cleanly with the existing Tabs setup; no changes needed.Also applies to: 30-33
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
11-18: Core hook structure and response parsing look solid.The core hook cleanly composes
wrapFetchWithPayment, wallet connection flow, and response parsing (parseAs+parseResponse), and the mutation API surface (fetchWithPaymentplus spreadmutation) matches typical React Query patterns.Also applies to: 43-88, 126-160
c5c1789 to
32a243c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
90-124: 402 error handling still swallows detailed error messages.The
throw new Error(...)at lines 117-119 is inside the sametryblock asresponse.json(), so thecatchat line 120 catches both JSON parse failures and the intentional throw, replacing the detailed message with a generic"Payment failed with status 402".Refactor to isolate the JSON parse from the error throw:
if (response.status === 402) { + let errorBody: PaymentRequiredResult["responseBody"]; try { - const errorBody = - (await response.json()) as PaymentRequiredResult["responseBody"]; - - // If a modal handler is provided, show the modal and handle retry/cancel - if (showErrorModal) { - return new Promise<unknown>((resolve, reject) => { - showErrorModal({ - errorData: errorBody, - onRetry: async () => { - // Retry the entire fetch+error handling logic recursively - try { - const result = await executeFetch(); - resolve(result); - } catch (error) { - reject(error); - } - }, - onCancel: () => { - reject(new Error("Payment cancelled by user")); - }, - }); - }); - } - - // If no modal handler, throw the error with details - throw new Error( - errorBody.errorMessage || `Payment failed: ${errorBody.error}`, - ); - } catch (_parseError) { - // If we can't parse the error body, throw a generic error + errorBody = (await response.json()) as PaymentRequiredResult["responseBody"]; + } catch { throw new Error("Payment failed with status 402"); } + + if (showErrorModal) { + return new Promise<unknown>((resolve, reject) => { + showErrorModal({ + errorData: errorBody, + onRetry: async () => { + try { + const result = await executeFetch(); + resolve(result); + } catch (error) { + reject(error); + } + }, + onCancel: () => { + reject(new Error("Payment cancelled by user")); + }, + }); + }); + } + + throw new Error( + errorBody.errorMessage || `Payment failed: ${errorBody.error}`, + ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (19)
.changeset/wet-maps-play.md(1 hunks)apps/playground-web/src/app/x402/components/X402RightSection.tsx(4 hunks)apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts(2 hunks)apps/portal/src/app/wallets/server/send-transactions/page.mdx(1 hunks)apps/portal/src/app/x402/client/page.mdx(3 hunks)apps/portal/src/app/x402/page.mdx(2 hunks)packages/thirdweb/src/exports/react.native.ts(1 hunks)packages/thirdweb/src/exports/react.ts(1 hunks)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts(1 hunks)packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts(1 hunks)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx(1 hunks)packages/thirdweb/src/react/web/ui/components/basic.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx(1 hunks)packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx(1 hunks)packages/thirdweb/src/x402/facilitator.ts(1 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts(4 hunks)packages/thirdweb/src/x402/settle-payment.ts(1 hunks)packages/thirdweb/src/x402/verify-payment.ts(1 hunks)packages/thirdweb/tsdoc.json(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
- packages/thirdweb/tsdoc.json
- apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts
- packages/thirdweb/src/x402/verify-payment.ts
- packages/thirdweb/src/x402/facilitator.ts
- packages/thirdweb/src/exports/react.ts
- packages/thirdweb/src/react/web/ui/x402/PaymentErrorModal.tsx
- packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
- packages/thirdweb/src/x402/fetchWithPayment.ts
- packages/thirdweb/src/react/web/ui/x402/SignInRequiredModal.tsx
- apps/portal/src/app/x402/client/page.mdx
- packages/thirdweb/src/react/web/ui/components/basic.tsx
- packages/thirdweb/src/exports/react.native.ts
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
📚 Learning: 2025-09-17T11:14:35.659Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/swap-ui.tsx:919-930
Timestamp: 2025-09-17T11:14:35.659Z
Learning: In the thirdweb codebase, useCustomTheme() hook can be used inside styled-components callbacks, contrary to the general React Rules of Hooks. This is a valid pattern in their implementation.
Applied to files:
.changeset/wet-maps-play.md
📚 Learning: 2025-05-27T19:54:55.885Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7177
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx:15-17
Timestamp: 2025-05-27T19:54:55.885Z
Learning: The `fetchDashboardContractMetadata` function from "3rdweb-sdk/react/hooks/useDashboardContractMetadata" has internal error handlers for all promises and cannot throw errors, so external error handling is not needed when calling this function.
Applied to files:
.changeset/wet-maps-play.mdpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout PR #7888.
Applied to files:
.changeset/wet-maps-play.mdapps/playground-web/src/app/x402/components/X402RightSection.tsxapps/portal/src/app/x402/page.mdx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 7888
File: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/payments/page.tsx:77-81
Timestamp: 2025-08-20T10:35:18.543Z
Learning: The webhooks/payments route exists at apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/payments/page.tsx and was added as part of the unified project layout changes.
Applied to files:
.changeset/wet-maps-play.mdpackages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.tsapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/portal/src/app/x402/page.mdx
📚 Learning: 2025-06-17T18:30:52.976Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7356
File: apps/nebula/src/app/not-found.tsx:1-1
Timestamp: 2025-06-17T18:30:52.976Z
Learning: In the thirdweb/js project, the React namespace is available for type annotations (like React.FC) without needing to explicitly import React. This is project-specific configuration that differs from typical TypeScript/React setups.
Applied to files:
.changeset/wet-maps-play.mdapps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/portal/src/app/x402/page.mdx
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-09-17T11:02:13.528Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 8044
File: packages/thirdweb/src/react/web/ui/Bridge/swap-widget/use-tokens.ts:15-17
Timestamp: 2025-09-17T11:02:13.528Z
Learning: The thirdweb `client` object is serializable and can safely be used in React Query keys, similar to the `contract` object.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsxpackages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.tsapps/portal/src/app/x402/page.mdx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
apps/playground-web/src/app/x402/components/X402RightSection.tsx
📚 Learning: 2025-10-16T19:00:34.707Z
Learnt from: jnsdls
Repo: thirdweb-dev/js PR: 8267
File: packages/thirdweb/src/extensions/erc20/read/getCurrencyMetadata.ts:47-52
Timestamp: 2025-10-16T19:00:34.707Z
Learning: In the thirdweb SDK's getCurrencyMetadata function (packages/thirdweb/src/extensions/erc20/read/getCurrencyMetadata.ts), zero decimals is not a valid value for native currency. If `options.contract.chain.nativeCurrency.decimals` is 0, it should be treated as missing/invalid data and trigger an API fetch to get the correct native currency metadata.
Applied to files:
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
📚 Learning: 2025-08-07T17:24:31.965Z
Learnt from: MananTank
Repo: thirdweb-dev/js PR: 7812
File: apps/dashboard/src/app/(app)/team/~/~project/[[...paths]]/page.tsx:1-11
Timestamp: 2025-08-07T17:24:31.965Z
Learning: In Next.js App Router, page components (page.tsx files) are server components by default and do not require the "server-only" import directive. The "server-only" directive is primarily used for utility functions, API helpers, and data access modules that should never be included in the client bundle.
Applied to files:
apps/portal/src/app/x402/page.mdx
🧬 Code graph analysis (2)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
wrapFetchWithPayment(51-156)
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (3)
packages/thirdweb/src/exports/react.native.ts (2)
useFetchWithPayment(120-120)UseFetchWithPaymentOptions(119-119)packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2)
useFetchWithPayment(163-238)UseFetchWithPaymentOptions(20-20)packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (2)
UseFetchWithPaymentOptions(11-17)useFetchWithPaymentCore(35-148)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Unit Tests
- GitHub Check: Lint Packages
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (13)
packages/thirdweb/src/x402/settle-payment.ts (1)
125-125: LGTM!The JSDoc tag update from
@bridge x402to@x402aligns with the broader documentation standardization in this PR.packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (4)
11-17: LGTM!The
UseFetchWithPaymentOptionstype is well-structured with appropriate optional fields for payment configuration and response parsing flexibility.
52-78: LGTM!The wallet connection flow is well-designed: it gracefully handles the missing wallet case by either prompting via modal callback or throwing a descriptive error, maintaining platform-agnostic core logic.
126-135: LGTM!Good error handling for non-402 failures with detailed status information, and proper delegation to the
parseResponsehelper.
150-160: LGTM!Clean helper function with appropriate handling for all documented
parseAsoptions.packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (2)
27-37: LGTM!The documentation correctly describes
maxValueas optional without claiming a non-existent default value. The past review concern has been addressed.
90-96: LGTM!Clean delegation to the core hook with appropriate documentation noting that React Native doesn't include modal UI and errors should be handled by the application.
apps/portal/src/app/x402/page.mdx (2)
1-1: LGTM!Appropriate imports added to support the new tabbed documentation UI.
36-98: LGTM!The tabbed documentation structure provides clear separation between TypeScript and React usage patterns. The examples accurately demonstrate the new
useFetchWithPaymenthook API andwrapFetchWithPaymentfunction..changeset/wet-maps-play.md (1)
1-111: LGTM!Comprehensive changeset documentation covering all features and customization options of the new
useFetchWithPaymenthook. The examples are clear and consistent with the implementation.apps/playground-web/src/app/x402/components/X402RightSection.tsx (3)
28-45: LGTM!Clean refactor to use the new
useFetchWithPaymenthook. ThehandlePayClickfunction is simplified and demonstrates the intended usage pattern well.
47-65: LGTM!The client code example accurately demonstrates the
useFetchWithPaymenthook usage pattern for playground visitors.
202-208: LGTM!The UI properly handles all states from the
useFetchWithPaymenthook: loading, error, and success with data display.
| - For user wallets in TypeScript applications, you can get it by calling `wallet.getAuthToken()` on a connected [`inAppWallet()`](/references/typescript/v5/inAppWallet) or [`ecosystemWallet()`](/references/typescript/v5/ecosystemWallet). | ||
|
|
||
| <OpenApiEndpoint path="/v1/contracts/{chainId}/{address}/write" method="POST" /> | ||
| <OpenApiEndpoint path="/v1/contracts/write" method="POST" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the API endpoint path change.
The endpoint path changed from /v1/contracts/{chainId}/{address}/write to /v1/contracts/write, removing the path parameters. Please confirm this reflects the actual API structure, as it's a significant change to the documented endpoint.
🏁 Script executed:
#!/bin/bash
# Search for other references to this endpoint path in the codebase
rg -n "v1/contracts.*write" --type=md --type=ts --type=tsx -g '!node_modules'Length of output: 27
🏁 Script executed:
#!/bin/bash
# Search for endpoint references - fix file type flag
rg -n "v1/contracts.*write" --type=md --type=ts -g '!node_modules'Length of output: 1042
🏁 Script executed:
#!/bin/bash
# Also search for the old endpoint pattern with path parameters
rg -n "chainId.*address.*write" -g '!node_modules'Length of output: 414
🏁 Script executed:
#!/bin/bash
# Search for the new endpoint pattern
rg -n "/v1/contracts/write" -g '!node_modules'Length of output: 1611
Codecov Report❌ Patch coverage is
❌ Your patch status has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #8444 +/- ##
==========================================
- Coverage 54.83% 54.83% -0.01%
==========================================
Files 919 919
Lines 60868 60868
Branches 4141 4140 -1
==========================================
- Hits 33375 33374 -1
- Misses 27391 27392 +1
Partials 102 102
🚀 New features to boost your workflow:
|

PR-Codex overview
This PR introduces the
x402payment protocol across the codebase, enhancing payment handling by integrating new functionalities and updating existing methods to support this protocol, including UI improvements for error handling and wallet connection.Detailed summary
@bridge x402to@x402in multiple files.useFetchWithPaymentReact hook for handling payments with automatic UI for errors and wallet connections.SignInRequiredModalfor prompting user sign-in.PaymentErrorModalfor displaying payment errors and options to fund wallets.wrapFetchWithPaymentto accept customizable options for payment handling.@x402.basic.tsx.useFetchWithPaymenthook andwrapFetchWithPayment.Summary by CodeRabbit
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.