Skip to content

PRO-3857/fix-wallet-connect-7702#468

Merged
RanaBug merged 3 commits intostagingfrom
PRO-3857/fix-wallet-connect-7702
Nov 28, 2025
Merged

PRO-3857/fix-wallet-connect-7702#468
RanaBug merged 3 commits intostagingfrom
PRO-3857/fix-wallet-connect-7702

Conversation

@RanaBug
Copy link
Collaborator

@RanaBug RanaBug commented Nov 26, 2025

Description

  • Fix walletConnect compatibility with 7702 by introducing wallet methods

How Has This Been Tested?

  • Existing unit tests and manual testing

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Summary by CodeRabbit

  • New Features

    • Added wallet capability reporting and batch send/status support for improved multi-transaction handling.
    • Consistent batch naming using provided batch names when available.
  • Bug Fixes

    • More accurate WalletConnect dropdown visibility and detection.
    • Asset selection now correctly appears only when no payload transaction or batches exist.
  • Refactor

    • Simplified internal wallet integration surface for clearer behavior.

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

@RanaBug RanaBug requested a review from IAmKio November 26, 2025 23:54
@RanaBug RanaBug self-assigned this Nov 26, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 2025

Walkthrough

Adds EIP-5792 batch handling and status tracking to WalletConnect, serializes BigInt for JSON responses, introduces WALLET_* method constants, adds optional batchName to batch data, and removes kit-derived wallet mode checks from WalletConnect dropdown logic in portfolio components.

Changes

Cohort / File(s) Summary
Portfolio UI adjustments
src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx, src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx
Removed kit from useTransactionKit destructuring; simplified shouldShowWalletConnectDropdown to rely on Wagmi/Privy connection state instead of kit-derived wallet mode
SendModal batch naming & rendering
src/components/BottomMenuModal/SendModal/SendModalTokensTabView.tsx
Use payload.batchName when present and respect batch.batchName; fallback to batch-{chainId}; render asset-selection block only when neither payload.transaction nor payload.batches exist
WalletConnect EIP-5792 + batch tracking
src/services/walletConnect.ts
Added in-memory batchIdToTxDataMap, getCallsStatus, getWalletCapabilities; handle WALLET_GET_CAPABILITIES, WALLET_SEND_CALLS, WALLET_GET_CALLS_STATUS in session requests; support single vs. multi-call flows, generate batch IDs, store txHash/userOpHash mappings, add extra error handling and Sentry logs; serialize BigInt values for JSON responses
Types updated
src/types/index.ts
Added optional batchName field to batch objects in SendModalSingleBatchedTransactionsData.batches
WalletConnect constants
src/utils/walletConnect.ts
Added exported constants: WALLET_GET_CAPABILITIES, WALLET_SEND_CALLS, WALLET_GET_CALLS_STATUS
Serialization utility
src/utils/common.ts
Added serializeBigInts(obj: any): any to recursively convert BigInt values to strings for JSON serialization

Sequence Diagram(s)

sequenceDiagram
    participant DApp as Wallet / DApp
    participant WC as WalletConnect Service
    participant UI as UI / User
    participant Chain as Blockchain

    rect rgb(230, 245, 255)
        note over WC: EIP-5792-capable request handling
    end

    DApp->>WC: WALLET_GET_CAPABILITIES(chainIds)
    WC->>WC: getWalletCapabilities(chainIds)
    WC-->>DApp: capabilities per chain

    DApp->>WC: WALLET_SEND_CALLS (single or batch)
    WC->>UI: Show confirmation (single vs batch)
    UI->>UI: User confirms

    alt Single call
        WC->>Chain: eth_sendTransaction
        Chain-->>WC: txHash
    else Batch (multiple calls)
        WC->>Chain: prepare & send batch / userOp
        Chain-->>WC: txHash and/or userOpHash
    end

    WC->>WC: store mapping in batchIdToTxDataMap
    WC-->>DApp: return batchId

    DApp->>WC: WALLET_GET_CALLS_STATUS(batchId)
    WC->>WC: getCallsStatus(batchId) -> check userOp / receipt
    WC-->>DApp: status (PENDING / CONFIRMED / REJECTED)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay special attention to src/services/walletConnect.ts for correctness of batch ID generation, storage of txHash/userOpHash, race conditions, and JSON BigInt serialization.
  • Verify integrations between getCallsStatus, getUserOperationStatus, and chain receipt polling.
  • Confirm UI gating changes in portfolio components don't regress display conditions.

