@pioneer-platform/pioneer-discovery-service 0.2.0 → 0.2.2
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/CHANGELOG.md +14 -0
- package/DAPP-INVESTIGATION.md +459 -0
- package/IMPLEMENTATION-PLAN.md +296 -0
- package/PRICE-DISCOVERY.md +319 -0
- package/README.md +10 -4
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +79 -5
- package/dist/agent/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/workers/dapp-investigator.worker.d.ts +110 -0
- package/dist/workers/dapp-investigator.worker.d.ts.map +1 -0
- package/dist/workers/dapp-investigator.worker.js +277 -0
- package/dist/workers/dapp-investigator.worker.js.map +1 -0
- package/dist/workers/price-discovery.worker.d.ts +57 -0
- package/dist/workers/price-discovery.worker.d.ts.map +1 -0
- package/dist/workers/price-discovery.worker.js +372 -0
- package/dist/workers/price-discovery.worker.js.map +1 -0
- package/package.json +1 -1
- package/src/agent/index.ts +95 -5
- package/src/types/index.ts +1 -1
- package/src/workers/dapp-investigator.worker.ts +379 -0
- package/src/workers/price-discovery.worker.ts +397 -0
|
@@ -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":"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"}
|
package/package.json
CHANGED
package/src/agent/index.ts
CHANGED
|
@@ -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.
|
|
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:
|
|
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
|
|
79
|
+
discoveryReporter.addLog('INFO', 'Phase 6: Web crawling (TODO - not implemented yet)');
|
|
68
80
|
// await this.crawlForNewDapps();
|
|
69
81
|
|
|
70
|
-
// Phase
|
|
82
|
+
// Phase 7: Generate report
|
|
71
83
|
await discoveryDB.updateCrawlerState({ currentPhase: 'reporting' });
|
|
72
|
-
discoveryReporter.addLog('INFO', 'Phase
|
|
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
|
*/
|
package/src/types/index.ts
CHANGED
|
@@ -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;
|