Skip to content

Conversation

@joaquim-verges
Copy link
Member

@joaquim-verges joaquim-verges commented Dec 11, 2025


PR-Codex overview

This PR introduces caching for permit signatures in the thirdweb library, specifically for the "upto" scheme. It enhances the useFetchWithPayment functionality to automatically reuse cached signatures, improving efficiency in payment handling.

Detailed summary

  • Added storage option for caching permit signatures in useFetchWithPayment and wrapFetchWithPayment.
  • Implemented caching logic in signPaymentHeader to reuse signatures based on allowance checks.
  • Updated documentation to reflect new caching behavior in payment processes.
  • Removed outdated example code from documentation regarding manual payment data retrieval.

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

Summary by CodeRabbit

  • New Features
    • Permit signatures for x402 payments are automatically cached and re-used on web and native, with platform-default storage and an optional override.
  • Improvements
    • Cached signatures are validated (deadline and on-chain allowance) before reuse and are cleared if a subsequent 402 invalidates them.
  • Documentation
    • Client docs updated to remove the manual storage example and describe automatic caching behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 11, 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 Dec 11, 2025 10:49am
nebula Ready Ready Preview Comment Dec 11, 2025 10:49am
thirdweb_playground Ready Ready Preview Comment Dec 11, 2025 10:49am
thirdweb-www Ready Ready Preview Comment Dec 11, 2025 10:49am
wallet-ui Ready Ready Preview Comment Dec 11, 2025 10:49am

@changeset-bot
Copy link

changeset-bot bot commented Dec 11, 2025

🦋 Changeset detected

Latest commit: a9ab172

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

This PR includes changesets to release 4 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch
wagmi-inapp 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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds AsyncStorage-backed caching for x402 "upto" permit signatures and threads an optional storage?: AsyncStorage option through hooks and signing/fetch utilities to store, reuse, and invalidate permit signatures across web and native flows.

Changes

Cohort / File(s) Summary
Changeset Documentation
​.changeset/warm-clouds-judge.md
Adds a patch-level changelog entry describing automatic caching/reuse of permit x402 signatures.
React Hooks — Core / Web / Native
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts, packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx, packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
Thread storage?: AsyncStorage through hook options; web hook supplies default webLocalStorage (via useMemo), native hook defaults to nativeLocalStorage; core options type updated and forwarded into the fetch wrapper.
Permit Signature Cache Utility
packages/thirdweb/src/x402/permitSignatureStorage.ts
New AsyncStorage-backed cache with getPermitCacheKey, getPermitSignatureFromCache, savePermitSignatureToCache, clearPermitSignatureFromCache, and exported types CachedPermitSignature and PermitCacheKeyParams.
Payment Flow Core (fetch)
packages/thirdweb/src/x402/fetchWithPayment.ts
wrapFetchWithPayment(..., options?) now accepts optional storage, passes options?.storage ?? webLocalStorage into header creation, and clears cached permit on a second 402 response via clearPermitSignatureFromCache.
Payment Header Signing
packages/thirdweb/src/x402/sign.ts
createPaymentHeader and signPaymentHeader now accept storage?: AsyncStorage; for the "upto" permit scheme, attempt to reuse cached permits (deadline + on‑chain allowance check) and save new permits to cache when created.
Docs / Example Update
apps/portal/src/app/x402/server/page.mdx
Removed the manual-storage example and added a note that wrapFetchWithPayment() and useFetchWithPayment() automatically handle caching and reuse of payment data.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/App
    participant Storage as AsyncStorage
    participant Signing as Payment Signing
    participant OnChain as On‑chain (ERC20)
    participant Server as x402 Server

    Client->>Server: Initial request (no permit)
    Server-->>Client: 402 Payment Required + requirements

    alt "upto" permit flow
        Signing->>Storage: getPermitSignatureFromCache(chainId, asset, owner, spender)
        Storage-->>Signing: cached permit? (payload, deadline, maxAmount) / null
        alt cached & not expired
            Signing->>OnChain: query allowance(owner, spender, asset)
            OnChain-->>Signing: allowance amount
            alt allowance >= required
                Signing-->>Client: reuse cached permit header
            else
                Signing->>Signing: create new permit signature
                Signing->>Storage: savePermitSignatureToCache(...)
                Signing-->>Client: new permit header
            end
        else no cached permit
            Signing->>Signing: create new permit signature
            Signing->>Storage: savePermitSignatureToCache(...)
            Signing-->>Client: new permit header
        end
    end

    Client->>Server: Retry with signed header
    Server-->>Client: Success OR 402 (second 402)

    alt second 402 response
        Client->>Storage: clearPermitSignatureFromCache(chainId, asset, owner, spender)
        Storage-->>Client: cleared
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review deadline and allowance validation in packages/thirdweb/src/x402/sign.ts.
  • Verify cache key normalization, JSON parsing, and error swallowing in packages/thirdweb/src/x402/permitSignatureStorage.ts.
  • Confirm cache clearing behavior on second 402 in packages/thirdweb/src/x402/fetchWithPayment.ts.
  • Validate default storage resolution and typings in web/native hooks and useFetchWithPaymentCore.ts.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description contains a PR-Codex-generated summary explaining the caching implementation, but lacks required sections from the template: issue tag (TEAM-0000), explicit reviewer notes, and testing instructions. Add the missing template sections: include the Linear issue tag (TEAM-0000 format), clarify important details for reviewers in a dedicated section, and specify how to test (unit tests, playground, etc.).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: implementing caching and reuse of x402 permit signatures for upto schemes, which aligns with the primary objective of the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Cache_and_reuse_x402_permit_signatures_for_upto_schemes

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 packages SDK Involves changes to the thirdweb SDK labels Dec 11, 2025
@joaquim-verges joaquim-verges changed the title Cache and reuse x402 permit signatures for upto schemes [SDK] Cache and reuse x402 permit signatures for upto schemes Dec 11, 2025
@joaquim-verges joaquim-verges marked this pull request as ready for review December 11, 2025 10:11
@joaquim-verges joaquim-verges requested review from a team as code owners December 11, 2025 10:11
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

