Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/components/UI/OptinMetrics/OptinMetrics.navigation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,15 @@ describe('OptinMetrics — interest questionnaire navigation branching', () => {
await waitFor(() => {
expect(Logger.error).toHaveBeenCalledWith(
expect.objectContaining({ message: 'provisioning failed' }),
'OptinMetrics: provisionFromMetadata failed',
expect.objectContaining({
tags: expect.objectContaining({
feature: 'qr-sync',
surface: 'import',
operation: 'provision_from_metadata',
source: 'OptinMetrics',
syncFlow: 'new_user',
}),
}),
);
});
});
Expand Down
27 changes: 17 additions & 10 deletions app/components/Views/AddDeviceToWallet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ import { showAddDeviceVerificationSheet } from '../../../core/QrSync/showAddDevi
import { useAddDeviceResetToInstructionsListener } from '../../../core/QrSync/useAddDeviceResetToInstructionsListener';
import { useIsQrTabSwitcherOpen } from '../../../core/QrSync/useIsQrTabSwitcherOpen';
import { useQrSyncImportNavigation } from '../../../core/QrSync/useQrSyncImportNavigation';
import {
QrSyncOperations,
QrSyncSurfaces,
QrSyncTelemetrySources,
reportQrSyncFailure,
} from '../../../core/QrSync/qrSyncTelemetry';
import type { AppNavigationProp } from '../../../core/NavigationService/types';
import Logger from '../../../util/Logger';
import {
selectQrSyncError,
selectQrSyncIsBusy,
Expand Down Expand Up @@ -119,10 +124,11 @@ const AddDeviceToWallet = () => {
const scannedQrPayload = content ?? data.content ?? '';

submitQrPayload(scannedQrPayload).catch((err: unknown) => {
Logger.error(
err as Error,
'AddDeviceToWallet: failed to submit scanned QR payload',
);
reportQrSyncFailure(err, {
surface: QrSyncSurfaces.SCANNER,
operation: QrSyncOperations.SUBMIT_SCANNED_PAYLOAD,
source: QrSyncTelemetrySources.ADD_DEVICE_ON_SCAN_SUCCESS,
});
});
},
[submitQrPayload],
Expand Down Expand Up @@ -150,11 +156,12 @@ const AddDeviceToWallet = () => {
}, [manualQrPayload, submitQrPayload]);

const triggerManualQrSubmit = useCallback(() => {
handleManualQrSubmit().catch((error: unknown) => {
Logger.error(
error as Error,
'AddDeviceToWallet: failed to submit manual QR payload',
);
handleManualQrSubmit().catch((submitError: unknown) => {
reportQrSyncFailure(submitError, {
surface: QrSyncSurfaces.SCANNER,
operation: QrSyncOperations.SUBMIT_MANUAL_PAYLOAD,
source: QrSyncTelemetrySources.ADD_DEVICE_MANUAL_SUBMIT,
});
});
}, [handleManualQrSubmit]);

Expand Down
17 changes: 17 additions & 0 deletions app/components/Views/QRScanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ import AddDeviceScannerRecovery, {
AddDeviceScannerPermissionDenied,
} from './AddDeviceScannerRecovery';
import { EXTENSION_ACCOUNT_SYNC_CONNECTION_FAILED_EVENT } from '../../../core/ExtensionAccountSync/types';
import {
QrSyncOperations,
QrSyncSurfaces,
QrSyncTelemetrySources,
reportQrSyncFailure,
} from '../../../core/QrSync/qrSyncTelemetry';

const sleep = (ms: number) =>
new Promise<void>((resolve) => {
Expand Down Expand Up @@ -258,6 +264,17 @@ const QRScanner = ({
if (classification !== 'valid') {
shouldReadBarCodeRef.current = false;
setIsCameraActive(false);
if (classification === 'invalid') {
reportQrSyncFailure(
new Error('Add-device QR scan classified as invalid'),
{
surface: QrSyncSurfaces.SCANNER,
operation: QrSyncOperations.CLASSIFY_SCAN_CONTENT,
errorCode: 'INVALID_PAYLOAD',
source: QrSyncTelemetrySources.QR_SCANNER_ADD_DEVICE,
},
);
}
trackEvent(
createEventBuilder(MetaMetricsEvents.QR_SCANNED)
.addProperties({
Expand Down
84 changes: 74 additions & 10 deletions app/core/QrSync/QrSyncController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ import {
RELAY_URL,
} from './constants';
import { routeIncomingQrSyncMessage } from './services/qr-sync-message-router';
import Logger from '../../util/Logger';
import {
addQrSyncPhaseBreadcrumb,
QrSyncOperations,
QrSyncSurfaces,
QrSyncTelemetrySources,
reportQrSyncFailure,
} from './qrSyncTelemetry';

const metadata: StateMetadata<QrSyncControllerState> = {
phase: {
Expand Down Expand Up @@ -221,10 +227,12 @@ export class QrSyncController extends BaseController<
try {
this.finalizeSecretImport();
} catch (error) {
Logger.error(
Comment thread
grvgoel81 marked this conversation as resolved.
error as Error,
'QrSyncController.importRemainingSecrets finalize',
);
reportQrSyncFailure(error, {
surface: QrSyncSurfaces.IMPORT,
operation: QrSyncOperations.IMPORT_REMAINING_SECRETS_FINALIZE,
phase: this.state.phase,
source: QrSyncTelemetrySources.CONTROLLER_IMPORT_REMAINING,
});
}
}

Expand Down Expand Up @@ -390,6 +398,13 @@ export class QrSyncController extends BaseController<
private readonly handleSessionServiceEvent = (event: QrSyncServiceEvent) => {
switch (event.type) {
case QrSyncActionTypes.OTP_DISPLAY_GRANT: {
const from = this.state.phase;
if (from !== QrSyncPhases.DISPLAYING_OTP) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.DISPLAYING_OTP,
});
}
this.update((state) => {
state.phase = QrSyncPhases.DISPLAYING_OTP;
state.otp = event.data;
Expand All @@ -398,13 +413,27 @@ export class QrSyncController extends BaseController<
break;
}
case QrSyncActionTypes.SYNC_READY: {
const from = this.state.phase;
if (from !== QrSyncPhases.REVIEWING_IMPORT) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.REVIEWING_IMPORT,
});
}
this.update((state) => {
state.phase = QrSyncPhases.REVIEWING_IMPORT;
state.error = null;
});
break;
}
case QrSyncActionTypes.SYNC_COMPLETED: {
const from = this.state.phase;
if (from !== QrSyncPhases.COMPLETED) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.COMPLETED,
});
}
this.update((state) => {
state.phase = QrSyncPhases.COMPLETED;
state.otp = null;
Expand Down Expand Up @@ -438,10 +467,17 @@ export class QrSyncController extends BaseController<
},
});

const from = this.state.phase;
this.update((state) => {
state.otp = null;
state.phase = QrSyncPhases.AWAITING_SYNC_READY;
});
if (from !== QrSyncPhases.AWAITING_SYNC_READY) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.AWAITING_SYNC_READY,
});
}
}

private async sendSyncCompleted(): Promise<void> {
Expand All @@ -450,11 +486,18 @@ export class QrSyncController extends BaseController<
version: QrSyncMessageVersion.V1,
});

const from = this.state.phase;
this.update((state) => {
state.phase = QrSyncPhases.COMPLETED;
state.otp = null;
state.error = null;
});
if (from !== QrSyncPhases.COMPLETED) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.COMPLETED,
});
}
await this.destroySession();
}