Possibly related PRs

Suggested reviewers

  • IAmKio
  • vignesha22

"I hopped through code with a carrot-bright grin,
Batched calls and capabilities tucked in my chin,
No more kit whispers — Privy takes the lead,
Names on each batch, and statuses freed,
🐇✨ Transactions leap forward, all ready to spin!"

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'PRO-3857/fix-wallet-connect-7702' uses a ticket reference format and vaguely references wallet-connect 7702, but doesn't clearly summarize the main technical change (introducing EIP-5792 wallet methods). Use a more descriptive title that clearly indicates the main change, such as 'Add EIP-5792 wallet methods for WalletConnect support' instead of relying solely on ticket references.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description adequately covers the purpose (fixing WalletConnect compatibility), testing approach (existing unit tests and manual testing), and correctly identifies it as a bug fix, matching the template structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 PRO-3857/fix-wallet-connect-7702

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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 26, 2025

Deploying x with  Cloudflare Pages  Cloudflare Pages

Latest commit: f2f9855
Status: ✅  Deploy successful!
Preview URL: https://08592c4a.x-e62.pages.dev
Branch Preview URL: https://pro-3857-fix-wallet-connect.x-e62.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 26, 2025

Deploying pillarx-debug with  Cloudflare Pages  Cloudflare Pages

Latest commit: f2f9855
Status: ✅  Deploy successful!
Preview URL: https://993f8d32.pillarx-debug.pages.dev
Branch Preview URL: https://pro-3857-fix-wallet-connect.pillarx-debug.pages.dev

View logs

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

🧹 Nitpick comments (3)
src/services/walletConnect.ts (2)

53-57: Potential memory leak: in-memory batch ID map grows unbounded.

The batchIdToTxDataMap Map is never cleaned up. Over time, as users execute transactions, this map will grow indefinitely since entries are only added but never removed.

Consider implementing a cleanup strategy:

 // In-memory mapping of batch IDs to transaction data
-const batchIdToTxDataMap = new Map<
+const BATCH_TTL_MS = 30 * 60 * 1000; // 30 minutes
+const batchIdToTxDataMap = new Map<
   string,
-  { txHash: string; userOpHash?: string }
+  { txHash: string; userOpHash?: string; createdAt: number }
 >();
+
+// Cleanup old batch entries periodically
+const cleanupOldBatches = () => {
+  const now = Date.now();
+  for (const [batchId, data] of batchIdToTxDataMap.entries()) {
+    if (now - data.createdAt > BATCH_TTL_MS) {
+      batchIdToTxDataMap.delete(batchId);
+    }
+  }
+};
+setInterval(cleanupOldBatches, 5 * 60 * 1000); // Run every 5 minutes

You would also need to update set calls to include createdAt: Date.now().


1590-1604: Async onSent callback may fail silently without error handling.

