tapimo 0.10.1 → 0.11.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.
Files changed (225) hide show
  1. package/dist/App.d.ts +5249 -3572
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js.map +1 -1
  4. package/dist/Webhooks.d.ts +1 -76
  5. package/dist/Webhooks.d.ts.map +1 -1
  6. package/dist/Webhooks.js +2 -144
  7. package/dist/Webhooks.js.map +1 -1
  8. package/dist/admin/App.d.ts +74 -74
  9. package/dist/admin/apps/earn-vaults.d.ts +1 -1
  10. package/dist/admin/apps/organizations.d.ts +3 -3
  11. package/dist/admin/apps/verified-tokens.d.ts +74 -74
  12. package/dist/analytics/Analytics.d.ts.map +1 -1
  13. package/dist/analytics/Analytics.js +206 -0
  14. package/dist/analytics/Analytics.js.map +1 -1
  15. package/dist/analytics/tables/requestEvents.d.ts +4 -0
  16. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  17. package/dist/analytics/tables/requestEvents.js +2 -0
  18. package/dist/analytics/tables/requestEvents.js.map +1 -1
  19. package/dist/apps/data/App.d.ts +3618 -2954
  20. package/dist/apps/data/App.d.ts.map +1 -1
  21. package/dist/apps/data/App.js +7 -2
  22. package/dist/apps/data/App.js.map +1 -1
  23. package/dist/apps/data/routes/activities.d.ts +1 -1
  24. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  25. package/dist/apps/data/routes/activities.js +55 -27
  26. package/dist/apps/data/routes/activities.js.map +1 -1
  27. package/dist/apps/data/routes/balances.d.ts +11 -11
  28. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  29. package/dist/apps/data/routes/balances.js +2 -6
  30. package/dist/apps/data/routes/balances.js.map +1 -1
  31. package/dist/apps/data/routes/blocks.d.ts +5 -5
  32. package/dist/apps/data/routes/earn.d.ts +4 -4
  33. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  34. package/dist/apps/data/routes/earn.js +63 -34
  35. package/dist/apps/data/routes/earn.js.map +1 -1
  36. package/dist/apps/data/routes/exchanges.d.ts +8 -4
  37. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  38. package/dist/apps/data/routes/exchanges.js +115 -77
  39. package/dist/apps/data/routes/exchanges.js.map +1 -1
  40. package/dist/apps/data/routes/fee-amm.d.ts +26 -26
  41. package/dist/apps/data/routes/receipts.d.ts +240 -239
  42. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  43. package/dist/apps/data/routes/receipts.js +15 -14
  44. package/dist/apps/data/routes/receipts.js.map +1 -1
  45. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  46. package/dist/apps/data/routes/tokens.d.ts +4 -4
  47. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  48. package/dist/apps/data/routes/tokens.js +4 -3
  49. package/dist/apps/data/routes/tokens.js.map +1 -1
  50. package/dist/apps/data/routes/transactions.d.ts +282 -282
  51. package/dist/apps/data/routes/transfers.d.ts +13 -13
  52. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  53. package/dist/apps/data/routes/transfers.js +25 -7
  54. package/dist/apps/data/routes/transfers.js.map +1 -1
  55. package/dist/apps/data/routes/valuation.d.ts +21 -21
  56. package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
  57. package/dist/apps/data/routes/webhooks.d.ts +1519 -643
  58. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  59. package/dist/apps/data/routes/webhooks.js +134 -740
  60. package/dist/apps/data/routes/webhooks.js.map +1 -1
  61. package/dist/apps/data/routes/zones.d.ts +3 -3
  62. package/dist/apps/funding/App.d.ts +233 -0
  63. package/dist/apps/funding/App.d.ts.map +1 -1
  64. package/dist/apps/funding/routes/deposit-addresses.d.ts +331 -1
  65. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  66. package/dist/apps/funding/routes/deposit-addresses.js +207 -11
  67. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  68. package/dist/apps/funding/routes/transfers.d.ts +33 -33
  69. package/dist/apps/management/App.d.ts +793 -16
  70. package/dist/apps/management/App.d.ts.map +1 -1
  71. package/dist/apps/management/routes/api-keys.d.ts +50 -50
  72. package/dist/apps/management/routes/faucet.d.ts +2 -2
  73. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  74. package/dist/apps/management/routes/members.d.ts +2 -2
  75. package/dist/apps/management/routes/projects.d.ts +40 -40
  76. package/dist/apps/management/routes/webhooks.d.ts +1031 -59
  77. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  78. package/dist/apps/management/routes/webhooks.js +27 -4
  79. package/dist/apps/management/routes/webhooks.js.map +1 -1
  80. package/dist/db/Db.d.ts.map +1 -1
  81. package/dist/db/Db.js +53 -0
  82. package/dist/db/Db.js.map +1 -1
  83. package/dist/db/Schema.d.ts +7 -1
  84. package/dist/db/Schema.d.ts.map +1 -1
  85. package/dist/db/Schema.js +9 -0
  86. package/dist/db/Schema.js.map +1 -1
  87. package/dist/db/tables/fundingDepositAddresses.d.ts +20 -0
  88. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  89. package/dist/db/tables/fundingDepositAddresses.js +17 -0
  90. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  91. package/dist/db/tables/fundingDeposits.d.ts +41 -0
  92. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  93. package/dist/db/tables/fundingDeposits.js +69 -0
  94. package/dist/db/tables/fundingDeposits.js.map +1 -1
  95. package/dist/db/tables/webhookQueueEvents.d.ts +3 -1
  96. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  97. package/dist/db/tables/webhookQueueEvents.js +4 -0
  98. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  99. package/dist/db/tables/webhookSubscriptions.d.ts +33 -69
  100. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  101. package/dist/db/tables/webhookSubscriptions.js +63 -100
  102. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  103. package/dist/internal/Auth.d.ts +0 -26
  104. package/dist/internal/Auth.d.ts.map +1 -1
  105. package/dist/internal/Auth.js +0 -33
  106. package/dist/internal/Auth.js.map +1 -1
  107. package/dist/internal/Credential.d.ts +27 -0
  108. package/dist/internal/Credential.d.ts.map +1 -0
  109. package/dist/internal/Credential.js +34 -0
  110. package/dist/internal/Credential.js.map +1 -0
  111. package/dist/internal/EarnRates.js +11 -7
  112. package/dist/internal/EarnRates.js.map +1 -1
  113. package/dist/internal/Log.d.ts +8 -0
  114. package/dist/internal/Log.d.ts.map +1 -1
  115. package/dist/internal/Log.js +4 -0
  116. package/dist/internal/Log.js.map +1 -1
  117. package/dist/internal/MetricSink.d.ts +50 -116
  118. package/dist/internal/MetricSink.d.ts.map +1 -1
  119. package/dist/internal/MetricSink.js +60 -106
  120. package/dist/internal/MetricSink.js.map +1 -1
  121. package/dist/internal/Schema.d.ts +3 -1
  122. package/dist/internal/Schema.d.ts.map +1 -1
  123. package/dist/internal/Schema.js +4 -2
  124. package/dist/internal/Schema.js.map +1 -1
  125. package/dist/internal/Tidx.d.ts +4 -3
  126. package/dist/internal/Tidx.d.ts.map +1 -1
  127. package/dist/internal/Tidx.js +2 -2
  128. package/dist/internal/Tidx.js.map +1 -1
  129. package/dist/internal/Viem.d.ts +4 -3
  130. package/dist/internal/Viem.d.ts.map +1 -1
  131. package/dist/internal/Viem.js +3 -2
  132. package/dist/internal/Viem.js.map +1 -1
  133. package/dist/internal/WebhookTransfer.d.ts +1 -55
  134. package/dist/internal/WebhookTransfer.d.ts.map +1 -1
  135. package/dist/internal/WebhookTransfer.js +1 -239
  136. package/dist/internal/WebhookTransfer.js.map +1 -1
  137. package/dist/internal/Webhooks.d.ts +85 -44
  138. package/dist/internal/Webhooks.d.ts.map +1 -1
  139. package/dist/internal/Webhooks.js +94 -73
  140. package/dist/internal/Webhooks.js.map +1 -1
  141. package/dist/internal/funding/DepositWebhook.d.ts +25 -0
  142. package/dist/internal/funding/DepositWebhook.d.ts.map +1 -0
  143. package/dist/internal/funding/DepositWebhook.js +74 -0
  144. package/dist/internal/funding/DepositWebhook.js.map +1 -0
  145. package/dist/internal/funding/Reconciliation.d.ts +6 -0
  146. package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
  147. package/dist/internal/funding/Reconciliation.js +42 -10
  148. package/dist/internal/funding/Reconciliation.js.map +1 -1
  149. package/dist/internal/funding/index.d.ts +1 -0
  150. package/dist/internal/funding/index.d.ts.map +1 -1
  151. package/dist/internal/funding/index.js +1 -0
  152. package/dist/internal/funding/index.js.map +1 -1
  153. package/dist/internal/index.d.ts +1 -0
  154. package/dist/internal/index.d.ts.map +1 -1
  155. package/dist/internal/index.js +1 -0
  156. package/dist/internal/index.js.map +1 -1
  157. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  158. package/dist/internal/webhookDestinations/slack.js +16 -0
  159. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  160. package/package.json +1 -1
  161. package/src/App.test.ts +30 -0
  162. package/src/App.ts +3 -0
  163. package/src/Client.test-d.ts +55 -2
  164. package/src/Webhooks.ts +2 -221
  165. package/src/analytics/Analytics.test.ts +34 -0
  166. package/src/analytics/Analytics.ts +206 -0
  167. package/src/analytics/tables/requestEvents.ts +6 -0
  168. package/src/apps/composition.test.ts +1 -0
  169. package/src/apps/data/App.ts +10 -2
  170. package/src/apps/data/routes/activities.test.ts +107 -11
  171. package/src/apps/data/routes/activities.ts +64 -32
  172. package/src/apps/data/routes/balances.ts +2 -6
  173. package/src/apps/data/routes/earn.ts +64 -34
  174. package/src/apps/data/routes/exchanges.test.ts +8 -3
  175. package/src/apps/data/routes/exchanges.ts +134 -82
  176. package/src/apps/data/routes/receipts.ts +16 -14
  177. package/src/apps/data/routes/tokens.ts +3 -2
  178. package/src/apps/data/routes/transactions.bench.ts +8 -0
  179. package/src/apps/data/routes/transfers.ts +36 -10
  180. package/src/apps/data/routes/webhooks.test.ts +179 -1497
  181. package/src/apps/data/routes/webhooks.ts +172 -975
  182. package/src/apps/funding/routes/deposit-addresses.test.ts +216 -2
  183. package/src/apps/funding/routes/deposit-addresses.ts +254 -18
  184. package/src/apps/funding/routes/transfers.test.ts +17 -2
  185. package/src/apps/management/routes/webhooks.test.ts +79 -1
  186. package/src/apps/management/routes/webhooks.ts +40 -4
  187. package/src/db/Db.test.ts +1 -5
  188. package/src/db/Db.ts +59 -0
  189. package/src/db/Schema.ts +13 -0
  190. package/src/db/tables/fundingDepositAddresses.test.ts +28 -0
  191. package/src/db/tables/fundingDepositAddresses.ts +39 -0
  192. package/src/db/tables/fundingDeposits.test.ts +82 -0
  193. package/src/db/tables/fundingDeposits.ts +130 -0
  194. package/src/db/tables/organizations.test.ts +5 -2
  195. package/src/db/tables/webhookQueueEvents.ts +7 -1
  196. package/src/db/tables/webhookSubscriptions.ts +89 -138
  197. package/src/internal/Auth.test.ts +0 -48
  198. package/src/internal/Auth.ts +0 -59
  199. package/src/internal/Credential.test.ts +51 -0
  200. package/src/internal/Credential.ts +58 -0
  201. package/src/internal/EarnRates.ts +13 -7
  202. package/src/internal/Log.test.ts +3 -1
  203. package/src/internal/Log.ts +14 -0
  204. package/src/internal/MetricSink.test.ts +75 -358
  205. package/src/internal/MetricSink.ts +117 -246
  206. package/src/internal/Schema.test.ts +18 -0
  207. package/src/internal/Schema.ts +13 -2
  208. package/src/internal/Tidx.ts +5 -4
  209. package/src/internal/Viem.test.ts +4 -0
  210. package/src/internal/Viem.ts +6 -4
  211. package/src/internal/WebhookTransfer.ts +1 -351
  212. package/src/internal/Webhooks.test.ts +111 -139
  213. package/src/internal/Webhooks.ts +181 -105
  214. package/src/internal/funding/DepositWebhook.test.ts +131 -0
  215. package/src/internal/funding/DepositWebhook.ts +101 -0
  216. package/src/internal/funding/Reconciliation.test.ts +14 -0
  217. package/src/internal/funding/Reconciliation.ts +161 -86
  218. package/src/internal/funding/index.test.ts +1 -0
  219. package/src/internal/funding/index.ts +1 -0
  220. package/src/internal/index.test.ts +1 -0
  221. package/src/internal/index.ts +1 -0
  222. package/src/internal/webhookDestinations/slack.ts +15 -0
  223. package/src/Webhooks.bench.ts +0 -714
  224. package/src/Webhooks.memory.bench.ts +0 -250
  225. package/src/Webhooks.test.ts +0 -146
