@pioneer-platform/pioneer-discovery-service 0.2.1 → 0.2.3

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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Price Discovery Worker
3
+ *
4
+ * Background worker that:
5
+ * 1. Discovers alternative free price data sources
6
+ * 2. Monitors primary asset prices for empty/missing data
7
+ * 3. Sends Discord alerts for critical price issues
8
+ * 4. Maintains a database of working price sources
9
+ *
10
+ * This runs as part of the discovery service cron cycle
11
+ */
12
+ interface PriceSourceStatus {
13
+ name: string;
14
+ url: string;
15
+ working: boolean;
16
+ lastChecked: number;
17
+ lastPrice?: number;
18
+ responseTime?: number;
19
+ error?: string;
20
+ }
21
+ export declare class PriceDiscoveryWorker {
22
+ private priceSourceCache;
23
+ private discordNotifier;
24
+ constructor();
25
+ private initDiscordNotifier;
26
+ /**
27
+ * Main discovery run - called by the discovery agent
28
+ */
29
+ run(): Promise<void>;
30
+ /**
31
+ * Phase 1: Discover and test free price sources
32
+ */
33
+ private discoverPriceSources;
34
+ /**
35
+ * Test a single price source
36
+ */
37
+ private testPriceSource;
38
+ /**
39
+ * Phase 2: Monitor primary assets for empty prices
40
+ */
41
+ private monitorPrimaryAssets;
42
+ /**
43
+ * Check if an asset has a valid price using working sources
44
+ */
45
+ private checkAssetPrice;
46
+ /**
47
+ * Phase 3: Report findings
48
+ */
49
+ private reportFindings;
50
+ /**
51
+ * Get current status of all price sources
52
+ */
53
+ getSourceStatus(): PriceSourceStatus[];
54
+ }
55
+ export declare const priceDiscoveryWorker: PriceDiscoveryWorker;
56
+ export {};
57
+ //# sourceMappingURL=price-discovery.worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-discovery.worker.d.ts","sourceRoot":"","sources":["../../src/workers/price-discovery.worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkEH,UAAU,iBAAiB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAUD,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,eAAe,CAAM;;YAOf,mBAAmB;IAejC;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB1B;;OAEG;YACW,oBAAoB;IA0BlC;;OAEG;YACW,eAAe;IAiF7B;;OAEG;YACW,oBAAoB;IAqClC;;OAEG;YACW,eAAe;IA0D7B;;OAEG;YACW,cAAc;IA2B5B;;OAEG;IACH,eAAe,IAAI,iBAAiB,EAAE;CAGzC;AAGD,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
@@ -0,0 +1,372 @@
1
+ "use strict";
2
+ /**
3
+ * Price Discovery Worker
4
+ *
5
+ * Background worker that:
6
+ * 1. Discovers alternative free price data sources
7
+ * 2. Monitors primary asset prices for empty/missing data
8
+ * 3. Sends Discord alerts for critical price issues
9
+ * 4. Maintains a database of working price sources
10
+ *
11
+ * This runs as part of the discovery service cron cycle
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __importDefault = (this && this.__importDefault) || function (mod) {
47
+ return (mod && mod.__esModule) ? mod : { "default": mod };
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.priceDiscoveryWorker = exports.PriceDiscoveryWorker = void 0;
51
+ const axios_1 = __importDefault(require("axios"));
52
+ const log = require('@pioneer-platform/loggerdog')();
53
+ const TAG = ' | price-discovery | ';
54
+ // CRITICAL: Major cryptocurrencies that should NEVER have empty prices
55
+ const PRIMARY_ASSETS = [
56
+ { caip: 'bip122:000000000019d6689c085ae165831e93/slip44:0', symbol: 'bitcoin', name: 'Bitcoin (BTC)' },
57
+ { caip: 'eip155:1/slip44:60', symbol: 'ethereum', name: 'Ethereum (ETH)' },
58
+ { caip: 'eip155:56/slip44:60', symbol: 'binancecoin', name: 'BNB Chain (BNB)' },
59
+ { caip: 'eip155:137/slip44:60', symbol: 'matic-network', name: 'Polygon (MATIC)' },
60
+ { caip: 'cosmos:cosmoshub-4/slip44:118', symbol: 'cosmos', name: 'Cosmos (ATOM)' },
61
+ { caip: 'cosmos:thorchain-mainnet-v1/slip44:931', symbol: 'thorchain', name: 'Thorchain (RUNE)' },
62
+ { caip: 'bip122:12a765e31ffd4059bada1e25190f6e98/slip44:2', symbol: 'litecoin', name: 'Litecoin (LTC)' },
63
+ { caip: 'bip122:00000000001a91e3dace36e2be3bf030/slip44:3', symbol: 'dogecoin', name: 'Dogecoin (DOGE)' },
64
+ ];
65
+ // Free price API sources (no API key required)
66
+ const FREE_PRICE_SOURCES = [
67
+ {
68
+ name: 'CoinGecko (Free)',
69
+ url: 'https://api.coingecko.com/api/v3/simple/price',
70
+ rateLimit: { requests: 10, perMinutes: 1 },
71
+ priority: 1,
72
+ },
73
+ {
74
+ name: 'Blockchain.com',
75
+ url: 'https://blockchain.info/ticker',
76
+ rateLimit: { requests: 600, perHour: 1 },
77
+ priority: 2,
78
+ },
79
+ {
80
+ name: 'CoinPaprika',
81
+ url: 'https://api.coinpaprika.com/v1/tickers',
82
+ rateLimit: { requests: 20, perMinutes: 1 },
83
+ priority: 3,
84
+ },
85
+ {
86
+ name: 'CryptoCompare',
87
+ url: 'https://min-api.cryptocompare.com/data/price',
88
+ rateLimit: { requests: 100, perHour: 1 },
89
+ priority: 4,
90
+ },
91
+ {
92
+ name: 'Binance Public',
93
+ url: 'https://api.binance.com/api/v3/ticker/price',
94
+ rateLimit: { requests: 1200, perMinutes: 1 },
95
+ priority: 5,
96
+ },
97
+ {
98
+ name: 'Kraken Public',
99
+ url: 'https://api.kraken.com/0/public/Ticker',
100
+ rateLimit: { requests: 1, perSecond: 1 },
101
+ priority: 6,
102
+ },
103
+ {
104
+ name: 'Coinbase Public',
105
+ url: 'https://api.coinbase.com/v2/exchange-rates',
106
+ rateLimit: { requests: 10, perSecond: 1 },
107
+ priority: 7,
108
+ },
109
+ ];
110
+ class PriceDiscoveryWorker {
111
+ constructor() {
112
+ this.priceSourceCache = new Map();
113
+ // Initialize Discord notifier if available
114
+ this.initDiscordNotifier();
115
+ }
116
+ async initDiscordNotifier() {
117
+ try {
118
+ // Try to import discord notifier from pioneer-server if available
119
+ // This will fail gracefully if running standalone
120
+ const notifierPath = '../../../pioneer-server/src/services/discord-notifier.service';
121
+ const { discordNotifier } = await Promise.resolve(`${notifierPath}`).then(s => __importStar(require(s)));
122
+ if (discordNotifier && typeof discordNotifier.isEnabled === 'function' && discordNotifier.isEnabled()) {
123
+ this.discordNotifier = discordNotifier;
124
+ log.info(TAG, '✅ Discord notifier connected for price alerts');
125
+ }
126
+ }
127
+ catch (error) {
128
+ log.debug(TAG, 'Discord notifier not available (standalone mode) - this is OK');
129
+ }
130
+ }
131
+ /**
132
+ * Main discovery run - called by the discovery agent
133
+ */
134
+ async run() {
135
+ const tag = TAG + 'run | ';
136
+ log.info(tag, '🔍 Starting price discovery worker...');
137
+ try {
138
+ // Phase 1: Test all free price sources
139
+ await this.discoverPriceSources();
140
+ // Phase 2: Check primary assets for empty prices
141
+ await this.monitorPrimaryAssets();
142
+ // Phase 3: Report findings
143
+ await this.reportFindings();
144
+ log.info(tag, '✅ Price discovery worker completed');
145
+ }
146
+ catch (error) {
147
+ log.error(tag, '❌ Price discovery worker failed:', error);
148
+ throw error;
149
+ }
150
+ }
151
+ /**
152
+ * Phase 1: Discover and test free price sources
153
+ */
154
+ async discoverPriceSources() {
155
+ const tag = TAG + 'discoverPriceSources | ';
156
+ log.info(tag, `Testing ${FREE_PRICE_SOURCES.length} free price sources...`);
157
+ for (const source of FREE_PRICE_SOURCES) {
158
+ try {
159
+ const status = await this.testPriceSource(source);
160
+ this.priceSourceCache.set(source.name, status);
161
+ if (status.working) {
162
+ log.info(tag, `✅ ${source.name} - Working (${status.responseTime}ms)`);
163
+ }
164
+ else {
165
+ log.warn(tag, `❌ ${source.name} - Failed: ${status.error}`);
166
+ }
167
+ // Small delay to respect rate limits
168
+ await new Promise(resolve => setTimeout(resolve, 500));
169
+ }
170
+ catch (error) {
171
+ log.error(tag, `Error testing ${source.name}:`, error);
172
+ }
173
+ }
174
+ const workingCount = Array.from(this.priceSourceCache.values()).filter(s => s.working).length;
175
+ log.info(tag, `Price source discovery complete: ${workingCount}/${FREE_PRICE_SOURCES.length} working`);
176
+ }
177
+ /**
178
+ * Test a single price source
179
+ */
180
+ async testPriceSource(source) {
181
+ const tag = TAG + 'testPriceSource | ';
182
+ const startTime = Date.now();
183
+ const status = {
184
+ name: source.name,
185
+ url: source.url,
186
+ working: false,
187
+ lastChecked: Date.now(),
188
+ };
189
+ try {
190
+ let price;
191
+ // Test with Bitcoin as the reference asset
192
+ switch (source.name) {
193
+ case 'CoinGecko (Free)':
194
+ const cgResponse = await axios_1.default.get(`${source.url}?ids=bitcoin&vs_currencies=usd`, {
195
+ timeout: 5000,
196
+ });
197
+ price = cgResponse.data?.bitcoin?.usd;
198
+ break;
199
+ case 'Blockchain.com':
200
+ const bcResponse = await axios_1.default.get(source.url, { timeout: 5000 });
201
+ price = bcResponse.data?.USD?.last;
202
+ break;
203
+ case 'CoinPaprika':
204
+ const cpResponse = await axios_1.default.get(`${source.url}/btc-bitcoin`, { timeout: 5000 });
205
+ price = cpResponse.data?.quotes?.USD?.price;
206
+ break;
207
+ case 'CryptoCompare':
208
+ const ccResponse = await axios_1.default.get(`${source.url}?fsym=BTC&tsyms=USD`, {
209
+ timeout: 5000,
210
+ });
211
+ price = ccResponse.data?.USD;
212
+ break;
213
+ case 'Binance Public':
214
+ const binanceResponse = await axios_1.default.get(`${source.url}?symbol=BTCUSDT`, {
215
+ timeout: 5000,
216
+ });
217
+ price = parseFloat(binanceResponse.data?.price);
218
+ break;
219
+ case 'Kraken Public':
220
+ const krakenResponse = await axios_1.default.get(`${source.url}?pair=XBTUSD`, {
221
+ timeout: 5000,
222
+ });
223
+ price = parseFloat(krakenResponse.data?.result?.XXBTZUSD?.c?.[0]);
224
+ break;
225
+ case 'Coinbase Public':
226
+ const coinbaseResponse = await axios_1.default.get(`${source.url}?currency=BTC`, {
227
+ timeout: 5000,
228
+ });
229
+ price = parseFloat(coinbaseResponse.data?.data?.rates?.USD);
230
+ break;
231
+ }
232
+ const responseTime = Date.now() - startTime;
233
+ if (price && price > 0) {
234
+ status.working = true;
235
+ status.lastPrice = price;
236
+ status.responseTime = responseTime;
237
+ log.debug(tag, `${source.name}: $${price} (${responseTime}ms)`);
238
+ }
239
+ else {
240
+ status.error = 'No price data returned';
241
+ }
242
+ }
243
+ catch (error) {
244
+ status.error = error.message || 'Unknown error';
245
+ status.responseTime = Date.now() - startTime;
246
+ log.debug(tag, `${source.name} failed:`, status.error);
247
+ }
248
+ return status;
249
+ }
250
+ /**
251
+ * Phase 2: Monitor primary assets for empty prices
252
+ */
253
+ async monitorPrimaryAssets() {
254
+ const tag = TAG + 'monitorPrimaryAssets | ';
255
+ log.info(tag, `Checking prices for ${PRIMARY_ASSETS.length} primary assets...`);
256
+ const results = [];
257
+ for (const asset of PRIMARY_ASSETS) {
258
+ try {
259
+ const result = await this.checkAssetPrice(asset);
260
+ results.push(result);
261
+ if (!result.hasPrice) {
262
+ log.error(tag, `🚨 EMPTY PRICE: ${asset.name} (${asset.caip})`);
263
+ // Send Discord alert (rate limited to 1 per 24h per asset)
264
+ if (this.discordNotifier) {
265
+ await this.discordNotifier.sendEmptyPriceAlert(asset.caip, asset.name);
266
+ }
267
+ }
268
+ else {
269
+ log.debug(tag, `✅ ${asset.name}: $${result.price} (from ${result.source})`);
270
+ }
271
+ // Small delay between checks
272
+ await new Promise(resolve => setTimeout(resolve, 300));
273
+ }
274
+ catch (error) {
275
+ log.error(tag, `Error checking ${asset.name}:`, error);
276
+ }
277
+ }
278
+ const emptyCount = results.filter(r => !r.hasPrice).length;
279
+ if (emptyCount > 0) {
280
+ log.warn(tag, `⚠️ Found ${emptyCount} primary assets with empty prices!`);
281
+ }
282
+ else {
283
+ log.info(tag, '✅ All primary assets have valid prices');
284
+ }
285
+ }
286
+ /**
287
+ * Check if an asset has a valid price using working sources
288
+ */
289
+ async checkAssetPrice(asset) {
290
+ const tag = TAG + 'checkAssetPrice | ';
291
+ // Try working sources in priority order
292
+ const workingSources = Array.from(this.priceSourceCache.values())
293
+ .filter(s => s.working)
294
+ .sort((a, b) => {
295
+ const aPriority = FREE_PRICE_SOURCES.find(f => f.name === a.name)?.priority || 999;
296
+ const bPriority = FREE_PRICE_SOURCES.find(f => f.name === b.name)?.priority || 999;
297
+ return aPriority - bPriority;
298
+ });
299
+ if (workingSources.length === 0) {
300
+ log.warn(tag, 'No working price sources available');
301
+ return {
302
+ asset: asset.name,
303
+ hasPrice: false,
304
+ checked: Date.now(),
305
+ };
306
+ }
307
+ // Try first working source
308
+ const source = workingSources[0];
309
+ try {
310
+ // For simplicity, we'll use CoinGecko format for all (could be extended)
311
+ if (source.name === 'CoinGecko (Free)') {
312
+ const response = await axios_1.default.get(`${source.url}?ids=${asset.symbol}&vs_currencies=usd`, { timeout: 5000 });
313
+ const price = response.data?.[asset.symbol]?.usd;
314
+ return {
315
+ asset: asset.name,
316
+ hasPrice: !!(price && price > 0),
317
+ price,
318
+ source: source.name,
319
+ checked: Date.now(),
320
+ };
321
+ }
322
+ // Fallback: assume no price
323
+ return {
324
+ asset: asset.name,
325
+ hasPrice: false,
326
+ checked: Date.now(),
327
+ };
328
+ }
329
+ catch (error) {
330
+ log.debug(tag, `Failed to check price for ${asset.name}:`, error);
331
+ return {
332
+ asset: asset.name,
333
+ hasPrice: false,
334
+ checked: Date.now(),
335
+ };
336
+ }
337
+ }
338
+ /**
339
+ * Phase 3: Report findings
340
+ */
341
+ async reportFindings() {
342
+ const tag = TAG + 'reportFindings | ';
343
+ const workingSources = Array.from(this.priceSourceCache.values()).filter(s => s.working);
344
+ const failedSources = Array.from(this.priceSourceCache.values()).filter(s => !s.working);
345
+ log.info(tag, '\n📊 Price Discovery Report:');
346
+ log.info(tag, ` Working Sources: ${workingSources.length}`);
347
+ log.info(tag, ` Failed Sources: ${failedSources.length}`);
348
+ if (workingSources.length > 0) {
349
+ log.info(tag, '\n✅ Working Price Sources:');
350
+ for (const source of workingSources) {
351
+ log.info(tag, ` - ${source.name}: $${source.lastPrice} (${source.responseTime}ms)`);
352
+ }
353
+ }
354
+ if (failedSources.length > 0) {
355
+ log.warn(tag, '\n❌ Failed Price Sources:');
356
+ for (const source of failedSources) {
357
+ log.warn(tag, ` - ${source.name}: ${source.error}`);
358
+ }
359
+ }
360
+ // TODO: Save findings to MongoDB for historical tracking
361
+ }
362
+ /**
363
+ * Get current status of all price sources
364
+ */
365
+ getSourceStatus() {
366
+ return Array.from(this.priceSourceCache.values());
367
+ }
368
+ }
369
+ exports.PriceDiscoveryWorker = PriceDiscoveryWorker;
370
+ // Export singleton instance
371
+ exports.priceDiscoveryWorker = new PriceDiscoveryWorker();
372
+ //# sourceMappingURL=price-discovery.worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-discovery.worker.js","sourceRoot":"","sources":["../../src/workers/price-discovery.worker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,kDAA0B;AAG1B,MAAM,GAAG,GAAG,OAAO,CAAC,6BAA6B,CAAC,EAAE,CAAC;AACrD,MAAM,GAAG,GAAG,uBAAuB,CAAC;AAEpC,uEAAuE;AACvE,MAAM,cAAc,GAAG;IACnB,EAAE,IAAI,EAAE,kDAAkD,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE;IACtG,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE;IAC1E,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,iBAAiB,EAAE;IAC/E,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,EAAE;IAClF,EAAE,IAAI,EAAE,+BAA+B,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE;IAClF,EAAE,IAAI,EAAE,wCAAwC,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,kBAAkB,EAAE;IACjG,EAAE,IAAI,EAAE,kDAAkD,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE;IACxG,EAAE,IAAI,EAAE,kDAAkD,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,iBAAiB,EAAE;CAC5G,CAAC;AAEF,+CAA+C;AAC/C,MAAM,kBAAkB,GAAG;IACvB;QACI,IAAI,EAAE,kBAAkB;QACxB,GAAG,EAAE,+CAA+C;QACpD,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;QAC1C,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,gCAAgC;QACrC,SAAS,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE;QACxC,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,aAAa;QACnB,GAAG,EAAE,wCAAwC;QAC7C,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;QAC1C,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,eAAe;QACrB,GAAG,EAAE,8CAA8C;QACnD,SAAS,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE;QACxC,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,6CAA6C;QAClD,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE;QAC5C,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,eAAe;QACrB,GAAG,EAAE,wCAAwC;QAC7C,SAAS,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;QACxC,QAAQ,EAAE,CAAC;KACd;IACD;QACI,IAAI,EAAE,iBAAiB;QACvB,GAAG,EAAE,4CAA4C;QACjD,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE;QACzC,QAAQ,EAAE,CAAC;KACd;CACJ,CAAC;AAoBF,MAAa,oBAAoB;IAI7B;QAHQ,qBAAgB,GAAmC,IAAI,GAAG,EAAE,CAAC;QAIjE,2CAA2C;QAC3C,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC/B,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC7B,IAAI,CAAC;YACD,kEAAkE;YAClE,kDAAkD;YAClD,MAAM,YAAY,GAAG,+DAA+D,CAAC;YACrF,MAAM,EAAE,eAAe,EAAE,GAAG,yBAAa,YAAY,uCAAC,CAAC;YACvD,IAAI,eAAe,IAAI,OAAO,eAAe,CAAC,SAAS,KAAK,UAAU,IAAI,eAAe,CAAC,SAAS,EAAE,EAAE,CAAC;gBACpG,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;gBACvC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,+CAA+C,CAAC,CAAC;YACnE,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,+DAA+D,CAAC,CAAC;QACpF,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,GAAG;QACL,MAAM,GAAG,GAAG,GAAG,GAAG,QAAQ,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,uCAAuC,CAAC,CAAC;QAEvD,IAAI,CAAC;YACD,uCAAuC;YACvC,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAElC,iDAAiD;YACjD,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAElC,2BAA2B;YAC3B,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAE5B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,oCAAoC,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,kCAAkC,EAAE,KAAK,CAAC,CAAC;YAC1D,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB;QAC9B,MAAM,GAAG,GAAG,GAAG,GAAG,yBAAyB,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,kBAAkB,CAAC,MAAM,wBAAwB,CAAC,CAAC;QAE5E,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;YACtC,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAClD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAE/C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACjB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,CAAC,IAAI,eAAe,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC;gBAC3E,CAAC;qBAAM,CAAC;oBACJ,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,MAAM,CAAC,IAAI,cAAc,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAChE,CAAC;gBAED,qCAAqC;gBACrC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,MAAM,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QAC9F,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,oCAAoC,YAAY,IAAI,kBAAkB,CAAC,MAAM,UAAU,CAAC,CAAC;IAC3G,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAAC,MAAoC;QAC9D,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,MAAM,MAAM,GAAsB;YAC9B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,OAAO,EAAE,KAAK;YACd,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;SAC1B,CAAC;QAEF,IAAI,CAAC;YACD,IAAI,KAAyB,CAAC;YAE9B,2CAA2C;YAC3C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;gBAClB,KAAK,kBAAkB;oBACnB,MAAM,UAAU,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,gCAAgC,EAAE;wBAC9E,OAAO,EAAE,IAAI;qBAChB,CAAC,CAAC;oBACH,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC;oBACtC,MAAM;gBAEV,KAAK,gBAAgB;oBACjB,MAAM,UAAU,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;oBAClE,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC;oBACnC,MAAM;gBAEV,KAAK,aAAa;oBACd,MAAM,UAAU,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;oBACnF,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;oBAC5C,MAAM;gBAEV,KAAK,eAAe;oBAChB,MAAM,UAAU,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,qBAAqB,EAAE;wBACnE,OAAO,EAAE,IAAI;qBAChB,CAAC,CAAC;oBACH,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC;oBAC7B,MAAM;gBAEV,KAAK,gBAAgB;oBACjB,MAAM,eAAe,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,iBAAiB,EAAE;wBACpE,OAAO,EAAE,IAAI;qBAChB,CAAC,CAAC;oBACH,KAAK,GAAG,UAAU,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;oBAChD,MAAM;gBAEV,KAAK,eAAe;oBAChB,MAAM,cAAc,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,cAAc,EAAE;wBAChE,OAAO,EAAE,IAAI;qBAChB,CAAC,CAAC;oBACH,KAAK,GAAG,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;oBAClE,MAAM;gBAEV,KAAK,iBAAiB;oBAClB,MAAM,gBAAgB,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,eAAe,EAAE;wBACnE,OAAO,EAAE,IAAI;qBAChB,CAAC,CAAC;oBACH,KAAK,GAAG,UAAU,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;oBAC5D,MAAM;YACd,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAE5C,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACrB,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC;gBACzB,MAAM,CAAC,YAAY,GAAG,YAAY,CAAC;gBACnC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,MAAM,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC;YACpE,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,KAAK,GAAG,wBAAwB,CAAC;YAC5C,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YAClB,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,IAAI,eAAe,CAAC;YAChD,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC7C,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,oBAAoB;QAC9B,MAAM,GAAG,GAAG,GAAG,GAAG,yBAAyB,CAAC;QAC5C,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,cAAc,CAAC,MAAM,oBAAoB,CAAC,CAAC;QAEhF,MAAM,OAAO,GAAuB,EAAE,CAAC;QAEvC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACjC,IAAI,CAAC;gBACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;gBACjD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAErB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACnB,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,mBAAmB,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;oBAEhE,2DAA2D;oBAC3D,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;wBACvB,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC3E,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACJ,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,KAAK,CAAC,IAAI,MAAM,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;gBAChF,CAAC;gBAED,6BAA6B;gBAC7B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,kBAAkB,KAAK,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YAC3D,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;QAC3D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACjB,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,UAAU,oCAAoC,CAAC,CAAC;QAC9E,CAAC;aAAM,CAAC;YACJ,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAAC,KAA+B;QACzD,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,CAAC;QAEvC,wCAAwC;QACxC,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;aAC5D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;aACtB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACX,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,IAAI,GAAG,CAAC;YACnF,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,IAAI,GAAG,CAAC;YACnF,OAAO,SAAS,GAAG,SAAS,CAAC;QACjC,CAAC,CAAC,CAAC;QAEP,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,oCAAoC,CAAC,CAAC;YACpD,OAAO;gBACH,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;QACN,CAAC;QAED,2BAA2B;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;QAEjC,IAAI,CAAC;YACD,yEAAyE;YACzE,IAAI,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACrC,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,GAAG,CAC5B,GAAG,MAAM,CAAC,GAAG,QAAQ,KAAK,CAAC,MAAM,oBAAoB,EACrD,EAAE,OAAO,EAAE,IAAI,EAAE,CACpB,CAAC;gBACF,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC;gBAEjD,OAAO;oBACH,KAAK,EAAE,KAAK,CAAC,IAAI;oBACjB,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC;oBAChC,KAAK;oBACL,MAAM,EAAE,MAAM,CAAC,IAAI;oBACnB,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;iBACtB,CAAC;YACN,CAAC;YAED,4BAA4B;YAC5B,OAAO;gBACH,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,6BAA6B,KAAK,CAAC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;YAClE,OAAO;gBACH,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,QAAQ,EAAE,KAAK;gBACf,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;QACN,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc;QACxB,MAAM,GAAG,GAAG,GAAG,GAAG,mBAAmB,CAAC;QAEtC,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACzF,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAEzF,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,8BAA8B,CAAC,CAAC;QAC9C,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,sBAAsB,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;QAE5D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,4BAA4B,CAAC,CAAC;YAC5C,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;gBAClC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC;YAC1F,CAAC;QACL,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,2BAA2B,CAAC,CAAC;YAC3C,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;gBACjC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YAC1D,CAAC;QACL,CAAC;QAED,yDAAyD;IAC7D,CAAC;IAED;;OAEG;IACH,eAAe;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,CAAC;CACJ;AA1SD,oDA0SC;AAED,4BAA4B;AACf,QAAA,oBAAoB,GAAG,IAAI,oBAAoB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pioneer-platform/pioneer-discovery-service",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "AI-powered discovery service for dApps, networks, and assets",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -8,6 +8,8 @@ import { discoveryDB } from '../db';
8
8
  import { dataFetchers } from '../fetchers';
9
9
  import { discoveryAnalyzer } from '../analyzer';
10
10
  import { discoveryReporter } from '../reporter';
11
+ import { priceDiscoveryWorker } from '../workers/price-discovery.worker';
12
+ import { dappInvestigatorWorker } from '../workers/dapp-investigator.worker';
11
13
  import type {
12
14
  DappDiscoveryRecord,
13
15
  NetworkDiscoveryRecord,
@@ -18,7 +20,7 @@ import { v4 as uuidv4 } from 'uuid';
18
20
  const log = require('@pioneer-platform/loggerdog')();
19
21
  const TAG = ' | discovery-agent | ';
20
22
 
21
- const AGENT_VERSION = '0.1.0';
23
+ const AGENT_VERSION = '0.2.0'; // Updated to include price discovery and dapp investigation
22
24
 
23
25
  export class DiscoveryAgent {
24
26
  private isRunning = false;
@@ -62,14 +64,24 @@ export class DiscoveryAgent {
62
64
  discoveryReporter.addLog('INFO', 'Phase 3: Analyzing pre-configured assets');
63
65
  await this.analyzePreconfiguredAssets();
64
66
 
65
- // Phase 4: TODO - Web crawling for new dapps
67
+ // Phase 4: Price Discovery & Monitoring
68
+ await discoveryDB.updateCrawlerState({ currentPhase: 'price-discovery' });
69
+ discoveryReporter.addLog('INFO', 'Phase 4: Price discovery and monitoring');
70
+ await this.runPriceDiscovery();
71
+
72
+ // Phase 5: Deep DApp Investigation
73
+ await discoveryDB.updateCrawlerState({ currentPhase: 'dapp-investigation' });
74
+ discoveryReporter.addLog('INFO', 'Phase 5: Deep dApp investigation');
75
+ await this.investigateDApps();
76
+
77
+ // Phase 6: TODO - Web crawling for new dapps
66
78
  await discoveryDB.updateCrawlerState({ currentPhase: 'crawling' });
67
- discoveryReporter.addLog('INFO', 'Phase 4: Web crawling (TODO - not implemented yet)');
79
+ discoveryReporter.addLog('INFO', 'Phase 6: Web crawling (TODO - not implemented yet)');
68
80
  // await this.crawlForNewDapps();
69
81
 
70
- // Phase 5: Generate report
82
+ // Phase 7: Generate report
71
83
  await discoveryDB.updateCrawlerState({ currentPhase: 'reporting' });
72
- discoveryReporter.addLog('INFO', 'Phase 4: Generating report');
84
+ discoveryReporter.addLog('INFO', 'Phase 7: Generating report');
73
85
  const finalReport = await discoveryReporter.finalizeReport();
74
86
 
75
87
  // Print summary
@@ -410,6 +422,84 @@ export class DiscoveryAgent {
410
422
  }
411
423
  }
412
424
 
425
+ /**
426
+ * Phase 4: Price Discovery & Monitoring
427
+ */
428
+ private async runPriceDiscovery(): Promise<void> {
429
+ const tag = TAG + 'runPriceDiscovery | ';
430
+
431
+ try {
432
+ log.info(tag, 'Running price discovery worker...');
433
+
434
+ await priceDiscoveryWorker.run();
435
+
436
+ // Log source status
437
+ const sources = priceDiscoveryWorker.getSourceStatus();
438
+ const workingCount = sources.filter(s => s.working).length;
439
+
440
+ discoveryReporter.addLog('INFO', `Price discovery: ${workingCount}/${sources.length} sources working`);
441
+
442
+ log.info(tag, 'Price discovery complete');
443
+ } catch (error) {
444
+ log.error(tag, 'Failed to run price discovery:', error);
445
+ discoveryReporter.addLog('ERROR', `Price discovery failed: ${error}`);
446
+ // Non-fatal - continue with other phases
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Phase 5: Deep DApp Investigation
452
+ */
453
+ private async investigateDApps(): Promise<void> {
454
+ const tag = TAG + 'investigateDApps | ';
455
+
456
+ try {
457
+ log.info(tag, 'Running deep dApp investigation...');
458
+
459
+ // Get dapps that need investigation (not investigated in last 7 days)
460
+ const allDapps = await discoveryDB.getDappsNeedingCheck(24 * 7); // 7 days
461
+
462
+ // Limit to 10 dapps per run to avoid excessive processing
463
+ const dappsToInvestigate = allDapps.slice(0, 10);
464
+
465
+ if (dappsToInvestigate.length === 0) {
466
+ log.info(tag, 'No dapps need investigation at this time');
467
+ return;
468
+ }
469
+
470
+ log.info(tag, `Investigating ${dappsToInvestigate.length} dApps...`);
471
+
472
+ const results = await dappInvestigatorWorker.run(dappsToInvestigate);
473
+
474
+ // Update dapp records with investigation results
475
+ for (const result of results) {
476
+ try {
477
+ await discoveryDB.updateDapp(result.dappId, {
478
+ scamScore: result.riskScore / 100, // Convert to 0-1 range
479
+ whitelist: result.recommendWhitelist,
480
+ lastChecked: result.investigatedAt,
481
+ });
482
+
483
+ if (result.recommendWhitelist) {
484
+ discoveryReporter.addLog('INFO', `✅ Whitelisted after investigation: ${result.dappName}`);
485
+ }
486
+
487
+ if (result.riskScore > 70) {
488
+ discoveryReporter.addLog('WARN', `⚠️ High risk dApp flagged: ${result.dappName} (risk: ${result.riskScore})`);
489
+ }
490
+ } catch (error) {
491
+ log.error(tag, `Failed to update dapp ${result.dappId}:`, error);
492
+ }
493
+ }
494
+
495
+ log.info(tag, 'DApp investigation complete');
496
+ } catch (error) {
497
+ log.error(tag, 'Failed to investigate dApps:', error);
498
+ discoveryReporter.addLog('ERROR', `DApp investigation failed: ${error}`);
499
+ // Non-fatal - continue with other phases
500
+ }
501
+ }
502
+
413
503
  /**
414
504
  * Check if agent is currently running
415
505
  */
@@ -258,7 +258,7 @@ export interface CrawlerState {
258
258
  lastRun: number;
259
259
  nextScheduledRun: number;
260
260
  isRunning: boolean;
261
- currentPhase?: 'fetching' | 'analyzing' | 'crawling' | 'reporting';
261
+ currentPhase?: 'fetching' | 'analyzing' | 'price-discovery' | 'dapp-investigation' | 'crawling' | 'reporting';
262
262
  progress?: {
263
263
  total: number;
264
264
  completed: number;