@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: 2

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/react/native/hooks/x402/useFetchWithPayment.ts (1)

92-100: Avoid spreading options when it may be undefined

options is optional, but this call:

return useFetchWithPaymentCore(client, {
  ...options,
  storage: options?.storage ?? nativeLocalStorage,
});

will throw if options is undefined because object spread cannot spread undefined.

Wrap the spread with a safe fallback:

-export function useFetchWithPayment(
-  client: ThirdwebClient,
-  options?: UseFetchWithPaymentOptions,
-) {
-  // Native version doesn't show modal, errors bubble up naturally
-  return useFetchWithPaymentCore(client, {
-    ...options,
-    storage: options?.storage ?? nativeLocalStorage,
-  });
-}
+export function useFetchWithPayment(
+  client: ThirdwebClient,
+  options?: UseFetchWithPaymentOptions,
+) {
+  // Native version doesn't show modal, errors bubble up naturally
+  return useFetchWithPaymentCore(client, {
+    ...(options ?? {}),
+    storage: options?.storage ?? nativeLocalStorage,
+  });
+}

This preserves existing behavior while safely defaulting storage for permit caching.

🧹 Nitpick comments (2)
packages/thirdweb/src/x402/sign.ts (1)

101-152: Permit caching logic is solid; consider trimming debug log and unused fields

The new "Permit" branch for the "upto" scheme looks correct:

  • Only caches when scheme === "upto" and storage is provided.
  • Reuse path validates:
    • Deadline (BigInt(cached.deadline) > now), and
    • On-chain allowance via allowance(...), comparing against minAmountRequired when present, otherwise maxAmountRequired.

This should prevent reusing signatures when they are expired or underfunded.

Two minor follow-ups:

  1. Debug logging in SDK code
if (currentAllowance >= threshold) {
  console.log("re-using cached signature");
  return cached.payload;
}

A bare console.log in library code can be noisy for consumers. Consider removing it or routing through whatever structured/debug logging facility the SDK uses (or guarding it behind a debug flag).

  1. Possibly unused maxAmount in CachedPermitSignature

When caching:

await savePermitSignatureToCache(
  storage,
  cacheParams,
  signedPayload,
  unsignedPaymentHeader.payload.authorization.validBefore,
  paymentRequirements.maxAmountRequired,
);

the maxAmount field is persisted, but in this file you only read payload and deadline from the cache. If no other call sites use maxAmount, you could drop it from the cached data to keep the payload lean; if you plan to use it later (e.g., for debugging or additional validation), keeping it is fine.