@@ -9,48 +9,22 @@ export type EventType = Webhooks.Envelope['type']
9
9
  /** Where a delivery attempt originated. */
10
10
  export type Trigger = 'manual_retry' | 'ping' | 'queue'
11
11
 
12
- /** Bounded poller stage names used by `webhook:poll`. */
13
- export type PollStage = 'cursor_write' | 'enrich' | 'rpc_head' | 'scan' | 'subscription_read'
14
-
15
- /** Bounded Durable Object or transport failure classes used by `webhook:poll-failure`. */
16
- export type PollFailure = 'other' | 'overloaded' | 'remote' | 'retryable'
17
-
18
- /** Context for one poller tick that the {@link Webhooks.createPoller.Result} omits. */
19
- export type TickMeta = {
20
- /** Chain polled this tick; used when the tick errored before a result existed. */
21
- chainId?: number | undefined
22
- /** Wall-clock duration of the tick in ms. */
23
- durationMs: number
24
- /** Whether the tick completed. */
25
- outcome: 'error' | 'ok'
26
- }
12
+ /** Bounded step names inside one block-fanout scan, used by `webhook:scan-step`. */
13
+ export type ScanStep =
14
+ | 'block_rpc'
15
+ | 'enrich'
16
+ | 'finalized_rpc'
17
+ | 'logs_rpc'
18
+ | 'match'
19
+ | 'stage'
20
+ | 'subscription_read'
27
21
 
