diff --git a/packages/tx-categorize/src/__snapshots__/txCategorize.test.ts.snap b/packages/tx-categorize/src/__snapshots__/txCategorize.test.ts.snap index 1762c83..2c4d16e 100644 --- a/packages/tx-categorize/src/__snapshots__/txCategorize.test.ts.snap +++ b/packages/tx-categorize/src/__snapshots__/txCategorize.test.ts.snap @@ -403,7 +403,7 @@ exports[`V5 / V6 categorization snapshots (alongside nock HTTP fixtures) matches "transactionType": "COMPOUND_V2_APPROVE", }, "v6": { - "readable": "Approved 0x1111111254eeb25477b68fb85ed929f73a960582 to spend", + "readable": "Approved 0x11111...60582 to spend", "toAddressName": "COMPOUND_C_TOKEN", "transactionCategory": "APPROVE", "transactionProtocol": "COMPOUND", @@ -675,7 +675,7 @@ exports[`V5 / V6 categorization snapshots (alongside nock HTTP fixtures) matches "transactionType": "ERC_20_APPROVE", }, "v6": { - "readable": "Approved 0x881d40237659c251811cec9c364ef91dc08d300c to spend", + "readable": "Approved 0x881d4...d300c to spend", "toAddressName": undefined, "transactionCategory": "APPROVE", "transactionProtocol": "ERC_20", @@ -1695,7 +1695,7 @@ exports[`V5 / V6 categorization snapshots (alongside nock HTTP fixtures) matches "transactionType": "WETH_APPROVE", }, "v6": { - "readable": "Approved 0x7a250d5630b4cf539739df2c5dacb4c659f2488d to spend", + "readable": "Approved 0x7a250...2488d to spend", "toAddressName": "WETH", "transactionCategory": "APPROVE", "transactionProtocol": "WETH", diff --git a/packages/tx-categorize/src/testCases.mock.ts b/packages/tx-categorize/src/testCases.mock.ts index 71d87d7..007f43c 100644 --- a/packages/tx-categorize/src/testCases.mock.ts +++ b/packages/tx-categorize/src/testCases.mock.ts @@ -130,10 +130,10 @@ export const txTestCaseReadableLabels: Record = { 'OPENSEA_V1.5_CANCEL_ORDER': 'Cancelled Order', 'OPENSEA_V1.4_CANCEL_ORDER': 'Cancelled Order', 'OPENSEA_V1.1_TRANSFER': 'Sent 0.012 ETH', - WETH_APPROVE: 'Approved 0x7a250d5630b4cf539739df2c5dacb4c659f2488d to spend', + WETH_APPROVE: 'Approved 0x7a250...2488d to spend', ERC_20_MINT: 'Minted Token', ERC_20_TRANSFER: 'Sent 0.0044 MKR', - ERC_20_APPROVE: 'Approved 0x881d40237659c251811cec9c364ef91dc08d300c to spend', + ERC_20_APPROVE: 'Approved 0x881d4...d300c to spend', POLYGON_BRIDGE_IN: 'Deposited 3K DAI to bridge', POLYGON_BRIDGE_OUT: 'Withdrew 883.8767 USDC', UNISWAP_V2_EXCHANGE: 'Swapped 0.2018 MKR for 112.3412 FST', @@ -189,7 +189,7 @@ export const txTestCaseReadableLabels: Record = { COMPOUND_V2_REPAY: 'Repaid 1.5M DAI', COMPOUND_V2_BORROW: 'Borrowed 100 DAI', COMPOUND_V2_WITHDRAW: 'Withdrew 571.35K DAI', - COMPOUND_V2_APPROVE: 'Approved 0x1111111254eeb25477b68fb85ed929f73a960582 to spend', + COMPOUND_V2_APPROVE: 'Approved 0x11111...60582 to spend', IDEX_DEPOSIT: 'Deposited 0.0034 ETH', IDEX_DEPOSIT_TOKEN: 'Deposited 18K SERC20', IDEX_WITHDRAW: 'Withdrew 364.2416 AURA', diff --git a/packages/tx-categorize/src/txCategorizeV6.ts b/packages/tx-categorize/src/txCategorizeV6.ts index 88c7a1e..bbca4f4 100644 --- a/packages/tx-categorize/src/txCategorizeV6.ts +++ b/packages/tx-categorize/src/txCategorizeV6.ts @@ -4,7 +4,7 @@ import { Language } from './localization' import { fallbackLngV2, tV2 } from './localizationV2' import { contractAddressMap, methodIdMap, topicHashMap } from './txSchemas/heuristicMap' import { DetermineTransactionMetadataInputV6, Transaction, TxMetadataV6, ValueTransfer } from './types' -import { TemplateContext, interpolateTemplate, refineActionForMultiAssets, titlecase } from './utils' +import { TemplateContext, interpolateTemplate, refineActionForMultiAssets, titlecase, truncateAddress } from './utils' import { Action } from './enums' import { DUST_THRESHOLD_WEI } from './constants' @@ -289,12 +289,13 @@ export const determineTransactionMetadataV6 = ( templateKey = receivedAssets.length > 0 && sentAssets.length === 0 ? 'TRANSFER_RECEIVED' : 'TRANSFER_SENT' } const template = tV2(templateKey, {}, language ?? fallbackLngV2) + const rawSpender = extractSpender(transaction) ?? '' const ctx: TemplateContext = txMetadata.transactionCategory === Action.APPROVE ? { sentAssets, receivedAssets, - spender: extractSpender(transaction) ?? '', + spender: truncateAddress(rawSpender), approvedAssets: extractApprovedAssets(transaction), } : { diff --git a/packages/tx-categorize/src/utils.test.ts b/packages/tx-categorize/src/utils.test.ts index ae82978..5c38b20 100644 --- a/packages/tx-categorize/src/utils.test.ts +++ b/packages/tx-categorize/src/utils.test.ts @@ -4,6 +4,7 @@ import { formatCompactNumber, interpolateTemplate, refineActionForMultiAssets, + truncateAddress, } from './utils' import { tV2 } from './localizationV2' import { ValueTransfer } from './types' @@ -213,10 +214,10 @@ describe('interpolateTemplate', () => { const result = interpolateTemplate(tV2('APPROVE'), { sentAssets: [], receivedAssets: [], - spender: '0x1234567890abcdef1234567890abcdef12345678', + spender: '0x12345...45678', approvedAssets: [makeValueTransfer({ symbol: 'USDC', amount: '1000000', decimal: 6 })], }) - expect(result).toBe('Approved 0x1234567890abcdef1234567890abcdef12345678 to spend 1 USDC') + expect(result).toBe('Approved 0x12345...45678 to spend 1 USDC') }) it('handles static templates with no variables', () => { @@ -275,3 +276,21 @@ describe('refineActionForMultiAssets', () => { expect(refineActionForMultiAssets(Action.VOTE, [], [])).toBe(Action.VOTE) }) }) + +describe('truncateAddress', () => { + it('truncates a standard Ethereum address', () => { + expect(truncateAddress('0x6B175474E89094C44Da98b954EedeAC495271d0F')).toBe('0x6B175...71d0F') + }) + + it('returns short strings unchanged', () => { + expect(truncateAddress('0x123456')).toBe('0x123456') + }) + + it('returns 10-char string unchanged', () => { + expect(truncateAddress('0x12345678')).toBe('0x12345678') + }) + + it('returns empty string unchanged', () => { + expect(truncateAddress('')).toBe('') + }) +}) diff --git a/packages/tx-categorize/src/utils.ts b/packages/tx-categorize/src/utils.ts index b387d5d..830f122 100644 --- a/packages/tx-categorize/src/utils.ts +++ b/packages/tx-categorize/src/utils.ts @@ -149,3 +149,9 @@ export const refineActionForMultiAssets = ( return action } + +export const truncateAddress = (address: string): string => { + if (address.length <= 40) return address + + return `${address.slice(0, 7)}...${address.slice(-5)}` +}