Skip to content

🐛 app: custom transport fixes#922

Open
dieguezguille wants to merge 15 commits intomainfrom
transport
Open

🐛 app: custom transport fixes#922
dieguezguille wants to merge 15 commits intomainfrom
transport

Conversation

@dieguezguille
Copy link
Copy Markdown
Member

@dieguezguille dieguezguille commented Mar 27, 2026


Open with Devin

Summary by CodeRabbit

  • Bug Fixes
    • Multi-chain account actions now honor the requested chain ID, preventing misrouted operations.
    • WebAuthn and fallback transaction flows use the requested chain ID for consistent behavior.
    • Send/transaction fallbacks reliably switch chains on failure and restore original chain afterward.
    • Disabled automatic transport retries to avoid duplicate requests and improve predictability.
    • Fallback transactions now include proper encoded calldata to ensure correct execution.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 27, 2026

🦋 Changeset detected

Latest commit: e79dd5b

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

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 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

Forwards an optional chainId through account client requests, uses that chainId in generated IDs and sendCalls, disables retries for the bundler transport, and adds chain-switching around a fallback sendTransaction; also adds two changeset files for patch releases of @exactly/mobile.

Changes

Cohort / File(s) Summary
Changesets
.changeset/cool-icons-grow.md, .changeset/fuzzy-adults-tease.md
Added two changeset entries declaring patch releases for @exactly/mobile with release notes about forwarding chainId and disabling retries in a custom transport.
Account client logic
src/utils/accountClient.ts
Accept optional chainId in wallet_sendCalls, compute requestedChainId, use it in WebAuthn and fallback IDs, pass chainId: requestedChainId to sendCalls, on sendCalls failure switch chain to requestedChainId and submit fallback sendTransaction (uses encodeFunctionData), then switch back; wrap bundler transport with noRetry(custom(...)); add imports encodeFunctionData, switchChain, and noRetry helper.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AccountClient
    participant WalletProvider
    participant Blockchain

    Client->>AccountClient: wallet_sendCalls({calls, from, chainId?})
    AccountClient->>AccountClient: determine requestedChainId = chainId || chain.id
    alt sendCalls succeeds
        AccountClient->>WalletProvider: sendCalls(..., chainId: requestedChainId)
        WalletProvider->>Blockchain: submit calls on requestedChainId
        Blockchain-->>WalletProvider: result
        WalletProvider-->>AccountClient: result
        AccountClient-->>Client: response (id includes requestedChainId)
    else sendCalls fails
        AccountClient->>AccountClient: switchChain(requestedChainId)
        AccountClient->>WalletProvider: sendTransaction({to, data, chainId: requestedChainId})
        WalletProvider->>Blockchain: submit tx on requestedChainId
        Blockchain-->>WalletProvider: result / error
        WalletProvider-->>AccountClient: result / error
        AccountClient->>AccountClient: switchChain(originalChainId) (finally)
        AccountClient-->>Client: response / error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • cruzdanilo
  • franm91
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references custom transport fixes, which aligns with the main change in accountClient.ts (bundler transport configuration change from custom(...) to noRetry(custom(...))). However, it omits mention of the other critical changes like chain ID forwarding and retry disabling.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch transport

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.

@sentry
Copy link
Copy Markdown

sentry bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 56.47668% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.86%. Comparing base (e76f034) to head (e79dd5b).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/components/pay/Repay.tsx 57.89% 24 Missing ⚠️
src/utils/accountClient.ts 14.81% 23 Missing ⚠️
src/utils/reportError.ts 59.09% 18 Missing ⚠️
src/components/send-funds/Amount.tsx 73.07% 7 Missing ⚠️
...rc/components/home/card-upgrade/UpgradeAccount.tsx 0.00% 4 Missing ⚠️
src/utils/useAuth.ts 0.00% 3 Missing ⚠️
src/components/loans/Review.tsx 33.33% 2 Missing ⚠️
src/components/roll-debt/RollDebt.tsx 88.23% 2 Missing ⚠️
src/utils/server.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #922      +/-   ##
==========================================
- Coverage   71.96%   71.86%   -0.11%     
==========================================
  Files         229      229              
  Lines        8389     8512     +123     
  Branches     2713     2762      +49     
