tapimo 0.10.0 → 0.10.2

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 (295) hide show
  1. package/dist/ApiKey.d.ts +2 -1
  2. package/dist/ApiKey.d.ts.map +1 -1
  3. package/dist/ApiKeys.d.ts +4 -2
  4. package/dist/ApiKeys.d.ts.map +1 -1
  5. package/dist/ApiKeys.js +26 -1
  6. package/dist/ApiKeys.js.map +1 -1
  7. package/dist/App.d.ts +6007 -5003
  8. package/dist/App.d.ts.map +1 -1
  9. package/dist/App.js +1 -0
  10. package/dist/App.js.map +1 -1
  11. package/dist/Webhooks.d.ts +1 -1
  12. package/dist/Webhooks.d.ts.map +1 -1
  13. package/dist/Webhooks.js +6 -6
  14. package/dist/Webhooks.js.map +1 -1
  15. package/dist/admin/App.d.ts +91 -84
  16. package/dist/admin/App.d.ts.map +1 -1
  17. package/dist/admin/apps/api-keys.d.ts +22 -13
  18. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  19. package/dist/admin/apps/api-keys.js +6 -0
  20. package/dist/admin/apps/api-keys.js.map +1 -1
  21. package/dist/admin/apps/organizations.d.ts +4 -2
  22. package/dist/admin/apps/organizations.d.ts.map +1 -1
  23. package/dist/admin/ui.generated.d.ts +1 -1
  24. package/dist/admin/ui.generated.js +69 -69
  25. package/dist/admin/ui.generated.js.map +1 -1
  26. package/dist/analytics/Analytics.d.ts.map +1 -1
  27. package/dist/analytics/Analytics.js +232 -0
  28. package/dist/analytics/Analytics.js.map +1 -1
  29. package/dist/analytics/tables/requestEvents.d.ts +4 -0
  30. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  31. package/dist/analytics/tables/requestEvents.js +2 -0
  32. package/dist/analytics/tables/requestEvents.js.map +1 -1
  33. package/dist/apps/Zones.d.ts +4266 -82
  34. package/dist/apps/Zones.d.ts.map +1 -1
  35. package/dist/apps/Zones.js +27 -8
  36. package/dist/apps/Zones.js.map +1 -1
  37. package/dist/apps/data/App.d.ts +3467 -2696
  38. package/dist/apps/data/App.d.ts.map +1 -1
  39. package/dist/apps/data/App.js +2 -0
  40. package/dist/apps/data/App.js.map +1 -1
  41. package/dist/apps/data/routes/activities.d.ts +2999 -100
  42. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  43. package/dist/apps/data/routes/activities.js +340 -2
  44. package/dist/apps/data/routes/activities.js.map +1 -1
  45. package/dist/apps/data/routes/addresses.d.ts +514 -0
  46. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  47. package/dist/apps/data/routes/balances.d.ts +26 -26
  48. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  49. package/dist/apps/data/routes/balances.js +7 -3
  50. package/dist/apps/data/routes/balances.js.map +1 -1
  51. package/dist/apps/data/routes/blocks.d.ts +4 -4
  52. package/dist/apps/data/routes/earn.d.ts +75 -75
  53. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  54. package/dist/apps/data/routes/earn.js +65 -36
  55. package/dist/apps/data/routes/earn.js.map +1 -1
  56. package/dist/apps/data/routes/exchanges.d.ts +124 -120
  57. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  58. package/dist/apps/data/routes/exchanges.js +115 -77
  59. package/dist/apps/data/routes/exchanges.js.map +1 -1
  60. package/dist/apps/data/routes/fee-amm.d.ts +24 -24
  61. package/dist/apps/data/routes/indexer.d.ts +2 -1
  62. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  63. package/dist/apps/data/routes/indexer.js +3 -2
  64. package/dist/apps/data/routes/indexer.js.map +1 -1
  65. package/dist/apps/data/routes/receipts.d.ts +4 -3
  66. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  67. package/dist/apps/data/routes/receipts.js +15 -14
  68. package/dist/apps/data/routes/receipts.js.map +1 -1
  69. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  70. package/dist/apps/data/routes/rpc.js +5 -1
  71. package/dist/apps/data/routes/rpc.js.map +1 -1
  72. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  73. package/dist/apps/data/routes/tokens.d.ts +1 -1
  74. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  75. package/dist/apps/data/routes/tokens.js +4 -3
  76. package/dist/apps/data/routes/tokens.js.map +1 -1
  77. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  78. package/dist/apps/data/routes/transfers.js +25 -7
  79. package/dist/apps/data/routes/transfers.js.map +1 -1
  80. package/dist/apps/data/routes/verified-tokens.d.ts +37 -37
  81. package/dist/apps/data/routes/webhooks.d.ts +71 -1
  82. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  83. package/dist/apps/data/routes/webhooks.js +177 -23
  84. package/dist/apps/data/routes/webhooks.js.map +1 -1
  85. package/dist/apps/data/routes/zones.js +1 -1
  86. package/dist/apps/data/routes/zones.js.map +1 -1
  87. package/dist/apps/funding/App.d.ts +322 -89
  88. package/dist/apps/funding/App.d.ts.map +1 -1
  89. package/dist/apps/funding/routes/deposit-addresses.d.ts +375 -45
  90. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  91. package/dist/apps/funding/routes/deposit-addresses.js +208 -12
  92. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  93. package/dist/apps/funding/routes/transfers.d.ts +32 -32
  94. package/dist/apps/funding/routes/transfers.js +1 -1
  95. package/dist/apps/funding/routes/transfers.js.map +1 -1
  96. package/dist/apps/management/App.d.ts +418 -418
  97. package/dist/apps/management/routes/api-keys.d.ts +50 -50
  98. package/dist/apps/management/routes/billing.d.ts +72 -72
  99. package/dist/apps/management/routes/billing.js +1 -1
  100. package/dist/apps/management/routes/billing.js.map +1 -1
  101. package/dist/apps/management/routes/faucet.d.ts +8 -8
  102. package/dist/apps/management/routes/faucet.js +2 -2
  103. package/dist/apps/management/routes/faucet.js.map +1 -1
  104. package/dist/apps/management/routes/invite-links.d.ts +42 -42
  105. package/dist/apps/management/routes/me.d.ts +6 -6
  106. package/dist/apps/management/routes/members.d.ts +14 -14
  107. package/dist/apps/management/routes/projects.d.ts +40 -40
  108. package/dist/apps/management/routes/usage.d.ts +8 -8
  109. package/dist/apps/management/routes/webhooks.d.ts +49 -49
  110. package/dist/apps/mcp.d.ts.map +1 -1
  111. package/dist/apps/mcp.js +4 -1
  112. package/dist/apps/mcp.js.map +1 -1
  113. package/dist/db/Db.d.ts +2 -0
  114. package/dist/db/Db.d.ts.map +1 -1
  115. package/dist/db/Db.js +90 -0
  116. package/dist/db/Db.js.map +1 -1
  117. package/dist/db/Schema.d.ts +19 -0
  118. package/dist/db/Schema.d.ts.map +1 -1
  119. package/dist/db/Schema.js +24 -0
  120. package/dist/db/Schema.js.map +1 -1
  121. package/dist/db/tables/fundingDepositAddresses.d.ts +20 -0
  122. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
  123. package/dist/db/tables/fundingDepositAddresses.js +17 -0
  124. package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
  125. package/dist/db/tables/fundingDeposits.d.ts +41 -0
  126. package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
  127. package/dist/db/tables/fundingDeposits.js +69 -0
  128. package/dist/db/tables/fundingDeposits.js.map +1 -1
  129. package/dist/db/tables/webhookQueueEvents.d.ts +122 -0
  130. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -0
  131. package/dist/db/tables/webhookQueueEvents.js +160 -0
  132. package/dist/db/tables/webhookQueueEvents.js.map +1 -0
  133. package/dist/db/tables/webhookSubscriptions.d.ts +2 -0
  134. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  135. package/dist/db/tables/webhookSubscriptions.js +10 -0
  136. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  137. package/dist/internal/Auth.d.ts +3 -26
  138. package/dist/internal/Auth.d.ts.map +1 -1
  139. package/dist/internal/Auth.js +6 -37
  140. package/dist/internal/Auth.js.map +1 -1
  141. package/dist/internal/Credential.d.ts +27 -0
  142. package/dist/internal/Credential.d.ts.map +1 -0
  143. package/dist/internal/Credential.js +34 -0
  144. package/dist/internal/Credential.js.map +1 -0
  145. package/dist/internal/EarnRates.js +11 -7
  146. package/dist/internal/EarnRates.js.map +1 -1
  147. package/dist/internal/Log.d.ts +8 -0
  148. package/dist/internal/Log.d.ts.map +1 -1
  149. package/dist/internal/Log.js +4 -0
  150. package/dist/internal/Log.js.map +1 -1
  151. package/dist/internal/MetricSink.d.ts +51 -0
  152. package/dist/internal/MetricSink.d.ts.map +1 -1
  153. package/dist/internal/MetricSink.js +54 -2
  154. package/dist/internal/MetricSink.js.map +1 -1
  155. package/dist/internal/RateLimit.d.ts +4 -6
  156. package/dist/internal/RateLimit.d.ts.map +1 -1
  157. package/dist/internal/RateLimit.js +15 -14
  158. package/dist/internal/RateLimit.js.map +1 -1
  159. package/dist/internal/Store.d.ts +2 -1
  160. package/dist/internal/Store.d.ts.map +1 -1
  161. package/dist/internal/Store.js +57 -18
  162. package/dist/internal/Store.js.map +1 -1
  163. package/dist/internal/Tidx.d.ts +4 -3
  164. package/dist/internal/Tidx.d.ts.map +1 -1
  165. package/dist/internal/Tidx.js +2 -2
  166. package/dist/internal/Tidx.js.map +1 -1
  167. package/dist/internal/Value.d.ts +2 -0
  168. package/dist/internal/Value.d.ts.map +1 -1
  169. package/dist/internal/Value.js +25 -0
  170. package/dist/internal/Value.js.map +1 -1
  171. package/dist/internal/Viem.d.ts +4 -3
  172. package/dist/internal/Viem.d.ts.map +1 -1
  173. package/dist/internal/Viem.js +3 -2
  174. package/dist/internal/Viem.js.map +1 -1
  175. package/dist/internal/WebhookTransfer.d.ts +20 -0
  176. package/dist/internal/WebhookTransfer.d.ts.map +1 -1
  177. package/dist/internal/WebhookTransfer.js +7 -4
  178. package/dist/internal/WebhookTransfer.js.map +1 -1
  179. package/dist/internal/Webhooks.d.ts +141 -0
  180. package/dist/internal/Webhooks.d.ts.map +1 -1
  181. package/dist/internal/Webhooks.js +139 -10
  182. package/dist/internal/Webhooks.js.map +1 -1
  183. package/dist/internal/funding/providers/relay.d.ts +3 -4
  184. package/dist/internal/funding/providers/relay.d.ts.map +1 -1
  185. package/dist/internal/funding/providers/relay.js +59 -8
  186. package/dist/internal/funding/providers/relay.js.map +1 -1
  187. package/dist/internal/index.d.ts +1 -0
  188. package/dist/internal/index.d.ts.map +1 -1
  189. package/dist/internal/index.js +1 -0
  190. package/dist/internal/index.js.map +1 -1
  191. package/package.json +3 -3
  192. package/src/ApiKeys.test.ts +20 -0
  193. package/src/ApiKeys.ts +28 -1
  194. package/src/App.test.ts +40 -8
  195. package/src/App.ts +1 -0
  196. package/src/Client.test-d.ts +34 -1
  197. package/src/Webhooks.ts +20 -18
  198. package/src/admin/apps/api-keys.test.ts +9 -0
  199. package/src/admin/apps/api-keys.ts +15 -0
  200. package/src/admin/ui/src/lib/query-client.ts +3 -2
  201. package/src/admin/ui/src/routes/api-keys.tsx +31 -9
  202. package/src/admin/ui.generated.ts +69 -69
  203. package/src/analytics/Analytics.test.ts +42 -0
  204. package/src/analytics/Analytics.ts +232 -0
  205. package/src/analytics/tables/requestEvents.ts +6 -0
  206. package/src/apps/Zones.test.ts +20 -3
  207. package/src/apps/Zones.ts +28 -8
  208. package/src/apps/composition.test.ts +2 -0
  209. package/src/apps/data/App.test.ts +2 -2
  210. package/src/apps/data/App.ts +2 -0
  211. package/src/apps/data/routes/activities.bench.ts +41 -0
  212. package/src/apps/data/routes/activities.test.ts +103 -2
  213. package/src/apps/data/routes/activities.ts +401 -2
  214. package/src/apps/data/routes/balances.bench.ts +21 -0
  215. package/src/apps/data/routes/balances.ts +7 -3
  216. package/src/apps/data/routes/blocks.bench.ts +34 -0
  217. package/src/apps/data/routes/coingecko.bench.ts +53 -0
  218. package/src/apps/data/routes/earn.bench.ts +71 -0
  219. package/src/apps/data/routes/earn.ts +66 -36
  220. package/src/apps/data/routes/exchanges.bench.ts +85 -0
  221. package/src/apps/data/routes/exchanges.test.ts +8 -3
  222. package/src/apps/data/routes/exchanges.ts +134 -82
  223. package/src/apps/data/routes/fee-amm.bench.ts +13 -0
  224. package/src/apps/data/routes/indexer.bench.ts +8 -0
  225. package/src/apps/data/routes/indexer.test.ts +2 -1
  226. package/src/apps/data/routes/indexer.ts +3 -2
  227. package/src/apps/data/routes/receipts.bench.ts +39 -0
  228. package/src/apps/data/routes/receipts.ts +16 -14
  229. package/src/apps/data/routes/rpc.bench.ts +14 -0
  230. package/src/apps/data/routes/rpc.test.ts +27 -0
  231. package/src/apps/data/routes/rpc.ts +5 -1
  232. package/src/apps/data/routes/tokenlist.bench.ts +3 -0
  233. package/src/apps/data/routes/tokens.bench.ts +76 -0
  234. package/src/apps/data/routes/tokens.ts +3 -2
  235. package/src/apps/data/routes/transactions.bench.ts +48 -0
  236. package/src/apps/data/routes/transfers.bench.ts +28 -0
  237. package/src/apps/data/routes/transfers.ts +36 -10
  238. package/src/apps/data/routes/valuation.bench.ts +14 -0
  239. package/src/apps/data/routes/verified-tokens.bench.ts +8 -0
  240. package/src/apps/data/routes/webhooks.bench.ts +26 -0
  241. package/src/apps/data/routes/webhooks.scan.test.ts +326 -0
  242. package/src/apps/data/routes/webhooks.test.ts +53 -6
  243. package/src/apps/data/routes/webhooks.ts +252 -24
  244. package/src/apps/data/routes/zones.bench.ts +32 -0
  245. package/src/apps/data/routes/zones.test.ts +1 -1
  246. package/src/apps/data/routes/zones.ts +1 -1
  247. package/src/apps/funding/routes/chains.test.ts +1 -1
  248. package/src/apps/funding/routes/deposit-addresses.test.ts +217 -3
  249. package/src/apps/funding/routes/deposit-addresses.ts +255 -19
  250. package/src/apps/funding/routes/providers.test.ts +1 -1
  251. package/src/apps/funding/routes/quotes.test.ts +1 -1
  252. package/src/apps/funding/routes/transfers.test.ts +20 -8
  253. package/src/apps/funding/routes/transfers.ts +1 -1
  254. package/src/apps/management/routes/billing.ts +1 -1
  255. package/src/apps/management/routes/faucet.ts +2 -2
  256. package/src/apps/management/routes/me.test.ts +31 -0
  257. package/src/apps/mcp.ts +4 -1
  258. package/src/db/Db.ts +117 -0
  259. package/src/db/Schema.ts +32 -0
  260. package/src/db/tables/fundingDepositAddresses.test.ts +28 -0
  261. package/src/db/tables/fundingDepositAddresses.ts +39 -0
  262. package/src/db/tables/fundingDeposits.test.ts +82 -0
  263. package/src/db/tables/fundingDeposits.ts +130 -0
  264. package/src/db/tables/webhookQueueEvents.ts +285 -0
  265. package/src/db/tables/webhookSubscriptions.ts +15 -0
  266. package/src/internal/Auth.test.ts +61 -98
  267. package/src/internal/Auth.ts +9 -63
  268. package/src/internal/Credential.test.ts +51 -0
  269. package/src/internal/Credential.ts +58 -0
  270. package/src/internal/EarnRates.ts +13 -7
  271. package/src/internal/EarnVaults.test.ts +2 -2
  272. package/src/internal/Log.test.ts +3 -1
  273. package/src/internal/Log.ts +14 -0
  274. package/src/internal/MetricSink.test.ts +108 -2
  275. package/src/internal/MetricSink.ts +112 -2
  276. package/src/internal/RateLimit.test.ts +23 -18
  277. package/src/internal/RateLimit.ts +23 -15
  278. package/src/internal/Store.test.ts +99 -13
  279. package/src/internal/Store.ts +52 -17
  280. package/src/internal/Tidx.test.ts +4 -4
  281. package/src/internal/Tidx.ts +5 -4
  282. package/src/internal/Value.test.ts +25 -0
  283. package/src/internal/Value.ts +29 -0
  284. package/src/internal/Viem.test.ts +4 -0
  285. package/src/internal/Viem.ts +6 -4
  286. package/src/internal/WebhookTransfer.test.ts +89 -0
  287. package/src/internal/WebhookTransfer.ts +11 -7
  288. package/src/internal/Webhooks.test.ts +488 -0
  289. package/src/internal/Webhooks.ts +291 -10
  290. package/src/internal/funding/Provider.test.ts +1 -1
  291. package/src/internal/funding/providers/index.test.ts +73 -3
  292. package/src/internal/funding/providers/relay.test.ts +1 -5
  293. package/src/internal/funding/providers/relay.ts +56 -12
  294. package/src/internal/index.test.ts +1 -0
  295. package/src/internal/index.ts +1 -0