Also applies to: 173-193

packages/thirdweb/src/x402/permitSignatureStorage.ts (1)

1-99: AsyncStorage-backed permit cache helpers look robust and low-risk

The cache key derivation and helpers are well-structured:

  • Key normalization with toLowerCase() on addresses avoids case-sensitivity bugs.
  • getPermitSignatureFromCache safely returns null on missing or malformed entries.
  • savePermitSignatureToCache and clearPermitSignatureFromCache swallow errors, which is reasonable since caching is optional.

The maxAmount field in CachedPermitSignature is currently only written, not read in the shown code; if it’s just informational, that’s fine, but you could drop it later to keep the cache footprint minimal.

📜 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 01dce0e and 3aa6e70.

📒 Files selected for processing (7)
  • .changeset/warm-clouds-judge.md (1 hunks)
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (3 hunks)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (3 hunks)
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2 hunks)
  • packages/thirdweb/src/x402/fetchWithPayment.ts (4 hunks)
  • packages/thirdweb/src/x402/permitSignatureStorage.ts (1 hunks)
  • packages/thirdweb/src/x402/sign.ts (5 hunks)
🧰 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 TypeScript 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 in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

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

Files:

  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
🧬 Code graph analysis (4)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
packages/thirdweb/src/x402/permitSignatureStorage.ts (1)
  • clearPermitSignatureFromCache (89-99)
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (1)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
  • useFetchWithPaymentCore (41-159)
packages/thirdweb/src/x402/sign.ts (3)
packages/thirdweb/src/x402/permitSignatureStorage.ts (3)
  • PermitCacheKeyParams (16-21)
  • getPermitSignatureFromCache (40-54)
  • savePermitSignatureToCache (64-82)
packages/thirdweb/src/chains/utils.ts (1)
  • getCachedChain (79-89)
packages/thirdweb/src/exports/x402.ts (1)
  • ERC20TokenAmount (17-17)
packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (1)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
  • useFetchWithPaymentCore (41-159)
⏰ 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: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Build Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
.changeset/warm-clouds-judge.md (1)

1-5: Changeset description matches behavior and scope

The summary clearly reflects the new x402 permit signature caching behavior and is scoped as a patch for thirdweb, which is appropriate.

packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (2)

5-23: Storage option threading in core hook looks correct

Extending UseFetchWithPaymentOptions with storage?: AsyncStorage and forwarding it to wrapFetchWithPayment keeps the core hook platform-agnostic while enabling permit signature caching. The option remains fully optional, so existing callers are preserved.


86-95: Forwarding storage to wrapFetchWithPayment is consistent with new options type

Passing storage: options?.storage alongside maxValue and paymentRequirementsSelector cleanly exposes caching behavior to all consumers of the core hook without changing its public signature.

packages/thirdweb/src/x402/sign.ts (1)

232-245: Threading storage into createPaymentHeader is consistent with signPaymentHeader

Accepting an optional storage?: AsyncStorage in createPaymentHeader and simply forwarding it to signPaymentHeader keeps the external API minimal while enabling permit caching where desired.

Comment on lines +259 to 271
// Default to webLocalStorage for permit signature caching
const resolvedOptions = useMemo(
() => ({
...options,
storage: options?.storage ?? webLocalStorage,
}),
[options],
);

