feat(bridge-controller): add SwapBridge failure telemetry schema and classifiers - #9947
Conversation
6c94234 to
bbf9454
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
There was a problem hiding this comment.
Pull request overview
Adds an additive telemetry schema and pure helper functions to classify SwapBridge failures into stable phases/error codes based on code path + tx-hash presence (not error_message), preparing richer analytics for a follow-up PR that will wire these fields into emitted events.
Changes:
- Introduces
FailurePhaseandSwapBridgeErrorCodeenums and exports them from the package entrypoint. - Adds
failure-telemetryclassifier helpers (+ unit tests) for quote-fetch, submit, and status/poll failure classification. - Extends Unified SwapBridge metrics context types with optional failure-telemetry and hash-presence fields for relevant events.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/bridge-controller/src/utils/metrics/types.ts | Adds HashPresenceData / FailureTelemetryData and threads them into selected Unified SwapBridge event context types. |
| packages/bridge-controller/src/utils/metrics/failure-telemetry.ts | New helper functions to derive error codes, phases, and hash-presence flags. |
| packages/bridge-controller/src/utils/metrics/failure-telemetry.test.ts | Unit tests covering the new classification helpers. |
| packages/bridge-controller/src/utils/metrics/constants.ts | Adds the new telemetry enums used by schema + helpers. |
| packages/bridge-controller/src/index.ts | Re-exports the new enums, types, and helper functions from the package entrypoint. |
| packages/bridge-controller/CHANGELOG.md | Documents the new telemetry exports/types for consumers. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
f8653bd to
c766626
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
| /** | ||
| * Classify a thrown value from submit (sign/broadcast) catch paths. | ||
| * | ||
| * @param error - The thrown value from submit. | ||
| * @returns The Mixpanel `error_code`. | ||
| */ | ||
| export const getSubmitErrorCode = (error: unknown): SwapBridgeErrorCode => { | ||
| if (error === undefined || error === null) { | ||
| return SwapBridgeErrorCode.MissingErrorObject; | ||
| } | ||
| if (error instanceof Error) { | ||
| return SwapBridgeErrorCode.Unknown; | ||
| } | ||
| return SwapBridgeErrorCode.NonErrorRejection; | ||
| }; | ||
|
|
||
| /** | ||
| * @param sourceHash - Source tx hash if known at emit time. | ||
| * @param destinationHash - Destination tx hash if known at emit time. | ||
| * @returns Boolean hash-presence properties. | ||
| */ | ||
| export const getHashPresenceProperties = ( | ||
| sourceHash?: string | null, | ||
| destinationHash?: string | null, | ||
| ): HashPresenceProperties => { | ||
| return { | ||
| source_hash_present: Boolean(sourceHash), | ||
| destination_hash_present: Boolean(destinationHash), | ||
| }; | ||
| }; |
There was a problem hiding this comment.
Please move these and the functions below to the bridge-status-controller's metrics utils
There was a problem hiding this comment.
Quote-fetch classification has to stay in bridge-controller (getQuoteFetchErrorCode + the shared enums/types), because Quotes Error is emitted there and status-controller cannot be a dependency of bridge-controller.
I have moved getHashPresenceProperties, getStatusFailurePhase, getSubmitErrorCode / getSubmitFailureTelemetry, and getStatusFailureTelemetry into bridge-status-controller metrics utils
fc635b0 to
e9a6717
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
0a5cd49 to
ce2699e
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
ce2699e to
edca7dc
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
bc1134c to
08b1a9d
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
08b1a9d to
b26ecce
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…classifiers Add FailurePhase and SwapBridgeErrorCode plus helpers so a follow-up emit can classify failures from the code path and hash presence, without parsing error_message. No Mixpanel payloads change in this commit.
…ields Quote fetch is pre-tx, so the changelog should not list source_hash_present or destination_hash_present on Quotes Error.
…ntroller Keep quote-fetch classification in bridge-controller. Hash presence and submit/status helpers belong with the controller that will emit them.
…etrics utils Fold submit/status failure telemetry helpers into the existing metrics files instead of a new module.
b26ecce to
1cb188b
Compare
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…ontroller Keep optional *Data types for Mixpanel event context and export required *Properties types as the classifier return shape so status-controller does not duplicate them.
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…sence Wire Quotes Error, submit Failed, EVM Failed, poll Failed, Submitted, and Completed to the classifiers from #9947. Mixpanel can now split quote vs broadcast vs source vs dest vs poll without parsing error_message.
…sence (MetaMask#9949) ## Explanation Stacked on MetaMask#9947 (schema + classifiers only). This PR **emits** the new Mixpanel fields so Failed is no longer a blended "on-chain" rate. Every value comes from the code path and hash presence, never from `error_message`. ### What each event emits | Event | Emitted by | What we emit | |---|---|---| | Quotes Error | `bridge-controller` | `failure_phase=quote`, `error_code` from `getQuoteFetchErrorCode(error)` | | Failed (submit catch, before `executeSubmitStrategy`) | `bridge-status-controller` | `getSubmitFailureTelemetry(error)` → `broadcast`, both hash flags `false` | | Failed (EVM tx meta) | `bridge-status-controller` | `source_execution` when the tx has a hash, else `broadcast`; `error_code` `unknown` when the tx carries an error, else `missing_error_object` | | Failed (status poll) | `bridge-status-controller` | `destination_execution` > `source_execution` > `poll`, from history src/dest hashes | | Submitted | `bridge-status-controller` | both hash flags `false` (emit is still before the submit strategy) | | Completed | `bridge-status-controller` | hash flags from bridge history src/dest hashes | ### Also added `promoteFailurePhase` (new in `bridge-status-controller/src/utils/metrics.ts`) reconciles the phase reported by the emitting path with the combined hash presence — history flags OR-ed with any caller-supplied flags. It upgrades to `destination_execution` on a dest hash and to `source_execution` on a source hash, but only from `broadcast` / `poll` / `unknown`, so a genuine no-hash broadcast failure is never reclassified as `poll`. On Failed, a caller-supplied `failure_phase` wins over the status-derived one, and its `error_code` is kept (defaulting to `unknown`); when the caller supplies no phase, both come from `getStatusFailureTelemetry`. ### Ownership `bridge-status-controller` owns submit, poll, and EVM emit. `bridge-controller` owns Quotes Error and the Failed property defaults — when a client omits them, Failed falls back to `failure_phase=unknown`, `error_code=unknown`, and both hash flags `false`, so the properties are always present in the payload. After MetaMask#9947 merges, retarget this PR to `main`. ## References * [WPN-1877](https://consensys.atlassian.net/browse/WPN-1877) * Epic: [WPN-1875](https://consensys.atlassian.net/browse/WPN-1875) * Schema: [WPN-1876](https://consensys.atlassian.net/browse/WPN-1876) * Depends on: MetaMask#9947 ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Changes are limited to analytics payload shaping and classifier wiring; swap/bridge execution behavior is unchanged. > > **Overview** > This PR **wires up emission** of the SwapBridge failure telemetry schema from MetaMask#9947 so Mixpanel events carry structured `failure_phase`, `error_code`, and hash-presence fields derived from code paths (not `error_message`). > > **`bridge-controller`** adds `failure_phase=quote` and a classified `error_code` on **Quotes Error** (via `getQuoteFetchErrorCode` in the quote-fetch catch and in the event property builder). **Failed** event assembly is adjusted so client-supplied failure fields merge correctly with quote/request metadata. > > **`bridge-status-controller`** attaches telemetry across the trade lifecycle: **Submitted** always emits both hash flags as `false`; **Completed** adds hash presence from bridge history; **Failed** combines broadcast catch helpers (`getBroadcastFailureProperties`), history-based classification (`getFailurePropertiesFromHistory`), EVM tx-meta fields, and **`promoteFailurePhase`** so phases align with combined hash presence without turning no-hash broadcast failures into `poll`. Failed events without a history item still get explicit defaults for all four fields. > > Public helper renames: `getSubmitFailureTelemetry` → `getBroadcastFailureProperties`, `getStatusFailureTelemetry` → `getFailurePropertiesFromHistory`. The **Failed** event type now requires **`FailureTelemetryProperties`** instead of optional telemetry data. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c73f0b7. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> [WPN-1877]: https://consensyssoftware.atlassian.net/browse/WPN-1877?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Explanation
Problem. SwapBridge
Failedtoday is one bucket. Mixpanel cannot tell quote-fetch vs submit vs source vs dest vs poll, or whether a source/dest hash existed. That hides Tron/non-EVM diagnosis behind a blended "on-chain" failure rate. Free-texterror_messageis not a reliable classifier.This PR adds the schema and classifiers only. It does not change Mixpanel payloads - runtime analytics behavior is unchanged until the follow-up wires these into emit paths.
Classification is always derived from the failing code path and hash presence, never from
error_messagetext.Content
Shared types live in bridge-controller: optional *Data for Mixpanel event context, and required *Properties as the classifier return shape. bridge-status-controller imports those types rather than redefining them.
@metamask/bridge-controllerFailurePhase,SwapBridgeErrorCodeenums ·HashPresenceData,FailureTelemetryData·HashPresenceProperties,FailureTelemetryProperties.getQuoteFetchErrorCode@metamask/bridge-status-controllergetSubmitErrorCode·getHashPresenceProperties·getStatusFailurePhase·getSubmitFailureTelemetry·getStatusFailureTelemetryEvent context types (all optional, additive)
failure_phase,error_codefailure_phase,error_code,source_hash_present,destination_hash_presentsource_hash_present,destination_hash_presentsource_hash_present,destination_hash_presentProperty values
failure_phasequote·broadcast·source_execution·destination_execution·poll·unknownerror_codequote_fetch_failed·missing_error_object·non_error_rejection·status_failed_without_reason·unknownsource_hash_present/destination_hash_presentClassification rules
getQuoteFetchErrorCode—missing_error_objectwhen the thrown value is nullish,quote_fetch_failedfor anError,non_error_rejectionotherwise.getSubmitFailureTelemetryalways reportsbroadcastwith both hash flagsfalse, because Core currently emits Failed beforeexecuteSubmitStrategy.getStatusFailurePhaseprefersdestination_executionwhen a dest hash exists, thensource_executionwhen a source hash exists, and falls back topollonly when neither is present.A follow-up PR (#9949) emits these fields from Quotes Error, submit catch, EVM failed, poll, Submitted, and Completed.
References
Checklist
Note
Low Risk
Additive types, exports, and pure classifier utilities with tests; no controller emit-path or runtime metrics behavior changes in this PR.
Overview
Introduces structured SwapBridge failure analytics ahead of wiring Mixpanel emits: stable
failure_phaseanderror_codevalues derived from code path and tx-hash presence, not fromerror_message.@metamask/bridge-controlleraddsFailurePhase/SwapBridgeErrorCode, optionalHashPresenceDataandFailureTelemetryDataon Unified SwapBridge event context types (Quotes Error, Failed, Submitted, Completed), exportsgetQuoteFetchErrorCodefor quote-fetch failures, and documents the split with bridge-status for submit/status.@metamask/bridge-status-controllerexports helpers to classify submit catch failures (getSubmitErrorCode,getSubmitFailureTelemetry→broadcast) and status/poll failures (getHashPresenceProperties,getStatusFailurePhase,getStatusFailureTelemetry), with unit tests.Runtime analytics payloads are unchanged until a follow-up calls these classifiers at emit sites.
Reviewed by Cursor Bugbot for commit 6474ce8. Bugbot is set up for automated code reviews on this repo. Configure here.