The onSent callback performs async operations (kit.getTransactionHash) but errors are not caught. If getTransactionHash fails, the batch mapping won't be stored, causing wallet_getCallsStatus to return incorrect status.

           onSent: async (userOpHashes: string[]) => {
             // Get transaction hash from userOp hash and store it with the batch ID
             if (userOpHashes && userOpHashes.length > 0) {
               const userOpHash = userOpHashes[0];
-              const txHash = await kit.getTransactionHash(
-                userOpHash,
-                callsChainId
-              );
-              if (txHash) {
-                setWalletConnectTxHash(txHash);
-                // Store mapping of batchId -> {txHash, userOpHash} for proper wallet_getCallsStatus lookup
-                batchIdToTxDataMap.set(batchId, { txHash, userOpHash });
+              try {
+                const txHash = await kit.getTransactionHash(
+                  userOpHash,
+                  callsChainId
+                );
+                if (txHash) {
+                  setWalletConnectTxHash(txHash);
+                  batchIdToTxDataMap.set(batchId, { txHash, userOpHash });
+                }
+              } catch (e) {
+                console.error('Failed to get transaction hash in onSent:', e);
+                // Still store the userOpHash so status can be checked
+                batchIdToTxDataMap.set(batchId, { txHash: '', userOpHash });
               }
             }
           },
src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx (1)

49-52: Stale comment: mentions delegatedEoa mode check that was removed.

The comment on lines 49-50 states "or if user is in delegatedEoa mode" but the actual wallet mode check has been removed from the condition. Either update the comment to reflect the new logic, or verify the removal was intentional.

-  // Don't show WalletConnectDropdown if user is connected via Wagmi or Privy with WalletConnect
-  // or if user is in delegatedEoa mode
+  // Don't show WalletConnectDropdown if user is connected via Wagmi or Privy with WalletConnect
   const shouldShowWalletConnectDropdown =
     !isConnected && !isPrivyConnectedViaWalletConnect;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 654bd55 and 71a1f90.

📒 Files selected for processing (6)
  • src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx (2 hunks)
  • src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx (3 hunks)
  • src/components/BottomMenuModal/SendModal/SendModalTokensTabView.tsx (3 hunks)
  • src/services/walletConnect.ts (7 hunks)
  • src/types/index.ts (1 hunks)
  • src/utils/walletConnect.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx
  • src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx
  • src/services/walletConnect.ts
📚 Learning: 2025-08-20T09:14:16.888Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 374
File: src/apps/pillarx-app/index.tsx:12-12
Timestamp: 2025-08-20T09:14:16.888Z
Learning: In this codebase, Transaction Kit providers are set up at the container level (src/containers/Authorized.tsx), not at individual app component levels. App components like src/apps/pillarx-app/index.tsx are children that consume the context through the provider tree.

Applied to files:

  • src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx
  • src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx
🧬 Code graph analysis (1)
src/services/walletConnect.ts (3)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/services/userOpStatus.ts (1)
  • getUserOperationStatus (3-44)
src/utils/walletConnect.ts (3)
  • WALLET_GET_CAPABILITIES (13-13)
  • WALLET_SEND_CALLS (14-14)
  • WALLET_GET_CALLS_STATUS (15-15)
⏰ 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). (4)
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: Cloudflare Pages: pillarx-debug
  • GitHub Check: build
🔇 Additional comments (12)
src/services/walletConnect.ts (5)

71-86: Returning REJECTED for missing batch may cause incorrect dApp behavior.

When txData is not found, returning status: 'REJECTED' could mislead dApps into thinking the user explicitly rejected the transaction. Per EIP-5792, there's no explicit "not found" status, but PENDING might be more appropriate when the batch ID exists but hasn't been processed yet.

Consider whether returning PENDING initially would be safer, as the batch ID may simply not have been stored yet due to timing.


1548-1552: txHash retrieval happens after user confirmation timeout but is stored immediately.

The code awaits getTransactionHash() which polls for up to 3 minutes. If the user doesn't confirm in time or rejects, txHash will be undefined, but batchIdToTxDataMap.set is only called if txHash exists. This is correct, but the batch ID has already been returned to the dApp. Subsequent wallet_getCallsStatus calls will return REJECTED until the tx is confirmed, which may confuse dApps expecting a pending state.


1626-1645: serializeBigInts helper is well-implemented.

The recursive BigInt serialization handles all cases correctly (null, undefined, arrays, objects, primitives). This ensures JSON serialization won't fail on BigInt values from viem.


1439-1447: The review comment is incorrect. Per EIP-5792, wallet_getCapabilities expects parameters as [Address, string[]], where the chain IDs array is the second parameter (index [1]), not the first.

The code at line 1440 correctly accesses request.params?.[1] for chain IDs. The EIP-5792 specification confirms that the parameter structure is:

  • params[0]: Wallet address (0x-prefixed hex string)
  • params[1]: Optional array of EIP-155 chain IDs (hex strings)

