diff --git a/packages/tron-wallet-snap/CHANGELOG.md b/packages/tron-wallet-snap/CHANGELOG.md index e0ec215c7..98c237181 100644 --- a/packages/tron-wallet-snap/CHANGELOG.md +++ b/packages/tron-wallet-snap/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Show the dapp URL reported in `originMetadata` in the "Request from" row of the sign-transaction and sign-message confirmations, marked "Not verified", and hide the row when there is nothing verifiable or self-reported to show (previously a WalletConnect channel id was displayed verbatim as the requesting site) +- Stop sending unverifiable origins to the security alerts API: a WalletConnect channel id or a self-reported dapp URL can flip a Blockaid verdict, so such requests are now reported as wallet-initiated - **BREAKING** Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) - **BREAKING** Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) - **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) diff --git a/packages/tron-wallet-snap/locales/en.json b/packages/tron-wallet-snap/locales/en.json index 7111ee221..4dd75e357 100644 --- a/packages/tron-wallet-snap/locales/en.json +++ b/packages/tron-wallet-snap/locales/en.json @@ -25,6 +25,12 @@ "confirmation.origin.tooltip": { "message": "This is the site asking for your confirmation." }, + "confirmation.origin.unverified": { + "message": "Not verified" + }, + "confirmation.origin.unverified.tooltip": { + "message": "This site is reported by the app that sent the request. MetaMask can't verify it." + }, "confirmation.from": { "message": "From" }, diff --git a/packages/tron-wallet-snap/locales/es.json b/packages/tron-wallet-snap/locales/es.json index 76c13c5bc..a49940bc2 100644 --- a/packages/tron-wallet-snap/locales/es.json +++ b/packages/tron-wallet-snap/locales/es.json @@ -25,6 +25,12 @@ "confirmation.origin.tooltip": { "message": "Este es el sitio que solicita su confirmación." }, + "confirmation.origin.unverified": { + "message": "Not verified" + }, + "confirmation.origin.unverified.tooltip": { + "message": "This site is reported by the app that sent the request. MetaMask can't verify it." + }, "confirmation.from": { "message": "De" }, diff --git a/packages/tron-wallet-snap/messages.json b/packages/tron-wallet-snap/messages.json index 1f26af063..494de1351 100644 --- a/packages/tron-wallet-snap/messages.json +++ b/packages/tron-wallet-snap/messages.json @@ -23,6 +23,12 @@ "confirmation.origin.tooltip": { "message": "This is the site asking for your confirmation." }, + "confirmation.origin.unverified": { + "message": "Not verified" + }, + "confirmation.origin.unverified.tooltip": { + "message": "This site is reported by the app that sent the request. MetaMask can't verify it." + }, "confirmation.from": { "message": "From" }, diff --git a/packages/tron-wallet-snap/src/handlers/cronjob/cronjob.test.tsx b/packages/tron-wallet-snap/src/handlers/cronjob/cronjob.test.tsx index c6c02294a..a4d58d1c2 100644 --- a/packages/tron-wallet-snap/src/handlers/cronjob/cronjob.test.tsx +++ b/packages/tron-wallet-snap/src/handlers/cronjob/cronjob.test.tsx @@ -197,6 +197,7 @@ function buildMockSignTransactionInterfaceContext( overrides: Partial = {}, ): ConfirmSignTransactionContext { return { + originMetadata: null, scope: Network.Mainnet, account: { id: 'account-1', diff --git a/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.test.ts b/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.test.ts index a6db9cfaf..51b3193b3 100644 --- a/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.test.ts +++ b/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.test.ts @@ -462,7 +462,7 @@ describe('ConfirmationHandler', () => { }); }); - it('passes formatted origin and transactionRawData to render', async () => { + it('passes the raw origin and transactionRawData to render', async () => { await withConfirmationHandler( async ({ handler, mockSnapClient, mockState }) => { mockRenderConfirmTransactionRequest.mockResolvedValue(true); @@ -476,7 +476,9 @@ describe('ConfirmationHandler', () => { mockSnapClient, mockState, expect.objectContaining({ - origin: 'example.com', + // The raw origin is kept: the view resolves what to display and + // the security scan only accepts a verifiable URL. + origin: 'https://example.com', transactionRawData: mockTransactionRawData, }), ); diff --git a/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.ts b/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.ts index 83f3bbdb5..84913c164 100644 --- a/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.ts +++ b/packages/tron-wallet-snap/src/services/confirmation/ConfirmationHandler.ts @@ -21,7 +21,6 @@ import { CONFIRM_SIGN_TRANSACTION_INTERFACE_NAME } from '../../ui/confirmation/v import type { ConfirmSignTransactionContext } from '../../ui/confirmation/views/ConfirmSignTransaction/types'; import { render as renderConfirmTransactionRequest } from '../../ui/confirmation/views/ConfirmTransactionRequest/render'; import { CONFIRM_TRANSACTION_INTERFACE_NAME } from '../../ui/confirmation/views/ConfirmTransactionRequest/types'; -import { formatOrigin } from '../../utils/formatOrigin'; import { SignTransactionRequestStruct } from '../../validation/structs'; import type { TronWalletKeyringRequest } from '../../validation/structs'; import { assertTransactionStructure } from '../../validation/transaction'; @@ -185,7 +184,7 @@ export class ConfirmationHandler { amount, fees, asset, - origin: formatOrigin(origin), + origin, accountType, transactionRawData, }, @@ -273,6 +272,7 @@ export class ConfirmationHandler { account, transaction: { rawDataHex: '', type: '' }, origin: 'MetaMask', + originMetadata: null, preferences, networkImage: TRX_IMAGE_SVG, scan: null, diff --git a/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.test.ts b/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.test.ts index df9df01fd..e4d233cf0 100644 --- a/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.test.ts +++ b/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.test.ts @@ -45,6 +45,48 @@ describe('TransactionScanService', () => { timestamp: Date.now(), }); + describe('origin forwarding', () => { + const scanWithOrigin = async ( + origin: string, + ): Promise['scanTransaction']> => { + const mockSecurityAlertsApiClient = createMockSecurityAlertsApiClient({ + simulation: { status: 'Success', account_summary: {} }, + validation: { status: 'Success', result_type: 'Benign' }, + } as SecurityAlertSimulationValidationResponse); + + const service = new TransactionScanService( + mockSecurityAlertsApiClient as unknown as SecurityAlertsApiClient, + createMockSnapClient() as unknown as SnapClient, + mockLogger, + ); + + await service.scanTransaction({ + accountAddress: 'TExvJsxzPyAZ2NtkrWgNKnbLkpqnFJ73DT', + transactionRawData: createWellFormedTransactionRawData(), + origin, + scope: Network.Mainnet, + options: ['simulation'], + }); + + return mockSecurityAlertsApiClient.scanTransaction; + }; + + it.each([ + ['a WalletConnect channel id', '4f3a1b2c-0000-4000-8000-000000000000'], + ['the MetaMask origin', 'MetaMask'], + ])('does not forward %s to the scan', async (_, origin) => { + expect(await scanWithOrigin(origin)).toHaveBeenCalledWith( + expect.objectContaining({ origin: 'https://metamask.io' }), + ); + }); + + it('forwards a verifiable origin to the scan', async () => { + expect(await scanWithOrigin('https://tronscan.org')).toHaveBeenCalledWith( + expect.objectContaining({ origin: 'https://tronscan.org' }), + ); + }); + }); + describe('estimated changes decimal precision', () => { it('computes display value from raw_value and decimals', async () => { const mockApiResponse: SecurityAlertSimulationValidationResponse = { diff --git a/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.ts b/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.ts index 86bee3d43..96a1f7ff6 100644 --- a/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.ts +++ b/packages/tron-wallet-snap/src/services/transaction-scan/TransactionScanService.ts @@ -1,4 +1,5 @@ import type { Logger } from '@metamask/snap-networks-utils'; +import { resolveOrigin } from '@metamask/snap-networks-utils'; import { BigNumber } from 'bignumber.js'; import type { Types as TronwebTypes } from 'tronweb'; @@ -20,7 +21,6 @@ import type { } from './types'; import { ScanStatus, SecurityAlertResponse, SimulationStatus } from './types'; -const METAMASK_ORIGIN = 'metamask'; const METAMASK_ORIGIN_URL = 'https://metamask.io'; export class TransactionScanService { @@ -103,7 +103,12 @@ export class TransactionScanService { const result = await this.#securityAlertsApiClient.scanTransaction({ accountAddress, transactionRawData, - origin: origin === METAMASK_ORIGIN ? METAMASK_ORIGIN_URL : origin, + // Only a verifiable origin may reach the scan: the URL is a core + // heuristic and can flip a verdict, so an unverifiable one (a + // WalletConnect channel id, or a URL self-reported by the requester) + // would let a dapp influence the check meant to catch it. Those are + // reported as wallet-initiated instead. + origin: resolveOrigin(origin).verifiedOrigin ?? METAMASK_ORIGIN_URL, options, }); diff --git a/packages/tron-wallet-snap/src/ui/confirmation/components/OriginRow.tsx b/packages/tron-wallet-snap/src/ui/confirmation/components/OriginRow.tsx new file mode 100644 index 000000000..4841cabba --- /dev/null +++ b/packages/tron-wallet-snap/src/ui/confirmation/components/OriginRow.tsx @@ -0,0 +1,65 @@ +import type { ComponentOrElement } from '@metamask/snaps-sdk'; +import { Box, Icon, Text as SnapText, Tooltip } from '@metamask/snaps-sdk/jsx'; + +import type { Locale } from '../../../utils/i18n'; +import { i18n } from '../../../utils/i18n'; + +export type OriginRowProps = { + /** Hostname to display. The row is not rendered when `null`. */ + displayOrigin: string | null; + /** Whether the hostname was reported by the requester and can't be verified. */ + isSelfReported: boolean; + locale: Locale; +}; + +/** + * The "Request from" row of a confirmation. + * + * A self-reported origin is displayed with an explicit "not verified" marker: + * it comes from the requesting app over a transport that cannot prove it, so + * showing it bare would imply a verification we never made. + * + * @param props - The component props. + * @param props.displayOrigin - Hostname to display, or `null` to render nothing. + * @param props.isSelfReported - Whether the hostname is unverifiable. + * @param props.locale - The locale used for the labels. + * @returns The origin row, or `null` when there is nothing to display. + */ +export const OriginRow = ({ + displayOrigin, + isSelfReported, + locale, +}: OriginRowProps): ComponentOrElement | null => { + if (!displayOrigin) { + return null; + } + + const translate = i18n(locale); + + return ( + + + + {translate('confirmation.origin')} + + + + + + + {displayOrigin} + {isSelfReported ? ( + + {translate('confirmation.origin.unverified')} + + ) : null} + + + ); +}; diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/ConfirmSignMessage.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/ConfirmSignMessage.tsx index a3dd0dcf0..a98656ca7 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/ConfirmSignMessage.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/ConfirmSignMessage.tsx @@ -1,3 +1,5 @@ +import type { SelfReportedOriginMetadata } from '@metamask/snap-networks-utils'; +import { resolveOrigin } from '@metamask/snap-networks-utils'; import type { ComponentOrElement } from '@metamask/snaps-sdk'; import { Address, @@ -6,11 +8,9 @@ import { Container, Footer, Heading, - Icon, Image, Section, Text as SnapText, - Tooltip, } from '@metamask/snaps-sdk/jsx'; import { Networks } from '../../../../constants'; @@ -19,6 +19,7 @@ import type { TronKeyringAccount } from '../../../../entities/keyring-account'; import { TRX_IMAGE_SVG } from '../../../../static/tron-logo'; import type { Locale } from '../../../../utils/i18n'; import { i18n } from '../../../../utils/i18n'; +import { OriginRow } from '../../components/OriginRow'; import { ConfirmSignMessageFormNames } from './events'; export type ConfirmSignMessageProps = { @@ -28,6 +29,7 @@ export type ConfirmSignMessageProps = { locale: Locale; networkImage: string | null; origin: string; + originMetadata: SelfReportedOriginMetadata | null; }; export const ConfirmSignMessage = ({ @@ -37,9 +39,14 @@ export const ConfirmSignMessage = ({ locale, networkImage, origin, + originMetadata, }: ConfirmSignMessageProps): ComponentOrElement => { const translate = i18n(locale); const { address } = account; + const { displayOrigin, isSelfReported } = resolveOrigin( + origin, + originMetadata, + ); const addressCaip10 = `${scope}:${address}` as | `0x${string}` | `${string}:${string}:${string}`; @@ -63,19 +70,11 @@ export const ConfirmSignMessage = ({
- {origin ? ( - - - - {translate('confirmation.origin')} - - - - - - {origin} - - ) : null} + {translate('confirmation.account')} diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/render.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/render.tsx index 62bcc9791..906ce6950 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/render.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignMessage/render.tsx @@ -6,7 +6,6 @@ import type { Network } from '../../../../constants'; import snapContext from '../../../../context'; import type { TronKeyringAccount } from '../../../../entities/keyring-account'; import { TRX_IMAGE_SVG } from '../../../../static/tron-logo'; -import { formatOrigin } from '../../../../utils/formatOrigin'; import { FALLBACK_LANGUAGE } from '../../../../utils/i18n'; import { SignMessageRequestStruct } from '../../../../validation/structs'; import { ConfirmSignMessage } from './ConfirmSignMessage'; @@ -30,6 +29,7 @@ export async function render( }, scope, origin, + originMetadata, } = request; // Decode the base64 message to get the raw message @@ -48,7 +48,8 @@ export async function render( scope={scope as Network} locale={locale} networkImage={TRX_IMAGE_SVG} - origin={formatOrigin(origin)} + origin={origin} + originMetadata={originMetadata ?? null} />, {}, ); diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx index 3dfccc742..6669982fa 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx @@ -1,3 +1,4 @@ +import { resolveOrigin } from '@metamask/snap-networks-utils'; import type { ComponentOrElement } from '@metamask/snaps-sdk'; import { Address, @@ -18,10 +19,10 @@ import { Networks } from '../../../../constants'; import { SimulationStatus } from '../../../../services/transaction-scan/types'; import { TRX_IMAGE_SVG } from '../../../../static/tron-logo'; import { FetchStatus } from '../../../../types/snap'; -import { formatOrigin } from '../../../../utils/formatOrigin'; import { i18n } from '../../../../utils/i18n'; import { EstimatedChanges } from '../../components/EstimatedChanges/EstimatedChanges'; import { Fees } from '../../components/Fees'; +import { OriginRow } from '../../components/OriginRow'; import { TransactionAlert } from '../../components/TransactionAlert/TransactionAlert'; import { ConfirmSignTransactionFormNames } from './events'; import type { ConfirmSignTransactionContext } from './types'; @@ -36,6 +37,7 @@ export const ConfirmSignTransaction = ({ account, scope, origin, + originMetadata, networkImage, preferences, scan, @@ -59,6 +61,11 @@ export const ConfirmSignTransaction = ({ const addressCaip10 = account ? `${scope}:${account.address}` : null; + const { displayOrigin, isSelfReported } = resolveOrigin( + origin, + originMetadata, + ); + let estimatedChangesSection: ComponentOrElement | null = null; if (preferences.simulateOnChainActions) { if (scan?.simulationStatus === SimulationStatus.Skipped) { @@ -133,19 +140,11 @@ export const ConfirmSignTransaction = ({ {/* Transaction Details */}
{/* Request from */} - {origin ? ( - - - - {translate('confirmation.origin')} - - - - - - {formatOrigin(origin)} - - ) : null} + {/* Account */} diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/render.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/render.tsx index c877771e7..bc0b44e15 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/render.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/render.tsx @@ -30,6 +30,7 @@ export const DEFAULT_CONTEXT: ConfirmSignTransactionContext = { type: '', }, origin: '', + originMetadata: null, networkImage: TRX_IMAGE_SVG, scan: null, scanFetchStatus: FetchStatus.Initial, @@ -74,6 +75,7 @@ export async function render( }, scope, origin, + originMetadata, } = request; // Build initial context @@ -82,7 +84,8 @@ export async function render( scope: scope as Network, account, transaction, - origin: origin ?? 'Unknown', + origin, + originMetadata: originMetadata ?? null, scanFetchStatus: FetchStatus.Loading, // Start as Loading (first fetch) not Fetching tokenPricesFetchStatus: FetchStatus.Initial, feesFetchStatus: FetchStatus.Initial, diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/types.ts b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/types.ts index 5bdb85704..20df0efaf 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/types.ts +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/types.ts @@ -1,3 +1,5 @@ +import type { SelfReportedOriginMetadata } from '@metamask/snap-networks-utils'; + import type { SpotPrices } from '../../../../clients/price-api/types'; import type { Network } from '../../../../constants'; import type { TronKeyringAccount } from '../../../../entities/keyring-account'; @@ -15,6 +17,7 @@ export type ConfirmSignTransactionContext = { type: string; }; origin: string; + originMetadata: SelfReportedOriginMetadata | null; preferences: Preferences; networkImage: string; scan: TransactionScanResult | null; diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx index 33dd8af5a..e1edd8e8b 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx @@ -1,3 +1,4 @@ +import { resolveOrigin } from '@metamask/snap-networks-utils'; import type { ComponentOrElement } from '@metamask/snaps-sdk'; import { Address, @@ -22,6 +23,7 @@ import { getExplorerUrl } from '../../../../utils/getExplorerUrl'; import { i18n } from '../../../../utils/i18n'; import { EstimatedChanges } from '../../components/EstimatedChanges/EstimatedChanges'; import { Fees } from '../../components/Fees'; +import { OriginRow } from '../../components/OriginRow'; import { TransactionAlert } from '../../components/TransactionAlert/TransactionAlert'; import { ConfirmSignAndSendTransactionFormNames } from './events'; import type { ConfirmTransactionRequestContext } from './types'; @@ -112,17 +114,11 @@ export const ConfirmTransactionRequest = ({ {/* Additional Details */}
{/* Request from */} - - - - {translate('confirmation.origin')} - - - - - - {origin} - + {null} {/* From */} diff --git a/packages/tron-wallet-snap/src/utils/formatOrigin.test.ts b/packages/tron-wallet-snap/src/utils/formatOrigin.test.ts deleted file mode 100644 index a38bd86e4..000000000 --- a/packages/tron-wallet-snap/src/utils/formatOrigin.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { formatOrigin } from './formatOrigin'; - -describe('formatOrigin', () => { - it('formats "metamask" as "MetaMask"', () => { - expect(formatOrigin('metamask')).toBe('MetaMask'); - }); - - it('formats "METAMASK" as "MetaMask"', () => { - expect(formatOrigin('METAMASK')).toBe('MetaMask'); - }); - - it('formats "MetaMask" as "MetaMask"', () => { - expect(formatOrigin('MetaMask')).toBe('MetaMask'); - }); - - it('formats "MeTaMaSk" (mixed case) as "MetaMask"', () => { - expect(formatOrigin('MeTaMaSk')).toBe('MetaMask'); - }); - - it('extracts hostname from valid URLs', () => { - expect(formatOrigin('https://dapp.example.com')).toBe('dapp.example.com'); - expect(formatOrigin('http://example.com')).toBe('example.com'); - expect(formatOrigin('https://subdomain.example.com:8080')).toBe( - 'subdomain.example.com', - ); - expect(formatOrigin('http://localhost:3000')).toBe('localhost'); - expect(formatOrigin('https://example.com/path/to/page')).toBe( - 'example.com', - ); - }); - - it('returns original value for invalid URLs', () => { - // Note: These should be rejected by validation, but formatOrigin is lenient - expect(formatOrigin('example.com')).toBe('example.com'); - expect(formatOrigin('not-a-url')).toBe('not-a-url'); - expect(formatOrigin('just some text')).toBe('just some text'); - }); - - it('returns "Unknown" for undefined', () => { - expect(formatOrigin(undefined)).toBe('Unknown'); - }); - - it('returns "Unknown" for empty string', () => { - expect(formatOrigin('')).toBe('Unknown'); - }); -}); diff --git a/packages/tron-wallet-snap/src/utils/formatOrigin.ts b/packages/tron-wallet-snap/src/utils/formatOrigin.ts deleted file mode 100644 index 316b05c42..000000000 --- a/packages/tron-wallet-snap/src/utils/formatOrigin.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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'). - */ -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'; - } - - // Try to extract hostname from URL - try { - return new URL(origin).hostname; - } catch { - // If not a valid URL, return the original value - // This shouldn't happen if validation is working correctly - return origin; - } -}