28
22
  /**
29
- * A webhook metric event, recorded by the {@link webhooks} sink. The poller
30
- * emits head recovery, poll, tick, and enqueue measurements; the delivery path
23
+ * A webhook metric event, recorded by the {@link webhooks} sink. The
24
+ * block-fanout scan path emits head and scan measurements; the delivery path
31
25
  * emits `webhook:delivery`.
32
26
  */
33
27
  export type WebhookEvent =
34
- | {
35
- /** Tempo chain whose poll cycle completed. */
36
- chainId: number
37
- /** Whether the poll cycle completed without scan or dispatch failures. */
38
- outcome: 'error' | 'ok'
39
- /** Requested boundary movement since the preceding successful poll, in blocks. */
40
- requestedBoundaryAdvanceBlocks?: number | undefined
41
- /** Execution path that acquired this poll claim. */
42
- trigger: 'alarm' | 'cron' | 'direct' | 'queue'
43
- /** Identifies a transport-neutral poll-cycle measurement. */
44
- type: 'webhook:poll-cycle'
45
- }
46
- | {
47
- /** Tempo chain whose durable progress was read. */
48
- chainId: number
49
- /** Blocks between the desired and fully drained boundaries. */
50
- lagBlocks: number
51
- /** Identifies durable coordinator progress. */
52
- type: 'webhook:head-progress'
53
- }
54
28
  | {
55
29
  /** Tempo chain whose listener was checked. */
56
30
  chainId: number
@@ -61,103 +35,53 @@ export type WebhookEvent =
61
35
  }