The implementation is correct and follows the specification.


199-236: Based on my verification of the EIP-5792 specification and codebase analysis, I can now provide the rewritten review comment:


getWalletCapabilities does not validate chain support per EIP-5792 specification.

While the function correctly uses the '0x0' key for all-chain capabilities, EIP-5792 explicitly requires: "Wallets MUST omit chains they do not support." The function currently returns identical capabilities for every requested chain without validating against supportedChains. When chainIds are provided by the client, all are echoed back with capabilities regardless of whether the wallet actually supports them. This violates spec compliance and could advertise capabilities for unsupported networks.

Filter requestChainIds against supportedChains (available in src/utils/blockchain.ts) before populating the response.

⛔ Skipped due to learnings
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.
src/types/index.ts (1)

16-22: LGTM!

The optional batchName field addition is backward-compatible and correctly typed. It aligns with the new WalletConnect batch transaction handling that uses this field for EIP-5792 batch identification.

src/utils/walletConnect.ts (1)

13-15: LGTM!

The new EIP-5792 wallet method constants are correctly defined and follow the existing naming pattern. These align with the WalletConnect protocol specifications for batch transaction handling.

src/components/BottomMenuModal/SendModal/SendModalTokensTabView.tsx (3)

1073-1074: LGTM!

The batchName extraction with fallback to batch-${batch.chainId} is consistent and correctly uses the optional field from the payload. This enables proper batch identification for EIP-5792 compliance while maintaining backward compatibility.


1091-1092: Batch name extraction is consistent with creation logic.

Good consistency between how batch names are assigned during creation (line 1074) and how they're used for estimation (line 1092). This ensures the correct batches are estimated and sent.


2354-2354: UI condition correctly hides asset selection for payload flows.

The change from {!isPayloadTransaction && ...} to {isPayloadTransaction || isPayloadBatches ? null : ...} correctly hides the asset selection UI for both single transaction and batch payloads from WalletConnect. This prevents user confusion when the transaction details are already determined by the dApp.

src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx (2)

2-7: Imports align with new hook usage

useState and useAccount are correctly imported and used (isAddCashLoading, isConnected). No issues here.


29-30: Destructuring only walletAddress from useTransactionKit is appropriate

Dropping kit here is fine since this component only needs walletAddress for the on-ramp flow and button disabling; wallet mode checks can remain centralized elsewhere (e.g., via verification hooks in container-level providers).

Based on learnings, this matches how Transaction Kit is provided and consumed in this codebase.

Copy link
Collaborator

@IAmKio IAmKio left a comment

Choose a reason for hiding this comment

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

Just a few suggestions and some stuff from CR but none of it is a showstopper 👍

Comment on lines 1627 to 1645
const serializeBigInts = (obj: any): any => {
if (obj === null || obj === undefined) {
return obj;
}
if (typeof obj === 'bigint') {
return obj.toString();
}
if (Array.isArray(obj)) {
return obj.map(serializeBigInts);
}
if (typeof obj === 'object') {
const serialized: any = {};
for (const [key, value] of Object.entries(obj)) {
serialized[key] = serializeBigInts(value);
}
return serialized;
}
return obj;
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels like it could be a utility

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed, I have moved this now to a utility file

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)
src/services/walletConnect.ts (1)

96-144: Add null check for userOpStatus to prevent runtime errors.

The getUserOperationStatus function can return undefined, but line 103 accesses userOpStatus.transaction without checking. This will throw a TypeError if the userOp status is not yet available.

