@shroud-fi/scanning 0.1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/cjs/backend.d.ts +2 -0
  4. package/dist/cjs/backend.d.ts.map +1 -0
  5. package/dist/cjs/backend.js +6 -0
  6. package/dist/cjs/backend.js.map +1 -0
  7. package/dist/cjs/constants.d.ts +14 -0
  8. package/dist/cjs/constants.d.ts.map +1 -0
  9. package/dist/cjs/constants.js +17 -0
  10. package/dist/cjs/constants.js.map +1 -0
  11. package/dist/cjs/cursor.d.ts +8 -0
  12. package/dist/cjs/cursor.d.ts.map +1 -0
  13. package/dist/cjs/cursor.js +28 -0
  14. package/dist/cjs/cursor.js.map +1 -0
  15. package/dist/cjs/dedup.d.ts +10 -0
  16. package/dist/cjs/dedup.d.ts.map +1 -0
  17. package/dist/cjs/dedup.js +48 -0
  18. package/dist/cjs/dedup.js.map +1 -0
  19. package/dist/cjs/detector.d.ts +9 -0
  20. package/dist/cjs/detector.d.ts.map +1 -0
  21. package/dist/cjs/detector.js +425 -0
  22. package/dist/cjs/detector.js.map +1 -0
  23. package/dist/cjs/errors.d.ts +21 -0
  24. package/dist/cjs/errors.d.ts.map +1 -0
  25. package/dist/cjs/errors.js +49 -0
  26. package/dist/cjs/errors.js.map +1 -0
  27. package/dist/cjs/index.d.ts +9 -0
  28. package/dist/cjs/index.d.ts.map +1 -0
  29. package/dist/cjs/index.js +33 -0
  30. package/dist/cjs/index.js.map +1 -0
  31. package/dist/cjs/package.json +1 -0
  32. package/dist/cjs/types.d.ts +90 -0
  33. package/dist/cjs/types.d.ts.map +1 -0
  34. package/dist/cjs/types.js +17 -0
  35. package/dist/cjs/types.js.map +1 -0
  36. package/dist/cjs/viem-backend.d.ts +14 -0
  37. package/dist/cjs/viem-backend.d.ts.map +1 -0
  38. package/dist/cjs/viem-backend.js +166 -0
  39. package/dist/cjs/viem-backend.js.map +1 -0
  40. package/dist/esm/backend.d.ts +2 -0
  41. package/dist/esm/backend.d.ts.map +1 -0
  42. package/dist/esm/backend.js +5 -0
  43. package/dist/esm/backend.js.map +1 -0
  44. package/dist/esm/constants.d.ts +14 -0
  45. package/dist/esm/constants.d.ts.map +1 -0
  46. package/dist/esm/constants.js +14 -0
  47. package/dist/esm/constants.js.map +1 -0
  48. package/dist/esm/cursor.d.ts +8 -0
  49. package/dist/esm/cursor.d.ts.map +1 -0
  50. package/dist/esm/cursor.js +24 -0
  51. package/dist/esm/cursor.js.map +1 -0
  52. package/dist/esm/dedup.d.ts +10 -0
  53. package/dist/esm/dedup.d.ts.map +1 -0
  54. package/dist/esm/dedup.js +44 -0
  55. package/dist/esm/dedup.js.map +1 -0
  56. package/dist/esm/detector.d.ts +9 -0
  57. package/dist/esm/detector.d.ts.map +1 -0
  58. package/dist/esm/detector.js +422 -0
  59. package/dist/esm/detector.js.map +1 -0
  60. package/dist/esm/errors.d.ts +21 -0
  61. package/dist/esm/errors.d.ts.map +1 -0
  62. package/dist/esm/errors.js +41 -0
  63. package/dist/esm/errors.js.map +1 -0
  64. package/dist/esm/index.d.ts +9 -0
  65. package/dist/esm/index.d.ts.map +1 -0
  66. package/dist/esm/index.js +15 -0
  67. package/dist/esm/index.js.map +1 -0
  68. package/dist/esm/types.d.ts +90 -0
  69. package/dist/esm/types.d.ts.map +1 -0
  70. package/dist/esm/types.js +16 -0
  71. package/dist/esm/types.js.map +1 -0
  72. package/dist/esm/viem-backend.d.ts +14 -0
  73. package/dist/esm/viem-backend.d.ts.map +1 -0
  74. package/dist/esm/viem-backend.js +162 -0
  75. package/dist/esm/viem-backend.js.map +1 -0
  76. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  77. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  78. package/dist/tsconfig.tsbuildinfo +1 -0
  79. package/package.json +61 -0
