tapimo 0.5.0 → 0.5.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 (197) hide show
  1. package/dist/App.d.ts +4854 -4829
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +20 -2
  4. package/dist/App.js.map +1 -1
  5. package/dist/Assets.js +3 -3
  6. package/dist/Assets.js.map +1 -1
  7. package/dist/Scope.d.ts.map +1 -1
  8. package/dist/Scope.js +1 -2
  9. package/dist/Scope.js.map +1 -1
  10. package/dist/Webhooks.d.ts +1 -1
  11. package/dist/Webhooks.d.ts.map +1 -1
  12. package/dist/Webhooks.js +5 -10
  13. package/dist/Webhooks.js.map +1 -1
  14. package/dist/admin/App.d.ts +6 -34
  15. package/dist/admin/App.d.ts.map +1 -1
  16. package/dist/admin/apps/api-keys.d.ts +3 -3
  17. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  18. package/dist/admin/apps/api-keys.js +4 -1
  19. package/dist/admin/apps/api-keys.js.map +1 -1
  20. package/dist/admin/apps/scopes.d.ts +6 -34
  21. package/dist/admin/apps/scopes.d.ts.map +1 -1
  22. package/dist/admin/apps/scopes.js +2 -2
  23. package/dist/admin/apps/scopes.js.map +1 -1
  24. package/dist/admin/apps/verified-tokens.d.ts +2 -2
  25. package/dist/admin/ui.generated.d.ts +1 -1
  26. package/dist/admin/ui.generated.js +33 -33
  27. package/dist/admin/ui.generated.js.map +1 -1
  28. package/dist/apps/data/App.d.ts +1398 -1405
  29. package/dist/apps/data/App.d.ts.map +1 -1
  30. package/dist/apps/data/App.js +25 -38
  31. package/dist/apps/data/App.js.map +1 -1
  32. package/dist/apps/data/FundingQuoteProviders.d.ts +108 -3
  33. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  34. package/dist/apps/data/FundingQuoteProviders.js +409 -2
  35. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  36. package/dist/apps/data/routes/activities.d.ts +245 -245
  37. package/dist/apps/data/routes/blocks.d.ts +123 -123
  38. package/dist/apps/data/routes/funding.d.ts +236 -146
  39. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  40. package/dist/apps/data/routes/funding.js +23 -12
  41. package/dist/apps/data/routes/funding.js.map +1 -1
  42. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  43. package/dist/apps/data/routes/indexer.js +9 -2
  44. package/dist/apps/data/routes/indexer.js.map +1 -1
  45. package/dist/apps/data/routes/rpc.d.ts +31 -31
  46. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  47. package/dist/apps/data/routes/transactions.d.ts +281 -281
  48. package/dist/apps/data/routes/transfers.d.ts +66 -73
  49. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  50. package/dist/apps/data/routes/transfers.js +21 -27
  51. package/dist/apps/data/routes/transfers.js.map +1 -1
  52. package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
  53. package/dist/apps/data/routes/webhooks.d.ts +35 -35
  54. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  55. package/dist/apps/data/routes/webhooks.js +176 -42
  56. package/dist/apps/data/routes/webhooks.js.map +1 -1
  57. package/dist/apps/management/App.d.ts +520 -504
  58. package/dist/apps/management/App.d.ts.map +1 -1
  59. package/dist/apps/management/Billing.d.ts.map +1 -1
  60. package/dist/apps/management/Billing.js +5 -4
  61. package/dist/apps/management/Billing.js.map +1 -1
  62. package/dist/apps/management/routes/api-keys.d.ts +120 -120
  63. package/dist/apps/management/routes/billing.d.ts +211 -211
  64. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  65. package/dist/apps/management/routes/billing.js +1 -1
  66. package/dist/apps/management/routes/billing.js.map +1 -1
  67. package/dist/apps/management/routes/invitations.d.ts +72 -72
  68. package/dist/apps/management/routes/invite-links.d.ts +159 -159
  69. package/dist/apps/management/routes/me.d.ts +17 -17
  70. package/dist/apps/management/routes/members.d.ts +80 -80
  71. package/dist/apps/management/routes/orgs.d.ts +59 -43
  72. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  73. package/dist/apps/management/routes/orgs.js +18 -1
  74. package/dist/apps/management/routes/orgs.js.map +1 -1
  75. package/dist/apps/management/routes/projects.d.ts +109 -109
  76. package/dist/apps/management/routes/scopes.d.ts +11 -11
  77. package/dist/apps/management/routes/usage.d.ts +79 -79
  78. package/dist/apps/mcp.js +2 -2
  79. package/dist/apps/mcp.js.map +1 -1
  80. package/dist/apps/relay/App.d.ts +2 -2
  81. package/dist/apps/relay/App.js +2 -2
  82. package/dist/apps/relay/Sponsorships.d.ts +3 -0
  83. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  84. package/dist/apps/relay/Sponsorships.js +15 -1
  85. package/dist/apps/relay/Sponsorships.js.map +1 -1
  86. package/dist/db/Db.d.ts.map +1 -1
  87. package/dist/db/Db.js +4 -0
  88. package/dist/db/Db.js.map +1 -1
  89. package/dist/db/Schema.js +1 -1
  90. package/dist/db/Schema.js.map +1 -1
  91. package/dist/db/tables/organizations.d.ts +6 -0
  92. package/dist/db/tables/organizations.d.ts.map +1 -1
  93. package/dist/db/tables/organizations.js +26 -0
  94. package/dist/db/tables/organizations.js.map +1 -1
  95. package/dist/db/tables/requestUsage.d.ts +2 -11
  96. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  97. package/dist/db/tables/requestUsage.js +1 -20
  98. package/dist/db/tables/requestUsage.js.map +1 -1
  99. package/dist/db/tables/sponsoredTransactions.d.ts +2 -1
  100. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  101. package/dist/db/tables/sponsoredTransactions.js +52 -10
  102. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  103. package/dist/internal/Auth.d.ts +4 -0
  104. package/dist/internal/Auth.d.ts.map +1 -1
  105. package/dist/internal/Auth.js +15 -8
  106. package/dist/internal/Auth.js.map +1 -1
  107. package/dist/internal/Cache.d.ts.map +1 -1
  108. package/dist/internal/Cache.js +9 -1
  109. package/dist/internal/Cache.js.map +1 -1
  110. package/dist/internal/EdgeCache.d.ts.map +1 -1
  111. package/dist/internal/EdgeCache.js +12 -3
  112. package/dist/internal/EdgeCache.js.map +1 -1
  113. package/dist/internal/FundingQuotes.d.ts +176 -56
  114. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  115. package/dist/internal/FundingQuotes.js +100 -5
  116. package/dist/internal/FundingQuotes.js.map +1 -1
  117. package/dist/internal/Log.d.ts +18 -12
  118. package/dist/internal/Log.d.ts.map +1 -1
  119. package/dist/internal/Log.js +2 -0
  120. package/dist/internal/Log.js.map +1 -1
  121. package/dist/internal/RateLimit.d.ts +2 -0
  122. package/dist/internal/RateLimit.d.ts.map +1 -1
  123. package/dist/internal/RateLimit.js +4 -4
  124. package/dist/internal/RateLimit.js.map +1 -1
  125. package/dist/internal/Tidx.d.ts +9 -3
  126. package/dist/internal/Tidx.d.ts.map +1 -1
  127. package/dist/internal/Tidx.js +13 -7
  128. package/dist/internal/Tidx.js.map +1 -1
  129. package/dist/internal/Viem.d.ts +14 -1
  130. package/dist/internal/Viem.d.ts.map +1 -1
  131. package/dist/internal/Viem.js +19 -1
  132. package/dist/internal/Viem.js.map +1 -1
  133. package/package.json +7 -2
  134. package/src/ApiKeys.test.ts +2 -0
  135. package/src/App.test.ts +15 -4
  136. package/src/App.ts +31 -5
  137. package/src/Assets.ts +3 -3
  138. package/src/Client.test-d.ts +30 -3
  139. package/src/Scope.ts +4 -2
  140. package/src/Webhooks.ts +5 -10
  141. package/src/admin/apps/api-keys.test.ts +27 -0
  142. package/src/admin/apps/api-keys.ts +8 -1
  143. package/src/admin/apps/scopes.test.ts +7 -2
  144. package/src/admin/apps/scopes.ts +2 -2
  145. package/src/admin/ui/src/components/Dialog.tsx +8 -2
  146. package/src/admin/ui/src/lib/scopes.test.ts +19 -0
  147. package/src/admin/ui/src/lib/scopes.ts +12 -0
  148. package/src/admin/ui/src/routes/api-keys.tsx +128 -27
  149. package/src/admin/ui.generated.ts +33 -33
  150. package/src/apps/composition.test.ts +77 -0
  151. package/src/apps/data/App.ts +25 -45
  152. package/src/apps/data/FundingQuoteProviders.test.ts +615 -5
  153. package/src/apps/data/FundingQuoteProviders.ts +570 -4
  154. package/src/apps/data/routes/funding.test.ts +181 -7
  155. package/src/apps/data/routes/funding.ts +27 -12
  156. package/src/apps/data/routes/indexer.test.ts +82 -20
  157. package/src/apps/data/routes/indexer.ts +8 -2
  158. package/src/apps/data/routes/tokens.test.ts +54 -0
  159. package/src/apps/data/routes/transfers.test.ts +73 -1
  160. package/src/apps/data/routes/transfers.ts +24 -30
  161. package/src/apps/data/routes/webhooks.test.ts +203 -0
  162. package/src/apps/data/routes/webhooks.ts +191 -54
  163. package/src/apps/management/Billing.test.ts +52 -3
  164. package/src/apps/management/Billing.ts +5 -4
  165. package/src/apps/management/routes/billing.ts +6 -1
  166. package/src/apps/management/routes/orgs.ts +19 -1
  167. package/src/apps/mcp.test.ts +18 -0
  168. package/src/apps/mcp.ts +2 -2
  169. package/src/apps/relay/App.test.ts +6 -5
  170. package/src/apps/relay/App.ts +2 -2
  171. package/src/apps/relay/Sponsorships.test.ts +2 -2
  172. package/src/apps/relay/Sponsorships.ts +24 -1
  173. package/src/db/Db.test.ts +5 -5
  174. package/src/db/Db.ts +4 -0
  175. package/src/db/Schema.ts +1 -1
  176. package/src/db/tables/billingSettings.test.ts +2 -2
  177. package/src/db/tables/organizations.test.ts +32 -2
  178. package/src/db/tables/organizations.ts +28 -0
  179. package/src/db/tables/requestUsage.ts +3 -23
  180. package/src/db/tables/sponsoredTransactions.test.ts +60 -2
  181. package/src/db/tables/sponsoredTransactions.ts +67 -21
  182. package/src/db/tables/stripeCustomers.test.ts +2 -2
  183. package/src/internal/Auth.test.ts +63 -10
  184. package/src/internal/Auth.ts +21 -9
  185. package/src/internal/Cache.test.ts +19 -0
  186. package/src/internal/Cache.ts +13 -1
  187. package/src/internal/EdgeCache.test.ts +40 -0
  188. package/src/internal/EdgeCache.ts +11 -3
  189. package/src/internal/FundingQuotes.test.ts +130 -3
  190. package/src/internal/FundingQuotes.ts +113 -6
  191. package/src/internal/Log.ts +21 -14
  192. package/src/internal/RateLimit.test.ts +12 -0
  193. package/src/internal/RateLimit.ts +4 -4
  194. package/src/internal/Tidx.test.ts +48 -0
  195. package/src/internal/Tidx.ts +21 -7
  196. package/src/internal/Viem.test.ts +46 -0
  197. package/src/internal/Viem.ts +34 -1
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { parseAbiItem } from 'viem';
2
+ import { decodeEventLog, encodeEventTopics, parseAbiItem } from 'viem';
3
3
  import * as z from 'zod/mini';
4
4
  import * as Auth from '../../../internal/Auth.js';
5
5
  import * as Cache from '../../../internal/Cache.js';
@@ -23,6 +23,8 @@ const defaultMaxPerOwner = 100;
23
23
  * paid-once / deliver-forever mismatch. API-key-owned subscriptions have no TTL.
24
24
  */
25
25
  const mppTtlMs = 30 * 24 * 60 * 60 * 1_000;
26
+ const redactedBetterstackToken = '[redacted]';
27
+ const redactedSlackUrl = 'https://hooks.slack.com/…';
26
28
  /**
27
29
  * The subscribable webhook event types, in alphabetical order. The source of
28
30
  * truth for the event-type *set*: the {@link EventType} union and the route zod
@@ -69,15 +71,13 @@ export var schema;
69
71
  const Url = z
70
72
  .url()
71
73
  .check(z.describe('Your HTTPS endpoint where Tempo sends signed event POSTs.'), z.meta({ examples: ['https://example.com/webhooks'] }));
72
- const SlackUrl = z
73
- .url()
74
- .check(z.describe('Your Slack incoming-webhook URL (`https://hooks.slack.com/services/…`).'), z.meta({ examples: ['https://hooks.slack.com/services/T000/B000/XXXXXXXX'] }));
74
+ const SlackUrl = z.url().check(z.refine((url) => url !== redactedSlackUrl, { error: 'A Slack webhook URL is required.' }), z.describe('Your Slack incoming-webhook URL (`https://hooks.slack.com/services/…`).'), z.meta({ examples: ['https://hooks.slack.com/services/T000/B000/XXXXXXXX'] }));
75
75
  const BetterstackUrl = z
76
76
  .url()
77
77
  .check(z.describe('Your Better Stack source ingest host URL (`https://<host>.betterstackdata.com`).'), z.meta({ examples: ['https://s1234567.eu-nbg-2.betterstackdata.com'] }));
78
- const BetterstackToken = z
79
- .string()
80
- .check(z.minLength(1), z.describe('Your Better Stack source token (sent as `Authorization: Bearer …`).'), z.meta({ examples: ['FczKcxEhjEDE58dBX7XaeX1q'] }));
78
+ const BetterstackToken = z.string().check(z.minLength(1), z.refine((token) => token !== redactedBetterstackToken, {
79
+ error: 'A Better Stack source token is required.',
80
+ }), z.describe('Your Better Stack source token (sent as `Authorization: Bearer …`).'), z.meta({ examples: ['FczKcxEhjEDE58dBX7XaeX1q'] }));
81
81
  /**
82
82
  * Where matched events are delivered: a `url` destination (a signed event POST
83
83
  * to your HTTPS endpoint), a `slack` destination (a formatted message posted to
@@ -101,6 +101,25 @@ export var schema;
101
101
  }),
102
102
  ])
103
103
  .check(z.describe('Delivery destination: an HTTPS URL, a Slack incoming-webhook URL, or a Better Stack source.'));
104
+ const PublicDestination = z
105
+ .discriminatedUnion('type', [
106
+ z.object({
107
+ type: z.literal('url'),
108
+ url: Url,
109
+ }),
110
+ z.object({
111
+ type: z.literal('slack'),
112
+ url: z.literal(redactedSlackUrl).check(z.describe('Redacted Slack webhook URL.')),
113
+ }),
114
+ z.object({
115
+ token: z
116
+ .literal(redactedBetterstackToken)
117
+ .check(z.describe('Redacted Better Stack source token.')),
118
+ type: z.literal('betterstack'),
119
+ url: BetterstackUrl,
120
+ }),
121
+ ])
122
+ .check(z.describe('Delivery destination with bearer credentials redacted.'));
104
123
  /**
105
124
  * Equality / membership operator for a filterable field. A consumer can pass a
106
125
  * bare value (shorthand for exact equality, the common case) or an operator
@@ -238,9 +257,8 @@ export var schema;
238
257
  error: 'Pass `signature` or `topic0`, not both.',
239
258
  }), z.refine((f) => !f.args || f.signature, {
240
259
  error: '`args` requires `signature` to decode the event.',
241
- }), z.refine((f) => Boolean(f.address) ||
242
- (Boolean(f.signature || f.topic0) && Boolean(f.topic1 || f.topic2 || f.topic3 || f.args)), {
243
- error: 'Addressless log filters must include an event signature or topic0 plus at least one indexed topic or argument.',
260
+ }), z.refine((f) => hasLogPushdown(f), {
261
+ error: 'Log filters must include a pushdown-capable address or an event/topic0 anchor plus an indexed topic or argument.',
244
262
  }));
245
263
  /**
246
264
  * `block:created` subscription filters. All optional and AND-combined; unlike
@@ -289,7 +307,7 @@ export var schema;
289
307
  createdAt: z.iso
290
308
  .datetime()
291
309
  .check(z.describe('When the subscription was created, as an ISO 8601 timestamp.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })),
292
- destination: Destination,
310
+ destination: PublicDestination,
293
311
  eventType: schema.EventType.check(z.meta({ examples: ['token:transfer'] })),
294
312
  expiresAt: z
295
313
  .optional(z.iso.datetime())
@@ -596,7 +614,9 @@ export var schema;
596
614
  ])
597
615
  .check(z.describe('Details for creating a webhook subscription.'));
598
616
  createWebhook.Response = z
599
- .extend(schema.Subscription, {
617
+ .object({
618
+ ...schema.Subscription.shape,
619
+ destination: Destination,
600
620
  secret: z
601
621
  .string()
602
622
  .check(z.describe('HMAC signing secret (`whsec_…`) used to verify Tempo webhook signatures. It is shown once when you create the webhook, so store it now.'), z.meta({ examples: ['whsec_abc123'] })),
@@ -1586,6 +1606,8 @@ export function matchesOperator(value, op, kind = 'string') {
1586
1606
  return o['in'].some((v) => operandEquals(value, v, kind));
1587
1607
  if ('not' in o)
1588
1608
  return !operandEquals(value, o['not'], kind);
1609
+ if ('eq' in o && kind === 'string')
1610
+ return operandEquals(value, o['eq'], kind);
1589
1611
  // Comparison object: AND-combine whichever of eq/gt/gte/lt/lte are present.
1590
1612
  const n = toBig(value);
1591
1613
  let ok = true;
@@ -1739,33 +1761,124 @@ function toLogEvent(log, timestamp, signature, event) {
1739
1761
  event: { name: event.name, signature, topic0: topic0.data },
1740
1762
  };
1741
1763
  }
1764
+ /** Narrows an exact or membership operator into an RPC filter value. */
1765
+ function pushValue(op) {
1766
+ const value = (() => {
1767
+ if (typeof op !== 'object' || op === null)
1768
+ return op;
1769
+ const operator = op;
1770
+ if ('eq' in operator)
1771
+ return operator['eq'];
1772
+ if (Array.isArray(operator['in']) && operator['in'].length > 0)
1773
+ return operator['in'];
1774
+ return undefined;
1775
+ })();
1776
+ const scalar = (item) => ['bigint', 'boolean', 'number', 'string'].includes(typeof item);
1777
+ if (scalar(value))
1778
+ return value;
1779
+ if (Array.isArray(value) && value.length > 0 && value.every(scalar))
1780
+ return value;
1781
+ return undefined;
1782
+ }
1783
+ /** Narrows an `eq`/`in` operator into a hex RPC filter value. */
1784
+ function pushHex(op) {
1785
+ const value = pushValue(op);
1786
+ if (typeof value === 'string')
1787
+ return value;
1788
+ if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
1789
+ return value;
1790
+ return undefined;
1791
+ }
1742
1792
  /** Narrows an `eq`/`in` address operator into the `getLogs` address pushdown. */
1743
1793
  function pushAddress(op) {
1744
- if (op === undefined)
1794
+ return pushHex(op);
1795
+ }
1796
+ /** Parses a human-readable event signature without letting refinements throw. */
1797
+ function parseEvent(signature) {
1798
+ if (!signature)
1745
1799
  return undefined;
1746
- if (typeof op === 'string')
1747
- return op;
1748
- if (typeof op === 'object' && op !== null) {
1749
- const o = op;
1750
- if (typeof o['eq'] === 'string')
1751
- return o['eq'];
1752
- if (Array.isArray(o['in']))
1753
- return o['in'].filter((v) => typeof v === 'string');
1800
+ try {
1801
+ const item = parseAbiItem(signature);
1802
+ return item.type === 'event' ? item : undefined;
1803
+ }
1804
+ catch {
1805
+ return undefined;
1806
+ }
1807
+ }
1808
+ /** Returns exact indexed arguments that viem can push into `eth_getLogs`. */
1809
+ function pushEventArgs(args, event) {
1810
+ const pushed = {};
1811
+ for (const [name, op] of Object.entries(args ?? {})) {
1812
+ const input = event.inputs.find((item) => item.indexed && item.name === name);
1813
+ const value = pushValue(op);
1814
+ if (input && value !== undefined)
1815
+ pushed[name] = value;
1816
+ }
1817
+ return pushed;
1818
+ }
1819
+ /** Checks that the accepted filter has a selective `eth_getLogs` representation. */
1820
+ function hasLogPushdown(f) {
1821
+ if (pushAddress(f.address) !== undefined)
1822
+ return true;
1823
+ const secondaryTopic = [f.topic1, f.topic2, f.topic3].some((topic) => pushHex(topic) !== undefined);
1824
+ const event = parseEvent(f.signature);
1825
+ if (event && !event.anonymous)
1826
+ return secondaryTopic || Object.keys(pushEventArgs(f.args, event)).length > 0;
1827
+ return pushHex(f.topic0) !== undefined && secondaryTopic;
1828
+ }
1829
+ /** Builds raw topic filters, preserving null wildcards between constrained positions. */
1830
+ function pushTopics(f, event) {
1831
+ const topics = [
1832
+ event ? encodeEventTopics({ abi: [event] })[0] : pushHex(f.topic0),
1833
+ pushHex(f.topic1),
1834
+ pushHex(f.topic2),
1835
+ pushHex(f.topic3),
1836
+ ];
1837
+ while (topics.at(-1) === undefined)
1838
+ topics.pop();
1839
+ return topics.map((topic) => topic ?? null);
1840
+ }
1841
+ /** Converts one raw JSON-RPC log into the viem-shaped scanner input. */
1842
+ function fromRpcLog(log) {
1843
+ return {
1844
+ address: log.address,
1845
+ blockNumber: log.blockNumber === null ? null : BigInt(log.blockNumber),
1846
+ data: log.data,
1847
+ logIndex: Value.hexToNumber(log.logIndex) ?? null,
1848
+ topics: log.topics,
1849
+ transactionHash: log.transactionHash,
1850
+ transactionIndex: Value.hexToNumber(log.transactionIndex) ?? null,
1851
+ };
1852
+ }
1853
+ /** Best-effort decodes a raw log using the subscription event. */
1854
+ function decodeScanLog(log, event) {
1855
+ try {
1856
+ const decoded = decodeEventLog({
1857
+ abi: [event],
1858
+ data: log.data,
1859
+ strict: false,
1860
+ topics: log.topics,
1861
+ });
1862
+ return {
1863
+ ...log,
1864
+ args: decoded.args,
1865
+ eventName: decoded.eventName,
1866
+ };
1867
+ }
1868
+ catch {
1869
+ return log;
1754
1870
  }
1755
- // `not`/range forms can't be pushed; widen here, re-check in JS.
1756
- return undefined;
1757
1871
  }
1758
1872
  /**
1759
- * Scans contract event logs over the confirmed window via viem's `getLogs`,
1760
- * then re-checks **every** filter in JS (so operators the RPC can't express
1761
- * `not`, ranges, decoded `args` still apply). Two modes:
1873
+ * Scans contract event logs over the confirmed window via `eth_getLogs`,
1874
+ * then re-checks **every** filter in JS. Operators the RPC cannot express
1875
+ * (`not`, ranges, non-indexed `args`) still apply. Two modes:
1762
1876
  * - **raw** (no `signature`): `topic0..3` are matched directly.
1763
- * - **decoded** (`signature` present): `getLogs` is given the `event`, so it
1764
- * pushes the event selector down, filters to matching logs, and best-effort
1765
- * decodes `args` — which the JS pass then matches by name.
1877
+ * - **decoded** (`signature` present): the selector, raw topics, and supported
1878
+ * indexed `args` are pushed down before the JS pass matches decoded values.
1766
1879
  *
1767
- * `address` (`eq`/`in`) is pushed down to the node; all other operators are
1768
- * re-checked in JS over the narrow window.
1880
+ * Exact and membership address/topic operators are pushed down to the node;
1881
+ * all operators are re-checked in JS over the narrow window.
1769
1882
  */
1770
1883
  async function scanLogs(options) {
1771
1884
  const { cache, client, cursor, filters, maxBlocks, safeBlock } = options;
@@ -1773,26 +1886,35 @@ async function scanLogs(options) {
1773
1886
  const window = scanWindow(cursor, safeBlock, maxBlocks);
1774
1887
  if (!window)
1775
1888
  return { items: [], scannedThrough: undefined };
1776
- const event = f.signature ? parseAbiItem(f.signature) : undefined;
1777
- if (event && !/^[A-Za-z_]\w*$/.test(event.name))
1889
+ const event = parseEvent(f.signature);
1890
+ if (f.signature && (!event || !/^[A-Za-z_]\w*$/.test(event.name)))
1778
1891
  throw new Webhooks.InvalidFilterError('log:emitted', [
1779
1892
  { message: 'Unsupported event signature.', path: ['signature'] },
1780
1893
  ]);
1781
1894
  const address = pushAddress(f.address);
1782
1895
  const fromBlock = BigInt(window.from);
1783
1896
  const toBlock = BigInt(window.to);
1784
- const logs = (event
1785
- ? await client.getLogs({
1897
+ const topics = pushTopics(f, event);
1898
+ const hasRawTopics = topics.slice(1).some((topic) => topic !== null);
1899
+ const args = event ? pushEventArgs(f.args, event) : {};
1900
+ const logs = (!event || hasRawTopics
1901
+ ? (await rpc(client, 'eth_getLogs', [
1902
+ {
1903
+ ...(address ? { address } : {}),
1904
+ fromBlock: blockTag(window.from),
1905
+ ...(topics.length > 0 ? { topics } : {}),
1906
+ toBlock: blockTag(window.to),
1907
+ },
1908
+ ]))
1909
+ .map(fromRpcLog)
1910
+ .map((log) => (event ? decodeScanLog(log, event) : log))
1911
+ : await client.getLogs({
1786
1912
  ...(address ? { address } : {}),
1913
+ ...(Object.keys(args).length > 0 ? { args: args } : {}),
1787
1914
  event,
1788
1915
  fromBlock,
1789
1916
  strict: false,
1790
1917
  toBlock,
1791
- })
1792
- : await client.getLogs({
1793
- ...(address ? { address } : {}),
1794
- fromBlock,
1795
- toBlock,
1796
1918
  }));
1797
1919
  logs.sort((a, b) => {
1798
1920
  const ab = Number(a.blockNumber ?? 0n);
@@ -2077,7 +2199,7 @@ function toPublic(subscription) {
2077
2199
  chainId: subscription.chainId,
2078
2200
  ...(subscription.context === undefined ? {} : { context: subscription.context }),
2079
2201
  createdAt: subscription.createdAt,
2080
- destination: subscription.destination,
2202
+ destination: redactDestination(subscription.destination),
2081
2203
  eventType: subscription.eventType,
2082
2204
  ...(subscription.expiresAt === undefined ? {} : { expiresAt: subscription.expiresAt }),
2083
2205
  failureCount: subscription.failureCount,
@@ -2092,7 +2214,19 @@ function toPublic(subscription) {
2092
2214
  }
2093
2215
  /** Public read shape plus the one-time signing secret (creation only). */
2094
2216
  function toCreated(subscription) {
2095
- return { ...toPublic(subscription), secret: subscription.secret };
2217
+ return {
2218
+ ...toPublic(subscription),
2219
+ destination: subscription.destination,
2220
+ secret: subscription.secret,
2221
+ };
2222
+ }
2223
+ /** Redacts bearer credentials from the destination returned by read endpoints. */
2224
+ function redactDestination(destination) {
2225
+ if (destination.type === 'slack')
2226
+ return { ...destination, url: redactedSlackUrl };
2227
+ if (destination.type === 'betterstack')
2228
+ return { ...destination, token: redactedBetterstackToken };
2229
+ return destination;
2096
2230
  }
2097
2231
  function mutationError(c, cause) {
2098
2232
  if (cause instanceof Webhooks.InvalidUrlError)