Apply this diff to add the null check:

   if (userOpHash) {
     try {
       const userOpStatus = await getUserOperationStatus(
         chainIdNumber,
         userOpHash
       );

+      if (!userOpStatus) {
+        // userOp not yet indexed, return pending
+        return {
+          version: '1.0',
+          id: batchId,
+          atomic: true,
+          status: 'PENDING',
+        };
+      }
+
       const finalTxHash = userOpStatus.transaction || txHash;
🧹 Nitpick comments (1)
src/services/walletConnect.ts (1)

54-58: Consider adding cleanup for in-memory batch mapping.

The batchIdToTxDataMap stores batch ID mappings indefinitely, which could lead to memory growth over time. Consider implementing a cleanup mechanism to remove old entries (e.g., after a certain time period or when status reaches a terminal state).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71a1f90 and f2f9855.

📒 Files selected for processing (4)
  • src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx (2 hunks)
  • src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx (3 hunks)
  • src/services/walletConnect.ts (7 hunks)
  • src/utils/common.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/apps/pillarx-app/components/WalletPortfolioButtons/WalletPortfolioButtons.tsx
  • src/apps/pillarx-app/components/PortfolioOverview/PortfolioOverview.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-04T14:34:00.373Z
Learnt from: RanaBug
Repo: pillarwallet/x PR: 445
File: src/containers/Authorized.tsx:63-77
Timestamp: 2025-11-04T14:34:00.373Z
Learning: In src/containers/Authorized.tsx, the tempKit useEffect intentionally excludes chainId from its dependency array because the kit is used by useWalletModeVerification to check wallet mode across all supported chains, regardless of the currently selected chainId. The chainId parameter is only used for initial kit configuration and does not affect the multi-chain verification results.

Applied to files:

  • src/services/walletConnect.ts
🧬 Code graph analysis (1)
src/services/walletConnect.ts (4)
src/apps/deposit/utils/blockchain.tsx (1)
  • getNetworkViem (71-94)
src/services/userOpStatus.ts (1)
  • getUserOperationStatus (3-44)
src/utils/walletConnect.ts (3)
  • WALLET_GET_CAPABILITIES (13-13)
  • WALLET_SEND_CALLS (14-14)
  • WALLET_GET_CALLS_STATUS (15-15)
src/utils/common.ts (1)
  • serializeBigInts (114-134)
⏰ 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). (4)
  • GitHub Check: unit-tests
  • GitHub Check: lint
  • GitHub Check: build
  • GitHub Check: Cloudflare Pages: pillarx-debug
🔇 Additional comments (6)
src/utils/common.ts (1)

112-134: LGTM! BigInt serialization utility is well-implemented.

The recursive function correctly handles the conversion of BigInt values to strings for JSON serialization, which is necessary for WalletConnect responses.

src/services/walletConnect.ts (5)

200-237: LGTM! Wallet capabilities helper correctly implements EIP-5792.

The function properly returns per-chain capabilities and differentiates between modular and delegatedEoa wallet modes.


1161-1169: LGTM! Optional chaining is now complete.

The wallet mode check now properly uses optional chaining on all method calls, preventing runtime errors if kit is undefined.


1440-1448: LGTM! Wallet capabilities request handling is correct.

Properly delegates to the helper function with appropriate parameters.


1614-1624: LGTM! Calls status request handling is correct.

Properly validates parameters and delegates to the helper function.


1626-1638: LGTM! Response serialization and error handling are well-implemented.

The BigInt serialization ensures JSON compatibility, and the nested try-catch provides proper error handling for the response sending.

Comment on lines +1492 to +1612
if (sendCallsParams.calls.length === 1) {
const singleCall = sendCallsParams.calls[0];

// Validate the single call
if (!singleCall.to || !singleCall.data) {
console.warn(
'WalletConnect wallet_sendCalls: Invalid single call',
singleCall
);
// eslint-disable-next-line quotes
throw new Error("Invalid call: missing 'to' or 'data'");
}

// Create a transaction object similar to eth_sendTransaction
const transaction = {
to: singleCall.to,
data: singleCall.data,
value: singleCall.value || '0x0',
};

const isApprovalTransaction =
!!transaction.data?.startsWith('0x095ea7b3');

if (isApprovalTransaction) {
const approvalRequest = {
title: 'WalletConnect Approval Request',
description: `${dAppName} is requesting approval for a contract.`,
transaction: {
to: checksumAddress(transaction.to as `0x${string}`),
data: transaction.data,
chainId: chainIdNumber,
},
};

showTransactionConfirmation(approvalRequest);
setWalletConnectPayload(approvalRequest);
} else {
const transactionRequest = {
title: 'WalletConnect Transaction Request',
description: `${dAppName} wants to send a transaction`,
transaction: {
to: checksumAddress(transaction.to as `0x${string}`),
value: transaction.value
? formatEther(
hexToBigInt(transaction.value as `0x${string}`)
)
: '0',
data: transaction.data,
chainId: chainIdNumber,
},
};

showTransactionConfirmation(transactionRequest);
setWalletConnectPayload(transactionRequest);
}

// Wait for transaction hash
const txHash = await getTransactionHash();
if (txHash) {
// Store the mapping for wallet_getCallsStatus
batchIdToTxDataMap.set(batchId, { txHash });
}

// Returns batch ID for EIP-5792 compliance
requestResponse = batchId;
}

// Multi-transaction batch flow
// Prepare the transaction data in the batch
// The batch will be created when user confirms in the SendModal
const transactionData = sendCallsParams.calls.map((call, i) => {
if (!call.to || !call.data) {
throw new Error(
`Invalid call at index ${i}: missing 'to' or 'data'`
);
}

return {
to: checksumAddress(call.to as `0x${string}`),
value: call.value
? formatEther(hexToBigInt(call.value as `0x${string}`))
: '0',
data: call.data as `0x${string}`,
chainId: callsChainId,
};
});

// Create confirmation payload
const batchRequest = {
title: 'WalletConnect Batch Transaction Request',
description: `${dAppName} wants to execute ${sendCallsParams.calls.length} transaction${sendCallsParams.calls.length > 1 ? 's' : ''}`,
batches: [
{
chainId: callsChainId,
transactions: transactionData,
batchName: batchId,
},
],
onSent: async (userOpHashes: string[]) => {
// Get transaction hash from userOp hash and store it with the batch ID
if (userOpHashes && userOpHashes.length > 0) {
const userOpHash = userOpHashes[0];
const txHash = await kit.getTransactionHash(
userOpHash,
callsChainId
);
if (txHash) {
setWalletConnectTxHash(txHash);
// Store mapping of batchId -> {txHash, userOpHash} for proper wallet_getCallsStatus lookup
batchIdToTxDataMap.set(batchId, { txHash, userOpHash });
}
}
},
};

showTransactionConfirmation(batchRequest);
setWalletConnectPayload(batchRequest);

// Returns batch ID for EIP-5792 compliance
requestResponse = batchId;
}
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 control flow: single-call path executes multi-call code.

