@waiaas/daemon 2.5.0-rc.1 → 2.6.0-rc
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/dist/api/middleware/error-handler.d.ts +1 -1
- package/dist/api/middleware/error-handler.js +2 -2
- package/dist/api/middleware/error-handler.js.map +1 -1
- package/dist/api/routes/admin.d.ts.map +1 -1
- package/dist/api/routes/admin.js +6 -30
- package/dist/api/routes/admin.js.map +1 -1
- package/dist/api/routes/incoming.d.ts +40 -0
- package/dist/api/routes/incoming.d.ts.map +1 -0
- package/dist/api/routes/incoming.js +281 -0
- package/dist/api/routes/incoming.js.map +1 -0
- package/dist/api/routes/openapi-schemas.d.ts +243 -2
- package/dist/api/routes/openapi-schemas.d.ts.map +1 -1
- package/dist/api/routes/openapi-schemas.js +77 -0
- package/dist/api/routes/openapi-schemas.js.map +1 -1
- package/dist/api/routes/wallets.d.ts +4 -0
- package/dist/api/routes/wallets.d.ts.map +1 -1
- package/dist/api/routes/wallets.js +173 -1
- package/dist/api/routes/wallets.js.map +1 -1
- package/dist/api/routes/x402.js +1 -1
- package/dist/api/routes/x402.js.map +1 -1
- package/dist/api/server.d.ts +4 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +12 -0
- package/dist/api/server.js.map +1 -1
- package/dist/infrastructure/config/loader.d.ts +43 -0
- package/dist/infrastructure/config/loader.d.ts.map +1 -1
- package/dist/infrastructure/config/loader.js +13 -1
- package/dist/infrastructure/config/loader.js.map +1 -1
- package/dist/infrastructure/database/index.d.ts +1 -1
- package/dist/infrastructure/database/index.d.ts.map +1 -1
- package/dist/infrastructure/database/index.js +1 -1
- package/dist/infrastructure/database/index.js.map +1 -1
- package/dist/infrastructure/database/migrate.d.ts +2 -2
- package/dist/infrastructure/database/migrate.d.ts.map +1 -1
- package/dist/infrastructure/database/migrate.js +83 -5
- package/dist/infrastructure/database/migrate.js.map +1 -1
- package/dist/infrastructure/database/schema.d.ts +381 -1
- package/dist/infrastructure/database/schema.d.ts.map +1 -1
- package/dist/infrastructure/database/schema.js +42 -2
- package/dist/infrastructure/database/schema.js.map +1 -1
- package/dist/infrastructure/settings/hot-reload.d.ts +9 -0
- package/dist/infrastructure/settings/hot-reload.d.ts.map +1 -1
- package/dist/infrastructure/settings/hot-reload.js +34 -5
- package/dist/infrastructure/settings/hot-reload.js.map +1 -1
- package/dist/infrastructure/settings/setting-keys.d.ts +2 -2
- package/dist/infrastructure/settings/setting-keys.d.ts.map +1 -1
- package/dist/infrastructure/settings/setting-keys.js +12 -3
- package/dist/infrastructure/settings/setting-keys.js.map +1 -1
- package/dist/lifecycle/daemon.d.ts +3 -1
- package/dist/lifecycle/daemon.d.ts.map +1 -1
- package/dist/lifecycle/daemon.js +84 -4
- package/dist/lifecycle/daemon.js.map +1 -1
- package/dist/notifications/channels/discord.d.ts.map +1 -1
- package/dist/notifications/channels/discord.js +17 -8
- package/dist/notifications/channels/discord.js.map +1 -1
- package/dist/notifications/channels/format-utils.d.ts +11 -0
- package/dist/notifications/channels/format-utils.d.ts.map +1 -0
- package/dist/notifications/channels/format-utils.js +19 -0
- package/dist/notifications/channels/format-utils.js.map +1 -0
- package/dist/notifications/channels/ntfy.d.ts.map +1 -1
- package/dist/notifications/channels/ntfy.js +15 -2
- package/dist/notifications/channels/ntfy.js.map +1 -1
- package/dist/notifications/channels/slack.d.ts.map +1 -1
- package/dist/notifications/channels/slack.js +16 -7
- package/dist/notifications/channels/slack.js.map +1 -1
- package/dist/notifications/channels/telegram.d.ts.map +1 -1
- package/dist/notifications/channels/telegram.js +17 -5
- package/dist/notifications/channels/telegram.js.map +1 -1
- package/dist/notifications/notification-service.d.ts +14 -0
- package/dist/notifications/notification-service.d.ts.map +1 -1
- package/dist/notifications/notification-service.js +83 -2
- package/dist/notifications/notification-service.js.map +1 -1
- package/dist/services/incoming/__tests__/incoming-tx-monitor-service.test.d.ts +11 -0
- package/dist/services/incoming/__tests__/incoming-tx-monitor-service.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/incoming-tx-monitor-service.test.js +432 -0
- package/dist/services/incoming/__tests__/incoming-tx-monitor-service.test.js.map +1 -0
- package/dist/services/incoming/__tests__/incoming-tx-queue.test.d.ts +12 -0
- package/dist/services/incoming/__tests__/incoming-tx-queue.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/incoming-tx-queue.test.js +419 -0
- package/dist/services/incoming/__tests__/incoming-tx-queue.test.js.map +1 -0
- package/dist/services/incoming/__tests__/incoming-tx-workers.test.d.ts +14 -0
- package/dist/services/incoming/__tests__/incoming-tx-workers.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/incoming-tx-workers.test.js +452 -0
- package/dist/services/incoming/__tests__/incoming-tx-workers.test.js.map +1 -0
- package/dist/services/incoming/__tests__/integration-pitfall.test.d.ts +17 -0
- package/dist/services/incoming/__tests__/integration-pitfall.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/integration-pitfall.test.js +653 -0
- package/dist/services/incoming/__tests__/integration-pitfall.test.js.map +1 -0
- package/dist/services/incoming/__tests__/integration-resilience.test.d.ts +14 -0
- package/dist/services/incoming/__tests__/integration-resilience.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/integration-resilience.test.js +501 -0
- package/dist/services/incoming/__tests__/integration-resilience.test.js.map +1 -0
- package/dist/services/incoming/__tests__/integration-wiring.test.d.ts +15 -0
- package/dist/services/incoming/__tests__/integration-wiring.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/integration-wiring.test.js +355 -0
- package/dist/services/incoming/__tests__/integration-wiring.test.js.map +1 -0
- package/dist/services/incoming/__tests__/safety-rules.test.d.ts +10 -0
- package/dist/services/incoming/__tests__/safety-rules.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/safety-rules.test.js +165 -0
- package/dist/services/incoming/__tests__/safety-rules.test.js.map +1 -0
- package/dist/services/incoming/__tests__/subscription-multiplexer.test.d.ts +2 -0
- package/dist/services/incoming/__tests__/subscription-multiplexer.test.d.ts.map +1 -0
- package/dist/services/incoming/__tests__/subscription-multiplexer.test.js +267 -0
- package/dist/services/incoming/__tests__/subscription-multiplexer.test.js.map +1 -0
- package/dist/services/incoming/incoming-tx-monitor-service.d.ts +98 -0
- package/dist/services/incoming/incoming-tx-monitor-service.d.ts.map +1 -0
- package/dist/services/incoming/incoming-tx-monitor-service.js +336 -0
- package/dist/services/incoming/incoming-tx-monitor-service.js.map +1 -0
- package/dist/services/incoming/incoming-tx-queue.d.ts +52 -0
- package/dist/services/incoming/incoming-tx-queue.d.ts.map +1 -0
- package/dist/services/incoming/incoming-tx-queue.js +109 -0
- package/dist/services/incoming/incoming-tx-queue.js.map +1 -0
- package/dist/services/incoming/incoming-tx-workers.d.ts +89 -0
- package/dist/services/incoming/incoming-tx-workers.d.ts.map +1 -0
- package/dist/services/incoming/incoming-tx-workers.js +176 -0
- package/dist/services/incoming/incoming-tx-workers.js.map +1 -0
- package/dist/services/incoming/index.d.ts +14 -0
- package/dist/services/incoming/index.d.ts.map +1 -0
- package/dist/services/incoming/index.js +11 -0
- package/dist/services/incoming/index.js.map +1 -0
- package/dist/services/incoming/safety-rules.d.ts +70 -0
- package/dist/services/incoming/safety-rules.d.ts.map +1 -0
- package/dist/services/incoming/safety-rules.js +68 -0
- package/dist/services/incoming/safety-rules.js.map +1 -0
- package/dist/services/incoming/subscription-multiplexer.d.ts +87 -0
- package/dist/services/incoming/subscription-multiplexer.d.ts.map +1 -0
- package/dist/services/incoming/subscription-multiplexer.js +169 -0
- package/dist/services/incoming/subscription-multiplexer.js.map +1 -0
- package/dist/services/signing-sdk/approval-channel-router.d.ts +1 -1
- package/dist/services/signing-sdk/approval-channel-router.d.ts.map +1 -1
- package/dist/services/signing-sdk/approval-channel-router.js +2 -3
- package/dist/services/signing-sdk/approval-channel-router.js.map +1 -1
- package/dist/services/signing-sdk/channels/wallet-notification-channel.js +1 -1
- package/dist/services/signing-sdk/channels/wallet-notification-channel.js.map +1 -1
- package/dist/services/x402/x402-domain-policy.d.ts +6 -1
- package/dist/services/x402/x402-domain-policy.d.ts.map +1 -1
- package/dist/services/x402/x402-domain-policy.js +6 -2
- package/dist/services/x402/x402-domain-policy.js.map +1 -1
- package/package.json +4 -4
- package/public/admin/assets/index-D06O_cSo.js +1 -0
- package/public/admin/index.html +1 -1
- package/public/admin/assets/index-BLLOYSZp.js +0 -1
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @see docs/35-notification-architecture.md
|
|
7
7
|
*/
|
|
8
|
+
import { EVENT_CATEGORY_MAP } from '@waiaas/core';
|
|
9
|
+
import { eq } from 'drizzle-orm';
|
|
8
10
|
import { getNotificationMessage } from './templates/message-templates.js';
|
|
9
11
|
import * as schema from '../infrastructure/database/schema.js';
|
|
10
12
|
import { generateId } from '../infrastructure/database/id.js';
|
|
@@ -13,6 +15,7 @@ const BROADCAST_EVENTS = new Set([
|
|
|
13
15
|
'KILL_SWITCH_ACTIVATED',
|
|
14
16
|
'KILL_SWITCH_RECOVERED',
|
|
15
17
|
'AUTO_STOP_TRIGGERED',
|
|
18
|
+
'TX_INCOMING_SUSPICIOUS',
|
|
16
19
|
]);
|
|
17
20
|
export class NotificationService {
|
|
18
21
|
channels = [];
|
|
@@ -20,6 +23,8 @@ export class NotificationService {
|
|
|
20
23
|
config = { locale: 'en', rateLimitRpm: 20 };
|
|
21
24
|
// Wallet notification side channel (v2.7 -- independent of traditional channels)
|
|
22
25
|
walletNotificationChannel = null;
|
|
26
|
+
// SettingsService for category filter (injected by daemon lifecycle)
|
|
27
|
+
settingsService = null;
|
|
23
28
|
// Rate limiter: Map<channelName, timestamps[]>
|
|
24
29
|
rateLimitMap = new Map();
|
|
25
30
|
constructor(opts) {
|
|
@@ -60,25 +65,47 @@ export class NotificationService {
|
|
|
60
65
|
setWalletNotificationChannel(channel) {
|
|
61
66
|
this.walletNotificationChannel = channel;
|
|
62
67
|
}
|
|
68
|
+
/** Set the SettingsService for category filtering (injected by daemon lifecycle). */
|
|
69
|
+
setSettingsService(service) {
|
|
70
|
+
this.settingsService = service;
|
|
71
|
+
}
|
|
63
72
|
/**
|
|
64
73
|
* Send notification via priority-based delivery with fallback.
|
|
65
74
|
* Tries channels in order; on failure, falls back to next channel.
|
|
66
75
|
* For broadcast events, sends to ALL channels.
|
|
67
76
|
*/
|
|
68
77
|
async notify(eventType, walletId, vars, details) {
|
|
78
|
+
// Look up wallet info for display (walletName, address, network)
|
|
79
|
+
const walletInfo = this.lookupWallet(walletId);
|
|
80
|
+
const mergedVars = walletInfo.walletName
|
|
81
|
+
? { walletName: walletInfo.walletName, ...vars }
|
|
82
|
+
: { walletName: walletId, ...vars };
|
|
83
|
+
// Category filter: check notifications.notify_categories (empty = allow all)
|
|
84
|
+
// BROADCAST_EVENTS always bypass the filter.
|
|
85
|
+
const isBroadcast = BROADCAST_EVENTS.has(eventType);
|
|
86
|
+
if (!isBroadcast && this.settingsService) {
|
|
87
|
+
const filtered = this.isCategoryFiltered(eventType);
|
|
88
|
+
if (filtered)
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
69
91
|
// Side channel: wallet app notification (independent of traditional channels, never blocks)
|
|
70
92
|
// Placed BEFORE the channels.length guard so it fires even with zero configured channels.
|
|
71
93
|
if (this.walletNotificationChannel) {
|
|
72
|
-
const { title: sideTitle, body: sideBody } = getNotificationMessage(eventType, this.config.locale,
|
|
94
|
+
const { title: sideTitle, body: sideBody } = getNotificationMessage(eventType, this.config.locale, mergedVars);
|
|
73
95
|
// Fire-and-forget with try/catch isolation (DAEMON-06)
|
|
74
96
|
this.walletNotificationChannel.notify(eventType, walletId, sideTitle, sideBody, details).catch(() => { });
|
|
75
97
|
}
|
|
76
98
|
if (this.channels.length === 0)
|
|
77
99
|
return; // No traditional channels configured
|
|
78
|
-
const { title, body } = getNotificationMessage(eventType, this.config.locale,
|
|
100
|
+
const { title, body } = getNotificationMessage(eventType, this.config.locale, mergedVars);
|
|
79
101
|
const payload = {
|
|
80
102
|
eventType,
|
|
81
103
|
walletId,
|
|
104
|
+
walletName: walletInfo.walletName,
|
|
105
|
+
walletAddress: walletInfo.walletAddress,
|
|
106
|
+
network: walletInfo.network,
|
|
107
|
+
title,
|
|
108
|
+
body,
|
|
82
109
|
message: `${title}\n${body}`,
|
|
83
110
|
details,
|
|
84
111
|
timestamp: Math.floor(Date.now() / 1000),
|
|
@@ -142,6 +169,29 @@ export class NotificationService {
|
|
|
142
169
|
// Log successful delivery
|
|
143
170
|
this.logDelivery(channel.name, payload, 'sent');
|
|
144
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if the event type is filtered out by notifications.notify_categories.
|
|
174
|
+
* Empty array = allow all. Returns true if the event should be suppressed.
|
|
175
|
+
*/
|
|
176
|
+
isCategoryFiltered(eventType) {
|
|
177
|
+
if (!this.settingsService)
|
|
178
|
+
return false;
|
|
179
|
+
try {
|
|
180
|
+
const filterJson = this.settingsService.get('notifications.notify_categories');
|
|
181
|
+
if (!filterJson || filterJson === '[]')
|
|
182
|
+
return false;
|
|
183
|
+
const allowed = JSON.parse(filterJson);
|
|
184
|
+
if (!Array.isArray(allowed) || allowed.length === 0)
|
|
185
|
+
return false;
|
|
186
|
+
const category = EVENT_CATEGORY_MAP[eventType];
|
|
187
|
+
if (!category)
|
|
188
|
+
return false;
|
|
189
|
+
return !allowed.includes(category);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
145
195
|
/** Check if channel is rate limited (sliding window). */
|
|
146
196
|
isRateLimited(channelName) {
|
|
147
197
|
const now = Date.now();
|
|
@@ -158,6 +208,37 @@ export class NotificationService {
|
|
|
158
208
|
timestamps.push(Date.now());
|
|
159
209
|
this.rateLimitMap.set(channelName, timestamps);
|
|
160
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* Look up wallet name, address, and network from DB.
|
|
213
|
+
* Returns empty strings when DB unavailable, walletId is empty/system, or wallet not found.
|
|
214
|
+
*/
|
|
215
|
+
lookupWallet(walletId) {
|
|
216
|
+
const empty = { walletName: '', walletAddress: '', network: '' };
|
|
217
|
+
if (!this.db || !walletId || walletId === 'system')
|
|
218
|
+
return empty;
|
|
219
|
+
try {
|
|
220
|
+
const row = this.db
|
|
221
|
+
.select({
|
|
222
|
+
name: schema.wallets.name,
|
|
223
|
+
publicKey: schema.wallets.publicKey,
|
|
224
|
+
defaultNetwork: schema.wallets.defaultNetwork,
|
|
225
|
+
chain: schema.wallets.chain,
|
|
226
|
+
})
|
|
227
|
+
.from(schema.wallets)
|
|
228
|
+
.where(eq(schema.wallets.id, walletId))
|
|
229
|
+
.get();
|
|
230
|
+
if (!row)
|
|
231
|
+
return empty;
|
|
232
|
+
return {
|
|
233
|
+
walletName: row.name,
|
|
234
|
+
walletAddress: row.publicKey,
|
|
235
|
+
network: row.defaultNetwork ?? row.chain,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return empty;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
161
242
|
/**
|
|
162
243
|
* Record notification delivery result to notification_logs table.
|
|
163
244
|
* Fire-and-forget: errors are swallowed to never block the pipeline.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-service.js","sourceRoot":"","sources":["../../src/notifications/notification-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"notification-service.js","sourceRoot":"","sources":["../../src/notifications/notification-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for IncomingTxMonitorService orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Tests lifecycle (start/stop), event emission, KillSwitch suppression,
|
|
5
|
+
* notification cooldown, and queue drain on stop.
|
|
6
|
+
*
|
|
7
|
+
* Uses mock dependencies (queue, multiplexer, workers, eventBus,
|
|
8
|
+
* killSwitch, notificationService).
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=incoming-tx-monitor-service.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incoming-tx-monitor-service.test.d.ts","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/incoming-tx-monitor-service.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for IncomingTxMonitorService orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Tests lifecycle (start/stop), event emission, KillSwitch suppression,
|
|
5
|
+
* notification cooldown, and queue drain on stop.
|
|
6
|
+
*
|
|
7
|
+
* Uses mock dependencies (queue, multiplexer, workers, eventBus,
|
|
8
|
+
* killSwitch, notificationService).
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
11
|
+
import { IncomingTxMonitorService } from '../incoming-tx-monitor-service.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Mock generateId for queue flush
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
let idCounter = 0;
|
|
16
|
+
vi.mock('../../../infrastructure/database/id.js', () => ({
|
|
17
|
+
generateId: () => `uuid-${++idCounter}`,
|
|
18
|
+
}));
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function makeTx(overrides = {}) {
|
|
23
|
+
return {
|
|
24
|
+
id: 'tx-001',
|
|
25
|
+
txHash: '0xabc',
|
|
26
|
+
walletId: 'wallet-001',
|
|
27
|
+
fromAddress: '0xsender',
|
|
28
|
+
amount: '1000000000',
|
|
29
|
+
tokenAddress: null,
|
|
30
|
+
chain: 'solana',
|
|
31
|
+
network: 'mainnet',
|
|
32
|
+
status: 'DETECTED',
|
|
33
|
+
blockNumber: 100,
|
|
34
|
+
detectedAt: 1700000000,
|
|
35
|
+
confirmedAt: null,
|
|
36
|
+
isSuspicious: false,
|
|
37
|
+
...overrides,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function makeConfig(overrides = {}) {
|
|
41
|
+
return {
|
|
42
|
+
enabled: true,
|
|
43
|
+
pollIntervalSec: 30,
|
|
44
|
+
retentionDays: 90,
|
|
45
|
+
dustThresholdUsd: 0.01,
|
|
46
|
+
amountMultiplier: 10,
|
|
47
|
+
cooldownMinutes: 5,
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a mock SQLite database that supports:
|
|
53
|
+
* - prepare().all() for wallet loading
|
|
54
|
+
* - prepare().run() for updates
|
|
55
|
+
* - prepare().get() for single-row queries
|
|
56
|
+
* - transaction() for batch operations
|
|
57
|
+
*/
|
|
58
|
+
function createMockSqlite() {
|
|
59
|
+
const runFn = vi.fn().mockReturnValue({ changes: 1 });
|
|
60
|
+
const getFn = vi.fn().mockReturnValue(undefined);
|
|
61
|
+
const allFn = vi.fn().mockReturnValue([]);
|
|
62
|
+
const prepareFn = vi.fn().mockReturnValue({
|
|
63
|
+
run: runFn,
|
|
64
|
+
get: getFn,
|
|
65
|
+
all: allFn,
|
|
66
|
+
});
|
|
67
|
+
const txFn = vi.fn((fn) => fn);
|
|
68
|
+
return {
|
|
69
|
+
prepare: prepareFn,
|
|
70
|
+
transaction: txFn,
|
|
71
|
+
exec: vi.fn(),
|
|
72
|
+
_runFn: runFn,
|
|
73
|
+
_getFn: getFn,
|
|
74
|
+
_allFn: allFn,
|
|
75
|
+
_prepareFn: prepareFn,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function createMockEventBus() {
|
|
79
|
+
return {
|
|
80
|
+
emit: vi.fn().mockReturnValue(true),
|
|
81
|
+
on: vi.fn(),
|
|
82
|
+
removeAllListeners: vi.fn(),
|
|
83
|
+
listenerCount: vi.fn().mockReturnValue(0),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createMockWorkers() {
|
|
87
|
+
return {
|
|
88
|
+
register: vi.fn(),
|
|
89
|
+
startAll: vi.fn(),
|
|
90
|
+
stopAll: vi.fn(),
|
|
91
|
+
size: 0,
|
|
92
|
+
isRunning: vi.fn().mockReturnValue(false),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function createMockKillSwitch(state = 'ACTIVE') {
|
|
96
|
+
return {
|
|
97
|
+
getState: vi.fn().mockReturnValue({
|
|
98
|
+
state,
|
|
99
|
+
activatedAt: null,
|
|
100
|
+
activatedBy: null,
|
|
101
|
+
}),
|
|
102
|
+
ensureInitialized: vi.fn(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createMockNotificationService() {
|
|
106
|
+
return {
|
|
107
|
+
notify: vi.fn(),
|
|
108
|
+
addChannel: vi.fn(),
|
|
109
|
+
replaceChannels: vi.fn(),
|
|
110
|
+
getChannelNames: vi.fn().mockReturnValue([]),
|
|
111
|
+
updateConfig: vi.fn(),
|
|
112
|
+
setWalletNotificationChannel: vi.fn(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function createMockSubscriberFactory() {
|
|
116
|
+
return vi.fn().mockReturnValue({
|
|
117
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
118
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
119
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
120
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
121
|
+
pollAll: vi.fn().mockResolvedValue(undefined),
|
|
122
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Tests
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
describe('IncomingTxMonitorService', () => {
|
|
129
|
+
let sqlite;
|
|
130
|
+
let eventBus;
|
|
131
|
+
let workers;
|
|
132
|
+
let killSwitch;
|
|
133
|
+
let notificationService;
|
|
134
|
+
let subscriberFactory;
|
|
135
|
+
beforeEach(() => {
|
|
136
|
+
vi.clearAllMocks();
|
|
137
|
+
idCounter = 0;
|
|
138
|
+
sqlite = createMockSqlite();
|
|
139
|
+
eventBus = createMockEventBus();
|
|
140
|
+
workers = createMockWorkers();
|
|
141
|
+
killSwitch = createMockKillSwitch('ACTIVE');
|
|
142
|
+
notificationService = createMockNotificationService();
|
|
143
|
+
subscriberFactory = createMockSubscriberFactory();
|
|
144
|
+
});
|
|
145
|
+
function createService(configOverrides = {}) {
|
|
146
|
+
return new IncomingTxMonitorService({
|
|
147
|
+
sqlite: sqlite,
|
|
148
|
+
db: {},
|
|
149
|
+
workers: workers,
|
|
150
|
+
eventBus: eventBus,
|
|
151
|
+
killSwitchService: killSwitch,
|
|
152
|
+
notificationService: notificationService,
|
|
153
|
+
subscriberFactory,
|
|
154
|
+
config: makeConfig(configOverrides),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// ── Lifecycle ──────────────────────────────────────────────────
|
|
158
|
+
describe('start()', () => {
|
|
159
|
+
it('loads wallets from DB and adds to multiplexer', async () => {
|
|
160
|
+
const walletRows = [
|
|
161
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pubkey1' },
|
|
162
|
+
{ id: 'w2', chain: 'ethereum', network: 'sepolia', public_key: 'pubkey2' },
|
|
163
|
+
];
|
|
164
|
+
// Make the wallet query return our test wallets
|
|
165
|
+
sqlite._allFn.mockReturnValueOnce(walletRows);
|
|
166
|
+
const service = createService();
|
|
167
|
+
await service.start();
|
|
168
|
+
// Verify wallet query was executed
|
|
169
|
+
expect(sqlite.prepare).toHaveBeenCalledWith(expect.stringContaining('monitor_incoming = 1'));
|
|
170
|
+
// Verify subscriber factory was called for both chain:network pairs
|
|
171
|
+
expect(subscriberFactory).toHaveBeenCalledTimes(2);
|
|
172
|
+
expect(subscriberFactory).toHaveBeenCalledWith('solana', 'mainnet');
|
|
173
|
+
expect(subscriberFactory).toHaveBeenCalledWith('ethereum', 'sepolia');
|
|
174
|
+
});
|
|
175
|
+
it('registers 6 background workers', async () => {
|
|
176
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
177
|
+
const service = createService();
|
|
178
|
+
await service.start();
|
|
179
|
+
expect(workers.register).toHaveBeenCalledTimes(6);
|
|
180
|
+
const registeredNames = workers.register.mock.calls.map((call) => call[0]);
|
|
181
|
+
expect(registeredNames).toContain('incoming-tx-flush');
|
|
182
|
+
expect(registeredNames).toContain('incoming-tx-retention');
|
|
183
|
+
expect(registeredNames).toContain('incoming-tx-confirm-solana');
|
|
184
|
+
expect(registeredNames).toContain('incoming-tx-confirm-evm');
|
|
185
|
+
expect(registeredNames).toContain('incoming-tx-poll-solana');
|
|
186
|
+
expect(registeredNames).toContain('incoming-tx-poll-evm');
|
|
187
|
+
});
|
|
188
|
+
it('handles wallet subscription failure gracefully (per-wallet isolation)', async () => {
|
|
189
|
+
const walletRows = [
|
|
190
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pubkey1' },
|
|
191
|
+
];
|
|
192
|
+
sqlite._allFn.mockReturnValueOnce(walletRows);
|
|
193
|
+
// Make subscriber factory throw for this wallet
|
|
194
|
+
subscriberFactory.mockRejectedValueOnce(new Error('RPC unavailable'));
|
|
195
|
+
const service = createService();
|
|
196
|
+
// Should not throw -- per-wallet error isolation
|
|
197
|
+
await expect(service.start()).resolves.toBeUndefined();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('stop()', () => {
|
|
201
|
+
it('drains queue before stopping multiplexer', async () => {
|
|
202
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
203
|
+
const service = createService();
|
|
204
|
+
await service.start();
|
|
205
|
+
// Push a transaction into the queue via the onTransaction callback
|
|
206
|
+
// We need to access the internal queue -- use the flush handler instead
|
|
207
|
+
// For this test, just verify stop() completes without error
|
|
208
|
+
await service.stop();
|
|
209
|
+
// The drain should have been called (queue is empty, so it's a no-op)
|
|
210
|
+
// The multiplexer stopAll should have been called
|
|
211
|
+
// We verify by checking no errors thrown
|
|
212
|
+
});
|
|
213
|
+
it('clears notification cooldown on stop', async () => {
|
|
214
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
215
|
+
const service = createService();
|
|
216
|
+
await service.start();
|
|
217
|
+
await service.stop();
|
|
218
|
+
// Internal state is cleared -- no way to directly test Map,
|
|
219
|
+
// but subsequent start should have fresh cooldowns
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
// ── Event emission ─────────────────────────────────────────────
|
|
223
|
+
describe('flush handler event emission', () => {
|
|
224
|
+
it('emits transaction:incoming for each inserted tx', async () => {
|
|
225
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
226
|
+
const service = createService();
|
|
227
|
+
await service.start();
|
|
228
|
+
// Get the flush handler from workers.register calls
|
|
229
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
230
|
+
expect(flushCall).toBeDefined();
|
|
231
|
+
const flushHandler = flushCall[1].handler;
|
|
232
|
+
// Mock queue.flush to return transactions
|
|
233
|
+
const tx = makeTx({ id: 'tx-001' });
|
|
234
|
+
// We need to push into the real queue and mock SQLite for flush
|
|
235
|
+
// Instead, let's mock the internal queue's flush method
|
|
236
|
+
const internalQueue = service.queue;
|
|
237
|
+
internalQueue.flush = vi.fn().mockReturnValue([tx]);
|
|
238
|
+
await flushHandler();
|
|
239
|
+
expect(eventBus.emit).toHaveBeenCalledWith('transaction:incoming', expect.objectContaining({
|
|
240
|
+
walletId: 'wallet-001',
|
|
241
|
+
txHash: '0xabc',
|
|
242
|
+
timestamp: 1700000000,
|
|
243
|
+
}));
|
|
244
|
+
});
|
|
245
|
+
it('emits transaction:incoming:suspicious for flagged transactions', async () => {
|
|
246
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
247
|
+
const service = createService();
|
|
248
|
+
await service.start();
|
|
249
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
250
|
+
const flushHandler = flushCall[1].handler;
|
|
251
|
+
// Create a tx with unknown token (will be flagged by UnknownTokenRule)
|
|
252
|
+
const tx = makeTx({ id: 'tx-002', tokenAddress: '0xunknown' });
|
|
253
|
+
// Mock the internal queue flush
|
|
254
|
+
const internalQueue = service.queue;
|
|
255
|
+
internalQueue.flush = vi.fn().mockReturnValue([tx]);
|
|
256
|
+
// Mock token_registry query to return no result (unregistered token)
|
|
257
|
+
sqlite._getFn.mockReturnValue(undefined);
|
|
258
|
+
await flushHandler();
|
|
259
|
+
// Should emit both events
|
|
260
|
+
expect(eventBus.emit).toHaveBeenCalledWith('transaction:incoming', expect.objectContaining({ txHash: '0xabc' }));
|
|
261
|
+
expect(eventBus.emit).toHaveBeenCalledWith('transaction:incoming:suspicious', expect.objectContaining({
|
|
262
|
+
txHash: '0xabc',
|
|
263
|
+
suspiciousReasons: expect.arrayContaining(['unknownToken']),
|
|
264
|
+
}));
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
// ── KillSwitch suppression ─────────────────────────────────────
|
|
268
|
+
describe('KillSwitch notification suppression', () => {
|
|
269
|
+
it('sends notifications when KillSwitch is ACTIVE', async () => {
|
|
270
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
271
|
+
killSwitch = createMockKillSwitch('ACTIVE');
|
|
272
|
+
const service = new IncomingTxMonitorService({
|
|
273
|
+
sqlite: sqlite,
|
|
274
|
+
db: {},
|
|
275
|
+
workers: workers,
|
|
276
|
+
eventBus: eventBus,
|
|
277
|
+
killSwitchService: killSwitch,
|
|
278
|
+
notificationService: notificationService,
|
|
279
|
+
subscriberFactory,
|
|
280
|
+
config: makeConfig(),
|
|
281
|
+
});
|
|
282
|
+
await service.start();
|
|
283
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
284
|
+
const flushHandler = flushCall[1].handler;
|
|
285
|
+
const tx = makeTx();
|
|
286
|
+
service.queue.flush = vi.fn().mockReturnValue([tx]);
|
|
287
|
+
await flushHandler();
|
|
288
|
+
expect(notificationService.notify).toHaveBeenCalled();
|
|
289
|
+
});
|
|
290
|
+
it('suppresses notifications when KillSwitch is SUSPENDED', async () => {
|
|
291
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
292
|
+
killSwitch = createMockKillSwitch('SUSPENDED');
|
|
293
|
+
const service = new IncomingTxMonitorService({
|
|
294
|
+
sqlite: sqlite,
|
|
295
|
+
db: {},
|
|
296
|
+
workers: workers,
|
|
297
|
+
eventBus: eventBus,
|
|
298
|
+
killSwitchService: killSwitch,
|
|
299
|
+
notificationService: notificationService,
|
|
300
|
+
subscriberFactory,
|
|
301
|
+
config: makeConfig(),
|
|
302
|
+
});
|
|
303
|
+
await service.start();
|
|
304
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
305
|
+
const flushHandler = flushCall[1].handler;
|
|
306
|
+
const tx = makeTx();
|
|
307
|
+
service.queue.flush = vi.fn().mockReturnValue([tx]);
|
|
308
|
+
await flushHandler();
|
|
309
|
+
// Events should still be emitted
|
|
310
|
+
expect(eventBus.emit).toHaveBeenCalledWith('transaction:incoming', expect.anything());
|
|
311
|
+
// But notifications should NOT be sent
|
|
312
|
+
expect(notificationService.notify).not.toHaveBeenCalled();
|
|
313
|
+
});
|
|
314
|
+
it('suppresses notifications when KillSwitch is LOCKED', async () => {
|
|
315
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
316
|
+
killSwitch = createMockKillSwitch('LOCKED');
|
|
317
|
+
const service = new IncomingTxMonitorService({
|
|
318
|
+
sqlite: sqlite,
|
|
319
|
+
db: {},
|
|
320
|
+
workers: workers,
|
|
321
|
+
eventBus: eventBus,
|
|
322
|
+
killSwitchService: killSwitch,
|
|
323
|
+
notificationService: notificationService,
|
|
324
|
+
subscriberFactory,
|
|
325
|
+
config: makeConfig(),
|
|
326
|
+
});
|
|
327
|
+
await service.start();
|
|
328
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
329
|
+
const flushHandler = flushCall[1].handler;
|
|
330
|
+
const tx = makeTx();
|
|
331
|
+
service.queue.flush = vi.fn().mockReturnValue([tx]);
|
|
332
|
+
await flushHandler();
|
|
333
|
+
// Events still emitted
|
|
334
|
+
expect(eventBus.emit).toHaveBeenCalled();
|
|
335
|
+
// Notifications NOT sent
|
|
336
|
+
expect(notificationService.notify).not.toHaveBeenCalled();
|
|
337
|
+
});
|
|
338
|
+
it('always writes DB records regardless of KillSwitch state', async () => {
|
|
339
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
340
|
+
killSwitch = createMockKillSwitch('LOCKED');
|
|
341
|
+
const service = new IncomingTxMonitorService({
|
|
342
|
+
sqlite: sqlite,
|
|
343
|
+
db: {},
|
|
344
|
+
workers: workers,
|
|
345
|
+
eventBus: eventBus,
|
|
346
|
+
killSwitchService: killSwitch,
|
|
347
|
+
notificationService: notificationService,
|
|
348
|
+
subscriberFactory,
|
|
349
|
+
config: makeConfig(),
|
|
350
|
+
});
|
|
351
|
+
await service.start();
|
|
352
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
353
|
+
const flushHandler = flushCall[1].handler;
|
|
354
|
+
// Suspicious tx (unknown token)
|
|
355
|
+
const tx = makeTx({ tokenAddress: '0xunknown' });
|
|
356
|
+
service.queue.flush = vi.fn().mockReturnValue([tx]);
|
|
357
|
+
sqlite._getFn.mockReturnValue(undefined);
|
|
358
|
+
await flushHandler();
|
|
359
|
+
// DB update for is_suspicious should still happen
|
|
360
|
+
expect(sqlite.prepare).toHaveBeenCalledWith(expect.stringContaining('UPDATE incoming_transactions SET is_suspicious = 1'));
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
// ── Notification cooldown ──────────────────────────────────────
|
|
364
|
+
describe('notification cooldown', () => {
|
|
365
|
+
it('suppresses second notification within cooldown window', async () => {
|
|
366
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
367
|
+
const service = createService({ cooldownMinutes: 5 });
|
|
368
|
+
await service.start();
|
|
369
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
370
|
+
const flushHandler = flushCall[1].handler;
|
|
371
|
+
const tx1 = makeTx({ id: 'tx-001', txHash: '0xabc1' });
|
|
372
|
+
const tx2 = makeTx({ id: 'tx-002', txHash: '0xabc2' });
|
|
373
|
+
// First flush: one tx
|
|
374
|
+
service.queue.flush = vi.fn().mockReturnValue([tx1]);
|
|
375
|
+
await flushHandler();
|
|
376
|
+
expect(notificationService.notify).toHaveBeenCalledTimes(1);
|
|
377
|
+
// Second flush: another tx for same wallet + event type within cooldown
|
|
378
|
+
service.queue.flush = vi.fn().mockReturnValue([tx2]);
|
|
379
|
+
await flushHandler();
|
|
380
|
+
// Still only 1 notification (second suppressed by cooldown)
|
|
381
|
+
expect(notificationService.notify).toHaveBeenCalledTimes(1);
|
|
382
|
+
});
|
|
383
|
+
it('allows notification after cooldown expires', async () => {
|
|
384
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
385
|
+
const service = createService({ cooldownMinutes: 5 });
|
|
386
|
+
await service.start();
|
|
387
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
388
|
+
const flushHandler = flushCall[1].handler;
|
|
389
|
+
const tx1 = makeTx({ id: 'tx-001', txHash: '0xabc1' });
|
|
390
|
+
service.queue.flush = vi.fn().mockReturnValue([tx1]);
|
|
391
|
+
await flushHandler();
|
|
392
|
+
expect(notificationService.notify).toHaveBeenCalledTimes(1);
|
|
393
|
+
// Simulate cooldown expiry by directly setting the cooldown map
|
|
394
|
+
const cooldownMap = service.notifyCooldown;
|
|
395
|
+
// Set last notified to 6 minutes ago (past the 5-minute cooldown)
|
|
396
|
+
cooldownMap.set('wallet-001:TX_INCOMING', Math.floor(Date.now() / 1000) - 360);
|
|
397
|
+
const tx2 = makeTx({ id: 'tx-002', txHash: '0xabc2' });
|
|
398
|
+
service.queue.flush = vi.fn().mockReturnValue([tx2]);
|
|
399
|
+
await flushHandler();
|
|
400
|
+
// Should now have 2 notifications
|
|
401
|
+
expect(notificationService.notify).toHaveBeenCalledTimes(2);
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
// ── updateConfig ───────────────────────────────────────────────
|
|
405
|
+
describe('updateConfig()', () => {
|
|
406
|
+
it('merges partial config', () => {
|
|
407
|
+
const service = createService({ pollIntervalSec: 30 });
|
|
408
|
+
service.updateConfig({ pollIntervalSec: 60, dustThresholdUsd: 0.05 });
|
|
409
|
+
const config = service.config;
|
|
410
|
+
expect(config.pollIntervalSec).toBe(60);
|
|
411
|
+
expect(config.dustThresholdUsd).toBe(0.05);
|
|
412
|
+
// Unchanged values preserved
|
|
413
|
+
expect(config.retentionDays).toBe(90);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
// ── Flush handler with cursor update ───────────────────────────
|
|
417
|
+
describe('flush handler cursor update', () => {
|
|
418
|
+
it('calls updateCursor after processing each tx', async () => {
|
|
419
|
+
sqlite._allFn.mockReturnValueOnce([]);
|
|
420
|
+
const service = createService();
|
|
421
|
+
await service.start();
|
|
422
|
+
const flushCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-flush');
|
|
423
|
+
const flushHandler = flushCall[1].handler;
|
|
424
|
+
const tx = makeTx({ blockNumber: 12345 });
|
|
425
|
+
service.queue.flush = vi.fn().mockReturnValue([tx]);
|
|
426
|
+
await flushHandler();
|
|
427
|
+
// Verify cursor update was called (INSERT OR REPLACE into incoming_tx_cursors)
|
|
428
|
+
expect(sqlite.prepare).toHaveBeenCalledWith(expect.stringContaining('incoming_tx_cursors'));
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
//# sourceMappingURL=incoming-tx-monitor-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"incoming-tx-monitor-service.test.js","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/incoming-tx-monitor-service.test.ts"],"names":[],"mappings":"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"}
|