@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.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- 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
|
+
}
|