Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Sep 29, 2025


PR-Codex overview

This PR focuses on enhancing the x402 payment system by improving token information discovery, updating components for better user experience, and refining payment handling logic.

Detailed summary

  • Improved token info discovery for x402 payments.
  • Updated X402PlaygroundOptions type to include chain, tokenAddress, tokenSymbol, tokenDecimals, amount, and payTo.
  • Changed payment handling in fetchWithPayment.ts to include error handling.
  • Updated constants.ts to use base chain instead of arbitrumSepolia.
  • Enhanced X402Playground component to manage user input and state.
  • Added new schemas for supported payment kinds and assets in schemas.ts.
  • Modified facilitator.ts to support dynamic filters for supported payment kinds.
  • Refined UI components for better user interaction in X402LeftSection and X402RightSection.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Introduced an interactive X402 Playground with configurable chain/token, amount, and pay-to. Includes wallet connect, “Pay” flow, live API response, and client/server code tabs.
  • Improvements

    • Switched default network to Base and aligned USDC to 6 decimals.
    • Auto-fills pay-to from connected wallet.
    • More reliable token info detection for payments and clearer errors when requirements can’t be met.
    • Middleware now computes price from inputs and supports simulated wait.
  • Documentation

    • Simplified X402 page to a single playground experience.

Copy link

vercel bot commented Sep 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs-v2 Ready Ready Preview Comment Sep 29, 2025 2:49am
nebula Ready Ready Preview Comment Sep 29, 2025 2:49am
thirdweb_playground Error Error Sep 29, 2025 2:49am
thirdweb-www Ready Ready Preview Comment Sep 29, 2025 2:49am
wallet-ui Ready Ready Preview Comment Sep 29, 2025 2:49am

Copy link

changeset-bot bot commented Sep 29, 2025

🦋 Changeset detected

Latest commit: 3c6fdd0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch

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

Copy link
Contributor

coderabbitai bot commented Sep 29, 2025

Walkthrough

Adds a new X402 playground UI (left/right sections and container), updates the X402 page to use it, adjusts middleware pricing/defaults, and enhances x402 core types/flows: facilitator “supported” API with filters, default asset detection, token metadata inference, response/error handling, and schema/type updates. Includes a changeset entry.

Changes