@@ -0,0 +1,16 @@
1
+ // Public surface types for @shroud-fi/scanning (Phase 3 — detection only).
2
+ //
3
+ // Privacy contract:
4
+ // - ScannerConfig accepts branded ScanningKey + SpendingKey directly. These
5
+ // remain in-process; the scanner never logs them, serializes them, or
6
+ // surfaces them in errors. Spending key is required because ECDH derivation
7
+ // of the per-payment stealth private key needs it; see compute-key.ts.
8
+ // - DetectedPayment.stealthPrivateKey is the recovered private key for the
9
+ // one-time stealth address. It NEVER touches a relayer or logger. Caller
10
+ // drives the sweep (via @shroud-fi/payments sweepETH/sweepERC20) and is
11
+ // responsible for not leaking it.
12
+ // - Detection is idempotent. Cursor is in-memory only and resets to
13
+ // `startBlock` on restart. The dedup LRU prevents double-yield within a
14
+ // single Scanner lifetime.
15
+ export {};
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,oBAAoB;AACpB,8EAA8E;AAC9E,0EAA0E;AAC1E,gFAAgF;AAChF,2EAA2E;AAC3E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,sCAAsC;AACtC,sEAAsE;AACtE,4EAA4E;AAC5E,+BAA+B"}
@@ -0,0 +1,14 @@
1
+ import { type PublicClient } from 'viem';
2
+ import type { AnnouncementHandler, BackendWatchOptions, FinalityLevel, RawAnnouncement, ScannerBackend, UnsubscribeFn } from './types.js';
3
+ interface ViemBackendOptions {
4
+ readonly getLogsChunkSize?: bigint;
5
+ }
6
+ export declare class ViemScannerBackend implements ScannerBackend {
7
+ #private;
8
+ constructor(publicClient: PublicClient, options?: ViemBackendOptions);
9
+ watchAnnouncements(handler: AnnouncementHandler, options: BackendWatchOptions): UnsubscribeFn;
10
+ getAnnouncementsInRange(fromBlock: bigint, toBlock: bigint, options: BackendWatchOptions): Promise<readonly RawAnnouncement[]>;
11
+ getLatestBlock(level: FinalityLevel): Promise<bigint>;
12
+ }
13
+ export {};
14
+ //# sourceMappingURL=viem-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viem-backend.d.ts","sourceRoot":"","sources":["../../src/viem-backend.ts"],"names":[],"mappings":"AAYA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAO3E,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAOpB,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAkBD,qBAAa,kBAAmB,YAAW,cAAc;;gBAI3C,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAKpE,kBAAkB,CAChB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,mBAAmB,GAC3B,aAAa;IA+BV,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IA8ChC,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CA2B5D"}
@@ -0,0 +1,162 @@
1
+ // viem-backed ScannerBackend. The only backend that ships in Phase 3.
2
+ //
3
+ // Privacy contract (binding):
4
+ // - No console.* anywhere in this file.
5
+ // - Error messages NEVER embed hex strings, key material, amounts, or raw RPC
6
+ // payloads. Short reason tags only ("getLogs failed", "watch failed",
7
+ // "getBlockNumber failed").
8
+ // - No JSON.stringify of args or logs.
9
+ // - Pending / malformed logs (missing blockNumber, blockHash, transactionHash,
10
+ // or logIndex) are silently skipped — they aren't actionable for detection
11
+ // and we don't want to leak their existence via thrown errors.
12
+ import { getAbiItem, hexToBytes } from 'viem';
13
+ import { ERC5564AnnouncerAbi } from '@shroud-fi/transport';
14
+ import { DEFAULT_GETLOGS_CHUNK_SIZE } from './constants.js';
15
+ import { BackendUnreachableError, InvalidFinalityLevelError, } from './errors.js';
16
+ const ANNOUNCEMENT_EVENT = getAbiItem({
17
+ abi: ERC5564AnnouncerAbi,
18
+ name: 'Announcement',
19
+ });
20
+ export class ViemScannerBackend {
21
+ #publicClient;
22
+ #chunkSize;
23
+ constructor(publicClient, options) {
24
+ this.#publicClient = publicClient;
25
+ this.#chunkSize = options?.getLogsChunkSize ?? DEFAULT_GETLOGS_CHUNK_SIZE;
26
+ }
27
+ watchAnnouncements(handler, options) {
28
+ const watchArgs = {
29
+ address: options.contractAddress,
30
+ abi: ERC5564AnnouncerAbi,
31
+ eventName: 'Announcement',
32
+ onLogs: (logs) => {
33
+ for (const log of logs) {
34
+ const decoded = decodeAnnouncementLog(log);
35
+ if (decoded === null)
36
+ continue;
37
+ // Fire-and-forget. Handler may return a promise; we intentionally do
38
+ // not await — backend semantics are "deliver, don't gate".
39
+ void handler(decoded);
40
+ }
41
+ },
42
+ onError: (_err) => {
43
+ // Privacy: do not surface the underlying RPC error payload. Tag only.
44
+ throw new BackendUnreachableError('watch failed');
45
+ },
46
+ };
47
+ const unsubscribe = options.schemeId !== undefined
48
+ ? this.#publicClient.watchContractEvent({
49
+ ...watchArgs,
50
+ args: { schemeId: options.schemeId },
51
+ })
52
+ : this.#publicClient.watchContractEvent(watchArgs);
53
+ return unsubscribe;
54
+ }
55
+ async getAnnouncementsInRange(fromBlock, toBlock, options) {
56
+ if (toBlock < fromBlock)
57
+ return [];
58
+ const collected = [];
59
+ let cursor = fromBlock;
60
+ while (cursor <= toBlock) {
61
+ const chunkEnd = cursor + this.#chunkSize - 1n > toBlock
62
+ ? toBlock
63
+ : cursor + this.#chunkSize - 1n;
64
+ let logs;
65
+ try {
66
+ const getLogsArgs = options.schemeId !== undefined
67
+ ? {
68
+ address: options.contractAddress,
69
+ event: ANNOUNCEMENT_EVENT,
70
+ args: { schemeId: options.schemeId },
71
+ fromBlock: cursor,
72
+ toBlock: chunkEnd,
73
+ }
74
+ : {
75
+ address: options.contractAddress,
76
+ event: ANNOUNCEMENT_EVENT,
77
+ fromBlock: cursor,
78
+ toBlock: chunkEnd,
79
+ };
80
+ logs = await this.#publicClient.getLogs(getLogsArgs);
81
+ }
82
+ catch {
83
+ // Privacy: short tag only. No original error payload.
84
+ throw new BackendUnreachableError('getLogs failed');
85
+ }
86
+ for (const log of logs) {
87
+ const decoded = decodeAnnouncementLog(log);
88
+ if (decoded === null)
89
+ continue;
90
+ collected.push(decoded);
91
+ }
92
+ cursor = chunkEnd + 1n;
93
+ }
94
+ return collected;
95
+ }
96
+ async getLatestBlock(level) {
97
+ // viem.getBlockNumber() always returns the *latest* head; for 'safe' and
98
+ // 'finalized' we must go through getBlock({ blockTag }) and extract .number.
99
+ if (level === 'unsafe') {
100
+ try {
101
+ return await this.#publicClient.getBlockNumber();
102
+ }
103
+ catch {
104
+ throw new BackendUnreachableError('getBlockNumber failed');
105
+ }
106
+ }
107
+ if (level !== 'safe' && level !== 'finalized') {
108
+ throw new InvalidFinalityLevelError(String(level));
109
+ }
110
+ try {
111
+ const block = await this.#publicClient.getBlock({ blockTag: level });
112
+ const num = block.number;
113
+ if (num === null) {
114
+ throw new BackendUnreachableError('getBlockNumber failed');
115
+ }
116
+ return num;
117
+ }
118
+ catch (err) {
119
+ if (err instanceof BackendUnreachableError)
120
+ throw err;
121
+ throw new BackendUnreachableError('getBlockNumber failed');
122
+ }
123
+ }
124
+ }
125
+ /**
126
+ * Convert a viem decoded log into a RawAnnouncement. Returns null for any
127
+ * pending / malformed log (missing block fields, missing args). Never throws.
128
+ */
129
+ function decodeAnnouncementLog(log) {
130
+ const args = log.args;
131
+ if (args === undefined)
132
+ return null;
133
+ if (args.schemeId === undefined ||
134
+ args.stealthAddress === undefined ||
135
+ args.caller === undefined ||
136
+ args.ephemeralPubKey === undefined ||
137
+ args.metadata === undefined) {
138
+ return null;
139
+ }
140
+ if (log.blockNumber === undefined ||
141
+ log.blockNumber === null ||
142
+ log.blockHash === undefined ||
143
+ log.blockHash === null ||
144
+ log.transactionHash === undefined ||
145
+ log.transactionHash === null ||
146
+ log.logIndex === undefined ||
147
+ log.logIndex === null) {
148
+ return null;
149
+ }
150
+ return {
151
+ schemeId: args.schemeId,
152
+ stealthAddress: args.stealthAddress,
153
+ caller: args.caller,
154
+ ephemeralPubKey: hexToBytes(args.ephemeralPubKey),
155
+ metadata: hexToBytes(args.metadata),
156
+ blockNumber: log.blockNumber,
157
+ blockHash: log.blockHash,
158
+ txHash: log.transactionHash,
159
+ logIndex: log.logIndex,
160
+ };
161
+ }
162
+ //# sourceMappingURL=viem-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viem-backend.js","sourceRoot":"","sources":["../../src/viem-backend.ts"],"names":[],"mappings":"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"}