@sip-protocol/sdk 0.7.2 → 0.7.4

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 (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +267 -0
  3. package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
  4. package/dist/browser.d.mts +10 -4
  5. package/dist/browser.d.ts +10 -4
  6. package/dist/browser.js +48874 -18336
  7. package/dist/browser.mjs +674 -48
  8. package/dist/chunk-4GRJ5MAW.mjs +152 -0
  9. package/dist/chunk-5D7A3L3W.mjs +717 -0
  10. package/dist/chunk-64AYA5F5.mjs +7834 -0
  11. package/dist/chunk-GMDGB22A.mjs +379 -0
  12. package/dist/chunk-I534WKN7.mjs +328 -0
  13. package/dist/chunk-IBZVA5Y7.mjs +1003 -0
  14. package/dist/chunk-PRRZAWJE.mjs +223 -0
  15. package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
  16. package/dist/chunk-YWGJ77A2.mjs +33806 -0
  17. package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
  18. package/dist/constants-LHAAUC2T.mjs +51 -0
  19. package/dist/dist-2OGQ7FED.mjs +3957 -0
  20. package/dist/dist-IFHPYLDX.mjs +254 -0
  21. package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
  22. package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
  23. package/dist/index-DXh2IGkz.d.ts +24681 -0
  24. package/dist/index-DeE1ZzA4.d.mts +24681 -0
  25. package/dist/index.d.mts +9 -3
  26. package/dist/index.d.ts +9 -3
  27. package/dist/index.js +48676 -17318
  28. package/dist/index.mjs +583 -19
  29. package/dist/interface-Bf7w1PLW.d.mts +679 -0
  30. package/dist/interface-Bf7w1PLW.d.ts +679 -0
  31. package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
  32. package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
  33. package/dist/proofs/halo2.d.mts +151 -0
  34. package/dist/proofs/halo2.d.ts +151 -0
  35. package/dist/proofs/halo2.js +350 -0
  36. package/dist/proofs/halo2.mjs +11 -0
  37. package/dist/proofs/kimchi.d.mts +160 -0
  38. package/dist/proofs/kimchi.d.ts +160 -0
  39. package/dist/proofs/kimchi.js +431 -0
  40. package/dist/proofs/kimchi.mjs +13 -0
  41. package/dist/proofs/noir.d.mts +1 -1
  42. package/dist/proofs/noir.d.ts +1 -1
  43. package/dist/proofs/noir.js +74 -18
  44. package/dist/proofs/noir.mjs +84 -24
  45. package/dist/solana-U3MEGU7W.mjs +280 -0
  46. package/dist/validity_proof-3POXLPNY.mjs +21 -0
  47. package/package.json +54 -21
  48. package/src/adapters/index.ts +41 -0
  49. package/src/adapters/jupiter.ts +571 -0
  50. package/src/adapters/near-intents.ts +135 -0
  51. package/src/advisor/advisor.ts +653 -0
  52. package/src/advisor/index.ts +54 -0
  53. package/src/advisor/tools.ts +303 -0
  54. package/src/advisor/types.ts +164 -0
  55. package/src/chains/ethereum/announcement.ts +536 -0
  56. package/src/chains/ethereum/bnb-optimizations.ts +474 -0
  57. package/src/chains/ethereum/commitment.ts +522 -0
  58. package/src/chains/ethereum/constants.ts +462 -0
  59. package/src/chains/ethereum/deployment.ts +596 -0
  60. package/src/chains/ethereum/gas-estimation.ts +538 -0
  61. package/src/chains/ethereum/index.ts +268 -0
  62. package/src/chains/ethereum/optimizations.ts +614 -0
  63. package/src/chains/ethereum/privacy-adapter.ts +855 -0
  64. package/src/chains/ethereum/registry.ts +584 -0
  65. package/src/chains/ethereum/rpc.ts +905 -0
  66. package/src/chains/ethereum/stealth.ts +491 -0
  67. package/src/chains/ethereum/token.ts +790 -0
  68. package/src/chains/ethereum/transfer.ts +637 -0
  69. package/src/chains/ethereum/types.ts +456 -0
  70. package/src/chains/ethereum/viewing-key.ts +455 -0
  71. package/src/chains/near/commitment.ts +608 -0
  72. package/src/chains/near/constants.ts +284 -0
  73. package/src/chains/near/function-call.ts +871 -0
  74. package/src/chains/near/history.ts +654 -0
  75. package/src/chains/near/implicit-account.ts +840 -0
  76. package/src/chains/near/index.ts +393 -0
  77. package/src/chains/near/native-transfer.ts +658 -0
  78. package/src/chains/near/nep141.ts +775 -0
  79. package/src/chains/near/privacy-adapter.ts +889 -0
  80. package/src/chains/near/resolver.ts +971 -0
  81. package/src/chains/near/rpc.ts +1016 -0
  82. package/src/chains/near/stealth.ts +419 -0
  83. package/src/chains/near/types.ts +317 -0
  84. package/src/chains/near/viewing-key.ts +876 -0
  85. package/src/chains/solana/anchor-transfer.ts +386 -0
  86. package/src/chains/solana/commitment.ts +577 -0
  87. package/src/chains/solana/constants.ts +126 -12
  88. package/src/chains/solana/ephemeral-keys.ts +543 -0
  89. package/src/chains/solana/index.ts +276 -1
  90. package/src/chains/solana/key-derivation.ts +418 -0
  91. package/src/chains/solana/kit-compat.ts +334 -0
  92. package/src/chains/solana/optimizations.ts +560 -0
  93. package/src/chains/solana/privacy-adapter.ts +605 -0
  94. package/src/chains/solana/providers/generic.ts +201 -0
  95. package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
  96. package/src/chains/solana/providers/helius-enhanced.ts +623 -0
  97. package/src/chains/solana/providers/helius.ts +402 -0
  98. package/src/chains/solana/providers/index.ts +85 -0
  99. package/src/chains/solana/providers/interface.ts +221 -0
  100. package/src/chains/solana/providers/quicknode.ts +409 -0
  101. package/src/chains/solana/providers/triton.ts +426 -0
  102. package/src/chains/solana/providers/webhook.ts +790 -0
  103. package/src/chains/solana/rpc-client.ts +1150 -0
  104. package/src/chains/solana/scan.ts +170 -73
  105. package/src/chains/solana/sol-transfer.ts +732 -0
  106. package/src/chains/solana/spl-transfer.ts +886 -0
  107. package/src/chains/solana/stealth-scanner.ts +703 -0
  108. package/src/chains/solana/sunspot-verifier.ts +453 -0
  109. package/src/chains/solana/transaction-builder.ts +755 -0
  110. package/src/chains/solana/transfer.ts +74 -5
  111. package/src/chains/solana/types.ts +77 -7
  112. package/src/chains/solana/utils.ts +110 -0
  113. package/src/chains/solana/viewing-key.ts +807 -0
  114. package/src/compliance/fireblocks.ts +921 -0
  115. package/src/compliance/index.ts +37 -0
  116. package/src/compliance/range-sas.ts +956 -0
  117. package/src/config/endpoints.ts +100 -0
  118. package/src/crypto.ts +11 -8
  119. package/src/errors.ts +82 -0
  120. package/src/evm/erc4337-relayer.ts +830 -0
  121. package/src/evm/index.ts +47 -0
  122. package/src/fees/calculator.ts +396 -0
  123. package/src/fees/index.ts +87 -0
  124. package/src/fees/near-contract.ts +429 -0
  125. package/src/fees/types.ts +268 -0
  126. package/src/index.ts +785 -1
  127. package/src/intent.ts +6 -3
  128. package/src/logger.ts +324 -0
  129. package/src/network/index.ts +80 -0
  130. package/src/network/proxy.ts +691 -0
  131. package/src/optimizations/index.ts +541 -0
  132. package/src/oracle/types.ts +1 -0
  133. package/src/privacy-backends/arcium-types.ts +727 -0
  134. package/src/privacy-backends/arcium.ts +719 -0
  135. package/src/privacy-backends/combined-privacy.ts +866 -0
  136. package/src/privacy-backends/cspl-token.ts +595 -0
  137. package/src/privacy-backends/cspl-types.ts +512 -0
  138. package/src/privacy-backends/cspl.ts +907 -0
  139. package/src/privacy-backends/health.ts +488 -0
  140. package/src/privacy-backends/inco-types.ts +323 -0
  141. package/src/privacy-backends/inco.ts +616 -0
  142. package/src/privacy-backends/index.ts +336 -0
  143. package/src/privacy-backends/interface.ts +906 -0
  144. package/src/privacy-backends/lru-cache.ts +343 -0
  145. package/src/privacy-backends/magicblock.ts +458 -0
  146. package/src/privacy-backends/mock.ts +258 -0
  147. package/src/privacy-backends/privacycash-types.ts +278 -0
  148. package/src/privacy-backends/privacycash.ts +456 -0
  149. package/src/privacy-backends/private-swap.ts +570 -0
  150. package/src/privacy-backends/rate-limiter.ts +683 -0
  151. package/src/privacy-backends/registry.ts +690 -0
  152. package/src/privacy-backends/router.ts +626 -0
  153. package/src/privacy-backends/shadowwire.ts +449 -0
  154. package/src/privacy-backends/sip-native.ts +256 -0
  155. package/src/privacy-logger.ts +191 -0
  156. package/src/production-safety.ts +373 -0
  157. package/src/proofs/aggregator.ts +1029 -0
  158. package/src/proofs/browser-composer.ts +1150 -0
  159. package/src/proofs/browser.ts +113 -25
  160. package/src/proofs/cache/index.ts +127 -0
  161. package/src/proofs/cache/interface.ts +545 -0
  162. package/src/proofs/cache/key-generator.ts +188 -0
  163. package/src/proofs/cache/lru-cache.ts +481 -0
  164. package/src/proofs/cache/multi-tier-cache.ts +575 -0
  165. package/src/proofs/cache/persistent-cache.ts +788 -0
  166. package/src/proofs/compliance-proof.ts +872 -0
  167. package/src/proofs/composer/base.ts +923 -0
  168. package/src/proofs/composer/index.ts +25 -0
  169. package/src/proofs/composer/interface.ts +518 -0
  170. package/src/proofs/composer/types.ts +383 -0
  171. package/src/proofs/converters/halo2.ts +452 -0
  172. package/src/proofs/converters/index.ts +208 -0
  173. package/src/proofs/converters/interface.ts +363 -0
  174. package/src/proofs/converters/kimchi.ts +462 -0
  175. package/src/proofs/converters/noir.ts +451 -0
  176. package/src/proofs/fallback.ts +888 -0
  177. package/src/proofs/halo2.ts +42 -0
  178. package/src/proofs/index.ts +471 -0
  179. package/src/proofs/interface.ts +13 -0
  180. package/src/proofs/kimchi.ts +42 -0
  181. package/src/proofs/lazy.ts +1004 -0
  182. package/src/proofs/mock.ts +25 -1
  183. package/src/proofs/noir.ts +111 -30
  184. package/src/proofs/orchestrator.ts +960 -0
  185. package/src/proofs/parallel/concurrency.ts +297 -0
  186. package/src/proofs/parallel/dependency-graph.ts +602 -0
  187. package/src/proofs/parallel/executor.ts +420 -0
  188. package/src/proofs/parallel/index.ts +131 -0
  189. package/src/proofs/parallel/interface.ts +685 -0
  190. package/src/proofs/parallel/worker-pool.ts +644 -0
  191. package/src/proofs/providers/halo2.ts +560 -0
  192. package/src/proofs/providers/index.ts +34 -0
  193. package/src/proofs/providers/kimchi.ts +641 -0
  194. package/src/proofs/validator.ts +881 -0
  195. package/src/proofs/verifier.ts +867 -0
  196. package/src/quantum/index.ts +112 -0
  197. package/src/quantum/winternitz-vault.ts +639 -0
  198. package/src/quantum/wots.ts +611 -0
  199. package/src/settlement/backends/direct-chain.ts +1 -0
  200. package/src/settlement/index.ts +9 -0
  201. package/src/settlement/router.ts +732 -46
  202. package/src/solana/index.ts +72 -0
  203. package/src/solana/jito-relayer.ts +687 -0
  204. package/src/solana/noir-verifier-types.ts +430 -0
  205. package/src/solana/noir-verifier.ts +816 -0
  206. package/src/stealth/address-derivation.ts +193 -0
  207. package/src/stealth/ed25519.ts +431 -0
  208. package/src/stealth/index.ts +233 -0
  209. package/src/stealth/meta-address.ts +221 -0
  210. package/src/stealth/secp256k1.ts +368 -0
  211. package/src/stealth/utils.ts +194 -0
  212. package/src/stealth.ts +50 -1504
  213. package/src/surveillance/algorithms/address-reuse.ts +143 -0
  214. package/src/surveillance/algorithms/cluster.ts +247 -0
  215. package/src/surveillance/algorithms/exchange.ts +295 -0
  216. package/src/surveillance/algorithms/temporal.ts +337 -0
  217. package/src/surveillance/analyzer.ts +442 -0
  218. package/src/surveillance/index.ts +64 -0
  219. package/src/surveillance/scoring.ts +372 -0
  220. package/src/surveillance/types.ts +264 -0
  221. package/src/sync/index.ts +106 -0
  222. package/src/sync/manager.ts +504 -0
  223. package/src/sync/mock-provider.ts +318 -0
  224. package/src/sync/oblivious.ts +625 -0
  225. package/src/tokens/index.ts +15 -0
  226. package/src/tokens/registry.ts +301 -0
  227. package/src/utils/deprecation.ts +94 -0
  228. package/src/utils/index.ts +9 -0
  229. package/src/wallet/ethereum/index.ts +68 -0
  230. package/src/wallet/ethereum/metamask-privacy.ts +420 -0
  231. package/src/wallet/ethereum/multi-wallet.ts +646 -0
  232. package/src/wallet/ethereum/privacy-adapter.ts +700 -0
  233. package/src/wallet/ethereum/types.ts +3 -1
  234. package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
  235. package/src/wallet/hardware/index.ts +10 -0
  236. package/src/wallet/hardware/ledger-privacy.ts +414 -0
  237. package/src/wallet/index.ts +71 -0
  238. package/src/wallet/near/adapter.ts +626 -0
  239. package/src/wallet/near/index.ts +86 -0
  240. package/src/wallet/near/meteor-wallet.ts +1153 -0
  241. package/src/wallet/near/my-near-wallet.ts +790 -0
  242. package/src/wallet/near/wallet-selector.ts +702 -0
  243. package/src/wallet/solana/adapter.ts +6 -4
  244. package/src/wallet/solana/index.ts +13 -0
  245. package/src/wallet/solana/privacy-adapter.ts +567 -0
  246. package/src/wallet/sui/types.ts +6 -4
  247. package/src/zcash/rpc-client.ts +13 -6
  248. package/dist/chunk-3INS3PR5.mjs +0 -884
  249. package/dist/chunk-3OVABDRH.mjs +0 -17096
  250. package/dist/chunk-DLDWZFYC.mjs +0 -1495
  251. package/dist/chunk-E6SZWREQ.mjs +0 -57
  252. package/dist/chunk-G33LB27A.mjs +0 -16166
  253. package/dist/chunk-HGU6HZRC.mjs +0 -231
  254. package/dist/chunk-L2K34JCU.mjs +0 -1496
  255. package/dist/chunk-SN4ZDTVW.mjs +0 -16166
  256. package/dist/constants-VOI7BSLK.mjs +0 -27
  257. package/dist/index-BYZbDjal.d.ts +0 -11390
  258. package/dist/index-CHB3KuOB.d.mts +0 -11859
  259. package/dist/index-CzWPI6Le.d.ts +0 -11859
  260. package/dist/index-xbWjohNq.d.mts +0 -11390
  261. package/dist/solana-5EMCTPTS.mjs +0 -46
  262. package/dist/solana-Q4NAVBTS.mjs +0 -46
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Temporal Pattern Detection Algorithm
3
+ *
4
+ * Analyzes transaction timing patterns to detect:
5
+ * - Regular schedules (e.g., weekly DCA, monthly payments)
6
+ * - Timezone inference from activity hours
7
+ * - Activity bursts that might indicate specific events
8
+ *
9
+ * These patterns can be used to de-anonymize users.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+
14
+ import type { AnalyzableTransaction, TemporalPatternResult } from '../types'
15
+
16
+ /**
17
+ * Maximum score deduction for temporal patterns (out of 15)
18
+ */
19
+ const MAX_DEDUCTION = 15
20
+
21
+ /**
22
+ * Deduction per detected pattern
23
+ */
24
+ const DEDUCTION_PER_PATTERN = 5
25
+
26
+ /**
27
+ * Minimum transactions to detect patterns
28
+ */
29
+ const MIN_TRANSACTIONS_FOR_PATTERN = 5
30
+
31
+ /**
32
+ * Threshold for considering a day-of-week as "regular"
33
+ * (percentage of transactions on that day)
34
+ */
35
+ const DAY_REGULARITY_THRESHOLD = 0.3
36
+
37
+ /**
38
+ * Threshold for considering an hour as "regular"
39
+ */
40
+ const HOUR_REGULARITY_THRESHOLD = 0.25
41
+
42
+ /**
43
+ * Timezone offset possibilities (UTC offsets)
44
+ */
45
+ const TIMEZONES: Record<string, number> = {
46
+ 'UTC-12': -12,
47
+ 'UTC-11': -11,
48
+ 'HST': -10,
49
+ 'AKST': -9,
50
+ 'PST': -8,
51
+ 'MST': -7,
52
+ 'CST': -6,
53
+ 'EST': -5,
54
+ 'AST': -4,
55
+ 'BRT': -3,
56
+ 'UTC-2': -2,
57
+ 'UTC-1': -1,
58
+ 'UTC': 0,
59
+ 'CET': 1,
60
+ 'EET': 2,
61
+ 'MSK': 3,
62
+ 'GST': 4,
63
+ 'PKT': 5,
64
+ 'BST': 6,
65
+ 'ICT': 7,
66
+ 'CST_ASIA': 8,
67
+ 'JST': 9,
68
+ 'AEST': 10,
69
+ 'AEDT': 11,
70
+ 'NZST': 12,
71
+ }
72
+
73
+ /**
74
+ * Analyze temporal patterns in transaction history
75
+ *
76
+ * @param transactions - Transaction history to analyze
77
+ * @returns Temporal pattern analysis result
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * const result = analyzeTemporalPatterns(transactions)
82
+ * console.log(result.patterns[0].type) // 'regular-schedule'
83
+ * console.log(result.inferredTimezone) // 'EST'
84
+ * ```
85
+ */
86
+ export function analyzeTemporalPatterns(
87
+ transactions: AnalyzableTransaction[]
88
+ ): TemporalPatternResult {
89
+ const patterns: TemporalPatternResult['patterns'] = []
90
+
91
+ if (transactions.length < MIN_TRANSACTIONS_FOR_PATTERN) {
92
+ return {
93
+ patterns: [],
94
+ scoreDeduction: 0,
95
+ }
96
+ }
97
+
98
+ // Extract timing data
99
+ const txTimes = transactions
100
+ .filter((tx) => tx.success && tx.timestamp > 0)
101
+ .map((tx) => new Date(tx.timestamp * 1000))
102
+
103
+ if (txTimes.length < MIN_TRANSACTIONS_FOR_PATTERN) {
104
+ return {
105
+ patterns: [],
106
+ scoreDeduction: 0,
107
+ }
108
+ }
109
+
110
+ // Analyze day-of-week distribution
111
+ const dayOfWeekPattern = analyzeDayOfWeekPattern(txTimes)
112
+ if (dayOfWeekPattern) {
113
+ patterns.push(dayOfWeekPattern)
114
+ }
115
+
116
+ // Analyze hour-of-day distribution
117
+ const hourPattern = analyzeHourPattern(txTimes)
118
+ if (hourPattern) {
119
+ patterns.push(hourPattern)
120
+ }
121
+
122
+ // Detect timezone from activity hours
123
+ const inferredTimezone = inferTimezone(txTimes)
124
+
125
+ // Detect activity bursts
126
+ const burstPattern = detectActivityBursts(transactions)
127
+ if (burstPattern) {
128
+ patterns.push(burstPattern)
129
+ }
130
+
131
+ // Calculate score deduction
132
+ const rawDeduction = patterns.length * DEDUCTION_PER_PATTERN
133
+ const scoreDeduction = Math.min(rawDeduction, MAX_DEDUCTION)
134
+
135
+ return {
136
+ patterns,
137
+ inferredTimezone,
138
+ scoreDeduction,
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Analyze day-of-week transaction patterns
144
+ */
145
+ function analyzeDayOfWeekPattern(
146
+ times: Date[]
147
+ ): TemporalPatternResult['patterns'][0] | null {
148
+ const dayCount = new Array(7).fill(0)
149
+
150
+ for (const time of times) {
151
+ dayCount[time.getUTCDay()]++
152
+ }
153
+
154
+ // Find dominant days
155
+ const total = times.length
156
+ const dominantDays: number[] = []
157
+
158
+ for (let day = 0; day < 7; day++) {
159
+ const percentage = dayCount[day] / total
160
+ if (percentage >= DAY_REGULARITY_THRESHOLD) {
161
+ dominantDays.push(day)
162
+ }
163
+ }
164
+
165
+ if (dominantDays.length === 0 || dominantDays.length > 3) {
166
+ return null // No clear pattern or too spread out
167
+ }
168
+
169
+ const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
170
+ const dominantDayNames = dominantDays.map((d) => dayNames[d]).join(', ')
171
+
172
+ return {
173
+ type: 'regular-schedule',
174
+ description: `Most transactions occur on ${dominantDayNames}`,
175
+ confidence: Math.max(...dominantDays.map((d) => dayCount[d] / total)),
176
+ evidence: {
177
+ dayOfWeek: dominantDays,
178
+ },
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Analyze hour-of-day transaction patterns
184
+ */
185
+ function analyzeHourPattern(
186
+ times: Date[]
187
+ ): TemporalPatternResult['patterns'][0] | null {
188
+ const hourCount = new Array(24).fill(0)
189
+
190
+ for (const time of times) {
191
+ hourCount[time.getUTCHours()]++
192
+ }
193
+
194
+ // Find active hours (group into 3-hour windows)
195
+ const total = times.length
196
+ const activeHours: number[] = []
197
+
198
+ for (let hour = 0; hour < 24; hour++) {
199
+ // Consider 3-hour windows
200
+ const windowCount =
201
+ hourCount[hour] +
202
+ hourCount[(hour + 1) % 24] +
203
+ hourCount[(hour + 2) % 24]
204
+ const windowPercentage = windowCount / total
205
+
206
+ if (windowPercentage >= HOUR_REGULARITY_THRESHOLD) {
207
+ if (!activeHours.includes(hour)) {
208
+ activeHours.push(hour)
209
+ }
210
+ }
211
+ }
212
+
213
+ if (activeHours.length === 0 || activeHours.length > 8) {
214
+ return null // No clear pattern
215
+ }
216
+
217
+ // Check if activity is concentrated (privacy risk)
218
+ const activeHourRange = Math.max(...activeHours) - Math.min(...activeHours)
219
+
220
+ if (activeHourRange <= 6) {
221
+ const startHour = Math.min(...activeHours)
222
+ const endHour = Math.max(...activeHours) + 2
223
+
224
+ return {
225
+ type: 'timezone-inference',
226
+ description: `Activity concentrated between ${startHour}:00-${endHour}:00 UTC`,
227
+ confidence: 0.7,
228
+ evidence: {
229
+ hourOfDay: activeHours,
230
+ },
231
+ }
232
+ }
233
+
234
+ return null
235
+ }
236
+
237
+ /**
238
+ * Infer timezone from activity patterns
239
+ * Assumes user is active during waking hours (8am-11pm local time)
240
+ */
241
+ function inferTimezone(times: Date[]): string | undefined {
242
+ const hourCount = new Array(24).fill(0)
243
+
244
+ for (const time of times) {
245
+ hourCount[time.getUTCHours()]++
246
+ }
247
+
248
+ // Find the 8-hour window with most activity
249
+ let maxActivity = 0
250
+ let bestStartHour = 0
251
+
252
+ for (let start = 0; start < 24; start++) {
253
+ let windowActivity = 0
254
+ for (let i = 0; i < 8; i++) {
255
+ windowActivity += hourCount[(start + i) % 24]
256
+ }
257
+
258
+ if (windowActivity > maxActivity) {
259
+ maxActivity = windowActivity
260
+ bestStartHour = start
261
+ }
262
+ }
263
+
264
+ // If activity is concentrated, infer timezone
265
+ const totalActivity = times.length
266
+ if (maxActivity / totalActivity < 0.6) {
267
+ return undefined // Activity too spread out
268
+ }
269
+
270
+ // Assume peak activity is around 10am-6pm local time
271
+ // So if peak starts at hour X UTC, user is probably at UTC+(14-X) or similar
272
+ const assumedLocalNoon = 12
273
+ const peakMidpoint = (bestStartHour + 4) % 24
274
+ const inferredOffset = (assumedLocalNoon - peakMidpoint + 24) % 24
275
+
276
+ // Find closest timezone
277
+ const normalizedOffset = inferredOffset > 12 ? inferredOffset - 24 : inferredOffset
278
+
279
+ for (const [name, offset] of Object.entries(TIMEZONES)) {
280
+ if (Math.abs(offset - normalizedOffset) <= 1) {
281
+ return name
282
+ }
283
+ }
284
+
285
+ return `UTC${normalizedOffset >= 0 ? '+' : ''}${normalizedOffset}`
286
+ }
287
+
288
+ /**
289
+ * Detect unusual activity bursts
290
+ */
291
+ function detectActivityBursts(
292
+ transactions: AnalyzableTransaction[]
293
+ ): TemporalPatternResult['patterns'][0] | null {
294
+ if (transactions.length < 10) {
295
+ return null
296
+ }
297
+
298
+ // Sort by timestamp
299
+ const sorted = [...transactions]
300
+ .filter((tx) => tx.timestamp > 0)
301
+ .sort((a, b) => a.timestamp - b.timestamp)
302
+
303
+ // Calculate gaps between transactions
304
+ const gaps: number[] = []
305
+ for (let i = 1; i < sorted.length; i++) {
306
+ gaps.push(sorted[i].timestamp - sorted[i - 1].timestamp)
307
+ }
308
+
309
+ // Calculate average gap
310
+ const avgGap = gaps.reduce((a, b) => a + b, 0) / gaps.length
311
+
312
+ // Find bursts (gaps < 10% of average)
313
+ const burstThreshold = avgGap * 0.1
314
+ let burstCount = 0
315
+
316
+ for (const gap of gaps) {
317
+ if (gap < burstThreshold && gap < 3600) {
318
+ // Less than 1 hour
319
+ burstCount++
320
+ }
321
+ }
322
+
323
+ const burstPercentage = burstCount / gaps.length
324
+
325
+ if (burstPercentage > 0.2) {
326
+ return {
327
+ type: 'activity-burst',
328
+ description: `${Math.round(burstPercentage * 100)}% of transactions occur in rapid succession`,
329
+ confidence: burstPercentage,
330
+ evidence: {
331
+ frequency: `${burstCount} bursts detected`,
332
+ },
333
+ }
334
+ }
335
+
336
+ return null
337
+ }