diff --git a/packages/snap/src/ui/confirmation/utils.test.ts b/packages/snap/src/ui/confirmation/utils.test.ts index ed0e0e9..91d0fe1 100644 --- a/packages/snap/src/ui/confirmation/utils.test.ts +++ b/packages/snap/src/ui/confirmation/utils.test.ts @@ -6,6 +6,7 @@ import { FetchStatus } from './api'; import { ConfirmationBanner, isFetchInProgress, + formatOrigin, isLocalTransactionValidationFailed, isRemoteTransactionScanLoading, requiresMaliciousAcknowledgement, @@ -40,6 +41,49 @@ describe('confirmation utils', () => { ); }); + describe('formatOrigin', () => { + it('returns "Unknown" for an undefined origin', () => { + expect(formatOrigin(undefined)).toBe('Unknown'); + }); + + it('returns "Unknown" for an empty origin', () => { + expect(formatOrigin('')).toBe('Unknown'); + }); + + it('returns "MetaMask" for the internal metamask origin', () => { + expect(formatOrigin('metamask')).toBe('MetaMask'); + }); + + it('returns "WalletConnect" for the wallet-connect origin', () => { + expect(formatOrigin('wallet-connect')).toBe('WalletConnect'); + }); + + it('matches known origins case-insensitively', () => { + expect(formatOrigin('MetaMask')).toBe('MetaMask'); + expect(formatOrigin('Wallet-Connect')).toBe('WalletConnect'); + }); + + it('returns the hostname for an http(s) URL', () => { + expect(formatOrigin('https://example.com')).toBe('example.com'); + expect(formatOrigin('https://app.example.com/path?q=1')).toBe( + 'app.example.com', + ); + expect(formatOrigin('http://example.com')).toBe('example.com'); + }); + + it('returns an empty string for a non-URL string (e.g. a WalletConnect channelId)', () => { + expect(formatOrigin('1234abcd-channel-id')).toBe(''); + }); + + it('returns an empty string for a non-http URL', () => { + expect(formatOrigin('ftp://example.com')).toBe(''); + }); + + it('returns an empty string for an invalid value', () => { + expect(formatOrigin('not a url')).toBe(''); + }); + }); + describe('isRemoteTransactionScanLoading', () => { it('disables confirm while scan is fetching', () => { expect( diff --git a/packages/snap/src/ui/confirmation/utils.ts b/packages/snap/src/ui/confirmation/utils.ts index a29bea0..66d12ff 100644 --- a/packages/snap/src/ui/confirmation/utils.ts +++ b/packages/snap/src/ui/confirmation/utils.ts @@ -36,32 +36,54 @@ export function getNetworkName(scope: KnownCaip2ChainId): string { return NetworkName[scope] ?? 'Unknown'; } +/** + * Display labels for known, non-URL origins, keyed by their lowercased raw value. + * Lets us show a friendly name for the internal MetaMask origin and WalletConnect + * channels instead of an empty/hidden origin row. + */ +const KNOWN_ORIGIN_LABELS: Record = { + metamask: 'MetaMask', + 'wallet-connect': 'WalletConnect', +}; + /** * Formats an origin for display purposes. * * @param origin - The origin string to format (e.g., 'metamask', 'https://example.com'). - * @returns The formatted origin string (e.g., 'MetaMask', 'example.com'). + * @returns The formatted origin string. `'Unknown'` for undefined/empty origins, a + * friendly label for known origins (e.g. `'MetaMask'`, `'WalletConnect'`), the + * hostname for http(s) URLs, or an empty string for any other value (e.g. a + * WalletConnect channelId or a non-http URL) so the UI hides the origin row. */ export function formatOrigin(origin: string | undefined): string { if (!origin) { return 'Unknown'; } - // Special case: format 'metamask' as 'MetaMask' (case-insensitive) - if (origin.toLowerCase() === 'metamask') { - return 'MetaMask'; + const knownLabel = KNOWN_ORIGIN_LABELS[origin.toLowerCase()]; + if (knownLabel) { + return knownLabel; } // Try to extract hostname from URL try { - return new URL(origin).hostname; + const url = new URL(origin); + return isHttpOrHttpsUrl(url) ? url.hostname : ''; } catch { - // If not a valid URL, return the original value - // This shouldn't happen if validation is working correctly - return origin; + return ''; } } +/** + * Checks whether a parsed URL uses an HTTP(S) protocol. + * + * @param url - The parsed URL to check. + * @returns Whether the URL uses HTTP or HTTPS. + */ +function isHttpOrHttpsUrl(url: URL): boolean { + return url.protocol === 'http:' || url.protocol === 'https:'; +} + /** * Gets the locale from the preferences. *