62
36
  | {
63
37
  chainId: number
64
- /** Blocks the probed chain head leads the listener's newest observation. */
65
- lagBlocks: number
66
- /** Identifies the out-of-band listener head probe. */
67
- type: 'webhook:listener-head-lag'
38
+ /** True once finality has deferred this block at least one round. */
39
+ deferred: boolean
40
+ /** Rows matched when dispatched; zero otherwise. */
41
+ matches: number
42
+ /** Block timestamp to scan completion; absent when the block was not read. */
43
+ scanLagMs?: number | undefined
44
+ /** Block timestamp to head observation; absent without a fresh stamp. */
45
+ observationLagMs?: number | undefined
46
+ /** How the head that enqueued this block was observed. */
47
+ origin: string
48
+ /** One scanned block's outcome. */
49
+ outcome: 'deferred' | 'dispatched' | 'empty'
50
+ /** Head observation to this scan attempt starting; absent without a stamp. */
51
+ queueWaitMs?: number | undefined
52
+ /** Identifies one block-fanout scan. */
53
+ type: 'webhook:scan-block'
54
+ /** Wall-clock work inside this scan attempt. */
55
+ workMs: number
68
56
  }
69
57
  | {
70
- /** Tempo chain whose recovery poke was dequeued. */
71
58
  chainId: number
72
- /** When the recovery poke was dequeued, as epoch milliseconds. */
73
- dequeuedAt: number
74
- /** When the recovery poke was enqueued, as epoch milliseconds. */
75
- enqueuedAt: number
76
- /** Recovery source carried through the Queue handoff. */
77
- trigger: 'alarm' | 'queue'
78
- /** Identifies recovery Queue latency, not observed head volume. */
79
- type: 'webhook:head-recovery'
80
- }
81
- | {
82
- /** Tempo chain whose poll claim was acquired. */
83
- chainId: number
84
- /** Delay from observing the desired boundary to acquiring the claim. */
85
- delayMs?: number | undefined
86
- /** Execution path that acquired the claim. */
87
- trigger: 'alarm' | 'cron' | 'direct' | 'queue'
88
- /** Identifies a poll beginning, before scan work runs. */
89
- type: 'webhook:poll-start'
90
- }
91
- | {
92
- /** Tempo chain whose poll failed. */
93
- chainId: number
94
- /** Bounded class of the thrown failure. */
95
- failure: PollFailure
96
- /** Operation boundary that rejected the poll. */
97
- stage: 'claim' | 'continuation_enqueue' | 'finish' | 'scan'
98
- /** Execution path that failed before recovery. */
99
- trigger: 'alarm' | 'cron' | 'direct' | 'queue'
100
- /** Identifies a transport-neutral poll failure. */
101
- type: 'webhook:poll-failure'
102
- }
103
- | {
104
- chainId: number
105
- durationMs: number
106
- eventType?: EventType | undefined
107
- outcome: 'error' | 'ok'
108
- stage: PollStage
109
- type: 'webhook:poll'
110
- }
111
- | {
112
- /** Tempo chain whose eligible subscription count was read. */
113
- chainId: number
114
- /** Active, unexpired subscriptions selected for the poll. */
59
+ /** Times this step ran across the batch, so the total can be normalized. */
115
60
  count: number
116
- /** Identifies a pre-scan subscription eligibility measurement. */
117
- type: 'webhook:poll-subscriptions'
61
+ /** Wall-clock duration of this step summed across the batch. */
62
+ durationMs: number
63
+ /** Bounded step name. */
64
+ step: ScanStep
65
+ /** Identifies per-step scan attribution. */
66
+ type: 'webhook:scan-step'
118
67
  }
119
68
  | {
120
- /** Chain scanned by this poll batch. */
121
69
  chainId: number
122
- /** Shared upper bound for concurrent upstream scans. */
123
- concurrency: number
124
- /** Scan duration, present only after completion. */
125
- durationMs?: number | undefined
126
- /** Subscription event type scanned by this batch. */
127
- eventType: EventType
128
- /** Subscriptions whose scan rejected, present only after completion. */
129
- failedSubscriptions?: number | undefined
130
- /** Matching rows retained by completed pages, present only after completion. */
131
- matchedItems?: number | undefined
132
- /** Largest block window requested by one subscription. */
133
- maxWindowBlocks: number
134
- /** Whether the checkpoint precedes allocation or follows scan completion. */
135
- phase: 'complete' | 'start'
136
- /** Outcome of the completed RPC-backed scan. */
137
- outcome?: 'empty' | 'error' | 'missing' | 'ok' | undefined
138
- /** JSON-RPC method used by this event type's scanner. */
139
- rpcMethod: 'eth_getBlockByNumber' | 'eth_getLogs'
140
- /** Subscriptions retained in this scan batch. */
141
- subscriptionCount: number
142
- /** Maximum subscriptions retained by this event-type batch. */
143
- subscriptionLimit: number
144
- /** Sum of subscription block windows in this scan batch. */
145
- totalWindowBlocks: number
146
- /** Identifies a memory-attribution scan boundary. */
147
- type: 'webhook:poll-scan'
148
- }
149
- | {
150
- cursorLagBlocks?: number | undefined
151
- meta: TickMeta
152
- result: Webhooks.createPoller.Result | undefined
153
- type: 'webhook:tick'
70
+ /** Age of the oldest tracked block, absent when none are pending. */
71
+ oldestPendingAgeMs?: number | undefined
72
+ /** Tracked blocks awaiting completion reports. */
73
+ pendingCount: number
74
+ /** Identifies the coordinator's tracked-scan backlog. */
75
+ type: 'webhook:scan-backlog'
76
+ /** Blocks between the chain head and the enqueue watermark. */
77
+ watermarkLagBlocks?: number | undefined
154
78
  }
