diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8cde5b05021d..541189975532 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1089,6 +1089,9 @@ "bridgeLowestCost": { "message": "Lowest cost" }, + "bridgeMalicious": { + "message": "Malicious" + }, "bridgeMaliciousTokenTitle": { "message": "Malicious token" }, @@ -1264,6 +1267,9 @@ "message": "Swapping $1 for $2", "description": "$1 is the amount of the source asset, $2 is the amount of the destination asset" }, + "bridgeSuspicious": { + "message": "Suspicious" + }, "bridgeSuspiciousTokenTitle": { "message": "Suspicious token" }, diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index 8cde5b05021d..541189975532 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -1089,6 +1089,9 @@ "bridgeLowestCost": { "message": "Lowest cost" }, + "bridgeMalicious": { + "message": "Malicious" + }, "bridgeMaliciousTokenTitle": { "message": "Malicious token" }, @@ -1264,6 +1267,9 @@ "message": "Swapping $1 for $2", "description": "$1 is the amount of the source asset, $2 is the amount of the destination asset" }, + "bridgeSuspicious": { + "message": "Suspicious" + }, "bridgeSuspiciousTokenTitle": { "message": "Suspicious token" }, diff --git a/ui/ducks/bridge/bridge.test.ts b/ui/ducks/bridge/bridge.test.ts index 626f2ec4d444..6c8526c4fccb 100644 --- a/ui/ducks/bridge/bridge.test.ts +++ b/ui/ducks/bridge/bridge.test.ts @@ -87,6 +87,7 @@ describe('Ducks - Bridge', () => { "isVerified": undefined, "name": "SYMBOL", "rwaData": undefined, + "securityData": undefined, "symbol": "SYMBOL", "tokenFiatAmount": undefined, } @@ -210,6 +211,7 @@ describe('Ducks - Bridge', () => { chainId: 'eip155:10', rwaData: undefined, isVerified: undefined, + securityData: undefined, iconUrl: 'https://static.cx.metamask.io/api/v2/tokenIcons/assets/eip155/10/erc20/0x13341431.png', }); diff --git a/ui/ducks/bridge/selectors.test.ts b/ui/ducks/bridge/selectors.test.ts index 28c38cfcd5f2..a54f0688225e 100644 --- a/ui/ducks/bridge/selectors.test.ts +++ b/ui/ducks/bridge/selectors.test.ts @@ -612,6 +612,7 @@ describe('Bridge selectors', () => { tokenFiatAmount: undefined, rwaData: undefined, isVerified: undefined, + securityData: undefined, }); }); }); @@ -647,6 +648,7 @@ describe('Bridge selectors', () => { "isVerified": undefined, "name": "DEST", "rwaData": undefined, + "securityData": undefined, "symbol": "DEST", "tokenFiatAmount": undefined, } @@ -680,6 +682,7 @@ describe('Bridge selectors', () => { tokenFiatAmount: undefined, rwaData: undefined, isVerified: undefined, + securityData: undefined, }); }); @@ -748,6 +751,7 @@ describe('Bridge selectors', () => { tokenFiatAmount: undefined, rwaData: undefined, isVerified: undefined, + securityData: undefined, }); }); }); diff --git a/ui/ducks/bridge/utils.test.ts b/ui/ducks/bridge/utils.test.ts index 8e050c5c77e8..4d7b5b55434b 100644 --- a/ui/ducks/bridge/utils.test.ts +++ b/ui/ducks/bridge/utils.test.ts @@ -1,6 +1,7 @@ import { type CaipAssetType } from '@metamask/utils'; import { MultichainNetworks } from '../../../shared/constants/multichain/networks'; import { CHAIN_IDS } from '../../../shared/constants/network'; +import { BridgeAssetSecurityDataType } from '../../pages/bridge/utils/tokens'; import { isSupportedBridgeChain, toBridgeToken } from './utils'; const BASE_PAYLOAD = { @@ -56,6 +57,39 @@ describe('isSupportedBridgeChain', () => { }); describe('toBridgeToken', () => { + describe('securityData', () => { + it('passes securityData from the payload when tokenMetadata has no securityData', () => { + const token = toBridgeToken( + { + ...BASE_PAYLOAD, + securityData: { type: BridgeAssetSecurityDataType.MALICIOUS }, + }, + { balance: '10' }, + ); + expect(token.securityData).toStrictEqual({ + type: BridgeAssetSecurityDataType.MALICIOUS, + }); + }); + + it('tokenMetadata.securityData takes precedence over payload.securityData', () => { + const token = toBridgeToken( + { + ...BASE_PAYLOAD, + securityData: { type: BridgeAssetSecurityDataType.SPAM }, + }, + { securityData: { type: BridgeAssetSecurityDataType.VERIFIED } }, + ); + expect(token.securityData).toStrictEqual({ + type: BridgeAssetSecurityDataType.VERIFIED, + }); + }); + + it('securityData is undefined when absent from both payload and tokenMetadata', () => { + const token = toBridgeToken(BASE_PAYLOAD, { balance: '10' }); + expect(token.securityData).toBeUndefined(); + }); + }); + describe('isVerified', () => { it('includes isVerified: true from the payload', () => { const token = toBridgeToken({ ...BASE_PAYLOAD, isVerified: true }); @@ -87,5 +121,51 @@ describe('toBridgeToken', () => { ); expect(token.isVerified).toBe(true); }); + + describe('securityData', () => { + it('returns true when tokenMetadata.securityData.type is VERIFIED', () => { + const token = toBridgeToken(BASE_PAYLOAD, { + securityData: { type: BridgeAssetSecurityDataType.VERIFIED }, + }); + expect(token.isVerified).toBe(true); + }); + + it('returns true when tokenMetadata.securityData.type is VERIFIED even if tokenMetadata.isVerified is false', () => { + const token = toBridgeToken(BASE_PAYLOAD, { + isVerified: false, + securityData: { type: BridgeAssetSecurityDataType.VERIFIED }, + }); + expect(token.isVerified).toBe(true); + }); + + it('falls back to tokenMetadata.isVerified when securityData.type is not VERIFIED', () => { + const token = toBridgeToken(BASE_PAYLOAD, { + isVerified: true, + securityData: { type: BridgeAssetSecurityDataType.WARNING }, + }); + expect(token.isVerified).toBe(true); + }); + + it('falls back to payload isVerified when securityData.type is not VERIFIED and tokenMetadata.isVerified is absent', () => { + const token = toBridgeToken( + { ...BASE_PAYLOAD, isVerified: true }, + { securityData: { type: BridgeAssetSecurityDataType.BENIGN } }, + ); + expect(token.isVerified).toBe(true); + }); + + it('returns false when securityData.type is not VERIFIED and tokenMetadata.isVerified is false', () => { + const token = toBridgeToken(BASE_PAYLOAD, { + isVerified: false, + securityData: { type: BridgeAssetSecurityDataType.SPAM }, + }); + expect(token.isVerified).toBe(false); + }); + + it('returns undefined when no securityData and no isVerified in either payload or tokenMetadata', () => { + const token = toBridgeToken(BASE_PAYLOAD, { balance: '50' }); + expect(token.isVerified).toBeUndefined(); + }); + }); }); }); diff --git a/ui/ducks/bridge/utils.ts b/ui/ducks/bridge/utils.ts index 752a379d6dc6..560358429dca 100644 --- a/ui/ducks/bridge/utils.ts +++ b/ui/ducks/bridge/utils.ts @@ -21,6 +21,7 @@ import { BRIDGE_CHAINID_COMMON_TOKEN_PAIR, } from '../../../shared/constants/bridge'; import { getAssetImageUrl } from '../../../shared/lib/asset-utils'; +import { BridgeAssetSecurityDataType } from '../../pages/bridge/utils/tokens'; import type { TokenPayload, BridgeToken } from './types'; // Re-export isNonEvmChainId from bridge-controller for backward compatibility @@ -212,6 +213,7 @@ export const toBridgeToken = ( accountType, rwaData, isVerified, + securityData, } = payload; const { chainId } = parseCaipAssetType(assetId); return { @@ -225,7 +227,12 @@ export const toBridgeToken = ( tokenFiatAmount: tokenMetadata?.tokenFiatAmount ?? tokenFiatAmount, accountType: tokenMetadata?.accountType ?? accountType, rwaData: tokenMetadata?.rwaData ?? rwaData, - isVerified: tokenMetadata?.isVerified ?? isVerified, + isVerified: + (tokenMetadata?.securityData?.type === + BridgeAssetSecurityDataType.VERIFIED || + tokenMetadata?.isVerified) ?? + isVerified, + securityData: tokenMetadata?.securityData ?? securityData, }; }; diff --git a/ui/hooks/bridge/__snapshots__/usePrefillFromSearchQuery.test.ts.snap b/ui/hooks/bridge/__snapshots__/usePrefillFromSearchQuery.test.ts.snap index 797684366b60..58063d68a5de 100644 --- a/ui/hooks/bridge/__snapshots__/usePrefillFromSearchQuery.test.ts.snap +++ b/ui/hooks/bridge/__snapshots__/usePrefillFromSearchQuery.test.ts.snap @@ -32,6 +32,7 @@ exports[`usePrefillFromSearchQuery should only set dest token 1`] = ` "isVerified": undefined, "name": "USDC", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -50,6 +51,7 @@ exports[`usePrefillFromSearchQuery should only set src token and amount 1`] = ` "isVerified": undefined, "name": "USDC", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -71,6 +73,7 @@ exports[`usePrefillFromSearchQuery should set evm bridge params 1`] = ` "isVerified": undefined, "name": "USDC", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -86,6 +89,7 @@ exports[`usePrefillFromSearchQuery should set evm bridge params 1`] = ` "isVerified": undefined, "name": "Solana", "rwaData": undefined, + "securityData": undefined, "symbol": "SOL", "tokenFiatAmount": undefined, }, @@ -104,6 +108,7 @@ exports[`usePrefillFromSearchQuery should set solana swap params 1`] = ` "isVerified": undefined, "name": "Solana", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -118,6 +123,7 @@ exports[`usePrefillFromSearchQuery should set solana swap params 1`] = ` "isVerified": undefined, "name": "Solana", "rwaData": undefined, + "securityData": undefined, "symbol": "SOL", "tokenFiatAmount": undefined, }, @@ -136,6 +142,7 @@ exports[`usePrefillFromSearchQuery should set src token after navigating from ER "isVerified": undefined, "name": "USDC", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -156,6 +163,7 @@ exports[`usePrefillFromSearchQuery should set src token after navigating from na "isVerified": undefined, "name": "Ethereum", "rwaData": undefined, + "securityData": undefined, "symbol": "ETH", "tokenFiatAmount": undefined, }, diff --git a/ui/hooks/bridge/usePrefillFromBridgeState.test.ts b/ui/hooks/bridge/usePrefillFromBridgeState.test.ts index 338fbfff4f56..dd429a78858c 100644 --- a/ui/hooks/bridge/usePrefillFromBridgeState.test.ts +++ b/ui/hooks/bridge/usePrefillFromBridgeState.test.ts @@ -91,6 +91,7 @@ describe('usePrefillFromBridgeState', () => { "isVerified": undefined, "name": "USDC", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -154,6 +155,7 @@ describe('usePrefillFromBridgeState', () => { "isVerified": undefined, "name": "USD Coin", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, @@ -168,6 +170,7 @@ describe('usePrefillFromBridgeState', () => { "isVerified": undefined, "name": "Native USD Coin (POS)", "rwaData": undefined, + "securityData": undefined, "symbol": "USDC", "tokenFiatAmount": undefined, }, diff --git a/ui/pages/bridge/prepare/__snapshots__/bridge-input-group.test.tsx.snap b/ui/pages/bridge/prepare/__snapshots__/bridge-input-group.test.tsx.snap index 01b511e42ea2..a9a4731378c1 100644 --- a/ui/pages/bridge/prepare/__snapshots__/bridge-input-group.test.tsx.snap +++ b/ui/pages/bridge/prepare/__snapshots__/bridge-input-group.test.tsx.snap @@ -371,6 +371,7 @@ exports[`BridgeInputGroup should render popular tokens 2`] = ` "isVerified": undefined, "name": "Ether", "rwaData": undefined, + "securityData": undefined, "symbol": "ETH", "tokenFiatAmount": 25.242128065034784, }, @@ -384,6 +385,7 @@ exports[`BridgeInputGroup should render popular tokens 2`] = ` "isVerified": undefined, "name": "Uniswap", "rwaData": undefined, + "securityData": undefined, "symbol": "UNI", "tokenFiatAmount": 0.0010728914112762384, }, @@ -397,6 +399,7 @@ exports[`BridgeInputGroup should render popular tokens 2`] = ` "isVerified": undefined, "name": "Link", "rwaData": undefined, + "securityData": undefined, "symbol": "LINK", "tokenFiatAmount": 0.0000030290553678041743, }, @@ -1067,6 +1070,7 @@ exports[`BridgeInputGroup should search for tokens 4`] = ` "isVerified": undefined, "name": "Ether", "rwaData": undefined, + "securityData": undefined, "symbol": "ETH", "tokenFiatAmount": 25.242128065034784, }, @@ -1080,6 +1084,7 @@ exports[`BridgeInputGroup should search for tokens 4`] = ` "isVerified": undefined, "name": "Uniswap", "rwaData": undefined, + "securityData": undefined, "symbol": "UNI", "tokenFiatAmount": 0.0010728914112762384, }, @@ -1093,6 +1098,7 @@ exports[`BridgeInputGroup should search for tokens 4`] = ` "isVerified": undefined, "name": "Link", "rwaData": undefined, + "securityData": undefined, "symbol": "LINK", "tokenFiatAmount": 0.0000030290553678041743, }, @@ -1520,6 +1526,7 @@ exports[`BridgeInputGroup should search for tokens with hasNextPage 3`] = ` "isVerified": undefined, "name": "Ether", "rwaData": undefined, + "securityData": undefined, "symbol": "ETH", "tokenFiatAmount": 25.242128065034784, }, @@ -1533,6 +1540,7 @@ exports[`BridgeInputGroup should search for tokens with hasNextPage 3`] = ` "isVerified": undefined, "name": "Uniswap", "rwaData": undefined, + "securityData": undefined, "symbol": "UNI", "tokenFiatAmount": 0.0010728914112762384, }, @@ -1546,6 +1554,7 @@ exports[`BridgeInputGroup should search for tokens with hasNextPage 3`] = ` "isVerified": undefined, "name": "Link", "rwaData": undefined, + "securityData": undefined, "symbol": "LINK", "tokenFiatAmount": 0.0000030290553678041743, }, diff --git a/ui/pages/bridge/prepare/components/bridge-asset-picker/__snapshots__/asset.test.tsx.snap b/ui/pages/bridge/prepare/components/bridge-asset-picker/__snapshots__/asset.test.tsx.snap index 0ce38d768f68..77cc1dccf001 100644 --- a/ui/pages/bridge/prepare/components/bridge-asset-picker/__snapshots__/asset.test.tsx.snap +++ b/ui/pages/bridge/prepare/components/bridge-asset-picker/__snapshots__/asset.test.tsx.snap @@ -396,109 +396,3 @@ exports[`BridgeAsset should render native asset 1`] = ` `; - -exports[`BridgeAsset should render verified badge when isVerified is true 1`] = ` -