The single-call handling (lines 1492-1557) doesn't prevent execution of the multi-call code (lines 1559-1612). This causes:

  1. Double confirmation dialogs shown to the user
  2. requestResponse set twice (lines 1556 and 1611)
  3. Unnecessary processing of the same call as a batch

The previous issue was an early return that prevented sending the response. Removing the return fixed that, but now both code paths execute.

Wrap the multi-call logic in an else block:

           // Returns batch ID for EIP-5792 compliance
           requestResponse = batchId;
-        }
+        } else {
+          // Multi-transaction batch flow
+          // Prepare the transaction data in the batch
+          // The batch will be created when user confirms in the SendModal
+          const transactionData = sendCallsParams.calls.map((call, i) => {

-        // Multi-transaction batch flow
-        // Prepare the transaction data in the batch
-        // The batch will be created when user confirms in the SendModal
-        const transactionData = sendCallsParams.calls.map((call, i) => {

Then close the else block after line 1611:

           // Returns batch ID for EIP-5792 compliance
           requestResponse = batchId;
+        }
       }
🤖 Prompt for AI Agents
In src/services/walletConnect.ts around lines 1492 to 1612, the single-call
branch does not prevent execution of the multi-call block so both paths run
(double dialogs and requestResponse overwritten). Fix by wrapping the multi-call
logic (currently starting just after the single-call block, lines ~1559-1612) in
an else clause corresponding to if (sendCallsParams.calls.length === 1) { ... }
and close the else after the multi-call code (after line 1611), ensuring the
single-call branch and multi-call branch are mutually exclusive and no duplicate
requestResponse assignments remain.

@RanaBug RanaBug merged commit 352c54a into staging Nov 28, 2025
7 checks passed
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