155
79
  | {
156
80
  chainId: number
157
- durationMs: number
158
- eventType: EventType
159
- ok: boolean
160
- type: 'webhook:enqueue'
81
+ /** Blocks between the observed head and the finalized head. */
82
+ lagBlocks: number
83
+ /** Identifies the finality gate's settling distance. */
84
+ type: 'webhook:scan-finality'
161
85
  }
162
86
  | {
163
87
  /** Atomic ledger claim outcome. */
@@ -185,9 +109,17 @@ export type WebhookEvent =
185
109
  deliveryLogMs?: number | undefined
186
110
  envelopeToQueueMs?: number | undefined
187
111
  endToEndMs?: number | undefined
112
+ /** Block timestamp to the attempt leaving; the total latency SLI. */
113
+ eventToAttemptMs?: number | undefined
188
114
  eventToEnvelopeMs?: number | undefined
115
+ /** Head observation to the attempt leaving; the controllable SLI. */
116
+ observedToAttemptMs?: number | undefined
117
+ /** Dequeue to the attempt leaving: claim, reads, signing. */
118
+ preflightMs?: number | undefined
189
119
  processingMs?: number | undefined
190
120
  queueWaitMs?: number | undefined
121
+ /** Response headers to bookkeeping complete; occupancy, not latency. */
122
+ settleMs?: number | undefined
191
123
  stateWriteMs?: number | undefined
192
124
  subscriptionReadMs?: number | undefined
193
125
  }
@@ -200,7 +132,7 @@ export type RequestSink = (entry: Log.Entry) => void
200
132
 
201
133
  /** Records webhook reliability metrics. Built by {@link webhooks}. */
