Skip to content
Closed
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
44 changes: 44 additions & 0 deletions packages/snap/src/ui/confirmation/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FetchStatus } from './api';
import {
ConfirmationBanner,
isFetchInProgress,
formatOrigin,
isLocalTransactionValidationFailed,
isRemoteTransactionScanLoading,
requiresMaliciousAcknowledgement,
Expand Down Expand Up @@ -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(
Expand Down
38 changes: 30 additions & 8 deletions packages/snap/src/ui/confirmation/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string> = {
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.
*
Expand Down
Loading