Skip to content

feat(analytics): report signing for internal transactions and add swap flow stages - #1008

Merged
JakeUrban merged 6 commits into
mainfrom
claude/fervent-albattani-yh5r0j
Sep 11, 2026
Merged

feat(analytics): report signing for internal transactions and add swap flow stages#1008
JakeUrban merged 6 commits into
mainfrom
claude/fervent-albattani-yh5r0j

Conversation

@JakeUrban

@JakeUrban JakeUrban commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What

This change makes signing countable.

Sends, swaps and trustline changes now report how signing ended. The record names one of three outcomes: a signature, a decline by the user, or a failure the user did not choose. Both kinds of transaction use the same three. Failure is new for transactions, so a signing fault previously went unrecorded.

Each flow reports every outcome at its own point. An approval reports once a signature exists. A failure reports when signing cannot produce one. A rejection reports when the user leaves the review without approving, by any route. An approval sets a latch as the review opens, so it is never counted as a rejection.

Collectible sends are out of scope, so they report nothing for signing.

Every signing event now names its origin, either a website or the wallet itself. Both origins carry the same properties. A single query therefore counts all signing, and the origin splits that count. The website identity resolves before signing, so a failed request stays attributable to the site that asked for it.

The swap flow gains its in-flight stage and its success stage. The send flow reports both. Swap reports neither, so nobody can follow a swap past its review sheet.

Seven screen names are deleted. Nothing reports them, and their screens no longer exist.

The schema version moves up. Consumers need to tell an old client from a new one.

Why

Only requests from websites report signing today. Every transaction the wallet composes itself reports nothing for the signing action. Approval rate and signing outcomes are therefore unmeasurable for those flows.

A stage event cannot replace this. Swap reports no in-flight stage and no success stage at all. A stage event also marks a point in the flow, not a decision. Two separate signals answer the two questions: the stage event reports where the user is, and the signing event reports what they chose.

This is the mobile half of a cross-platform change. stellar/freighter#3008 is the browser-extension half. The two were written together, so both clients report the same events with the same shape.

Known limitations

This is not a purely additive change. Existing signing events gain a property, seven screen names disappear, and the schema version moves up. Any dashboard that depends on those names, or on the previous schema version, needs attention.

This pull request mixes a cleanup with the feature. The seven unused screen names are unrelated to signing. They are included because they sit in the same place, and their removal only deletes lines. Ask, and they move to a separate pull request.

No device run. Verification is automated only. Nothing here changes the screen, so there is nothing to show. Both platforms deserve a manual pass before this leaves draft, because the new reports sit inside real signing paths.

One funnel stays open. The flow exits without reporting an outcome when the wallet locks itself during a confirmation. An in-flight stage then has no ending. This behaviour predates this change, and this pull request does not address it.

Collectible sends share the send screen names. Stage events cannot separate them, and they report no signing outcome at all. Both are out of scope here.

Checklist

PR structure

  • This PR does not mix refactoring changes with feature changes (break it down into smaller PRs if not). — the deletion is a cleanup; see Known limitations.
  • This PR has reasonably narrow scope (break it down into smaller PRs if not).
  • This PR includes relevant before and after screenshots/videos highlighting these changes. — not applicable; nothing on screen changes.
  • I took the time to review my own PR.

Testing

  • These changes have been tested and confirmed to work as intended on Android.
  • These changes have been tested and confirmed to work as intended on iOS.
  • These changes have been tested and confirmed to work as intended on small iOS screens.
  • These changes have been tested and confirmed to work as intended on small Android screens.
  • I have tried to break these changes while extensively testing them.
  • This PR adds tests for the new functionality or fixes.

Release

  • This is not a breaking change. — see Known limitations.
  • This PR updates existing JSDocs when applicable.
  • This PR adds JSDocs to new functionalities.
  • I've checked with the product team if we should add metrics to these changes.
  • I've shared relevant before and after screenshots/videos highlighting these changes with the design team and they've approved the changes. — not applicable; nothing on screen changes.

Verification

Check Result
Test suite 3260 pass, no failures
Type check clean
Lint clean
Format clean

New tests cover each signing outcome, the origin on both kinds of transaction, the attribution of a failed website request, and the three swap stages surviving a rapid swap.

The pre-commit hook could not run here. The network policy blocks the package host it installs from. Its three steps ran directly instead: lint and format on the staged files, then the test suite, then the type check. All three passed, and the commit then skipped the hook.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z

…p stages

Internal transactions reported nothing for the signing action. Only a
dApp request did, so approvals and signing outcomes were unmeasurable
for the transactions the wallet composes itself.

Add `signing.transaction_failed`. The message and auth-entry families
each carry approved, rejected and failed. The transaction family had
no failure event, so a signing fault went unreported.

Add a `source` property to every signing event: `dapp_api` for a
WalletConnect request, `internal` for a wallet-composed one. Both
origins now emit the same events with the same properties, so one query
counts all signing and `source` splits it. An internal transaction has
no origin, so it omits that property.

