tapimo 0.11.0 → 0.12.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/dist/App.d.ts +2296 -1010
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +2 -2
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +15 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +7 -0
- package/dist/Assets.js.map +1 -1
- package/dist/admin/App.d.ts +53 -4
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +4 -4
- package/dist/admin/apps/earn-vaults.d.ts +114 -0
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +59 -6
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +4 -2
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +9 -9
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +151 -151
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +1699 -749
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +56 -7
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +62 -0
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
- package/dist/apps/data/ZoneSelection.js +44 -0
- package/dist/apps/data/ZoneSelection.js.map +1 -0
- package/dist/apps/data/routes/activities.d.ts +2367 -4
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +368 -87
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +471 -340
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +28 -28
- package/dist/apps/data/routes/balances.js +1 -1
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +4 -4
- package/dist/apps/data/routes/blocks.js +1 -1
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +176 -176
- package/dist/apps/data/routes/earn.d.ts +131 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +157 -25
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +825 -119
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +16 -13
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +9 -11
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +34 -34
- package/dist/apps/data/routes/receipts.d.ts +230 -230
- package/dist/apps/data/routes/receipts.js +1 -1
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.js +24 -10
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +16 -15
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +14 -0
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +34 -15
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.js +1 -1
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +7 -9
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +4 -4
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +2 -2
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +30 -30
- package/dist/apps/funding/App.d.ts +27 -27
- package/dist/apps/funding/routes/chains.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.js +5 -4
- package/dist/apps/funding/routes/chains.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +16 -16
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +8 -8
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts +13 -13
- package/dist/apps/funding/routes/providers.d.ts.map +1 -1
- package/dist/apps/funding/routes/providers.js +3 -2
- package/dist/apps/funding/routes/providers.js.map +1 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
- package/dist/apps/funding/routes/quotes.js +2 -2
- package/dist/apps/funding/routes/quotes.js.map +1 -1
- package/dist/apps/management/App.d.ts +627 -293
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +19 -6
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +114 -54
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +76 -30
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +103 -30
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +91 -32
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +33 -33
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +23 -12
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +46 -13
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +79 -29
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +86 -41
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +100 -41
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +5 -5
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +14 -10
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +93 -72
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +37 -18
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +48 -8
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +48 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +90 -28
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +72 -21
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +8 -5
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +78 -78
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +41 -31
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +34 -6
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +36 -17
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +147 -130
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +80 -35
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +12 -0
- package/dist/apps/mpp/internal/Adapter.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 +40 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +23 -1
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +19 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +5 -0
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +10 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
- package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueCompletions.js +32 -0
- package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
- package/dist/db/tables/webhookQueueEvents.d.ts +14 -20
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +68 -47
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +16 -5
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +29 -5
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +8 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +13 -3
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +11 -7
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +41 -2
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +167 -0
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +26 -22
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +128 -39
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +13 -8
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +16 -5
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +5 -7
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +6 -7
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +15 -6
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +45 -15
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +2 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +16 -4
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +29 -12
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +37 -20
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +2 -2
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +9 -8
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/DepositAddress.d.ts +4 -4
- package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
- package/dist/internal/funding/DepositAddress.js +9 -8
- package/dist/internal/funding/DepositAddress.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +13 -12
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +19 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
- package/dist/internal/funding/providers/stargate.js +143 -0
- package/dist/internal/funding/providers/stargate.js.map +1 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +2 -2
- package/src/App.test.ts +48 -0
- package/src/App.ts +4 -2
- package/src/Assets.test.ts +18 -0
- package/src/Assets.ts +22 -0
- package/src/Client.test-d.ts +265 -16
- package/src/admin/apps/earn-vaults.test.ts +143 -0
- package/src/admin/apps/earn-vaults.ts +83 -6
- package/src/admin/apps/organizations.ts +6 -2
- package/src/admin/ui/src/routes/api-keys.tsx +36 -0
- package/src/admin/ui.generated.ts +151 -189
- package/src/apps/data/App.test.ts +31 -0
- package/src/apps/data/App.ts +56 -8
- package/src/apps/data/ZoneSelection.test.ts +196 -0
- package/src/apps/data/ZoneSelection.ts +108 -0
- package/src/apps/data/routes/activities.test.ts +328 -1
- package/src/apps/data/routes/activities.ts +498 -93
- package/src/apps/data/routes/balances.ts +1 -1
- package/src/apps/data/routes/blocks.ts +1 -1
- package/src/apps/data/routes/earn.test.ts +183 -23
- package/src/apps/data/routes/earn.ts +225 -29
- package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
- package/src/apps/data/routes/exchanges.test.ts +31 -1
- package/src/apps/data/routes/exchanges.ts +19 -15
- package/src/apps/data/routes/fee-amm.ts +18 -11
- package/src/apps/data/routes/receipts.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +75 -0
- package/src/apps/data/routes/rpc.ts +23 -9
- package/src/apps/data/routes/tokenlist.ts +21 -18
- package/src/apps/data/routes/tokens.test.ts +29 -0
- package/src/apps/data/routes/tokens.ts +38 -15
- package/src/apps/data/routes/transactions.ts +1 -1
- package/src/apps/data/routes/transfers.test.ts +3 -3
- package/src/apps/data/routes/transfers.ts +12 -9
- package/src/apps/data/routes/webhooks.ts +6 -2
- package/src/apps/data/routes/zones.test.ts +19 -13
- package/src/apps/funding/routes/chains.test.ts +211 -7
- package/src/apps/funding/routes/chains.ts +19 -12
- package/src/apps/funding/routes/deposit-addresses.test.ts +394 -0
- package/src/apps/funding/routes/deposit-addresses.ts +84 -72
- package/src/apps/funding/routes/providers.ts +9 -5
- package/src/apps/funding/routes/quotes.test.ts +46 -0
- package/src/apps/funding/routes/quotes.ts +8 -5
- package/src/apps/funding/routes/transfers.test.ts +35 -32
- package/src/apps/management/Billing.test.ts +26 -0
- package/src/apps/management/Billing.ts +24 -7
- package/src/apps/management/routes/api-keys.test.ts +178 -0
- package/src/apps/management/routes/api-keys.ts +203 -144
- package/src/apps/management/routes/billing.test.ts +209 -0
- package/src/apps/management/routes/billing.ts +254 -177
- package/src/apps/management/routes/faucet.test.ts +39 -0
- package/src/apps/management/routes/faucet.ts +75 -41
- package/src/apps/management/routes/invitations.test.ts +216 -0
- package/src/apps/management/routes/invitations.ts +186 -109
- package/src/apps/management/routes/invite-links.test.ts +263 -1
- package/src/apps/management/routes/invite-links.ts +295 -214
- package/src/apps/management/routes/me.test.ts +49 -0
- package/src/apps/management/routes/me.ts +42 -32
- package/src/apps/management/routes/members.test.ts +124 -1
- package/src/apps/management/routes/members.ts +81 -51
- package/src/apps/management/routes/orgs.test.ts +176 -0
- package/src/apps/management/routes/orgs.ts +101 -59
- package/src/apps/management/routes/projects.test.ts +183 -0
- package/src/apps/management/routes/projects.ts +124 -60
- package/src/apps/management/routes/scopes.test.ts +44 -0
- package/src/apps/management/routes/scopes.ts +37 -28
- package/src/apps/management/routes/usage.test.ts +92 -0
- package/src/apps/management/routes/usage.ts +186 -151
- package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
- package/src/apps/management/routes/verified-token-requests.ts +166 -133
- package/src/apps/management/routes/webhooks.test.ts +234 -0
- package/src/apps/management/routes/webhooks.ts +191 -78
- package/src/apps/mpp/internal/Adapter.ts +13 -1
- package/src/db/Db.test.ts +41 -0
- package/src/db/Db.ts +62 -0
- package/src/db/Schema.ts +28 -3
- package/src/db/tables/earnVaults.test.ts +22 -0
- package/src/db/tables/earnVaults.ts +11 -1
- package/src/db/tables/webhookDeliveries.ts +13 -0
- package/src/db/tables/webhookQueueCompletions.ts +55 -0
- package/src/db/tables/webhookQueueEvents.ts +95 -61
- package/src/db/tables/webhookSubscriptions.ts +45 -6
- package/src/internal/Auth.ts +23 -5
- package/src/internal/Cache.ts +14 -7
- package/src/internal/EarnVaults.capabilities.test.ts +33 -0
- package/src/internal/EarnVaults.test.ts +79 -0
- package/src/internal/EarnVaults.ts +277 -0
- package/src/internal/OpenApi.ts +165 -49
- package/src/internal/Response.test.ts +95 -0
- package/src/internal/Response.ts +40 -2
- package/src/internal/Schema.test.ts +16 -0
- package/src/internal/Schema.ts +7 -7
- package/src/internal/Store.test.ts +186 -0
- package/src/internal/Store.ts +57 -17
- package/src/internal/Tidx.test.ts +19 -0
- package/src/internal/Tidx.ts +16 -4
- package/src/internal/Webhooks.test.ts +122 -8
- package/src/internal/Webhooks.ts +59 -29
- package/src/internal/funding/Deposit.ts +71 -64
- package/src/internal/funding/DepositAddress.ts +50 -43
- package/src/internal/funding/Provider.ts +164 -142
- package/src/internal/funding/providers/index.test.ts +185 -0
- package/src/internal/funding/providers/stargate.ts +184 -0
- package/src/providers/index.test.ts +1 -0
- package/src/providers/index.ts +9 -0
|
@@ -348,6 +348,41 @@ describe('recordSuccess / recordFailure', () => {
|
|
|
348
348
|
expect(read?.lastDeliveryAt).toBe('2026-01-01T00:00:00.000Z')
|
|
349
349
|
})
|
|
350
350
|
|
|
351
|
+
test('recordSuccess refreshes the stamp at most once per precision window', async () => {
|
|
352
|
+
const db = TestApp.database()
|
|
353
|
+
const created = await Webhooks.createSubscription(db, createInput())
|
|
354
|
+
const stamped = await Webhooks.recordSuccess(db, created, { now: fixedNow })
|
|
355
|
+
expect(stamped.lastDeliveryAt).toBe(fixedNow().toISOString())
|
|
356
|
+
|
|
357
|
+
// A success moments later has nothing to move: the counter is already zero
|
|
358
|
+
// and the stamp is inside its window, so the row is never rewritten.
|
|
359
|
+
const soon = new Date(fixedNow().getTime() + 1_000)
|
|
360
|
+
await Webhooks.recordSuccess(db, stamped, { now: () => soon })
|
|
361
|
+
const unmoved = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
362
|
+
expect(unmoved?.lastDeliveryAt, 'the stored stamp must not move').toBe(fixedNow().toISOString())
|
|
363
|
+
|
|
364
|
+
// Past any plausible window; the exact bound is internal.
|
|
365
|
+
const later = new Date(fixedNow().getTime() + 3_600_000)
|
|
366
|
+
await Webhooks.recordSuccess(db, stamped, { now: () => later })
|
|
367
|
+
const moved = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
368
|
+
expect(moved?.lastDeliveryAt).toBe(later.toISOString())
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
test('recordSuccess always resets a non-zero failure counter', async () => {
|
|
372
|
+
const db = TestApp.database()
|
|
373
|
+
const created = await Webhooks.createSubscription(db, createInput())
|
|
374
|
+
// Stamp first, so only the counter is out of date: the reset must not be
|
|
375
|
+
// suppressed by a fresh stamp.
|
|
376
|
+
const stamped = await Webhooks.recordSuccess(db, created, { now: fixedNow })
|
|
377
|
+
const failed = await Webhooks.recordFailure(db, stamped, { now: fixedNow })
|
|
378
|
+
expect(failed.failureCount).toBe(1)
|
|
379
|
+
|
|
380
|
+
await Webhooks.recordSuccess(db, failed, { now: fixedNow })
|
|
381
|
+
|
|
382
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, created.id)
|
|
383
|
+
expect(read?.failureCount, 'a pending reset must never be skipped').toBe(0)
|
|
384
|
+
})
|
|
385
|
+
|
|
351
386
|
test('recordFailure auto-disables at the threshold and drops from the active index', async () => {
|
|
352
387
|
const db = TestApp.database()
|
|
353
388
|
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
@@ -375,6 +410,37 @@ describe('recordSuccess / recordFailure', () => {
|
|
|
375
410
|
await Webhooks.listActiveForChain(db, { chainId: 4217, eventTypes: ['token:transfer'] }),
|
|
376
411
|
).toMatchInlineSnapshot(`[]`)
|
|
377
412
|
})
|
|
413
|
+
|
|
414
|
+
test('recordFailure leaves an already-disabled subscription alone at the cap', async () => {
|
|
415
|
+
const db = TestApp.database()
|
|
416
|
+
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
417
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: fixedNow })
|
|
418
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: fixedNow })
|
|
419
|
+
expect(subscription.status).toBe('disabled')
|
|
420
|
+
|
|
421
|
+
// A straggler failing after the disable has nothing to move: the row is
|
|
422
|
+
// never rewritten.
|
|
423
|
+
const later = new Date(fixedNow().getTime() + 1_000)
|
|
424
|
+
await Webhooks.recordFailure(db, subscription, { maxFailures: 2, now: () => later })
|
|
425
|
+
|
|
426
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, subscription.id)
|
|
427
|
+
expect(read?.failureCount, 'the stored counter must not move').toBe(2)
|
|
428
|
+
expect(read?.updatedAt, 'the stored stamp must not move').toBe(fixedNow().toISOString())
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
test('recordFailure re-disables a re-enabled subscription still at the cap', async () => {
|
|
432
|
+
const db = TestApp.database()
|
|
433
|
+
let subscription = await Webhooks.createSubscription(db, createInput())
|
|
434
|
+
subscription = await Webhooks.recordFailure(db, subscription, { maxFailures: 1, now: fixedNow })
|
|
435
|
+
expect(subscription.status).toBe('disabled')
|
|
436
|
+
|
|
437
|
+
await Webhooks.updateSubscription(db, apiKeyOwner, subscription.id, { status: 'active' })
|
|
438
|
+
|
|
439
|
+
// The counter still sits at the cap, so this failure must write again.
|
|
440
|
+
await Webhooks.recordFailure(db, subscription, { maxFailures: 1, now: fixedNow })
|
|
441
|
+
const read = await Webhooks.getSubscription(db, apiKeyOwner, subscription.id)
|
|
442
|
+
expect(read?.status).toBe('disabled')
|
|
443
|
+
})
|
|
378
444
|
})
|
|
379
445
|
|
|
380
446
|
describe('sign / verify', () => {
|
|
@@ -1015,7 +1081,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1015
1081
|
expect(await Webhooks.getQueueEvent(db, reference)).toBeNull()
|
|
1016
1082
|
})
|
|
1017
1083
|
|
|
1018
|
-
test('
|
|
1084
|
+
test('stages a replay once its completion has expired', async () => {
|
|
1019
1085
|
vi.useFakeTimers({ toFake: ['Date'] })
|
|
1020
1086
|
vi.setSystemTime(fixedNow())
|
|
1021
1087
|
try {
|
|
@@ -1031,9 +1097,9 @@ describe('ensureQueueEvents', () => {
|
|
|
1031
1097
|
}),
|
|
1032
1098
|
).toBe(true)
|
|
1033
1099
|
|
|
1034
|
-
// Past the dedupe window an unpruned
|
|
1100
|
+
// Past the dedupe window an unpruned completion must not suppress a
|
|
1035
1101
|
// replay.
|
|
1036
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1102
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1037
1103
|
const replay = await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }])
|
|
1038
1104
|
expect(replay.created).toBe(1)
|
|
1039
1105
|
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toMatchObject({
|
|
@@ -1044,7 +1110,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1044
1110
|
}
|
|
1045
1111
|
})
|
|
1046
1112
|
|
|
1047
|
-
test('
|
|
1113
|
+
test('stamps a fresh observation on a post-expiry replay', async () => {
|
|
1048
1114
|
vi.useFakeTimers({ toFake: ['Date'] })
|
|
1049
1115
|
vi.setSystemTime(fixedNow())
|
|
1050
1116
|
try {
|
|
@@ -1057,7 +1123,7 @@ describe('ensureQueueEvents', () => {
|
|
|
1057
1123
|
status: 'succeeded',
|
|
1058
1124
|
})
|
|
1059
1125
|
|
|
1060
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1126
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1061
1127
|
const observedAt = Date.now() - 250
|
|
1062
1128
|
await Webhooks.ensureQueueEvents(db, [{ envelope, subscription }], { observedAt })
|
|
1063
1129
|
|
|
@@ -1070,6 +1136,38 @@ describe('ensureQueueEvents', () => {
|
|
|
1070
1136
|
vi.useRealTimers()
|
|
1071
1137
|
}
|
|
1072
1138
|
})
|
|
1139
|
+
|
|
1140
|
+
test('suppresses only the completed obligation when staging a mixed batch', async () => {
|
|
1141
|
+
const db = TestApp.database()
|
|
1142
|
+
const done = await stagedObligation(db, 4)
|
|
1143
|
+
const claim = await Webhooks.claimQueueEvent(db, done.reference)
|
|
1144
|
+
if (claim.type !== 'claimed') throw new Error(`Unexpected claim: ${claim.type}`)
|
|
1145
|
+
await Webhooks.completeQueueEvent(db, done.reference, {
|
|
1146
|
+
claimedAt: claim.record.attemptingAt!,
|
|
1147
|
+
status: 'failed',
|
|
1148
|
+
})
|
|
1149
|
+
|
|
1150
|
+
const fresh = Webhooks.buildEnvelope({
|
|
1151
|
+
blockNumber: 124,
|
|
1152
|
+
createdAt: new Date(),
|
|
1153
|
+
data: { amount: '1' },
|
|
1154
|
+
logIndex: 5,
|
|
1155
|
+
subscription: done.subscription,
|
|
1156
|
+
})
|
|
1157
|
+
const batch = await Webhooks.ensureQueueEvents(db, [
|
|
1158
|
+
{ envelope: done.envelope, subscription: done.subscription },
|
|
1159
|
+
{ envelope: fresh, subscription: done.subscription },
|
|
1160
|
+
])
|
|
1161
|
+
|
|
1162
|
+
expect(batch.created).toBe(1)
|
|
1163
|
+
await expect(Webhooks.claimQueueEvent(db, done.reference)).resolves.toEqual({
|
|
1164
|
+
status: 'failed',
|
|
1165
|
+
type: 'terminal',
|
|
1166
|
+
})
|
|
1167
|
+
await expect(Webhooks.claimQueueEvent(db, batch.references[1]!)).resolves.toMatchObject({
|
|
1168
|
+
type: 'claimed',
|
|
1169
|
+
})
|
|
1170
|
+
})
|
|
1073
1171
|
})
|
|
1074
1172
|
|
|
1075
1173
|
describe('claimQueueEvent', () => {
|
|
@@ -1082,6 +1180,22 @@ describe('claimQueueEvent', () => {
|
|
|
1082
1180
|
vi.useRealTimers()
|
|
1083
1181
|
})
|
|
1084
1182
|
|
|
1183
|
+
test('reads an expired completion as missing', async () => {
|
|
1184
|
+
const db = TestApp.database()
|
|
1185
|
+
const { reference } = await stagedObligation(db)
|
|
1186
|
+
const claim = await Webhooks.claimQueueEvent(db, reference)
|
|
1187
|
+
if (claim.type !== 'claimed') throw new Error(`Unexpected claim: ${claim.type}`)
|
|
1188
|
+
await Webhooks.completeQueueEvent(db, reference, {
|
|
1189
|
+
claimedAt: claim.record.attemptingAt!,
|
|
1190
|
+
status: 'succeeded',
|
|
1191
|
+
})
|
|
1192
|
+
|
|
1193
|
+
// Matches a pruned terminal row: past the dedupe window the completion is
|
|
1194
|
+
// invisible everywhere, not only to staging.
|
|
1195
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1196
|
+
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toEqual({ type: 'missing' })
|
|
1197
|
+
})
|
|
1198
|
+
|
|
1085
1199
|
test('claims a due obligation once and reclaims after staleness', async () => {
|
|
1086
1200
|
const db = TestApp.database()
|
|
1087
1201
|
const { reference } = await stagedObligation(db)
|
|
@@ -1146,17 +1260,17 @@ describe('claimQueueEvent', () => {
|
|
|
1146
1260
|
|
|
1147
1261
|
// Pending obligations never expire; only terminal transitions stamp a
|
|
1148
1262
|
// dedupe deadline.
|
|
1149
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1263
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs * 3))
|
|
1150
1264
|
await expect(Webhooks.claimQueueEvent(db, reference)).resolves.toMatchObject({
|
|
1151
1265
|
type: 'claimed',
|
|
1152
1266
|
})
|
|
1153
1267
|
})
|
|
1154
1268
|
|
|
1155
|
-
test('
|
|
1269
|
+
test('leaves pending obligations claimable across a prune', async () => {
|
|
1156
1270
|
const db = TestApp.database()
|
|
1157
1271
|
const { reference } = await stagedObligation(db)
|
|
1158
1272
|
|
|
1159
|
-
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.
|
|
1273
|
+
vi.setSystemTime(new Date(fixedNow().getTime() + Webhooks.dedupeRetentionMs + 1_000))
|
|
1160
1274
|
await Webhooks.pruneExpired(db)
|
|
1161
1275
|
const claim = await Webhooks.claimQueueEvent(db, reference)
|
|
1162
1276
|
expect(claim.type).toBe('claimed')
|
package/src/internal/Webhooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Hash, Hex } from 'ox'
|
|
|
3
3
|
import type * as app_Webhooks from '../apps/data/routes/webhooks.js'
|
|
4
4
|
import type * as Db from '../db/Db.js'
|
|
5
5
|
import * as WebhookDeliveries from '../db/tables/webhookDeliveries.js'
|
|
6
|
+
import * as WebhookQueueCompletions from '../db/tables/webhookQueueCompletions.js'
|
|
6
7
|
import * as WebhookQueueEvents from '../db/tables/webhookQueueEvents.js'
|
|
7
8
|
import * as WebhookSubscriptions from '../db/tables/webhookSubscriptions.js'
|
|
8
9
|
import type * as Metrics from '../Metrics.js'
|
|
@@ -95,7 +96,8 @@ export type Subscription = {
|
|
|
95
96
|
filters: Record<string, unknown>
|
|
96
97
|
/** Subscription id (`wh_…`). */
|
|
97
98
|
id: string
|
|
98
|
-
/** ISO timestamp of the last successful delivery, if any.
|
|
99
|
+
/** ISO timestamp of the last successful delivery, if any. Display only:
|
|
100
|
+
* refreshed at most once per precision window. */
|
|
99
101
|
lastDeliveryAt?: string | undefined
|
|
100
102
|
/** Authenticated owner. */
|
|
101
103
|
owner: Owner
|
|
@@ -105,7 +107,8 @@ export type Subscription = {
|
|
|
105
107
|
secret: string
|
|
106
108
|
/** Lifecycle status. */
|
|
107
109
|
status: Status
|
|
108
|
-
/** ISO timestamp of the last mutation.
|
|
110
|
+
/** ISO timestamp of the last mutation. May lag repeat deliveries; see
|
|
111
|
+
* `lastDeliveryAt`. */
|
|
109
112
|
updatedAt: string
|
|
110
113
|
}
|
|
111
114
|
|
|
@@ -406,11 +409,18 @@ export async function getCursor(db: Db.Db, subscriptionId: string): Promise<stri
|
|
|
406
409
|
* deliveries past retention. Reads already filter expiry at query time; this
|
|
407
410
|
* is the janitor reclaiming space, run best-effort on a schedule.
|
|
408
411
|
*/
|
|
409
|
-
export async function pruneExpired(
|
|
412
|
+
export async function pruneExpired(
|
|
413
|
+
db: Db.Db,
|
|
414
|
+
options: pruneExpired.Options = {},
|
|
415
|
+
): Promise<pruneExpired.Result> {
|
|
410
416
|
const iso = (options.now ?? (() => new Date()))().toISOString()
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
// Sequential on purpose: the subscription delete cascades into the other
|
|
418
|
+
// tables, so overlapping it with their prunes risks deadlocks.
|
|
419
|
+
return {
|
|
420
|
+
completions: await WebhookQueueCompletions.pruneExpired(db, iso),
|
|
421
|
+
deliveries: await WebhookDeliveries.pruneExpired(db, iso),
|
|
422
|
+
subscriptions: await WebhookSubscriptions.pruneExpired(db, iso),
|
|
423
|
+
}
|
|
414
424
|
}
|
|
415
425
|
|
|
416
426
|
export declare namespace pruneExpired {
|
|
@@ -419,6 +429,16 @@ export declare namespace pruneExpired {
|
|
|
419
429
|
/** Clock used for expiry evaluation; injectable for tests. */
|
|
420
430
|
now?: (() => Date) | undefined
|
|
421
431
|
}
|
|
432
|
+
|
|
433
|
+
/** Rows deleted per table; a count pinned at the batch bound means expiry is outpacing pruning. */
|
|
434
|
+
type Result = {
|
|
435
|
+
/** Deleted `webhook_queue_completions` rows. */
|
|
436
|
+
completions: number
|
|
437
|
+
/** Deleted `webhook_deliveries` rows. */
|
|
438
|
+
deliveries: number
|
|
439
|
+
/** Deleted `webhook_subscriptions` rows. */
|
|
440
|
+
subscriptions: number
|
|
441
|
+
}
|
|
422
442
|
}
|
|
423
443
|
|
|
424
444
|
/**
|
|
@@ -427,6 +447,13 @@ export declare namespace pruneExpired {
|
|
|
427
447
|
*/
|
|
428
448
|
export const deliveryRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
429
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Completions dedupe window: replays of a completed obligation are suppressed
|
|
452
|
+
* this long. Deliberately its own literal so per-deployment log-retention
|
|
453
|
+
* tuning cannot move it.
|
|
454
|
+
*/
|
|
455
|
+
export const dedupeRetentionMs = 7 * 24 * 60 * 60 * 1_000
|
|
456
|
+
|
|
430
457
|
/** A compact Cloudflare Queue message referencing a durable staged envelope. */
|
|
431
458
|
export type QueueReference = {
|
|
432
459
|
/** Idempotent event id of the staged envelope. */
|
|
@@ -441,8 +468,6 @@ export type QueueEvent = QueueReference & {
|
|
|
441
468
|
createdAt: string
|
|
442
469
|
/** Full envelope loaded by the Queue consumer. */
|
|
443
470
|
envelope: Envelope
|
|
444
|
-
/** ISO timestamp after which an unconsumed envelope may be pruned. */
|
|
445
|
-
expiresAt: string
|
|
446
471
|
}
|
|
447
472
|
|
|
448
473
|
/** A claim newer than this counts as an in-flight attempt; above the 10s destination timeout default. */
|
|
@@ -467,7 +492,6 @@ export async function ensureQueueEvents(
|
|
|
467
492
|
options: ensureQueueEvents.Options = {},
|
|
468
493
|
): Promise<ensureQueueEvents.Result> {
|
|
469
494
|
const now = (options.now ?? (() => new Date()))()
|
|
470
|
-
const expiresAt = new Date(now.getTime() + (options.ttl ?? deliveryRetentionMs)).toISOString()
|
|
471
495
|
// Carried so the delivery worker can measure observation-to-attempt without
|
|
472
496
|
// re-reading the block; replays have no observation and stay null.
|
|
473
497
|
const observedAt =
|
|
@@ -476,9 +500,6 @@ export async function ensureQueueEvents(
|
|
|
476
500
|
createdAt: now.toISOString(),
|
|
477
501
|
envelope,
|
|
478
502
|
eventId: envelope.id,
|
|
479
|
-
// Inert while pending; the meaningful dedupe deadline is stamped at
|
|
480
|
-
// terminal transitions.
|
|
481
|
-
expiresAt,
|
|
482
503
|
// Due immediately; the value orders the sweep so it is never null here.
|
|
483
504
|
nextAttemptAt: now.toISOString(),
|
|
484
505
|
observedAt,
|
|
@@ -499,14 +520,12 @@ export declare namespace ensureQueueEvents {
|
|
|
499
520
|
subscription: Subscription
|
|
500
521
|
}
|
|
501
522
|
|
|
502
|
-
/**
|
|
523
|
+
/** Staging options. */
|
|
503
524
|
type Options = {
|
|
504
525
|
/** Clock used for staging timestamps. */
|
|
505
526
|
now?: (() => Date) | undefined
|
|
506
527
|
/** Epoch ms the head owing these events was observed; absent on replays. */
|
|
507
528
|
observedAt?: number | undefined
|
|
508
|
-
/** Staged-envelope TTL in ms; defaults to {@link deliveryRetentionMs}. */
|
|
509
|
-
ttl?: number | undefined
|
|
510
529
|
}
|
|
511
530
|
|
|
512
531
|
/** Created-row count plus references for every requested obligation. */
|
|
@@ -542,7 +561,7 @@ export function completeQueueEvent(
|
|
|
542
561
|
): Promise<boolean> {
|
|
543
562
|
return WebhookQueueEvents.complete(db, reference, {
|
|
544
563
|
...options,
|
|
545
|
-
expiresAt: new Date(Date.now() +
|
|
564
|
+
expiresAt: new Date(Date.now() + dedupeRetentionMs).toISOString(),
|
|
546
565
|
})
|
|
547
566
|
}
|
|
548
567
|
|
|
@@ -552,7 +571,7 @@ export declare namespace completeQueueEvent {
|
|
|
552
571
|
/** Claim marker from the claimed record's `attemptingAt`. */
|
|
553
572
|
claimedAt: string
|
|
554
573
|
/** Terminal obligation outcome. */
|
|
555
|
-
status:
|
|
574
|
+
status: WebhookQueueCompletions.TerminalStatus
|
|
556
575
|
}
|
|
557
576
|
}
|
|
558
577
|
|
|
@@ -766,10 +785,17 @@ export async function getDelivery(
|
|
|
766
785
|
*/
|
|
767
786
|
export const maxFailures = 10
|
|
768
787
|
|
|
788
|
+
/**
|
|
789
|
+
* How stale `lastDeliveryAt` may get before a success rewrites it. Read only
|
|
790
|
+
* for display, so exactness is traded for not rewriting the row per delivery.
|
|
791
|
+
*/
|
|
792
|
+
const lastDeliveryPrecisionMs = 60_000
|
|
793
|
+
|
|
769
794
|
/**
|
|
770
795
|
* Records a successful delivery: resets the consecutive-failure counter and
|
|
771
|
-
*
|
|
772
|
-
*
|
|
796
|
+
* refreshes `lastDeliveryAt` at most once per precision window. Returns the
|
|
797
|
+
* written row, or the caller's snapshot when the write was skipped or the row
|
|
798
|
+
* deleted.
|
|
773
799
|
*/
|
|
774
800
|
export async function recordSuccess(
|
|
775
801
|
db: Db.Db,
|
|
@@ -777,11 +803,13 @@ export async function recordSuccess(
|
|
|
777
803
|
options: recordSuccess.Options = {},
|
|
778
804
|
): Promise<Subscription> {
|
|
779
805
|
const now = (options.now ?? (() => new Date()))()
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
806
|
+
const next = await WebhookSubscriptions.recordSuccess(db, subscription.id, {
|
|
807
|
+
now: now.toISOString(),
|
|
808
|
+
staleBefore: new Date(now.getTime() - lastDeliveryPrecisionMs).toISOString(),
|
|
809
|
+
})
|
|
810
|
+
// No row means the write was skipped or the row was deleted; neither
|
|
811
|
+
// resurrects, so keep the caller's timestamps.
|
|
812
|
+
return next ?? { ...subscription, failureCount: 0 }
|
|
785
813
|
}
|
|
786
814
|
|
|
787
815
|
export declare namespace recordSuccess {
|
|
@@ -796,7 +824,9 @@ export declare namespace recordSuccess {
|
|
|
796
824
|
* Records a failed delivery: atomically increments the consecutive-failure
|
|
797
825
|
* counter and, once it reaches `maxFailures`, sets `status: 'disabled'` so the
|
|
798
826
|
* subscription drops out of the partial active index (surfaced via the API).
|
|
799
|
-
*
|
|
827
|
+
* Once disabled at the cap the row is left alone, so an outage's in-flight
|
|
828
|
+
* failures stop contending on its lock. Returns the written row, or the
|
|
829
|
+
* caller's snapshot advanced optimistically.
|
|
800
830
|
*/
|
|
801
831
|
export async function recordFailure(
|
|
802
832
|
db: Db.Db,
|
|
@@ -808,14 +838,14 @@ export async function recordFailure(
|
|
|
808
838
|
const max = options.maxFailures ?? maxFailures
|
|
809
839
|
const next = await WebhookSubscriptions.recordFailure(db, subscription.id, max, iso)
|
|
810
840
|
if (next) return next
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
|
|
841
|
+
// No row means skipped (already disabled at the cap) or deleted; neither
|
|
842
|
+
// resurrects, so keep the caller's timestamps. Capped so chained skips
|
|
843
|
+
// cannot grow the counter past what the store would hold.
|
|
844
|
+
const failureCount = Math.min(subscription.failureCount + 1, max)
|
|
814
845
|
return {
|
|
815
846
|
...subscription,
|
|
816
847
|
failureCount,
|
|
817
848
|
...(failureCount >= max ? { status: 'disabled' as const } : {}),
|
|
818
|
-
updatedAt: iso,
|
|
819
849
|
}
|
|
820
850
|
}
|
|
821
851
|
|
|
@@ -4,6 +4,7 @@ import type * as Db from '../../db/Db.js'
|
|
|
4
4
|
import * as FundingDepositAddresses from '../../db/tables/fundingDepositAddresses.js'
|
|
5
5
|
import * as FundingDeposits from '../../db/tables/fundingDeposits.js'
|
|
6
6
|
import * as Id from '../Id.js'
|
|
7
|
+
import * as OpenApi from '../OpenApi.js'
|
|
7
8
|
import * as Schema from '../Schema.js'
|
|
8
9
|
import * as Provider from './Provider.js'
|
|
9
10
|
|
|
@@ -115,22 +116,25 @@ export namespace schema {
|
|
|
115
116
|
)
|
|
116
117
|
|
|
117
118
|
/** Customer-safe explanation of a deposit status. */
|
|
118
|
-
export const StatusReason =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
119
|
+
export const StatusReason = OpenApi.component(
|
|
120
|
+
z
|
|
121
|
+
.object({
|
|
122
|
+
code: z
|
|
123
|
+
.enum(statusReasonCodes)
|
|
124
|
+
.check(
|
|
125
|
+
z.describe('Stable machine-readable reason code.'),
|
|
126
|
+
z.meta({ examples: ['delivery_liquidity_unavailable'] }),
|
|
127
|
+
),
|
|
128
|
+
message: z
|
|
129
|
+
.string()
|
|
130
|
+
.check(
|
|
131
|
+
z.describe('Human-readable explanation of the current status.'),
|
|
132
|
+
z.meta({ examples: ['The required destination amount is not currently available.'] }),
|
|
133
|
+
),
|
|
134
|
+
})
|
|
135
|
+
.check(z.describe('Why a deposit needs attention or recovery.')),
|
|
136
|
+
'FundingDepositStatusReason',
|
|
137
|
+
)
|
|
134
138
|
|
|
135
139
|
/** Public fields persisted as the deposit snapshot. */
|
|
136
140
|
export const Snapshot = z
|
|
@@ -189,54 +193,57 @@ export namespace schema {
|
|
|
189
193
|
)
|
|
190
194
|
|
|
191
195
|
/** One detected funding deposit. */
|
|
192
|
-
export const FundingDeposit =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
196
|
+
export const FundingDeposit = OpenApi.component(
|
|
197
|
+
z
|
|
198
|
+
.object({
|
|
199
|
+
createdAt: z.iso
|
|
200
|
+
.datetime()
|
|
201
|
+
.check(
|
|
202
|
+
z.describe('When the deposit was first detected (ISO 8601).'),
|
|
203
|
+
z.meta({ examples: ['2026-08-04T00:01:00.000Z'] }),
|
|
204
|
+
),
|
|
205
|
+
depositAddressId: Snapshot.shape.depositAddressId,
|
|
206
|
+
detectionTrigger: z.optional(DetectionTrigger),
|
|
207
|
+
destinationAmount: Snapshot.shape.destinationAmount,
|
|
208
|
+
destinationAmountRequired: Snapshot.shape.destinationAmountRequired,
|
|
209
|
+
destinationChain: Snapshot.shape.destinationChain,
|
|
210
|
+
destinationToken: Snapshot.shape.destinationToken,
|
|
211
|
+
destinationTransactionHashes: Snapshot.shape.destinationTransactionHashes,
|
|
212
|
+
id: z
|
|
213
|
+
.string()
|
|
214
|
+
.check(
|
|
215
|
+
z.regex(/^fdp_[A-Za-z0-9_-]+$/),
|
|
216
|
+
z.describe('Funding deposit id (`fdp_…`).'),
|
|
217
|
+
z.meta({ examples: ['fdp_001785792060000_2ZPE2gvateYEQ0dQslgvkhjx'] }),
|
|
218
|
+
),
|
|
219
|
+
provider: Snapshot.shape.provider,
|
|
220
|
+
recipient: Snapshot.shape.recipient,
|
|
221
|
+
refundAddress: Snapshot.shape.refundAddress,
|
|
222
|
+
refundAmount: Snapshot.shape.refundAmount,
|
|
223
|
+
refundTransactionHashes: Snapshot.shape.refundTransactionHashes,
|
|
224
|
+
sender: Snapshot.shape.sender,
|
|
225
|
+
sourceAmount: Snapshot.shape.sourceAmount,
|
|
226
|
+
sourceChain: Snapshot.shape.sourceChain,
|
|
227
|
+
sourceToken: Snapshot.shape.sourceToken,
|
|
228
|
+
sourceTransactionHashes: Snapshot.shape.sourceTransactionHashes,
|
|
229
|
+
sourceTransferIndex: z
|
|
230
|
+
.optional(z.number().check(z.int(), z.minimum(0)))
|
|
231
|
+
.check(
|
|
232
|
+
z.describe('Verified transfer position within the source transaction.'),
|
|
233
|
+
z.meta({ examples: [0] }),
|
|
234
|
+
),
|
|
235
|
+
status: Status,
|
|
236
|
+
statusReason: z.optional(StatusReason),
|
|
237
|
+
updatedAt: z.iso
|
|
238
|
+
.datetime()
|
|
239
|
+
.check(
|
|
240
|
+
z.describe('When the deposit last materially changed (ISO 8601).'),
|
|
241
|
+
z.meta({ examples: ['2026-08-04T00:02:00.000Z'] }),
|
|
242
|
+
),
|
|
243
|
+
})
|
|
244
|
+
.check(z.describe('One detected deposit and its delivery status.')),
|
|
245
|
+
'FundingDeposit',
|
|
246
|
+
)
|
|
240
247
|
}
|
|
241
248
|
|
|
242
249
|
/** Stored public snapshot of a funding deposit. */
|