return useFetchWithPaymentCore(
client,
options,
resolvedOptions,
showErrorModal,
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 spreading of possibly-undefined options (runtime TypeError risk)

options is optional, but this block:

const resolvedOptions = useMemo(
  () => ({
    ...options,
    storage: options?.storage ?? webLocalStorage,
  }),
  [options],
);

will throw at runtime when options is undefined because object spread cannot spread undefined.

Use a safe fallback object when options is not provided:

-  const resolvedOptions = useMemo(
-    () => ({
-      ...options,
-      storage: options?.storage ?? webLocalStorage,
-    }),
-    [options],
-  );
+  const resolvedOptions = useMemo(
+    () => ({
+      ...(options ?? {}),
+      storage: options?.storage ?? webLocalStorage,
+    }),
+    [options],
+  );

Additionally, consider updating the hook JSDoc above to document the new options.storage parameter and its default (webLocalStorage), for parity with the native hook docs.

🤖 Prompt for AI Agents
In packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx around
lines 259 to 271, the code spreads the possibly-undefined options object which
will throw if options is undefined; change the memo to spread a safe fallback
(e.g., options ?? {}) and set storage with options?.storage ?? webLocalStorage
so the spread never receives undefined, then pass that resolvedOptions into
useFetchWithPaymentCore; also update the hook JSDoc above to document the
options.storage parameter and its default (webLocalStorage).

@github-actions
Copy link
Contributor

github-actions bot commented Dec 11, 2025

size-limit report 📦

Path Size
@thirdweb-dev/nexus (esm) 105.66 KB (0%)
@thirdweb-dev/nexus (cjs) 319.47 KB (0%)

@codecov
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.66%. Comparing base (01dce0e) to head (e681bbd).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #8538   +/-   ##
=======================================
  Coverage   54.66%   54.66%           
=======================================
  Files         921      921           
  Lines       61163    61163           
  Branches     4151     4149    -2     
=======================================
  Hits        33434    33434           
  Misses      27627    27627           
  Partials      102      102           
Flag Coverage Δ
packages 54.66% <ø> (ø)
🚀 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.

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: 1

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/fetchWithPayment.ts (1)

143-172: Cache invalidation inconsistency with default storage.

The code defaults to webLocalStorage when caching signatures (line 143), but only clears the cache when options?.storage is explicitly provided (line 165). This means when callers don't pass storage, signatures are cached in webLocalStorage but never invalidated on a subsequent 402.

Compute the effective storage once and use it consistently:

 export function wrapFetchWithPayment(
   fetch: typeof globalThis.fetch,
   client: ThirdwebClient,
   wallet: Wallet,
   options?: {
     maxValue?: bigint;
     paymentRequirementsSelector?: (
       paymentRequirements: RequestedPaymentRequirements[],
     ) => RequestedPaymentRequirements | undefined;
     storage?: AsyncStorage;
   },
 ) {
   return async (input: RequestInfo, init?: RequestInit) => {
+    const effectiveStorage = options?.storage ?? webLocalStorage;
     // ... existing code ...

     const paymentHeader = await createPaymentHeader(
       client,
       account,
       selectedPaymentRequirements,
       x402Version,
-      options?.storage ?? webLocalStorage,
+      effectiveStorage,
     );

     // ... existing code ...

     // If payment was rejected (still 402), clear cached signature
-    if (secondResponse.status === 402 && options?.storage) {
-      await clearPermitSignatureFromCache(options.storage, {
+    if (secondResponse.status === 402) {
+      await clearPermitSignatureFromCache(effectiveStorage, {
         chainId: paymentChainId,
         asset: selectedPaymentRequirements.asset,
         owner: getAddress(account.address),
         spender: getAddress(selectedPaymentRequirements.payTo),
       });
     }
🧹 Nitpick comments (1)
packages/thirdweb/src/x402/sign.ts (1)

103-112: Redundant storage check.

Since shouldCache is defined as paymentRequirements.scheme === "upto" && storage !== undefined, the subsequent storage check on line 115 and 182 is redundant. The code is correct but could be simplified.

-      if (shouldCache && storage) {
+      if (shouldCache) {
         const cached = await getPermitSignatureFromCache(storage, cacheParams);

Also apply to line 182.

📜 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 3aa6e70 and 390dc19.

📒 Files selected for processing (8)
  • .changeset/warm-clouds-judge.md (1 hunks)
  • apps/portal/src/app/x402/server/page.mdx (1 hunks)
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (3 hunks)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (3 hunks)
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2 hunks)
  • packages/thirdweb/src/x402/fetchWithPayment.ts (4 hunks)
  • packages/thirdweb/src/x402/permitSignatureStorage.ts (1 hunks)
  • packages/thirdweb/src/x402/sign.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
  • .changeset/warm-clouds-judge.md
🧰 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 TypeScript 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 in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

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

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/x402/sign.ts
🧬 Code graph analysis (1)
packages/thirdweb/src/x402/sign.ts (4)
packages/thirdweb/src/x402/types.ts (2)
  • x402Version (14-14)
  • ERC20TokenAmount (125-138)
packages/thirdweb/src/x402/permitSignatureStorage.ts (3)
  • PermitCacheKeyParams (16-21)
  • getPermitSignatureFromCache (40-54)
  • savePermitSignatureToCache (64-82)
packages/thirdweb/src/chains/utils.ts (1)
  • getCachedChain (79-89)
packages/thirdweb/src/exports/x402.ts (1)
  • ERC20TokenAmount (17-17)
⏰ 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: Analyze (javascript)
🔇 Additional comments (4)
apps/portal/src/app/x402/server/page.mdx (1)

135-135: Documentation accurately reflects the new caching behavior.

The statement correctly informs users that the SDK now handles caching and reuse of payment data automatically, removing the need for manual backend storage management.

packages/thirdweb/src/x402/sign.ts (3)

133-147: Threshold logic is sound.

The fallback from minAmountRequired to maxAmountRequired correctly handles both cases. The optional chaining safely handles when extra or minAmountRequired is undefined.


181-192: Cache storage implementation is correct.

The signature is properly cached with the deadline from validBefore and the maxAmountRequired, which aligns with the retrieval threshold logic.


232-246: Clean parameter threading.

The storage parameter is properly threaded through to signPaymentHeader with accurate JSDoc documentation.

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: 0

♻️ Duplicate comments (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)

64-69: Critical: Storage default inconsistency remains unfixed.

The past review flagged this exact issue as addressed, but the bug is still present:

  • Line 143 defaults to webLocalStorage when options?.storage is undefined
  • Line 165 only clears the cache when options?.storage is explicitly provided (truthy)

Impact: When callers don't pass options.storage, permit signatures are cached in webLocalStorage but never invalidated on a second 402 response. This breaks the intended cache-invalidation behavior for the default case.

Apply this diff to compute effective storage once and use it consistently:

 export function wrapFetchWithPayment(
   fetch: typeof globalThis.fetch,
   client: ThirdwebClient,
   wallet: Wallet,
   options?: {
     maxValue?: bigint;
     paymentRequirementsSelector?: (
       paymentRequirements: RequestedPaymentRequirements[],
     ) => RequestedPaymentRequirements | undefined;
     /**
      * Storage for caching permit signatures (for "upto" scheme).
      * When provided, permit signatures will be cached and reused if the on-chain allowance is sufficient.
+     * Defaults to `webLocalStorage` in browser environments.
      */
     storage?: AsyncStorage;
   },
 ) {
   return async (input: RequestInfo, init?: RequestInit) => {
+    const effectiveStorage = options?.storage ?? webLocalStorage;
+
     const response = await fetch(input, init);
 
     // ... (existing code)
 
     const paymentHeader = await createPaymentHeader(
       client,
       account,
       selectedPaymentRequirements,
       x402Version,
-      options?.storage ?? webLocalStorage,
+      effectiveStorage,
     );
 
     // ... (existing code)
 
     const secondResponse = await fetch(input, newInit);
 
     // If payment was rejected (still 402), clear cached signature
-    if (secondResponse.status === 402 && options?.storage) {
-      await clearPermitSignatureFromCache(options.storage, {
+    if (secondResponse.status === 402) {
+      await clearPermitSignatureFromCache(effectiveStorage, {
         chainId: paymentChainId,
         asset: selectedPaymentRequirements.asset,
         owner: getAddress(account.address),
         spender: getAddress(selectedPaymentRequirements.payTo),
       });
     }
 
     return secondResponse;
   };
 }

Also applies to: 143-172

🧹 Nitpick comments (1)
packages/thirdweb/src/x402/sign.ts (1)

115-149: Consider clarifying the threshold logic and improving type safety.

The caching reuse logic is sound but has two areas for improvement:

  1. Type safety issue (lines 134-138): The type assertion assumes extra can contain minAmountRequired, but the actual type (ERC20TokenAmount["asset"]["eip712"]) doesn't include this property. This is fragile and could break if the type evolves.

  2. Missing explanation (lines 139-141): The logic prefers minAmountRequired over maxAmountRequired for the allowance threshold, but this choice isn't documented. Adding a comment would help future maintainers understand why.

Apply this diff to improve type safety and clarity:

             // Determine threshold - use minAmountRequired if present, else maxAmountRequired
             const extra = paymentRequirements.extra as
               | (ERC20TokenAmount["asset"]["eip712"] & {
                   minAmountRequired?: string;
                 })
               | undefined;
+            // For "upto" schemes, prefer minAmountRequired (actual payment) over maxAmountRequired (permit ceiling)
+            // to avoid requesting a new signature when the cached permit still covers the minimum payment.
             const threshold = extra?.minAmountRequired
               ? BigInt(extra.minAmountRequired)
               : BigInt(paymentRequirements.maxAmountRequired);

Additionally, consider defining a proper type for the extended extra field in schemas.ts or types.ts:

// In types.ts or schemas.ts
export type UptoSchemeExtra = ERC20TokenAmount["asset"]["eip712"] & {
  minAmountRequired?: string;
};

Then use it:

-            const extra = paymentRequirements.extra as
-              | (ERC20TokenAmount["asset"]["eip712"] & {
-                  minAmountRequired?: string;
-                })
-              | undefined;
+            const extra = paymentRequirements.extra as UptoSchemeExtra | undefined;
📜 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 390dc19 and e681bbd.

📒 Files selected for processing (8)
  • .changeset/warm-clouds-judge.md (1 hunks)
  • apps/portal/src/app/x402/server/page.mdx (1 hunks)
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (3 hunks)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (3 hunks)
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2 hunks)
  • packages/thirdweb/src/x402/fetchWithPayment.ts (4 hunks)
  • packages/thirdweb/src/x402/permitSignatureStorage.ts (1 hunks)
  • packages/thirdweb/src/x402/sign.ts (5 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .changeset/warm-clouds-judge.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
  • apps/portal/src/app/x402/server/page.mdx
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
🧰 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 TypeScript 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 in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

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

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
🧬 Code graph analysis (2)
packages/thirdweb/src/x402/sign.ts (3)
packages/thirdweb/src/x402/types.ts (2)
  • x402Version (14-14)
  • ERC20TokenAmount (125-138)
packages/thirdweb/src/x402/permitSignatureStorage.ts (3)
  • PermitCacheKeyParams (16-21)
  • getPermitSignatureFromCache (40-54)
  • savePermitSignatureToCache (64-82)
packages/thirdweb/src/chains/utils.ts (1)
  • getCachedChain (79-89)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)
packages/thirdweb/src/x402/permitSignatureStorage.ts (1)
  • clearPermitSignatureFromCache (89-99)
⏰ 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: E2E Tests (pnpm, vite)
  • GitHub Check: Unit Tests
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)

3-7: LGTM: Imports are appropriate.

The new imports support the caching and address normalization requirements for the x402 permit signature flow.

packages/thirdweb/src/x402/sign.ts (5)

103-112: LGTM: Cache setup is correct.

The cache key parameters properly identify a unique permit signature by chainId, asset, owner, and spender. The shouldCache flag correctly restricts caching to the "upto" scheme with explicit storage.


118-131: LGTM: Deadline and allowance validation is correct.

The cache reuse logic properly validates both temporal (deadline) and on-chain state (allowance) before reusing a cached signature. The RPC call to check allowance is necessary for security, even though it adds latency.


172-191: LGTM: Signature caching logic is correct.

The code properly persists the newly generated signature along with its deadline and max amount. The try-catch in savePermitSignatureToCache (from the storage module) ensures caching failures don't break the signing flow.


6-6: LGTM: Imports support the new caching functionality.

The imports for allowance (on-chain check), AsyncStorage (caching interface), and permit signature storage utilities are all necessary for the caching implementation.

Also applies to: 10-10, 14-18


81-81: LGTM: Storage parameter threading is correct.

The storage?: AsyncStorage parameter is properly threaded through signPaymentHeader and createPaymentHeader to enable optional signature caching.

Also applies to: 236-236

@joaquim-verges joaquim-verges force-pushed the Cache_and_reuse_x402_permit_signatures_for_upto_schemes branch from e681bbd to a9ab172 Compare December 11, 2025 10:36
@joaquim-verges joaquim-verges merged commit f5ab57f into main Dec 11, 2025
17 of 22 checks passed
@joaquim-verges joaquim-verges deleted the Cache_and_reuse_x402_permit_signatures_for_upto_schemes branch December 11, 2025 10:40
@joaquim-verges joaquim-verges mentioned this pull request Dec 11, 2025
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: 1

♻️ Duplicate comments (1)
packages/thirdweb/src/x402/fetchWithPayment.ts (1)

64-69: Critical: Cache invalidation doesn't use the same storage as caching (default case bug)

This issue was previously flagged but not fully resolved. When options.storage is not provided:

  1. Line 143: Signatures are cached to webLocalStorage (via the ?? fallback)
  2. Line 165: Cache clearing only occurs when options?.storage is explicitly truthy
  3. Result: Cached signatures in webLocalStorage are never invalidated on subsequent 402 responses

This breaks the intended "clear on second 402" behavior for the default storage case.

Fix: Compute the effective storage once and use it consistently:

 export function wrapFetchWithPayment(
   fetch: typeof globalThis.fetch,
   client: ThirdwebClient,
   wallet: Wallet,
   options?: {
     maxValue?: bigint;
     paymentRequirementsSelector?: (
       paymentRequirements: RequestedPaymentRequirements[],
     ) => RequestedPaymentRequirements | undefined;
     storage?: AsyncStorage;
   },
 ) {
   return async (input: RequestInfo, init?: RequestInit) => {
+    const storage = options?.storage ?? webLocalStorage;
+
     const response = await fetch(input, init);
     // ...
     
     const paymentHeader = await createPaymentHeader(
       client,
       account,
       selectedPaymentRequirements,
       x402Version,
-      options?.storage ?? webLocalStorage,
+      storage,
     );
     
     // ...
     
-    if (secondResponse.status === 402 && options?.storage) {
-      await clearPermitSignatureFromCache(options.storage, {
+    if (secondResponse.status === 402) {
+      await clearPermitSignatureFromCache(storage, {
         chainId: paymentChainId,
         asset: selectedPaymentRequirements.asset,
         owner: getAddress(account.address),
         spender: getAddress(selectedPaymentRequirements.payTo),
       });
     }

Also applies to: 143-143, 165-172

📜 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 e681bbd and a9ab172.

📒 Files selected for processing (8)
  • .changeset/warm-clouds-judge.md (1 hunks)
  • apps/portal/src/app/x402/server/page.mdx (1 hunks)
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (3 hunks)
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (3 hunks)
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx (2 hunks)
  • packages/thirdweb/src/x402/fetchWithPayment.ts (4 hunks)
  • packages/thirdweb/src/x402/permitSignatureStorage.ts (1 hunks)
  • packages/thirdweb/src/x402/sign.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts
  • packages/thirdweb/src/x402/permitSignatureStorage.ts
  • packages/thirdweb/src/react/web/hooks/x402/useFetchWithPayment.tsx
🧰 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 TypeScript 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 in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

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

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/x402/sign.ts
  • packages/thirdweb/src/x402/fetchWithPayment.ts
  • packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts
🧬 Code graph analysis (2)
packages/thirdweb/src/x402/sign.ts (5)
packages/thirdweb/src/x402/schemas.ts (2)
  • RequestedPaymentRequirements (40-42)
  • RequestedPaymentPayload (24-26)
packages/thirdweb/src/x402/types.ts (2)
  • x402Version (14-14)
  • ERC20TokenAmount (125-138)
packages/thirdweb/src/x402/permitSignatureStorage.ts (3)
  • PermitCacheKeyParams (16-21)
  • getPermitSignatureFromCache (40-54)
  • savePermitSignatureToCache (64-82)
packages/thirdweb/src/exports/extensions/erc20.ts (1)
  • allowance (35-35)
packages/thirdweb/src/exports/x402.ts (1)
  • ERC20TokenAmount (17-17)
packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (1)
packages/thirdweb/src/react/core/hooks/x402/useFetchWithPaymentCore.ts (1)
  • useFetchWithPaymentCore (41-159)
⏰ 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, webpack)
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Unit Tests
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/portal/src/app/x402/server/page.mdx (1)

135-135: LGTM!

The documentation accurately reflects the new automatic caching behavior for permit signatures, correctly noting that backend storage is no longer needed for this purpose.

packages/thirdweb/src/react/native/hooks/x402/useFetchWithPayment.ts (1)

97-100: LGTM!

The storage defaulting logic is correct and consistent with the web implementation. When options?.storage is not provided, it properly falls back to nativeLocalStorage for the React Native environment.

packages/thirdweb/src/x402/sign.ts (1)

228-246: LGTM!

The storage parameter is correctly threaded through createPaymentHeader to signPaymentHeader, enabling the caching behavior for the "upto" scheme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

packages Portal Involves changes to the Portal (docs) codebase. SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants