/** @module-tag localnet */ import type * as z from 'zod/mini' import { Value as core_Value } from 'ox' import { Db, Schema } from 'tapimo' import { FxOracle } from 'tapimo/apps' import { Addresses } from 'viem/tempo' import * as TestApp from '../../../../test/App.js' import * as Runtime from '../../../../test/runtime.js' import * as Tidx from '../../../internal/Tidx.js' import * as Transfers from './transfers.js' // Deterministic EUR-based rate set so conversion math is exact in assertions. const fixed = FxOracle.from({ name: 'fixed', rates: async () => ({ asOf: '2026-01-01T00:00:00.000Z', base: 'EUR', rates: { AUD: '1.6', USD: '1.0' }, }), }) describe('OpenAPI', () => { test('publishes generator-ready transfer, activity, and valuation contracts', async () => { const app = TestApp.create({ auth: false }) const spec = await (await app.request('/openapi.json')).json() const operations = { addressActivities: spec.paths['/v1/addresses/{address}/activities'].get, addressValuation: spec.paths['/v1/addresses/{address}/valuation'].get, transactionActivities: spec.paths['/v1/transactions/{transactionHash}/activities'].get, transfers: spec.paths['/v1/transfers'].get, } const summarizeError = ( response: (typeof operations)[keyof typeof operations]['responses'][number], ) => { const schema = response.$ref ?? response.content?.['application/json']?.schema if (!schema || typeof schema === 'string' || !('$ref' in schema)) return schema const name = schema.$ref.split('/').at(-1)! return { codes: spec.components.schemas[name].properties.error.properties.code.enum, schema, } } const summarize = (operation: (typeof operations)[keyof typeof operations]) => ({ errors: Object.fromEntries( [400, 401, 403, 404, 409, 429, 500, 502] .filter((status) => operation.responses[status]) .map((status) => [status, summarizeError(operation.responses[status])]), ), operationId: operation.operationId, response: operation.responses[200].content['application/json'].schema, }) type ActivityVariant = { $ref?: string } const activityVariants = spec.components.schemas.ActivityItem.anyOf as ActivityVariant[] const firstActivityVariant = activityVariants[0]!.$ref!.split('/').at(-1)! expect({ components: [ 'ActivityEntry', 'ActivityEvent', 'ActivityGroup', 'ActivityItem', 'ActivityMeta', 'ActivityToken', 'ActivityTransfer', 'ActivityValuedAmount', 'AddressActivityList', 'AddressValuation', 'TokenTransfer', 'TokenTransferList', 'TokenTransferListMeta', 'TokenTransferToken', 'TransactionActivityList', 'ValuationPricing', 'ValuationValue', 'ValuedTokenAmount', ].filter((name) => spec.components.schemas[name]), descriptions: { activityValuedAmount: spec.components.schemas.ActivityValuedAmount.description, valuedTokenAmount: spec.components.schemas.ValuedTokenAmount.description, }, formats: { activityTransfer: spec.components.schemas.ActivityTransfer.properties.timestamp.format, pricing: spec.components.schemas.ValuationPricing.properties.asOf.format, transfer: spec.components.schemas.TokenTransfer.properties.timestamp.format, }, models: { activityEntry: spec.components.schemas.ActivityEntry.anyOf, activityGroupItems: spec.components.schemas.ActivityGroup.properties.data.properties.items.items, activityItemEvents: spec.components.schemas[firstActivityVariant].properties.events.items, activityItemVariants: { count: activityVariants.length, first: activityVariants[0], last: activityVariants.at(-1), referenced: activityVariants.every((variant) => variant.$ref), stableName: spec.components.schemas.ActivityItemSharesRedemptionRequested.properties.type.const, }, activityList: spec.components.schemas.AddressActivityList.properties.data.items, activityListMeta: spec.components.schemas.AddressActivityList.properties.meta, activityTransferSourceAmount: spec.components.schemas.ActivityTransfer.properties.sourceAmount, activityTransferSourceToken: spec.components.schemas.ActivityTransfer.properties.sourceToken, transactionActivityList: spec.components.schemas.TransactionActivityList.properties.data.items, transferDestinationAmount: spec.components.schemas.TokenTransfer.properties.destinationAmount, transferDestinationToken: spec.components.schemas.TokenTransfer.properties.destinationToken, transferList: spec.components.schemas.TokenTransferList.properties.data.items, transferListMeta: spec.components.schemas.TokenTransferList.properties.meta, transferSourceAmount: spec.components.schemas.TokenTransfer.properties.sourceAmount, transferSourceToken: spec.components.schemas.TokenTransfer.properties.sourceToken, valuationPricing: spec.components.schemas.AddressValuation.properties.pricing, }, operations: Object.fromEntries( Object.entries(operations).map(([name, operation]) => [name, summarize(operation)]), ), }).toMatchInlineSnapshot(` { "components": [ "ActivityEntry", "ActivityEvent", "ActivityGroup", "ActivityItem", "ActivityMeta", "ActivityToken", "ActivityTransfer", "ActivityValuedAmount", "AddressActivityList", "AddressValuation", "TokenTransfer", "TokenTransferList", "TokenTransferListMeta", "TokenTransferToken", "TransactionActivityList", "ValuationPricing", "ValuationValue", "ValuedTokenAmount", ], "descriptions": { "activityValuedAmount": "A token amount carrying its nominal value in the requested denomination.", "valuedTokenAmount": "A token amount carrying its nominal value in the requested denomination.", }, "formats": { "activityTransfer": "date-time", "pricing": "date-time", "transfer": "date-time", }, "models": { "activityEntry": [ { "$ref": "#/components/schemas/ActivityItem", }, { "$ref": "#/components/schemas/ActivityGroup", }, ], "activityGroupItems": { "$ref": "#/components/schemas/ActivityItem", }, "activityItemEvents": { "$ref": "#/components/schemas/ActivityEvent", }, "activityItemVariants": { "count": 65, "first": { "$ref": "#/components/schemas/ActivityItemAssetsDeposited", }, "last": { "$ref": "#/components/schemas/ActivityItemUnknown", }, "referenced": true, "stableName": "shares-redemption-requested", }, "activityList": { "$ref": "#/components/schemas/ActivityEntry", }, "activityListMeta": { "$ref": "#/components/schemas/ActivityMeta", }, "activityTransferSourceAmount": { "$ref": "#/components/schemas/ActivityValuedAmount", }, "activityTransferSourceToken": { "$ref": "#/components/schemas/ActivityToken", }, "transactionActivityList": { "$ref": "#/components/schemas/ActivityItem", }, "transferDestinationAmount": { "$ref": "#/components/schemas/ValuedTokenAmount", }, "transferDestinationToken": { "$ref": "#/components/schemas/TokenTransferToken", }, "transferList": { "$ref": "#/components/schemas/TokenTransfer", }, "transferListMeta": { "$ref": "#/components/schemas/TokenTransferListMeta", }, "transferSourceAmount": { "$ref": "#/components/schemas/ValuedTokenAmount", }, "transferSourceToken": { "$ref": "#/components/schemas/TokenTransferToken", }, "valuationPricing": { "anyOf": [ { "$ref": "#/components/schemas/ValuationPricing", }, { "type": "null", }, ], "description": "Rate provenance, or \`null\` when every valued holding was already denominated in \`currency\`.", }, }, "operations": { "addressActivities": { "errors": { "400": { "codes": [ "address_invalid", "api_key_malformed", "chain_id_invalid", "chain_id_unsupported", "query_invalid", ], "schema": { "$ref": "#/components/schemas/AddressInvalidOrApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidError", }, }, "401": { "codes": [ "api_key_invalid", "api_key_missing", "unauthorized", ], "schema": { "$ref": "#/components/schemas/AuthenticationError", }, }, "403": { "codes": [ "api_key_forbidden", "api_key_ip_forbidden", "forbidden", ], "schema": { "$ref": "#/components/schemas/ForbiddenError", }, }, "429": "#/components/responses/RateLimited", "500": "#/components/responses/InternalError", "502": { "codes": [ "upstream_error", ], "schema": { "$ref": "#/components/schemas/UpstreamError", }, }, }, "operationId": "getAddressActivities", "response": { "$ref": "#/components/schemas/AddressActivityList", }, }, "addressValuation": { "errors": { "400": { "codes": [ "address_invalid", "api_key_malformed", "chain_id_invalid", "chain_id_unsupported", "query_invalid", ], "schema": { "$ref": "#/components/schemas/AddressInvalidOrApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidError", }, }, "401": { "codes": [ "api_key_invalid", "api_key_missing", "unauthorized", ], "schema": { "$ref": "#/components/schemas/AuthenticationError", }, }, "403": { "codes": [ "api_key_forbidden", "api_key_ip_forbidden", "forbidden", ], "schema": { "$ref": "#/components/schemas/ForbiddenError", }, }, "429": "#/components/responses/RateLimited", "500": "#/components/responses/InternalError", "502": { "codes": [ "upstream_error", ], "schema": { "$ref": "#/components/schemas/UpstreamError", }, }, }, "operationId": "getAddressValuation", "response": { "$ref": "#/components/schemas/AddressValuation", }, }, "transactionActivities": { "errors": { "400": { "codes": [ "api_key_malformed", "chain_id_invalid", "chain_id_unsupported", "query_invalid", "transaction_invalid", ], "schema": { "$ref": "#/components/schemas/ApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidOrTransactionInvalidError", }, }, "401": { "codes": [ "api_key_invalid", "api_key_missing", "unauthorized", ], "schema": { "$ref": "#/components/schemas/AuthenticationError", }, }, "403": { "codes": [ "api_key_forbidden", "api_key_ip_forbidden", "forbidden", ], "schema": { "$ref": "#/components/schemas/ForbiddenError", }, }, "404": { "codes": [ "transaction_not_found", ], "schema": { "$ref": "#/components/schemas/TransactionNotFoundError", }, }, "409": { "codes": [ "transaction_ambiguous", ], "schema": { "$ref": "#/components/schemas/TransactionAmbiguousError", }, }, "429": "#/components/responses/RateLimited", "500": "#/components/responses/InternalError", "502": { "codes": [ "upstream_error", ], "schema": { "$ref": "#/components/schemas/UpstreamError", }, }, }, "operationId": "getTransactionActivities", "response": { "$ref": "#/components/schemas/TransactionActivityList", }, }, "transfers": { "errors": { "400": { "codes": [ "api_key_malformed", "chain_id_invalid", "chain_id_unsupported", "query_invalid", ], "schema": { "$ref": "#/components/schemas/ApiKeyMalformedOrChainIdInvalidOrChainIdUnsupportedOrQueryInvalidError", }, }, "401": { "codes": [ "api_key_invalid", "api_key_missing", "unauthorized", ], "schema": { "$ref": "#/components/schemas/AuthenticationError", }, }, "403": { "codes": [ "api_key_forbidden", "api_key_ip_forbidden", "forbidden", ], "schema": { "$ref": "#/components/schemas/ForbiddenError", }, }, "429": "#/components/responses/RateLimited", "500": "#/components/responses/InternalError", "502": { "codes": [ "upstream_error", ], "schema": { "$ref": "#/components/schemas/UpstreamError", }, }, }, "operationId": "getTransfers", "response": { "$ref": "#/components/schemas/TokenTransferList", }, }, }, } `) }) }) describe('GET /transfers', () => { test('parses granular token includes', () => { expect( Transfers.schema.getTransfers.Query.parse({ include: 'token.logoUri,token.verified', }).include, ).toMatchInlineSnapshot(` [ "token.logoUri", "token.verified", ] `) expect(Transfers.schema.getTransfers.Query.safeParse({ include: 'token' }).success).toBe(false) }) test.runIf(Runtime.get().mode === 'testnet')( 'excludes historical non-TIP-20 transfers for affected mainnet accounts', async () => { const client = TestApp.client() const cases = [ { address: Schema.Address.parse('0xad511ba481696d73bcf47d411ad9ea4f77023ddc'), blockNumber: '17681311', expectedCount: 1, }, { address: Schema.Address.parse('0x5eb391a6c707ae1168989bbc6c5f5db4a0e06837'), blockNumber: '31227810', expectedCount: 0, }, ] for (const { address, blockNumber, expectedCount } of cases) { const response = await client.v1.transfers.$get( { query: { address, 'blockNumber.from': blockNumber, 'blockNumber.to': blockNumber, chainId: '4217', include: 'totalCount', limit: '10', page: '1', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data).toHaveLength(expectedCount) expect(body.data.every((entry) => entry.sourceToken.address.startsWith('0x20c0'))).toBe( true, ) expect(body.meta?.totalCount).toBe(expectedCount) } }, ) test.runIf(Runtime.get().mode === 'testnet')( 'returns an empty page for an address with only non-TIP-20 events', async () => { const client = TestApp.client({ tidx: { auth: process.env.TIDX_AUTH, baseUrl: Tidx.url[4217] }, }) const response = await client.v1.transfers.$get( { query: { address: '0x9ef044cb9b80230b60a46b4ed3958672419e668f', chainId: '4217', limit: '10', page: '1', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body).toMatchInlineSnapshot(` { "data": [], "nextCursor": null, } `) }, ) test('degrades unavailable verification data to null valuations', async () => { const db = () => Db.postgres({ connectionString: 'postgresql://postgres:postgres@127.0.0.1:1/none', }) const client = TestApp.client({ db, fx: { oracle: fixed } }) const response = await client.v1.transfers.$get( { query: { limit: '5', token: TestApp.tokenWithHolders, 'valuation.currency': 'USD', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.length).toBeGreaterThan(0) for (const entry of body.data) expect(entry.sourceAmount.valuation).toBeNull() const emptyResponse = await client.v1.transfers.$get( { query: { limit: '5', token: `0x${'11'.repeat(20)}` } }, TestApp.auth, ) const empty = await TestApp.json(emptyResponse, Transfers.schema.getTransfers.Response) expect(emptyResponse.status).toBe(200) expect(empty.data).toMatchInlineSnapshot(`[]`) expect(emptyResponse.headers.get('server-timing')?.includes('verified_tokens;dur=')).not.toBe( true, ) const unsupportedResponse = await client.v1.transfers.$get( { query: { limit: '5', token: TestApp.tokenWithHolders, 'valuation.currency': 'JPY', }, }, TestApp.auth, ) const unsupported = await TestApp.json(unsupportedResponse, Schema.ErrorResponse) expect(unsupportedResponse.status).toBe(400) expect(unsupported.error.code).toBe('query_invalid') }) test('returns an unscoped transfer page with token metadata', async () => { const client = TestApp.client() const baseResponse = await client.v1.transfers.$get({ query: { limit: '5' } }, TestApp.auth) const base = await TestApp.json(baseResponse, Transfers.schema.getTransfers.Response) expect(baseResponse.status).toBe(200) expect(baseResponse.headers.get('server-timing')?.includes('transfers;dur=')).toBe(true) expect(Array.isArray(base.data)).toBe(true) expect(base.data.length).toBeLessThanOrEqual(5) // Pagination fields stay at the response root; cursor pagination has no // `page`, just an opaque `nextCursor`. expect('page' in base).toBe(false) expect(base.nextCursor === null || typeof base.nextCursor === 'string').toBe(true) expect( base.data.every( (entry) => /^0x[0-9a-f]{40}$/.test(entry.sourceToken.address) && /^\d+$/.test(entry.sourceAmount.baseUnits) && typeof entry.sourceAmount.currency === 'string' && Number.isInteger(entry.sourceAmount.decimals) && typeof entry.sourceAmount.formatted === 'string' && typeof entry.sourceToken.currency === 'string' && Number.isInteger(entry.sourceToken.decimals) && typeof entry.sourceToken.name === 'string' && typeof entry.sourceToken.symbol === 'string' && Number.isInteger(entry.blockNumber) && /^0x[0-9a-f]{40}$/.test(entry.sender) && /^0x[0-9a-f]{40}$/.test(entry.recipient) && /^0x[0-9a-f]{64}$/.test(entry.transactionHash) && !Number.isNaN(Date.parse(entry.timestamp)), ), ).toBe(true) // Transfers default to newest-first (descending block number). const blocks = base.data.map((entry) => entry.blockNumber) expect(blocks.every((block, index) => index === 0 || blocks[index - 1]! >= block)).toBe(true) // Quantities live beside the token reference; curated fields remain opt-in. expect( base.data.every( (entry) => !('amount' in entry.sourceToken) && !('valuation' in entry.sourceToken) && entry.sourceToken.logoUri === undefined && entry.sourceToken.verified === undefined, ), ).toBe(true) const includedResponse = await client.v1.transfers.$get( { query: { limit: '5', include: 'token.logoUri,token.verified' } }, TestApp.auth, ) const included = await TestApp.json(includedResponse, Transfers.schema.getTransfers.Response) expect(includedResponse.status).toBe(200) // Curated fields remain optional without moving quantities back into the token. expect( included.data.every( (entry) => /^0x[0-9a-f]{40}$/.test(entry.sourceToken.address) && /^\d+$/.test(entry.sourceAmount.baseUnits) && !('amount' in entry.sourceToken) && !('valuation' in entry.sourceToken) && typeof entry.sourceToken.symbol === 'string' && Number.isInteger(entry.sourceToken.decimals) && typeof entry.sourceToken.verified === 'boolean', ), ).toBe(true) }) test('narrows transfers to a single token', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { limit: '5', token: TestApp.token } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.every((entry) => entry.sourceToken.address === TestApp.token)).toBe(true) }) test('narrows transfers by address (either side)', async () => { const client = TestApp.client() const runtime = Runtime.get() const address = Schema.Address.parse( runtime.mode === 'testnet' ? '0xb1f382bd6deed37895528a6387bae4cd9569ac3c' : runtime.fixtures!.fundedAccounts[0]!, ) const response = await client.v1.transfers.$get( { query: { address, include: 'totalCount', limit: '10' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect( body.data.every((entry) => entry.sender === address || entry.recipient === address), ).toBe(true) expect(body.meta!.totalCount).toBeGreaterThanOrEqual(body.data.length) expect(body.meta!.totalCountCapped).toBe(body.meta!.totalCount! >= Schema.countCap) }) test.runIf(Runtime.get().mode === 'testnet')( 'merges both address sides before paginating', async () => { const client = TestApp.client() const address = '0xabb0023644c54a20d0fe92becfa764dacaf59ba9' const toBlock = '27608730' const [senderResponse, recipientResponse, addressResponse] = await Promise.all([ client.v1.transfers.$get( { query: { 'blockNumber.to': toBlock, limit: '10', order: 'desc', sender: address }, }, TestApp.auth, ), client.v1.transfers.$get( { query: { 'blockNumber.to': toBlock, limit: '10', order: 'desc', recipient: address }, }, TestApp.auth, ), client.v1.transfers.$get( { query: { address, 'blockNumber.to': toBlock, limit: '10', order: 'desc' }, }, TestApp.auth, ), ]) const sender = await TestApp.json(senderResponse, Transfers.schema.getTransfers.Response) const recipient = await TestApp.json( recipientResponse, Transfers.schema.getTransfers.Response, ) const combined = await TestApp.json(addressResponse, Transfers.schema.getTransfers.Response) expect([senderResponse.status, recipientResponse.status, addressResponse.status]).toEqual([ 200, 200, 200, ]) const seen = new Set() const expected = [...sender.data, ...recipient.data] .sort((a, b) => b.blockNumber - a.blockNumber || b.logIndex - a.logIndex) .filter((entry) => { if (seen.has(entry.id)) return false seen.add(entry.id) return true }) .slice(0, 10) expect(expected).toHaveLength(10) expect(combined.data.map(({ id }) => id)).toEqual(expected.map(({ id }) => id)) const firstResponse = await client.v1.transfers.$get( { query: { address, 'blockNumber.to': toBlock, limit: '5', order: 'desc' }, }, TestApp.auth, ) const first = await TestApp.json(firstResponse, Transfers.schema.getTransfers.Response) expect(firstResponse.status).toBe(200) expect(first.nextCursor).not.toBeNull() if (!first.nextCursor) return const [cursorResponse, positionalResponse] = await Promise.all([ client.v1.transfers.$get( { query: { address, 'blockNumber.to': toBlock, cursor: first.nextCursor, limit: '5', order: 'desc', }, }, TestApp.auth, ), client.v1.transfers.$get( { query: { address, 'blockNumber.to': toBlock, limit: '5', order: 'desc', page: '2', }, }, TestApp.auth, ), ]) const cursor = await TestApp.json(cursorResponse, Transfers.schema.getTransfers.Response) const positional = await TestApp.json( positionalResponse, Transfers.schema.getTransfers.Response, ) const expectedSecondPage = expected.slice(5, 10).map(({ id }) => id) expect([cursorResponse.status, positionalResponse.status]).toEqual([200, 200]) expect([...first.data, ...cursor.data].map(({ id }) => id)).toEqual( expected.map(({ id }) => id), ) expect(positional.data.map(({ id }) => id)).toEqual(expectedSecondPage) }, ) test.runIf(Runtime.get().mode === 'testnet')( 'deduplicates self-transfers in address pages and counts', async () => { const client = TestApp.client() const address = '0xabb0023644c54a20d0fe92becfa764dacaf59ba9' const transactionHash = '0x915a11bcf4b5e667c631f9fb09ac8729f3b436f24eb0b552e54ec13a490ef57f' const response = await client.v1.transfers.$get( { query: { address, 'blockNumber.from': '27608730', 'blockNumber.to': '27608730', include: 'totalCount', limit: '50', token: '0x20c0000000000000000000000000000000000003', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.filter(({ id }) => id === `${transactionHash}-233`)).toHaveLength(1) expect(body.meta).toMatchObject({ totalCount: body.data.length, totalCountCapped: false, }) }, ) test('narrows transfers by sender and recipient independently', async () => { const client = TestApp.client() const seedResponse = await client.v1.transfers.$get({ query: { limit: '5' } }, TestApp.auth) const seed = await TestApp.json(seedResponse, Transfers.schema.getTransfers.Response) expect(seedResponse.status).toBe(200) const { sender, recipient } = seed.data[0]! expect(sender).toMatch(/^0x[0-9a-f]{40}$/) expect(recipient).toMatch(/^0x[0-9a-f]{40}$/) const bySenderResponse = await client.v1.transfers.$get( { query: { limit: '5', sender } }, TestApp.auth, ) const bySender = await TestApp.json(bySenderResponse, Transfers.schema.getTransfers.Response) expect(bySenderResponse.status).toBe(200) expect(bySender.data.every((entry) => entry.sender === sender)).toBe(true) const byRecipientResponse = await client.v1.transfers.$get( { query: { limit: '5', recipient } }, TestApp.auth, ) const byRecipient = await TestApp.json( byRecipientResponse, Transfers.schema.getTransfers.Response, ) expect(byRecipientResponse.status).toBe(200) expect(byRecipient.data.every((entry) => entry.recipient === recipient)).toBe(true) }) test('paginates recipient scans through the real indexer', async () => { const runtime = Runtime.get() const recipient = Schema.Address.parse( runtime.mode === 'testnet' ? '0xabb0023644c54a20d0fe92becfa764dacaf59ba9' : runtime.fixtures!.fundedAccounts[0]!, ) const tidx = Tidx.getClient({ chainId: runtime.chainId, tidx: runtime.mode === 'localnet' ? { baseUrl: runtime.tidxUrl } : { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl }, }) const bounds = runtime.mode === 'testnet' ? { toBlock: 27_608_730 } : {} const reference = await Transfers.scan( { tidx }, { chainId: runtime.chainId, limit: 2, order: 'desc', recipient, ...bounds }, ) const first = await Transfers.scan( { tidx }, { chainId: runtime.chainId, limit: 1, order: 'desc', recipient, ...bounds }, ) expect(reference.items).toHaveLength(2) expect(first.items).toHaveLength(1) expect(first.nextCursor).not.toBeNull() if (!first.nextCursor) return const [cursor, positional] = await Promise.all([ Transfers.scan( { tidx }, { chainId: runtime.chainId, cursor: first.nextCursor, limit: 1, order: 'desc', recipient, ...bounds, }, ), Transfers.scan( { tidx }, { chainId: runtime.chainId, limit: 1, order: 'desc', page: 2, recipient, ...bounds }, ), ]) const ids = (page: Transfers.scan.Page) => page.items.map(({ data }) => `${data.transactionHash}-${data.logIndex}`) const [sameAddress, senderAddress] = await Promise.all([ Transfers.scan( { tidx }, { chainId: runtime.chainId, eitherSide: recipient, limit: 2, order: 'desc', recipient, ...bounds, }, ), Transfers.scan( { tidx }, { chainId: runtime.chainId, eitherSide: first.items[0]!.data.sender, limit: 1, order: 'desc', recipient, ...bounds, }, ), ]) expect(ids(first)).toEqual(ids(reference).slice(0, 1)) expect(ids(cursor)).toEqual(ids(reference).slice(1, 2)) expect(ids(positional)).toEqual(ids(reference).slice(1, 2)) expect(ids(sameAddress)).toEqual(ids(reference)) expect(ids(senderAddress)).toEqual(ids(first)) expect([...reference.items, ...cursor.items, ...positional.items].every(({ data }) => data.recipient === recipient && data.address.startsWith('0x20c'))).toBe(true) // prettier-ignore }) test('rejects mixing `address` with `sender` or `recipient`', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { address: TestApp.token, sender: TestApp.token } }, TestApp.auth, ) const body = await TestApp.json(response, Schema.ErrorResponse) expect(response.status).toMatchInlineSnapshot(`400`) expect(body.error.code).toMatchInlineSnapshot(`"query_invalid"`) }) test('walks the cursor to a disjoint next page', async () => { const client = TestApp.client() const firstResponse = await client.v1.transfers.$get({ query: { limit: '5' } }, TestApp.auth) const first = await TestApp.json(firstResponse, Transfers.schema.getTransfers.Response) expect(firstResponse.status).toBe(200) if (!first.nextCursor) return const secondResponse = await client.v1.transfers.$get( { query: { limit: '5', cursor: first.nextCursor } }, TestApp.auth, ) const second = await TestApp.json(secondResponse, Transfers.schema.getTransfers.Response) expect(secondResponse.status).toBe(200) expect(secondResponse.headers.get('cache-control')).toMatchInlineSnapshot( `"private, max-age=60, stale-while-revalidate=300"`, ) expect(second.data.length).toBeGreaterThan(0) // Each row's identity on the unscoped feed is `(blockNumber, txHash, // sourceToken, sender, recipient, amount)` — a single transaction can // emit transfers across multiple contracts, so the per-token feed's // `(txHash, blockNumber)` shortcut is not unique here. type Entry = z.output const keyOf = (entry: Entry) => `${entry.blockNumber}:${entry.transactionHash}:${entry.sourceToken.address}:${entry.sender}:${entry.recipient}:${entry.sourceAmount.baseUnits}` const firstKeys = new Set(first.data.map(keyOf)) expect(second.data.every((entry) => !firstKeys.has(keyOf(entry)))).toBe(true) }) test('rejects invalid timestamp bounds', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { 'timestamp.from': 'not-a-timestamp' } }, TestApp.auth, ) const body = await TestApp.json(response, Schema.ErrorResponse) expect(response.status).toMatchInlineSnapshot(`400`) expect(body.error.code).toMatchInlineSnapshot(`"query_invalid"`) }) test('rejects invalid pagination', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get({ query: { limit: '0' } }, TestApp.auth) const body = await TestApp.json(response, Schema.ErrorResponse) expect(response.status).toMatchInlineSnapshot(`400`) expect(body.error.code).toMatchInlineSnapshot(`"query_invalid"`) }) test('serves a bounded positional page via `page`', async () => { const client = TestApp.client() // Ascending order anchors the feed at the first indexed transfer, so // positional pages are deterministic: the offset page must equal the // matching slice of the head page. const headResponse = await client.v1.transfers.$get( { query: { limit: '10', order: 'asc' } }, TestApp.auth, ) const head = await TestApp.json(headResponse, Transfers.schema.getTransfers.Response) expect(headResponse.status).toBe(200) expect(head.data.length).toBe(10) const offsetResponse = await client.v1.transfers.$get( { query: { limit: '5', order: 'asc', page: '2' } }, TestApp.auth, ) const page = await TestApp.json(offsetResponse, Transfers.schema.getTransfers.Response) expect(offsetResponse.status).toBe(200) type Entry = z.output const keyOf = (entry: Entry) => `${entry.blockNumber}:${entry.transactionHash}:${entry.sourceToken.address}:${entry.sender}:${entry.recipient}:${entry.sourceAmount.baseUnits}` expect(page.data.map(keyOf)).toEqual(head.data.slice(5, 10).map(keyOf)) // Offset pages still return a keyset cursor so clients can switch to deep // cursor traversal from a positional jump. expect(typeof page.nextCursor).toBe('string') }) test('rejects `page` combined with `cursor`', async () => { const client = TestApp.client() const seedResponse = await client.v1.transfers.$get({ query: { limit: '5' } }, TestApp.auth) const seed = await TestApp.json(seedResponse, Transfers.schema.getTransfers.Response) expect(seedResponse.status).toBe(200) if (!seed.nextCursor) return const response = await client.v1.transfers.$get( { query: { cursor: seed.nextCursor, page: '2' } }, TestApp.auth, ) const body = await TestApp.json(response, Schema.ErrorResponse) expect(response.status).toMatchInlineSnapshot(`400`) expect(body.error.code).toMatchInlineSnapshot(`"query_invalid"`) }) test('rejects a page window beyond the bounded cap', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { limit: '20', page: '501' } }, TestApp.auth, ) const body = await TestApp.json(response, Schema.ErrorResponse) expect(response.status).toMatchInlineSnapshot(`400`) expect(body.error.code).toMatchInlineSnapshot(`"query_invalid"`) }) test.runIf(Runtime.get().mode === 'testnet')( 'resolves memo and attribution on demand via include=memo', async () => { const client = TestApp.client() // Real Moderato account that both sends and receives MPP-attributed // transfers (memos prefixed with the attribution tag). const account = '0xd92087abdc3af63b284b6cf8282061df5f0f7dda' const response = await client.v1.transfers.$get( { query: { address: account, include: 'memo', limit: '50' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) // At least one transfer carries a resolved MPP service name; attribution // fingerprints are suppressed from the display `memo`, so any present // memo is decoded text. expect( body.data.some((t) => typeof t.attribution === 'string' && t.attribution.length > 0), ).toBe(true) expect(body.data.every((t) => t.memo === undefined || typeof t.memo === 'string')).toBe(true) // Without `include=memo`, the same page omits both fields. const bareResponse = await client.v1.transfers.$get( { query: { address: account, limit: '50' } }, TestApp.auth, ) const bare = await TestApp.json(bareResponse, Transfers.schema.getTransfers.Response) expect(bareResponse.status).toBe(200) expect(bare.data.every((t) => t.attribution === undefined && t.memo === undefined)).toBe(true) }, ) test('embeds a capped unscoped total count on demand', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { include: 'totalCount', limit: '5' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(response.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(true) expect(body.meta).toBeDefined() expect(Number.isInteger(body.meta?.totalCount)).toBe(true) expect(body.meta!.totalCount).toBeGreaterThanOrEqual(body.data.length) expect(body.meta!.totalCount).toBeLessThanOrEqual(Schema.countCap) expect(body.meta!.totalCountCapped).toBe(body.meta!.totalCount! >= Schema.countCap) }) test('embeds a capped token-scoped total count on demand', async () => { const client = TestApp.client() if (Runtime.get().mode === 'localnet') expect(TestApp.token).toBeDefined() // A token-scoped page: the count is the matched-row total, independent of // the page `limit`, and surfaces under `meta` (not the pagination fields). const response = await client.v1.transfers.$get( { query: { token: TestApp.token, include: 'totalCount', limit: '5' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(response.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(true) expect(body.meta).toBeDefined() expect(Number.isInteger(body.meta?.totalCount)).toBe(true) expect(body.meta!.totalCount).toBeGreaterThanOrEqual(body.data.length) expect(typeof body.meta?.totalCountCapped).toBe('boolean') // The count never exceeds the cap; `totalCountCapped` is set iff the cap was hit. expect(body.meta!.totalCount).toBeLessThanOrEqual(Schema.countCap) expect(body.meta!.totalCountCapped).toBe(body.meta!.totalCount! >= Schema.countCap) // Without `include=totalCount`, the same page omits `meta` entirely and // does not run the count query. const bareResponse = await client.v1.transfers.$get( { query: { token: TestApp.token, limit: '5' } }, TestApp.auth, ) const bare = await TestApp.json(bareResponse, Transfers.schema.getTransfers.Response) expect(bareResponse.status).toBe(200) // Without the include, counts are omitted (`meta` carries at most rate // provenance). expect(bare.meta?.totalCount).toBeUndefined() expect(bare.meta?.totalCountCapped).toBeUndefined() expect(bareResponse.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(false) }) test.skipIf(Runtime.get().mode !== 'localnet')( 'folds a cross-token transfer into one row via include=crossToken', async () => { const crossToken = Runtime.get().fixtures?.crossTokenTransfer expect(crossToken).toBeDefined() if (!crossToken) return const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { address: crossToken.sender, include: 'crossToken', limit: '50' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(response.headers.get('server-timing')?.includes('transfers_cross_token;dur=')).toBe( true, ) // The transaction's several transfer legs fold into a single row: the // token swapped into the DEX becomes `sourceToken`, and the token // delivered to the recipient becomes `destinationToken`. const folded = body.data.filter((entry) => entry.transactionHash === crossToken.hash) expect(folded.length).toBe(1) const transfer = folded[0]! expect(transfer.sender).toBe(crossToken.sender) expect(transfer.recipient).toBe(crossToken.recipient) expect(transfer.sourceToken.address).toBe(crossToken.sourceToken) expect(transfer.destinationToken?.address).toBe(crossToken.destinationToken) expect(transfer.sourceAmount.baseUnits).toMatch(/^\d+$/) expect(transfer.destinationAmount?.baseUnits).toMatch(/^\d+$/) expect('amount' in transfer.sourceToken).toBe(false) expect('amount' in transfer.destinationToken!).toBe(false) // Without `include=crossToken`, the transaction stays as its raw per-log // legs (more than one row) and no row carries `destinationToken`. const bareResponse = await client.v1.transfers.$get( { query: { address: crossToken.sender, limit: '50' } }, TestApp.auth, ) const bare = await TestApp.json(bareResponse, Transfers.schema.getTransfers.Response) expect(bareResponse.status).toBe(200) expect( bare.data.filter((entry) => entry.transactionHash === crossToken.hash).length, ).toBeGreaterThan(1) expect(bare.data.every((entry) => entry.destinationToken === undefined)).toBe(true) }, ) test.skipIf(Runtime.get().mode !== 'localnet')( 'counts address-scoped cross-token transfers', async () => { const crossToken = Runtime.get().fixtures?.crossTokenTransfer expect(crossToken).toBeDefined() if (!crossToken) return const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { address: crossToken.sender, include: 'crossToken,totalCount', limit: '200', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(response.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(true) expect(body.data.filter((entry) => entry.transactionHash === crossToken.hash)).toHaveLength(1) expect(body.nextCursor).toBeNull() expect(body.meta).toMatchObject({ totalCount: body.data.length, totalCountCapped: false, }) }, ) test.skipIf(Runtime.get().mode !== 'localnet')( 'applies address page offsets after merging both sides', async () => { const address = Runtime.get().fixtures?.crossTokenTransfer?.sender expect(address).toBeDefined() if (!address) return const client = TestApp.client() const [referenceResponse, firstResponse, secondResponse] = await Promise.all([ client.v1.transfers.$get({ query: { address, limit: '10' } }, TestApp.auth), client.v1.transfers.$get({ query: { address, limit: '5' } }, TestApp.auth), client.v1.transfers.$get({ query: { address, limit: '5', page: '2' } }, TestApp.auth), ]) const reference = await TestApp.json( referenceResponse, Transfers.schema.getTransfers.Response, ) const first = await TestApp.json(firstResponse, Transfers.schema.getTransfers.Response) const second = await TestApp.json(secondResponse, Transfers.schema.getTransfers.Response) expect([referenceResponse.status, firstResponse.status, secondResponse.status]).toEqual([ 200, 200, 200, ]) expect(reference.data).toHaveLength(10) expect(first.data.map(({ id }) => id)).toEqual(reference.data.slice(0, 5).map(({ id }) => id)) expect(second.data.map(({ id }) => id)).toEqual( reference.data.slice(5, 10).map(({ id }) => id), ) }, ) test.skipIf(Runtime.get().mode !== 'localnet')( 'counts address-scoped plumbing from complete cross-token legs', async () => { const crossToken = Runtime.get().fixtures?.crossTokenTransfer expect(crossToken).toBeDefined() if (!crossToken) return const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { address: Addresses.stablecoinDex, include: 'crossToken,totalCount', limit: '50', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.some((entry) => entry.transactionHash === crossToken.hash)).toBe(false) expect(body.meta).toMatchObject({ totalCount: body.data.length, totalCountCapped: false, }) }, ) test.skipIf(Runtime.get().mode !== 'localnet')( 'counts token-scoped cross-token transfers', async () => { const crossToken = Runtime.get().fixtures?.crossTokenTransfer expect(crossToken).toBeDefined() if (!crossToken) return const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { include: 'crossToken,totalCount', limit: '50', token: crossToken.destinationToken, }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(response.headers.get('server-timing')?.includes('transfers_count;dur=')).toBe(true) expect(body.meta).toBeDefined() expect(body.meta!.totalCount).toBeGreaterThanOrEqual(body.data.length) }, ) test('omits transfer valuations unless a denomination is requested', async () => { const db = TestApp.database() await TestApp.verifiedSeed(db, Runtime.get().chainId) const client = TestApp.client({ db }) const response = await client.v1.transfers.$get( { query: { limit: '5', token: TestApp.tokenWithHolders } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.length).toBeGreaterThan(0) for (const entry of body.data) expect('valuation' in entry.sourceAmount).toBe(false) expect(body.meta?.valuation).toBeUndefined() expect(response.headers.get('server-timing')).not.toContain('valuation_rates;dur=') }) test('values transfer legs in a requested denomination', async () => { const db = TestApp.database() await TestApp.verifiedSeed(db, Runtime.get().chainId) const client = TestApp.client({ db, fx: { oracle: fixed } }) // Denominations are case-insensitive. const response = await client.v1.transfers.$get( { query: { limit: '5', token: TestApp.tokenWithHolders, 'valuation.currency': 'aud', }, }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.length).toBeGreaterThan(0) // USD -> AUD at the fixed 1.6 cross rate, floored to 6 dp. for (const entry of body.data) expect(entry.sourceAmount.valuation).toEqual({ amount: core_Value.format((BigInt(entry.sourceAmount.baseUnits) * 16n) / 10n, 6), currency: 'AUD', }) expect(body.meta?.valuation).toMatchInlineSnapshot(` { "asOf": "2026-01-01T00:00:00.000Z", "basis": "nominal", "source": "fixed", } `) }) test.skipIf(Runtime.get().mode !== 'localnet')( 'nulls leg values for unverified tokens', async () => { // Curate only pathUSD so the faucet token queried below is unverified // for this app, and its legs never value. const db = TestApp.database() await TestApp.verifiedSeed(db, Runtime.get().chainId, { tokens: [ { address: TestApp.tokenWithHolders, currency: 'USD', decimals: 6, name: 'PathUSD', symbol: 'pathUSD', }, ], }) const client = TestApp.client({ db }) const token = Runtime.get().fixtures!.transferTokenAddresses.find( (address) => address.toLowerCase() !== TestApp.tokenWithHolders, )! const response = await client.v1.transfers.$get( { query: { limit: '5', token, 'valuation.currency': 'USD' } }, TestApp.auth, ) const body = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(body.data.length).toBeGreaterThan(0) for (const entry of body.data) expect(entry.sourceAmount.valuation).toBeNull() }, ) test('surfaces a 502 when transfer data is unavailable', async () => { // Point at an unreachable indexer so the upstream query fails deterministically. const client = TestApp.client({ tidx: { baseUrl: 'http://127.0.0.1:1' } }) const response = await client.v1.transfers.$get({ query: {} }, TestApp.auth) const body = await TestApp.json(response, Schema.ErrorResponse) const { requestId, error } = body const { message, ...stableError } = error expect(response.status).toMatchInlineSnapshot(`502`) expect(typeof requestId).toMatchInlineSnapshot(`"string"`) expect(typeof message).toMatchInlineSnapshot(`"string"`) expect(stableError).toMatchInlineSnapshot(` { "code": "upstream_error", } `) }) test('bounds an unscoped transfer page by timestamp', async () => { const client = TestApp.client() const runtime = Runtime.get() const tidx = Tidx.getClient({ chainId: runtime.chainId, tidx: runtime.mode === 'localnet' ? { baseUrl: runtime.tidxUrl } : { auth: process.env.TIDX_AUTH, baseUrl: runtime.tidxUrl }, }) const latest = await Transfers.scan( { tidx }, { chainId: runtime.chainId, limit: 1, order: 'desc' }, ) const latestTransfer = latest.items[0]?.data if (latestTransfer === undefined) throw new Error('expected an indexed transfer') const block = await tidx.fetch({ chainId: runtime.chainId, query: `SELECT timestamp FROM blocks WHERE num = ${latestTransfer.blockNumber} LIMIT 1`, }) const seconds = block.rows[0]?.['timestamp'] if (typeof seconds !== 'number') throw new Error('expected an indexed block timestamp') const timestamp = new Date(seconds * 1_000).toISOString() const response = await client.v1.transfers.$get( { query: { limit: '5', order: 'desc', 'timestamp.from': timestamp, 'timestamp.to': timestamp, }, }, TestApp.auth, ) const page = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(page.data.length).toBeGreaterThan(0) expect(page.data.every((item) => Date.parse(item.timestamp) === Date.parse(timestamp))).toBe( true, ) const fractionalResponse = await client.v1.transfers.$get( { query: { limit: '5', order: 'desc', 'timestamp.from': new Date(seconds * 1_000 + 500).toISOString(), 'timestamp.to': new Date(seconds * 1_000 + 999).toISOString(), }, }, TestApp.auth, ) const fractional = await TestApp.json( fractionalResponse, Transfers.schema.getTransfers.Response, ) expect(fractionalResponse.status).toBe(200) expect(fractional.data).toEqual([]) const emptyResponse = await client.v1.transfers.$get( { query: { limit: '5', order: 'desc', 'timestamp.to': '1970-01-01T00:00:00Z', }, }, TestApp.auth, ) const empty = await TestApp.json(emptyResponse, Transfers.schema.getTransfers.Response) expect(emptyResponse.status).toBe(200) expect(empty.data).toEqual([]) }) test('accepts the largest positional window', async () => { const client = TestApp.client() const response = await client.v1.transfers.$get( { query: { limit: '50', order: 'desc', page: '10' } }, TestApp.auth, ) const page = await TestApp.json(response, Transfers.schema.getTransfers.Response) expect(response.status).toBe(200) expect(page.data.length).toBeLessThanOrEqual(50) }) }) describe('collate', () => { /** A minimal transfer row at a `(block, log index)` position. */ const row = (block: number, logIndex: number) => ({ block_num: block, log_idx: logIndex, tx_hash: `0x${'cc'.repeat(32)}`, }) test('passes a lone engine-paged stream through untouched', () => { const rows = [row(40, 4), row(20, 2)] expect( Transfers.collate([{ capped: false, rows, sample: false }], { limit: 3, offset: 0, order: 'desc', }), ).toEqual({ capped: false, rows, }) }) test('merges split streams: dedupes, orders globally, applies the window', () => { const shared = row(30, 3) const { capped, rows } = Transfers.collate( [ { capped: false, rows: [row(40, 4), shared], sample: false }, { capped: false, rows: [{ ...shared }, row(10, 1)], sample: false }, ], { limit: 2, offset: 1, order: 'desc' }, ) expect(capped).toBe(false) expect(rows.map((entry) => entry['block_num'])).toEqual([30, 10]) }) test('orders and pages a below-cap fallback sample', () => { const { capped, rows } = Transfers.collate( [{ capped: false, rows: [row(10, 1), row(30, 3)], sample: true }], { limit: 2, offset: 0, order: 'desc' }, ) expect(capped).toBe(false) expect(rows.map((entry) => entry['block_num'])).toEqual([30, 10]) }) test('preserves the cap marker on an ordered stream', () => { const rows = [row(30, 3), row(10, 1)] expect( Transfers.collate([{ capped: true, rows, sample: false }], { limit: 2, offset: 0, order: 'desc', }), ).toEqual({ capped: true, rows }) }) test('preserves resumable progress on a lone ordered stream', () => { const rows = [row(30, 3), row(10, 1)] const resume = { offset: 4, position: [10, 1] as const } expect( Transfers.collate([{ capped: true, resume, rows, sample: false }], { limit: 2, offset: 0, order: 'desc', }), ).toEqual({ capped: true, resume, rows }) }) test('preserves common resumable progress across split ordered streams', () => { const result = Transfers.collate( [ { capped: true, resume: { offset: 0, position: [20, 2] }, rows: [row(50, 5), row(30, 3)], sample: false, }, { capped: true, resume: { offset: 0, position: [25, 2] }, rows: [row(45, 4), row(25, 2)], sample: false, }, ], { limit: 5, offset: 1, order: 'desc' }, ) expect(result).toEqual({ capped: true, resume: { offset: 0, position: [25, 2] }, rows: [row(45, 4), row(30, 3), row(25, 2)], }) }) test('returns a complete split window before the common resume point', () => { const result = Transfers.collate( [ { capped: true, resume: { offset: 0, position: [20, 2] }, rows: [row(50, 5), row(30, 3)], sample: false, }, { capped: true, resume: { offset: 0, position: [25, 2] }, rows: [row(45, 4), row(25, 2)], sample: false, }, ], { limit: 3, offset: 0, order: 'desc' }, ) expect(result).toEqual({ capped: false, rows: [row(50, 5), row(45, 4), row(30, 3)], }) }) test('flags a fallback sample that hit the cap', () => { const sample = Array.from({ length: Schema.countCap }, (_, index) => row(index + 1, index)) const { capped, rows } = Transfers.collate( [ { capped: false, rows: [row(40_000, 20_000)], sample: false }, { capped: true, rows: sample, sample: true }, ], { limit: 3, offset: 0, order: 'desc' }, ) expect(capped).toBe(true) expect(rows.map((entry) => entry['block_num'])).toEqual([40_000, 10_000, 9_999]) }) }) describe('foldCount', () => { const swapper = `0x${'11'.repeat(20)}` const recipient = `0x${'22'.repeat(20)}` const sourceToken = '0x20c0000000000000000000000000000000000001' const destinationToken = '0x20c0000000000000000000000000000000000002' /** One `Transfer` leg row within a transaction. */ const leg = (txHash: string, logIndex: number, from: string, to: string, token: string) => ({ address: token, from, log_idx: logIndex, to, tx_hash: txHash, value: '100', }) const plain = leg(`0x${'ee'.repeat(32)}`, 0, swapper, recipient, sourceToken) test('subtracts classified bundle legs', () => { const txHash = `0x${'dd'.repeat(32)}` // The account server's approve+swap+forward multicall: source into the // DEX, swapped token back, delivery to the recipient, plus the fee leg. const rows = [ leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken), leg(txHash, 1, Addresses.stablecoinDex, swapper, destinationToken), leg(txHash, 2, swapper, recipient, destinationToken), leg(txHash, 3, swapper, Addresses.feeManager, sourceToken), plain, ] expect(Transfers.foldCount(rows, { capped: false })).toEqual({ totalCount: 2, totalCountCapped: false, }) }) test('classifies filtered plumbing rows from complete transaction legs', () => { const txHash = `0x${'dd'.repeat(32)}` const rows = [ leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken), leg(txHash, 1, Addresses.stablecoinDex, swapper, destinationToken), leg(txHash, 2, swapper, recipient, destinationToken), leg(txHash, 3, swapper, Addresses.feeManager, sourceToken), ] expect( Transfers.foldCount(rows.slice(0, 2), { capped: false, classificationRows: rows }), ).toEqual({ totalCount: 0, totalCountCapped: false, }) }) test('uses log order when classifying complete transaction legs', () => { const txHash = `0x${'dd'.repeat(32)}` const thirdToken = '0x20c0000000000000000000000000000000000003' const rows = [ leg(txHash, 1, swapper, Addresses.stablecoinDex, sourceToken), leg(txHash, 4, Addresses.stablecoinDex, swapper, destinationToken), leg(txHash, 5, swapper, Addresses.stablecoinDex, destinationToken), leg(txHash, 7, Addresses.stablecoinDex, swapper, thirdToken), ] const delivery = leg(txHash, 8, swapper, recipient, thirdToken) expect( Transfers.foldCount(rows, { capped: false, classificationRows: [rows[2]!, rows[3]!, delivery, rows[0]!, rows[1]!], }), ).toEqual({ totalCount: 4, totalCountCapped: false, }) }) test('keeps a cap-truncated sample conservative', () => { const txHash = `0x${'dd'.repeat(32)}` // A partial bundle: the DEX return leg fell outside the cap, defeating // classification. The conservative pass still suppresses the plumbing. const rows = [ leg(txHash, 0, swapper, Addresses.stablecoinDex, sourceToken), leg(txHash, 2, swapper, recipient, destinationToken), leg(txHash, 3, swapper, Addresses.feeManager, sourceToken), plain, ] expect(Transfers.foldCount(rows, { capped: false })).toEqual({ totalCount: 4, totalCountCapped: false, }) expect(Transfers.foldCount(rows, { capped: true })).toEqual({ totalCount: 2, totalCountCapped: true, }) }) })