From 9aca9893faa3f25a1ebf329328f3abdc26d59bf9 Mon Sep 17 00:00:00 2001 From: Alexander Khrushkov Date: Sun, 1 Mar 2026 02:26:15 +0400 Subject: [PATCH 1/3] feat: add centralized error handling with getErrorMessage --- package-lock.json | 25 ++++----------- package.json | 2 +- src/components/wallet/UnlockWallet.tsx | 3 +- .../wallet/modals/ConnectIntentModal.tsx | 7 ++-- src/components/wallet/modals/LookupModal.tsx | 3 +- .../wallet/modals/PaymentRequestModal.tsx | 7 ++-- src/components/wallet/modals/SendModal.tsx | 3 +- .../wallet/modals/SendPaymentRequestModal.tsx | 3 +- src/components/wallet/modals/SwapModal.tsx | 3 +- src/components/wallet/modals/TopUpModal.tsx | 5 +-- .../wallet/onboarding/useOnboardingFlow.ts | 5 +-- .../wallet/shared/RegisterNametagModal.tsx | 3 +- src/platform/extension/SphereProvider.tsx | 3 +- src/platform/extension/background/index.ts | 3 +- .../extension/background/message-handler.ts | 31 +++++++++--------- .../extension/background/wallet-manager.ts | 6 +++- src/platform/extension/popup/main.tsx | 6 ++++ src/sdk/errors.ts | 32 +++++++++++++++++++ 18 files changed, 95 insertions(+), 55 deletions(-) create mode 100644 src/sdk/errors.ts diff --git a/package-lock.json b/package-lock.json index d4cbc7e..cc16eee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@noble/hashes": "^1.7.2", "@scure/base": "^1.2.4", "@tanstack/react-query": "^5.90.0", - "@unicitylabs/sphere-sdk": "^0.5.4", + "@unicitylabs/sphere-sdk": "^0.5.5", "lucide-react": "^0.552.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -68,7 +68,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -2358,7 +2357,6 @@ "integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -2376,7 +2374,6 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -2437,7 +2434,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -2705,9 +2701,9 @@ } }, "node_modules/@unicitylabs/sphere-sdk": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/@unicitylabs/sphere-sdk/-/sphere-sdk-0.5.4.tgz", - "integrity": "sha512-MdkVAEdU9lWD8WGLerwaMxZZJBv1/BQpwd8cnhowRUY4IRCC6EZVIYJP8soshyWPLUQ1f0E0KFfez9arQc88Sg==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@unicitylabs/sphere-sdk/-/sphere-sdk-0.5.5.tgz", + "integrity": "sha512-vh+4IyvFD0m0jCh0USoguBULd7umutO8n1/kPlqrbSKEFqLnULKPLuWKuUP1qMwcEFzHO1AuvQunXOYxx3liNA==", "license": "MIT", "dependencies": { "@noble/curves": "^2.0.1", @@ -2858,7 +2854,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3273,7 +3268,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3964,7 +3958,6 @@ "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5069,9 +5062,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.36", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.36.tgz", - "integrity": "sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==", + "version": "1.12.38", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.38.tgz", + "integrity": "sha512-vwzxmasAy9hZigxtqTbFEwp8ZdZ975TiqVDwj5bKx5sR+zi5ucUQy9mbVTkKM9GzqdLdxux/hTw2nmN5J7POMA==", "license": "MIT" }, "node_modules/locate-path": { @@ -5624,7 +5617,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5822,7 +5814,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6681,7 +6672,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6864,7 +6854,6 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", diff --git a/package.json b/package.json index e3d1549..536d7cf 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@noble/hashes": "^1.7.2", "@scure/base": "^1.2.4", "@tanstack/react-query": "^5.90.0", - "@unicitylabs/sphere-sdk": "^0.5.4", + "@unicitylabs/sphere-sdk": "^0.5.5", "lucide-react": "^0.552.0", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/src/components/wallet/UnlockWallet.tsx b/src/components/wallet/UnlockWallet.tsx index b14fcd1..f9044c9 100644 --- a/src/components/wallet/UnlockWallet.tsx +++ b/src/components/wallet/UnlockWallet.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { Loader2, Lock, AlertCircle } from 'lucide-react'; +import { getErrorMessage } from '@/sdk/errors'; interface UnlockWalletProps { onUnlock: (password: string) => Promise; @@ -20,7 +21,7 @@ export function UnlockWallet({ onUnlock }: UnlockWalletProps) { try { await onUnlock(password); } catch (err) { - setError((err as Error).message || 'Failed to unlock wallet'); + setError(getErrorMessage(err)); } finally { setIsLoading(false); } diff --git a/src/components/wallet/modals/ConnectIntentModal.tsx b/src/components/wallet/modals/ConnectIntentModal.tsx index e08dbcc..80276ac 100644 --- a/src/components/wallet/modals/ConnectIntentModal.tsx +++ b/src/components/wallet/modals/ConnectIntentModal.tsx @@ -15,6 +15,7 @@ import { useEffect, useState, useRef, useCallback } from 'react'; import { MessageSquare, Key, Zap, Loader2 } from 'lucide-react'; import { ERROR_CODES } from '@unicitylabs/sphere-sdk/connect'; +import { getErrorMessage } from '@/sdk/errors'; import { BaseModal } from '@/components/ui/BaseModal'; import { ModalHeader } from '@/components/ui/ModalHeader'; import { Button } from '@/components/ui/Button'; @@ -264,7 +265,7 @@ function L1SendIntentModal({ await resolveIntent(id, { result: { success: true, txHash: result.txHash } }); onClose(); } catch (e) { - setError(e instanceof Error ? e.message : 'Send failed'); + setError(getErrorMessage(e)); } finally { setLoading(false); } @@ -402,7 +403,7 @@ function DmIntentModal({ }); onClose(); } catch (e) { - setError(e instanceof Error ? e.message : 'Failed to send DM'); + setError(getErrorMessage(e)); } finally { setLoading(false); } @@ -488,7 +489,7 @@ function SignMessageIntentModal({ await resolveIntent(id, { result: { signature: signResult.signature } }); onClose(); } catch (e) { - setError(e instanceof Error ? e.message : 'Signing failed'); + setError(getErrorMessage(e)); } finally { setLoading(false); } diff --git a/src/components/wallet/modals/LookupModal.tsx b/src/components/wallet/modals/LookupModal.tsx index 281aacf..cc25040 100644 --- a/src/components/wallet/modals/LookupModal.tsx +++ b/src/components/wallet/modals/LookupModal.tsx @@ -2,6 +2,7 @@ import { useState, useCallback, useEffect } from 'react'; import { Key, Search, Loader2, Copy, Check } from 'lucide-react'; import { BaseModal, ModalHeader } from '@/components/ui'; import { useSphereContext, useIdentity } from '@/sdk'; +import { getErrorMessage } from '@/sdk/errors'; interface ResolvedInfo { nametag?: string; @@ -77,7 +78,7 @@ export function LookupModal({ isOpen, onClose }: LookupModalProps) { setResult(info as ResolvedInfo); } } catch (err) { - setError(err instanceof Error ? err.message : 'Lookup failed'); + setError(getErrorMessage(err)); } finally { setIsLoading(false); } diff --git a/src/components/wallet/modals/PaymentRequestModal.tsx b/src/components/wallet/modals/PaymentRequestModal.tsx index 5c08643..be5684c 100644 --- a/src/components/wallet/modals/PaymentRequestModal.tsx +++ b/src/components/wallet/modals/PaymentRequestModal.tsx @@ -2,6 +2,7 @@ import { Check, Sparkles, Trash2, Loader2, XIcon, ArrowRight, Clock, Receipt, Al import { useTransfer } from '@/sdk'; import { useState } from 'react'; import { BaseModal, ModalHeader, EmptyState } from '@/components/ui'; +import { getErrorMessage } from '@/sdk/errors'; export enum PaymentRequestStatus { PENDING = 'pending', @@ -56,11 +57,7 @@ export function PaymentRequestsModal({ isOpen, onClose, requests, pendingCount, await transfer({ recipient, amount: req.amount.toString(), coinId: req.coinId }); paid(req); } catch (error: unknown) { - let errorMessage = 'Transaction failed'; - if (error instanceof Error) { - errorMessage = error.message.includes('Insufficient') ? 'Insufficient funds' : error.message; - } - setErrors(prev => ({ ...prev, [req.id]: errorMessage })); + setErrors(prev => ({ ...prev, [req.id]: getErrorMessage(error) })); } finally { setProcessingId(null); } diff --git a/src/components/wallet/modals/SendModal.tsx b/src/components/wallet/modals/SendModal.tsx index 7d290a0..030eb8e 100644 --- a/src/components/wallet/modals/SendModal.tsx +++ b/src/components/wallet/modals/SendModal.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useRef, useCallback } from 'react'; import { ArrowRight, Loader2, User, CheckCircle, Coins, Hash, Copy, Check } from 'lucide-react'; import type { Asset } from '@unicitylabs/sphere-sdk'; import { useAssets, useTransfer, useSphereContext, CurrencyUtils } from '@/sdk'; +import { getErrorMessage } from '@/sdk/errors'; import { BaseModal, ModalHeader, Button } from '@/components/ui'; type Step = 'recipient' | 'asset' | 'amount' | 'confirm' | 'processing' | 'success'; @@ -169,7 +170,7 @@ export function SendModal({ isOpen, onClose, prefill }: SendModalProps) { setStep('success'); } catch (e: unknown) { console.error(e); - setRecipientError(e instanceof Error ? e.message : "Transfer failed"); + setRecipientError(getErrorMessage(e)); setStep('confirm'); } }; diff --git a/src/components/wallet/modals/SendPaymentRequestModal.tsx b/src/components/wallet/modals/SendPaymentRequestModal.tsx index 111970f..c9d35a6 100644 --- a/src/components/wallet/modals/SendPaymentRequestModal.tsx +++ b/src/components/wallet/modals/SendPaymentRequestModal.tsx @@ -10,6 +10,7 @@ import { ArrowRight, Loader2, User, CheckCircle, Hash, Receipt } from 'lucide-re import { TokenRegistry, toSmallestUnit } from '@unicitylabs/sphere-sdk'; import { BaseModal, ModalHeader, Button } from '@/components/ui'; import { POPUP_MESSAGES } from '@/shared/messages'; +import { getErrorMessage } from '@/sdk/errors'; type Step = 'recipient' | 'coin' | 'amount' | 'confirm' | 'processing' | 'success'; @@ -154,7 +155,7 @@ export function SendPaymentRequestModal({ isOpen, onClose, prefill }: SendPaymen setRequestId(result.requestId || null); setStep('success'); } catch (e: unknown) { - setError(e instanceof Error ? e.message : 'Failed to send payment request'); + setError(getErrorMessage(e)); setStep('confirm'); } }; diff --git a/src/components/wallet/modals/SwapModal.tsx b/src/components/wallet/modals/SwapModal.tsx index 8c4e0f3..cf2690a 100644 --- a/src/components/wallet/modals/SwapModal.tsx +++ b/src/components/wallet/modals/SwapModal.tsx @@ -3,6 +3,7 @@ import { ArrowDownUp, Loader2, TrendingUp, CheckCircle, ArrowDown } from 'lucide import type { Asset } from '@unicitylabs/sphere-sdk'; import { useIdentity, useAssets, useTransfer } from '@/sdk'; import { CurrencyUtils } from '@/sdk'; +import { getErrorMessage } from '@/sdk/errors'; import { BaseModal, ModalHeader, Button } from '@/components/ui'; type Step = 'swap' | 'processing' | 'success'; @@ -85,7 +86,7 @@ export function SwapModal({ isOpen, onClose }: SwapModalProps) { setStep('success'); } catch (e: unknown) { console.error('Swap failed:', e); - setError(e instanceof Error ? e.message : 'Swap failed'); + setError(getErrorMessage(e)); setStep('swap'); } }; diff --git a/src/components/wallet/modals/TopUpModal.tsx b/src/components/wallet/modals/TopUpModal.tsx index 5fb6913..9b9e4a3 100644 --- a/src/components/wallet/modals/TopUpModal.tsx +++ b/src/components/wallet/modals/TopUpModal.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { Plus, Sparkles, CheckCircle, XCircle } from 'lucide-react'; import { useIdentity } from '@/sdk'; import { BaseModal, ModalHeader, Button } from '@/components/ui'; +import { getErrorMessage } from '@/sdk/errors'; const FAUCET_API_URL = 'https://faucet.unicity.network/api/v1/faucet/request'; @@ -54,7 +55,7 @@ export function TopUpModal({ isOpen, onClose }: TopUpModalProps) { success: false, coin, amount, - message: error instanceof Error ? error.message : 'Unknown error', + message: getErrorMessage(error), })) ) ); @@ -68,7 +69,7 @@ export function TopUpModal({ isOpen, onClose }: TopUpModalProps) { setTimeout(() => setFaucetSuccess(false), 3000); } } catch (error) { - setFaucetError(error instanceof Error ? error.message : 'Failed to request tokens'); + setFaucetError(getErrorMessage(error)); } finally { setIsFaucetLoading(false); } diff --git a/src/components/wallet/onboarding/useOnboardingFlow.ts b/src/components/wallet/onboarding/useOnboardingFlow.ts index 2a78b3f..8c06d9b 100644 --- a/src/components/wallet/onboarding/useOnboardingFlow.ts +++ b/src/components/wallet/onboarding/useOnboardingFlow.ts @@ -9,6 +9,7 @@ */ import { useState, useCallback, useEffect } from "react"; import { useSphereContext } from "@/sdk/context"; +import { getErrorMessage } from "@/sdk/errors"; export type NametagAvailability = "idle" | "checking" | "available" | "taken"; @@ -202,7 +203,7 @@ export function useOnboardingFlow() { setProcessingStatus("Setup complete!"); setIsProcessingComplete(true); } catch (e) { - const message = e instanceof Error ? e.message : "Invalid recovery phrase"; + const message = getErrorMessage(e); setError(message); setStep("restore"); } finally { @@ -243,7 +244,7 @@ export function useOnboardingFlow() { setProcessingStatus("Setup complete!"); setIsProcessingComplete(true); } catch (e) { - const message = e instanceof Error ? e.message : "Failed to create wallet"; + const message = getErrorMessage(e); setError(message); setStep("passwordSetup"); } finally { diff --git a/src/components/wallet/shared/RegisterNametagModal.tsx b/src/components/wallet/shared/RegisterNametagModal.tsx index 90f02db..5432433 100644 --- a/src/components/wallet/shared/RegisterNametagModal.tsx +++ b/src/components/wallet/shared/RegisterNametagModal.tsx @@ -3,6 +3,7 @@ import { X, Loader2, ArrowRight, Tag, CheckCircle2, AlertCircle } from 'lucide-r import { useQueryClient } from '@tanstack/react-query'; import { useSphereContext } from '@/sdk/context'; import { SPHERE_KEYS } from '@/sdk/queryKeys'; +import { getErrorMessage } from '@/sdk/errors'; type NametagAvailability = 'idle' | 'checking' | 'available' | 'taken'; @@ -93,7 +94,7 @@ export function RegisterNametagModal({ isOpen, onClose }: RegisterNametagModalPr setNametagInput(''); }, 1500); } catch (e) { - setError(e instanceof Error ? e.message : 'Registration failed'); + setError(getErrorMessage(e)); } finally { setIsBusy(false); } diff --git a/src/platform/extension/SphereProvider.tsx b/src/platform/extension/SphereProvider.tsx index b7aecdc..334358c 100644 --- a/src/platform/extension/SphereProvider.tsx +++ b/src/platform/extension/SphereProvider.tsx @@ -3,6 +3,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { TokenRegistry, NETWORKS } from '@unicitylabs/sphere-sdk'; import { SphereContext, type SphereContextValue } from '@/sdk/context'; import { SPHERE_KEYS } from '@/sdk/queryKeys'; +import { getErrorMessage } from '@/sdk/errors'; import type { WalletIdentity } from '@/sdk/types'; import type { Asset, Token, TransactionHistoryEntry } from '@unicitylabs/sphere-sdk'; import type { AggregatorConfig, NametagInfo, NametagResolution, PendingTransaction } from '@/shared/types'; @@ -76,7 +77,7 @@ export function ExtensionSphereProvider({ children }: { children: React.ReactNod } catch { /* non-fatal — identity loaded without nametag */ } } } catch (err) { - setError((err as Error).message); + setError(getErrorMessage(err)); } finally { setIsLoading(false); } diff --git a/src/platform/extension/background/index.ts b/src/platform/extension/background/index.ts index ba217a5..d44fea8 100644 --- a/src/platform/extension/background/index.ts +++ b/src/platform/extension/background/index.ts @@ -8,6 +8,7 @@ */ import { handleContentMessage, handlePopupMessage } from './message-handler'; +import { getErrorMessage } from '@/sdk/errors'; import { initConnectHost, destroyConnectHost, isConnectHostActive, openPopupForConnect } from './connect-host'; import { isExtensionConnectEnvelope, EXT_MSG_TO_HOST } from '@unicitylabs/sphere-sdk/connect/browser'; @@ -48,7 +49,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { console.error('Message handler error:', error); sendResponse({ success: false, - error: error.message || 'Unknown error', + error: getErrorMessage(error), }); }); diff --git a/src/platform/extension/background/message-handler.ts b/src/platform/extension/background/message-handler.ts index 6375c3a..0b2783f 100644 --- a/src/platform/extension/background/message-handler.ts +++ b/src/platform/extension/background/message-handler.ts @@ -9,6 +9,7 @@ */ import { walletManager } from './wallet-manager'; +import { getErrorMessage } from '@/sdk/errors'; import { initConnectHost, destroyConnectHost, @@ -154,7 +155,7 @@ export async function handleContentMessage( return { type: `${type}_RESPONSE`, success: false, - error: (error as Error).message || 'Unknown error', + error: getErrorMessage(error), }; } } @@ -449,7 +450,7 @@ export async function handlePopupMessage( console.error('Background popup handler error:', error); return { success: false, - error: (error as Error).message || 'Unknown error', + error: getErrorMessage(error), }; } } @@ -725,7 +726,7 @@ async function handleSignNostrEventRequest( return { type: 'SPHERE_SIGN_NOSTR_EVENT_RESPONSE', success: false, - error: (error as Error).message || 'Failed to sign event', + error: getErrorMessage(error), }; } } @@ -766,7 +767,7 @@ async function handleResolveNametag( return { type: 'SPHERE_RESOLVE_NAMETAG_RESPONSE', success: false, - error: (error as Error).message, + error: getErrorMessage(error), }; } } @@ -807,7 +808,7 @@ async function handleCheckNametagAvailable( return { type: 'SPHERE_CHECK_NAMETAG_AVAILABLE_RESPONSE', success: false, - error: (error as Error).message, + error: getErrorMessage(error), }; } } @@ -825,7 +826,7 @@ async function handlePopupCheckNametagAvailable( } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to check nametag availability', + error: getErrorMessage(error), }; } } @@ -860,7 +861,7 @@ async function handlePopupRegisterNametag( console.error('[NametagHandler] Registration error:', error); return { success: false, - error: (error as Error).message || 'Failed to register nametag', + error: getErrorMessage(error), }; } } @@ -880,7 +881,7 @@ async function handlePopupGetMyNametag(): Promise<{ } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to get nametag', + error: getErrorMessage(error), }; } } @@ -920,7 +921,7 @@ async function handleGetMyNametag( return { type: 'SPHERE_GET_MY_NAMETAG_RESPONSE', success: false, - error: (error as Error).message || 'Failed to get nametag', + error: getErrorMessage(error), }; } } @@ -936,7 +937,7 @@ async function handlePopupGetAggregatorConfig(): Promise<{ } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to get aggregator config', + error: getErrorMessage(error), }; } } @@ -951,7 +952,7 @@ async function handlePopupSetAggregatorConfig(config: { } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to set aggregator config', + error: getErrorMessage(error), }; } } @@ -970,7 +971,7 @@ async function handlePopupSendTokens( } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to send tokens', + error: getErrorMessage(error), }; } } @@ -987,7 +988,7 @@ async function handlePopupResolveNametag( } catch (error) { return { success: false, - error: (error as Error).message || 'Failed to resolve nametag', + error: getErrorMessage(error), }; } } @@ -1043,10 +1044,10 @@ async function handleApproveTransaction(requestId: string): Promise<{ success: b type: 'SPHERE_TRANSACTION_RESULT', requestId, success: false, - error: (error as Error).message, + error: getErrorMessage(error), }); - return { success: false, error: (error as Error).message }; + return { success: false, error: getErrorMessage(error) }; } } diff --git a/src/platform/extension/background/wallet-manager.ts b/src/platform/extension/background/wallet-manager.ts index 17420a4..d853cdc 100644 --- a/src/platform/extension/background/wallet-manager.ts +++ b/src/platform/extension/background/wallet-manager.ts @@ -11,10 +11,14 @@ * - Nametag operations */ -import { Sphere } from '@unicitylabs/sphere-sdk'; +import { Sphere, logger } from '@unicitylabs/sphere-sdk'; import type { Asset, Token, TransactionHistoryEntry } from '@unicitylabs/sphere-sdk'; import { createBrowserProviders } from '@unicitylabs/sphere-sdk/impl/browser'; +// Expose SDK logger on globalThis for runtime debugging in service worker console. +// Usage: logger.configure({ debug: true }) or logger.setTagDebug("Nostr", true) +(globalThis as unknown as Record).logger = logger; + type BrowserProviders = ReturnType; import type { IdentityInfo, diff --git a/src/platform/extension/popup/main.tsx b/src/platform/extension/popup/main.tsx index 228f2fd..5402e67 100644 --- a/src/platform/extension/popup/main.tsx +++ b/src/platform/extension/popup/main.tsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ExtensionSphereProvider } from '../SphereProvider'; import { PopupApp } from './PopupApp'; +import { getErrorMessage } from '@/sdk/errors'; import './styles.css'; const queryClient = new QueryClient({ @@ -11,6 +12,11 @@ const queryClient = new QueryClient({ staleTime: 30_000, retry: 1, }, + mutations: { + onError: (err) => { + console.error('[Mutation error]', getErrorMessage(err)); + }, + }, }, }); diff --git a/src/sdk/errors.ts b/src/sdk/errors.ts new file mode 100644 index 0000000..9b839a9 --- /dev/null +++ b/src/sdk/errors.ts @@ -0,0 +1,32 @@ +import { isSphereError, type SphereErrorCode } from '@unicitylabs/sphere-sdk'; + +const ERROR_MESSAGES: Record = { + INSUFFICIENT_BALANCE: 'Insufficient balance', + INVALID_RECIPIENT: 'Recipient not found', + TRANSPORT_ERROR: 'Connection issue. Check your network', + TIMEOUT: 'Request timed out. Try again', + NOT_INITIALIZED: 'Wallet not initialized', + ALREADY_INITIALIZED: 'Wallet already exists', + INVALID_CONFIG: 'Invalid configuration', + INVALID_IDENTITY: 'Invalid recovery phrase', + TRANSFER_FAILED: 'Transfer failed. Try again', + STORAGE_ERROR: 'Storage error', + AGGREGATOR_ERROR: 'Network unavailable. Try again', + VALIDATION_ERROR: 'Invalid input', + NETWORK_ERROR: 'Network error. Check your connection', + DECRYPTION_ERROR: 'Wrong password', + MODULE_NOT_AVAILABLE: 'Feature not available', +}; + +export function getErrorMessage(err: unknown): string { + if (isSphereError(err)) { + return ERROR_MESSAGES[err.code] ?? err.message; + } + return err instanceof Error ? err.message : 'Something went wrong'; +} + +export function getErrorCode(err: unknown): SphereErrorCode | null { + return isSphereError(err) ? err.code : null; +} + +export { isSphereError }; From a217121284bed98cf50845b08f257a465bdedd9d Mon Sep 17 00:00:00 2001 From: Alexander Khrushkov Date: Sun, 1 Mar 2026 02:53:07 +0400 Subject: [PATCH 2/3] feat: improve error messages and rename Nametag to Unicity ID in logs --- src/components/ui/BaseModal.tsx | 2 +- src/components/wallet/UnlockWallet.tsx | 4 ++-- .../wallet/onboarding/StartScreen.tsx | 8 +++---- .../wallet/onboarding/useOnboardingFlow.ts | 2 +- .../wallet/shared/AddressSelector.tsx | 2 +- .../extension/background/message-handler.ts | 4 ++-- .../extension/background/wallet-manager.ts | 22 +++++++++---------- src/sdk/errors.ts | 19 ++++++---------- 8 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/components/ui/BaseModal.tsx b/src/components/ui/BaseModal.tsx index 1e9129b..175b392 100644 --- a/src/components/ui/BaseModal.tsx +++ b/src/components/ui/BaseModal.tsx @@ -61,7 +61,7 @@ export function BaseModal({
e.stopPropagation()} - className={`relative w-full ${sizeClasses[size]} max-h-[70dvh] sm:max-h-[600px] bg-white dark:bg-[#111] border border-neutral-200 dark:border-white/10 rounded-3xl shadow-2xl pointer-events-auto flex flex-col overflow-hidden opacity-0 scale-95 translate-y-4 transition-all duration-300 ease-out ${className}`} + className={`relative w-full ${sizeClasses[size]} max-h-[70dvh] sm:max-h-150 bg-white dark:bg-[#111] border border-neutral-200 dark:border-white/10 rounded-3xl shadow-2xl pointer-events-auto flex flex-col overflow-hidden opacity-0 scale-95 translate-y-4 transition-all duration-300 ease-out ${className}`} > {/* Background Orbs */} {showOrbs && ( diff --git a/src/components/wallet/UnlockWallet.tsx b/src/components/wallet/UnlockWallet.tsx index f9044c9..bbe20c9 100644 --- a/src/components/wallet/UnlockWallet.tsx +++ b/src/components/wallet/UnlockWallet.tsx @@ -37,7 +37,7 @@ export function UnlockWallet({ onUnlock }: UnlockWalletProps) { {/* Lock Icon */}
-
+
@@ -79,7 +79,7 @@ export function UnlockWallet({ onUnlock }: UnlockWalletProps) {