-
Notifications
You must be signed in to change notification settings - Fork 619
[Paywall] Support string price format when tokenAddress is not provided #8330
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
[Paywall] Support string price format when tokenAddress is not provided #8330
Conversation
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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. |
|
Caution Review failedThe pull request is closed. WalkthroughThe GET paywall route now treats Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant PaywallRoute as /api/paywall (GET)
Client->>PaywallRoute: GET /api/paywall?amount=...&tokenAddress=... (tokenAddress optional)
alt tokenAddress provided
PaywallRoute->>PaywallRoute: build price object\n{ amount, asset: { address, decimals } }
PaywallRoute-->>Client: 200 { price: { amount, asset } }
else tokenAddress missing
PaywallRoute->>PaywallRoute: use plain amount string
PaywallRoute-->>Client: 200 { price: "amount" }
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8330 +/- ##
=======================================
Coverage 54.89% 54.89%
=======================================
Files 919 919
Lines 60702 60702
Branches 4130 4130
=======================================
Hits 33321 33321
Misses 27279 27279
Partials 102 102
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/playground-web/src/app/api/paywall/route.ts (1)
77-79: Consider omitting tokenAddress from response when undefined.When
tokenAddressis not provided, it will appear asnullorundefinedin the response. For cleaner API design, consider conditionally including it only when present.payment: { amount, - tokenAddress, + ...(tokenAddress && { tokenAddress }), },
📜 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 (1)
apps/playground-web/src/app/api/paywall/route.ts(2 hunks)
🧰 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@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless 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@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless 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
**/*.{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
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
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor 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: Bearerheader – 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, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/playground-web/src/app/api/paywall/route.ts
🧬 Code graph analysis (1)
apps/playground-web/src/app/api/paywall/route.ts (1)
apps/playground-web/src/app/payments/x402/components/constants.ts (1)
token(4-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Unit Tests
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/playground-web/src/app/api/paywall/route.ts (2)
44-44: LGTM: tokenAddress is now optional.The removal of the default fallback to
token.addresscorrectly makes tokenAddress optional, aligning with the PR objective to support string price format when tokenAddress is not provided.
56-62: Add "$" prefix to amount when tokenAddress is absent to match thirdweb's expected USD string format.The code correctly passes a converted object when
tokenAddressis provided, but whentokenAddressis absent (line 62), thirdweb'ssettlePaymentAPI expects a USD-formatted string (e.g.,"$0.01"). Currently,amountis passed as a raw numeric string (e.g.,"0.01") from the query parameter. Update line 62 to prepend "$" to the amount:price: tokenAddress ? {...} : \${amount}``⛔ Skipped due to learnings
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.
| price: tokenAddress ? { | ||
| amount: toUnits(amount, parseInt(decimals)).toString(), | ||
| asset: { | ||
| address: tokenAddress as `0x${string}`, | ||
| decimals: decimals ? parseInt(decimals) : token.decimals, | ||
| }, | ||
| }, | ||
| } : amount, |
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.
Inconsistent decimals fallback creates risk of incorrect unit conversion.
Line 60 falls back to token.decimals when the decimals query parameter is not provided, but this is only safe when tokenAddress matches the imported token.address. If a different token address is provided without specifying decimals, the conversion at line 57 will use the wrong decimal places, leading to incorrect payment amounts.
Consider one of these solutions:
Option 1 (recommended): Require decimals when tokenAddress is provided:
const amount = queryParams.get("amount") || "0.01";
const payTo = queryParams.get("payTo") ?? undefined;
const tokenAddress = queryParams.get("tokenAddress");
- const decimals = queryParams.get("decimals") || token.decimals.toString();
+ const decimals = queryParams.get("decimals");
// ... later in validation
if (!chainId) {
return NextResponse.json(
{ error: "Missing required parameters" },
{ status: 400 },
);
}
+
+ if (tokenAddress && !decimals) {
+ return NextResponse.json(
+ { error: "decimals parameter is required when tokenAddress is provided" },
+ { status: 400 },
+ );
+ }Option 2: Only use the fallback when tokenAddress matches the known token:
price: tokenAddress ? {
amount: toUnits(amount, parseInt(decimals)).toString(),
asset: {
address: tokenAddress as `0x${string}`,
- decimals: decimals ? parseInt(decimals) : token.decimals,
+ decimals: parseInt(decimals || (tokenAddress === token.address ? token.decimals.toString() : "18")),
},
} : amount,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| price: tokenAddress ? { | |
| amount: toUnits(amount, parseInt(decimals)).toString(), | |
| asset: { | |
| address: tokenAddress as `0x${string}`, | |
| decimals: decimals ? parseInt(decimals) : token.decimals, | |
| }, | |
| }, | |
| } : amount, | |
| price: tokenAddress ? { | |
| amount: toUnits(amount, parseInt(decimals)).toString(), | |
| asset: { | |
| address: tokenAddress as `0x${string}`, | |
| decimals: parseInt(decimals || (tokenAddress === token.address ? token.decimals.toString() : "18")), | |
| }, | |
| } : amount, |
🤖 Prompt for AI Agents
In apps/playground-web/src/app/api/paywall/route.ts around lines 56 to 62, the
code falls back to token.decimals when the decimals query param is missing even
if a different tokenAddress is provided, which can produce incorrect unit
conversion; change the logic so that when tokenAddress is provided decimals is
required: validate that decimals is present (return a 4xx/error response if
missing), parse it with parseInt once and use that parsed value for toUnits and
the asset.decimals field; alternatively (if you prefer the other option) only
use token.decimals as a fallback when tokenAddress === token.address, but the
preferred fix is to enforce and validate the decimals parameter when a custom
tokenAddress is used.
size-limit report 📦
|
1d2d827 to
8d53618
Compare

PR-Codex overview
This PR focuses on modifying the handling of the
tokenAddressin theGETfunction of thepaywallroute. It adjusts how thepriceobject is constructed based on the presence oftokenAddress.Detailed summary
const tokenAddressto directly assign the value fromqueryParamswithout a fallback.priceobject to conditionally includeamountonly iftokenAddressis present.priceobject to reflect the new conditional logic.Summary by CodeRabbit