@@ -376,7 +376,7 @@ export var schema;
376
376
  sender: Schema.Address.check(z.describe('Address that sent the transfer.'), z.meta({ examples: ['0xe7687128b0a808c2831ff94d4f7b2fb35c65af38'] })),
377
377
  timestamp: z.iso
378
378
  .datetime()
379
- .check(z.describe('Block timestamp as an ISO 8601 string.'), z.meta({ examples: ['2026-01-14T18:38:03.000Z'] })),
379
+ .check(z.describe('Block timestamp as an ISO 8601 string.'), z.meta({ examples: ['2026-01-14T18:38:03.685Z'] })),
380
380
  token: z.optional(z
381
381
  .object({
382
382
  decimals: z
@@ -444,7 +444,7 @@ export var schema;
444
444
  .check(z.int(), z.nonnegative(), z.describe('Index of the log within its block.'), z.meta({ examples: [0] })),
445
445
  timestamp: z.iso
446
446
  .datetime()
447
- .check(z.describe('When the block was produced, as an ISO 8601 timestamp.'), z.meta({ examples: ['2026-01-14T18:38:03.000Z'] })),
447
+ .check(z.describe('When the block was produced, as an ISO 8601 timestamp.'), z.meta({ examples: ['2026-01-14T18:38:03.685Z'] })),
448
448
  topics: z.array(Schema.Hash).check(z.describe('Log topics, starting with the event signature hash (topic 0).'), z.meta({
449
449
  examples: [
450
450
  [
@@ -486,7 +486,7 @@ export var schema;
486
486
  .check(z.int(), z.nonnegative(), z.describe('Number of transactions included in this block.'), z.meta({ examples: [3] })),
487
487
  timestamp: z.iso
488
488
  .datetime()
489
- .check(z.describe('When the block was produced, as an ISO 8601 timestamp.'), z.meta({ examples: ['2026-01-14T18:38:03.000Z'] })),
489
+ .check(z.describe('When the block was produced, as an ISO 8601 timestamp.'), z.meta({ examples: ['2026-01-14T18:38:03.685Z'] })),
490
490
  })
491
491
  .check(z.describe('Payload for a `block:created` event (a new block header).'));
492
492
  /** A `ping` (synthetic test-delivery) payload, sent by `POST /webhooks/:id/ping`. */
@@ -1659,15 +1659,11 @@ export async function poll(options) {
1659
1659
  // Token metadata is best-effort for webhooks: transfers use it for display,
1660
1660
  // while transactions expose fee-token references without blocking delivery.
1661
1661
  const enrichStartedAt = Date.now();
1662
- const data = eventType === 'token:transfer'
1663
- ? await withTokenMetadata(item.data, { cache: cache_metadata, chainId, client })
1664
- : eventType === 'transaction:included'
1665
- ? await withFeeTokenMetadata(item.data, {
1666
- cache: cache_metadata,
1667
- chainId,
1668
- client,
1669
- })
1670
- : item.data;
1662
+ const data = await enrichEventData(eventType, item.data, {
1663
+ cache: cache_metadata,
1664
+ chainId,
1665
+ client,
1666
+ });
1671
1667
  durations.enrich += Date.now() - enrichStartedAt;
1672
1668
  observed.add('enrich');
1673
1669
  options.signal?.throwIfAborted();
@@ -1685,7 +1681,7 @@ export async function poll(options) {
1685
1681
  for (const item of page.items)
1686
1682
  dispatchables.push(await buildDispatchable(item));
1687
1683
  options.signal?.throwIfAborted();
1688
- const delivered = await dispatchBatch(dispatchables);
1684
+ const delivered = await dispatchBatch(dispatchables, { db });
1689
1685
  if (delivered.ok) {
1690
1686
  accepted = page.items.at(-1)?.cursor;
1691
1687
  result.dispatched += dispatchables.length;
@@ -2237,6 +2233,17 @@ function decodeScanLog(log, event) {
2237
2233
  return log;
2238
2234
  }
2239
2235
  }
2236
+ /**
2237
+ * Attaches best-effort token metadata to one matched row before delivery.
2238
+ * Transfers gain display metadata; transactions resolve their fee token.
2239
+ */
2240
+ export async function enrichEventData(eventType, data, options) {
2241
+ if (eventType === 'token:transfer')
2242
+ return withTokenMetadata(data, options);
2243
+ if (eventType === 'transaction:included')
2244
+ return withFeeTokenMetadata(data, options);
2245
+ return data;
2246
+ }
2240
2247
  /** Scans contract logs for one subscription, then re-checks every filter in memory. */
2241
2248
  async function scanLogs(options) {
2242
2249
  const { batchSize, cache, client, cursor, filters, maxBlocks, safeBlock } = options;
@@ -2286,7 +2293,7 @@ async function scanLogs(options) {
2286
2293
  continue;
2287
2294
  if (!matchesLog(log, f, event))
2288
2295
  continue;
2289
- const data = toLogEvent(log, blockIso(await cache.header(blockNumber)), f.signature, event);
2296
+ const data = toLogEvent(log, Value.blockToIso(await cache.header(blockNumber)), f.signature, event);
2290
2297
  if (data) {
2291
2298
  items.push({ cursor: [data.blockNumber, data.logIndex], data });
2292
2299
  if (items.length >= batchSize)
@@ -2338,13 +2345,6 @@ function matchesLog(log, f, event) {
2338
2345
  }
2339
2346
  return true;
2340
2347
  }
2341
- // block:created (getBlock)
2342
- /** Converts a viem block's `timestamp` (seconds) to an ISO 8601 string. */
2343
- function blockIso(block) {
2344
- if (!block || typeof block.timestamp !== 'bigint')
2345
- return undefined;
2346
- return new Date(Number(block.timestamp) * 1000).toISOString();
2347
- }
2348
2348
  /** Builds the delivered `block:created` payload from a viem block header. */
2349
2349
  function toBlockEvent(block) {
2350
2350
  const number = block.number === null ? undefined : Number(block.number);
@@ -2353,7 +2353,7 @@ function toBlockEvent(block) {
2353
2353
  const miner = Schema.Address.safeParse(block.miner);
2354
2354
  const gasUsed = typeof block.gasUsed === 'bigint' ? Number(block.gasUsed) : undefined;
2355
2355
  const gasLimit = typeof block.gasLimit === 'bigint' ? Number(block.gasLimit) : undefined;
2356
- const timestamp = blockIso(block);
2356
+ const timestamp = Value.blockToIso(block);
2357
2357
  const transactionCount = Array.isArray(block.transactions) ? block.transactions.length : 0;
2358
2358
  if (number === undefined ||
2359
2359
  !hash.success ||
@@ -2449,6 +2449,9 @@ async function scanTxs(options) {
2449
2449
  ? block['transactions']
2450
2450
  : [];
2451
2451
  const timestamp = asString(block['timestamp']);
2452
+ // A transaction's own `blockTimestamp` is seconds-only, so the delivered
2453
+ // timestamp comes from the enclosing block, which carries milliseconds.
2454
+ const blockTime = Value.blockToIso(block);
2452
2455
  for (let offset = 0; offset < transactions.length; offset++) {
2453
2456
  const transaction = transactions[offset];
2454
2457
  const transactionIndex = Value.hexToNumber(transaction['transactionIndex']) ?? offset;
@@ -2459,7 +2462,8 @@ async function scanTxs(options) {
2459
2462
  const parsed = Transactions.schema.Rpc.Transaction.safeParse(transaction);
2460
2463
  if (!parsed.success)
2461
2464
  continue;
2462
- const data = Transactions.humanizeTransaction(parsed.data);
2465
+ const humanized = Transactions.humanizeTransaction(parsed.data);
2466
+ const data = blockTime === undefined ? humanized : { ...humanized, timestamp: blockTime };
2463
2467
  items.push({ cursor: [blockNumber, transactionIndex], data });
2464
2468
  if (items.length >= batchSize)
2465
2469
  return { hasMore: true, items };
@@ -2608,6 +2612,156 @@ function unauthorized(c) {
2608
2612
  status: 401,
2609
2613
  });
2610
2614
  }
2615
+ // Pure per-block scanning (no RPC, no cursors, no pagination).
2616
+ // Pure over its inputs: the caller fetches the raw block (with transactions)
2617
+ // and its logs; enrichment happens downstream. Rows require a parsable block number.
2618
+ /**
2619
+ * Matches one fetched block against a subscription set, producing the rows
2620
+ * each event type delivers. Invalid stored filters fail closed into `invalid`.
2621
+ */
2622
+ export function scanBlock(options) {
2623
+ const { block, logs, subscriptions } = options;
2624
+ const blockNumber = Value.hexToNumber(block['number']);
2625
+ const timestamp = Value.blockToIso(block);
2626
+ const header = toScanHeader(block);
2627
+ const viemLogs = logs.map(fromRpcLog);
2628
+ const matches = [];
2629
+ const invalid = [];
2630
+ for (const subscription of subscriptions) {
2631
+ const filters = (() => {
2632
+ try {
2633
+ return parseFilters(subscription.eventType, subscription.filters);
2634
+ }
2635
+ catch (error) {
2636
+ invalid.push({ error, subscription });
2637
+ return undefined;
2638
+ }
2639
+ })();
2640
+ if (filters === undefined)
2641
+ continue;
2642
+ const matched = matches.length;
2643
+ const push = (data, logIndex) => {
2644
+ if (blockNumber !== undefined)
2645
+ matches.push({ blockNumber, data, logIndex, subscription });
2646
+ };
2647
+ // A throwing matcher invalidates only this subscription, rolling back any
2648
+ // rows it matched before the failure.
2649
+ try {
2650
+ switch (subscription.eventType) {
2651
+ case 'block:created': {
2652
+ const f = filters;
2653
+ if (!matchesBlock(header, f))
2654
+ break;
2655
+ const data = toBlockEvent(header);
2656
+ if (data)
2657
+ push(data, 0);
2658
+ break;
2659
+ }
2660
+ case 'log:emitted': {
2661
+ const f = filters;
2662
+ const event = parseEvent(f.signature);
2663
+ if (f.signature && (!event || !/^[A-Za-z_]\w*$/.test(event.name))) {
2664
+ invalid.push({
2665
+ error: new Webhooks.InvalidFilterError('log:emitted', [
2666
+ { message: 'Unsupported event signature.', path: ['signature'] },
2667
+ ]),
2668
+ subscription,
2669
+ });
2670
+ break;
2671
+ }
2672
+ // Validate arg names up front so a bad filter fails closed even
2673
+ // when the block has no decodable logs.
2674
+ const unknown = event && f.args
2675
+ ? Object.keys(f.args).find((name) => !event.inputs.some((input) => input.name === name))
2676
+ : undefined;
2677
+ if (unknown !== undefined) {
2678
+ invalid.push({
2679
+ error: new Webhooks.InvalidFilterError('log:emitted', [
2680
+ { message: `Unknown event argument: ${unknown}.`, path: ['args', unknown] },
2681
+ ]),
2682
+ subscription,
2683
+ });
2684
+ break;
2685
+ }
2686
+ for (const log of viemLogs) {
2687
+ const decoded = event ? decodeScanLog(log, event) : log;
2688
+ const logIndex = decoded.logIndex ?? undefined;
2689
+ if (logIndex === undefined)
2690
+ continue;
2691
+ if (!matchesLog(decoded, f, event))
2692
+ continue;
2693
+ const data = toLogEvent(decoded, timestamp, f.signature, event);
2694
+ if (data)
2695
+ push(data, logIndex);
2696
+ }
2697
+ break;
2698
+ }
2699
+ case 'token:transfer': {
2700
+ if (timestamp === undefined)
2701
+ break;
2702
+ const f = filters;
2703
+ for (const log of logs) {
2704
+ const data = WebhookTransfer.decode(log, timestamp);
2705
+ if (!data || !WebhookTransfer.matches(data, f))
2706
+ continue;
2707
+ push(data, data.logIndex);
2708
+ }
2709
+ break;
2710
+ }
2711
+ case 'transaction:included': {
2712
+ const f = filters;
2713
+ const includeCalls = f.includeCalls ?? true;
2714
+ const transactions = Array.isArray(block['transactions'])
2715
+ ? block['transactions']
2716
+ : [];
2717
+ const rawTimestamp = asString(block['timestamp']);
2718
+ for (let offset = 0; offset < transactions.length; offset++) {
2719
+ const transaction = transactions[offset];
2720
+ if (!transaction || typeof transaction !== 'object')
2721
+ continue;
2722
+ const tx = transaction;
2723
+ const transactionIndex = Value.hexToNumber(tx['transactionIndex']) ?? offset;
2724
+ if (!matchesTx(tx, f, includeCalls, rawTimestamp, blockNumber ?? 0))
2725
+ continue;
2726
+ const parsed = Transactions.schema.Rpc.Transaction.safeParse(tx);
2727
+ if (!parsed.success)
2728
+ continue;
2729
+ const humanized = Transactions.humanizeTransaction(parsed.data);
2730
+ // A transaction's own timestamp is seconds-only; the enclosing
2731
+ // block carries milliseconds.
2732
+ push(timestamp === undefined ? humanized : { ...humanized, timestamp }, transactionIndex);
2733
+ }
2734
+ break;
2735
+ }
2736
+ }
2737
+ }
2738
+ catch (error) {
2739
+ matches.length = matched;
2740
+ invalid.push({ error, subscription });
2741
+ }
2742
+ }
2743
+ return { invalid, matches };
2744
+ }
2745
+ /** Normalizes a raw RPC header to the viem-shaped fields the block matchers read. */
2746
+ function toScanHeader(block) {
2747
+ const quantity = (value) => {
2748
+ try {
2749
+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint'
2750
+ ? BigInt(value)
2751
+ : undefined;
2752
+ }
2753
+ catch {
2754
+ return undefined;
2755
+ }
2756
+ };
2757
+ return {
2758
+ ...block,
2759
+ gasLimit: quantity(block['gasLimit']),
2760
+ gasUsed: quantity(block['gasUsed']),
2761
+ number: quantity(block['number']),
2762
+ timestamp: quantity(block['timestamp']),
2763
+ };
2764
+ }
2611
2765
  class BlockUnavailableError extends Error {
2612
2766
  name = 'Webhooks.BlockUnavailableError';
2613
2767
  constructor(blockNumber) {