Skip to content

fix: execute native within noblocks#421

Merged
chibie merged 7 commits intomainfrom
fix-execute-native-within-noblocks
Mar 13, 2026
Merged

fix: execute native within noblocks#421
chibie merged 7 commits intomainfrom
fix-execute-native-within-noblocks

Conversation

@onahprosper
Copy link
Copy Markdown
Collaborator

@onahprosper onahprosper commented Mar 13, 2026

Description

Describe the purpose of this PR along with any background information and the impacts of the proposed change. For the benefit of the community, please do not assume prior context.

Provide details that support your chosen implementation, including: breaking changes, alternatives considered, changes to the API, contracts etc.

References

Include any links supporting this change such as a:

If there are no references, simply delete this section.

Testing

Describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this project has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors.

Please include any manual steps for testing end-to-end or functionality not covered by unit/integration tests.

Also include details of the environment this PR was developed in (language/platform/browser version).

  • This change adds test coverage for new/changed/fixed functionality

Checklist

  • I have added documentation and tests for new/changed functionality in this PR
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not main

By submitting a PR, I agree to Paycrest's Contributor Code of Conduct and Contribution Guide.

Summary by CodeRabbit

  • New Features

    • New bundler API endpoints for generating/executing userops (upgrade & transfer), sponsored execution, and Nexus status checks; comprehensive userop and sponsored-execution tooling.
  • Chores

    • Bundler traffic routed through internal API (/api/bundler); UI flows updated to use it with optional Authorization headers.
    • Environment/config updates: external bundler URL now optional, sponsor private key added to env example, and delegation contract config removed.
  • Other

    • Middleware updated to include /api/bundler routes for instrumentation.

* Added new API routes for executing user operations, generating transfer user operations, and checking Nexus account status.
* Introduced support for EIP-7702 sponsored transactions and delegation contract handling.
* Updated bundler server URL configuration to use a default path, simplifying environment variable requirements.
* Refactored existing components and hooks to utilize the new bundler API structure, enhancing transaction handling and user experience.
* Removed deprecated delegation contract address handling, streamlining the codebase for future development.
* Cleaned up the .env.example file by removing extra blank lines for improved readability and consistency.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a server-side bundler subsystem (ERC‑4337 / EIP‑7702): new bundler libraries and constants, six new Next.js API routes under /api/bundler, client changes routing bundler calls to internal /api/bundler with Authorization headers, and .env.example documenting sponsor private key and clearing public bundler URL.

Changes

Cohort / File(s) Summary
Environment & Types
\.env\.example, app/lib/config.ts, app/types.ts
Documented SPONSOR_EVM_WALLET_PRIVATE_KEY, cleared NEXT_PUBLIC_BUNDLER_SERVER_URL, removed delegationContractAddress override from config and types.
Bundler Core
app/lib/bundler/chains.ts, app/lib/bundler/config.ts, app/lib/bundler/userOp.ts, app/lib/bundler/executeSponsored.ts
New server-side bundler library: supported chains and RPC/client helpers, constants/config, full userOp generation/execution (ERC‑4337 v0.6) with paymaster/prefund logic, and sponsored execution (EIP‑7702) including delegation handling, fee estimation, and retry behavior.
Server API Routes
app/api/bundler/execute-sponsored/route.ts, app/api/bundler/execute/route.ts, app/api/bundler/generate-transfer-userop/route.ts, app/api/bundler/generate-userop/route.ts, app/api/bundler/is-nexus/route.ts, app/api/bundler/nexus/status/route.ts
Added POST/GET endpoints for generating/executing userOps, transfer userOps, sponsored execution, and Nexus status checks with input validation, client resolution, prefund/execution orchestration, and error mapping.
Client Integration & UI
app/components/WalletTransferApprovalModal.tsx, app/hooks/useSmartWalletTransfer.ts, app/pages/TransactionPreview.tsx
Switched bundler calls to fixed internal /api/bundler path, added access-token Authorization headers and retry for token retrieval, removed config-based bundler URL validation, and adjusted minor UI flow (suppressed pre-delegation toast).
Middleware
middleware.ts
Extended matcher to instrument /api/bundler and /api/bundler/:path*.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Client Component
    participant APIRoute as /api/bundler (server)
    participant Chain as chains.ts (client creation)
    participant UserOp as userOp module
    participant RPC as Blockchain/RPC

    User->>Client: Request transfer/upgrade
    Client->>APIRoute: POST /generate-userop
    APIRoute->>Chain: parseChainId / getClients
    Chain->>RPC: create publicClient
    APIRoute->>UserOp: generateUserOp(publicClient,...)
    UserOp->>RPC: estimate gas / fetch fees / call paymaster
    UserOp-->>APIRoute: unsigned userOp + hash
    APIRoute-->>Client: return userOp
    Client->>APIRoute: POST /execute (signed userOp)
    APIRoute->>UserOp: ensurePrefundIfNeeded / executeUserOp
    UserOp->>RPC: check balance / send prefund / submit to EntryPoint
    UserOp-->>APIRoute: transactionHash
    APIRoute-->>Client: execution result
Loading
sequenceDiagram
    actor User
    participant Client as Client Component
    participant APIRoute as /api/bundler/execute-sponsored
    participant Chain as chains.ts (getClients with sponsor)
    participant Sponsored as executeSponsored module
    participant RPC as Blockchain/RPC

    User->>Client: Initiate sponsored tx
    Client->>APIRoute: POST /execute-sponsored (rpcUrl, account, callData, eip7702Authorization?)
    APIRoute->>Chain: getClients(chainId, rpcUrl) (walletClient uses sponsor key)
    APIRoute->>Sponsored: executeSponsored(...)
    alt eip7702Authorization present
        Sponsored->>Sponsored: parseAuth, build delegation tx
        Sponsored->>RPC: send delegation tx (type: eip7702)
        RPC-->>Sponsored: receipt
        Sponsored-->>APIRoute: delegationTransactionHash
    else no delegation
        Sponsored->>RPC: get nonce, estimate fees
        Sponsored->>RPC: send execution tx with sponsor wallet
        RPC-->>Sponsored: tx hash / receipt
        Sponsored-->>APIRoute: transactionHash
    end
    APIRoute-->>Client: return tx hash(es)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • 5ran6
  • chibie

Poem

🐰 I hopped through code with tiny feet,