==========================================
+ Hits         6037     6117      +80     
- Misses       2121     2156      +35     
- Partials      231      239       +8     
Flag Coverage Δ
e2e 71.86% <56.47%> (+0.35%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

gemini-code-assist[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

sentry[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@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.

♻️ Duplicate comments (4)
src/utils/accountClient.ts (4)

160-163: ⚠️ Potential issue | 🟡 Minor

Remove the prettier-ignore suppression.

This repo does not allow formatting suppressions in source files; let Prettier wrap this call normally. As per coding guidelines "Follow ESLint, Prettier, and Solhint rules strictly; do not argue with the linter" and "Use only static analysis annotations (@ts-expect-error, eslint-disable, solhint-disable, slither-disable, cspell:ignore) and TODO/HACK/FIXME markers; do not use JSDoc, explanatory prose, region markers, or inline labels".


174-179: ⚠️ Potential issue | 🟠 Major

Don't encode requestedChainId here unless this branch actually honors it.

The WebAuthn path still sends through the default-chain client, and wallet_getCallsStatus later polls that same client. If requestedChainId !== chain.id, the operation still executes on the default network while the returned id claims otherwise. Either reject mismatches here or create a chain-specific client before sendUserOperation().

💡 minimal guard
             const requestedChainId = chainId ? hexToNumber(chainId) : chain.id;
             if (queryClient.getQueryData<AuthMethod>(["method"]) === "webauthn") {
+              if (requestedChainId !== chain.id) throw new Error("unsupported chain");
               const { hash } = await client.sendUserOperation({

206-215: ⚠️ Potential issue | 🔴 Critical

Don't switch the shared ownerConfig inside this fallback.

src/utils/wagmi/owner.ts exports a singleton config, so concurrent requests can race each other here. Even without concurrency, the finally block clobbers any pre-existing non-default selection because it always hard-resets to chain.id. Use an isolated per-request client, or serialize this section and restore the real prior chain.


239-241: ⚠️ Potential issue | 🟠 Major

eth_sendTransaction is still pinned to the default chain.

This branch ignores any chainId on the incoming transaction and always calls sendCalls() with chain.id, so cross-chain eth_sendTransaction requests still go to the default network. Use the request's chain when present, or reject mismatches before falling back to client.request().


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d93dca5f-a56d-4bf8-a8a6-a55f0ba2227d

📥 Commits

Reviewing files that changed from the base of the PR and between ae0aad2 and 89535f1.

📒 Files selected for processing (3)
  • .changeset/cool-icons-grow.md
  • .changeset/fuzzy-adults-tease.md
  • src/utils/accountClient.ts

devin-ai-integration[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@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.

♻️ Duplicate comments (3)
src/utils/accountClient.ts (3)

172-177: ⚠️ Potential issue | 🟠 Major

Reject non-default chains on the WebAuthn path.

client.sendUserOperation() is still tied to the default chain, but the returned id is stamped with requestedChainId. A non-default request can execute on one network and then be polled/reported as another.

💡 compact guard
             const requestedChainId = chainId ? hexToNumber(chainId) : chain.id;
             if (queryClient.getQueryData<AuthMethod>(["method"]) === "webauthn") {
+              if (requestedChainId !== chain.id) throw new Error("unsupported chain");
               const { hash } = await client.sendUserOperation({

204-214: ⚠️ Potential issue | 🔴 Critical

Don't switch a shared wagmi config inside this fallback.

This still mutates ownerConfig globally. Overlapping requests can race on the active chain, and even a single request restores to chain.id instead of the caller’s actual previous chain. Use an isolated client, or capture/restore the exact previous chain under a lock.


237-239: ⚠️ Potential issue | 🟠 Major

Don't hard-code the default chain in eth_sendTransaction.

This branch still ignores params[0].chainId, so a cross-chain request is silently forced onto chain.id. If this flow is intentionally single-chain, reject mismatches instead of rewriting them.

💡 compact fix
-                const { to, data = "0x", value = 0n } = params[0] as TransactionRequest;
+                const { to, chainId, data = "0x", value = 0n } = params[0] as TransactionRequest;
                 const { id } = await sendCalls(ownerConfig, {
-                  chainId: chain.id,
+                  chainId: chainId ?? chain.id,

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ca0632f0-363c-4faf-a10d-f3e234755abf

📥 Commits

Reviewing files that changed from the base of the PR and between 700e0e4 and ca87c0e.

📒 Files selected for processing (3)
  • .changeset/cool-icons-grow.md
  • .changeset/fuzzy-adults-tease.md
  • src/utils/accountClient.ts

sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@dieguezguille dieguezguille force-pushed the transport branch 2 times, most recently from 4868ef9 to eb6d3a6 Compare April 17, 2026 18:58
sentry[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 16 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 18 additional findings in Devin Review.

Open in Devin Review

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c19bd5d9db

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +146 to +149
tokenSelectionType === "to"
? selected.address === fromToken.token.address
? parseUnits(formatUnits(old.fromAmount, fromToken.token.decimals), toToken.token.decimals)
: old.fromAmount
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve current amount when swapping token sides

This branch now reuses old.fromAmount when a user selects the current source token from the “to” picker, but after this refactor fromAmount is no longer kept in sync with route results when editing the “to” side. In that flow, handleAmountChange leaves old.fromAmount stale (often 0n), so swapping token sides can reset or miscompute the amount and then drive incorrect quote/simulation inputs. This should use the current derived fromAmount (or persist it before updating tokens) instead of relying on old.fromAmount.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e79dd5b5f1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

burntOptions: { haptic: "error", preset: "error" },
});
reportError(error);
if (!reportError(error).authKnown)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid reporting cancelled bridge auth flows

handleError now calls reportError(error) unconditionally before checking authKnown, so wallet/passkey cancellations during bridge or transfer are still sent to Sentry as warnings. In this flow, user-initiated rejects are expected and can happen frequently (especially when retrying quotes), so this change introduces telemetry noise compared to the previous early-return cancellation guards. Classify first and skip reportError for auth-cancelled cases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 19 additional findings in Devin Review.

Open in Devin Review

Comment on lines 487 to 508
},
},
});
setEnableSimulations(false);
const { status } = await waitForCallsStatus(exa, { id });
if (status === "failure") throw new Error("failed to repay with external asset");
},
onMutate() {
setEnableSimulations(false);
if (!externalAsset) return;
if (!route?.fromAmount) return;
setDisplayValues({
amount: Number(route.fromAmount) / 10 ** externalAsset.decimals,
usdAmount: (Number(externalAsset.priceUSD) * Number(route.fromAmount)) / 10 ** externalAsset.decimals,
});
},
onSettled() {
setEnableSimulations(true);
},
onError(error) {
reportError(error);
if (reportError(error).authKnown) resetExternalRepay();
},
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚩 Repay external asset mutation missing onSuccess query invalidation

The repayWithExternalAsset mutation in Repay.tsx:443-508 does not invalidate assetQueryKey on success, while the non-external repay mutation does (Repay.tsx:432-434). This means after a successful external repay, the asset data won't refresh until the next automatic refetch. However, this is NOT a regression — the old code's repayWithExternalAsset also lacked onSuccess query invalidation. The non-external repay previously used useWriteContract which had the invalidation. Consider adding onSuccess to repayWithExternalAsset for consistency.

(Refers to lines 443-508)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant