@waiaas/daemon 2.5.0 → 2.6.0-rc.1
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/connect-info.d.ts.map +1 -1
- package/dist/api/routes/connect-info.js +6 -0
- package/dist/api/routes/connect-info.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 +337 -24
- package/dist/api/routes/openapi-schemas.d.ts.map +1 -1
- package/dist/api/routes/openapi-schemas.js +78 -0
- package/dist/api/routes/openapi-schemas.js.map +1 -1
- package/dist/api/routes/tokens.d.ts.map +1 -1
- package/dist/api/routes/tokens.js +1 -0
- package/dist/api/routes/tokens.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 +123 -6
- package/dist/infrastructure/database/migrate.js.map +1 -1
- package/dist/infrastructure/database/schema.d.ts +400 -1
- package/dist/infrastructure/database/schema.d.ts.map +1 -1
- package/dist/infrastructure/database/schema.js +43 -2
- package/dist/infrastructure/database/schema.js.map +1 -1
- package/dist/infrastructure/oracle/coingecko-oracle.d.ts +9 -1
- package/dist/infrastructure/oracle/coingecko-oracle.d.ts.map +1 -1
- package/dist/infrastructure/oracle/coingecko-oracle.js +40 -23
- package/dist/infrastructure/oracle/coingecko-oracle.js.map +1 -1
- package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts +11 -10
- package/dist/infrastructure/oracle/coingecko-platform-ids.d.ts.map +1 -1
- package/dist/infrastructure/oracle/coingecko-platform-ids.js +20 -12
- package/dist/infrastructure/oracle/coingecko-platform-ids.js.map +1 -1
- package/dist/infrastructure/oracle/index.d.ts +1 -1
- package/dist/infrastructure/oracle/index.d.ts.map +1 -1
- package/dist/infrastructure/oracle/index.js +1 -1
- package/dist/infrastructure/oracle/index.js.map +1 -1
- package/dist/infrastructure/oracle/oracle-chain.d.ts.map +1 -1
- package/dist/infrastructure/oracle/oracle-chain.js +7 -4
- package/dist/infrastructure/oracle/oracle-chain.js.map +1 -1
- package/dist/infrastructure/oracle/price-cache.d.ts +24 -8
- package/dist/infrastructure/oracle/price-cache.d.ts.map +1 -1
- package/dist/infrastructure/oracle/price-cache.js +32 -11
- package/dist/infrastructure/oracle/price-cache.js.map +1 -1
- package/dist/infrastructure/oracle/pyth-feed-ids.d.ts +11 -8
- package/dist/infrastructure/oracle/pyth-feed-ids.d.ts.map +1 -1
- package/dist/infrastructure/oracle/pyth-feed-ids.js +16 -17
- package/dist/infrastructure/oracle/pyth-feed-ids.js.map +1 -1
- package/dist/infrastructure/oracle/pyth-oracle.d.ts.map +1 -1
- package/dist/infrastructure/oracle/pyth-oracle.js +7 -4
- package/dist/infrastructure/oracle/pyth-oracle.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/infrastructure/token-registry/token-registry-service.d.ts +1 -0
- package/dist/infrastructure/token-registry/token-registry-service.d.ts.map +1 -1
- package/dist/infrastructure/token-registry/token-registry-service.js +15 -1
- package/dist/infrastructure/token-registry/token-registry-service.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 +21 -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 +18 -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 +20 -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 +21 -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 +89 -2
- package/dist/notifications/notification-service.js.map +1 -1
- package/dist/pipeline/database-policy-engine.d.ts +9 -5
- package/dist/pipeline/database-policy-engine.d.ts.map +1 -1
- package/dist/pipeline/database-policy-engine.js +42 -10
- package/dist/pipeline/database-policy-engine.js.map +1 -1
- package/dist/pipeline/resolve-effective-amount-usd.d.ts +1 -1
- package/dist/pipeline/resolve-effective-amount-usd.d.ts.map +1 -1
- package/dist/pipeline/resolve-effective-amount-usd.js +5 -3
- package/dist/pipeline/resolve-effective-amount-usd.js.map +1 -1
- package/dist/pipeline/stages.d.ts.map +1 -1
- package/dist/pipeline/stages.js +7 -1
- package/dist/pipeline/stages.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 +357 -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/wc-session-service.d.ts +0 -2
- package/dist/services/wc-session-service.d.ts.map +1 -1
- package/dist/services/wc-session-service.js +2 -23
- package/dist/services/wc-session-service.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
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration wiring tests for incoming TX monitoring pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the 3 bug fixes from Plan 01 (Phase 230) work correctly
|
|
5
|
+
* when components are wired together:
|
|
6
|
+
*
|
|
7
|
+
* BUG-1: BackgroundWorkers instance sharing -- monitor registers 6 workers to the provided instance
|
|
8
|
+
* BUG-2: Polling worker handlers -- invoke subscriber.pollAll() for solana and ethereum chains
|
|
9
|
+
* BUG-3: Gap recovery wiring -- onGapRecovery invokes createGapRecoveryHandler -> subscriber.pollAll()
|
|
10
|
+
*
|
|
11
|
+
* These tests use real IncomingTxMonitorService with mock boundaries (DB, IChainSubscriber)
|
|
12
|
+
* to verify cross-component wiring at the integration level.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
15
|
+
import { IncomingTxMonitorService } from '../incoming-tx-monitor-service.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Mock generateId for queue flush
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
let idCounter = 0;
|
|
20
|
+
vi.mock('../../../infrastructure/database/id.js', () => ({
|
|
21
|
+
generateId: () => `uuid-${++idCounter}`,
|
|
22
|
+
}));
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
function makeConfig(overrides = {}) {
|
|
27
|
+
return {
|
|
28
|
+
enabled: true,
|
|
29
|
+
pollIntervalSec: 30,
|
|
30
|
+
retentionDays: 90,
|
|
31
|
+
dustThresholdUsd: 0.01,
|
|
32
|
+
amountMultiplier: 10,
|
|
33
|
+
cooldownMinutes: 5,
|
|
34
|
+
...overrides,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createMockSqlite() {
|
|
38
|
+
const runFn = vi.fn().mockReturnValue({ changes: 1 });
|
|
39
|
+
const getFn = vi.fn().mockReturnValue(undefined);
|
|
40
|
+
const allFn = vi.fn().mockReturnValue([]);
|
|
41
|
+
const prepareFn = vi.fn().mockReturnValue({
|
|
42
|
+
run: runFn,
|
|
43
|
+
get: getFn,
|
|
44
|
+
all: allFn,
|
|
45
|
+
});
|
|
46
|
+
const txFn = vi.fn((fn) => fn);
|
|
47
|
+
return {
|
|
48
|
+
prepare: prepareFn,
|
|
49
|
+
transaction: txFn,
|
|
50
|
+
exec: vi.fn(),
|
|
51
|
+
_runFn: runFn,
|
|
52
|
+
_getFn: getFn,
|
|
53
|
+
_allFn: allFn,
|
|
54
|
+
_prepareFn: prepareFn,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function createMockEventBus() {
|
|
58
|
+
return {
|
|
59
|
+
emit: vi.fn().mockReturnValue(true),
|
|
60
|
+
on: vi.fn(),
|
|
61
|
+
removeAllListeners: vi.fn(),
|
|
62
|
+
listenerCount: vi.fn().mockReturnValue(0),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createMockWorkers() {
|
|
66
|
+
return {
|
|
67
|
+
register: vi.fn(),
|
|
68
|
+
startAll: vi.fn(),
|
|
69
|
+
stopAll: vi.fn(),
|
|
70
|
+
size: 0,
|
|
71
|
+
isRunning: vi.fn().mockReturnValue(false),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function createMockSubscriberFactory() {
|
|
75
|
+
return vi.fn().mockReturnValue({
|
|
76
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
77
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
78
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
79
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
80
|
+
pollAll: vi.fn().mockResolvedValue(undefined),
|
|
81
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** Create a fresh IncomingTxMonitorService with isolated mocks. */
|
|
85
|
+
function createFreshService(opts = {}) {
|
|
86
|
+
const sqlite = createMockSqlite();
|
|
87
|
+
const eventBus = createMockEventBus();
|
|
88
|
+
const workers = createMockWorkers();
|
|
89
|
+
const subscriberFactory = createMockSubscriberFactory();
|
|
90
|
+
// Return empty wallets from DB query
|
|
91
|
+
sqlite._allFn.mockReturnValue([]);
|
|
92
|
+
const service = new IncomingTxMonitorService({
|
|
93
|
+
sqlite: sqlite,
|
|
94
|
+
db: {},
|
|
95
|
+
workers: workers,
|
|
96
|
+
eventBus: eventBus,
|
|
97
|
+
killSwitchService: null,
|
|
98
|
+
notificationService: null,
|
|
99
|
+
subscriberFactory,
|
|
100
|
+
config: makeConfig(opts.configOverrides),
|
|
101
|
+
});
|
|
102
|
+
return { service, sqlite, eventBus, workers, subscriberFactory };
|
|
103
|
+
}
|
|
104
|
+
// ===========================================================================
|
|
105
|
+
// Section 1: BUG-1 -- BackgroundWorkers Instance Sharing
|
|
106
|
+
// ===========================================================================
|
|
107
|
+
describe('BUG-1: BackgroundWorkers Instance Sharing', () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
vi.clearAllMocks();
|
|
110
|
+
idCounter = 0;
|
|
111
|
+
});
|
|
112
|
+
it('monitor service registers workers to the provided BackgroundWorkers instance', async () => {
|
|
113
|
+
const { service, workers } = createFreshService();
|
|
114
|
+
await service.start();
|
|
115
|
+
// Assert workers.register was called exactly 6 times
|
|
116
|
+
expect(workers.register).toHaveBeenCalledTimes(6);
|
|
117
|
+
// Assert the exact 6 worker names
|
|
118
|
+
const registeredNames = workers.register.mock.calls.map((call) => call[0]);
|
|
119
|
+
expect(registeredNames).toEqual(expect.arrayContaining([
|
|
120
|
+
'incoming-tx-flush',
|
|
121
|
+
'incoming-tx-retention',
|
|
122
|
+
'incoming-tx-confirm-solana',
|
|
123
|
+
'incoming-tx-confirm-evm',
|
|
124
|
+
'incoming-tx-poll-solana',
|
|
125
|
+
'incoming-tx-poll-evm',
|
|
126
|
+
]));
|
|
127
|
+
// Verify exactly these 6 (no more, no less)
|
|
128
|
+
expect(registeredNames).toHaveLength(6);
|
|
129
|
+
await service.stop();
|
|
130
|
+
});
|
|
131
|
+
it('all registered worker handlers are callable functions', async () => {
|
|
132
|
+
const { service, workers } = createFreshService();
|
|
133
|
+
await service.start();
|
|
134
|
+
// Extract handler functions from workers.register.mock.calls
|
|
135
|
+
for (const call of workers.register.mock.calls) {
|
|
136
|
+
const config = call[1];
|
|
137
|
+
// Each registered worker should have a handler that is a function
|
|
138
|
+
expect(typeof config.handler).toBe('function');
|
|
139
|
+
// Call each handler and assert it does not throw
|
|
140
|
+
const handler = config.handler;
|
|
141
|
+
await expect(handler()).resolves.not.toThrow();
|
|
142
|
+
}
|
|
143
|
+
await service.stop();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
// ===========================================================================
|
|
147
|
+
// Section 2: BUG-2 -- Polling Worker Handlers
|
|
148
|
+
// ===========================================================================
|
|
149
|
+
describe('BUG-2: Polling Worker Handlers', () => {
|
|
150
|
+
beforeEach(() => {
|
|
151
|
+
vi.clearAllMocks();
|
|
152
|
+
idCounter = 0;
|
|
153
|
+
});
|
|
154
|
+
it('Solana polling worker calls pollAll() on solana subscribers', async () => {
|
|
155
|
+
const sqlite = createMockSqlite();
|
|
156
|
+
const eventBus = createMockEventBus();
|
|
157
|
+
const workers = createMockWorkers();
|
|
158
|
+
const mockPollAll = vi.fn().mockResolvedValue(undefined);
|
|
159
|
+
const subscriberFactory = vi.fn().mockReturnValue({
|
|
160
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
161
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
162
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
163
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
164
|
+
pollAll: mockPollAll,
|
|
165
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
166
|
+
});
|
|
167
|
+
// Return one solana wallet from DB
|
|
168
|
+
sqlite._allFn.mockReturnValueOnce([
|
|
169
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pk1' },
|
|
170
|
+
]);
|
|
171
|
+
const service = new IncomingTxMonitorService({
|
|
172
|
+
sqlite: sqlite,
|
|
173
|
+
db: {},
|
|
174
|
+
workers: workers,
|
|
175
|
+
eventBus: eventBus,
|
|
176
|
+
killSwitchService: null,
|
|
177
|
+
notificationService: null,
|
|
178
|
+
subscriberFactory,
|
|
179
|
+
config: makeConfig(),
|
|
180
|
+
});
|
|
181
|
+
await service.start();
|
|
182
|
+
// Extract 'incoming-tx-poll-solana' handler
|
|
183
|
+
const pollSolanaCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-poll-solana');
|
|
184
|
+
expect(pollSolanaCall).toBeDefined();
|
|
185
|
+
const handler = pollSolanaCall[1].handler;
|
|
186
|
+
await handler();
|
|
187
|
+
// Assert subscriber.pollAll() was called
|
|
188
|
+
expect(mockPollAll).toHaveBeenCalledTimes(1);
|
|
189
|
+
await service.stop();
|
|
190
|
+
});
|
|
191
|
+
it('EVM polling worker calls pollAll() on ethereum subscribers', async () => {
|
|
192
|
+
const sqlite = createMockSqlite();
|
|
193
|
+
const eventBus = createMockEventBus();
|
|
194
|
+
const workers = createMockWorkers();
|
|
195
|
+
const mockPollAll = vi.fn().mockResolvedValue(undefined);
|
|
196
|
+
const subscriberFactory = vi.fn().mockReturnValue({
|
|
197
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
198
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
199
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
200
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
201
|
+
pollAll: mockPollAll,
|
|
202
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
203
|
+
});
|
|
204
|
+
// Return one ethereum wallet from DB
|
|
205
|
+
sqlite._allFn.mockReturnValueOnce([
|
|
206
|
+
{ id: 'w2', chain: 'ethereum', network: 'mainnet', public_key: 'pk2' },
|
|
207
|
+
]);
|
|
208
|
+
const service = new IncomingTxMonitorService({
|
|
209
|
+
sqlite: sqlite,
|
|
210
|
+
db: {},
|
|
211
|
+
workers: workers,
|
|
212
|
+
eventBus: eventBus,
|
|
213
|
+
killSwitchService: null,
|
|
214
|
+
notificationService: null,
|
|
215
|
+
subscriberFactory,
|
|
216
|
+
config: makeConfig(),
|
|
217
|
+
});
|
|
218
|
+
await service.start();
|
|
219
|
+
// Extract 'incoming-tx-poll-evm' handler
|
|
220
|
+
const pollEvmCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-poll-evm');
|
|
221
|
+
expect(pollEvmCall).toBeDefined();
|
|
222
|
+
const handler = pollEvmCall[1].handler;
|
|
223
|
+
await handler();
|
|
224
|
+
// Assert subscriber.pollAll() was called
|
|
225
|
+
expect(mockPollAll).toHaveBeenCalledTimes(1);
|
|
226
|
+
await service.stop();
|
|
227
|
+
});
|
|
228
|
+
it('polling worker handles subscriber.pollAll() errors gracefully', async () => {
|
|
229
|
+
const sqlite = createMockSqlite();
|
|
230
|
+
const eventBus = createMockEventBus();
|
|
231
|
+
const workers = createMockWorkers();
|
|
232
|
+
const mockPollAll = vi.fn().mockRejectedValue(new Error('RPC timeout'));
|
|
233
|
+
const subscriberFactory = vi.fn().mockReturnValue({
|
|
234
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
235
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
236
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
237
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
238
|
+
pollAll: mockPollAll,
|
|
239
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
240
|
+
});
|
|
241
|
+
// Return one solana wallet from DB
|
|
242
|
+
sqlite._allFn.mockReturnValueOnce([
|
|
243
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pk1' },
|
|
244
|
+
]);
|
|
245
|
+
const service = new IncomingTxMonitorService({
|
|
246
|
+
sqlite: sqlite,
|
|
247
|
+
db: {},
|
|
248
|
+
workers: workers,
|
|
249
|
+
eventBus: eventBus,
|
|
250
|
+
killSwitchService: null,
|
|
251
|
+
notificationService: null,
|
|
252
|
+
subscriberFactory,
|
|
253
|
+
config: makeConfig(),
|
|
254
|
+
});
|
|
255
|
+
await service.start();
|
|
256
|
+
// Extract 'incoming-tx-poll-solana' handler
|
|
257
|
+
const pollSolanaCall = workers.register.mock.calls.find((call) => call[0] === 'incoming-tx-poll-solana');
|
|
258
|
+
const handler = pollSolanaCall[1].handler;
|
|
259
|
+
// Spy on console.warn to verify error logging
|
|
260
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
261
|
+
// Should NOT throw -- error is caught and logged
|
|
262
|
+
await expect(handler()).resolves.not.toThrow();
|
|
263
|
+
// Verify warning was logged
|
|
264
|
+
expect(warnSpy).toHaveBeenCalledWith('Solana polling worker error:', expect.any(Error));
|
|
265
|
+
warnSpy.mockRestore();
|
|
266
|
+
await service.stop();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
// ===========================================================================
|
|
270
|
+
// Section 3: BUG-3 -- Gap Recovery Wiring
|
|
271
|
+
// ===========================================================================
|
|
272
|
+
describe('BUG-3: Gap Recovery Wiring', () => {
|
|
273
|
+
beforeEach(() => {
|
|
274
|
+
vi.clearAllMocks();
|
|
275
|
+
idCounter = 0;
|
|
276
|
+
});
|
|
277
|
+
it('onGapRecovery invokes createGapRecoveryHandler which calls subscriber.pollAll()', async () => {
|
|
278
|
+
const sqlite = createMockSqlite();
|
|
279
|
+
const eventBus = createMockEventBus();
|
|
280
|
+
const workers = createMockWorkers();
|
|
281
|
+
const mockPollAll = vi.fn().mockResolvedValue(undefined);
|
|
282
|
+
const subscriberFactory = vi.fn().mockReturnValue({
|
|
283
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
284
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
285
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
286
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
287
|
+
pollAll: mockPollAll,
|
|
288
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
289
|
+
});
|
|
290
|
+
// Return one solana wallet from DB
|
|
291
|
+
sqlite._allFn.mockReturnValueOnce([
|
|
292
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pk1' },
|
|
293
|
+
]);
|
|
294
|
+
const service = new IncomingTxMonitorService({
|
|
295
|
+
sqlite: sqlite,
|
|
296
|
+
db: {},
|
|
297
|
+
workers: workers,
|
|
298
|
+
eventBus: eventBus,
|
|
299
|
+
killSwitchService: null,
|
|
300
|
+
notificationService: null,
|
|
301
|
+
subscriberFactory,
|
|
302
|
+
config: makeConfig(),
|
|
303
|
+
});
|
|
304
|
+
await service.start();
|
|
305
|
+
// Access the multiplexer's onGapRecovery callback via internal deps
|
|
306
|
+
const multiplexer = service.multiplexer;
|
|
307
|
+
const onGapRecovery = multiplexer.deps.onGapRecovery;
|
|
308
|
+
expect(onGapRecovery).toBeDefined();
|
|
309
|
+
expect(typeof onGapRecovery).toBe('function');
|
|
310
|
+
// Call onGapRecovery for solana:mainnet
|
|
311
|
+
await onGapRecovery('solana', 'mainnet', ['w1']);
|
|
312
|
+
// Assert subscriber.pollAll() was called (via createGapRecoveryHandler -> entry.subscriber.pollAll())
|
|
313
|
+
expect(mockPollAll).toHaveBeenCalledTimes(1);
|
|
314
|
+
await service.stop();
|
|
315
|
+
});
|
|
316
|
+
it('onGapRecovery handles missing chain:network gracefully', async () => {
|
|
317
|
+
const sqlite = createMockSqlite();
|
|
318
|
+
const eventBus = createMockEventBus();
|
|
319
|
+
const workers = createMockWorkers();
|
|
320
|
+
const mockPollAll = vi.fn().mockResolvedValue(undefined);
|
|
321
|
+
const subscriberFactory = vi.fn().mockReturnValue({
|
|
322
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
323
|
+
waitForDisconnect: vi.fn().mockReturnValue(new Promise(() => { })),
|
|
324
|
+
subscribe: vi.fn().mockResolvedValue(undefined),
|
|
325
|
+
unsubscribe: vi.fn().mockResolvedValue(undefined),
|
|
326
|
+
pollAll: mockPollAll,
|
|
327
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
328
|
+
});
|
|
329
|
+
// Only return a solana wallet (no ethereum)
|
|
330
|
+
sqlite._allFn.mockReturnValueOnce([
|
|
331
|
+
{ id: 'w1', chain: 'solana', network: 'mainnet', public_key: 'pk1' },
|
|
332
|
+
]);
|
|
333
|
+
const service = new IncomingTxMonitorService({
|
|
334
|
+
sqlite: sqlite,
|
|
335
|
+
db: {},
|
|
336
|
+
workers: workers,
|
|
337
|
+
eventBus: eventBus,
|
|
338
|
+
killSwitchService: null,
|
|
339
|
+
notificationService: null,
|
|
340
|
+
subscriberFactory,
|
|
341
|
+
config: makeConfig(),
|
|
342
|
+
});
|
|
343
|
+
await service.start();
|
|
344
|
+
// Access onGapRecovery
|
|
345
|
+
const multiplexer = service.multiplexer;
|
|
346
|
+
const onGapRecovery = multiplexer.deps.onGapRecovery;
|
|
347
|
+
// Call onGapRecovery for 'ethereum:mainnet' which has no subscriber
|
|
348
|
+
// Should NOT throw -- createGapRecoveryHandler gracefully skips missing entries
|
|
349
|
+
await expect(onGapRecovery('ethereum', 'mainnet', ['w1'])).resolves.not.toThrow();
|
|
350
|
+
// solana pollAll should NOT have been called (we requested ethereum)
|
|
351
|
+
expect(mockPollAll).not.toHaveBeenCalled();
|
|
352
|
+
await service.stop();
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
//# sourceMappingURL=integration-wiring.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-wiring.test.js","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/integration-wiring.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for incoming transaction safety rules.
|
|
3
|
+
*
|
|
4
|
+
* Covers all 3 rule implementations with edge cases:
|
|
5
|
+
* - DustAttackRule: below/above threshold, null usdPrice
|
|
6
|
+
* - UnknownTokenRule: unregistered token, registered token, native transfer
|
|
7
|
+
* - LargeAmountRule: above/below multiplier, null avg, null price
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=safety-rules.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety-rules.test.d.ts","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/safety-rules.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for incoming transaction safety rules.
|
|
3
|
+
*
|
|
4
|
+
* Covers all 3 rule implementations with edge cases:
|
|
5
|
+
* - DustAttackRule: below/above threshold, null usdPrice
|
|
6
|
+
* - UnknownTokenRule: unregistered token, registered token, native transfer
|
|
7
|
+
* - LargeAmountRule: above/below multiplier, null avg, null price
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import { DustAttackRule, UnknownTokenRule, LargeAmountRule, } from '../safety-rules.js';
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
function makeTx(overrides = {}) {
|
|
15
|
+
return {
|
|
16
|
+
id: 'tx-001',
|
|
17
|
+
txHash: '0xabc',
|
|
18
|
+
walletId: 'wallet-001',
|
|
19
|
+
fromAddress: '0xsender',
|
|
20
|
+
amount: '1000000000', // 1 SOL in lamports (9 decimals)
|
|
21
|
+
tokenAddress: null,
|
|
22
|
+
chain: 'solana',
|
|
23
|
+
network: 'mainnet',
|
|
24
|
+
status: 'DETECTED',
|
|
25
|
+
blockNumber: 100,
|
|
26
|
+
detectedAt: 1700000000,
|
|
27
|
+
confirmedAt: null,
|
|
28
|
+
isSuspicious: false,
|
|
29
|
+
...overrides,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function makeContext(overrides = {}) {
|
|
33
|
+
return {
|
|
34
|
+
dustThresholdUsd: 0.01,
|
|
35
|
+
amountMultiplier: 10,
|
|
36
|
+
isRegisteredToken: true,
|
|
37
|
+
usdPrice: 100, // $100 per whole token
|
|
38
|
+
avgIncomingUsd: 500, // average incoming is $500
|
|
39
|
+
decimals: 9,
|
|
40
|
+
...overrides,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// DustAttackRule
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
describe('DustAttackRule', () => {
|
|
47
|
+
const rule = new DustAttackRule();
|
|
48
|
+
it('has name "dust"', () => {
|
|
49
|
+
expect(rule.name).toBe('dust');
|
|
50
|
+
});
|
|
51
|
+
it('flags transaction below dust threshold', () => {
|
|
52
|
+
// amount=10 lamports, price=$100, decimals=9 -> USD = 10 * 100 / 1e9 = 0.000001
|
|
53
|
+
const tx = makeTx({ amount: '10' });
|
|
54
|
+
const ctx = makeContext({ dustThresholdUsd: 0.01 });
|
|
55
|
+
expect(rule.check(tx, ctx)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it('does not flag transaction above dust threshold', () => {
|
|
58
|
+
// amount=1e9 (1 SOL), price=$100 -> USD = 100
|
|
59
|
+
const tx = makeTx({ amount: '1000000000' });
|
|
60
|
+
const ctx = makeContext({ dustThresholdUsd: 0.01 });
|
|
61
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
it('returns false when usdPrice is null (safe default)', () => {
|
|
64
|
+
const tx = makeTx({ amount: '10' });
|
|
65
|
+
const ctx = makeContext({ usdPrice: null });
|
|
66
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
it('flags when USD value exactly equals threshold', () => {
|
|
69
|
+
// amount * price / 10^decimals = 0.01
|
|
70
|
+
// amount = 0.01 * 1e9 / 100 = 100000
|
|
71
|
+
const tx = makeTx({ amount: '100000' });
|
|
72
|
+
const ctx = makeContext({ dustThresholdUsd: 0.01, usdPrice: 100, decimals: 9 });
|
|
73
|
+
// 100000 * 100 / 1e9 = 0.01 -- NOT less than threshold
|
|
74
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
it('handles EVM decimals (18)', () => {
|
|
77
|
+
// amount=100 wei, price=$2000, decimals=18 -> USD = 100 * 2000 / 1e18 = 0.0000000000000002
|
|
78
|
+
const tx = makeTx({ amount: '100', chain: 'ethereum' });
|
|
79
|
+
const ctx = makeContext({ usdPrice: 2000, decimals: 18, dustThresholdUsd: 0.01 });
|
|
80
|
+
expect(rule.check(tx, ctx)).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// UnknownTokenRule
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
describe('UnknownTokenRule', () => {
|
|
87
|
+
const rule = new UnknownTokenRule();
|
|
88
|
+
it('has name "unknownToken"', () => {
|
|
89
|
+
expect(rule.name).toBe('unknownToken');
|
|
90
|
+
});
|
|
91
|
+
it('flags token transfer with unregistered token', () => {
|
|
92
|
+
const tx = makeTx({ tokenAddress: '0xunknown' });
|
|
93
|
+
const ctx = makeContext({ isRegisteredToken: false });
|
|
94
|
+
expect(rule.check(tx, ctx)).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
it('does not flag token transfer with registered token', () => {
|
|
97
|
+
const tx = makeTx({ tokenAddress: '0xknown' });
|
|
98
|
+
const ctx = makeContext({ isRegisteredToken: true });
|
|
99
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
it('does not flag native transfer (tokenAddress = null)', () => {
|
|
102
|
+
const tx = makeTx({ tokenAddress: null });
|
|
103
|
+
const ctx = makeContext({ isRegisteredToken: false });
|
|
104
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// LargeAmountRule
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
describe('LargeAmountRule', () => {
|
|
111
|
+
const rule = new LargeAmountRule();
|
|
112
|
+
it('has name "largeAmount"', () => {
|
|
113
|
+
expect(rule.name).toBe('largeAmount');
|
|
114
|
+
});
|
|
115
|
+
it('flags transaction above multiplier * average', () => {
|
|
116
|
+
// amount=100e9 lamports (100 SOL), price=$100, decimals=9 -> USD = 10000
|
|
117
|
+
// avgIncoming=$500, multiplier=10 -> threshold = $5000
|
|
118
|
+
// 10000 > 5000 -> flagged
|
|
119
|
+
const tx = makeTx({ amount: '100000000000' });
|
|
120
|
+
const ctx = makeContext({
|
|
121
|
+
usdPrice: 100,
|
|
122
|
+
avgIncomingUsd: 500,
|
|
123
|
+
amountMultiplier: 10,
|
|
124
|
+
decimals: 9,
|
|
125
|
+
});
|
|
126
|
+
expect(rule.check(tx, ctx)).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
it('does not flag transaction below multiplier * average', () => {
|
|
129
|
+
// amount=1e9 (1 SOL), price=$100 -> USD = 100
|
|
130
|
+
// avgIncoming=$500, multiplier=10 -> threshold = $5000
|
|
131
|
+
// 100 < 5000 -> not flagged
|
|
132
|
+
const tx = makeTx({ amount: '1000000000' });
|
|
133
|
+
const ctx = makeContext({
|
|
134
|
+
usdPrice: 100,
|
|
135
|
+
avgIncomingUsd: 500,
|
|
136
|
+
amountMultiplier: 10,
|
|
137
|
+
decimals: 9,
|
|
138
|
+
});
|
|
139
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
it('returns false when avgIncomingUsd is null (safe default)', () => {
|
|
142
|
+
const tx = makeTx({ amount: '100000000000' });
|
|
143
|
+
const ctx = makeContext({ avgIncomingUsd: null });
|
|
144
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
it('returns false when usdPrice is null (safe default)', () => {
|
|
147
|
+
const tx = makeTx({ amount: '100000000000' });
|
|
148
|
+
const ctx = makeContext({ usdPrice: null });
|
|
149
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
it('does not flag when exactly at multiplier * average', () => {
|
|
152
|
+
// amount=50e9 (50 SOL), price=$100 -> USD = 5000
|
|
153
|
+
// avgIncoming=$500, multiplier=10 -> threshold = $5000
|
|
154
|
+
// 5000 > 5000 is false -> not flagged
|
|
155
|
+
const tx = makeTx({ amount: '50000000000' });
|
|
156
|
+
const ctx = makeContext({
|
|
157
|
+
usdPrice: 100,
|
|
158
|
+
avgIncomingUsd: 500,
|
|
159
|
+
amountMultiplier: 10,
|
|
160
|
+
decimals: 9,
|
|
161
|
+
});
|
|
162
|
+
expect(rule.check(tx, ctx)).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=safety-rules.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety-rules.test.js","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/safety-rules.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-multiplexer.test.d.ts","sourceRoot":"","sources":["../../../../src/services/incoming/__tests__/subscription-multiplexer.test.ts"],"names":[],"mappings":""}
|