-
Notifications
You must be signed in to change notification settings - Fork 588
[SDK] Support ERC-2612 permit for x402 payments #8128
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
Conversation
🦋 Changeset detectedLatest commit: 536f520 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 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 |
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a changeset and ABI updates, removes several MinimalAccount ABI hooks, and extends x402 payment flow with client-aware detection and dual signing (EIP‑2612 permit vs transferWithAuthorization). Exposes facilitator address, adjusts header/nonce handling, updates playground UI/constants and middleware, and fixes dashboard pagination links. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Application
participant Wrap as wrapFetchWithPayment
participant Net as fetch()
participant Header as createPaymentHeader
participant Detect as getSupportedSignatureType
participant Permit as signERC2612Permit
participant Auth as signERC3009Authorization
App->>Wrap: fetch(url, init)
Wrap->>Net: fetch(url, init)
Net-->>Wrap: Response (402)
alt 402 Payment Required
Wrap->>Header: createPaymentHeader(client, account, requirements)
Header->>Detect: getSupportedSignatureType(client, asset, chainId, eip712Extras)
Detect-->>Header: { usePermit?, useTransferWithAuthorization? }
alt usePermit
Header->>Permit: signERC2612Permit(...)
Permit-->>Header: signed permit + nonce
else useTransferWithAuthorization
Header->>Auth: signERC3009Authorization(...) (createNonce())
Auth-->>Header: signed auth + nonce
end
Header-->>Wrap: x402 header (includes nonce)
Wrap->>Net: fetch(url, init + x402 header)
Net-->>Wrap: Response (success/error)
else non-402
Wrap-->>App: original response
end
Wrap-->>App: Response
sequenceDiagram
autonumber
participant Client as ThirdwebClient
participant Detect as getSupportedSignatureType
participant ABI as resolveContractAbi/getContract
participant Sel as toFunctionSelector
Client->>Detect: asset, chainId, eip712Extras
Detect->>ABI: resolveContractAbi/getContract(asset, chain)
ABI-->>Detect: ABI (or none)
Detect->>Sel: compute/check function selectors (permit / transferWithAuthorization)
Sel-->>Detect: support flags
Detect-->>Client: supported signature type
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (2)
Comment |
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. |
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
packages/thirdweb/src/x402/fetchWithPayment.ts (2)
117-124
: Do not send Access-Control-Expose-Headers in requests.This is a response header; setting it on the request has no effect. Remove it to avoid confusion.
Apply this diff:
const newInit = { ...initParams, headers: { ...(initParams.headers || {}), "X-PAYMENT": paymentHeader, - "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE", }, __is402Retry: true, };
12-50
: Add required TSDoc custom tag (e.g., @beta) for public API.Repo guideline requires a custom tag on public symbols.
Apply this diff:
/** * Enables the payment of APIs using the x402 payment protocol. * @@ * @bridge x402 + * @beta */
packages/thirdweb/src/x402/sign.ts (3)
133-148
: Update TSDoc to match new signature and add required tag.Docs still reference x402Version param. Add a custom tag per repo guideline.
Apply this diff:
/** * Creates and encodes a payment header for the given client and payment requirements. * - * @param client - The signer wallet instance used to create the payment header - * @param x402Version - The version of the X402 protocol to use + * @param client - The thirdweb client used for chain/ABI resolution * @param paymentRequirements - The payment requirements containing scheme and network information * @returns A promise that resolves to the encoded payment header string */ export async function createPaymentHeader( client: ThirdwebClient, account: Account, paymentRequirements: RequestedPaymentRequirements, ): Promise<string> { const payment = await signPaymentHeader(client, account, paymentRequirements); return encodePayment(payment); } +/** + * @beta + */
167-214
: Validate domain info (name/version) before signing EIP‑3009; fetch or fail clearly.If extra.name/version are missing, signTypedData may fail or produce invalid payloads. Add validation.
Apply this diff:
async function signERC3009Authorization( @@ - const name = extra?.name; - const version = extra?.version; + const name = extra?.name; + const version = extra?.version; + if (!name || !version) { + throw new Error( + "Missing contract domain: extra.name and extra.version are required for EIP-3009 signing", + ); + } @@ - const signature = await account.signTypedData({ + const signature = await account.signTypedData({ types: {
216-262
: Validate domain info (name/version) before signing EIP‑2612 permit.Same concern as above; also good to keep error messages explicit.
Apply this diff:
async function signERC2612Permit( account: Account, { from, value, validBefore, nonce }: ExactEvmPayloadAuthorization, { asset, network, extra }: RequestedPaymentRequirements, ): Promise<{ signature: Hex }> { const chainId = networkToChainId(network); - const name = extra?.name; - const version = extra?.version; + const name = extra?.name; + const version = extra?.version; + if (!name || !version) { + throw new Error( + "Missing contract domain: extra.name and extra.version are required for EIP-2612 signing", + ); + } const facilitatorAddress = extra?.facilitatorAddress; if (!facilitatorAddress) { throw new Error( "facilitatorAddress is required in PaymentRequirements extra to pay with permit-based assets", ); }
🧹 Nitpick comments (2)
packages/thirdweb/src/x402/sign.ts (2)
59-74
: Correct param docs for signPaymentHeader (client is not a wallet).Minor doc fix to avoid confusion.
Apply this diff:
/** * Signs a payment header using the provided client and payment requirements. * - * @param client - The signer wallet instance used to sign the payment header + * @param client - ThirdwebClient used for chain/ABI resolution * @param paymentRequirements - The payment requirements containing scheme and network information * @param unsignedPaymentHeader - The unsigned payment payload to be signed * @returns A promise that resolves to the signed payment payload */
269-277
: Safer Node fallback without require; avoid CJS in ESM bundles.Use dynamic import of node:crypto to stay bundler/ESM-friendly.
Apply this diff:
-async function createNonce(): Promise<Hex> { - const cryptoObj = - typeof globalThis.crypto !== "undefined" && - typeof globalThis.crypto.getRandomValues === "function" - ? globalThis.crypto - : // Dynamic require is needed to support node.js - // eslint-disable-next-line @typescript-eslint/no-require-imports - require("crypto").webcrypto; - return toHex(cryptoObj.getRandomValues(new Uint8Array(32))); -} +async function createNonce(): Promise<Hex> { + const hasWebCrypto = + typeof globalThis.crypto !== "undefined" && + typeof globalThis.crypto.getRandomValues === "function"; + const cryptoObj = hasWebCrypto + ? globalThis.crypto + : (await import("node:crypto")).webcrypto; + return toHex(cryptoObj.getRandomValues(new Uint8Array(32))); +}
📜 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 ignored due to path filters (1)
packages/thirdweb/src/extensions/erc20/__generated__/USDC/write/transferWithAuthorization.ts
is excluded by!**/__generated__/**
📒 Files selected for processing (7)
.changeset/giant-suns-drive.md
(1 hunks)packages/thirdweb/scripts/generate/abis/erc20/USDC.json
(1 hunks)packages/thirdweb/scripts/generate/abis/erc7702/MinimalAccount.json
(0 hunks)packages/thirdweb/src/x402/common.ts
(3 hunks)packages/thirdweb/src/x402/facilitator.ts
(1 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts
(2 hunks)packages/thirdweb/src/x402/sign.ts
(9 hunks)
💤 Files with no reviewable changes (1)
- packages/thirdweb/scripts/generate/abis/erc7702/MinimalAccount.json
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{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 localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
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
Prefertype
aliases overinterface
except for nominal shapes
Avoidany
andunknown
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:
packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/x402/fetchWithPayment.ts
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/x402/fetchWithPayment.ts
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
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/facilitator.ts
packages/thirdweb/src/x402/fetchWithPayment.ts
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
.changeset/*.md
📄 CodeRabbit inference engine (AGENTS.md)
.changeset/*.md
: Each change inpackages/*
must include a changeset for the appropriate package
Version bump rules: patch for non‑API changes; minor for new/modified public API
Files:
.changeset/giant-suns-drive.md
🧠 Learnings (3)
📚 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/common.ts
.changeset/giant-suns-drive.md
📚 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 src/extensions/** : Auto-generated contracts from ABI definitions in extensions
Applied to files:
packages/thirdweb/src/x402/common.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 .changeset/*.md : Version bump rules: patch for non‑API changes; minor for new/modified public API
Applied to files:
.changeset/giant-suns-drive.md
🧬 Code graph analysis (3)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
packages/thirdweb/src/exports/thirdweb.ts (1)
ThirdwebClient
(25-25)
packages/thirdweb/src/x402/sign.ts (4)
packages/thirdweb/src/x402/schemas.ts (1)
networkToChainId
(59-80)packages/thirdweb/src/x402/common.ts (1)
detectSupportedAuthorizationMethods
(254-280)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)packages/thirdweb/src/x402/types.ts (1)
x402Version
(17-17)
packages/thirdweb/src/x402/common.ts (3)
packages/thirdweb/src/x402/facilitator.ts (1)
facilitator
(59-197)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)packages/thirdweb/src/extensions/erc20/__generated__/USDC/write/transferWithAuthorization.ts (1)
isTransferWithAuthorizationSupported
(75-82)
⏰ 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: Size
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Build Packages
- GitHub Check: Unit Tests
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
105-109
: LGTM: updated createPaymentHeader call matches new signature.
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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (1)
217-259
: Restore server-wallet pagination routePagination links now drop the
/server-wallets
segment, so clicking “next” sends users to the generic transactions page instead of page N of the server-wallet table. That breaks pagination for this view. Please restore the original route segment (or ensure an equivalent route exists that still renders this table).- href={`/team/${teamSlug}/${project.slug}/transactions?page=${ + href={`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${ currentPage > 1 ? currentPage - 1 : 1 }`} ... - href={`/team/${teamSlug}/${project.slug}/transactions?page=${pageNumber}`} + href={`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${pageNumber}`} ... - href(`/team/${teamSlug}/${project.slug}/transactions?page=${ + href(`/team/${teamSlug}/${project.slug}/transactions/server-wallets?page=${ currentPage < totalPages ? currentPage + 1 : totalPages }`}
📜 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 (9)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
(3 hunks)apps/playground-web/src/app/api/paywall/route.ts
(1 hunks)apps/playground-web/src/app/payments/x402/components/constants.ts
(1 hunks)apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
(4 hunks)apps/playground-web/src/middleware.ts
(2 hunks)packages/thirdweb/src/x402/common.ts
(4 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts
(2 hunks)packages/thirdweb/src/x402/sign.ts
(9 hunks)packages/thirdweb/src/x402/types.ts
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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 localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
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
Prefertype
aliases overinterface
except for nominal shapes
Avoidany
andunknown
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:
packages/thirdweb/src/x402/fetchWithPayment.ts
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
apps/playground-web/src/middleware.ts
packages/thirdweb/src/x402/types.ts
apps/playground-web/src/app/api/paywall/route.ts
apps/playground-web/src/app/payments/x402/components/constants.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/thirdweb/src/x402/fetchWithPayment.ts
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
apps/playground-web/src/middleware.ts
packages/thirdweb/src/x402/types.ts
apps/playground-web/src/app/api/paywall/route.ts
apps/playground-web/src/app/payments/x402/components/constants.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
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/types.ts
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.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
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
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 withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
apps/playground-web/src/middleware.ts
apps/playground-web/src/app/api/paywall/route.ts
apps/playground-web/src/app/payments/x402/components/constants.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/_
(e.g., Button, Input, Tabs, Card)
UseNavLink
for internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()
from@/lib/utils
for conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"
; usenext/headers
, server‑only env, heavy data fetching, andredirect()
where appropriate
Client Components must start with'use client'
; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()
from cookies, sendAuthorization: Bearer <token>
header, and return typed results (avoidany
)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeys
and set sensiblestaleTime/cacheTime
(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-js
in server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
className
prop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
**/types.ts
📄 CodeRabbit inference engine (AGENTS.md)
Provide and re‑use local type barrels in a
types.ts
file
Files:
packages/thirdweb/src/x402/types.ts
🧠 Learnings (13)
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#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/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#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/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.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 apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
📚 Learning: 2025-06-06T23:46:08.795Z
Learnt from: MananTank
PR: thirdweb-dev/js#7298
File: apps/dashboard/src/app/nebula-app/move-funds/move-funds.tsx:424-424
Timestamp: 2025-06-06T23:46:08.795Z
Learning: The thirdweb project has an ESLint rule that restricts direct usage of `defineChain`. When it's necessary to use `defineChain` directly, it's acceptable to disable the rule with `// eslint-disable-next-line no-restricted-syntax`.
Applied to files:
apps/playground-web/src/middleware.ts
apps/playground-web/src/app/payments/x402/components/constants.ts
📚 Learning: 2025-06-06T23:47:55.122Z
Learnt from: MananTank
PR: thirdweb-dev/js#7298
File: apps/dashboard/src/app/nebula-app/move-funds/move-funds.tsx:255-277
Timestamp: 2025-06-06T23:47:55.122Z
Learning: The `transfer` function from `thirdweb/extensions/erc20` accepts human-readable amounts via the `amount` property and automatically handles conversion to base units (wei) by fetching the token decimals internally. Manual conversion using `toWei()` is not required when using the `amount` property.
Applied to files:
packages/thirdweb/src/x402/types.ts
📚 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
📚 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/x402-client-preview.tsx
📚 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/x402-client-preview.tsx
📚 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 : Use React Query (`tanstack/react-query`) for all client data fetching.
Applied to files:
apps/playground-web/src/app/payments/x402/components/x402-client-preview.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/common.ts
📚 Learning: 2025-08-28T12:24:37.171Z
Learnt from: MananTank
PR: thirdweb-dev/js#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/x402/common.ts
📚 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/exports/** : Every public symbol must have comprehensive TSDoc with at least one `example` block that compiles and custom annotation tags (`beta`, `internal`, `experimental`)
Applied to files:
packages/thirdweb/src/x402/common.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 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.)
Applied to files:
packages/thirdweb/src/x402/common.ts
🧬 Code graph analysis (6)
packages/thirdweb/src/x402/fetchWithPayment.ts (2)
packages/thirdweb/src/exports/thirdweb.ts (1)
ThirdwebClient
(25-25)packages/thirdweb/src/x402/types.ts (1)
x402Version
(13-13)
apps/playground-web/src/middleware.ts (1)
packages/thirdweb/src/x402/settle-payment.ts (1)
settlePayment
(126-187)
apps/playground-web/src/app/payments/x402/components/constants.ts (1)
packages/thirdweb/src/exports/chains.ts (1)
arbitrumSepolia
(12-12)
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx (1)
apps/playground-web/src/app/payments/x402/components/constants.ts (2)
chain
(6-6)token
(7-7)
packages/thirdweb/src/x402/sign.ts (4)
packages/thirdweb/src/x402/schemas.ts (2)
RequestedPaymentRequirements
(48-50)RequestedPaymentPayload
(33-35)packages/thirdweb/src/x402/types.ts (1)
ERC20TokenAmount
(89-100)packages/thirdweb/src/x402/common.ts (1)
detectSupportedAuthorizationMethods
(255-299)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)
packages/thirdweb/src/x402/common.ts (4)
packages/thirdweb/src/x402/facilitator.ts (1)
facilitator
(59-197)packages/thirdweb/src/x402/types.ts (1)
ERC20TokenAmount
(89-100)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)packages/thirdweb/src/extensions/erc20/__generated__/USDC/write/transferWithAuthorization.ts (1)
isTransferWithAuthorizationSupported
(75-82)
⏰ 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, vite)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: Lint Packages
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
packages/thirdweb/src/x402/common.ts (2)
118-121
: Guardextra
spread from undefinedLine 118 spreads
(asset as ERC20TokenAmount["asset"]).eip712
directly. When the facilitator (or caller) omits EIP‑712 metadata—which is still the default for most supported assets—eip712
isundefined
, and spreading it throwsTypeError: Cannot convert undefined or null to object
, breaking every payment request on runtime. Please guard with a nullish fallback before spreading.- extra: { - facilitatorAddress: facilitator.address, - ...(asset as ERC20TokenAmount["asset"]).eip712, - }, + extra: { + facilitatorAddress: facilitator.address, + ...(((asset as ERC20TokenAmount["asset"]).eip712) ?? {}), + },
250-299
: Add required TSDoc for the new public API
SupportedAuthorizationMethods
anddetectSupportedAuthorizationMethods
are part of the published surface underpackages/thirdweb
, so per our SDK guidelines every public symbol must ship with TSDoc that includes a stability tag (e.g.@beta
) and at least one compiling@example
. Please add the missing documentation blocks.+/** + * Describes which authorization flows an ERC-20 contract supports. + * @beta + * @example + * ```ts + * const support: SupportedAuthorizationMethods = { + * hasPermit: true, + * hasTransferWithAuthorization: false, + * }; + * ``` + */ export type SupportedAuthorizationMethods = { hasPermit: boolean; hasTransferWithAuthorization: boolean; }; +/** + * Detects whether an ERC-20 asset supports ERC-2612 permit or ERC-3009 transferWithAuthorization. + * @beta + * @example + * ```ts + * import { detectSupportedAuthorizationMethods } from "thirdweb/x402"; + * const result = await detectSupportedAuthorizationMethods({ + * client, + * asset: "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + * chainId: 1, + * eip712Extras: undefined, + * }); + * console.log(result.hasPermit, result.hasTransferWithAuthorization); + * ``` + */ export async function detectSupportedAuthorizationMethods(args: {packages/thirdweb/src/x402/sign.ts (1)
85-134
: Don’t default to transferWithAuthorization when unsupportedWhen both
hasPermit
andhasTransferWithAuthorization
are false, the currentelse
branch still builds an EIP‑3009 payload. This produces invalid signatures (or throws once the signer hits missing domain fields) and breaks payments for tokens that support neither flow. We must only use transferWithAuthorization whenhasTransferWithAuthorization
is true, otherwise fall back to permit when available, and fail fast if neither is supported.- // only use permit if no transfer with authorization is supported - if (hasPermit && !hasTransferWithAuthorization) { + if (hasTransferWithAuthorization) { + const nonce = await createNonce(); + const unsignedPaymentHeader = preparePaymentHeader( + from, + x402Version, + paymentRequirements, + nonce, // random nonce + ); + const { signature } = await signERC3009Authorization( + account, + unsignedPaymentHeader.payload.authorization, + paymentRequirements, + ); + return { + ...unsignedPaymentHeader, + payload: { + ...unsignedPaymentHeader.payload, + signature, + }, + }; + } else if (hasPermit) { const nonce = await nonces({ contract: getContract({ address: paymentRequirements.asset, chain: getCachedChain(chainId), client: client, }), owner: from, }); @@ - } else { - // default to transfer with authorization - const nonce = await createNonce(); - const unsignedPaymentHeader = preparePaymentHeader( - from, - x402Version, - paymentRequirements, - nonce, // random nonce - ); - const { signature } = await signERC3009Authorization( - account, - unsignedPaymentHeader.payload.authorization, - paymentRequirements, - ); - return { - ...unsignedPaymentHeader, - payload: { - ...unsignedPaymentHeader.payload, - signature, - }, - }; - } + } + throw new Error( + "Asset does not support permit or transferWithAuthorization on this chain", + );packages/thirdweb/src/x402/fetchWithPayment.ts (1)
105-110
: Good call on threading the client throughRouting the
ThirdwebClient
intocreatePaymentHeader
lines up with the new permit/authorization detection path and keeps the retry flow self-contained. Looks solid.apps/playground-web/src/app/api/paywall/route.ts (1)
6-9
: Updated success copy reads wellThe revised message matches the new payment flow and keeps the handler unchanged otherwise.
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
Show resolved
Hide resolved
size-limit report 📦
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8128 +/- ##
==========================================
+ Coverage 56.26% 56.28% +0.01%
==========================================
Files 906 906
Lines 59193 59193
Branches 4173 4172 -1
==========================================
+ Hits 33305 33316 +11
+ Misses 25783 25772 -11
Partials 105 105
🚀 New features to boost your workflow:
|
b3603fa
to
c05f0e2
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. |
c05f0e2
to
a1e06b2
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx (1)
26-45
: Payment amount is off by 12 orders of magnitude for USDC
BigInt(1 * 10 ** 18)
assumes an 18‑decimals token and ends up charging 1e12 USDC (and even for 18‑decimals tokens it bills 1 whole token while the UI promises 0.1). This will either overflow the allowance path or silently overcharge the shopper. Derive the base-unit amount fromtoken.decimals
and align it with the displayed 0.1 token price.- const fetchWithPay = wrapFetchWithPayment( - fetch, - THIRDWEB_CLIENT, - activeWallet, - BigInt(1 * 10 ** 18), - ); + const paymentAmount = + (10n ** BigInt(token.decimals)) / 10n; // 0.1 token in base units + const fetchWithPay = wrapFetchWithPayment( + fetch, + THIRDWEB_CLIENT, + activeWallet, + paymentAmount, + );packages/thirdweb/src/x402/common.ts (1)
196-229
: Avoid floating‑point math when computing atomic amountsUsing JS floating arithmetic for USD->atomic conversion can introduce precision errors. Prefer integer math via parseUnits.
Apply this diff:
@@ -import { toFunctionSelector } from "viem/utils"; +import { parseUnits } from "viem"; +import { toFunctionSelector } from "viem/utils"; @@ - maxAmountRequired = (parsedUsdAmount * 10 ** asset.decimals).toString(); + maxAmountRequired = parseUnits( + parsedUsdAmount.toString(), + asset.decimals, + ).toString();packages/thirdweb/src/x402/sign.ts (1)
285-294
: Replace require with dynamic import for Node crypto in ESMrequire is not defined in ESM builds; use a dynamic import of node:crypto instead.
Apply this diff:
- const cryptoObj = - typeof globalThis.crypto !== "undefined" && - typeof globalThis.crypto.getRandomValues === "function" - ? globalThis.crypto - : // Dynamic require is needed to support node.js - // eslint-disable-next-line @typescript-eslint/no-require-imports - require("crypto").webcrypto; + const cryptoObj = + typeof globalThis.crypto !== "undefined" && + typeof globalThis.crypto.getRandomValues === "function" + ? globalThis.crypto + : (await import("node:crypto")).webcrypto;
🧹 Nitpick comments (1)
packages/thirdweb/src/x402/facilitator.ts (1)
79-82
: Explicit return type on facilitator — LGTM; align @returns textReturn type annotation looks good. Please update the function TSDoc’s @returns line to reference ThirdwebX402Facilitator (and mention the address field) instead of “FacilitatorConfig”.
As per coding guidelines
📜 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 (11)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
(3 hunks)apps/playground-web/src/app/api/paywall/route.ts
(1 hunks)apps/playground-web/src/app/payments/x402/components/constants.ts
(1 hunks)apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
(4 hunks)apps/playground-web/src/middleware.ts
(2 hunks)packages/thirdweb/src/exports/x402.ts
(1 hunks)packages/thirdweb/src/x402/common.ts
(4 hunks)packages/thirdweb/src/x402/facilitator.ts
(3 hunks)packages/thirdweb/src/x402/fetchWithPayment.ts
(2 hunks)packages/thirdweb/src/x402/sign.ts
(9 hunks)packages/thirdweb/src/x402/types.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/playground-web/src/middleware.ts
- packages/thirdweb/src/x402/fetchWithPayment.ts
- apps/playground-web/src/app/payments/x402/components/constants.ts
- packages/thirdweb/src/x402/types.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{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 localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
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
Prefertype
aliases overinterface
except for nominal shapes
Avoidany
andunknown
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/api/paywall/route.ts
packages/thirdweb/src/exports/x402.ts
packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/x402/sign.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
packages/thirdweb/src/x402/common.ts
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
**/*.{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/api/paywall/route.ts
packages/thirdweb/src/exports/x402.ts
packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/x402/sign.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
packages/thirdweb/src/x402/common.ts
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
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
UseNavLink
for internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()
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 withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()
to retrieve JWT from cookies on server side
UseAuthorization: Bearer
header – never embed tokens in URLs
Return typed results (e.g.,Project[]
,User[]
) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query
)
Use descriptive, stablequeryKeys
for React Query cache hits
ConfigurestaleTime
/cacheTime
in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-js
in server components
Files:
apps/playground-web/src/app/api/paywall/route.ts
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
packages/thirdweb/src/exports/**
📄 CodeRabbit inference engine (CLAUDE.md)
packages/thirdweb/src/exports/**
: Export everything viaexports/
directory, grouped by feature in the SDK public API
Every public symbol must have comprehensive TSDoc with at least one@example
block that compiles and custom annotation tags (@beta
,@internal
,@experimental
)
Files:
packages/thirdweb/src/exports/x402.ts
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/exports/x402.ts
packages/thirdweb/src/x402/facilitator.ts
packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/x402/common.ts
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}
: Import UI primitives from@/components/ui/_
(e.g., Button, Input, Tabs, Card)
UseNavLink
for internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()
from@/lib/utils
for conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"
; usenext/headers
, server‑only env, heavy data fetching, andredirect()
where appropriate
Client Components must start with'use client'
; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()
from cookies, sendAuthorization: Bearer <token>
header, and return typed results (avoidany
)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeys
and set sensiblestaleTime/cacheTime
(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-js
in server components (client-side only)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
className
prop on the root element of every component
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
🧠 Learnings (13)
📓 Common learnings
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
📚 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 packages/thirdweb/exports/** : Export all public API via `packages/thirdweb/exports/`, grouped by feature
Applied to files:
packages/thirdweb/src/exports/x402.ts
📚 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/exports/** : Export everything via `exports/` directory, grouped by feature in the SDK public API
Applied to files:
packages/thirdweb/src/exports/x402.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 : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx
📚 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/x402-client-preview.tsx
📚 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 : Use React Query (`tanstack/react-query`) for all client data fetching.
Applied to files:
apps/playground-web/src/app/payments/x402/components/x402-client-preview.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/common.ts
📚 Learning: 2025-08-28T12:24:37.171Z
Learnt from: MananTank
PR: thirdweb-dev/js#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/x402/common.ts
📚 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/exports/** : Every public symbol must have comprehensive TSDoc with at least one `example` block that compiles and custom annotation tags (`beta`, `internal`, `experimental`)
Applied to files:
packages/thirdweb/src/x402/common.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 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.)
Applied to files:
packages/thirdweb/src/x402/common.ts
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#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/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
📚 Learning: 2025-08-20T10:35:18.543Z
Learnt from: jnsdls
PR: thirdweb-dev/js#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/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.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 apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use `NavLink` for internal navigation with automatic active states in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx
🧬 Code graph analysis (4)
packages/thirdweb/src/x402/facilitator.ts (1)
packages/thirdweb/src/x402/schemas.ts (3)
RequestedPaymentPayload
(33-35)RequestedPaymentRequirements
(48-50)FacilitatorSettleResponse
(55-57)
packages/thirdweb/src/x402/sign.ts (4)
packages/thirdweb/src/x402/schemas.ts (2)
RequestedPaymentRequirements
(48-50)RequestedPaymentPayload
(33-35)packages/thirdweb/src/x402/types.ts (1)
ERC20TokenAmount
(89-100)packages/thirdweb/src/x402/common.ts (1)
detectSupportedAuthorizationMethods
(255-299)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx (1)
apps/playground-web/src/app/payments/x402/components/constants.ts (2)
chain
(6-6)token
(7-7)
packages/thirdweb/src/x402/common.ts (4)
packages/thirdweb/src/x402/facilitator.ts (1)
facilitator
(79-219)packages/thirdweb/src/x402/types.ts (1)
ERC20TokenAmount
(89-100)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)packages/thirdweb/src/extensions/erc20/__generated__/USDC/write/transferWithAuthorization.ts (1)
isTransferWithAuthorizationSupported
(75-82)
⏰ 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). (2)
- GitHub Check: Size
- GitHub Check: Unit Tests
🔇 Additional comments (12)
apps/playground-web/src/app/api/paywall/route.ts (1)
6-9
: Updated success copy looks goodThe revised message communicates the payment completion clearly and stays aligned with the updated x402 flow.
apps/playground-web/src/app/payments/x402/components/x402-client-preview.tsx (1)
32-44
: Stop sendingpayTo
as the shopper’s walletWe’re still populating
payTo
withactiveWallet.getAccount()?.address
, so the backend will try to settle funds back to the caller, breaking the payment flow. Drop this param (or send the fixed merchant address once the middleware exposes it) so settlement stays server-controlled.const searchParams = new URLSearchParams(); searchParams.set("chainId", chain.id.toString()); - searchParams.set("payTo", activeWallet.getAccount()?.address || "");
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/server-wallets/wallet-table/wallet-table-ui.client.tsx (1)
218-221
: Verify the new /transactions route supports these query paramsThe hrefs now target /transactions. Confirm that:
- page is handled by the unified transactions page, and
- testTxWithWallet preserves the server‑wallets context (no regression from the former /transactions/server-wallets path).
Also applies to: 235-236, 247-250
packages/thirdweb/src/x402/facilitator.ts (1)
94-94
: Exposing facilitator.address — LGTMAddress is correctly wired to serverWalletAddress.
packages/thirdweb/src/exports/x402.ts (1)
4-6
: Publicly re‑exporting ThirdwebX402Facilitator — LGTMThis aligns the exports surface with the new type.
packages/thirdweb/src/x402/common.ts (2)
118-121
: Good fix: guard spreading eip712 extrasUsing the nullish fallback prevents runtime TypeError when eip712 is undefined.
250-299
: Document new public authorization‑detection APISupportedAuthorizationMethods and detectSupportedAuthorizationMethods are public; add TSDoc with stability tags and compiling examples.
Apply this diff:
@@ -export type SupportedAuthorizationMethods = { +/** + * Describes which authorization flows an ERC‑20 contract supports. + * @beta + * @example + * ```ts + * const methods: SupportedAuthorizationMethods = { + * usePermit: true, + * useTransferWithAuthorization: false, + * }; + * ``` + */ +export type SupportedAuthorizationMethods = { usePermit: boolean; useTransferWithAuthorization: boolean; }; @@ -export async function detectSupportedAuthorizationMethods(args: { +/** + * Detects whether an ERC‑20 asset supports ERC‑2612 Permit or EIP‑3009 TransferWithAuthorization. + * Prefers TransferWithAuthorization when both are present. + * @beta + * @example + * ```ts + * const support = await detectSupportedAuthorizationMethods({ + * client, + * asset: "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + * chainId: 1, + * eip712Extras: { name: "USD Coin", version: "2", primaryType: "Permit" }, + * }); + * console.log(support.usePermit, support.useTransferWithAuthorization); + * ``` + */ +export async function detectSupportedAuthorizationMethods(args: { client: ThirdwebClient; asset: string; chainId: number; eip712Extras: ERC20TokenAmount["asset"]["eip712"] | undefined; }): Promise<SupportedAuthorizationMethods> {As per coding guidelines
packages/thirdweb/src/x402/sign.ts (5)
67-84
: Authorization method selection — LGTMPrefers TWA, falls back to Permit, else throws. This addresses the prior default‑to‑3009 issue.
85-113
: Permit path with nonces() — LGTMCorrectly derives nonce from ERC20Permit and uses it in the header.
112-133
: TransferWithAuthorization path — LGTMRandom 32‑byte nonce and signing flow look correct.
195-222
: Typed‑data payload BigInt conversions — LGTMConverting value and time bounds to BigInt matches EIP‑3009 types.
228-278
: Permit signing checks — LGTMValidates facilitatorAddress/name/version and uses uint256 nonce/deadline correctly.
export type ThirdwebX402Facilitator = { | ||
url: `${string}://${string}`; | ||
address: string; | ||
createAuthHeaders: () => Promise<{ | ||
verify: Record<string, string>; | ||
settle: Record<string, string>; | ||
supported: Record<string, string>; | ||
list: Record<string, string>; | ||
}>; | ||
verify: ( | ||
payload: RequestedPaymentPayload, | ||
paymentRequirements: RequestedPaymentRequirements, | ||
) => Promise<VerifyResponse>; | ||
settle: ( | ||
payload: RequestedPaymentPayload, | ||
paymentRequirements: RequestedPaymentRequirements, | ||
) => Promise<FacilitatorSettleResponse>; | ||
supported: () => Promise<SupportedPaymentKindsResponse>; | ||
}; |
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.
🛠️ Refactor suggestion | 🟠 Major
Add TSDoc for new public type ThirdwebX402Facilitator
This is part of the public API; per SDK guidelines, it needs TSDoc with stability tag and a compiling example. Add a brief doc and show the address field in the example.
As per coding guidelines
🤖 Prompt for AI Agents
In packages/thirdweb/src/x402/facilitator.ts around lines 18 to 36, the new
public type ThirdwebX402Facilitator lacks TSDoc; add a TSDoc block immediately
above the type that marks stability (e.g., @stability stable or the appropriate
tag), provides a short description of the type, documents each field (url,
address, createAuthHeaders, verify, settle, supported), and includes a minimal
compiling example showing instantiation/usage that explicitly shows the address
field; keep the doc concise and follow existing SDK doc style and tags.
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/thirdweb/src/x402/sign.ts (1)
142-151
: Add required TSDoc tags and example for public APIpackages/thirdweb public exports require comprehensive TSDoc with a compiling @example and a custom tag.
As per coding guidelines
/** * Creates and encodes a payment header for the given client and payment requirements. * * @param client - The signer wallet instance used to create the payment header * @param x402Version - The version of the X402 protocol to use * @param paymentRequirements - The payment requirements containing scheme and network information * @returns A promise that resolves to the encoded payment header string + * @example + * ```ts + * import { createPaymentHeader } from "thirdweb/x402/sign"; + * const header = await createPaymentHeader(client, account, { + * scheme: "evm", + * network: "eip155:1", + * asset: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + * maxAmountRequired: "1000000", + * maxTimeoutSeconds: 600, + * payTo: "0xYourFacilitator", + * extra: { name: "USD Coin", version: "2", primaryType: "TransferWithAuthorization" } + * }, 1); + * ``` + * @beta */
🧹 Nitpick comments (5)
packages/thirdweb/src/x402/sign.ts (5)
114-121
: Update outdated inline commentThis path isn’t a “default” anymore. Tweak the comment to avoid confusion.
- // default to transfer with authorization + // TransferWithAuthorization path (EIP-3009)
168-181
: Doc param name mismatch (“walletClient” vs “account”)JSDoc references walletClient but the function param is account. Fix the annotation to prevent confusion.
- * @param walletClient - The wallet client that will sign the authorization + * @param account - The account that will sign the authorization
231-249
: Strengthen typing forextra
based on selected methodAt runtime you validate
facilitatorAddress
,name
, andversion
. Prefer a discriminated union forRequestedPaymentRequirements.extra
keyed byeip712.primaryType
so TypeScript enforces required fields per method (Permit requires facilitatorAddress, name, version).As per coding guidelines
Also applies to: 269-276
288-297
: Avoidrequire
in ESM; use dynamicimport("node:crypto")
Using
require
in ESM can break in Node/browser builds without CJS shim. Dynamic import is safer and tree-shakeable.async function createNonce(): Promise<Hex> { const cryptoObj = typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function" ? globalThis.crypto - : // Dynamic require is needed to support node.js - // eslint-disable-next-line @typescript-eslint/no-require-imports - require("crypto").webcrypto; - return toHex(cryptoObj.getRandomValues(new Uint8Array(32))); + : (await import("node:crypto")).webcrypto; + const bytes = new Uint8Array(32); + cryptoObj.getRandomValues(bytes); + return toHex(bytes); }
34-39
: Tiny cleanup: avoid string→string toString()
validAfter
/validBefore
are already strings; calling.toString()
again is redundant. Keep as bigint locally and stringify once at assignment.- const validAfter = BigInt( - Math.floor(Date.now() / 1000) - 600, // 10 minutes before - ).toString(); - const validBefore = BigInt( - Math.floor(Date.now() / 1000 + paymentRequirements.maxTimeoutSeconds), - ).toString(); + const validAfter = BigInt(Math.floor(Date.now() / 1000) - 600); // 10 minutes before + const validBefore = BigInt( + Math.floor(Date.now() / 1000 + paymentRequirements.maxTimeoutSeconds), + ); ... - validAfter: validAfter.toString(), - validBefore: validBefore.toString(), + validAfter: validAfter.toString(), + validBefore: validBefore.toString(),Also applies to: 51-54
📜 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 (3)
packages/thirdweb/src/x402/common.ts
(4 hunks)packages/thirdweb/src/x402/sign.ts
(9 hunks)packages/thirdweb/src/x402/types.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/thirdweb/src/x402/common.ts
- packages/thirdweb/src/x402/types.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{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 localtypes.ts
barrels
Prefer type aliases over interface except for nominal shapes
Avoidany
andunknown
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
Prefertype
aliases overinterface
except for nominal shapes
Avoidany
andunknown
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:
packages/thirdweb/src/x402/sign.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
packages/thirdweb/src/x402/sign.ts
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/sign.ts
🧠 Learnings (1)
📓 Common learnings
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
🧬 Code graph analysis (1)
packages/thirdweb/src/x402/sign.ts (5)
packages/thirdweb/src/exports/thirdweb.ts (3)
Hex
(230-230)ThirdwebClient
(25-25)getContract
(43-43)packages/thirdweb/src/x402/schemas.ts (3)
RequestedPaymentRequirements
(48-50)RequestedPaymentPayload
(33-35)networkToChainId
(59-80)packages/thirdweb/src/x402/types.ts (1)
ERC20TokenAmount
(91-102)packages/thirdweb/src/x402/common.ts (1)
getSupportedSignatureType
(251-289)packages/thirdweb/src/chains/utils.ts (1)
getCachedChain
(79-89)
⏰ 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, vite)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Build Packages
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Unit Tests
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
packages/thirdweb/src/x402/sign.ts (1)
84-139
: Authorization method selection fixed — LGTMSwitching on the detected signature type and throwing when none are supported resolves the prior critical default-to-3009 bug. Prefer TWA, else Permit, else fail — correct.
PR-Codex overview
This PR introduces support for ERC-2612 permit functionality in the
thirdweb
library, enhancing payment capabilities forx402
transactions. It updates various components, adds new types, and modifies existing functions to accommodate the new feature.Detailed summary
x402
payments.message
inapps/playground-web/src/app/api/paywall/route.ts
to reflect payment success.transferWithAuthorization
function inpackages/thirdweb/scripts/generate/abis/erc20/USDC.json
.wrapFetchWithPayment
to accept aclient
parameter.packages/thirdweb/src/x402/sign.ts
to include ERC-2612 permit logic.ThirdwebX402Facilitator
type with new methods for payment processing.createPaymentHeader
to includeclient
parameter.Summary by CodeRabbit
New Features
UI
Refactor
Chores