Spun userOps light and fees discreet,
Routes now hum and sponsors cheer,
Transactions leap — carrots near,
A joyful hop — bundler springtime sweet!

🚥 Pre-merge checks | ❌ 5

❌ Failed checks (5 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR title 'fix: execute native within noblocks' is vague and does not match the extensive bundler-focused implementation changes shown in the changeset. Revise title to accurately reflect the actual changes, e.g., 'feat: implement bundler API routes with sponsored execution and internal authentication' or similar.
Description check ⚠️ Warning PR description is entirely a template with no implementation-specific details, background, testing steps, or explanation of the bundler changes made. Fill in all template sections with concrete details: describe bundler implementation, EIP-7702 sponsorship, chain/client management, API routes, internal auth flow, and testing performed.
Linked Issues check ⚠️ Warning Linked issue #407 concerns recipient name editability and alert UI; the changeset implements bundler infrastructure (APIs, sponsored execution, chain management). The issue and changeset are unrelated. Link correct issues that address bundler implementation, sponsored execution, and internal authentication. If #407 is incorrect, update the PR to reference the intended issues.
Out of Scope Changes check ⚠️ Warning Major scope mismatch: PR implements ~1,800 LOC of bundler APIs, sponsored execution, and chain management; linked issue #407 is about recipient UI editing. Most changes are out of scope for the stated objective. Either relink to correct bundler-related issues or split this PR into separate changes: one for recipient UI (#407) and one for bundler infrastructure.
Docstring Coverage ⚠️ Warning Docstring coverage is 62.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-execute-native-within-noblocks
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread app/api/bundler/execute-sponsored/route.ts Outdated
Comment thread app/api/bundler/execute-sponsored/route.ts Outdated
Comment thread app/api/bundler/execute/route.ts Outdated
Comment thread app/pages/TransactionPreview.tsx Outdated
Comment thread app/pages/TransactionPreview.tsx Outdated
Comment thread .env.example
…utes

* Eliminated unnecessary console log statements in the bundler execution routes and the TransactionPreview component to clean up the code and improve performance.
Copy link
Copy Markdown
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: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/hooks/useSmartWalletTransfer.ts (1)

236-246: ⚠️ Potential issue | 🟡 Minor

Update stale error message referencing removed configuration.

The error message at line 244 still references NEXT_PUBLIC_BUNDLER_SERVER_URL, but the hook now uses a hardcoded internal path. This would confuse users debugging connection issues.

🔧 Suggested fix
           throw new Error(
             isNetworkErr
-              ? `Cannot reach the transaction server. Check that NEXT_PUBLIC_BUNDLER_SERVER_URL is set and the server is running. For ${chainName}, ensure the server has been restarted with support for this network.`
+              ? `Cannot reach the bundler API. For ${chainName}, ensure the server is running and supports this network.`
               : (fetchErr as Error)?.message ?? "Network request failed",
           );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/hooks/useSmartWalletTransfer.ts` around lines 236 - 246, The catch block
in useSmartWalletTransfer (catching fetchErr) still mentions
NEXT_PUBLIC_BUNDLER_SERVER_URL even though the code uses a hardcoded internal
path; update the thrown Error message in that catch to remove the stale env var
reference and instead mention the actual internal endpoint or service (e.g., the
bundler/transaction server or the hardcoded path used), so the error reads
something like “Cannot reach the transaction server at <internal path> — ensure
the server is running/restarted with support for this network” and keep the
existing branch for non-network errors returning fetchErr.message; modify the
message in the catch in useSmartWalletTransfer accordingly.
🧹 Nitpick comments (4)
app/api/bundler/is-nexus/route.ts (1)

1-45: Re-export the canonical handler instead of duplicating it.

This alias is byte-for-byte identical to app/api/bundler/nexus/status/route.ts; keeping one implementation avoids drift the next time validation or response fields change.

Suggested fix
-import { NextRequest, NextResponse } from "next/server";
-import { getClients, parseChainId, parseRpcUrl } from "@/app/lib/bundler/chains";
-import { getNexusStatus } from "@/app/lib/bundler/userOp";
-
-/**
- * GET /api/bundler/is-nexus?smartAccountAddress=0x...&chainId=56&rpcUrl=...
- * Alias for nexus/status for compatibility with upgrade-server path.
- */
-export async function GET(request: NextRequest) {
-  try {
-    const { searchParams } = request.nextUrl;
-    const smartAccountAddress = searchParams.get("smartAccountAddress");
-    const chainIdRaw = searchParams.get("chainId");
-    const rpcUrl = parseRpcUrl(searchParams.get("rpcUrl"));
-
-    if (!rpcUrl) {
-      return NextResponse.json({ error: "rpcUrl is required" }, { status: 400 });
-    }
-    if (!smartAccountAddress || !/^0x[0-9a-fA-F]{40}$/.test(smartAccountAddress)) {
-      return NextResponse.json(
-        { error: "Query param smartAccountAddress (0x + 40 hex) is required" },
-        { status: 400 }
-      );
-    }
-
-    const chainId = parseChainId(chainIdRaw);
-    const { publicClient } = getClients(chainId, rpcUrl);
-    const status = await getNexusStatus(publicClient, smartAccountAddress as `0x${string}`);
-
-    return NextResponse.json({
-      smartAccountAddress,
-      chainId,
-      rpcUrl,
-      ...status,
-    });
-  } catch (error) {
-    return NextResponse.json(
-      {
-        error:
-          error instanceof Error ? error.message : "Failed to check nexus status",
-      },
-      { status: 400 }
-    );
-  }
-}
+export { GET } from "../nexus/status/route";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/is-nexus/route.ts` around lines 1 - 45, This file duplicates
the nexus status handler; replace the local GET implementation by re-exporting
the canonical GET handler from the existing nexus status route module (the file
that currently implements the nexus/status GET export). Remove the duplicated
logic (validation, parseChainId/parseRpcUrl/getClients/getNexusStatus usage) and
instead add a single re-export like: export { GET } from '<the nexus status
route module>'; ensure the exported symbol is the same GET handler so this alias
stays byte-for-byte identical going forward.
app/api/bundler/generate-userop/route.ts (2)

61-65: Redundant null coalescing for validated field.

rpcUrl is already validated as required at line 24, so the ?? null fallback on line 64 is unreachable.

🔧 Minor cleanup
     return NextResponse.json({
       ...result,
       chainId,
-      rpcUrl: rpcUrl ?? null,
+      rpcUrl,
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/generate-userop/route.ts` around lines 61 - 65, The rpcUrl
field is validated as required earlier, so remove the redundant `?? null`
fallback when returning the response; update the return in the route handler
that calls NextResponse.json to simply include rpcUrl (e.g., use rpcUrl directly
instead of `rpcUrl ?? null`) so the response mirrors the validated value and
avoids unreachable code.

8-11: Consider warning when PAYMASTER_URL is set but PAYMASTER_API_KEY is missing.

When PAYMASTER_URL is configured but PAYMASTER_API_KEY is absent, the paymaster request proceeds without the API key header. Depending on the paymaster service, this may result in unclear 401/403 errors rather than a helpful diagnostic message.

Consider logging a warning during startup or request handling when this configuration mismatch is detected.

Also applies to: 54-56

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/generate-userop/route.ts` around lines 8 - 11, Add a
startup/request-time warning when PAYMASTER_URL is set but PAYMASTER_API_KEY is
missing: detect the mismatch by checking the constants PAYMASTER_URL and
PAYMASTER_API_KEY and emit a clear warning (via the app logger or console.warn)
either during module initialization or at the start of the request handler
(e.g., the route/generate handler) so callers see a diagnostic instead of opaque
401/403 responses; apply the same check and warning wherever the PAYMASTER_*
values are used (including the other occurrence referenced at lines 54-56).
app/api/bundler/execute/route.ts (1)

66-78: Clarify why paymasterAndData is hardcoded.

Line 76 sets paymasterAndData: "0x" regardless of what's in raw.paymasterAndData. This appears intentional for self-funded execution, but a brief comment would clarify this is deliberate and prevent future maintainers from "fixing" it.

📝 Add clarifying comment
     const signedUserOp: SerializedUserOperation = {
       sender: getAddress(sender) as `0x${string}`,
       nonce: String(raw.nonce ?? ""),
       initCode: (raw.initCode ?? "0x") as `0x${string}`,
       callData: (raw.callData ?? "0x") as `0x${string}`,
       callGasLimit: String(raw.callGasLimit ?? "0"),
       verificationGasLimit: String(raw.verificationGasLimit ?? "0"),
       preVerificationGas: String(raw.preVerificationGas ?? "0"),
       maxFeePerGas: String(raw.maxFeePerGas ?? "0"),
       maxPriorityFeePerGas: String(raw.maxPriorityFeePerGas ?? "0"),
+      // This route is for self-funded execution; paymaster data is intentionally ignored
       paymasterAndData: "0x",
       signature: (raw.signature ?? "0x") as `0x${string}`,
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/execute/route.ts` around lines 66 - 78, The object
signedUserOp currently sets paymasterAndData: "0x" unconditionally which is
intentional for self-funded execution; update the signedUserOp construction (in
route.ts where signedUserOp is created) to include a short clarifying comment
above or inline explaining that paymasterAndData is deliberately hardcoded to
"0x" to enforce self-funded ops and that raw.paymasterAndData is intentionally
ignored, so future maintainers won't change it; optionally mention any
security/behavior rationale (e.g., preventing external paymaster usage) and
reference raw.paymasterAndData in the comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.example:
- Around line 140-143: The comment in .env.example references the wrong env var
name: it mentions SPONSOR_PRIVATE_KEY but the actual variable used is
SPONSOR_EVM_WALLET_PRIVATE_KEY (as read in app/lib/bundler/chains.ts). Update
the comment text to refer to SPONSOR_EVM_WALLET_PRIVATE_KEY (or add both names
if you want to clarify legacy vs current) so the fallback guidance matches the
variable declaration and the code that consumes it.

In `@app/api/bundler/execute-sponsored/route.ts`:
- Line 30: Remove the two leftover debug console.log calls that print
delegationContractAddress and the other temporary debug at the same spot;
replace them with structured logging using the app's logger at an appropriate
level (e.g., logger.debug or processLogger.debug) or delete entirely if not
needed. Locate the console.log("delegationContractAddress",
delegationContractAddress) and the other console.log on the same function/route
in execute-sponsored/route.ts and either remove them or swap to a structured
logger call that includes a clear message and the variable (e.g., logger.debug({
msg: "delegationContractAddress", delegationContractAddress })). Ensure no raw
console.log remains in that route handler.

In `@app/api/bundler/generate-transfer-userop/route.ts`:
- Around line 59-68: The catch currently returns 500 for all failures; changes
should detect client-side validation errors from parseChainId, parseRpcUrl, the
calls[] validation and BigInt parsing and return a 400 Bad Request instead.
Implement a small ValidationError (or throw Error with a distinct name) from the
input validation paths (where parseChainId, parseRpcUrl, calls[] checks and
BigInt(...) are invoked) and in the catch block test for that error type/name
(or known parsing error names) to return NextResponse.json({ error:
error.message }, { status: 400 }) for validation failures, falling back to the
existing 500 response for other errors. Ensure you update the code locations
that call parseChainId/parseRpcUrl/BigInt and any calls[] validation to throw
the new ValidationError so the catch can distinguish them.

In `@app/components/WalletTransferApprovalModal.tsx`:
- Around line 275-288: The bundlerServerUrl validation in
WalletTransferApprovalModal incorrectly uses new URL(bundlerServerUrl) on a
relative path (bundlerServerUrl = "/api/bundler"), causing the catch to throw a
misleading "Invalid NEXT_PUBLIC_BUNDLER_SERVER_URL" error; remove the entire URL
parsing/validation try/catch block that references new URL(...) and the protocol
checks so the hardcoded internal path is accepted, leaving the meeApiKey
existence check intact.

In `@app/lib/bundler/chains.ts`:
- Around line 31-35: The parseRpcUrl function currently allows arbitrary URLs
which lets callers proxy requests to any host; change parseRpcUrl to resolve
incoming values against the server-side chain configuration (or a strict host
allowlist) instead of accepting arbitrary strings: lookup the requested chain
identifier in the server-side chains config and return the configured RPC URL,
or if you must accept a URL validate the hostname against an explicit allowlist
and reject loopback (localhost, 127.0.0.0/8, ::1), link-local and private ranges
(10/8, 172.16/12, 192.168/16, 169.254/16, etc.) and non-HTTP schemes, throwing
an error from parseRpcUrl when the URL is not an approved/resolved endpoint so
callers of parseRpcUrl/http(rpcUrl) cannot target arbitrary hosts.
- Around line 58-69: getClients currently always calls getSponsorAccount and
constructs a WalletClient, causing handlers that only need a PublicClient to
fail when no sponsor key exists; change getClients to make wallet creation
opt-in by adding an optional parameter (e.g., includeWallet: boolean = true) or
accept an optional sponsorAccount argument, then only call getSponsorAccount and
createWalletClient when includeWallet is true (or sponsorAccount is provided);
ensure the function signature and return type reflect that walletClient can be
undefined (or return a separate object shape) and update callers of getClients
(e.g., endpoints /api/bundler/nexus/status, /api/bundler/is-nexus,
/api/bundler/generate-transfer-userop) to call it with includeWallet=false when
they only need the PublicClient; keep SUPPORTED_CHAINS, createPublicClient, and
transport: http(rpcUrl) usage unchanged.
- Around line 19-20: The parseChainId function currently defaults missing/empty
input to bsc.id (56); change it to treat undefined/null/'' as invalid and throw
a clear error instead. In parseChainId(value) check for value === undefined ||
value === null || value === '' and throw a descriptive Error (e.g. "Missing or
empty chainId"); then parse numeric strings (Number(value) or parseInt) and
validate the result is a finite positive integer before returning it. Keep
references to parseChainId and bsc.id so reviewers can find the location.

In `@app/lib/bundler/executeSponsored.ts`:
- Around line 61-75: In the executeSponsored branch that checks
params.eip7702Authorization, the code sets delegationTransactionHash via
walletClient.sendTransaction and waits for the receipt but returns without
exposing that delegation hash; update the early return so it includes the
delegationTransactionHash (e.g., return an object containing both
transactionHash and delegationTransactionHash or a dedicated
delegationTransactionHash field) so callers can detect the delegation path after
parseAuth/walletClient.sendTransaction and
publicClient.waitForTransactionReceipt.

In `@app/lib/bundler/userOp.ts`:
- Around line 787-812: The current code hard-caps callGasLimit and
verificationGasLimit using minBigInt against GAS_CONFIG.callGasLimit and
GAS_CONFIG.verificationGasLimit even when estimateGas succeeds, which forces
large legitimate estimates down and causes AA23/OOG; change the logic so the
static GAS_CONFIG values act only as fallbacks when estimation fails: keep the
estimate path from publicClient.estimateGas (the try block and call to
multiplyByBps) and remove the minBigInt clamping against GAS_CONFIG.callGasLimit
there, only apply GAS_CONFIG.callGasLimit as a ceiling if estimateGas threw
(i.e., when you set the fallback callGasLimit in the catch), and similarly
ensure verificationGasLimit (computed from callGasLimit and constants) is not
forcibly minBigInt-capped by GAS_CONFIG.verificationGasLimit when an actual
estimate was available — instead use the config only as a fallback/maximum when
no estimate exists.
- Around line 813-827: The multipliers are currently shrinking suggested fees
(5_000n = 50%) and the fallback can produce maxPriorityFeePerGas > maxFeePerGas;
change multiplyByBps(...) calls (used where fees are set after
publicClient.estimateFeesPerGas() and publicClient.getGasPrice()) to use a
multiplier > 10_000n (e.g., 15_000n) so we add headroom instead of reducing
fees, and ensure maxPriorityFeePerGas is capped to not exceed maxFeePerGas by
computing it as min(maxFeePerGas, maxBigInt(maxFeePerGas / 10n, 1_000_000n))
(use functions multiplyByBps and maxBigInt and the variables
maxFeePerGas/maxPriorityFeePerGas to locate the fixes).

In `@app/pages/TransactionPreview.tsx`:
- Line 430: Remove the debug console.log statements left in
TransactionPreview.tsx: delete the console.log("res", res) statement and the
console.log that prints the transaction hash (e.g., the one logging txHash)
inside the transaction handling/submit flow (look for the
submit/handleSend/handleConfirm function or where "res" and "txHash" variables
are used). Ensure no console.* debug logging remains in that component before
merging.

---

Outside diff comments:
In `@app/hooks/useSmartWalletTransfer.ts`:
- Around line 236-246: The catch block in useSmartWalletTransfer (catching
fetchErr) still mentions NEXT_PUBLIC_BUNDLER_SERVER_URL even though the code
uses a hardcoded internal path; update the thrown Error message in that catch to
remove the stale env var reference and instead mention the actual internal
endpoint or service (e.g., the bundler/transaction server or the hardcoded path
used), so the error reads something like “Cannot reach the transaction server at
<internal path> — ensure the server is running/restarted with support for this
network” and keep the existing branch for non-network errors returning
fetchErr.message; modify the message in the catch in useSmartWalletTransfer
accordingly.

---

Nitpick comments:
In `@app/api/bundler/execute/route.ts`:
- Around line 66-78: The object signedUserOp currently sets paymasterAndData:
"0x" unconditionally which is intentional for self-funded execution; update the
signedUserOp construction (in route.ts where signedUserOp is created) to include
a short clarifying comment above or inline explaining that paymasterAndData is
deliberately hardcoded to "0x" to enforce self-funded ops and that
raw.paymasterAndData is intentionally ignored, so future maintainers won't
change it; optionally mention any security/behavior rationale (e.g., preventing
external paymaster usage) and reference raw.paymasterAndData in the comment.

In `@app/api/bundler/generate-userop/route.ts`:
- Around line 61-65: The rpcUrl field is validated as required earlier, so
remove the redundant `?? null` fallback when returning the response; update the
return in the route handler that calls NextResponse.json to simply include
rpcUrl (e.g., use rpcUrl directly instead of `rpcUrl ?? null`) so the response
mirrors the validated value and avoids unreachable code.
- Around line 8-11: Add a startup/request-time warning when PAYMASTER_URL is set
but PAYMASTER_API_KEY is missing: detect the mismatch by checking the constants
PAYMASTER_URL and PAYMASTER_API_KEY and emit a clear warning (via the app logger
or console.warn) either during module initialization or at the start of the
request handler (e.g., the route/generate handler) so callers see a diagnostic
instead of opaque 401/403 responses; apply the same check and warning wherever
the PAYMASTER_* values are used (including the other occurrence referenced at
lines 54-56).

In `@app/api/bundler/is-nexus/route.ts`:
- Around line 1-45: This file duplicates the nexus status handler; replace the
local GET implementation by re-exporting the canonical GET handler from the
existing nexus status route module (the file that currently implements the
nexus/status GET export). Remove the duplicated logic (validation,
parseChainId/parseRpcUrl/getClients/getNexusStatus usage) and instead add a
single re-export like: export { GET } from '<the nexus status route module>';
ensure the exported symbol is the same GET handler so this alias stays
byte-for-byte identical going forward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 81f631d6-53dc-48c9-9bca-8f8b25d8171b

📥 Commits

Reviewing files that changed from the base of the PR and between 2e51068 and bbf2b9c.

📒 Files selected for processing (16)
  • .env.example
  • app/api/bundler/execute-sponsored/route.ts
  • app/api/bundler/execute/route.ts
  • app/api/bundler/generate-transfer-userop/route.ts
  • app/api/bundler/generate-userop/route.ts
  • app/api/bundler/is-nexus/route.ts
  • app/api/bundler/nexus/status/route.ts
  • app/components/WalletTransferApprovalModal.tsx
  • app/hooks/useSmartWalletTransfer.ts
  • app/lib/bundler/chains.ts
  • app/lib/bundler/config.ts
  • app/lib/bundler/executeSponsored.ts
  • app/lib/bundler/userOp.ts
  • app/lib/config.ts
  • app/pages/TransactionPreview.tsx
  • app/types.ts
💤 Files with no reviewable changes (2)
  • app/types.ts
  • app/lib/config.ts

Comment thread .env.example
Comment thread app/api/bundler/execute-sponsored/route.ts Outdated
Comment thread app/api/bundler/generate-transfer-userop/route.ts
Comment thread app/components/WalletTransferApprovalModal.tsx Outdated
Comment thread app/lib/bundler/chains.ts Outdated
Comment thread app/lib/bundler/chains.ts Outdated
Comment thread app/lib/bundler/executeSponsored.ts
Comment thread app/lib/bundler/userOp.ts
Comment thread app/lib/bundler/userOp.ts Outdated
Comment thread app/pages/TransactionPreview.tsx Outdated
Copy link
Copy Markdown
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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/bundler/execute/route.ts`:
- Around line 100-129: The code currently calls ensurePrefundIfNeeded(...) which
performs an irreversible depositTo before executeUserOp(...), risking lost
sponsor funds if execution later reverts; modify the flow to perform a preflight
simulation (e.g., simulateUserOp or callStatic style check) using the same
inputs derived from getRequiredPrefundWei(...) and signedUserOp, and only if the
simulation succeeds proceed to call ensurePrefundIfNeeded(...); alternatively
split the endpoint so prefund is an explicit separate action the client must
call (move ensurePrefundIfNeeded out of the executeUserOp handler) and surface
simulation results to the caller so the deposit is not made automatically prior
to a confirmed successful execution.
- Around line 42-55: The code is overwriting the client-signed UserOperation
fields (notably raw.sender and paymasterAndData) for deployed accounts; instead
preserve the original signed payload. Change the logic around variables sender,
hasInitCode, smartAccountAddress and paymasterAndData so you do not replace
raw.sender with smartAccountAddress when hasInitCode is false — always use
raw.sender as the canonical sender from the client (but validate it and
throw/return an error if it's malformed), and remove any code that silently
forces paymasterAndData = "0x"; apply the same fix to the similar block around
lines 66-77. Ensure validation uses the existing raw.sender checks (the regex)
and surface an explicit error on invalid inputs rather than substituting values.
- Around line 24-31: The handler currently calls getClients(chainId, rpcUrl)
before verifying that parsed chainId is valid; move or add validation after
parseChainId (using parseChainId and the chainId variable) so that if chainId is
unsupported or invalid you return a client error via NextResponse.json({ error:
"chainId is required or unsupported" }, { status: 400 }) instead of letting
getClients throw and produce a 500; ensure the same validation is applied at the
later check around the logic that currently lives at lines 92-97 to keep
behavior consistent.
- Around line 68-90: The current builder for signedUserOp zero-fills gas/fee
fields (callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas,
maxPriorityFeePerGas) which lets incomplete payloads pass; update the
construction/validation so these fields are required: remove the "?? '0'"
fallbacks and instead check raw.callGasLimit, raw.verificationGasLimit,
raw.preVerificationGas, raw.maxFeePerGas, and raw.maxPriorityFeePerGas for
null/undefined and return a 400 JSON error (similar to the existing
nonce/callData/signature checks) if any are missing or empty before creating
signedUserOp or submitting handleOps. Ensure you reference the signedUserOp
creation and the same validation pattern used for nonce/callData/signature.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c7286a7-36f4-46c7-978c-53d2f1215b5c

📥 Commits

Reviewing files that changed from the base of the PR and between bbf2b9c and 0805e20.

📒 Files selected for processing (3)
  • app/api/bundler/execute-sponsored/route.ts
  • app/api/bundler/execute/route.ts
  • app/pages/TransactionPreview.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/api/bundler/execute-sponsored/route.ts
  • app/pages/TransactionPreview.tsx

Comment thread app/api/bundler/execute/route.ts Outdated
Comment thread app/api/bundler/execute/route.ts
Comment thread app/api/bundler/execute/route.ts
Comment thread app/api/bundler/execute/route.ts
* Added new API routes for bundler execution and improved error responses for missing parameters.
* Updated RPC URL handling to prevent SSRF vulnerabilities by using server-side configuration.
* Refactored components and hooks to include authorization headers for API requests, enhancing security.
* Improved chain ID validation to ensure it is a finite positive integer.
* Cleaned up the .env.example file for better readability and consistency.
@chibie chibie changed the title Fix execute native within noblocks fix: execute native within noblocks Mar 13, 2026
* Adjusted the gas fee multipliers from 5,000 to 11,000 for both maxFeePerGas and maxPriorityFeePerGas to improve transaction cost estimation.
* Ensured maxPriorityFeePerGas is calculated using minBigInt to prevent exceeding maxFeePerGas, enhancing fee accuracy and reliability.
Copy link
Copy Markdown
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: 5

♻️ Duplicate comments (2)
app/lib/bundler/userOp.ts (1)

823-832: ⚠️ Potential issue | 🟠 Major

Clamp the fallback priority fee to maxFeePerGas.

In the gasPrice branches, low-fee networks can still produce maxPriorityFeePerGas > maxFeePerGas, which makes the returned EIP-1559 fee tuple invalid.

Suggested change
-      maxPriorityFeePerGas = maxBigInt(maxFeePerGas / 10n, 1_000_000n);
+      maxPriorityFeePerGas = minBigInt(
+        maxBigInt(maxFeePerGas / 10n, 1_000_000n),
+        maxFeePerGas
+      );
@@
-      maxPriorityFeePerGas = maxBigInt(maxFeePerGas / 10n, 1_000_000n);
+      maxPriorityFeePerGas = minBigInt(
+        maxBigInt(maxFeePerGas / 10n, 1_000_000n),
+        maxFeePerGas
+      );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/bundler/userOp.ts` around lines 823 - 832, In the gas-price fallback
branches (handling fees.gasPrice bigint and the publicClient.getGasPrice()
catch), clamp maxPriorityFeePerGas so it never exceeds maxFeePerGas: after
computing maxFeePerGas and maxPriorityFeePerGas (using multiplyByBps and
maxBigInt), set maxPriorityFeePerGas = min(maxPriorityFeePerGas, maxFeePerGas)
(or use an equivalent helper) to ensure the returned EIP-1559 tuple is valid;
update both the fees.gasPrice bigint branch and the publicClient.getGasPrice()
fallback in userOp.ts accordingly.
app/api/bundler/execute/route.ts (1)

76-109: ⚠️ Potential issue | 🟠 Major

Reject malformed userOp fields with 400s.

These checks only verify presence. Strings like "abc" in gas/fee fields or non-hex callData / signature will survive this block and then blow up later in BigInt() or ABI encoding, which currently turns a bad request into a 500.

Suggested change
+    const numericFields = [
+      "nonce",
+      "callGasLimit",
+      "verificationGasLimit",
+      "preVerificationGas",
+      "maxFeePerGas",
+      "maxPriorityFeePerGas",
+    ] as const;
+    for (const field of numericFields) {
+      try {
+        if (raw[field] == null || raw[field] === "") throw new Error();
+        BigInt(String(raw[field]));
+      } catch {
+        return NextResponse.json(
+          { error: `userOp.${field} must be a bigint-compatible value` },
+          { status: 400 }
+        );
+      }
+    }
+    if (typeof raw.callData !== "string" || !raw.callData.startsWith("0x")) {
+      return NextResponse.json(
+        { error: "userOp.callData must be a 0x-prefixed hex string" },
+        { status: 400 }
+      );
+    }
+    if (typeof raw.signature !== "string" || !raw.signature.startsWith("0x")) {
+      return NextResponse.json(
+        { error: "userOp.signature must be a 0x-prefixed hex string" },
+        { status: 400 }
+      );
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/execute/route.ts` around lines 76 - 109, The current presence
checks (missingGasOrFee) allow invalid strings like "abc" or non-hex data to
reach BigInt/ABI and cause 500s; add explicit validation before building
signedUserOp: verify numeric gas/fee/nonce fields (raw.callGasLimit,
raw.verificationGasLimit, raw.preVerificationGas, raw.maxFeePerGas,
raw.maxPriorityFeePerGas, raw.nonce) are parseable as non-negative integers
(BigInt-parsable or /^\d+$/) and return NextResponse.json({error: ...}, {status:
400}) on failure; validate hex fields initCode, callData, paymasterAndData (if
present), and signature are valid 0x-prefixed hex strings of allowed length (use
/^0x[0-9a-fA-F]*$/ and signature length check) before creating the
SerializedUserOperation object so malformed inputs produce 400s instead of 500s.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/bundler/nexus/status/route.ts`:
- Around line 9-40: The GET handler currently returns 400 for all errors; keep
the existing 400 responses for parameter validation (the smartAccountAddress
regex check) but change the outer catch to return status 500 for
runtime/RPC/provider failures (errors thrown by getClients or getNexusStatus).
Specifically, update the catch block in the exported async function GET to
detect non-validation errors and return NextResponse.json({ error: error
instanceof Error ? error.message : "Failed to check nexus status" }, { status:
500 }), while leaving the initial validation branch and its
NextResponse.json(..., { status: 400 }) unchanged so only bad input yields 400
and service/runtime failures yield 500.

In `@app/components/WalletTransferApprovalModal.tsx`:
- Around line 283-286: Replace the direct call to getAccessToken() with
getAccessTokenWithRetry() in WalletTransferApprovalModal so bundler requests get
a retried token; specifically, update the code that builds authHeaders
(currently using getAccessToken and falling back to {}) to call
getAccessTokenWithRetry(), await it, and use the returned token in the
Authorization header to avoid sending an empty header object for
middleware-protected /api/bundler/* calls.

In `@app/hooks/useSmartWalletTransfer.ts`:
- Line 132: The catch message is stale because the code hardcodes bundlerUrl =
"/api/bundler" in useSmartWalletTransfer.ts but still instructs users to set
NEXT_PUBLIC_BUNDLER_SERVER_URL; update the recovery text to match the
implementation: either (A) switch bundlerUrl to read from
process.env.NEXT_PUBLIC_BUNDLER_SERVER_URL (falling back to "/api/bundler") and
keep the existing env var troubleshooting message, or (B) keep the hardcoded
const bundlerUrl = "/api/bundler" and change the catch/log text to tell users
that the request targets the internal endpoint "/api/bundler" and to check
server routing/next.js API route availability (or contact the app owner) instead
of advising setting NEXT_PUBLIC_BUNDLER_SERVER_URL; apply the same change for
the messages around the other block (lines referenced near 239-249) so both
catch handlers are consistent.
- Around line 227-234: The code calls fetch(`${bundlerUrl}/execute-sponsored`)
after calling getAccessToken() which can return null; because the bundler routes
are middleware-protected you must require a non-null token before making the
request. Update the logic in useSmartWalletTransfer (the block around
getAccessToken, headers, and the fetch call) to check the returned value from
getAccessToken(), and if it is null either throw/return an explicit error or
surface a user-facing flow (e.g., request re-authentication) instead of calling
fetch without Authorization; ensure the headers.Authorization is only used when
a token exists and abort the network call when token === null so the protected
endpoint is not invoked with no credentials.

In `@app/lib/bundler/userOp.ts`:
- Around line 803-809: The code currently clamps preVerificationGas using
minBigInt(..., GAS_CONFIG.preVerificationGas) which forces large calldata
estimates back down; remove that upper clamp so the estimate can scale with
calldata. Specifically, replace the minBigInt call around
estimatedPreVerification/maxBigInt with a simple assignment that uses
preVerificationGas = maxBigInt(estimatedPreVerification, 45_000n) (keep
calldataBytes and estimatedPreVerification calculations intact) and stop
applying GAS_CONFIG.preVerificationGas as an upper bound in this calculation.

---

Duplicate comments:
In `@app/api/bundler/execute/route.ts`:
- Around line 76-109: The current presence checks (missingGasOrFee) allow
invalid strings like "abc" or non-hex data to reach BigInt/ABI and cause 500s;
add explicit validation before building signedUserOp: verify numeric
gas/fee/nonce fields (raw.callGasLimit, raw.verificationGasLimit,
raw.preVerificationGas, raw.maxFeePerGas, raw.maxPriorityFeePerGas, raw.nonce)
are parseable as non-negative integers (BigInt-parsable or /^\d+$/) and return
NextResponse.json({error: ...}, {status: 400}) on failure; validate hex fields
initCode, callData, paymasterAndData (if present), and signature are valid
0x-prefixed hex strings of allowed length (use /^0x[0-9a-fA-F]*$/ and signature
length check) before creating the SerializedUserOperation object so malformed
inputs produce 400s instead of 500s.

In `@app/lib/bundler/userOp.ts`:
- Around line 823-832: In the gas-price fallback branches (handling
fees.gasPrice bigint and the publicClient.getGasPrice() catch), clamp
maxPriorityFeePerGas so it never exceeds maxFeePerGas: after computing
maxFeePerGas and maxPriorityFeePerGas (using multiplyByBps and maxBigInt), set
maxPriorityFeePerGas = min(maxPriorityFeePerGas, maxFeePerGas) (or use an
equivalent helper) to ensure the returned EIP-1559 tuple is valid; update both
the fees.gasPrice bigint branch and the publicClient.getGasPrice() fallback in
userOp.ts accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 245c5dde-6bec-4d1c-afc5-43b3e561601e

📥 Commits

Reviewing files that changed from the base of the PR and between 0805e20 and 2d52d4b.

📒 Files selected for processing (13)
  • .env.example
  • app/api/bundler/execute-sponsored/route.ts
  • app/api/bundler/execute/route.ts
  • app/api/bundler/generate-transfer-userop/route.ts
  • app/api/bundler/generate-userop/route.ts
  • app/api/bundler/is-nexus/route.ts
  • app/api/bundler/nexus/status/route.ts
  • app/components/WalletTransferApprovalModal.tsx
  • app/hooks/useSmartWalletTransfer.ts
  • app/lib/bundler/chains.ts
  • app/lib/bundler/userOp.ts
  • app/pages/TransactionPreview.tsx
  • middleware.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/api/bundler/is-nexus/route.ts
  • .env.example
  • app/pages/TransactionPreview.tsx
  • app/lib/bundler/chains.ts
  • app/api/bundler/execute-sponsored/route.ts
  • app/api/bundler/generate-transfer-userop/route.ts
  • app/api/bundler/generate-userop/route.ts

Comment thread app/api/bundler/nexus/status/route.ts
Comment thread app/components/WalletTransferApprovalModal.tsx Outdated
Comment thread app/hooks/useSmartWalletTransfer.ts
Comment thread app/hooks/useSmartWalletTransfer.ts
Comment thread app/lib/bundler/userOp.ts
Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
app/lib/bundler/userOp.ts (1)

806-809: ⚠️ Potential issue | 🟠 Major

Remove the preVerificationGas hard ceiling.

Line [806] caps dynamic estimation with GAS_CONFIG.preVerificationGas, which can underprice large calldata userOps and cause pre-verification failure.

Suggested fix
-  preVerificationGas = minBigInt(
-    maxBigInt(estimatedPreVerification, 45_000n),
-    GAS_CONFIG.preVerificationGas
-  );
+  preVerificationGas = maxBigInt(
+    estimatedPreVerification,
+    GAS_CONFIG.preVerificationGas
+  );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/lib/bundler/userOp.ts` around lines 806 - 809, The code currently forces
preVerificationGas to be minBigInt(maxBigInt(estimatedPreVerification, 45_000n),
GAS_CONFIG.preVerificationGas), which applies a hard ceiling
(GAS_CONFIG.preVerificationGas) that can underprice large calldata userOps;
remove that ceiling so preVerificationGas is computed without capping by
GAS_CONFIG.preVerificationGas (e.g. compute preVerificationGas as
maxBigInt(estimatedPreVerification, 45_000n) or equivalent), updating the
expression that sets preVerificationGas and any logic that relies on it
(references: preVerificationGas, estimatedPreVerification, maxBigInt, minBigInt,
GAS_CONFIG.preVerificationGas).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/lib/bundler/userOp.ts`:
- Around line 618-628: generateUserOp is ignoring paymaster options because the
sponsorship block calling fetchPaymasterAndData was commented out; restore that
logic so when options.paymasterUrl (and optional options.paymasterApiKey) are
provided you call fetchPaymasterAndData(userOp, options.paymasterUrl,
options.paymasterApiKey), assign userOp.paymasterAndData =
sponsor.paymasterAndData and set numeric fields (preVerificationGas,
verificationGasLimit, callGasLimit) to BigInt if present; keep the default '0x'
fallback when no sponsor is returned and ensure any exceptions from
fetchPaymasterAndData are caught/logged so generateUserOp still returns a valid
userOp.
- Around line 202-212: The current parsing in the hex handling branch is too
permissive and may return bogus addresses; tighten it by explicitly validating
the revert selector and exact payload layout before slicing out the address:
check raw.slice(0,8) equals the known 4-byte selector (0x6ca7b806) and that
raw.length is at least the expected 4+32 bytes (i.e., 8+64 hex chars) before
extracting addrHex from the last 40 chars, and in the fallback branch only
accept fixed-length (or well-formed) payloads (validate raw.length and expected
padding/offset) before using raw.slice(...) and calling getAddress; use the
existing identifiers (hex, raw, addrHex, getAddress) and return a
failure/undefined or throw if validations fail instead of returning a
potentially bogus address.

---

Duplicate comments:
In `@app/lib/bundler/userOp.ts`:
- Around line 806-809: The code currently forces preVerificationGas to be
minBigInt(maxBigInt(estimatedPreVerification, 45_000n),
GAS_CONFIG.preVerificationGas), which applies a hard ceiling
(GAS_CONFIG.preVerificationGas) that can underprice large calldata userOps;
remove that ceiling so preVerificationGas is computed without capping by
GAS_CONFIG.preVerificationGas (e.g. compute preVerificationGas as
maxBigInt(estimatedPreVerification, 45_000n) or equivalent), updating the
expression that sets preVerificationGas and any logic that relies on it
(references: preVerificationGas, estimatedPreVerification, maxBigInt, minBigInt,
GAS_CONFIG.preVerificationGas).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26a4e9aa-b2fd-4294-ad44-64f5c8cedb2b

📥 Commits

Reviewing files that changed from the base of the PR and between 2d52d4b and 8ca790c.

📒 Files selected for processing (1)
  • app/lib/bundler/userOp.ts

Comment thread app/lib/bundler/userOp.ts Outdated
Comment thread app/lib/bundler/userOp.ts
* Changed error response status from 400 to 500 in the nexus status route to reflect server errors.
* Refactored WalletTransferApprovalModal to use getAccessTokenWithRetry for improved token retrieval.
* Enhanced error handling in useSmartWalletTransfer to throw an error if authentication is required, ensuring better user feedback.
Copy link
Copy Markdown
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)
app/api/bundler/nexus/status/route.ts (1)

10-23: ⚠️ Potential issue | 🟠 Major

Return 400 for invalid chainId and reserve 500 for runtime failures.

Line 22 validation errors currently fall into the outer catch (Line 33), so bad input is returned as 500. Split validation from runtime handling so client errors stay 400.

Suggested fix
 export async function GET(request: NextRequest) {
-  try {
-    const { searchParams } = request.nextUrl;
-    const smartAccountAddress = searchParams.get("smartAccountAddress");
-    const chainIdRaw = searchParams.get("chainId");
+  const { searchParams } = request.nextUrl;
+  const smartAccountAddress = searchParams.get("smartAccountAddress");
+  const chainIdRaw = searchParams.get("chainId");
 
-    if (!smartAccountAddress || !/^0x[0-9a-fA-F]{40}$/.test(smartAccountAddress)) {
-      return NextResponse.json(
-        { error: "Query param smartAccountAddress (0x + 40 hex) is required" },
-        { status: 400 }
-      );
-    }
+  if (!smartAccountAddress || !/^0x[0-9a-fA-F]{40}$/.test(smartAccountAddress)) {
+    return NextResponse.json(
+      { error: "Query param smartAccountAddress (0x + 40 hex) is required" },
+      { status: 400 }
+    );
+  }
 
-    const chainId = parseChainId(chainIdRaw);
+  let chainId: number;
+  try {
+    chainId = parseChainId(chainIdRaw);
+  } catch (error) {
+    return NextResponse.json(
+      { error: error instanceof Error ? error.message : "Invalid chainId" },
+      { status: 400 }
+    );
+  }
+
+  try {
     const rpcUrl = parseRpcUrl(chainId);
     const { publicClient } = getClients(chainId, rpcUrl, false);
     const status = await getNexusStatus(publicClient, smartAccountAddress as `0x${string}`);
@@
-  } catch (error) {
+  } catch (error) {
     return NextResponse.json(
       {
         error:
           error instanceof Error ? error.message : "Failed to check nexus status",
       },
       { status: 500 }
     );
   }
 }

Also applies to: 33-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/bundler/nexus/status/route.ts` around lines 10 - 23, The handler
currently lets invalid chainIdRaw errors bubble to the outer catch and return
500; before calling parseRpcUrl or other runtime code, validate chainIdRaw and
handle parseChainId failures as client errors: check that chainIdRaw exists and
is a valid chain id (e.g., numeric or matches expected pattern) or call
parseChainId inside a small try/catch and if it throws return
NextResponse.json({ error: "Invalid chainId" }, { status: 400 }); then continue
to call parseRpcUrl(chainId) and the rest; keep the outer try/catch for true
runtime failures so validation returns 400 while unexpected errors still return
500.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/api/bundler/nexus/status/route.ts`:
- Around line 6-7: The route docstring incorrectly lists rpcUrl as a query
parameter; update the comment above the GET handler (the docstring for the Nexus
status route in route.ts) to remove `rpcUrl` from the example query string and
description so it only documents `smartAccountAddress` and `chainId` (the
handler computes rpcUrl via parseRpcUrl(chainId)). Ensure the GET
/api/bundler/nexus/status line and any accompanying explanatory text reflect
that rpcUrl is not accepted as a query param.

---

Duplicate comments:
In `@app/api/bundler/nexus/status/route.ts`:
- Around line 10-23: The handler currently lets invalid chainIdRaw errors bubble
to the outer catch and return 500; before calling parseRpcUrl or other runtime
code, validate chainIdRaw and handle parseChainId failures as client errors:
check that chainIdRaw exists and is a valid chain id (e.g., numeric or matches
expected pattern) or call parseChainId inside a small try/catch and if it throws
return NextResponse.json({ error: "Invalid chainId" }, { status: 400 }); then
continue to call parseRpcUrl(chainId) and the rest; keep the outer try/catch for
true runtime failures so validation returns 400 while unexpected errors still
return 500.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b99ac7b5-f764-495c-b7ff-f00b2f4fd624

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca790c and a7c7926.

📒 Files selected for processing (3)
  • app/api/bundler/nexus/status/route.ts
  • app/components/WalletTransferApprovalModal.tsx
  • app/hooks/useSmartWalletTransfer.ts

Comment thread app/api/bundler/nexus/status/route.ts Outdated
* Revised API documentation to clarify query parameters for the Nexus status endpoint, removing the RPC URL requirement.
* Enhanced error handling in getSenderAddressFromInitCode to streamline address extraction from revert data, improving reliability and clarity in error responses.
@chibie chibie merged commit b3f47ea into main Mar 13, 2026
1 check passed
@chibie chibie deleted the fix-execute-native-within-noblocks branch March 13, 2026 13:32
@coderabbitai coderabbitai bot mentioned this pull request Mar 31, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants