diff --git a/packages/tx-categorize/src/testCases.mock.ts b/packages/tx-categorize/src/testCases.mock.ts index e676baa..f3883d2 100644 --- a/packages/tx-categorize/src/testCases.mock.ts +++ b/packages/tx-categorize/src/testCases.mock.ts @@ -109,6 +109,10 @@ export const txTestCases = { BALANCER_STAKE: '0xf72595e9c0bfa407b608b11747954896834df998398ef1b27e3204ffbbae5a5b', } +export const arbitrumTxTestCases = { + METAMASK_V1_EXCHANGE: '0x51f3fec98c9e0794aa6b73402a6f1e030aea8f746ccc6559b47b8102b2051596', +} + /** * V6 readable labels for Ethereum mainnet test cases (from sender perspective). * Maps category key → expected human-readable label produced by determineTransactionMetadataV6. diff --git a/packages/tx-categorize/src/txCategorize.test.ts b/packages/tx-categorize/src/txCategorize.test.ts index 3430566..46794ca 100644 --- a/packages/tx-categorize/src/txCategorize.test.ts +++ b/packages/tx-categorize/src/txCategorize.test.ts @@ -6,6 +6,7 @@ import nock from 'nock' import { ChainId } from './networks' import { + arbitrumTxTestCases, lineaTxTestCaseReadableLabels, lineaTxTestCases, txTestCaseReadableLabels, @@ -266,6 +267,21 @@ describe('#txCategorizeV6', () => { expect(`${categorizedTxV5['transactionType']}-${data.hash}`).toBe(`${txCategory}-${data.hash}`) } }) + it('categorizes a arbitrum tx correctly', async () => { + for (const [txCategory, txHash] of Object.entries(arbitrumTxTestCases)) { + const { data, chainId } = await getTxWithLogsFromPrimitives(txHash, ChainId.ARBITRUM) + const categorizedTxV6 = determineTransactionMetadataV6( + { + transaction: data, + subjectAddress: createAccountId(data.from, chainId), + }, + Language.en, + false, + 49, + ) + expect(`${categorizedTxV6['transactionType']}-${data.hash}`).toBe(`${txCategory}-${data.hash}`) + } + }) it('produces correct readable labels for ethereum txs', async () => { for (const [txCategory, txHash] of Object.entries(txTestCases)) { const expectedReadable = txTestCaseReadableLabels[txCategory] diff --git a/packages/tx-categorize/src/txSchemas/heuristicMap.ts b/packages/tx-categorize/src/txSchemas/heuristicMap.ts index 8709a33..d42b37a 100644 --- a/packages/tx-categorize/src/txSchemas/heuristicMap.ts +++ b/packages/tx-categorize/src/txSchemas/heuristicMap.ts @@ -555,7 +555,12 @@ const determinants: DeterminantMap = { { hash: '0xf341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567', name: WITHDRAW }, { hash: '0x1849bd6a030a1bca28b83437fd3de96f3d27a5d172fa7e9c78e7b61468928a39', name: EXCHANGE, priority: 1 }, { hash: '0x7f4091b46c33e918a0f3aa42307641d17bb67029427a5369e54b353984238705', name: EXCHANGE, priority: 1 }, - { hash: '0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d', name: EXCHANGE, priority: 1 }, + { + hash: '0xbeee1e6e7fe307ddcf84b0a16137a4430ad5e2480fc4f4a8e250ab56ccd7630d', + name: EXCHANGE, + priority: 1, + protocol: 'METAMASK_V1', + }, { hash: '0x5152abf959f6564662358c2e52b702259b78bac5ee7842a0f01937e670efcc7d', name: CANCEL_ORDER }, { hash: '0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b', name: REGISTER }, { hash: '0x9cc2048b8af5eadff75759a3169b369efc538fb79c760fd396a4b355410b41b7', name: EXCHANGE, priority: 5 }, // aggregator exchange diff --git a/packages/tx-categorize/test-fixtures/nock/42161-0x51f3fec98c9e0794aa6b73402a6f1e030aea8f746ccc6559b47b8102b2051596.json b/packages/tx-categorize/test-fixtures/nock/42161-0x51f3fec98c9e0794aa6b73402a6f1e030aea8f746ccc6559b47b8102b2051596.json new file mode 100644 index 0000000..28a8825 --- /dev/null +++ b/packages/tx-categorize/test-fixtures/nock/42161-0x51f3fec98c9e0794aa6b73402a6f1e030aea8f746ccc6559b47b8102b2051596.json @@ -0,0 +1,61 @@ +[ + { + "scope": "https://primitives.api.cx.metamask.io:443", + "method": "GET", + "path": "/v1/networks/42161/transactions/0x51f3fec98c9e0794aa6b73402a6f1e030aea8f746ccc6559b47b8102b2051596?includeLogs=true&includeValueTransfers=true", + "body": "", + "status": 200, + "response": "0b8b0a0064d32cad5a0dea9506040892d8ce210622509fef2fd9d9e7ffde397f8a2cb136fe30a0d2b9ef6b4cccc24e40d3267dea7c4e24f0309b1ba4e36b80a58105604556f601d331c5308fa787eb542044bf767f2afe85cb5fefcff30dec807f4959bcd275b82676ad7336eb5c91665b259171e61cabd7e6ee4d44ad761b05c908a58836d8c06fe6edd307017ba5d2ca067eb83e5d602f1bc4bc4cd8ff859b79be9951c4f3be801d08a9bd447959e8eb22bbe88efc0a117f8485b1b48783df7f7a7db43cc15e1ba1ea20fd4e14913ac940cf968d250a539fe925b5308d826db5390b8946192658a45ae7b05e35060d59dd4654d8e0f53c4f4263ed28f8cd39037652edbd74aa3e3fdd7a3a608b52111136c868e0f76e617c3a3c79c23e36f0ebe3f561deed6474b3c7bcdc1ce283f0a582ce1417860dde61d6147640d8e072c8fbaab52ea1ca9e39b5796689892b6ca8776981a9aa95b552850dd6e9f0f83b86dbf33ba7d3e104fb9a0fe75c85fdbf8c4cb3f27486fda77f879e98417355adb5c798d8932d542c30a42d16aa4588974d2b0e4c1213519331b80fe2b602bd7b4561555d634cc7149fde3b6cf022af35c30e95bb76698c433ac306917efb381f602f6383072db0871ddef9fa7dd8e0693e26ecf0cee5264fb0c165edda5ffffd9cb0c3f3f892e703fcbf79825a97c07e6670d290bacc428d73b5b64a8fda69d4ec6ba89a0a2a330265827ca0c8b41b0d4570524d5eb03c4579a8a26de087a7cb69fae58d88539ecf519adcb9c6086c92bdfb70276265ea2e19c6b332511b29830b7031e19bafde7e2b00397cf3d5db2fde3adc3e6532459e9c908c5fab7d79384b6d9cdc3b3a2d53a55e9a0776b4c652d1b4f69272190954a58b2ad2f73b1ae4e5bbd37c7ece78519aaec20166342a80a2ba8c572d44de0354406b12e272e87ca626a755f48846a6366a8c903a1baaad2a315bae350bb6a288195d9377540fd47ed9e0e1f07a9f95c5010c955f973d0aeaa54a364dcfb77e86fd27c0bf868b6549494bef2116a5ae5e2af52839eae208e4521d5737222d294688d38777675312d800ff9a15753c7173205a23576b020e2dea6a3ec6901069a8aa935bc5ca254736f8658359a9efe9c3e1f5074f917fc18eff6ff1e69c472c129a512cc9875a5327c33696731f31a70ad9ecab34f63a4b693496c8acb184d8381730524bdc785c3e195e6611650e09a0f617379dc348bb336ea30eda767625848769a640edfe11c6a411d7112a877d5d1a9663ad44297d32824fd44cb1c5e592448cd39673512a239a2f44335aa964a57af450eb0b2fd957134738d316ce4151ab9dba9234a3493c4b6be2a3a08f1099a5cbd08a99a8dc7bd435825ad53e597a0f1f2c0b5b672861b3db33ef73d571d4c99a8473f6bde4db75089e2ba274604ef3ca5f369861003116004074389516ca6b9d98e004e3e601f7a6d21c415ce8053bc45c0f533370d9febc00301cb1108a99deab45c9ce12a92a9463387a695b57f3e2186599774fb8d070ff3c7f29554208098e35c9a3b0e63e8db7d3803d8fc302105497e946e8ab15dbbd6424d0f531e3d8f2738cfffb7f", + "rawHeaders": [ + "Date", + "Tue, 12 May 2026 17:55:22 GMT", + "Content-Type", + "application/json; charset=utf-8", + "Transfer-Encoding", + "chunked", + "Connection", + "keep-alive", + "content-security-policy", + "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests", + "cross-origin-opener-policy", + "same-origin", + "cross-origin-resource-policy", + "same-origin", + "origin-agent-cluster", + "?1", + "referrer-policy", + "no-referrer", + "strict-transport-security", + "max-age=15552000; includeSubDomains", + "x-content-type-options", + "nosniff", + "x-dns-prefetch-control", + "off", + "x-download-options", + "noopen", + "x-frame-options", + "SAMEORIGIN", + "x-permitted-cross-domain-policies", + "none", + "x-xss-protection", + "0", + "vary", + "Origin", + "Cache-Control", + "max-age=15", + "x-cache", + "MISS", + "cf-cache-status", + "DYNAMIC", + "set-cookie", + "__cf_bm=4qbTk81Eb9HqlTgOd1.uPwTBFRinpegM8oY_oMTTbOc-1778608522.1173654-1.0.1.1-G.xfPJfharOFgkiPPKuE1TspQ7kmqD1YYqi9_..NRgB.TtVRrezLvzknE.us54GTwuxko55A7JsdmdhwNsCZX_77tPqVuCRp390LxvvnuS3FXX0Un_YR0GUMJSVqT48a; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.cx.metamask.io; Expires=Tue, 12 May 2026 18:25:22 GMT", + "Content-Encoding", + "br", + "Server", + "cloudflare", + "CF-RAY", + "9fab4b3f3933ba4b-SEA" + ], + "responseIsBinary": true + } +] \ No newline at end of file