Skip to content

Conversation

micaelae
Copy link
Member

@micaelae micaelae commented Sep 30, 2025

Explanation

This PR introduces server‑sent events quote streaming and integrates incremental quote updates into the bridge controller polling flow using the @microsoft/fetch-event-source library

  • Add private handleQuoteStreaming handler that calls getQuoteStream when the sseEnabled flag is enabled in LaunchDarkly
  • Reuse existing polling, metrics and validation utilities when processing server-sent quotes

Note: clients need to patch the @microsoft/fetch-event-source dependency such that it rejects instead of resolving when the quote request is cancelled. This preserves the controller's expected request cancellation behavior

Other changes

  • Extract some logic from bridge-controller and move them to utility files for better readability
  • Remove cache options from spot-prices and getQuote api calls since they are only required by the extension client
  • Pass abortSignal to fetchAssetPricesForCurrency in order to cancel exchange rate fetching when quote parameters change

Client integration PRs

References

Fixes: https://consensyssoftware.atlassian.net/browse/SWAPS-3026
Related to https://github.com/consensys-vertical-apps/va-mmcx-bridge-api/pull/517

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

Note

Introduce server‑sent events quote streaming with incremental updates, refactor fee/sorting logic, tighten error/abort handling, and adjust fetch interfaces; add @microsoft/fetch-event-source.

  • Bridge Controller:
    • Add SSE quote streaming gated by sseEnabled; integrate into polling with incremental onmessage handling via fetchBridgeQuoteStream and early quotesInitialLoadTime update.
    • Clear/replace stale quotes on new streams; set quotesLastFetched at start; increment quotesRefreshCount post-process; improved error messages and abort handling; resetState(reason).
    • Pass AbortSignal to asset price fetches; guard balance checks with try/catch; keep Solana min-balance fetch async.
  • Utils/Refactors:
    • Extract fee logic to utils/quote-fees.appendFeesToQuotes and sorting to utils/quote.sortQuotes.
    • Add utils/snaps.getMinimumBalanceForRentExemptionInLamports.
    • utils/fetch: factor formatQueryParams; add fetchBridgeQuoteStream; drop client-specific cache options for getQuote and spot-prices; allow FetchFunction (RequestInfo|URL|string).
  • Types/Validators:
    • Add optional sseEnabled to PlatformConfigSchema; enhance BridgeControllerState docs; minor type imports (e.g., InternalAccount, BridgeClientId).
  • Dependencies/Config:
    • Add @microsoft/fetch-event-source@^2.0.1; include tests in tsconfig.
  • Tests:
    • New SSE tests and snapshots; update existing tests for timing, error text, and state changes.
  • Changelog:
    • Document SSE streaming, refactors, removed cache options, and abort-signal for prices.

Written by Cursor Bugbot for commit d35699b. This will update automatically on new commits. Configure here.

@micaelae micaelae requested review from a team as code owners September 30, 2025 19:34
@micaelae micaelae marked this pull request as draft September 30, 2025 19:34
@micaelae
Copy link
Member Author

@metamaskbot publish-preview

cursor[bot]

This comment was marked as outdated.

Copy link

socket-security bot commented Sep 30, 2025

Warning

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
@microsoft/[email protected] has Network access.

Module: globalThis["fetch"]

Location: Package overview

From: packages/bridge-controller/package.jsonnpm/@microsoft/[email protected]

ℹ Read more on: This package | This alert | What is network access?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at [email protected].

Suggestion: Packages should remove all network access that is functionally unnecessary. Consumers should audit network access to ensure legitimate use.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@microsoft/[email protected]. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@micaelae
Copy link
Member Author

@metamaskbot publish-preview

@micaelae
Copy link
Member Author