Expand Down Expand Up @@ -482,10 +525,12 @@ export class QrSyncController extends BaseController<
entropySource: primaryEntropySource,
});
} catch (error) {
Logger.error(
error as Error,
'QrSyncController.enrichPrimaryProvisioningEntry',
);
reportQrSyncFailure(error, {
surface: QrSyncSurfaces.IMPORT,
operation: QrSyncOperations.ENRICH_PRIMARY_PROVISIONING_ENTRY,
phase: this.state.phase,
source: QrSyncTelemetrySources.CONTROLLER_ENRICH_PRIMARY,
});
}
}

Expand Down Expand Up @@ -517,7 +562,11 @@ export class QrSyncController extends BaseController<
await this.client.sendResponse(message);
}

private transitionTo(phase: QrSyncPhase): void {
private transitionTo(phase: QrSyncPhase, errorCode?: QrSyncErrorCode): void {
const from = this.state.phase;
if (from !== phase) {
addQrSyncPhaseBreadcrumb({ from, to: phase, errorCode });
}
this.update((state) => {
state.phase = phase;
});
Expand Down Expand Up @@ -557,6 +606,21 @@ export class QrSyncController extends BaseController<
await this.destroySession();

if (wireType === QrSyncActionTypes.SYNC_ERROR && error) {
const from = this.state.phase;
if (from !== QrSyncPhases.FAILED) {
addQrSyncPhaseBreadcrumb({
from,
to: QrSyncPhases.FAILED,
errorCode: error.code,
});
}
reportQrSyncFailure(new Error(error.message), {
surface: QrSyncSurfaces.SESSION,
operation: QrSyncOperations.TERMINATE_WITH_ERROR,
errorCode: error.code,
phase: from,
source: QrSyncTelemetrySources.CONTROLLER,
});
this.update((state) => {
state.phase = QrSyncPhases.FAILED;
state.error = error;
Expand Down
13 changes: 13 additions & 0 deletions app/core/QrSync/completeExistingUserQrSyncImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { showImportFailedSheet } from '../../components/Views/AddDeviceToWallet/
import Engine from '../Engine';
import type { AppNavigationProp } from '../NavigationService/types';
import { isDuplicateMnemonicError } from './duplicateMnemonicError';
import {
QrSyncOperations,
QrSyncSurfaces,
QrSyncSyncFlows,
QrSyncTelemetrySources,
reportQrSyncFailure,
} from './qrSyncTelemetry';

export {
DUPLICATE_MNEMONIC_ERROR_MESSAGES,
Expand Down Expand Up @@ -37,6 +44,12 @@ const runExistingUserQrSyncImport = async (
return;
}

reportQrSyncFailure(error, {
surface: QrSyncSurfaces.IMPORT,
operation: QrSyncOperations.EXISTING_USER_MNEMONIC_IMPORT,
source: QrSyncTelemetrySources.COMPLETE_EXISTING_USER_IMPORT,
syncFlow: QrSyncSyncFlows.EXISTING_USER,
});
navigation.navigate(Routes.WALLET_VIEW);
showImportFailedSheet(navigation);
}
Expand Down
Loading
Loading