tapimo 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/dist/ApiKey.d.ts +3 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +10 -0
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +7 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +9 -2
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +20117 -19183
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +61 -4
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +84 -74
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +14 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -2
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +2 -0
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +11 -11
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +67 -67
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +2 -0
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +3 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/data/App.d.ts +11696 -13937
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +4 -8
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +271 -271
- package/dist/apps/data/routes/addresses.d.ts +398 -167
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +278 -8
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +114 -2
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +123 -123
- package/dist/apps/data/routes/coingecko.d.ts +170 -170
- package/dist/apps/data/routes/earn.d.ts +498 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +876 -4
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +10 -10
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/indexer.d.ts +6 -6
- package/dist/apps/data/routes/indexer.js +4 -1
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/rpc.js +4 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/tokens.d.ts +449 -449
- package/dist/apps/data/routes/transactions.d.ts +2 -2
- package/dist/apps/data/routes/transfers.d.ts +81 -81
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/webhooks.d.ts +9 -9
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/funding/Access.d.ts +16 -0
- package/dist/apps/funding/Access.d.ts.map +1 -0
- package/dist/apps/funding/Access.js +22 -0
- package/dist/apps/funding/Access.js.map +1 -0
- package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1640 -3217
- package/dist/apps/funding/App.d.ts.map +1 -0
- package/dist/apps/funding/App.js +61 -0
- package/dist/apps/funding/App.js.map +1 -0
- package/dist/apps/funding/Inventory.d.ts +5 -0
- package/dist/apps/funding/Inventory.d.ts.map +1 -0
- package/dist/apps/funding/Inventory.js +25 -0
- package/dist/apps/funding/Inventory.js.map +1 -0
- package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
- package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
- package/dist/apps/funding/ProviderAttempts.js +24 -0
- package/dist/apps/funding/ProviderAttempts.js.map +1 -0
- package/dist/apps/funding/index.d.ts +2 -0
- package/dist/apps/funding/index.d.ts.map +1 -0
- package/dist/apps/funding/index.js +2 -0
- package/dist/apps/funding/index.js.map +1 -0
- package/dist/apps/funding/routes/chains.d.ts +169 -0
- package/dist/apps/funding/routes/chains.d.ts.map +1 -0
- package/dist/apps/funding/routes/chains.js +80 -0
- package/dist/apps/funding/routes/chains.js.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts +1997 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.js +778 -0
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/funding/routes/providers.d.ts +91 -0
- package/dist/apps/funding/routes/providers.d.ts.map +1 -0
- package/dist/apps/funding/routes/providers.js +56 -0
- package/dist/apps/funding/routes/providers.js.map +1 -0
- package/dist/apps/funding/routes/quotes.d.ts +426 -0
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
- package/dist/apps/funding/routes/quotes.js +188 -0
- package/dist/apps/funding/routes/quotes.js.map +1 -0
- package/dist/apps/funding/routes/transfers.d.ts +1999 -0
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
- package/dist/apps/funding/routes/transfers.js +614 -0
- package/dist/apps/funding/routes/transfers.js.map +1 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +430 -165
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +298 -21
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +75 -1
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +6 -6
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/invitations.d.ts +122 -122
- package/dist/apps/management/routes/invite-links.d.ts +38 -38
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +99 -99
- package/dist/apps/management/routes/projects.d.ts +25 -25
- package/dist/apps/management/routes/scopes.d.ts +1 -1
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +77 -77
- package/dist/apps/management/routes/webhooks.d.ts +128 -128
- package/dist/apps/mcp.d.ts +2 -3
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +16 -14
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/metadata.d.ts +4 -0
- package/dist/apps/metadata.d.ts.map +1 -1
- package/dist/apps/metadata.js +10 -0
- package/dist/apps/metadata.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +44 -44
- package/dist/cloudflare.d.ts +50 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +178 -0
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +45 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +12 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +20 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.js +12 -0
- package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
- package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/fundingDeposits.d.ts +74 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +126 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/internal/Auth.d.ts +15 -11
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +52 -17
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.js +1 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +2 -2
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +10 -4
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/IpAllowlist.d.ts +15 -0
- package/dist/internal/IpAllowlist.d.ts.map +1 -0
- package/dist/internal/IpAllowlist.js +27 -0
- package/dist/internal/IpAllowlist.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +1 -1
- package/dist/internal/OpenApi.js +1 -1
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Tidx.d.ts +6 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +8 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -5
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +42 -0
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +37 -5
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts +1 -5
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +4 -3
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +102 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +5 -0
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +16 -2
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +348 -54
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/SourceObservation.d.ts +108 -0
- package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
- package/dist/internal/funding/SourceObservation.js +143 -0
- package/dist/internal/funding/SourceObservation.js.map +1 -0
- package/dist/internal/funding/Subsidy.d.ts +2 -2
- package/dist/internal/funding/Subsidy.d.ts.map +1 -1
- package/dist/internal/funding/Subsidy.js +1 -1
- package/dist/internal/funding/Subsidy.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +43 -1
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +438 -27
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +3 -3
- package/src/ApiKey.test.ts +35 -0
- package/src/ApiKey.ts +27 -0
- package/src/ApiKeys.test.ts +29 -0
- package/src/ApiKeys.ts +11 -3
- package/src/App.test.ts +55 -5
- package/src/App.ts +79 -5
- package/src/Client.test-d.ts +203 -28
- package/src/admin/apps/api-keys.test.ts +25 -1
- package/src/admin/apps/api-keys.ts +6 -2
- package/src/admin/ui.generated.ts +67 -67
- package/src/analytics/Analytics.ts +4 -0
- package/src/apps/composition.test.ts +130 -3
- package/src/apps/data/App.test.ts +0 -12
- package/src/apps/data/App.ts +4 -8
- package/src/apps/data/routes/balances.test.ts +42 -0
- package/src/apps/data/routes/balances.ts +218 -85
- package/src/apps/data/routes/earn.test.ts +363 -0
- package/src/apps/data/routes/earn.ts +1141 -3
- package/src/apps/data/routes/indexer.ts +2 -1
- package/src/apps/data/routes/rpc.ts +2 -0
- package/src/apps/data/routes/tokens.test.ts +6 -8
- package/src/apps/funding/Access.ts +26 -0
- package/src/apps/funding/App.test.ts +78 -0
- package/src/apps/funding/App.ts +78 -0
- package/src/apps/funding/Inventory.ts +30 -0
- package/src/apps/funding/ProviderAttempts.ts +26 -0
- package/src/apps/funding/index.ts +1 -0
- package/src/apps/funding/routes/chains.test.ts +78 -0
- package/src/apps/funding/routes/chains.ts +101 -0
- package/src/apps/funding/routes/deposit-addresses.test.ts +1649 -0
- package/src/apps/funding/routes/deposit-addresses.ts +932 -0
- package/src/apps/funding/routes/providers.test.ts +165 -0
- package/src/apps/funding/routes/providers.ts +73 -0
- package/src/apps/funding/routes/quotes.test.ts +511 -0
- package/src/apps/funding/routes/quotes.ts +205 -0
- package/src/apps/funding/routes/transfers.test.ts +1236 -0
- package/src/apps/funding/routes/transfers.ts +704 -0
- package/src/apps/index.ts +1 -0
- package/src/apps/management/routes/api-keys.test.ts +98 -0
- package/src/apps/management/routes/api-keys.ts +96 -1
- package/src/apps/mcp.test.ts +17 -2
- package/src/apps/mcp.ts +18 -13
- package/src/apps/metadata.ts +12 -0
- package/src/apps/mpp/internal/Policy.test.ts +7 -1
- package/src/cloudflare.ts +279 -0
- package/src/db/Db.ts +71 -0
- package/src/db/Schema.ts +24 -0
- package/src/db/tables/fundingDepositAddresses.ts +28 -0
- package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
- package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
- package/src/db/tables/fundingDeposits.test.ts +239 -0
- package/src/db/tables/fundingDeposits.ts +214 -1
- package/src/internal/Auth.test.ts +205 -5
- package/src/internal/Auth.ts +63 -19
- package/src/internal/Cache.test.ts +37 -3
- package/src/internal/Cache.ts +1 -1
- package/src/internal/EdgeCache.test.ts +7 -7
- package/src/internal/EdgeCache.ts +13 -4
- package/src/internal/IpAllowlist.test.ts +52 -0
- package/src/internal/IpAllowlist.ts +39 -0
- package/src/internal/Log.test.ts +7 -1
- package/src/internal/OpenApi.ts +1 -1
- package/src/internal/Tidx.test.ts +28 -0
- package/src/internal/Tidx.ts +19 -3
- package/src/internal/Viem.test.ts +28 -0
- package/src/internal/Viem.ts +21 -5
- package/src/internal/funding/Deposit.ts +74 -5
- package/src/internal/funding/Evidence.test.ts +2 -2
- package/src/internal/funding/Evidence.ts +4 -9
- package/src/internal/funding/Provider.ts +115 -0
- package/src/internal/funding/Reconciliation.local.test.ts +7 -0
- package/src/internal/funding/Reconciliation.test.ts +1364 -109
- package/src/internal/funding/Reconciliation.ts +467 -88
- package/src/internal/funding/SourceObservation.test.ts +301 -0
- package/src/internal/funding/SourceObservation.ts +267 -0
- package/src/internal/funding/Subsidy.ts +2 -2
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/relay.test.ts +94 -4
- package/src/internal/funding/providers/relay.ts +533 -27
- package/dist/apps/data/routes/funding.d.ts.map +0 -1
- package/dist/apps/data/routes/funding.js +0 -1557
- package/dist/apps/data/routes/funding.js.map +0 -1
- package/src/apps/data/routes/funding.test.ts +0 -2994
- package/src/apps/data/routes/funding.ts +0 -1821
|
@@ -77,6 +77,180 @@ describe('listByAddress', () => {
|
|
|
77
77
|
})
|
|
78
78
|
})
|
|
79
79
|
|
|
80
|
+
describe('listByDepositAddress', () => {
|
|
81
|
+
const seedAddressDeposit = (address: Awaited<ReturnType<typeof seedAddress>>, index: number) => {
|
|
82
|
+
const sourceTransactionHash = `fe${index.toString(16).padStart(62, '0')}`
|
|
83
|
+
return Deposit.create(db, {
|
|
84
|
+
now: new Date(Date.UTC(2026, 1, 2, 0, 0, index)),
|
|
85
|
+
providerRequestId: `relay_deposit_address_request_${index}`,
|
|
86
|
+
snapshot: TestFunding.depositSnapshot(address.id, {
|
|
87
|
+
provider: address.snapshot.provider,
|
|
88
|
+
sourceTransactionHashes: [sourceTransactionHash],
|
|
89
|
+
}),
|
|
90
|
+
sourceTransactionHash,
|
|
91
|
+
sourceTransferIndex: 0,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test('pages one reusable address across provider records newest-first', async () => {
|
|
96
|
+
const depositAddress = 'TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE'
|
|
97
|
+
const firstAddress = await seedAddress({
|
|
98
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
99
|
+
address: depositAddress,
|
|
100
|
+
provider: { id: 'relay_deposit_address_first', name: 'Relay' },
|
|
101
|
+
}),
|
|
102
|
+
})
|
|
103
|
+
const secondAddress = await seedAddress({
|
|
104
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
105
|
+
address: depositAddress,
|
|
106
|
+
provider: { id: 'relay_deposit_address_second', name: 'Relay' },
|
|
107
|
+
subsidize: true,
|
|
108
|
+
}),
|
|
109
|
+
})
|
|
110
|
+
const first = await seedAddressDeposit(firstAddress, 1)
|
|
111
|
+
const second = await seedAddressDeposit(secondAddress, 2)
|
|
112
|
+
const third = await seedAddressDeposit(firstAddress, 3)
|
|
113
|
+
const unrelatedAddress = await seedAddress({
|
|
114
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
115
|
+
address: 'TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3',
|
|
116
|
+
provider: { id: 'relay_deposit_address_unrelated', name: 'Relay' },
|
|
117
|
+
recipient: `0x${'fe'.repeat(20)}`,
|
|
118
|
+
}),
|
|
119
|
+
})
|
|
120
|
+
await seedAddressDeposit(unrelatedAddress, 4)
|
|
121
|
+
|
|
122
|
+
const page = await FundingDeposits.listByDepositAddress(db, {
|
|
123
|
+
depositAddress,
|
|
124
|
+
limit: 2,
|
|
125
|
+
owner,
|
|
126
|
+
})
|
|
127
|
+
expect(page.map((row) => row.id)).toEqual([third.id, second.id])
|
|
128
|
+
const rest = await FundingDeposits.listByDepositAddress(db, {
|
|
129
|
+
cursor: { createdAt: second.createdAt, id: second.id },
|
|
130
|
+
depositAddress,
|
|
131
|
+
limit: 2,
|
|
132
|
+
owner,
|
|
133
|
+
})
|
|
134
|
+
expect(rest.map((row) => row.id)).toEqual([first.id])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('narrows project-attributed owners to their project', async () => {
|
|
138
|
+
const depositAddress = 'TWd4WrZ9wn84f5x1hZhL4DHvk738ns5jwb'
|
|
139
|
+
const address = await seedAddress({
|
|
140
|
+
projectId: 'prj_deposit',
|
|
141
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
142
|
+
address: depositAddress,
|
|
143
|
+
provider: { id: 'relay_deposit_address_project', name: 'Relay' },
|
|
144
|
+
}),
|
|
145
|
+
})
|
|
146
|
+
await seedAddressDeposit(address, 5)
|
|
147
|
+
|
|
148
|
+
expect(
|
|
149
|
+
await FundingDeposits.listByDepositAddress(db, {
|
|
150
|
+
depositAddress,
|
|
151
|
+
limit: 10,
|
|
152
|
+
owner: { ...owner, projectId: 'prj_other' },
|
|
153
|
+
}),
|
|
154
|
+
).toEqual([])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('normalizes EVM address casing', async () => {
|
|
158
|
+
const depositAddress = `0x${'Ab'.repeat(20)}`
|
|
159
|
+
const source = TestFunding.transferSnapshot()
|
|
160
|
+
const address = await seedAddress({
|
|
161
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
162
|
+
address: depositAddress,
|
|
163
|
+
provider: { id: 'relay_deposit_address_evm', name: 'Relay' },
|
|
164
|
+
recipient: `0x${'fd'.repeat(20)}`,
|
|
165
|
+
refundAddress: depositAddress,
|
|
166
|
+
sourceChain: source.sourceChain,
|
|
167
|
+
sourceToken: source.sourceToken,
|
|
168
|
+
}),
|
|
169
|
+
})
|
|
170
|
+
const record = await seedAddressDeposit(address, 6)
|
|
171
|
+
|
|
172
|
+
const rows = await FundingDeposits.listByDepositAddress(db, {
|
|
173
|
+
depositAddress,
|
|
174
|
+
limit: 10,
|
|
175
|
+
owner,
|
|
176
|
+
})
|
|
177
|
+
expect(rows.map((row) => row.id)).toEqual([record.id])
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe('listByRecipient', () => {
|
|
182
|
+
test('pages recipient deposits across addresses and hides foreign ownership', async () => {
|
|
183
|
+
const recipient = '0xABcdEFABcdEFabcdEfAbCdefabcdeFABcDEFabCD'
|
|
184
|
+
const firstAddress = await seedAddress({
|
|
185
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
186
|
+
provider: { id: 'relay_recipient_first', name: 'Relay' },
|
|
187
|
+
recipient,
|
|
188
|
+
}),
|
|
189
|
+
})
|
|
190
|
+
const secondAddress = await seedAddress({
|
|
191
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
192
|
+
provider: { id: 'relay_recipient_second', name: 'Relay' },
|
|
193
|
+
recipient,
|
|
194
|
+
subsidize: true,
|
|
195
|
+
}),
|
|
196
|
+
})
|
|
197
|
+
const first = await seedDeposit(firstAddress)
|
|
198
|
+
const second = await seedDeposit(secondAddress)
|
|
199
|
+
const third = await seedDeposit(firstAddress)
|
|
200
|
+
const unrelatedAddress = await seedAddress()
|
|
201
|
+
await seedDeposit(unrelatedAddress)
|
|
202
|
+
const foreignAddress = await seedAddress({
|
|
203
|
+
orgId: 'org_other',
|
|
204
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
205
|
+
provider: { id: 'relay_recipient_foreign', name: 'Relay' },
|
|
206
|
+
recipient,
|
|
207
|
+
}),
|
|
208
|
+
})
|
|
209
|
+
await seedDeposit(foreignAddress)
|
|
210
|
+
|
|
211
|
+
const page = await FundingDeposits.listByRecipient(db, {
|
|
212
|
+
limit: 2,
|
|
213
|
+
owner,
|
|
214
|
+
recipient: recipient.toLowerCase(),
|
|
215
|
+
})
|
|
216
|
+
expect(page.map((row) => row.id)).toEqual([third.id, second.id])
|
|
217
|
+
const rest = await FundingDeposits.listByRecipient(db, {
|
|
218
|
+
cursor: { createdAt: second.createdAt, id: second.id },
|
|
219
|
+
limit: 2,
|
|
220
|
+
owner,
|
|
221
|
+
recipient: recipient.toLowerCase(),
|
|
222
|
+
})
|
|
223
|
+
expect(rest.map((row) => row.id)).toEqual([first.id])
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('narrows project-attributed owners to their project', async () => {
|
|
227
|
+
const recipient = `0x${'ab'.repeat(20)}`
|
|
228
|
+
const firstAddress = await seedAddress({
|
|
229
|
+
projectId: 'prj_deposit',
|
|
230
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
231
|
+
provider: { id: 'relay_recipient_project', name: 'Relay' },
|
|
232
|
+
recipient,
|
|
233
|
+
}),
|
|
234
|
+
})
|
|
235
|
+
const secondAddress = await seedAddress({
|
|
236
|
+
projectId: 'prj_other',
|
|
237
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
238
|
+
provider: { id: 'relay_recipient_other_project', name: 'Relay' },
|
|
239
|
+
recipient,
|
|
240
|
+
}),
|
|
241
|
+
})
|
|
242
|
+
const first = await seedDeposit(firstAddress)
|
|
243
|
+
await seedDeposit(secondAddress)
|
|
244
|
+
|
|
245
|
+
const rows = await FundingDeposits.listByRecipient(db, {
|
|
246
|
+
limit: 10,
|
|
247
|
+
owner: { ...owner, projectId: 'prj_deposit' },
|
|
248
|
+
recipient,
|
|
249
|
+
})
|
|
250
|
+
expect(rows.map((row) => row.id)).toEqual([first.id])
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
80
254
|
describe('getOwned', () => {
|
|
81
255
|
test('returns owned deposits and hides foreign ownership', async () => {
|
|
82
256
|
const address = await seedAddress()
|
|
@@ -227,6 +401,71 @@ describe('update', () => {
|
|
|
227
401
|
})
|
|
228
402
|
})
|
|
229
403
|
|
|
404
|
+
describe('recordRequestObservation', () => {
|
|
405
|
+
test('attaches the first poll and webhook timestamps without changing material state', async () => {
|
|
406
|
+
const address = await seedAddress()
|
|
407
|
+
const record = await seedDeposit(address, { providerRequestId: 'relay_observed' })
|
|
408
|
+
await FundingDeposits.recordRequestObservation(db, {
|
|
409
|
+
depositAddressId: address.id,
|
|
410
|
+
pollObservedAt: '2026-01-02T00:00:02.000Z',
|
|
411
|
+
providerRequestId: 'relay_observed',
|
|
412
|
+
webhookReceivedAt: '2026-01-02T00:00:03.000Z',
|
|
413
|
+
})
|
|
414
|
+
await FundingDeposits.recordRequestObservation(db, {
|
|
415
|
+
depositAddressId: address.id,
|
|
416
|
+
pollObservedAt: '2026-01-02T00:00:01.000Z',
|
|
417
|
+
providerRequestId: 'relay_observed',
|
|
418
|
+
webhookReceivedAt: '2026-01-02T00:00:04.000Z',
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
expect(await FundingDeposits.get(db, record.id)).toMatchInlineSnapshot(
|
|
422
|
+
{
|
|
423
|
+
createdAt: expect.any(String),
|
|
424
|
+
depositAddressId: expect.any(String),
|
|
425
|
+
id: expect.any(String),
|
|
426
|
+
providerOutputToken: expect.any(Object),
|
|
427
|
+
snapshot: expect.any(Object),
|
|
428
|
+
updatedAt: expect.any(String),
|
|
429
|
+
},
|
|
430
|
+
`
|
|
431
|
+
{
|
|
432
|
+
"createdAt": Any<String>,
|
|
433
|
+
"depositAddressId": Any<String>,
|
|
434
|
+
"detectionTrigger": null,
|
|
435
|
+
"environment": "production",
|
|
436
|
+
"id": Any<String>,
|
|
437
|
+
"orgId": "org_deposits",
|
|
438
|
+
"pollObservedAt": "2026-01-02T00:00:01.000Z",
|
|
439
|
+
"projectId": null,
|
|
440
|
+
"providerOutputAmount": null,
|
|
441
|
+
"providerOutputToken": Any<Object>,
|
|
442
|
+
"providerRequestId": "relay_observed",
|
|
443
|
+
"providerRequestIds": [
|
|
444
|
+
"relay_observed",
|
|
445
|
+
],
|
|
446
|
+
"providerState": null,
|
|
447
|
+
"providerTransactionHashes": [],
|
|
448
|
+
"providerTransferIndex": 0,
|
|
449
|
+
"retryState": null,
|
|
450
|
+
"settlementTransaction": null,
|
|
451
|
+
"settlementTransactionHash": null,
|
|
452
|
+
"snapshot": Any<Object>,
|
|
453
|
+
"sourceChainId": "tron:0x2b6653dc",
|
|
454
|
+
"sourceTransactionHash": "0000000000000000000000000000000000000000000000000000000000000011",
|
|
455
|
+
"sourceTransferIndex": 0,
|
|
456
|
+
"status": "detected",
|
|
457
|
+
"statusReason": null,
|
|
458
|
+
"subsidyAmount": null,
|
|
459
|
+
"tempoGasPaid": null,
|
|
460
|
+
"updatedAt": Any<String>,
|
|
461
|
+
"version": 1,
|
|
462
|
+
"webhookReceivedAt": "2026-01-02T00:00:03.000Z",
|
|
463
|
+
}
|
|
464
|
+
`,
|
|
465
|
+
)
|
|
466
|
+
})
|
|
467
|
+
})
|
|
468
|
+
|
|
230
469
|
describe('toPublic', () => {
|
|
231
470
|
test('omits private reconciliation and settlement state', async () => {
|
|
232
471
|
const address = await seedAddress()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { sql, type JSONColumnType, type Selectable } from 'kysely'
|
|
2
2
|
|
|
3
3
|
import type * as Db from '../Db.js'
|
|
4
4
|
import type * as db_Schema from '../Schema.js'
|
|
@@ -40,6 +40,63 @@ export function insert(db: Db.Db, record: Record): Promise<Record> {
|
|
|
40
40
|
.executeTakeFirstOrThrow()
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** Inserts a chain-observed source transfer or returns its existing deposit. */
|
|
44
|
+
export async function insertOrGetSource(
|
|
45
|
+
db: Db.Db,
|
|
46
|
+
record: Record & { sourceTransferIndex: number },
|
|
47
|
+
): Promise<insertOrGetSource.Result> {
|
|
48
|
+
const inserted = await db.kysely
|
|
49
|
+
.insertInto('funding_deposits')
|
|
50
|
+
.values({
|
|
51
|
+
...record,
|
|
52
|
+
providerRequestIds: JSON.stringify(record.providerRequestIds),
|
|
53
|
+
providerTransactionHashes: JSON.stringify(record.providerTransactionHashes),
|
|
54
|
+
})
|
|
55
|
+
.onConflict((oc) =>
|
|
56
|
+
oc.columns(['depositAddressId', 'sourceTransactionHash', 'sourceTransferIndex']).doNothing(),
|
|
57
|
+
)
|
|
58
|
+
.returningAll()
|
|
59
|
+
.executeTakeFirst()
|
|
60
|
+
if (inserted) return { record: inserted, type: 'created' }
|
|
61
|
+
const existing = await db.kysely
|
|
62
|
+
.selectFrom('funding_deposits')
|
|
63
|
+
.selectAll()
|
|
64
|
+
.where('depositAddressId', '=', record.depositAddressId)
|
|
65
|
+
.where('sourceTransactionHash', '=', record.sourceTransactionHash)
|
|
66
|
+
.where('sourceTransferIndex', '=', record.sourceTransferIndex)
|
|
67
|
+
.executeTakeFirstOrThrow()
|
|
68
|
+
return { record: existing, type: 'existing' }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export declare namespace insertOrGetSource {
|
|
72
|
+
/** Result of idempotently persisting one verified source transfer. */
|
|
73
|
+
type Result = { record: Record; type: 'created' | 'existing' }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Serializes source attribution for one address and transaction. */
|
|
77
|
+
export function withSourceTransaction<const result>(
|
|
78
|
+
db: Db.Db,
|
|
79
|
+
options: withSourceTransaction.Options<result>,
|
|
80
|
+
): Promise<result> {
|
|
81
|
+
return db.transaction(async (tx) => {
|
|
82
|
+
const key = `${options.depositAddressId}:${options.sourceTransactionHash.toLowerCase()}`
|
|
83
|
+
await sql`SELECT pg_advisory_xact_lock(hashtextextended(${key}, 0))`.execute(tx.kysely)
|
|
84
|
+
return options.fn(tx)
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export declare namespace withSourceTransaction {
|
|
89
|
+
/** Source identity and transaction-scoped work protected by its advisory lock. */
|
|
90
|
+
type Options<result> = {
|
|
91
|
+
/** Funding deposit address that received the transfer. */
|
|
92
|
+
depositAddressId: string
|
|
93
|
+
/** Database work serialized for the source transaction. */
|
|
94
|
+
fn: (db: Db.Db) => Promise<result>
|
|
95
|
+
/** Source transaction whose deposit attribution must not race. */
|
|
96
|
+
sourceTransactionHash: string
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
43
100
|
/** Gets deposits created from one provider-observed source transaction. */
|
|
44
101
|
export function listByProviderObservation(
|
|
45
102
|
db: Db.Db,
|
|
@@ -67,6 +124,45 @@ export declare namespace listByProviderObservation {
|
|
|
67
124
|
}
|
|
68
125
|
}
|
|
69
126
|
|
|
127
|
+
/** Attaches first-observation timestamps to every deposit from one provider request. */
|
|
128
|
+
export function recordRequestObservation(
|
|
129
|
+
db: Db.Db,
|
|
130
|
+
options: recordRequestObservation.Options,
|
|
131
|
+
): Promise<Record[]> {
|
|
132
|
+
return db.kysely
|
|
133
|
+
.updateTable('funding_deposits')
|
|
134
|
+
.set({
|
|
135
|
+
...(options.pollObservedAt
|
|
136
|
+
? {
|
|
137
|
+
pollObservedAt: sql<string>`LEAST(COALESCE(poll_observed_at, ${options.pollObservedAt}), ${options.pollObservedAt})`,
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
140
|
+
...(options.webhookReceivedAt
|
|
141
|
+
? {
|
|
142
|
+
webhookReceivedAt: sql<string>`LEAST(COALESCE(webhook_received_at, ${options.webhookReceivedAt}), ${options.webhookReceivedAt})`,
|
|
143
|
+
}
|
|
144
|
+
: {}),
|
|
145
|
+
})
|
|
146
|
+
.where('depositAddressId', '=', options.depositAddressId)
|
|
147
|
+
.where('providerRequestId', '=', options.providerRequestId)
|
|
148
|
+
.returningAll()
|
|
149
|
+
.execute()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export declare namespace recordRequestObservation {
|
|
153
|
+
/** First observations associated with one provider request. */
|
|
154
|
+
type Options = {
|
|
155
|
+
/** Funding deposit address id (`fda_…`). */
|
|
156
|
+
depositAddressId: string
|
|
157
|
+
/** When polling first observed the provider request. */
|
|
158
|
+
pollObservedAt?: string | undefined
|
|
159
|
+
/** Provider request identifier. */
|
|
160
|
+
providerRequestId: string
|
|
161
|
+
/** When Tempo first received an authenticated provider webhook. */
|
|
162
|
+
webhookReceivedAt?: string | undefined
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
70
166
|
/** Gets deposits associated with one address and source transaction. */
|
|
71
167
|
export function listBySourceTransaction(
|
|
72
168
|
db: Db.Db,
|
|
@@ -91,6 +187,32 @@ export declare namespace listBySourceTransaction {
|
|
|
91
187
|
}
|
|
92
188
|
}
|
|
93
189
|
|
|
190
|
+
/** Lists nonterminal source transfers that await provider attribution. */
|
|
191
|
+
export function listUnattributed(db: Db.Db, depositAddressId: string): Promise<Record[]> {
|
|
192
|
+
return db.kysely
|
|
193
|
+
.selectFrom('funding_deposits')
|
|
194
|
+
.selectAll()
|
|
195
|
+
.where('depositAddressId', '=', depositAddressId)
|
|
196
|
+
.where('providerRequestId', 'is', null)
|
|
197
|
+
.where('status', 'not in', ['completed', 'refunded'])
|
|
198
|
+
.orderBy('createdAt', 'asc')
|
|
199
|
+
.orderBy('id', 'asc')
|
|
200
|
+
.execute()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Returns whether an address has a source transfer awaiting provider attribution. */
|
|
204
|
+
export async function hasUnattributed(db: Db.Db, depositAddressId: string): Promise<boolean> {
|
|
205
|
+
return Boolean(
|
|
206
|
+
await db.kysely
|
|
207
|
+
.selectFrom('funding_deposits')
|
|
208
|
+
.select('id')
|
|
209
|
+
.where('depositAddressId', '=', depositAddressId)
|
|
210
|
+
.where('providerRequestId', 'is', null)
|
|
211
|
+
.limit(1)
|
|
212
|
+
.executeTakeFirst(),
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
94
216
|
/** Gets a funding deposit without ownership scoping. */
|
|
95
217
|
export function get(db: Db.Db, id: string): Promise<Record | undefined> {
|
|
96
218
|
return db.kysely
|
|
@@ -155,6 +277,97 @@ export declare namespace listByAddress {
|
|
|
155
277
|
}
|
|
156
278
|
}
|
|
157
279
|
|
|
280
|
+
/** Lists deposits for one reusable source-chain address newest-first. */
|
|
281
|
+
export function listByDepositAddress(
|
|
282
|
+
db: Db.Db,
|
|
283
|
+
options: listByDepositAddress.Options,
|
|
284
|
+
): Promise<Record[]> {
|
|
285
|
+
const { cursor, depositAddress, limit, owner } = options
|
|
286
|
+
const address = /^0x[0-9a-fA-F]{40}$/.test(depositAddress)
|
|
287
|
+
? depositAddress.toLowerCase()
|
|
288
|
+
: depositAddress
|
|
289
|
+
let query = db.kysely
|
|
290
|
+
.selectFrom('funding_deposits')
|
|
291
|
+
.innerJoin(
|
|
292
|
+
'funding_deposit_addresses',
|
|
293
|
+
'funding_deposit_addresses.id',
|
|
294
|
+
'funding_deposits.depositAddressId',
|
|
295
|
+
)
|
|
296
|
+
.selectAll('funding_deposits')
|
|
297
|
+
.where('funding_deposit_addresses.address', '=', address)
|
|
298
|
+
.where('funding_deposit_addresses.environment', '=', owner.environment)
|
|
299
|
+
.where('funding_deposit_addresses.orgId', '=', owner.orgId)
|
|
300
|
+
.where('funding_deposits.environment', '=', owner.environment)
|
|
301
|
+
.where('funding_deposits.orgId', '=', owner.orgId)
|
|
302
|
+
if (owner.projectId !== undefined)
|
|
303
|
+
query = query
|
|
304
|
+
.where('funding_deposit_addresses.projectId', '=', owner.projectId)
|
|
305
|
+
.where('funding_deposits.projectId', '=', owner.projectId)
|
|
306
|
+
if (cursor)
|
|
307
|
+
query = query.where((eb) =>
|
|
308
|
+
eb.or([
|
|
309
|
+
eb('funding_deposits.createdAt', '<', cursor.createdAt),
|
|
310
|
+
eb.and([
|
|
311
|
+
eb('funding_deposits.createdAt', '=', cursor.createdAt),
|
|
312
|
+
eb('funding_deposits.id', '<', cursor.id),
|
|
313
|
+
]),
|
|
314
|
+
]),
|
|
315
|
+
)
|
|
316
|
+
return query
|
|
317
|
+
.orderBy('funding_deposits.createdAt', 'desc')
|
|
318
|
+
.orderBy('funding_deposits.id', 'desc')
|
|
319
|
+
.limit(limit)
|
|
320
|
+
.execute()
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export declare namespace listByDepositAddress {
|
|
324
|
+
/** Deposit address filter, ownership, and paging options. */
|
|
325
|
+
type Options = {
|
|
326
|
+
/** Exclusive lower bound from the previous page. */
|
|
327
|
+
cursor?: listByAddress.Cursor | undefined
|
|
328
|
+
/** Reusable source-chain address that received the funds. */
|
|
329
|
+
depositAddress: string
|
|
330
|
+
/** Maximum rows to return. */
|
|
331
|
+
limit: number
|
|
332
|
+
/** Ownership filter derived from the authenticated API key. */
|
|
333
|
+
owner: Owner
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Lists one recipient's owner-visible deposits newest-first. */
|
|
338
|
+
export function listByRecipient(db: Db.Db, options: listByRecipient.Options): Promise<Record[]> {
|
|
339
|
+
const { cursor, limit, owner, recipient } = options
|
|
340
|
+
let query = db.kysely
|
|
341
|
+
.selectFrom('funding_deposits')
|
|
342
|
+
.selectAll()
|
|
343
|
+
.where('environment', '=', owner.environment)
|
|
344
|
+
.where('orgId', '=', owner.orgId)
|
|
345
|
+
.where(sql<boolean>`lower(snapshot->>'recipient') = ${recipient.toLowerCase()}`)
|
|
346
|
+
if (owner.projectId !== undefined) query = query.where('projectId', '=', owner.projectId)
|
|
347
|
+
if (cursor)
|
|
348
|
+
query = query.where((eb) =>
|
|
349
|
+
eb.or([
|
|
350
|
+
eb('createdAt', '<', cursor.createdAt),
|
|
351
|
+
eb.and([eb('createdAt', '=', cursor.createdAt), eb('id', '<', cursor.id)]),
|
|
352
|
+
]),
|
|
353
|
+
)
|
|
354
|
+
return query.orderBy('createdAt', 'desc').orderBy('id', 'desc').limit(limit).execute()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export declare namespace listByRecipient {
|
|
358
|
+
/** Recipient filter, ownership, and paging options. */
|
|
359
|
+
type Options = {
|
|
360
|
+
/** Exclusive lower bound from the previous page. */
|
|
361
|
+
cursor?: listByAddress.Cursor | undefined
|
|
362
|
+
/** Maximum rows to return. */
|
|
363
|
+
limit: number
|
|
364
|
+
/** Ownership filter derived from the authenticated API key. */
|
|
365
|
+
owner: Owner
|
|
366
|
+
/** Tempo account that receives completed deposits. */
|
|
367
|
+
recipient: string
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
158
371
|
/** Applies a version-guarded deposit update. */
|
|
159
372
|
export function update(db: Db.Db, options: update.Options): Promise<Record | undefined> {
|
|
160
373
|
return db.kysely
|