Emit the three outcomes from the send and swap flows. Approved and
failed come from each flow's signing step. Rejected comes from the
review sheet's explicit cancel — the confirm path dismisses the sheet
separately, so an approval never reaches it.

Add the processing and success stages to the swap flow. The swap
processing screen tracked its own status but held no analytics code, so
a swap could not be followed past the review sheet while the send flow
reported both stages.

Delete seven screen names that nothing emits. Their routes no longer
exist.

Bump the schema version to 4, matching the extension.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-f0da3e7f6ab25f94ca3f
Backend: V1 prod + V2 staging (no sandbox configured for @JakeUrban). SDF collaborators only — install instructions in the release description.

@JakeUrban
JakeUrban marked this pull request as ready for review September 10, 2026 23:40
Copilot AI balanced review requested due to automatic review settings September 10, 2026 23:40
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-4166d04c174106b6a317
Backend: V1 prod + V2 staging (no sandbox configured for @JakeUrban). SDF collaborators only — install instructions in the release description.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds unified signing-outcome analytics for wallet-originated transactions and swap processing/success stages.

Changes:

  • Adds signing source and outcome events.
  • Instruments send and swap signing paths.
  • Adds swap processing/success telemetry and schema v4.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/services/analytics/types.ts Adds signing source metadata.
src/services/analytics/transactions.ts Adds internal signing helpers and source properties.
src/services/analytics/index.ts Exposes new analytics helpers.
src/services/analytics/core.ts Bumps schema version.
src/config/analyticsConfig.ts Adds swap stages and transaction failure event.
src/components/screens/SwapScreen/screens/SwapProcessingScreen.tsx Emits swap processing and success stages.
src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx Attempts to track review rejection.
src/components/screens/SwapScreen/hooks/useSwapTransaction.ts Tracks signing success and failure.
src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx Tracks send signing outcomes.
__tests__/services/analytics/transactions.test.ts Tests signing event payloads.
__tests__/services/analytics/core.test.ts Updates schema assertions and swap throttling coverage.
__tests__/components/screens/SwapScreen/useSwapTransaction.test.ts Updates analytics mocks.
__tests__/components/screens/SendScreen/screens/TransactionAmountScreen.test.tsx Updates analytics mocks.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx Outdated
Comment thread src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx Outdated
Comment thread src/components/screens/SwapScreen/screens/SwapProcessingScreen.tsx Outdated
Comment thread src/services/analytics/transactions.ts
Comment thread src/services/analytics/transactions.ts
Report a rejection when the user leaves the review sheet, not when a
Cancel button runs. The swap footer dismissed the sheet directly, and
the sheet renders custom content, so the close handler never ran. Swap
rejections therefore never reported. Swipe-down and backdrop dismissal
missed both flows. A confirmation now sets an approval latch, so an
approval is not reported as a rejection.

Key the swap success stage to the transaction hash, not the settled
status. That status also waits for the follow-up details request, and
that request only logs when it fails. A settled swap whose details
never arrived dropped out of the funnel.

Report signing for collectible sends and trustline changes. Both sign a
wallet-composed transaction, so both belong in the internal signing
totals.

Report a signing failure for transactions a website requests. The
failure outcome covered wallet-composed transactions only, so a
WalletConnect signing fault stayed unreported.

Add the new helpers to the shared test mock, so every suite resolves
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z
Copilot AI review requested due to automatic review settings September 11, 2026 03:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

tests/components/screens/SendScreen/screens/TransactionAmountScreen.test.tsx:66

  • These new analytics methods are only stubbed, so the existing confirmation test still passes if approval/failure/rejection reporting is removed or misclassified. Add assertions to the successful-sign and pre-submit-failure cases, and exercise onDismiss both with and without the approval latch.
    tests/components/screens/SwapScreen/useSwapTransaction.test.ts:114
  • The new signing calls are mocked but never asserted. The existing null-signature and successful-swap cases should verify trackInternalSignedTransactionError and trackInternalSignedTransaction respectively (and that the opposite event is absent), otherwise this new instrumentation can regress while the suite remains green.

src/hooks/useManageTokens.ts:346

  • The removal path has the same gap as addition: a thrown signing error skips this success call and reaches the generic token-management catch without emitting signing.transaction_failed. Report the caught signing error locally, then rethrow for the existing failure behavior.
        // A trustline change is a wallet-composed transaction, so it reports
        // its signing outcome like a send or a swap does.
        analytics.trackInternalSignedTransaction();