@metamaskbot publish-preview

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "1.3.0-preview-5eac8e7",
  "@metamask-previews/accounts-controller": "33.1.0-preview-5eac8e7",
  "@metamask-previews/address-book-controller": "6.1.1-preview-5eac8e7",
  "@metamask-previews/announcement-controller": "7.0.3-preview-5eac8e7",
  "@metamask-previews/app-metadata-controller": "1.0.0-preview-5eac8e7",
  "@metamask-previews/approval-controller": "7.1.3-preview-5eac8e7",
  "@metamask-previews/assets-controllers": "77.0.2-preview-5eac8e7",
  "@metamask-previews/base-controller": "8.4.0-preview-5eac8e7",
  "@metamask-previews/bridge-controller": "47.2.0-preview-5eac8e7",
  "@metamask-previews/bridge-status-controller": "47.2.0-preview-5eac8e7",
  "@metamask-previews/build-utils": "3.0.3-preview-5eac8e7",
  "@metamask-previews/chain-agnostic-permission": "1.1.1-preview-5eac8e7",
  "@metamask-previews/composable-controller": "11.0.0-preview-5eac8e7",
  "@metamask-previews/controller-utils": "11.14.0-preview-5eac8e7",
  "@metamask-previews/delegation-controller": "0.7.0-preview-5eac8e7",
  "@metamask-previews/earn-controller": "8.0.0-preview-5eac8e7",
  "@metamask-previews/eip-5792-middleware": "1.2.0-preview-5eac8e7",
  "@metamask-previews/eip1193-permission-middleware": "1.0.0-preview-5eac8e7",
  "@metamask-previews/ens-controller": "17.0.1-preview-5eac8e7",
  "@metamask-previews/error-reporting-service": "2.1.0-preview-5eac8e7",
  "@metamask-previews/eth-json-rpc-provider": "5.0.0-preview-5eac8e7",
  "@metamask-previews/foundryup": "1.0.1-preview-5eac8e7",
  "@metamask-previews/gas-fee-controller": "24.0.0-preview-5eac8e7",
  "@metamask-previews/gator-permissions-controller": "0.2.0-preview-5eac8e7",
  "@metamask-previews/json-rpc-engine": "10.1.0-preview-5eac8e7",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.7-preview-5eac8e7",
  "@metamask-previews/keyring-controller": "23.1.0-preview-5eac8e7",
  "@metamask-previews/logging-controller": "6.0.4-preview-5eac8e7",
  "@metamask-previews/message-manager": "13.0.0-preview-5eac8e7",
  "@metamask-previews/messenger": "0.3.0-preview-5eac8e7",
  "@metamask-previews/multichain-account-service": "1.4.0-preview-5eac8e7",
  "@metamask-previews/multichain-api-middleware": "1.2.0-preview-5eac8e7",
  "@metamask-previews/multichain-network-controller": "1.0.0-preview-5eac8e7",
  "@metamask-previews/multichain-transactions-controller": "5.0.0-preview-5eac8e7",
  "@metamask-previews/name-controller": "8.0.3-preview-5eac8e7",
  "@metamask-previews/network-controller": "24.2.0-preview-5eac8e7",
  "@metamask-previews/network-enablement-controller": "2.1.0-preview-5eac8e7",
  "@metamask-previews/notification-services-controller": "18.1.0-preview-5eac8e7",
  "@metamask-previews/permission-controller": "11.0.6-preview-5eac8e7",
  "@metamask-previews/permission-log-controller": "4.0.0-preview-5eac8e7",
  "@metamask-previews/phishing-controller": "14.0.0-preview-5eac8e7",
  "@metamask-previews/polling-controller": "14.0.0-preview-5eac8e7",
  "@metamask-previews/preferences-controller": "20.0.1-preview-5eac8e7",
  "@metamask-previews/profile-sync-controller": "25.1.0-preview-5eac8e7",
  "@metamask-previews/rate-limit-controller": "6.0.3-preview-5eac8e7",
  "@metamask-previews/remote-feature-flag-controller": "1.7.0-preview-5eac8e7",
  "@metamask-previews/sample-controllers": "2.0.0-preview-5eac8e7",
  "@metamask-previews/seedless-onboarding-controller": "4.0.0-preview-5eac8e7",
  "@metamask-previews/selected-network-controller": "24.0.0-preview-5eac8e7",
  "@metamask-previews/shield-controller": "0.2.0-preview-5eac8e7",
  "@metamask-previews/signature-controller": "34.0.0-preview-5eac8e7",
  "@metamask-previews/subscription-controller": "0.5.0-preview-5eac8e7",
  "@metamask-previews/token-search-discovery-controller": "3.3.0-preview-5eac8e7",
  "@metamask-previews/transaction-controller": "60.5.0-preview-5eac8e7",
  "@metamask-previews/user-operation-controller": "39.0.0-preview-5eac8e7"
}

@micaelae micaelae force-pushed the swaps3025-quote-streaming branch 2 times, most recently from 45b6fbd to cfbd89b Compare October 7, 2025 21:36
@micaelae micaelae force-pushed the swaps3025-quote-streaming branch from cfbd89b to f3be855 Compare October 7, 2025 21:38
@micaelae micaelae changed the title feat: stream swap quotes feat: (controllers) stream swap quotes Oct 9, 2025
@micaelae micaelae force-pushed the swaps3025-quote-streaming branch from 1e904bd to 6ae7634 Compare October 10, 2025 01:24
@micaelae micaelae force-pushed the swaps3025-quote-streaming branch from 6ae7634 to bcf5a41 Compare October 10, 2025 16:36
Copy link

socket-security bot commented Oct 10, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​microsoft/​fetch-event-source@​2.0.110010010083100

View full report

@micaelae
Copy link
Member Author

@SocketSecurity @microsoft/[email protected]

@micaelae micaelae force-pushed the swaps3025-quote-streaming branch from 0eaa63a to 9ee0ebb Compare October 11, 2025 01:01
@micaelae
Copy link
Member Author

@metamaskbot publish-preview

@micaelae micaelae changed the title feat: (controllers) stream swap quotes feat: stream swap quotes using server-sent events Oct 13, 2025
@micaelae micaelae marked this pull request as ready for review October 13, 2025 18:14
cursor[bot]

This comment was marked as outdated.

Copy link
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "1.4.2-preview-5cf3ff79",
  "@metamask-previews/accounts-controller": "33.1.1-preview-5cf3ff79",
  "@metamask-previews/address-book-controller": "6.2.0-preview-5cf3ff79",
  "@metamask-previews/announcement-controller": "7.1.0-preview-5cf3ff79",
  "@metamask-previews/app-metadata-controller": "1.1.0-preview-5cf3ff79",
  "@metamask-previews/approval-controller": "7.2.0-preview-5cf3ff79",
  "@metamask-previews/assets-controllers": "80.0.0-preview-5cf3ff79",
  "@metamask-previews/base-controller": "8.4.1-preview-5cf3ff79",
  "@metamask-previews/bridge-controller": "50.0.0-preview-5cf3ff79",
  "@metamask-previews/bridge-status-controller": "50.0.0-preview-5cf3ff79",
  "@metamask-previews/build-utils": "3.0.4-preview-5cf3ff79",
  "@metamask-previews/chain-agnostic-permission": "1.2.0-preview-5cf3ff79",
  "@metamask-previews/composable-controller": "11.1.0-preview-5cf3ff79",
  "@metamask-previews/controller-utils": "11.14.1-preview-5cf3ff79",
  "@metamask-previews/core-backend": "1.0.1-preview-5cf3ff79",
  "@metamask-previews/delegation-controller": "0.8.0-preview-5cf3ff79",
  "@metamask-previews/earn-controller": "8.0.1-preview-5cf3ff79",
  "@metamask-previews/eip-5792-middleware": "1.2.2-preview-5cf3ff79",
  "@metamask-previews/eip1193-permission-middleware": "1.0.1-preview-5cf3ff79",
  "@metamask-previews/ens-controller": "17.1.0-preview-5cf3ff79",
  "@metamask-previews/error-reporting-service": "2.2.1-preview-5cf3ff79",
  "@metamask-previews/eth-json-rpc-provider": "5.0.1-preview-5cf3ff79",
  "@metamask-previews/foundryup": "1.0.1-preview-5cf3ff79",
  "@metamask-previews/gas-fee-controller": "24.1.0-preview-5cf3ff79",
  "@metamask-previews/gator-permissions-controller": "0.2.1-preview-5cf3ff79",
  "@metamask-previews/json-rpc-engine": "10.1.1-preview-5cf3ff79",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-5cf3ff79",
  "@metamask-previews/keyring-controller": "23.1.1-preview-5cf3ff79",
  "@metamask-previews/logging-controller": "6.1.0-preview-5cf3ff79",
  "@metamask-previews/message-manager": "13.0.1-preview-5cf3ff79",
  "@metamask-previews/messenger": "0.3.0-preview-5cf3ff79",
  "@metamask-previews/multichain-account-service": "1.6.1-preview-5cf3ff79",
  "@metamask-previews/multichain-api-middleware": "1.2.1-preview-5cf3ff79",
  "@metamask-previews/multichain-network-controller": "1.0.1-preview-5cf3ff79",
  "@metamask-previews/multichain-transactions-controller": "5.1.0-preview-5cf3ff79",
  "@metamask-previews/name-controller": "8.1.0-preview-5cf3ff79",
  "@metamask-previews/network-controller": "24.2.1-preview-5cf3ff79",
  "@metamask-previews/network-enablement-controller": "2.1.1-preview-5cf3ff79",
  "@metamask-previews/notification-services-controller": "18.3.0-preview-5cf3ff79",
  "@metamask-previews/permission-controller": "11.1.0-preview-5cf3ff79",
  "@metamask-previews/permission-log-controller": "4.1.0-preview-5cf3ff79",
  "@metamask-previews/phishing-controller": "14.1.2-preview-5cf3ff79",
  "@metamask-previews/polling-controller": "14.0.1-preview-5cf3ff79",
  "@metamask-previews/preferences-controller": "20.0.2-preview-5cf3ff79",
  "@metamask-previews/profile-sync-controller": "25.1.1-preview-5cf3ff79",
  "@metamask-previews/rate-limit-controller": "6.1.0-preview-5cf3ff79",
  "@metamask-previews/remote-feature-flag-controller": "1.8.0-preview-5cf3ff79",
  "@metamask-previews/sample-controllers": "2.0.1-preview-5cf3ff79",
  "@metamask-previews/seedless-onboarding-controller": "4.1.0-preview-5cf3ff79",
  "@metamask-previews/selected-network-controller": "24.0.1-preview-5cf3ff79",
  "@metamask-previews/shield-controller": "0.3.2-preview-5cf3ff79",
  "@metamask-previews/signature-controller": "34.0.1-preview-5cf3ff79",
  "@metamask-previews/subscription-controller": "1.0.1-preview-5cf3ff79",
  "@metamask-previews/token-search-discovery-controller": "3.4.0-preview-5cf3ff79",
  "@metamask-previews/transaction-controller": "60.6.1-preview-5cf3ff79",
  "@metamask-previews/user-operation-controller": "39.1.0-preview-5cf3ff79"
}

@micaelae micaelae enabled auto-merge (squash) October 13, 2025 18:24
);
expect(bridgeController.state.quotesLastFetched).toBeNull();
expect(bridgeController.state.quotesLastFetched).toBeCloseTo(
Date.now() - 1000,
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the 1000, 500, and 10000 values set arbitrarily, and could they affect the output of these test cases?

Copy link
Member Author

@micaelae micaelae Oct 13, 2025

Choose a reason for hiding this comment

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

They're not arbitrary and can affect some of the tests. Most of the tests advance to the next timer (ignores the number of seconds). But some tests that check for intermediate and loading states advance to specific timestamps, which depend on the timeouts set when mocking the server

serverEventHandlers.onValidQuoteReceived(quoteResponse).then((v) => {
return v;
});
} catch (error) {
Copy link

Choose a reason for hiding this comment

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

Bug: SSE Handler Fails on Malformed JSON and Unhandled Promises

The fetchBridgeQuoteStream's onMessage handler has two issues. JSON.parse(event.data) lacks error handling, which can crash the SSE connection with malformed JSON. Additionally, the async serverEventHandlers.onValidQuoteReceived call is not awaited or caught, leading to unhandled promise rejections and potential silent failures.

Fix in Cursor Fix in Web

@micaelae micaelae merged commit 8c27414 into main Oct 13, 2025
243 checks passed
@micaelae micaelae deleted the swaps3025-quote-streaming branch October 13, 2025 19:01
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.

3 participants