Add volume telemetry for swaps and payments with USD pricing - #2984
Conversation
Implements the requirements in design-docs/swap-send-usd-volume/requirements.md for the extension. payment.completed/failed and swap.completed/failed now carry a USD-denominated source-leg value (amount_usd + status/rate/source/ freshness), full asset identity (code/issuer/type, with SAC-to-classic collapse), token amounts, and — for swaps — the settled destination amount read from the transaction result, quote vs. settled slippage, and a bounded failure_category alongside the existing reason_code. - helpers/usdVolume.ts: half-up USD rounding, per-leg USD derivation, slippage math, SAC-collapse asset classification, and the reason_code -> failure_category mapping. - helpers/confirmationPriceSnapshot.ts: one price fetch covering both legs, started at confirmation and never blocking signing/submission; falls back to the cached display price if still pending at terminal status. - helpers/transactionResult.ts: reads the settled pathPaymentStrictSend destination amount from the transaction's result XDR. - useSubmitTxData.tsx is now the single, centralized emit site for every terminal event (success and failure). This fixes the double-emit-on- remount bug in SubmitFail's old effect and means a quote expiring at submit now also emits swap.failed (failure_category: slippage) alongside the existing swap.quote_expired, with no special-casing needed. - schema_version bumped to "3" per the versioning requirement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015guUa7BbRavdRi9bHBSSi1
Four fixes from review against the requirements doc, plus the emit-site test coverage the helpers-only tests were missing. - swap.failed now carries from_amount: TR-1 requires every terminal event to carry the source token amount, settled or rejected; payment.failed already had it. - failure_category now classifies an answered-without-a-verdict response as transport, not unknown: a 5xx (outcome undetermined), 408 (timeout), 429 (rate limit), or 403 (proxy) without result_codes never judged the transaction (TR-72). unknown is reserved for a definitive 4xx rejection that carried no result codes. - The confirmation price fetch is now actually cancelled, not abandoned: getTokenPrices takes an optional AbortSignal (a true network abort on the v1 path; the v2 request runs in the background worker across a message boundary the signal cannot cross, so there it skips an unsent request and rejects an unwanted result). resolve() aborts a still-pending fetch, and a new cancel() on the handle covers pre-submission failures, called from fetchData's catch (TR-11, TR-71). - A rejected fetch now falls back to the display-cache price (cached_display) instead of resolving as confirmation_fetch with no prices, which reported priceable legs as no_price (TR-11). New useSubmitTxData.telemetry.test.tsx covers the emit shapes end to end with every external call mocked: property sets per event, settled destination amount parsed from real result XDR, both slippage figures, quote-expiry rejections landing on swap.failed as slippage, transport classification, the cached_display fallback, no_price omission, and TR-9's single two-leg price request. Note: SubmitTransaction.test.tsx "shows verify account modal and confirms password" fails identically on the base branch (verified at the merge base); it is not a regression of this branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015guUa7BbRavdRi9bHBSSi1
Comments across the volume telemetry implementation cited a requirements doc (TR-N ids, §section refs) that lives outside this repo and isn't a stable reference for anyone reading this code later. Rewrote each comment to state the reasoning inline instead of pointing at a numbered requirement, so it stands on its own. No behavior change. tsc clean; the four affected suites (usdVolume, confirmationPriceSnapshot, transactionResult, and the emit-site telemetry test) still pass in full. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015guUa7BbRavdRi9bHBSSi1
…ption Documents a real edge case found during review: the balances-based SAC reverse-lookup only works when the account holds the asset (so its classic form is findable) or when the balances cache is fresh. A SAC-wrapped classic asset the account doesn't hold, passed as a raw C... issuer, gets silently misreported as soroban. Both current call sites avoid this - the source leg is always drawn from a held-balance picker, and the swap destination leg is always normalized to a classic G... issuer before reaching this function. Flagging it so a future caller (e.g. a destination_asset query param path that isn't currently wired up) doesn't reintroduce it unknowingly. No behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015guUa7BbRavdRi9bHBSSi1
… doing The comment only described the removed telemetry effect and why it was removed. Added a leading line stating the component's actual current responsibility - rendering the failure screen by classifying the error into a RESULT_CODES case - before the historical note. No behavior change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015guUa7BbRavdRi9bHBSSi1
…ap-send-volume-k973lt # Conflicts: # extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx
v17's generated XDR types replaced the old flat union interfaces with per-arm classes: TransactionResultExt/InnerTransactionResultExt are now abstract and must be built via their .v0() factory instead of `new`, and TransactionResultResult/OperationResult/OperationResultTr/ PathPaymentStrictSendResult only expose their arm-specific fields after narrowing on `.type`, so a bare `.results`/`.tr`/`.success` access no longer type-checks without an explicit narrowing check first.
|
PR Preview build is ready: https://github.com/stellar/freighter/releases/tag/untagged-4335d8cc8e17849d1ff2 |
There was a problem hiding this comment.
Pull request overview
Adds USD-volume, slippage, asset classification, and failure telemetry to internal swap and payment flows.
Changes:
- Captures confirmation-time prices with cancellation and cache fallback.
- Reports settled swap/payment outcomes and categorized failures.
- Adds telemetry helpers, schema v3, and unit/integration coverage.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
useSubmitTxData.tsx |
Centralizes terminal transaction telemetry. |
useSubmitTxData.telemetry.test.tsx |
Tests emitted transaction metrics. |
SubmitFail/index.tsx |
Removes duplicate failure emission. |
usdVolume.ts |
Implements USD, slippage, identity, and failure calculations. |
usdVolume.test.ts |
Tests volume helpers. |
transactionResult.ts |
Extracts settled swap amounts from result XDR. |
transactionResult.test.ts |
Tests result-XDR parsing. |
metrics.ts |
Bumps analytics schema to v3. |
metrics.test.ts |
Updates schema assertions. |
confirmationPriceSnapshot.ts |
Captures cancellable confirmation-time prices. |
confirmationPriceSnapshot.test.ts |
Tests snapshot and fallback behavior. |
@shared/api/internal.ts |
Adds cancellation support to price requests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- findPathPaymentStrictSendIndex always returned -1 for fee-bump swaps: FeeBumpTransaction exposes operations via innerTransaction.operations, not a top-level operations property. - The failure reason code always took operations[0], but a swap that prepends changeTrust reports one result code per operation (["op_success", "op_under_dest_min"]) - now picks the first code that isn't itself a success/no-op. - submitFreighterTransaction's custom-network branch discarded the Horizon error's structured response, so failure-category classification always degraded to "transport" on custom networks. - The confirmation price snapshot's cached-display fallback didn't include the destination's stellar.expert spot-price fallback that the receive card itself uses for non-held tokens, so it could under-report a price the user actually saw on screen.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
extension/src/helpers/confirmationPriceSnapshot.ts:97
- On the cached fallback path,
sourcedescribes the endpoint requested now, not the endpoint that producedcachedDisplayPrices. If theuse_token_prices_v2kill switch changes after the display cache was populated, a v1 cached rate is labeledtoken_prices_v2(or vice versa), corrupting source attribution. Persist the cache's price-source metadata and use it here, or report an explicit cached/unknown source.
source,
JakeUrban reviewed and rejected two of the four Copilot findings addressed in b4dee37: - The destination spot-price fallback in the confirmation price snapshot: spotPrice is fetched at a different time from a different source than /token-prices, so folding it into the snapshot's cached-display fallback was the wrong call - omitting it is correct. - The custom-network Horizon error response passthrough in submitFreighterTransaction: this telemetry isn't emitted for custom networks at all, so the fix was moot. The fee-bump operations unwrap (transactionResult.ts) and the reasonCode index-0 fix (useSubmitTxData.tsx) stand - only those two were reverted.
sourceUsd.leg.value is rounded to 2dp for display, so a nonzero source value under half a cent (e.g. $0.003) rounds to 0 and was silently dropping usd_slippage_pct - even though computeUsdSlippagePct already checks the unrounded value for an actual zero. Gate on leg status only and let it decide. Also adds a regression test confirming reasonCode still falls back to the transaction-level code (tx_bad_seq) when no operation ran.
…tination REASON_CODE_TO_FAILURE_CATEGORY mapped op_no_trust/op_not_authorized (the destination-side path-payment/payment result codes) but not their source-side counterparts, op_src_no_trust/op_src_not_authorized. Those are common (the sender lacks a trustline or authorization for the asset it's sending) and were silently falling through to protocol_other instead of trustline.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx:403
- A rejected signing action does reach this branch indirectly: the signing block above only replaces
signedXDRon fulfillment and otherwise continues by submitting the unsignedpreparedTransaction. That makes a local signing failure look like a network/protocol failure (commonlytx_bad_auth) in the new telemetry. Stop the flow whensignFreighterTransactionis rejected before dispatchingsubmitFreighterTransaction.
// Submission was attempted and we're reacting to its outcome — the
// single, centralized failure-emit site (fixes the old effect-based
// double-emit-on-remount bug in SubmitFail). A pre-submission failure
// (signing, simulation) never reaches here, since nothing above this
// point calls submitFreighterTransaction.
extension/src/helpers/transactionResult.ts:56
- A
txFailedresult is atomic and did not settle any operation, even if an earlier per-operation entry isop_success. Accepting it here can therefore return a destination amount that never moved, contrary to this helper's contract. Only decode operation results when the inner transaction result istxSuccess.
if (
innerTxResult.type !== "txSuccess" &&
innerTxResult.type !== "txFailed"
) {
extension/src/helpers/usdVolume.ts:227
- Strict-send/path-payment failures can return
op_src_no_trustorop_src_not_authorizedfor the source trustline. These source-side counterparts currently fall through toprotocol_other, so the bounded failure category is inaccurate. Map them alongside the equivalent destination trustline codes.
op_no_trust: "trustline",
op_src_no_trust: "trustline",
op_line_full: "trustline",
op_not_authorized: "trustline",
Reverts the usdSlippagePct gate change from c651aaa per author feedback - keeping sourceUsd.leg.value !== 0 alongside the leg-status checks. The reasonCode transaction-fallback regression test added in that same commit stands.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extension/src/helpers/usdVolume.ts:176
- This lookup misses a supported SAC-only balance.
injectLocalTokenBalancesexplicitly represents a locally saved SAC without a classic trustline asCODE:CONTRACT_IDwhile retaining the wrappedCODE:G_ISSUERinbalance.name(@shared/api/helpers/injectLocalTokenBalances.ts:80-113,138-153). Because this loop skips every contract issuer, that asset is reported assoroban; its contract canonical ID is then filtered out bygetTokenPrices, so the send also loses USD volume. Resolve SAC identity from the matching contract balance's retained metadata (not only from a separate classic balance), and add this fixture as a regression test.
const classicMatch = Object.values(balances ?? {}).find(
(balance): boolean => {
if (!("issuer" in balance.token)) {
return false;
}
const classicIssuer = balance.token.issuer.key;
if (isContractId(classicIssuer) || balance.token.code !== code) {
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx:182
- This starts a telemetry-only price request even when usage-data sharing is disabled.
emitMetricreturns immediately for opted-out users (helpers/metrics.ts:574-577), so the fetched snapshot is guaranteed to be discarded while still adding a backend request to every send/swap. Gate snapshot creation and the associated telemetry derivation onsettingsDataSharingSelector, or make the snapshot helper return a no-request handle when consent is off.
snapshotHandle = sourceIdentity
? startConfirmationPriceSnapshot({
canonicalIds: [
getCanonicalFromAsset(sourceIdentity.code, sourceIdentity.issuer),
...(destIdentity
? [
getCanonicalFromAsset(
destIdentity.code,
destIdentity.issuer,
),
]
: []),
],
networkDetails,
useV2: useTokenPricesV2,
cachedDisplayPrices,
})
The custom-network fix reverted in 9b3a3cc was dropped on a premise that doesn't holdCorrection of record / low-severity data quality — not a merge blocker. The affected events are filterable in Amplitude, so nothing pollutes pubnet aggregates. TL;DR: One of the two fixes reverted in 9b3a3cc was reverted because "this telemetry isn't emitted for custom networks at all." That isn't the case — the failure metrics do fire on custom networks; there's no network gate anywhere between the rejected submission and Amplitude. The practical consequence is small (every custom-network failure reports as an undifferentiated transport failure, and those events carry a network property so they can simply be excluded from analysis), but the one-line fix that was dropped was correct and does work. Flagging mainly so the reasoning doesn't get reapplied — the conclusion "not worth fixing" may still be right, just not for the stated reason. Detailed explanation (for agents)The revert: 9b3a3cc dropped Why the premise fails — three independent checks:
freighter/extension/src/helpers/metrics.ts Lines 566 to 585 in d486db9
Resulting behaviour on custom networks: The custom branch rejects with freighter/extension/src/popup/ducks/transactionSubmission.ts Lines 109 to 123 in d486db9 Downstream, freighter/extension/src/helpers/usdVolume.ts Lines 275 to 291 in d486db9 So every custom-network failure emits Why this is still low severity: Suggested fixes:
|
Five new union type aliases where
|
Question: is dropping failure telemetry for pre-submission failures intended?Question / scope check — not a merge blocker, but worth an answer before merge since it changes what the dashboards measure. TL;DR: Today the three failure metrics fire whenever the user lands on the failure screen, whatever put them there — including failures that happen before the transaction is ever submitted, like a rejected signing or a failed simulation. This PR moves the emit to the submission-rejected path only, which fixes a real double-counting bug but also means those pre-submission failures now emit nothing at all. That's a narrower definition of "failed" than what's currently being recorded, so any existing failure-rate chart will step down when this ships for a reason unrelated to anything actually improving. Is the narrower definition what was wanted, or should pre-submission failures still emit somewhere? Detailed explanation (for agents)Before — After — the only emit site is the The move itself is right — the old effect re-fired on remount and double-counted, which is called out in the new file comment, and centralizing next to the price snapshot and transaction result is the correct shape. The question is only about the events that fall outside the new site's reach. Concretely, what no longer emits: anything that reaches the failure screen without A related nit in the same area: the new comment at the rejected branch asserts
That's true as a statement about this branch, but it reads as though pre-submission failures are handled elsewhere, when the change is that they're no longer counted at all. Worth rewording to say that explicitly — it's the kind of thing that will otherwise be re-derived by whoever next investigates a failure-rate discontinuity. Possible resolutions:
|
Telemetry emission relies on ~30 non-null assertions instead of narrowingSuggestion / robustness — correct today, but brittle against future edits; not a merge blocker. TL;DR: The centralized telemetry emission uses roughly thirty non-null assertions on three values that are genuinely null for collectible sends. The assertions are safe today only because every branch that uses them happens to be unreachable for collectibles, but that invariant lives far from the use sites and nothing enforces it — a future edit that breaks it would throw mid-flow in a path that's supposed to degrade gracefully, rather than fail visibly at a type check. The project's anti-patterns guide calls this exact pattern out; narrowing once per branch would eliminate all the assertions without changing behavior. Detailed explanation (for agents)Root cause: They are then consumed with and similarly at lines 283–290, 348–365, 419–445, and 454–470. Each site relies on the code-distant invariant "this branch only runs when Guidance: Failure mode if the invariant ever breaks: a Suggested fixes (in increasing order of depth):
|
Partial price-fetch responses discard cached fallback pricesLow severity — telemetry-only gap on swaps whose destination token the price API doesn't cover; not a merge blocker. TL;DR: When the confirmation-time price fetch succeeds but only covers some of the requested tokens, the snapshot keeps just the fresh response and throws away the cached display prices it was holding as a fallback — so the uncovered leg gets reported as unpriced even though a usable price was captured at confirmation. When the fetch fails outright, the code does fall back to those cached prices, so a partial success paradoxically yields worse coverage than a total failure. The code's own stated principle for the failure path is that coverage takes priority over freshness; merging fresh-first, cached-otherwise per token would apply that same principle to partial responses. (This differs from the earlier spot-price discussion — spot price is a different source; the cached display prices come from the same token-prices API, just captured earlier.) Steps to reproduce:
Detailed explanation (for agents)Root cause: in freighter/extension/src/helpers/confirmationPriceSnapshot.ts Lines 74 to 99 in d486db9 The catch block's comment states the intended priority — "coverage takes priority over freshness" — which the success branch doesn't honor for partially-covered responses. Trigger path: Test gap: Suggested fixes (in increasing order of depth):
|
…asserts Per team review: - LegUsdStatus, AssetKind, FailureCategory, PriceSource, PriceFreshness are now enums instead of string union type aliases, matching the project's established convention (freighter-best-practices: "Prefer Enums Over Union Type Aliases"). String values are unchanged, so emitted telemetry is unaffected. - LegUsdResult is now a real discriminated union (LegUsdOk | LegUsdUnpriced) keyed on the enum, so `unrounded`/`value`/`rate` narrow from a `status === LegUsdStatus.Ok` check instead of needing `!`. - Replaced every remaining non-null assertion in useSubmitTxData.tsx (sourceIdentity!, destIdentity!, snapshotHandle!, transactionSimulation.preparedTransaction!) with explicit narrowing guards (`if (!x) throw`) placed where the correlated invariant actually holds (non-collectible for source/snapshot, additionally non-collectible swap for dest). - startConfirmationPriceSnapshot: a confirmation-fetch result that omits any requested canonical id (e.g. a non-held swap destination /token-prices has no entry for) is no longer used at all, even for the ids it does cover - falls back wholesale to the display-cache prices instead of merging, since a partial fetch isn't trustworthy enough to use partially. Verified: tsc clean, full test:ci green (219/219 suites), build:extension compiles.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Suppressed comments (1)
extension/src/helpers/confirmationPriceSnapshot.ts:115
- On this fallback path,
sourcedescribes the endpoint selected for the new confirmation request, not the endpoint that producedcachedDisplayPrices. Because theuse_token_prices_v2kill switch can change after the cache was populated, a cached v2 rate can be reported astoken_prices_v1(or vice versa). Carry the cached rate’s provenance with the cache, or omit/mark the source unknown when that provenance is unavailable.
return {
pricesById: cachedDisplayPrices,
freshness: PriceFreshness.CachedDisplay,
source,
};
Adds an explicit isCustomNetwork guard in useSubmitTxData so swap/payment/collectible-send completed and failed events never fire for custom networks, instead of restoring the previously reverted transactionSubmission.ts response passthrough.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (1)
@shared/api/internal.ts:768
- The new cancellation behavior is not exercised at this API boundary. The snapshot tests mock
getTokenPrices, so they only verify that its signal is aborted—not that a pre-aborted v2 call skipsfetchBackendV2, that an in-flight v2 result is rejected after abort, or that v1 forwards the signal tofetch. Please add those cases to@shared/api/__tests__/internal.test.tsso the cancellation guarantee cannot regress.
if (signal?.aborted) {
throw new DOMException("token-prices request aborted", "AbortError");
833f8ab replaced `transactionSimulation.preparedTransaction!` with a `if (!preparedTransaction) throw` guard while converting non-null assertions to narrowing. That guard is wrong: simulateTx's "classic" arm returns only a recommendedFee and no payload, so preparedTransaction is null for every classic payment. Its XDR reaches the hook via the `xdr` prop and the signing step supplies signedXDR, which is why the old assertion was harmless - the value was always overwritten before use. preparedTransaction only carries a value for a Soroban/token transfer, or for a hardware wallet, where HardwareSign stores the signed XDR there. The guard threw before signing, so every classic send failed outright. That's what CI caught: the three hard e2e failures were all classic XLM payments, never reaching /submit-tx or the post-submit balance refetch. Uses `?? ""` instead - the same fallback Send/index.tsx already applies to this field - so there's no non-null assertion and no behavior change. Adds a regression test covering preparedTransaction: null; makeState now parameterizes the field, which is why the existing telemetry tests (all of which set it) missed this.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx:294
leg.valueis already rounded to two decimals, so a real source value below $0.005 becomes0here and suppresses USD slippage even though the unrounded denominator is nonzero. Checkunrounded.isZero()(or letcomputeUsdSlippagePctperform its existing zero check) so low-value swaps still report their percentage.
sourceUsd.leg.value !== 0
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx:172
- The confirmation price request starts even when data sharing is disabled, although every eventual
emitMetriccall is then guaranteed to no-op. This adds an unnecessary backend request for every opted-out payment/swap, and the v2 request cannot be cancelled once dispatched. Gate snapshot creation and its dependent telemetry branches onsettingsDataSharingSelector.
snapshotHandle = sourceIdentity
? startConfirmationPriceSnapshot({
New enums deviate from the repo's member-naming conventionStyle / consistency — naming only, no behavior or telemetry impact; not a merge blocker. TL;DR: The five new enums from the union-type conversion use PascalCase member names, while the project's style guide and every existing enum in the codebase use SCREAMING_SNAKE_CASE members. The string values are untouched, so nothing changes on the wire — this is purely about keeping the codebase's enum shape uniform, and it's cheapest to align now while the enums are brand new and only referenced within this PR's files. Detailed explanation (for agents)Convention: Deviating enums (all introduced by the union→enum conversion):
freighter/extension/src/helpers/usdVolume.ts Lines 30 to 36 in 4dd8502
freighter/extension/src/helpers/usdVolume.ts Lines 134 to 140 in 4dd8502
freighter/extension/src/helpers/usdVolume.ts Lines 218 to 231 in 4dd8502
freighter/extension/src/helpers/confirmationPriceSnapshot.ts Lines 4 to 14 in 4dd8502 Fix: rename members to SCREAMING_SNAKE ( |
CassioMG
left a comment
There was a problem hiding this comment.
This is looking good to me! Aside from the name convention nit
Per CassioMG's review: code-style.md's "Enum Values" section shows
SCREAMING_SNAKE members regardless of how the enum itself is named, and
every existing enum in the repo follows it (ActionStatus, RequestState,
AccountType, WalletType, NetworkCongestion). The five enums added by the
union->enum conversion used PascalCase members instead.
Mechanical rename of members only - LegUsdStatus, AssetKind,
FailureCategory, PriceSource, PriceFreshness. Every string value is
byte-for-byte unchanged, so the Amplitude wire contract is untouched;
the telemetry tests assert on those raw literals ("ok",
"confirmation_fetch", ...) and still pass unmodified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1rnFig2zYeUtbwnszHQwc
|
Done in a9cd6a5 — confirmed the convention and renamed the members on all five enums. Checked the guidance before applying:
Generated by Claude Code |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
extension/src/popup/components/InternalTransaction/SubmitTransaction/hooks/useSubmitTxData.tsx:198
- If
signFreighterTransactionrejects,signedXDRremains"", but execution still dispatchessubmitFreighterTransactionbelow. This sends an invalid transaction, overwrites the original signing error with a submission error, and now emits a misleading payment/swap failure even though submission should never have been attempted. Return/throw immediately unless signing fulfilled with a non-empty payload before dispatching the submit thunk.
let signedXDR = transactionSimulation.preparedTransaction ?? "";
Summary
Adds analytics for swap and send transactions: how much value (in USD) each transaction moved, how the price moved between quote and settlement, and why a transaction failed when it did. Prices are captured once, when the user confirms, so the numbers we report stay consistent even if the market moves before the transaction finishes.
Key Changes
New Telemetry Infrastructure
usdVolume.ts: The math and classification behind the numbers we reportconfirmationPriceSnapshot.ts: Captures prices at the moment the user confirmstransactionResult.ts: Reads what actually happened on-chainEnhanced Transaction Submission
useSubmitTxData.tsxis now the one place that reports a transaction's outcome, for both successes and failuresSupporting Changes