Cohort / File(s) Summary
Playground UI components
apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx, .../X402Playground.tsx, .../X402RightSection.tsx, .../constants.ts, .../types.ts, apps/playground-web/src/app/payments/x402/page.tsx
Introduces X402 playground left/right panels and container; moves page to a single playground component; switches default chain to base and sets USDC decimals to 6; adds shared types; token/chain/amount/payTo state wiring and client/server code previews.
Middleware
apps/playground-web/src/middleware.ts
Adds dynamic defaults (amount, token), sets facilitator waitUtil to "simulated", computes price via toUnits, and threads token address/decimals into settlement.
X402 core: facilitator/common/fetch/schemas/types
packages/thirdweb/src/x402/facilitator.ts, .../common.ts, .../fetchWithPayment.ts, .../schemas.ts, .../types.ts
Adds facilitator waitUtil, client field, and filtered supported() API with new response schema/types; enhances default asset and token metadata detection; updates processPriceToAtomicAmount return type to DefaultAsset; improves 402 response parsing and error surfacing; makes ERC20 asset decimals/eip712 optional; introduces SupportedSignatureType schema and DefaultAsset type.
Changeset
.changeset/dirty-experts-kiss.md
Adds patch changeset for “thirdweb” describing improvement to token info discovery for x402 payments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant L as X402LeftSection (UI)
  participant R as X402RightSection (UI)
  participant WF as wrapFetchWithPayment
  participant F as ThirdwebX402Facilitator
  participant S as Paywalled API (middleware)
  participant W as Wallet
  participant C as Chain

  U->>L: Select chain/token/amount/payTo
  L->>R: Options (chain, tokenAddress, decimals, amount, payTo)
  U->>R: Click "Pay"
  R->>WF: fetch(url, opts) wrapped with payment
  WF->>S: Initial request (no payment)
  S-->>WF: 402 response (requirements or error)
  WF->>F: supported({ chainId, tokenAddress? })
  F-->>WF: Supported kinds + default/supported assets
  WF->>W: Request signature/tx per selected requirement
  W->>C: Submit on-chain approval/permit/transfer
  C-->>W: Submitted/confirmed (per waitUtil)
  WF->>S: Retry with X-402 payment headers
  S-->>WF: 200 OK + JSON
  WF-->>R: Response data or error
  R-->>U: Show result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jnsdls

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description retains the placeholder template comments and includes a high-level PR-Codex overview but fails to replace the template with the required “Notes for the reviewer” and “How to test” sections, and it does not supply an issue tag or clear testing instructions. Please remove the unused template comments and populate the standard template sections by adding the relevant issue tag, a clear “Notes for the reviewer” section, and a “How to test” section with concrete steps for validation and testing.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title “[SDK] Improve token info discovery for x402 payments” succinctly and accurately conveys the main enhancement by specifying the SDK context and the core functionality change in a clear, single sentence.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Improve_token_info_discovery_for_x402_payments

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added Playground Changes involving the Playground codebase. packages SDK Involves changes to the thirdweb SDK labels Sep 29, 2025
@joaquim-verges joaquim-verges changed the title Improve token info discovery for x402 payments [SDK] Improve token info discovery for x402 payments Sep 29, 2025
@joaquim-verges joaquim-verges marked this pull request as ready for review September 29, 2025 02:32
@joaquim-verges joaquim-verges requested review from a team as code owners September 29, 2025 02:32
Copy link
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

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.

Copy link
Contributor

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 64.69 KB (0%) 1.3 s (0%) 109 ms (+297.6% 🔺) 1.5 s
thirdweb (cjs) 361.52 KB (0%) 7.3 s (0%) 313 ms (+25.44% 🔺) 7.6 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 58 ms (+2216.67% 🔺) 173 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 15 ms (+699.26% 🔺) 25 ms
thirdweb/react (minimal + tree-shaking) 19.14 KB (0%) 383 ms (0%) 56 ms (+4514.29% 🔺) 439 ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/playground-web/src/app/payments/x402/components/X402Playground.tsx (1)

19-44: Add explicit return type for X402Playground

Line 19 currently declares export function X402Playground() without a return type, but our TypeScript guideline calls for explicit return types. Please annotate the component accordingly.

Apply this diff:

-export function X402Playground() {
+export function X402Playground(): React.JSX.Element {
apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx (1)

14-153: Add explicit return type for X402LeftSection

Line 14 defines export function X402LeftSection(...) without a return type. To stay consistent with our TypeScript rules, please specify the component’s return type.

Use this update:

-export function X402LeftSection(props: {
+export function X402LeftSection(props: {
   options: X402PlaygroundOptions;
   setOptions: React.Dispatch<React.SetStateAction<X402PlaygroundOptions>>;
-}) {
+}): React.JSX.Element {
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between e2931df and 3c6fdd0.

📒 Files selected for processing (13)
  • .changeset/dirty-experts-kiss.md (1 hunks)
  • apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/app/payments/x402/components/X402Playground.tsx (1 hunks)
  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx (1 hunks)
  • apps/playground-web/src/app/payments/x402/components/constants.ts (1 hunks)
  • apps/playground-web/src/app/payments/x402/components/types.ts (1 hunks)
  • apps/playground-web/src/app/payments/x402/page.tsx (2 hunks)
  • apps/playground-web/src/middleware.ts (3 hunks)
  • packages/thirdweb/src/x402/common.ts (5 hunks)
  • packages/thirdweb/src/x402/facilitator.ts (7 hunks)
  • packages/thirdweb/src/x402/fetchWithPayment.ts (2 hunks)
  • packages/thirdweb/src/x402/schemas.ts (2 hunks)
  • packages/thirdweb/src/x402/types.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose

**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from @/types where applicable
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size

Files:

  • apps/playground-web/src/app/payments/x402/components/X402Playground.tsx
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/facilitator.ts
  • apps/playground-web/src/app/payments/x402/components/constants.ts
  • apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/payments/x402/page.tsx
  • apps/playground-web/src/middleware.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/schemas.ts
  • apps/playground-web/src/app/payments/x402/components/types.ts
  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
  • packages/thirdweb/src/x402/types.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)

Files:

  • apps/playground-web/src/app/payments/x402/components/X402Playground.tsx
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/facilitator.ts
  • apps/playground-web/src/app/payments/x402/components/constants.ts
  • apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/payments/x402/page.tsx
  • apps/playground-web/src/middleware.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/schemas.ts
  • apps/playground-web/src/app/payments/x402/components/types.ts
  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
  • packages/thirdweb/src/x402/types.ts
apps/{dashboard,playground-web}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Use NavLink for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Use cn() from @/lib/utils for conditional class logic
Use design system tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components (Node edge): Start files with import "server-only";
Client Components (browser): Begin files with 'use client';
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header – never embed tokens in URLs
Return typed results (e.g., Project[], User[]) – avoid any
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys for React Query cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components

Files:

  • apps/playground-web/src/app/payments/x402/components/X402Playground.tsx
  • apps/playground-web/src/app/payments/x402/components/constants.ts
  • apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/payments/x402/page.tsx
  • apps/playground-web/src/middleware.ts
  • apps/playground-web/src/app/payments/x402/components/types.ts
  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
packages/thirdweb/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

packages/thirdweb/**/*.{ts,tsx}: Every public symbol must have comprehensive TSDoc with at least one compiling @example and a custom tag (@beta, @internal, @experimental, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Lazy‑load heavy dependencies inside async paths (e.g., const { jsPDF } = await import("jspdf"))

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/facilitator.ts
  • packages/thirdweb/src/x402/common.ts
  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/types.ts
**/types.ts

📄 CodeRabbit inference engine (AGENTS.md)

Provide and re‑use local type barrels in a types.ts file

Files:

  • apps/playground-web/src/app/payments/x402/components/types.ts
  • packages/thirdweb/src/x402/types.ts
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

.changeset/*.md: Each change in packages/* must include a changeset for the appropriate package
Version bump rules: patch for non‑API changes; minor for new/modified public API

Files:

  • .changeset/dirty-experts-kiss.md
🧠 Learnings (7)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).

Applied to files:

  • apps/playground-web/src/app/payments/x402/components/X402Playground.tsx
  • apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/payments/x402/page.tsx
  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
PR: thirdweb-dev/js#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/x402/facilitator.ts
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to apps/{dashboard,playground}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/_` (e.g., Button, Input, Tabs, Card)

Applied to files:

  • apps/playground-web/src/app/payments/x402/page.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to packages/thirdweb/src/wallets/** : EIP-1193, EIP-5792, EIP-7702 standard support in wallet modules

Applied to files:

  • packages/thirdweb/src/x402/schemas.ts
  • packages/thirdweb/src/x402/types.ts
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.

Applied to files:

  • apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
📚 Learning: 2025-08-29T15:37:38.513Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:37:38.513Z
Learning: Applies to .changeset/*.md : Version bump rules: patch for non‑API changes; minor for new/modified public API

Applied to files:

  • .changeset/dirty-experts-kiss.md
📚 Learning: 2025-06-26T19:46:04.024Z
Learnt from: gregfromstl
PR: thirdweb-dev/js#7450
File: packages/thirdweb/src/bridge/Webhook.ts:57-81
Timestamp: 2025-06-26T19:46:04.024Z
Learning: In the onramp webhook schema (`packages/thirdweb/src/bridge/Webhook.ts`), the `currencyAmount` field is intentionally typed as `z.number()` while other amount fields use `z.string()` because `currencyAmount` represents fiat currency amounts in decimals (like $10.50), whereas other amount fields represent token amounts in wei (very large integers that benefit from bigint representation). The different naming convention (`currencyAmount` vs `amount`) reflects this intentional distinction.

Applied to files:

  • packages/thirdweb/src/x402/types.ts
🧬 Code graph analysis (8)
apps/playground-web/src/app/payments/x402/components/X402Playground.tsx (4)
apps/playground-web/src/app/payments/x402/components/types.ts (1)
  • X402PlaygroundOptions (4-11)
apps/playground-web/src/app/payments/x402/components/constants.ts (2)
  • chain (6-6)
  • token (7-10)
apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx (1)
  • X402LeftSection (14-153)
apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx (1)
  • X402RightSection (24-268)
packages/thirdweb/src/x402/facilitator.ts (1)
packages/thirdweb/src/x402/schemas.ts (1)
  • FacilitatorSupportedResponse (94-96)
apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx (3)
apps/playground-web/src/app/payments/x402/components/types.ts (1)
  • X402PlaygroundOptions (4-11)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
apps/playground-web/src/app/payments/components/LeftSection.tsx (1)
  • LeftSection (21-492)
apps/playground-web/src/app/payments/x402/page.tsx (1)
apps/playground-web/src/app/payments/x402/components/X402Playground.tsx (1)
  • X402Playground (19-44)
apps/playground-web/src/middleware.ts (2)
apps/playground-web/src/app/payments/x402/components/constants.ts (1)
  • token (7-10)
packages/thirdweb/src/x402/settle-payment.ts (1)
  • settlePayment (126-187)
packages/thirdweb/src/x402/common.ts (2)
packages/thirdweb/src/x402/types.ts (3)
  • DefaultAsset (109-109)
  • ERC20TokenAmount (96-107)
  • SupportedSignatureType (92-94)
packages/thirdweb/src/x402/facilitator.ts (3)
  • facilitator (89-244)
  • supported (204-240)
  • ThirdwebX402Facilitator (24-46)
apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx (3)
apps/playground-web/src/app/payments/x402/components/types.ts (1)
  • X402PlaygroundOptions (4-11)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
  • wrapFetchWithPayment (51-138)
apps/playground-web/src/lib/client.ts (1)
  • THIRDWEB_CLIENT (20-43)
packages/thirdweb/src/x402/types.ts (1)
packages/thirdweb/src/x402/schemas.ts (2)
  • SupportedSignatureTypeSchema (60-63)
  • FacilitatorSupportedAssetSchema (65-73)
⏰ 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). (7)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript)

Comment on lines +62 to +67
setOptions((v) => ({
...v,
tokenAddress: token.address as `0x${string}`,
tokenSymbol: token.symbol || "",
tokenDecimals: token.decimals || 18,
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix decimals fallback to handle zero-decimal tokens

Line 66 uses token.decimals || 18, which incorrectly overwrites legitimate zero-decimal tokens with 18 and inflates the on-chain amount by 10^18. This breaks payments for any 0-decimal asset (e.g., certain governance tokens). Please switch to a nullish check so that 0 is preserved.

Apply this diff:

-      tokenDecimals: token.decimals || 18,
+      tokenDecimals: token.decimals ?? 18,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setOptions((v) => ({
...v,
tokenAddress: token.address as `0x${string}`,
tokenSymbol: token.symbol || "",
tokenDecimals: token.decimals || 18,
}));
setOptions((v) => ({
...v,
tokenAddress: token.address as `0x${string}`,
tokenSymbol: token.symbol || "",
tokenDecimals: token.decimals ?? 18,
}));
🤖 Prompt for AI Agents
In apps/playground-web/src/app/payments/x402/components/X402LeftSection.tsx
around lines 62 to 67, the fallback uses `token.decimals || 18` which overwrites
legitimate 0 values; change the fallback to a nullish check so zero is preserved
(e.g., set tokenDecimals to `token.decimals ?? 18` or otherwise explicitly check
for null/undefined) ensuring the decimals remain a number and 0 is not replaced
by 18.

Comment on lines +108 to +115
payTo: ${props.options.payTo},
network: defineChain(${props.options.chain.id}),
price: {
amount: "${Number(props.options.amount) * 10 ** props.options.tokenDecimals}",
asset: {
address: ${props.options.tokenAddress},
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix server code interpolation and amount conversion

Rendering the snippet as-is drops the quotes around payTo/address, so the preview outputs payTo: 0xabc... which is invalid JavaScript and will throw immediately when copied. On top of that, coercing amount with Number(...) * 10 ** ... will emit scientific notation (e.g. 1e+24) for larger values, which breaks settlePayment. The snippet already imports toUnits, so we should rely on it and keep the interpolated values quoted.

-    payTo: ${props.options.payTo},
+    payTo: "${props.options.payTo}",
@@
-        amount: "${Number(props.options.amount) * 10 ** props.options.tokenDecimals}",
+        amount: toUnits("${props.options.amount}", ${props.options.tokenDecimals}).toString(),
@@
-        address: ${props.options.tokenAddress},
+        address: "${props.options.tokenAddress}",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
payTo: ${props.options.payTo},
network: defineChain(${props.options.chain.id}),
price: {
amount: "${Number(props.options.amount) * 10 ** props.options.tokenDecimals}",
asset: {
address: ${props.options.tokenAddress},
},
},
payTo: "${props.options.payTo}",
network: defineChain(${props.options.chain.id}),
price: {
amount: toUnits("${props.options.amount}", ${props.options.tokenDecimals}).toString(),
asset: {
address: "${props.options.tokenAddress}",
},
},
🤖 Prompt for AI Agents
In apps/playground-web/src/app/payments/x402/components/X402RightSection.tsx
around lines 108 to 115, the generated snippet interpolates payTo and token
address without quotes and converts amount using Number(...) * 10 ** ..., which
yields invalid JS and scientific notation; update the template so payTo and
token address are rendered as quoted strings, and replace the manual numeric
conversion with the existing toUnits(props.options.amount,
props.options.tokenDecimals) call (stringified) to produce a safe integer string
for amount; keep defineChain(${props.options.chain.id}) as-is and ensure the
final amount value is inserted as a quoted string.

Comment on lines +41 to +60
const amount = queryParams.get("amount") || "0.01";
const tokenAddress = queryParams.get("tokenAddress") || token.address;
const decimals = queryParams.get("decimals") || token.decimals.toString();

const result = await settlePayment({
resourceUrl,
method,
paymentData,
payTo: payTo as `0x${string}`,
network: defineChain(Number(chainId)),
price: "$0.01",
// price: {
// amount: toUnits(amount as string, parseInt(decimals as string)).toString(),
// asset: {
// address: tokenAddress as `0x${string}`,
// decimals: decimals ? parseInt(decimals) : token.decimals,
// eip712: {
// name: token.name,
// version: token.version,
// },
// },
// },
price: {
amount: toUnits(
amount as string,
parseInt(decimals as string),
).toString(),
asset: {
address: tokenAddress as `0x${string}`,
decimals: decimals ? parseInt(decimals) : token.decimals,
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate decimals before computing price units.

Line 52 now calls toUnits with parseInt(decimals as string), but we never confirm the query param is actually numeric. If a client sends a malformed decimals (or even an empty string), parseInt yields NaN, toUnits throws, and the middleware responds with a 500 instead of a controlled 4xx. Please validate the parsed value and use the safe number when constructing the price payload.

Apply this diff to harden the handler:

-  const amount = queryParams.get("amount") || "0.01";
-  const tokenAddress = queryParams.get("tokenAddress") || token.address;
-  const decimals = queryParams.get("decimals") || token.decimals.toString();
+  const amount = queryParams.get("amount") || "0.01";
+  const tokenAddress = queryParams.get("tokenAddress") || token.address;
+  const decimalsParam = queryParams.get("decimals");
+  const decimals =
+    decimalsParam === null
+      ? token.decimals
+      : Number.parseInt(decimalsParam, 10);
+
+  if (!Number.isInteger(decimals) || decimals < 0) {
+    return NextResponse.json(
+      { error: "Invalid token decimals parameter" },
+      { status: 400 },
+    );
+  }
@@
-    price: {
-      amount: toUnits(
-        amount as string,
-        parseInt(decimals as string),
-      ).toString(),
-      asset: {
-        address: tokenAddress as `0x${string}`,
-        decimals: decimals ? parseInt(decimals) : token.decimals,
-      },
-    },
+    price: {
+      amount: toUnits(amount, decimals).toString(),
+      asset: {
+        address: tokenAddress as `0x${string}`,
+        decimals,
+      },
+    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const amount = queryParams.get("amount") || "0.01";
const tokenAddress = queryParams.get("tokenAddress") || token.address;
const decimals = queryParams.get("decimals") || token.decimals.toString();
const result = await settlePayment({
resourceUrl,
method,
paymentData,
payTo: payTo as `0x${string}`,
network: defineChain(Number(chainId)),
price: "$0.01",
// price: {
// amount: toUnits(amount as string, parseInt(decimals as string)).toString(),
// asset: {
// address: tokenAddress as `0x${string}`,
// decimals: decimals ? parseInt(decimals) : token.decimals,
// eip712: {
// name: token.name,
// version: token.version,
// },
// },
// },
price: {
amount: toUnits(
amount as string,
parseInt(decimals as string),
).toString(),
asset: {
address: tokenAddress as `0x${string}`,
decimals: decimals ? parseInt(decimals) : token.decimals,
},
},
const amount = queryParams.get("amount") || "0.01";
const tokenAddress = queryParams.get("tokenAddress") || token.address;
const decimalsParam = queryParams.get("decimals");
const decimals =
decimalsParam === null
? token.decimals
: Number.parseInt(decimalsParam, 10);
if (!Number.isInteger(decimals) || decimals < 0) {
return NextResponse.json(
{ error: "Invalid token decimals parameter" },
{ status: 400 },
);
}
const result = await settlePayment({
resourceUrl,
method,
paymentData,
payTo: payTo as `0x${string}`,
network: defineChain(Number(chainId)),
price: {
amount: toUnits(amount, decimals).toString(),
asset: {
address: tokenAddress as `0x${string}`,
decimals,
},
},
🤖 Prompt for AI Agents
In apps/playground-web/src/middleware.ts around lines 41 to 60, the handler
calls toUnits(parseInt(decimals as string)) without validating the query param;
if decimals is non-numeric this produces NaN and crashes with a 500. Parse the
decimals param to a number, check Number.isFinite/!Number.isNaN and that it's an
integer within a valid range (e.g. 0–36); if invalid, return a 400 response
explaining the invalid decimals; otherwise use the parsed safeNumber for both
toUnits and the asset.decimals field (falling back to token.decimals when the
param is missing). Ensure no direct call to toUnits with a potentially NaN
value.

Copy link

codecov bot commented Sep 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.28%. Comparing base (e2931df) to head (3c6fdd0).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8142      +/-   ##
==========================================
- Coverage   56.28%   56.28%   -0.01%     
==========================================
  Files         906      906              
  Lines       59208    59208              
  Branches     4180     4178       -2     
==========================================
- Hits        33324    33323       -1     
- Misses      25780    25781       +1     
  Partials      104      104              
Flag Coverage Δ
packages 56.28% <ø> (-0.01%) ⬇️
see 5 files with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
packages Playground Changes involving the Playground codebase. SDK Involves changes to the thirdweb SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant