diff --git a/packages/wallet/src/components/dappRequests/TransactionAssetList.tsx b/packages/wallet/src/components/dappRequests/TransactionAssetList.tsx index 9b10b625e6d..4f1723ba2d9 100644 --- a/packages/wallet/src/components/dappRequests/TransactionAssetList.tsx +++ b/packages/wallet/src/components/dappRequests/TransactionAssetList.tsx @@ -118,8 +118,17 @@ function formatAmountWithLocale( asset: TransactionAsset, formatNumberOrString: LocalizationContextState['formatNumberOrString'], ): string { + // For NFTs, prefer the Blockaid summary (e.g. "BAYC #8817") or construct + // a label from symbol/name + token ID so users can identify the specific item. if (!asset.amount) { - return asset.symbol ?? asset.name ?? '' + if (asset.summary) { + return asset.summary + } + const label = asset.symbol ?? asset.name ?? '' + if (asset.tokenId) { + return label ? `${label} #${asset.tokenId}` : `#${asset.tokenId}` + } + return label } const formattedAmount = formatNumberOrString({ @@ -127,6 +136,14 @@ function formatAmountWithLocale( type: NumberType.TokenNonTx, }) + // For ERC1155 with quantity and token ID, show both + if (asset.tokenId) { + const label = asset.symbol ?? asset.name ?? '' + return label + ? `${formattedAmount} ${label} #${asset.tokenId}` + : `${formattedAmount} #${asset.tokenId}` + } + return `${formattedAmount} ${asset.symbol ?? ''}` } diff --git a/packages/wallet/src/features/dappRequests/types.ts b/packages/wallet/src/features/dappRequests/types.ts index 5fa70f59040..c7b793f6a0a 100644 --- a/packages/wallet/src/features/dappRequests/types.ts +++ b/packages/wallet/src/features/dappRequests/types.ts @@ -120,6 +120,10 @@ export interface TransactionAsset { logoUrl?: string /** Spender address (for approvals) */ spenderAddress?: string + /** NFT token ID from Blockaid scan (ERC721/ERC1155) */ + tokenId?: string + /** Human-readable summary from Blockaid scan (e.g. "BAYC #8817") */ + summary?: string } /** diff --git a/packages/wallet/src/features/dappRequests/utils/blockaidUtils.test.ts b/packages/wallet/src/features/dappRequests/utils/blockaidUtils.test.ts index 132c4f6097c..f277d07c265 100644 --- a/packages/wallet/src/features/dappRequests/utils/blockaidUtils.test.ts +++ b/packages/wallet/src/features/dappRequests/utils/blockaidUtils.test.ts @@ -235,7 +235,102 @@ describe('blockaidUtils', () => { }) }) - describe('parseReceivingAssets', () => { + + it('should parse ERC721 sending assets with token_id and summary', () => { + const assetsDiffs = [ + { + asset: { + type: 'ERC721', + symbol: 'BAYC', + name: 'Bored Ape Yacht Club', + address: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', + logo_url: 'https://example.com/bayc.png', + chain_id: 1, + }, + out: [ + { + token_id: '8817', + summary: 'BAYC #8817', + usd_price: '50000', + }, + ], + in: [], + }, + ] as any + + const result = parseSendingAssets(assetsDiffs, TEST_CHAIN_ID) + + expect(result).not.toBeNull() + expect(result?.type).toBe(TransactionSectionType.Sending) + expect(result?.assets).toHaveLength(1) + expect(result?.assets[0]).toEqual({ + type: 'ERC721', + symbol: 'BAYC', + name: 'Bored Ape Yacht Club', + amount: undefined, + usdValue: '50000', + logoUrl: 'https://example.com/bayc.png', + address: '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', + chainId: TEST_CHAIN_ID, + tokenId: '8817', + summary: 'BAYC #8817', + }) + }) + + it('should parse ERC1155 sending assets with token_id and value', () => { + const assetsDiffs = [ + { + asset: { + type: 'ERC1155', + symbol: 'ITEMS', + name: 'Game Items', + address: '0x1155contract', + logo_url: 'https://example.com/items.png', + chain_id: 1, + }, + out: [ + { + value: '3', + token_id: '42', + usd_price: '150', + }, + ], + in: [], + }, + ] as any + + const result = parseSendingAssets(assetsDiffs, TEST_CHAIN_ID) + + expect(result?.assets[0]?.tokenId).toBe('42') + expect(result?.assets[0]?.amount).toBe('3') + }) + + it('should not add tokenId for ERC20 assets even if token_id is present in response', () => { + const assetsDiffs = [ + { + asset: { + type: 'ERC20', + symbol: 'USDC', + name: 'USD Coin', + address: '0xusdc', + chain_id: 1, + }, + out: [ + { + value: '100', + token_id: 'unexpected', + }, + ], + in: [], + }, + ] as any + + const result = parseSendingAssets(assetsDiffs, TEST_CHAIN_ID) + + expect(result?.assets[0]?.tokenId).toBeUndefined() + }) + + describe('parseReceivingAssets', () => { it('should return null when no assets are being received', () => { const assetsDiffs = [ { @@ -339,7 +434,66 @@ describe('blockaidUtils', () => { }) }) - describe('parseApprovals', () => { + + it('should parse ERC721 receiving assets with token_id and summary', () => { + const assetsDiffs = [ + { + asset: { + type: 'ERC721', + symbol: 'PUNK', + name: 'CryptoPunks', + address: '0xpunks', + logo_url: 'https://example.com/punk.png', + chain_id: 1, + }, + out: [], + in: [ + { + token_id: '3100', + summary: 'CryptoPunk #3100', + usd_price: '75000', + }, + ], + }, + ] as any + + const result = parseReceivingAssets(assetsDiffs, TEST_CHAIN_ID) + + expect(result).not.toBeNull() + expect(result?.type).toBe(TransactionSectionType.Receiving) + expect(result?.assets[0]?.tokenId).toBe('3100') + expect(result?.assets[0]?.summary).toBe('CryptoPunk #3100') + expect(result?.assets[0]?.amount).toBeUndefined() + }) + + it('should parse ERC1155 receiving assets with token_id and value', () => { + const assetsDiffs = [ + { + asset: { + type: 'ERC1155', + symbol: 'LAND', + name: 'Sandbox Land', + address: '0xland', + chain_id: 1, + }, + out: [], + in: [ + { + value: '5', + token_id: '99', + usd_price: '500', + }, + ], + }, + ] as any + + const result = parseReceivingAssets(assetsDiffs, TEST_CHAIN_ID) + + expect(result?.assets[0]?.tokenId).toBe('99') + expect(result?.assets[0]?.amount).toBe('5') + }) + + describe('parseApprovals', () => { it('should return null when no exposures exist', () => { const exposures = [] as any diff --git a/packages/wallet/src/features/dappRequests/utils/blockaidUtils.ts b/packages/wallet/src/features/dappRequests/utils/blockaidUtils.ts index 017530046e2..7852e961fd4 100644 --- a/packages/wallet/src/features/dappRequests/utils/blockaidUtils.ts +++ b/packages/wallet/src/features/dappRequests/utils/blockaidUtils.ts @@ -239,6 +239,8 @@ export function parseSendingAssets(assetsDiffs: AssetDiffs, chainId: UniverseCha } const asset = assetDiff.asset + const isNft = asset.type === 'ERC721' || asset.type === 'ERC1155' + sendingAssets.push({ type: asset.type, symbol: asset.symbol, @@ -248,6 +250,8 @@ export function parseSendingAssets(assetsDiffs: AssetDiffs, chainId: UniverseCha logoUrl: asset.logo_url, address: getAssetAddress(asset), chainId, + ...(isNft && outAmount.token_id ? { tokenId: String(outAmount.token_id) } : {}), + ...(isNft && outAmount.summary ? { summary: outAmount.summary } : {}), }) } }) @@ -276,6 +280,8 @@ export function parseReceivingAssets(assetsDiffs: AssetDiffs, chainId: UniverseC } const asset = assetDiff.asset + const isNft = asset.type === 'ERC721' || asset.type === 'ERC1155' + receivingAssets.push({ type: asset.type, symbol: asset.symbol, @@ -285,6 +291,8 @@ export function parseReceivingAssets(assetsDiffs: AssetDiffs, chainId: UniverseC logoUrl: asset.logo_url, address: getAssetAddress(asset), chainId, + ...(isNft && inAmount.token_id ? { tokenId: String(inAmount.token_id) } : {}), + ...(isNft && inAmount.summary ? { summary: inAmount.summary } : {}), }) } })