import * as Deposit from '../../internal/funding/Deposit.js' import * as DepositAddress from '../../internal/funding/DepositAddress.js' import * as TestApp from '../../../test/App.js' import * as TestFunding from '../../../test/Funding.js' import * as FundingDeposits from './fundingDeposits.js' const db = TestApp.database() const owner = { environment: 'production', orgId: 'org_deposits' } as const let addressCounter = 0 async function seedAddress(input: Partial = {}) { const index = addressCounter++ const now = new Date(Date.UTC(2026, 0, 1, 0, 0, index)) return DepositAddress.create(db, { apiKeyId: 'key_test', deliveryStrategy: 'provider', environment: owner.environment, id: DepositAddress.generateId(now), now, orgId: owner.orgId, providerOutputToken: TestFunding.providerOutputToken(), snapshot: TestFunding.depositAddressSnapshot({ provider: { id: `relay_${index}`, name: 'Relay' }, recipient: `0x${index.toString(16).padStart(40, '0')}`, }), ...input, }) } let depositCounter = 0 function seedDeposit( address: Awaited>, input: Partial = {}, ) { const index = depositCounter++ const now = new Date(Date.UTC(2026, 0, 2, 0, 0, index)) const sourceTransactionHash = index.toString(16).padStart(64, '0') return Deposit.create(db, { now, providerRequestId: `relay_request_${index}`, snapshot: TestFunding.depositSnapshot(address.id, { provider: address.snapshot.provider, sourceTransactionHashes: [sourceTransactionHash], }), sourceTransactionHash, sourceTransferIndex: 0, ...input, }) } describe('listByAddress', () => { test('pages one owner-visible address newest-first', async () => { const address = await seedAddress() const first = await seedDeposit(address) const second = await seedDeposit(address) const third = await seedDeposit(address) const foreign = await seedAddress({ orgId: 'org_other' }) await seedDeposit(foreign) const page = await FundingDeposits.listByAddress(db, owner, address.id, { limit: 2 }) expect(page.map((row) => row.id)).toEqual([third.id, second.id]) const rest = await FundingDeposits.listByAddress(db, owner, address.id, { cursor: { createdAt: second.createdAt, id: second.id }, limit: 2, }) expect(rest.map((row) => row.id)).toEqual([first.id]) }) test('narrows project-attributed owners to their project', async () => { const address = await seedAddress({ projectId: 'prj_deposit' }) await seedDeposit(address) expect( await FundingDeposits.listByAddress(db, { ...owner, projectId: 'prj_other' }, address.id, { limit: 10, }), ).toEqual([]) }) }) describe('listByDepositAddress', () => { const seedAddressDeposit = (address: Awaited>, index: number) => { const sourceTransactionHash = `fe${index.toString(16).padStart(62, '0')}` return Deposit.create(db, { now: new Date(Date.UTC(2026, 1, 2, 0, 0, index)), providerRequestId: `relay_deposit_address_request_${index}`, snapshot: TestFunding.depositSnapshot(address.id, { provider: address.snapshot.provider, sourceTransactionHashes: [sourceTransactionHash], }), sourceTransactionHash, sourceTransferIndex: 0, }) } test('pages one reusable address across provider records newest-first', async () => { const depositAddress = 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE' const firstAddress = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ address: depositAddress, provider: { id: 'relay_deposit_address_first', name: 'Relay' }, }), }) const secondAddress = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ address: depositAddress, provider: { id: 'relay_deposit_address_second', name: 'Relay' }, subsidize: true, }), }) const first = await seedAddressDeposit(firstAddress, 1) const second = await seedAddressDeposit(secondAddress, 2) const third = await seedAddressDeposit(firstAddress, 3) const unrelatedAddress = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ address: 'TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3', provider: { id: 'relay_deposit_address_unrelated', name: 'Relay' }, recipient: `0x${'fe'.repeat(20)}`, }), }) await seedAddressDeposit(unrelatedAddress, 4) const page = await FundingDeposits.listByDepositAddress(db, { depositAddress, limit: 2, owner, }) expect(page.map((row) => row.id)).toEqual([third.id, second.id]) const rest = await FundingDeposits.listByDepositAddress(db, { cursor: { createdAt: second.createdAt, id: second.id }, depositAddress, limit: 2, owner, }) expect(rest.map((row) => row.id)).toEqual([first.id]) }) test('narrows project-attributed owners to their project', async () => { const depositAddress = 'TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb' const address = await seedAddress({ projectId: 'prj_deposit', snapshot: TestFunding.depositAddressSnapshot({ address: depositAddress, provider: { id: 'relay_deposit_address_project', name: 'Relay' }, }), }) await seedAddressDeposit(address, 5) expect( await FundingDeposits.listByDepositAddress(db, { depositAddress, limit: 10, owner: { ...owner, projectId: 'prj_other' }, }), ).toEqual([]) }) test('normalizes EVM address casing', async () => { const depositAddress = `0x${'Ab'.repeat(20)}` const source = TestFunding.transferSnapshot() const address = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ address: depositAddress, provider: { id: 'relay_deposit_address_evm', name: 'Relay' }, recipient: `0x${'fd'.repeat(20)}`, refundAddress: depositAddress, sourceChain: source.sourceChain, sourceToken: source.sourceToken, }), }) const record = await seedAddressDeposit(address, 6) const rows = await FundingDeposits.listByDepositAddress(db, { depositAddress, limit: 10, owner, }) expect(rows.map((row) => row.id)).toEqual([record.id]) }) }) describe('listByRecipient', () => { test('pages recipient deposits across addresses and hides foreign ownership', async () => { const recipient = '0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD' const firstAddress = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_recipient_first', name: 'Relay' }, recipient, }), }) const secondAddress = await seedAddress({ snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_recipient_second', name: 'Relay' }, recipient, subsidize: true, }), }) const first = await seedDeposit(firstAddress) const second = await seedDeposit(secondAddress) const third = await seedDeposit(firstAddress) const unrelatedAddress = await seedAddress() await seedDeposit(unrelatedAddress) const foreignAddress = await seedAddress({ orgId: 'org_other', snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_recipient_foreign', name: 'Relay' }, recipient, }), }) await seedDeposit(foreignAddress) const page = await FundingDeposits.listByRecipient(db, { limit: 2, owner, recipient: recipient.toLowerCase(), }) expect(page.map((row) => row.id)).toEqual([third.id, second.id]) const rest = await FundingDeposits.listByRecipient(db, { cursor: { createdAt: second.createdAt, id: second.id }, limit: 2, owner, recipient: recipient.toLowerCase(), }) expect(rest.map((row) => row.id)).toEqual([first.id]) }) test('narrows project-attributed owners to their project', async () => { const recipient = `0x${'ab'.repeat(20)}` const firstAddress = await seedAddress({ projectId: 'prj_deposit', snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_recipient_project', name: 'Relay' }, recipient, }), }) const secondAddress = await seedAddress({ projectId: 'prj_other', snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_recipient_other_project', name: 'Relay' }, recipient, }), }) const first = await seedDeposit(firstAddress) await seedDeposit(secondAddress) const rows = await FundingDeposits.listByRecipient(db, { limit: 10, owner: { ...owner, projectId: 'prj_deposit' }, recipient, }) expect(rows.map((row) => row.id)).toEqual([first.id]) }) }) describe('getOwned', () => { test('returns owned deposits and hides foreign ownership', async () => { const address = await seedAddress() const record = await seedDeposit(address) expect((await FundingDeposits.getOwned(db, owner, record.id))?.id).toBe(record.id) expect( await FundingDeposits.getOwned(db, { ...owner, orgId: 'org_other' }, record.id), ).toBeUndefined() }) }) describe('insert', () => { test('derives identity, ownership, and route from the deposit address', async () => { const address = await seedAddress({ projectId: 'prj_deposit' }) const foreign = await seedAddress({ orgId: 'org_other' }) const sourceTransactionHash = 'bb'.repeat(32) const record = await Deposit.create(db, { providerRequestId: 'relay_request_insert', snapshot: TestFunding.depositSnapshot(address.id, { destinationChain: foreign.snapshot.destinationChain, destinationToken: foreign.snapshot.destinationToken, provider: foreign.snapshot.provider, recipient: foreign.snapshot.recipient, refundAddress: foreign.snapshot.refundAddress, sourceChain: { addressFormat: 'hex', id: 'eip155:1', kind: 'evm', name: 'Ethereum' }, sourceToken: foreign.snapshot.sourceToken, }), sourceTransactionHash, sourceTransferIndex: 7, }) expect(record).toMatchObject({ depositAddressId: address.id, environment: address.environment, id: expect.stringMatching(/^fdp_/), orgId: address.orgId, projectId: address.projectId, providerOutputToken: address.providerOutputToken, snapshot: { destinationChain: address.snapshot.destinationChain, destinationToken: address.snapshot.destinationToken, provider: address.snapshot.provider, recipient: address.snapshot.recipient, refundAddress: address.snapshot.refundAddress, sourceChain: address.snapshot.sourceChain, sourceToken: address.snapshot.sourceToken, sourceTransactionHashes: [sourceTransactionHash], }, }) }) test('deduplicates one source transfer across deposit addresses', async () => { const firstAddress = await seedAddress() const secondAddress = await seedAddress() const sourceTransactionHash = 'aa'.repeat(32) await seedDeposit(firstAddress, { sourceTransactionHash, sourceTransferIndex: 7 }) await expect( seedDeposit(secondAddress, { sourceTransactionHash, sourceTransferIndex: 7 }), ).rejects.toMatchObject({ code: '23505' }) }) }) describe('update', () => { test('applies allowed transitions and rejects terminal exits', async () => { const address = await seedAddress() const record = await seedDeposit(address) const bridging = await Deposit.transition(db, { expectedVersion: 1, id: record.id, status: 'bridging', }) expect(bridging.type).toBe('applied') const completed = await Deposit.transition(db, { expectedVersion: 2, id: record.id, status: 'completed', }) expect(completed.type).toBe('applied') expect( await Deposit.transition(db, { expectedVersion: 3, id: record.id, status: 'refunding', }), ).toMatchObject({ type: 'invalid' }) }) test('persists reconciliation state without changing source identity', async () => { const address = await seedAddress() const foreign = await seedAddress({ orgId: 'org_other' }) const record = await seedDeposit(address) const destinationTransactionHash = `0x${'cc'.repeat(32)}` const providerTransactionHash = `0x${'dd'.repeat(32)}` const result = await Deposit.transition(db, { expectedVersion: 1, id: record.id, providerOutputAmount: { baseUnits: '98889820', currency: 'USD', decimals: 6, formatted: '98.88982', }, providerRequestIds: ['relay-request'], providerState: { request: 'provider-state' }, providerTransactionHashes: [providerTransactionHash], retryState: { attempts: 1 }, settlementTransaction: `0x${'ab'.repeat(32)}`, settlementTransactionHash: `0x${'ee'.repeat(32)}`, snapshot: { ...record.snapshot, depositAddressId: foreign.id, destinationTransactionHashes: [destinationTransactionHash], sourceChain: { addressFormat: 'hex', id: 'eip155:1', kind: 'evm', name: 'Ethereum' }, sourceTransactionHashes: ['changed-source-transaction'], }, status: 'bridging', subsidyAmount: { baseUnits: '1110180', currency: 'USD', decimals: 6, formatted: '1.11018', }, tempoGasPaid: '21000', }) expect(result).toMatchObject({ record: { depositAddressId: record.depositAddressId, providerOutputAmount: { baseUnits: '98889820' }, providerRequestIds: ['relay-request'], providerState: { request: 'provider-state' }, providerTransactionHashes: [providerTransactionHash], retryState: { attempts: 1 }, settlementTransaction: `0x${'ab'.repeat(32)}`, settlementTransactionHash: `0x${'ee'.repeat(32)}`, snapshot: { depositAddressId: record.snapshot.depositAddressId, destinationTransactionHashes: [destinationTransactionHash], sourceChain: record.snapshot.sourceChain, sourceTransactionHashes: record.snapshot.sourceTransactionHashes, }, status: 'bridging', subsidyAmount: { baseUnits: '1110180' }, tempoGasPaid: '21000', }, type: 'applied', }) }) }) describe('recordRequestObservation', () => { test('attaches the first poll and webhook timestamps without changing material state', async () => { const address = await seedAddress() const record = await seedDeposit(address, { providerRequestId: 'relay_observed' }) await FundingDeposits.recordRequestObservation(db, { depositAddressId: address.id, pollObservedAt: '2026-01-02T00:00:02.000Z', providerRequestId: 'relay_observed', webhookReceivedAt: '2026-01-02T00:00:03.000Z', }) await FundingDeposits.recordRequestObservation(db, { depositAddressId: address.id, pollObservedAt: '2026-01-02T00:00:01.000Z', providerRequestId: 'relay_observed', webhookReceivedAt: '2026-01-02T00:00:04.000Z', }) expect(await FundingDeposits.get(db, record.id)).toMatchInlineSnapshot( { createdAt: expect.any(String), depositAddressId: expect.any(String), id: expect.any(String), providerOutputToken: expect.any(Object), snapshot: expect.any(Object), updatedAt: expect.any(String), }, ` { "createdAt": Any, "depositAddressId": Any, "detectionTrigger": null, "environment": "production", "id": Any, "orgId": "org_deposits", "pollObservedAt": "2026-01-02T00:00:01.000Z", "projectId": null, "providerOutputAmount": null, "providerOutputToken": Any, "providerRequestId": "relay_observed", "providerRequestIds": [ "relay_observed", ], "providerState": null, "providerTransactionHashes": [], "providerTransferIndex": 0, "retryState": null, "settlementTransaction": null, "settlementTransactionHash": null, "snapshot": Any, "sourceChainId": "tron:0x2b6653dc", "sourceTransactionHash": "0000000000000000000000000000000000000000000000000000000000000011", "sourceTransferIndex": 0, "status": "detected", "statusReason": null, "subsidyAmount": null, "tempoGasPaid": null, "updatedAt": Any, "version": 1, "webhookReceivedAt": "2026-01-02T00:00:03.000Z", } `, ) }) }) describe('toPublic', () => { test('omits private reconciliation and settlement state', async () => { const address = await seedAddress() const record = await seedDeposit(address, { providerOutputAmount: { baseUnits: '98889820', currency: 'USD', decimals: 6, formatted: '98.88982', }, providerRequestIds: ['relay-request'], providerState: { correlation: 'private' }, settlementTransaction: `0x${'ab'.repeat(32)}`, subsidyAmount: { baseUnits: '1110180', currency: 'USD', decimals: 6, formatted: '1.11018', }, }) const public_ = Deposit.toPublic(record) expect(public_.sourceTransferIndex).toBe(record.sourceTransferIndex) expect(public_).not.toMatchObject({ providerOutputAmount: expect.anything(), providerOutputToken: expect.anything(), providerRequestIds: expect.anything(), providerState: expect.anything(), settlementTransaction: expect.anything(), subsidyAmount: expect.anything(), }) }) }) describe('list', () => { test('pages organization deposits and narrows project-attributed owners', async () => { const listOwner = { environment: owner.environment, orgId: 'org_deposit_list' } as const const firstAddress = await seedAddress({ orgId: listOwner.orgId }) const projectAddress = await seedAddress({ orgId: listOwner.orgId, projectId: 'prj_deposit_list', }) const siblingAddress = await seedAddress({ orgId: listOwner.orgId, projectId: 'prj_deposit_sibling', }) const first = await seedDeposit(firstAddress) const project = await seedDeposit(projectAddress) const sibling = await seedDeposit(siblingAddress) const foreignAddress = await seedAddress({ orgId: 'org_deposit_list_foreign' }) await seedDeposit(foreignAddress) const sandboxAddress = await seedAddress({ environment: 'sandbox', orgId: listOwner.orgId, }) await seedDeposit(sandboxAddress) const page = await FundingDeposits.list(db, { limit: 2, owner: listOwner }) expect(page.map((record) => record.id)).toEqual([sibling.id, project.id]) const rest = await FundingDeposits.list(db, { cursor: { createdAt: project.createdAt, id: project.id }, limit: 2, owner: listOwner, }) expect(rest.map((record) => record.id)).toEqual([first.id]) expect( ( await FundingDeposits.list(db, { limit: 10, owner: { ...listOwner, projectId: 'prj_deposit_list' }, }) ).map((record) => record.id), ).toEqual([project.id]) }) test('AND-combines filters and counts through the same ownership scope', async () => { const listOwner = { environment: owner.environment, orgId: 'org_deposit_filters' } as const const recipient = `0x${'ab'.repeat(20)}` const address = await seedAddress({ orgId: listOwner.orgId, snapshot: TestFunding.depositAddressSnapshot({ provider: { id: 'relay_filters', name: 'Relay' }, recipient, }), }) const match = await seedDeposit(address) const unrelatedAddress = await seedAddress({ orgId: listOwner.orgId }) await seedDeposit(unrelatedAddress) const filters = { depositAddress: address.address, destinationTokenKeys: [address.destinationTokenKey], providerId: address.providerId, recipient: recipient.toLowerCase(), sourceChainIds: [address.sourceChainId], sourceTokenKeys: [address.sourceTokenKey], status: 'detected', } as const expect( (await FundingDeposits.list(db, { ...filters, limit: 10, owner: listOwner })).map( (record) => record.id, ), ).toEqual([match.id]) expect(await FundingDeposits.count(db, { ...filters, cap: 10, owner: listOwner })).toBe(1) expect(await FundingDeposits.count(db, { ...filters, cap: 0, owner: listOwner })).toBe(1) expect( await FundingDeposits.count(db, { ...filters, destinationTokenKeys: [], cap: 10, owner: listOwner, }), ).toBe(0) }) })