src/helpers/walletKitUtil.ts:566

  • For exceptions raised while parsing or signing, dappDomain is still undefined because it is assigned only after signing returns. Consequently this failure path also drops origin; resolve the session metadata before entering the signing try so both failure forms receive it.
    // Signing threw. The user already approved the prompt, so this is a fault
    // and not a decision.
    analytics.trackSignedTransactionError({
      error: errorMessage,
      ...(dappDomain ? { dappDomain } : {}),

Comment thread src/components/screens/SendScreen/screens/SendCollectibleReview.tsx Outdated
Comment thread src/helpers/walletKitUtil.ts Outdated
Comment thread src/hooks/useManageTokens.ts
Comment thread src/components/screens/SwapScreen/screens/SwapAmountScreen.tsx
Comment thread src/helpers/walletKitUtil.ts Outdated
Comment thread src/hooks/useManageTokens.ts
…ch correctly

Resolve the website identity before signing. It was read only after
signing succeeded, so every signing failure reported no origin and
could not be attributed to the site that asked for it.

Clear the approval latch as the review sheet opens, not as it closes.
The dismiss handler returns early when the sheet is not present, so an
earlier approval could stay latched and swallow the next rejection.

Report a trustline signing failure. Signing can throw, and the
enclosing handler reported only the operation failure, so a signing
fault stayed outside the failure outcome.

Remove the collectible send reporting. Collectibles are out of scope
for this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z
Copilot AI review requested due to automatic review settings September 11, 2026 20:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

src/services/analytics/transactions.ts:70

  • The wallet-composed collectible-send path is still uninstrumented: SendCollectibleReview.tsx calls signTransaction but emits none of these approved/rejected/failed signing events. As a result, the stated “every transaction” funnel—and the companion PR’s explicit collectible-send case—remains incomplete. Please wire all three outcomes into that review flow as well.
export const trackInternalSignedTransaction = (): void => {
  track(AnalyticsEvent.SIGN_TRANSACTION_SUCCESS, { source: "internal" });

Comment thread src/hooks/useManageTokens.ts
Comment thread src/hooks/useManageTokens.ts
Report a rejection when the user leaves a trustline prompt without
approving, by any route. The add and remove prompts reported an
approval and a failure but never a rejection, so the trustline funnel
had no decline side. Each prompt now marks an approval as it confirms,
and clears that mark as it opens.

Report nothing for a token the wallet stores on the device. Those add
and remove without signing, so they have no signing outcome.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z
Copilot AI review requested due to automatic review settings September 11, 2026 21:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/services/analytics/transactions.ts:52

  • This JSDoc still lists collectible sends as an emitting internal flow, but the PR explicitly keeps collectible signing uninstrumented and instead adds trustline changes. Update the examples so callers and analytics consumers do not infer unsupported collectible coverage.

src/components/screens/AddTokenScreen/AddTokenScreen.tsx:402

  • This dismissal also runs for the CannotRemoveTokenBottomSheet branches used by native assets and assets with a positive balance (lines 240–268). Those sheets offer no approval and never attempt signing, but promptSigns is true for these non-custom tokens, so closing the informational sheet is incorrectly counted as a signing rejection. Gate this event on whether the removable confirmation content was actually shown.
            onDismiss: () => reportDismissal(hasApprovedRemoveRef),

Comment thread src/components/screens/AddTokenScreen/AddTokenScreen.tsx Outdated
Comment thread src/components/screens/TokenDetailsScreen/TokenDetailsScreen.tsx
Comment thread src/components/screens/AddTokenScreen/AddTokenScreen.tsx Outdated
The add prompt and the security detail prompt belong to the same add
attempt. Opening the security details closed the add prompt and reported
a rejection, and approving there reported an approval as well. Suppress
the report during the hand-off and report the outcome of the security
detail prompt instead.

The removal prompt shows a message when the wallet cannot remove the
token. That message offers no signing decision, so leaving it is not a
rejection. Read the prompt body from one function, so the prompt the user
sees and the outcome the wallet reports always agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qkq7j6uvUMU1bBQdcuGX8Z
Copilot AI review requested due to automatic review settings September 11, 2026 22:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/helpers/walletKitUtil.ts:540

  • The new test passes dappDomain directly to the analytics helper, so it does not verify this integration—the exact area that previously lost attribution because metadata was resolved after signing. Add an approveSessionRequest test that makes signing return null (and throw) and asserts trackSignedTransactionError receives the resolved website domain.
    src/services/analytics/transactions.ts:52
  • This docstring says collectible sends emit internal signing events, but the PR explicitly leaves collectible sends uninstrumented. That makes the public SigningSource documentation inaccurate; list only the internal flows currently covered.

Copilot AI review requested due to automatic review settings September 11, 2026 22:24
@JakeUrban
JakeUrban merged commit b23fcfa into main Sep 11, 2026
12 of 14 checks passed
@JakeUrban
JakeUrban deleted the claude/fervent-albattani-yh5r0j branch September 11, 2026 22:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Comment thread jest.setup.js
Comment on lines +282 to +285
trackSignedTransactionError: jest.fn(),
trackInternalSignedTransaction: jest.fn(),
trackInternalSignedTransactionRejected: jest.fn(),
trackInternalSignedTransactionError: jest.fn(),

// Removing a contract token only drops it from the local custom-token list,
// so it works only for tokens that are on screen *because* of that list. One
// the backend reports on its own would come straight back on the next poll;
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.

4 participants