202
134
  export type WebhookSink = {
203
- /** Ships buffered metrics (call once per poller tick / delivery). */
135
+ /** Ships buffered metrics (call once per scan batch / delivery). */
204
136
  flush(): void
205
137
  /** Records one webhook event. */
206
138
  record(event: WebhookEvent): void
@@ -275,6 +207,9 @@ export function requests(backend: Metrics): RequestSink {
275
207
  backend.histogram('funding_quote_provider_duration_ms', attempt.durationMs, tags_attempt)
276
208
  }
277
209
 
210
+ if (entry.fundingDepositCountFailed)
211
+ backend.count('funding_deposit_count_failure_count', 1, tags)
212
+
278
213
  if (entry.fundingTransferCountFailed)
279
214
  backend.count('funding_transfer_count_failure_count', 1, tags)
280
215
 
@@ -356,144 +291,61 @@ export function webhooks(backend: Metrics): WebhookSink {
356
291
  },
357
292
  record(event) {
358
293
  switch (event.type) {
359
- case 'webhook:head-recovery': {
360
- const tags = { chain_id: String(event.chainId), trigger: event.trigger }
361
- backend.count('webhook_head_recovery_queue_count', 1, tags)
362
- backend.histogram(
363
- 'webhook_head_recovery_queue_wait_ms',
364
- Math.max(0, event.dequeuedAt - event.enqueuedAt),
365
- tags,
366
- )
367
- return
368
- }
369
- case 'webhook:head-progress':
370
- backend.gauge('webhook_head_progress_lag_blocks', event.lagBlocks, {
371
- chain_id: String(event.chainId),
372
- })
373
- return
374
294
  case 'webhook:head-skipped':
375
295
  backend.gauge('webhook_head_skipped_count', event.skippedHeads, {
376
296
  chain_id: String(event.chainId),
377
297
  })
378
298
  return
379
- case 'webhook:listener-head-lag':
380
- backend.gauge('webhook_listener_head_lag_blocks', event.lagBlocks, {
381
- chain_id: String(event.chainId),
382
- })
383
- return
384
- case 'webhook:poll-failure':
385
- backend.count('webhook_poll_failure_count', 1, {
386
- chain_id: String(event.chainId),
387
- failure: event.failure,
388
- stage: event.stage,
389
- trigger: event.trigger,
390
- })
391
- return
392
- case 'webhook:poll': {
299
+ case 'webhook:scan-block': {
393
300
  const tags = {
394
301
  chain_id: String(event.chainId),
395
- ...(event.eventType === undefined ? {} : { event_type: event.eventType }),
302
+ origin: event.origin,
396
303
  outcome: event.outcome,
397
- stage: event.stage,
398
- }
399
- backend.histogram('webhook_poller_stage_duration_ms', event.durationMs, tags)
400
- return
401
- }
402
- case 'webhook:poll-scan': {
403
- const tags = {
404
- chain_id: String(event.chainId),
405
- event_type: event.eventType,
406
- phase: event.phase,
407
304
  }
408
- backend.count('webhook_poller_scan_checkpoint_count', 1, tags)
409
- backend.histogram('webhook_poller_scan_concurrency', event.concurrency, tags)
410
- backend.histogram('webhook_poller_scan_subscription_count', event.subscriptionCount, tags)
411
- backend.histogram('webhook_poller_scan_subscription_limit', event.subscriptionLimit, tags)
412
- backend.histogram('webhook_poller_scan_max_window_blocks', event.maxWindowBlocks, tags)
413
- backend.histogram(
414
- 'webhook_poller_scan_total_window_blocks',
415
- event.totalWindowBlocks,
416
- tags,
417
- )
418
- if (event.durationMs !== undefined)
419
- backend.histogram('webhook_poller_scan_duration_ms', event.durationMs, tags)
420
- if (event.failedSubscriptions !== undefined)
421
- backend.count(
422
- 'webhook_poller_scan_failed_subscription_count',
423
- event.failedSubscriptions,
424
- tags,
425
- )
426
- if (event.matchedItems !== undefined)
427
- backend.count('webhook_poller_scan_matched_item_count', event.matchedItems, tags)
428
- if (event.outcome !== undefined)
429
- backend.count('webhook_poller_rpc_scan_count', 1, {
305
+ backend.count('webhook_scan_block_count', 1, tags)
306
+ if (event.matches > 0) backend.count('webhook_scan_match_count', event.matches, tags)
307
+ backend.histogram('webhook_scan_work_ms', event.workMs, tags)
308
+ // Deferred rounds are chain finality, not queue depth, so the wait
309
+ // carries the distinction rather than silently averaging the two.
310
+ if (event.queueWaitMs !== undefined)
311
+ backend.histogram('webhook_scan_queue_wait_ms', event.queueWaitMs, {
312
+ ...tags,
313
+ deferred: String(event.deferred),
314
+ })
315
+ if (event.observationLagMs !== undefined)
316
+ backend.histogram('webhook_scan_observation_lag_ms', event.observationLagMs, {
317
+ chain_id: String(event.chainId),
318
+ origin: event.origin,
319
+ })
320
+ // Block timestamp to scan completion: how far behind the chain the
321
+ // scanner is, reported by every scanned block rather than only by
322
+ // blocks that produce a delivery.
323
+ if (event.scanLagMs !== undefined)
324
+ backend.histogram('webhook_scan_lag_ms', event.scanLagMs, {
430
325
  chain_id: String(event.chainId),
431
- event_type: event.eventType,
432
- outcome: event.outcome,
433
- rpc_method: event.rpcMethod,
434
326
  })
435
327
  return
436
328
  }
437
- case 'webhook:poll-subscriptions':
438
- backend.gauge('webhook_poller_active_subscription_count', event.count, {
439
- chain_id: String(event.chainId),
440
- })
441
- return
442
- case 'webhook:poll-cycle': {
443
- const tags = {
444
- chain_id: String(event.chainId),
445
- outcome: event.outcome,
446
- trigger: event.trigger,
447
- }
448
- backend.count('webhook_poll_cycle_count', 1, tags)
449
- if (event.requestedBoundaryAdvanceBlocks !== undefined)
450
- backend.histogram(
451
- 'webhook_poll_requested_boundary_advance_blocks',
452
- event.requestedBoundaryAdvanceBlocks,
453
- tags,
454
- )
455
- return
456
- }
457
- case 'webhook:poll-start': {
458
- const tags = { chain_id: String(event.chainId), trigger: event.trigger }
459
- backend.count('webhook_poll_start_count', 1, tags)
460
- if (event.delayMs !== undefined)
461
- backend.histogram('webhook_poll_start_delay_ms', event.delayMs, tags)
329
+ case 'webhook:scan-step': {
330
+ const tags = { chain_id: String(event.chainId), step: event.step }
331
+ // Batch totals, so the count is required to read a per-invocation
332
+ // mean: a ten-block batch otherwise reports ten times a one-block
333
+ // batch at identical per-call latency.
334
+ backend.histogram('webhook_scan_step_duration_ms', event.durationMs, tags)
335
+ backend.count('webhook_scan_step_count', event.count, tags)
462
336
  return
463
337
  }
464
- case 'webhook:tick': {
465
- const { meta, result } = event
466
- const chain_id = String(result?.chainId ?? meta.chainId ?? 'unknown')
467
- backend.count('webhook_poller_tick_count', 1, { chain_id, outcome: meta.outcome })
468
- backend.histogram('webhook_poller_tick_duration_ms', meta.durationMs, {
469
- chain_id,
470
- outcome: meta.outcome,
471
- })
472
- if (event.cursorLagBlocks !== undefined)
473
- backend.gauge('webhook_subscription_cursor_lag_blocks', event.cursorLagBlocks, {
474
- chain_id,
475
- })
476
- if (!result) return
477
- backend.histogram('webhook_poller_subscription_count', result.subscriptions, { chain_id })
478
- backend.count('webhook_poller_scanned_count', result.scanned, { chain_id })
479
- backend.gauge('webhook_poller_safe_block', result.safeBlock, { chain_id })
480
- backend.count('webhook_poller_dispatch_count', result.dispatched, {
481
- chain_id,
482
- outcome: 'dispatched',
483
- })
484
- backend.count('webhook_poller_dispatch_count', result.failed, {
485
- chain_id,
486
- outcome: 'failed',
487
- })
338
+ case 'webhook:scan-backlog': {
339
+ const tags = { chain_id: String(event.chainId) }
340
+ backend.gauge('webhook_scan_pending_count', event.pendingCount, tags)
341
+ backend.gauge('webhook_scan_oldest_pending_age_ms', event.oldestPendingAgeMs ?? 0, tags)
342
+ if (event.watermarkLagBlocks !== undefined)
343
+ backend.gauge('webhook_scan_watermark_lag_blocks', event.watermarkLagBlocks, tags)
488
344
  return
489
345
  }
490
- case 'webhook:enqueue': {
491
- const tags = { chain_id: String(event.chainId), event_type: event.eventType }
492
- const outcome = event.ok ? 'ok' : 'error'
493
- backend.count('webhook_dispatch_enqueue_count', 1, { ...tags, outcome })
494
- backend.histogram('webhook_dispatch_enqueue_duration_ms', event.durationMs, {
495
- ...tags,
496
- outcome,
346
+ case 'webhook:scan-finality': {
347
+ backend.gauge('webhook_scan_finality_lag_blocks', event.lagBlocks, {
348
+ chain_id: String(event.chainId),
497
349
  })
498
350
  return
499
351
  }
@@ -541,6 +393,25 @@ export function webhooks(backend: Metrics): WebhookSink {
541
393
  })
542
394
  if (timings?.deliveryLogMs !== undefined)
543
395
  backend.histogram('webhook_delivery_log_write_ms', timings.deliveryLogMs, tags)
396
+ if (timings?.preflightMs !== undefined)
397
+ backend.histogram('webhook_delivery_preflight_ms', timings.preflightMs, tags)
398
+ if (timings?.settleMs !== undefined)
399
+ backend.histogram('webhook_delivery_settle_ms', timings.settleMs, tags)
400
+ // The two headline latency SLIs, both ending when the attempt leaves
401
+ // rather than when bookkeeping finishes. Attempt-bucketed for the
402
+ // same reason as end-to-end: retries carry designed backoff.
403
+ if (timings?.eventToAttemptMs !== undefined)
404
+ backend.histogram('webhook_event_to_attempt_ms', timings.eventToAttemptMs, {
405
+ ...tags,
406
+ attempt_bucket: attemptBucket(event.attempt),
407
+ outcome,
408
+ })
409
+ if (timings?.observedToAttemptMs !== undefined)
410
+ backend.histogram('webhook_observed_to_attempt_ms', timings.observedToAttemptMs, {
411
+ ...tags,
412
+ attempt_bucket: attemptBucket(event.attempt),
413
+ outcome,
414
+ })
544
415
  if (timings?.envelopeToQueueMs !== undefined)
545
416
  backend.histogram(
546
417
  'webhook_delivery_envelope_to_queue_ms',
@@ -33,6 +33,24 @@ describe('ChainIdQuery', () => {
33
33
  })
34
34
  })
35
35
 
36
+ describe('Limit', () => {
37
+ test('defaults to 10 and accepts page sizes through 50', () => {
38
+ expect(Schema.Limit.parse(undefined)).toMatchInlineSnapshot(`10`)
39
+ expect(Schema.Limit.parse('5')).toMatchInlineSnapshot(`5`)
40
+ expect(Schema.Limit.parse('50')).toMatchInlineSnapshot(`50`)
41
+ })
42
+
43
+ test('caps larger page sizes at 50', () => {
44
+ expect(Schema.Limit.parse('51')).toMatchInlineSnapshot(`50`)
45
+ expect(Schema.Limit.parse('200')).toMatchInlineSnapshot(`50`)
46
+ expect(Schema.Limit.parse('10000')).toMatchInlineSnapshot(`50`)
47
+ })
48
+
49
+ test('rejects page sizes below the minimum', () => {
50
+ expect(Schema.Limit.safeParse('4').success).toMatchInlineSnapshot(`false`)
51
+ })
52
+ })
53
+
36
54
  describe('timestampBound', () => {
37
55
  test('normalizes RFC 3339 offsets to UTC', () => {
38
56
  const bound = Schema.timestampBound('swaps', 'from')
@@ -337,11 +337,22 @@ export const TokenAmount = describe(
337
337
  */
338
338
  export const minLimit = 5
339
339
 
340
+ /** Maximum number of rows returned per page. Larger requests are clamped to this value. */
341
+ export const maxLimit = 50
342
+
340
343
  /** Page-size query parameter shared across list endpoints. */
341
344
  export const Limit = z
342
- ._default(z.coerce.number().check(z.int(), z.gte(minLimit), z.lte(200)), 10)
345
+ ._default(
346
+ z.pipe(
347
+ z.coerce.number().check(z.int(), z.gte(minLimit)),
348
+ z.transform((value) => Math.min(value, maxLimit)),
349
+ ),
350
+ 10,
351
+ )
343
352
  .check(
344
- z.describe('How many items to return per page (5\u2013200, default 10).'),
353
+ z.describe(
354
+ 'How many items to return per page (5\u201350, default 10). Use `nextCursor` to fetch more.',
355
+ ),
345
356
  z.meta({ examples: [10] }),
346
357
  )
347
358
 
@@ -1,7 +1,8 @@
1
1
  import { Tidx } from 'tidx.ts'
2
2
  import type { Chain } from 'viem'
3
3
 
4
- import * as Auth from './Auth.js'
4
+ import type * as Auth from './Auth.js'
5
+ import * as Credential from './Credential.js'
5
6
  import * as Errors from './Errors.js'
6
7
  import type * as Log from './Log.js'
7
8
  import * as Viem from './Viem.js'
@@ -40,7 +41,7 @@ export function resolve(
40
41
  ): resolve.Resolved {
41
42
  const config = typeof tidx === 'function' ? tidx(context) : tidx
42
43
  return {
43
- ...Auth.resolveCredential({ chainId: context.chainId, value: config?.auth }),
44
+ ...Credential.resolveCredential({ chainId: context.chainId, value: config?.auth }),
44
45
  ...(context.zone && config?.zoneHeaders ? { zoneHeaders: config.zoneHeaders } : {}),
45
46
  baseUrl:
46
47
  Viem.resolveUrl(config?.baseUrl, context.chainId) ??
@@ -319,7 +320,7 @@ export declare namespace getClient {
319
320
  type Tidx =
320
321
  | {
321
322
  /** Shared or per-chain TIDX credentials. Values containing `:` use Basic auth; others use Bearer auth. */
322
- auth?: Auth.ChainCredential | undefined
323
+ auth?: Credential.ChainCredential | undefined
323
324
  /** Resolves the TIDX base URL for a Tempo chain id, or a static base URL. */
324
325
  baseUrl?: UrlResolver | undefined
325
326
  /** Headers sent to Zone indexer upstreams. */
@@ -327,7 +328,7 @@ export declare namespace getClient {
327
328
  }
328
329
  | ((context: Viem.resolveRpc.Context) => {
329
330
  /** Shared or per-chain TIDX credentials, or undefined for none. */
330
- auth?: Auth.ChainCredential | undefined
331
+ auth?: Credential.ChainCredential | undefined
331
332
  /** TIDX base URL to use; falls back to the built-in public host when omitted. */
332
333
  baseUrl?: string | undefined
333
334
  /** Headers sent to Zone indexer upstreams. */
@@ -220,6 +220,10 @@ describe('resolveRpc', () => {
220
220
  })
221
221
 
222
222
  describe('getClient', () => {
223
+ test('disables CCIP-Read', () => {
224
+ expect(Viem.getClient({ chainId: 4217 }).ccipRead).toBe(false)
225
+ })
226
+
223
227
  test('disables multicall for Zones', () => {
224
228
  const l1Client = Viem.getClient({ chainId: 4217 })
225
229
  const zoneClient = Viem.getClient({ chainId: zone.id, zone })
@@ -6,7 +6,8 @@ import type { TempoActions } from 'viem/tempo'
6
6
  import { tempoMainnet, tempoTestnet } from 'viem/tempo/chains'
7
7
  import type * as z from 'zod/mini'
8
8
 
9
- import * as Auth from './Auth.js'
9
+ import type * as Auth from './Auth.js'
10
+ import * as Credential from './Credential.js'
10
11
  import type * as Schema from './Schema.js'
11
12
 
12
13
  /** Known Tempo chains keyed by id, for metadata lookups. */
@@ -79,7 +80,7 @@ export function resolveRpc(
79
80
  context: resolveRpc.Context,
80
81
  ): resolveRpc.Resolved {
81
82
  const config = typeof rpc === 'function' ? rpc(context) : rpc
82
- const auth = Auth.resolveCredential({ chainId: context.chainId, value: config?.auth })
83
+ const auth = Credential.resolveCredential({ chainId: context.chainId, value: config?.auth })
83
84
  const publicZoneUrl = context.zone
84
85
  ? (resolveUrl(config?.publicZoneUrl, context.chainId) ?? context.zone.rpcUrls.default.http[0])
85
86
  : undefined
@@ -165,6 +166,7 @@ export function getClient(options: getClient.Options): getClient.ReturnType {
165
166
  // configure a deployed multicall3, can still serve the aggregated call.
166
167
  // Zones disable multicall entirely and issue concurrent direct `eth_call`s instead.
167
168
  batch: { multicall: zone ? false : { deployless: true } },
169
+ ccipRead: false,
168
170
  chain: { ...tempoMainnet, id: chainId },
169
171
  transport: http(rpcUrl, fetchOptions ? { fetchOptions } : {}),
170
172
  })
@@ -250,7 +252,7 @@ export declare namespace getClient {
250
252
  type Rpc =
251
253
  | {
252
254
  /** Shared or per-chain RPC credentials. Values containing `:` use Basic auth; others use Bearer auth. */
253
- auth?: Auth.ChainCredential | undefined
255
+ auth?: Credential.ChainCredential | undefined
254
256
  /** Resolves the public Zone RPC URL used with caller-provided authorization. */
255
257
  publicZoneUrl?: UrlResolver | undefined
256
258
  /** Headers sent to Zone RPC upstreams. */
@@ -260,7 +262,7 @@ export declare namespace getClient {
260
262
  }
261
263
  | ((context: resolveRpc.Context) => {
262
264
  /** Shared or per-chain RPC credentials, or undefined for none. */
263
- auth?: Auth.ChainCredential | undefined
265
+ auth?: Credential.ChainCredential | undefined
264
266
  /** Public Zone RPC URL used with caller-provided authorization. */
265
267
  publicZoneUrl?: string | undefined
266
268
  /** Headers sent to Zone RPC upstreams. */