nyxora 26.6.19 → 26.6.21
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/README.md +18 -1
- package/bin/nyxora.mjs +32 -0
- package/dist/packages/core/src/agent/reasoning.js +11 -1
- package/dist/packages/core/src/config/parser.js +121 -7
- package/dist/packages/core/src/gateway/chat.js +82 -0
- package/dist/packages/core/src/gateway/cli.js +63 -0
- package/dist/packages/core/src/gateway/server.js +117 -56
- package/dist/packages/core/src/gateway/setup.js +39 -22
- package/dist/packages/core/src/utils/formatter.test.js +40 -0
- package/dist/packages/core/src/utils/skillManager.js +91 -0
- package/dist/packages/core/src/utils/userWhitelistManager.js +41 -36
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +2 -2
- package/dist/packages/core/src/web3/aggregator/defiRouter.js +3 -0
- package/dist/packages/core/src/web3/skills/bridgeToken.js +4 -0
- package/dist/packages/core/src/web3/skills/checkRegistryStatus.js +13 -0
- package/dist/packages/core/src/web3/skills/customTx.js +2 -0
- package/dist/packages/core/src/web3/skills/defiLending.js +2 -0
- package/dist/packages/core/src/web3/skills/getPrice.js +11 -6
- package/dist/packages/core/src/web3/skills/manageCustomTokens.js +18 -32
- package/dist/packages/core/src/web3/skills/marketAnalysis.js +3 -1
- package/dist/packages/core/src/web3/skills/mintNft.js +2 -0
- package/dist/packages/core/src/web3/skills/provideLiquidity.js +2 -0
- package/dist/packages/core/src/web3/skills/revokeApprovals.js +2 -0
- package/dist/packages/core/src/web3/skills/swapToken.js +4 -2
- package/dist/packages/core/src/web3/skills/transfer.js +2 -0
- package/dist/packages/core/src/web3/skills/yieldVault.js +2 -0
- package/dist/packages/core/src/web3/utils/tokens.js +9 -1
- package/package.json +2 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/reasoning.ts +12 -1
- package/packages/core/src/config/parser.ts +119 -9
- package/packages/core/src/gateway/chat.ts +85 -0
- package/packages/core/src/gateway/cli.ts +63 -0
- package/packages/core/src/gateway/server.ts +132 -60
- package/packages/core/src/gateway/setup.ts +39 -27
- package/packages/core/src/utils/formatter.test.ts +41 -0
- package/packages/core/src/utils/skillManager.ts +98 -0
- package/packages/core/src/utils/userWhitelistManager.ts +48 -39
- package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +2 -2
- package/packages/core/src/web3/aggregator/defiRouter.ts +4 -0
- package/packages/core/src/web3/skills/bridgeToken.ts +3 -0
- package/packages/core/src/web3/skills/checkRegistryStatus.ts +13 -0
- package/packages/core/src/web3/skills/customTx.ts +1 -0
- package/packages/core/src/web3/skills/defiLending.ts +1 -0
- package/packages/core/src/web3/skills/getPrice.ts +11 -6
- package/packages/core/src/web3/skills/manageCustomTokens.ts +18 -29
- package/packages/core/src/web3/skills/marketAnalysis.ts +2 -1
- package/packages/core/src/web3/skills/mintNft.ts +1 -0
- package/packages/core/src/web3/skills/provideLiquidity.ts +1 -0
- package/packages/core/src/web3/skills/revokeApprovals.ts +1 -0
- package/packages/core/src/web3/skills/swapToken.ts +3 -2
- package/packages/core/src/web3/skills/transfer.ts +1 -0
- package/packages/core/src/web3/skills/yieldVault.ts +1 -0
- package/packages/core/src/web3/utils/tokens.ts +9 -1
- package/packages/dashboard/dist/assets/{index-DnQrbB4c.css → index-CQNHWZtN.css} +1 -1
- package/packages/dashboard/dist/assets/index-Di9x08yk.js +16 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package.json +1 -1
- package/packages/mcp-server/package.json +1 -1
- package/packages/policy/package.json +1 -1
- package/packages/signer/package.json +1 -1
- package/dist/packages/core/src/agent/limitOrderManager.js +0 -124
- package/dist/packages/core/src/system/pluginManager.js +0 -91
- package/dist/packages/core/src/system/skills/installSkill.js +0 -52
- package/dist/packages/core/src/test-all-routers.js +0 -81
- package/dist/packages/core/src/test-router.js +0 -38
- package/dist/packages/core/src/web3/skills/autonomousDefi.js +0 -191
- package/dist/packages/core/src/web3/skills/createWallet.js +0 -34
- package/dist/packages/core/src/web3/skills/limitOrder.js +0 -106
- package/dist/packages/core/src/web3/utils/protocolRegistry.js +0 -46
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/packages/core/src/__tests__/reasoning.test.ts +0 -81
- package/packages/core/src/__tests__/tokens.test.ts +0 -55
- package/packages/core/src/__tests__/web3.test.ts +0 -50
- package/packages/core/src/agent/reasoning.d.ts.map +0 -1
- package/packages/core/src/config/parser.d.ts.map +0 -1
- package/packages/core/src/gateway/cli.d.ts.map +0 -1
- package/packages/core/src/memory/logger.d.ts.map +0 -1
- package/packages/core/src/test-all-routers.ts +0 -59
- package/packages/core/src/test-router.ts +0 -49
- package/packages/core/src/web3/config.d.ts.map +0 -1
- package/packages/core/src/web3/skills/getBalance.d.ts.map +0 -1
- package/packages/dashboard/dist/assets/index-BAXifdMN.js +0 -16
|
@@ -52,6 +52,8 @@ const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
|
|
|
52
52
|
const getTxHistory_1 = require("../web3/skills/getTxHistory");
|
|
53
53
|
const checkRegistryStatus_1 = require("../web3/skills/checkRegistryStatus");
|
|
54
54
|
const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
|
|
55
|
+
const userWhitelistManager_1 = require("../utils/userWhitelistManager");
|
|
56
|
+
const tokens_2 = require("../web3/utils/tokens");
|
|
55
57
|
// System Skills
|
|
56
58
|
const browseWeb_1 = require("../system/skills/browseWeb");
|
|
57
59
|
const executeShell_1 = require("../system/skills/executeShell");
|
|
@@ -77,6 +79,8 @@ const episodic_1 = require("../memory/episodic");
|
|
|
77
79
|
const reflection_1 = require("../memory/reflection");
|
|
78
80
|
// Initialize Google Auth
|
|
79
81
|
(0, googleAuthModule_1.initGoogleAuth)();
|
|
82
|
+
// Synchronize all active skills to config.yaml on startup
|
|
83
|
+
(0, skillManager_1.syncAllSkillsToConfig)();
|
|
80
84
|
const util_1 = __importDefault(require("util"));
|
|
81
85
|
// Intercept console.log and console.error
|
|
82
86
|
const originalLog = console.log;
|
|
@@ -307,34 +311,62 @@ app.post('/api/defi-keys', (req, res) => {
|
|
|
307
311
|
res.status(500).json({ error: error.message });
|
|
308
312
|
}
|
|
309
313
|
});
|
|
314
|
+
const allSkills = [
|
|
315
|
+
getBalance_1.getBalanceToolDefinition,
|
|
316
|
+
transfer_1.transferToolDefinition,
|
|
317
|
+
getPrice_1.getPriceToolDefinition,
|
|
318
|
+
swapToken_1.swapTokenToolDefinition,
|
|
319
|
+
bridgeToken_1.bridgeTokenToolDefinition,
|
|
320
|
+
mintNft_1.mintNftToolDefinition,
|
|
321
|
+
customTx_1.customTxToolDefinition,
|
|
322
|
+
checkAddress_1.checkAddressToolDefinition,
|
|
323
|
+
getMyAddress_1.getMyAddressToolDefinition,
|
|
324
|
+
checkSecurity_1.checkSecurityToolDefinition,
|
|
325
|
+
checkPortfolio_1.checkPortfolioToolDefinition,
|
|
326
|
+
marketAnalysis_1.marketAnalysisToolDefinition,
|
|
327
|
+
manageCustomTokens_1.manageCustomTokensDefinition,
|
|
328
|
+
defiLending_1.aaveSupplyToolDefinition,
|
|
329
|
+
revokeApprovals_2.revokeApprovalToolDefinition,
|
|
330
|
+
yieldVault_1.vaultDepositToolDefinition,
|
|
331
|
+
provideLiquidity_1.provideLiquidityToolDefinition,
|
|
332
|
+
getTxHistory_1.getTxHistoryToolDefinition,
|
|
333
|
+
createLimitOrder_1.createLimitOrderToolDefinition,
|
|
334
|
+
checkRegistryStatus_1.checkRegistryStatusToolDefinition
|
|
335
|
+
];
|
|
336
|
+
const systemSkills = [
|
|
337
|
+
executeShell_1.runTerminalCommandToolDefinition,
|
|
338
|
+
readFile_1.readLocalFileToolDefinition,
|
|
339
|
+
writeFile_1.writeLocalFileToolDefinition,
|
|
340
|
+
generateExcel_1.generateExcelToolDefinition,
|
|
341
|
+
browseWeb_1.browseWebsiteToolDefinition,
|
|
342
|
+
updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
|
|
343
|
+
analyzeDocument_1.analyzeDocumentToolDefinition,
|
|
344
|
+
searchWeb_1.searchWebToolDefinition,
|
|
345
|
+
googleWorkspace_1.readGmailInboxToolDefinition,
|
|
346
|
+
googleWorkspace_1.listCalendarEventsToolDefinition,
|
|
347
|
+
googleWorkspace_1.appendRowToSheetsToolDefinition,
|
|
348
|
+
googleWorkspace_1.readGoogleDocsToolDefinition,
|
|
349
|
+
googleWorkspace_1.readGoogleFormResponsesToolDefinition,
|
|
350
|
+
editFile_1.editLocalFileToolDefinition,
|
|
351
|
+
gitManager_1.gitManagerToolDefinition,
|
|
352
|
+
xManager_1.xManagerToolDefinition,
|
|
353
|
+
notionWorkspace_1.notionWorkspaceToolDefinition,
|
|
354
|
+
audioTranscribe_1.audioTranscribeToolDefinition,
|
|
355
|
+
summarizeText_1.summarizeTextToolDefinition
|
|
356
|
+
];
|
|
310
357
|
app.get('/api/stats', (req, res) => {
|
|
311
|
-
|
|
358
|
+
const stats = tracker_1.Tracker.getStats();
|
|
359
|
+
const dbPath = (0, paths_1.getPath)('memory.db');
|
|
360
|
+
const activeWeb3 = allSkills.filter(s => (0, skillManager_1.isSkillActive)(s.function.name)).length;
|
|
361
|
+
const activeSystem = systemSkills.filter(s => (0, skillManager_1.isSkillActive)(s.function.name)).length;
|
|
362
|
+
const totalSkills = allSkills.length + systemSkills.length;
|
|
363
|
+
const activeSkills = activeWeb3 + activeSystem;
|
|
364
|
+
res.json({ ...stats, memoryPath: dbPath, totalSkills, activeSkills });
|
|
312
365
|
});
|
|
313
366
|
app.get('/api/logs', (req, res) => {
|
|
314
367
|
res.json(tracker_1.Tracker.getLogs());
|
|
315
368
|
});
|
|
316
369
|
app.get('/api/skills', (req, res) => {
|
|
317
|
-
const allSkills = [
|
|
318
|
-
getBalance_1.getBalanceToolDefinition,
|
|
319
|
-
transfer_1.transferToolDefinition,
|
|
320
|
-
getPrice_1.getPriceToolDefinition,
|
|
321
|
-
swapToken_1.swapTokenToolDefinition,
|
|
322
|
-
bridgeToken_1.bridgeTokenToolDefinition,
|
|
323
|
-
mintNft_1.mintNftToolDefinition,
|
|
324
|
-
customTx_1.customTxToolDefinition,
|
|
325
|
-
checkAddress_1.checkAddressToolDefinition,
|
|
326
|
-
getMyAddress_1.getMyAddressToolDefinition,
|
|
327
|
-
checkSecurity_1.checkSecurityToolDefinition,
|
|
328
|
-
checkPortfolio_1.checkPortfolioToolDefinition,
|
|
329
|
-
marketAnalysis_1.marketAnalysisToolDefinition,
|
|
330
|
-
manageCustomTokens_1.manageCustomTokensDefinition,
|
|
331
|
-
defiLending_1.aaveSupplyToolDefinition,
|
|
332
|
-
revokeApprovals_2.revokeApprovalToolDefinition,
|
|
333
|
-
yieldVault_1.vaultDepositToolDefinition,
|
|
334
|
-
provideLiquidity_1.provideLiquidityToolDefinition,
|
|
335
|
-
getTxHistory_1.getTxHistoryToolDefinition,
|
|
336
|
-
createLimitOrder_1.createLimitOrderToolDefinition
|
|
337
|
-
];
|
|
338
370
|
const skillsWithStatus = allSkills.map(skill => ({
|
|
339
371
|
...skill,
|
|
340
372
|
isActive: (0, skillManager_1.isSkillActive)(skill.function.name)
|
|
@@ -342,27 +374,6 @@ app.get('/api/skills', (req, res) => {
|
|
|
342
374
|
res.json(skillsWithStatus);
|
|
343
375
|
});
|
|
344
376
|
app.get('/api/skills/system', (req, res) => {
|
|
345
|
-
const systemSkills = [
|
|
346
|
-
executeShell_1.runTerminalCommandToolDefinition,
|
|
347
|
-
readFile_1.readLocalFileToolDefinition,
|
|
348
|
-
writeFile_1.writeLocalFileToolDefinition,
|
|
349
|
-
generateExcel_1.generateExcelToolDefinition,
|
|
350
|
-
browseWeb_1.browseWebsiteToolDefinition,
|
|
351
|
-
updateSecurityPolicy_1.updateSecurityPolicyToolDefinition,
|
|
352
|
-
analyzeDocument_1.analyzeDocumentToolDefinition,
|
|
353
|
-
searchWeb_1.searchWebToolDefinition,
|
|
354
|
-
googleWorkspace_1.readGmailInboxToolDefinition,
|
|
355
|
-
googleWorkspace_1.listCalendarEventsToolDefinition,
|
|
356
|
-
googleWorkspace_1.appendRowToSheetsToolDefinition,
|
|
357
|
-
googleWorkspace_1.readGoogleDocsToolDefinition,
|
|
358
|
-
googleWorkspace_1.readGoogleFormResponsesToolDefinition,
|
|
359
|
-
editFile_1.editLocalFileToolDefinition,
|
|
360
|
-
gitManager_1.gitManagerToolDefinition,
|
|
361
|
-
xManager_1.xManagerToolDefinition,
|
|
362
|
-
notionWorkspace_1.notionWorkspaceToolDefinition,
|
|
363
|
-
audioTranscribe_1.audioTranscribeToolDefinition,
|
|
364
|
-
summarizeText_1.summarizeTextToolDefinition
|
|
365
|
-
];
|
|
366
377
|
const skillsWithStatus = systemSkills.map(skill => ({
|
|
367
378
|
...skill,
|
|
368
379
|
isActive: (0, skillManager_1.isSkillActive)(skill.function.name)
|
|
@@ -377,6 +388,41 @@ app.post('/api/skills/toggle', (req, res) => {
|
|
|
377
388
|
(0, skillManager_1.toggleSkill)(skillName, active);
|
|
378
389
|
res.json({ success: true, skillName, active });
|
|
379
390
|
});
|
|
391
|
+
// Portfolio Whitelist Routes
|
|
392
|
+
app.get('/api/portfolio/whitelist', async (req, res) => {
|
|
393
|
+
const whitelist = (0, userWhitelistManager_1.getUserWhitelist)();
|
|
394
|
+
res.json(whitelist);
|
|
395
|
+
});
|
|
396
|
+
app.post('/api/portfolio/whitelist', async (req, res) => {
|
|
397
|
+
const { walletAddress, chainName, tokenAddress, symbol, decimals } = req.body;
|
|
398
|
+
if (!walletAddress || !chainName || !tokenAddress) {
|
|
399
|
+
return res.status(400).json({ error: 'Missing required fields' });
|
|
400
|
+
}
|
|
401
|
+
await (0, userWhitelistManager_1.saveTokenToWhitelist)(walletAddress, chainName, tokenAddress, 'manual', symbol, decimals);
|
|
402
|
+
res.json({ success: true });
|
|
403
|
+
});
|
|
404
|
+
app.delete('/api/portfolio/whitelist', (req, res) => {
|
|
405
|
+
const { walletAddress, chainName, tokenAddress } = req.body;
|
|
406
|
+
if (!walletAddress || !chainName || !tokenAddress) {
|
|
407
|
+
return res.status(400).json({ error: 'Missing required fields' });
|
|
408
|
+
}
|
|
409
|
+
(0, userWhitelistManager_1.removeTokenFromWhitelist)(walletAddress, chainName, tokenAddress);
|
|
410
|
+
res.json({ success: true });
|
|
411
|
+
});
|
|
412
|
+
app.get('/api/portfolio/token-metadata', async (req, res) => {
|
|
413
|
+
const { chain, address } = req.query;
|
|
414
|
+
if (!chain || !address || typeof address !== 'string') {
|
|
415
|
+
return res.status(400).json({ error: 'Missing chain or address' });
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const client = (0, config_1.getPublicClient)(chain);
|
|
419
|
+
const metadata = await (0, tokens_2.getTokenMetadata)(client, address);
|
|
420
|
+
res.json(metadata);
|
|
421
|
+
}
|
|
422
|
+
catch (err) {
|
|
423
|
+
res.status(500).json({ error: err.message });
|
|
424
|
+
}
|
|
425
|
+
});
|
|
380
426
|
// Google Workspace Auth Routes
|
|
381
427
|
app.get('/api/auth/google/url', (req, res) => {
|
|
382
428
|
const url = (0, googleAuthModule_1.getAuthUrl)();
|
|
@@ -563,6 +609,7 @@ app.post('/api/transactions/:id/reject', async (req, res) => {
|
|
|
563
609
|
let cachedTrending = null;
|
|
564
610
|
let lastTrendingFetch = 0;
|
|
565
611
|
let cachedPrices = {};
|
|
612
|
+
let cachedPriceChanges = {};
|
|
566
613
|
let lastPricesFetch = 0;
|
|
567
614
|
app.get('/api/trending', async (req, res) => {
|
|
568
615
|
const now = Date.now();
|
|
@@ -591,17 +638,20 @@ app.get('/api/trending', async (req, res) => {
|
|
|
591
638
|
res.status(500).json({ error: err.message });
|
|
592
639
|
}
|
|
593
640
|
});
|
|
641
|
+
app.get('/api/wallet', async (req, res) => {
|
|
642
|
+
try {
|
|
643
|
+
const userAddress = await (0, config_1.getAddress)();
|
|
644
|
+
res.json({ address: userAddress });
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
res.status(500).json({ error: error.message });
|
|
648
|
+
}
|
|
649
|
+
});
|
|
594
650
|
app.get('/api/portfolio', async (req, res) => {
|
|
595
651
|
try {
|
|
596
652
|
const userAddress = await (0, config_1.getAddress)();
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
if (fs_1.default.existsSync(customTokensPath)) {
|
|
600
|
-
try {
|
|
601
|
-
customTokens = JSON.parse(fs_1.default.readFileSync(customTokensPath, 'utf8'));
|
|
602
|
-
}
|
|
603
|
-
catch (e) { }
|
|
604
|
-
}
|
|
653
|
+
const whitelist = (0, userWhitelistManager_1.getUserWhitelist)();
|
|
654
|
+
const userCustomTokens = whitelist[userAddress.toLowerCase()] || [];
|
|
605
655
|
const portfolio = {};
|
|
606
656
|
await Promise.all(config_1.SUPPORTED_CHAIN_NAMES.map(async (chainName) => {
|
|
607
657
|
portfolio[chainName] = [];
|
|
@@ -618,11 +668,14 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
618
668
|
isNative: true
|
|
619
669
|
});
|
|
620
670
|
}
|
|
621
|
-
// 2. Combine TOKEN_MAP and
|
|
671
|
+
// 2. Combine TOKEN_MAP and YAML whitelist for this chain
|
|
622
672
|
const tokensToQuery = { ...(tokens_1.TOKEN_MAP[chainName] || {}) };
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
673
|
+
// Inject whitelisted tokens
|
|
674
|
+
userCustomTokens.forEach(t => {
|
|
675
|
+
if (t.chainName === chainName && t.symbol && t.address) {
|
|
676
|
+
tokensToQuery[t.symbol.toUpperCase()] = t.address;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
626
679
|
// 3. Query all ERC-20 balances in parallel
|
|
627
680
|
await Promise.all(Object.entries(tokensToQuery).map(async ([symbol, address]) => {
|
|
628
681
|
if (address === '0x0000000000000000000000000000000000000000')
|
|
@@ -640,7 +693,8 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
640
693
|
functionName: 'decimals'
|
|
641
694
|
});
|
|
642
695
|
const [bal, decimals] = await Promise.all([balPromise, decPromise]);
|
|
643
|
-
|
|
696
|
+
const isCustom = userCustomTokens.some(t => t.chainName === chainName && t.address === address);
|
|
697
|
+
if (bal > 0n || isCustom) {
|
|
644
698
|
portfolio[chainName].push({
|
|
645
699
|
symbol,
|
|
646
700
|
address,
|
|
@@ -681,9 +735,11 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
681
735
|
const uniqueAddrs = Array.from(addressesToFetch);
|
|
682
736
|
const now = Date.now();
|
|
683
737
|
let priceMap = cachedPrices;
|
|
738
|
+
let changeMap = cachedPriceChanges;
|
|
684
739
|
if (uniqueAddrs.length > 0 && now - lastPricesFetch > 2 * 60 * 1000) {
|
|
685
740
|
try {
|
|
686
741
|
const newPrices = {};
|
|
742
|
+
const newChanges = {};
|
|
687
743
|
await Promise.all(uniqueAddrs.map(async (addr) => {
|
|
688
744
|
try {
|
|
689
745
|
const res = await (0, httpClient_1.safeFetch)(`https://api.dexscreener.com/latest/dex/tokens/${addr}`);
|
|
@@ -706,9 +762,11 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
706
762
|
const quoteAddr = bestPair.quoteToken?.address?.toLowerCase();
|
|
707
763
|
if (baseAddr === addr) {
|
|
708
764
|
newPrices[addr] = parseFloat(bestPair.priceUsd);
|
|
765
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
709
766
|
}
|
|
710
767
|
else if (quoteAddr === addr && bestPair.priceNative && parseFloat(bestPair.priceNative) > 0) {
|
|
711
768
|
newPrices[addr] = parseFloat(bestPair.priceUsd) / parseFloat(bestPair.priceNative);
|
|
769
|
+
newChanges[addr] = bestPair.priceChange?.h24 || 0;
|
|
712
770
|
}
|
|
713
771
|
}
|
|
714
772
|
}
|
|
@@ -720,7 +778,9 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
720
778
|
}));
|
|
721
779
|
console.log('DexScreener Fetched Prices:', newPrices);
|
|
722
780
|
cachedPrices = { ...cachedPrices, ...newPrices };
|
|
781
|
+
cachedPriceChanges = { ...cachedPriceChanges, ...newChanges };
|
|
723
782
|
priceMap = cachedPrices;
|
|
783
|
+
changeMap = cachedPriceChanges;
|
|
724
784
|
lastPricesFetch = now;
|
|
725
785
|
}
|
|
726
786
|
catch (e) {
|
|
@@ -735,6 +795,7 @@ app.get('/api/portfolio', async (req, res) => {
|
|
|
735
795
|
lookupAddr = ((tokens_1.TOKEN_MAP[chain]?.[wToken]) || '').toLowerCase();
|
|
736
796
|
}
|
|
737
797
|
t.priceUsd = priceMap[lookupAddr] || 0;
|
|
798
|
+
t.priceChange24h = changeMap[lookupAddr] || 0;
|
|
738
799
|
}
|
|
739
800
|
}
|
|
740
801
|
res.json(portfolio);
|
|
@@ -10,22 +10,6 @@ const picocolors_1 = __importDefault(require("picocolors"));
|
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const paths_1 = require("../config/paths");
|
|
12
12
|
const parser_1 = require("../config/parser");
|
|
13
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
14
|
-
function encryptKey(privateKey, password) {
|
|
15
|
-
const salt = crypto_1.default.randomBytes(16);
|
|
16
|
-
const key = crypto_1.default.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
|
|
17
|
-
const iv = crypto_1.default.randomBytes(12);
|
|
18
|
-
const cipher = crypto_1.default.createCipheriv('aes-256-gcm', key, iv);
|
|
19
|
-
let encrypted = cipher.update(privateKey, 'utf8', 'hex');
|
|
20
|
-
encrypted += cipher.final('hex');
|
|
21
|
-
const authTag = cipher.getAuthTag().toString('hex');
|
|
22
|
-
return {
|
|
23
|
-
salt: salt.toString('hex'),
|
|
24
|
-
iv: iv.toString('hex'),
|
|
25
|
-
authTag,
|
|
26
|
-
encryptedData: encrypted
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
13
|
const accounts_1 = require("viem/accounts");
|
|
30
14
|
async function runSetupWizard() {
|
|
31
15
|
console.clear();
|
|
@@ -274,9 +258,9 @@ Provider: ${config.llm.provider}`;
|
|
|
274
258
|
privateKey = '0x' + Buffer.from(account.getHdKey().privateKey).toString('hex');
|
|
275
259
|
prompts_1.log.success('New Wallet Generated!');
|
|
276
260
|
prompts_1.log.info(`Address: ${account.address}`);
|
|
277
|
-
prompts_1.log.info(`Private Key:
|
|
261
|
+
prompts_1.log.info(`Private Key: [REDACTED - Saved securely to vault]`);
|
|
278
262
|
prompts_1.log.info(`Seed Phrase (Mnemonic): ${seedPhrase}`);
|
|
279
|
-
prompts_1.log.warn('IMPORTANT: Write down these 12 words
|
|
263
|
+
prompts_1.log.warn('IMPORTANT: Write down these 12 words NOW! This is your ONLY backup. The credentials have been securely injected into your local OS vault.');
|
|
280
264
|
}
|
|
281
265
|
}
|
|
282
266
|
// --- OS SKILLS ---
|
|
@@ -343,16 +327,26 @@ Provider: ${config.llm.provider}`;
|
|
|
343
327
|
}));
|
|
344
328
|
if ((0, prompts_1.isCancel)(telegramToken))
|
|
345
329
|
return process.exit(0);
|
|
346
|
-
if (telegramToken &&
|
|
330
|
+
if (telegramToken && telegramToken.trim() !== '') {
|
|
331
|
+
authorizedChatId = undefined;
|
|
332
|
+
}
|
|
333
|
+
const activeToken = telegramToken || config.integrations?.telegram?.bot_token;
|
|
334
|
+
if (activeToken && !authorizedChatId) {
|
|
347
335
|
const s = (0, prompts_1.spinner)();
|
|
348
336
|
const pin = Math.floor(100000 + Math.random() * 900000).toString();
|
|
349
337
|
(0, prompts_1.note)(picocolors_1.default.cyan(`1. Open Telegram and search for your Bot.\n2. Send this exact message to your bot:\n\n /auth ${pin}\n\nWaiting for your message...`), 'Telegram Pairing Required');
|
|
350
338
|
s.start(`Waiting for /auth ${pin} on Telegram...`);
|
|
339
|
+
let bot = null;
|
|
351
340
|
try {
|
|
352
341
|
const { Telegraf } = require('telegraf');
|
|
353
|
-
|
|
342
|
+
bot = new Telegraf(activeToken);
|
|
354
343
|
let paired = false;
|
|
344
|
+
let failedAttempts = {};
|
|
355
345
|
bot.command('auth', (ctx) => {
|
|
346
|
+
const chatId = ctx.chat.id.toString();
|
|
347
|
+
if (failedAttempts[chatId] >= 5) {
|
|
348
|
+
return ctx.reply('❌ Too many failed attempts. You are locked out.');
|
|
349
|
+
}
|
|
356
350
|
const text = ctx.message.text.split(' ');
|
|
357
351
|
if (text[1] === pin) {
|
|
358
352
|
authorizedChatId = ctx.chat.id;
|
|
@@ -361,18 +355,38 @@ Provider: ${config.llm.provider}`;
|
|
|
361
355
|
bot.stop();
|
|
362
356
|
}
|
|
363
357
|
else {
|
|
358
|
+
failedAttempts[chatId] = (failedAttempts[chatId] || 0) + 1;
|
|
364
359
|
ctx.reply('❌ Invalid PIN.');
|
|
365
360
|
}
|
|
366
361
|
});
|
|
367
362
|
bot.launch();
|
|
368
363
|
// Wait until paired
|
|
369
|
-
|
|
364
|
+
let attempts = 0;
|
|
365
|
+
while (!paired && attempts < 120) {
|
|
370
366
|
await new Promise(r => setTimeout(r, 1000));
|
|
367
|
+
attempts++;
|
|
368
|
+
}
|
|
369
|
+
if (!paired) {
|
|
370
|
+
s.stop('Timeout waiting for Telegram pairing. Setup will continue, but Telegram integration is disabled.');
|
|
371
|
+
try {
|
|
372
|
+
bot.stop();
|
|
373
|
+
}
|
|
374
|
+
catch (e) { }
|
|
375
|
+
authorizedChatId = undefined;
|
|
376
|
+
telegramToken = '';
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
s.stop(`Bot successfully paired with Chat ID: ${authorizedChatId}`);
|
|
371
380
|
}
|
|
372
|
-
s.stop(`Bot successfully paired with Chat ID: ${authorizedChatId}`);
|
|
373
381
|
}
|
|
374
382
|
catch (err) {
|
|
375
383
|
s.stop(`Failed to start bot listener: ${err.message}. You can pair it later.`);
|
|
384
|
+
// Try to stop the bot if it was initialized before the error (L17)
|
|
385
|
+
try {
|
|
386
|
+
if (bot)
|
|
387
|
+
bot.stop();
|
|
388
|
+
}
|
|
389
|
+
catch (e) { }
|
|
376
390
|
}
|
|
377
391
|
}
|
|
378
392
|
}
|
|
@@ -424,6 +438,9 @@ Provider: ${config.llm.provider}`;
|
|
|
424
438
|
if (authorizedChatId) {
|
|
425
439
|
config.integrations.telegram.authorized_chat_id = authorizedChatId;
|
|
426
440
|
}
|
|
441
|
+
else if (config.integrations.telegram) {
|
|
442
|
+
delete config.integrations.telegram.authorized_chat_id;
|
|
443
|
+
}
|
|
427
444
|
(0, parser_1.saveConfig)(config);
|
|
428
445
|
// Sync disabled_skills.json based on user selection
|
|
429
446
|
const allWeb3Skills = [
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const formatter_1 = require("./formatter");
|
|
5
|
+
(0, vitest_1.describe)('Formatter Utilities', () => {
|
|
6
|
+
(0, vitest_1.it)('should format transaction success correctly for swap', () => {
|
|
7
|
+
const tx = {
|
|
8
|
+
id: 'test-1',
|
|
9
|
+
type: 'swap',
|
|
10
|
+
chainName: 'ethereum',
|
|
11
|
+
status: 'pending',
|
|
12
|
+
nonce: "0",
|
|
13
|
+
details: {
|
|
14
|
+
fromToken: 'eth',
|
|
15
|
+
toToken: 'usdc',
|
|
16
|
+
amountStr: '1'
|
|
17
|
+
},
|
|
18
|
+
createdAt: Date.now()
|
|
19
|
+
};
|
|
20
|
+
const rawResult = '{"txHash":"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"}';
|
|
21
|
+
const output = (0, formatter_1.formatTransactionSuccess)(tx, rawResult);
|
|
22
|
+
(0, vitest_1.expect)(output).toContain('Network:** Ethereum');
|
|
23
|
+
(0, vitest_1.expect)(output).toContain('Action:** Swapped 1 ETH to USDC');
|
|
24
|
+
(0, vitest_1.expect)(output).toContain('Tx Hash:** `0x1234...cdef`');
|
|
25
|
+
});
|
|
26
|
+
(0, vitest_1.it)('should format transaction error correctly', () => {
|
|
27
|
+
const tx = {
|
|
28
|
+
id: 'test-2',
|
|
29
|
+
type: 'bridge',
|
|
30
|
+
chainName: 'base_sepolia',
|
|
31
|
+
status: 'failed',
|
|
32
|
+
nonce: "0",
|
|
33
|
+
details: {},
|
|
34
|
+
createdAt: Date.now()
|
|
35
|
+
};
|
|
36
|
+
const output = (0, formatter_1.formatTransactionError)(tx, 'Insufficient funds');
|
|
37
|
+
(0, vitest_1.expect)(output).toContain('Transaction Failed (Base Sepolia)');
|
|
38
|
+
(0, vitest_1.expect)(output).toContain('Error: Insufficient funds');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -6,9 +6,54 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getDisabledSkills = getDisabledSkills;
|
|
7
7
|
exports.toggleSkill = toggleSkill;
|
|
8
8
|
exports.isSkillActive = isSkillActive;
|
|
9
|
+
exports.syncAllSkillsToConfig = syncAllSkillsToConfig;
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const paths_1 = require("../config/paths");
|
|
12
|
+
const parser_1 = require("../config/parser");
|
|
11
13
|
let disabledSkillsCache = null;
|
|
14
|
+
const reverseSkillMapping = {
|
|
15
|
+
// OS Skills
|
|
16
|
+
'read_local_file': { category: 'os', name: 'readFile' },
|
|
17
|
+
'write_local_file': { category: 'os', name: 'writeFile' },
|
|
18
|
+
'edit_local_file': { category: 'os', name: 'editFile' },
|
|
19
|
+
'generate_excel_file': { category: 'os', name: 'generateExcel' },
|
|
20
|
+
'analyze_document': { category: 'os', name: 'analyzeDocument' },
|
|
21
|
+
'run_terminal_command': { category: 'os', name: 'executeShell' },
|
|
22
|
+
'browse_website': { category: 'os', name: 'browseWeb' },
|
|
23
|
+
'search_web': { category: 'os', name: 'searchWeb' },
|
|
24
|
+
'read_gmail_inbox': { category: 'os', name: 'readGmail' },
|
|
25
|
+
'list_calendar_events': { category: 'os', name: 'listCalendar' },
|
|
26
|
+
'append_row_to_sheets': { category: 'os', name: 'appendSheets' },
|
|
27
|
+
'read_google_docs': { category: 'os', name: 'readDocs' },
|
|
28
|
+
'read_google_form_responses': { category: 'os', name: 'readForms' },
|
|
29
|
+
'execute_git_command': { category: 'os', name: 'gitManager' },
|
|
30
|
+
'manage_twitter': { category: 'os', name: 'xManager' },
|
|
31
|
+
'manage_notion': { category: 'os', name: 'notionWorkspace' },
|
|
32
|
+
'transcribe_audio': { category: 'os', name: 'audioTranscribe' },
|
|
33
|
+
'summarize_text': { category: 'os', name: 'summarizeText' },
|
|
34
|
+
'update_security_policy': { category: 'os', name: 'updateSecurityPolicy' },
|
|
35
|
+
// Web3 Skills
|
|
36
|
+
'transfer_token': { category: 'web3', name: 'transfer' },
|
|
37
|
+
'transfer_native': { category: 'web3', name: 'transfer' },
|
|
38
|
+
'swap_token': { category: 'web3', name: 'swap' },
|
|
39
|
+
'bridge_token': { category: 'web3', name: 'bridge' },
|
|
40
|
+
'mint_nft': { category: 'web3', name: 'mintNft' },
|
|
41
|
+
'custom_tx': { category: 'web3', name: 'customTx' },
|
|
42
|
+
'check_address': { category: 'web3', name: 'checkAddress' },
|
|
43
|
+
'get_my_address': { category: 'web3', name: 'getMyAddress' },
|
|
44
|
+
'check_token_security': { category: 'web3', name: 'checkSecurity' },
|
|
45
|
+
'check_portfolio': { category: 'web3', name: 'checkPortfolio' },
|
|
46
|
+
'analyze_market': { category: 'web3', name: 'marketAnalysis' },
|
|
47
|
+
'manage_custom_tokens': { category: 'web3', name: 'manageCustomTokens' },
|
|
48
|
+
'get_price': { category: 'web3', name: 'getPrice' },
|
|
49
|
+
'supply_aave': { category: 'web3', name: 'aaveSupply' },
|
|
50
|
+
'revoke_approval': { category: 'web3', name: 'revokeApproval' },
|
|
51
|
+
'deposit_yield_vault': { category: 'web3', name: 'vaultDeposit' },
|
|
52
|
+
'provide_liquidity_v3': { category: 'web3', name: 'provideLiquidity' },
|
|
53
|
+
'get_tx_history': { category: 'web3', name: 'getTxHistory' },
|
|
54
|
+
'check_registry_status': { category: 'web3', name: 'checkRegistryStatus' },
|
|
55
|
+
'create_limit_order': { category: 'web3', name: 'createLimitOrder' }
|
|
56
|
+
};
|
|
12
57
|
function getDisabledSkillsFile() {
|
|
13
58
|
return (0, paths_1.getPath)('disabled_skills.json');
|
|
14
59
|
}
|
|
@@ -43,8 +88,54 @@ function toggleSkill(skillName, active) {
|
|
|
43
88
|
}
|
|
44
89
|
disabledSkillsCache = Array.from(current);
|
|
45
90
|
fs_1.default.writeFileSync(getDisabledSkillsFile(), JSON.stringify(disabledSkillsCache, null, 2));
|
|
91
|
+
// Sync to config.yaml
|
|
92
|
+
const mapping = reverseSkillMapping[skillName];
|
|
93
|
+
if (mapping) {
|
|
94
|
+
const config = (0, parser_1.loadConfig)();
|
|
95
|
+
if (!config.skills)
|
|
96
|
+
config.skills = { web3: [], os: [] };
|
|
97
|
+
const categoryArray = mapping.category === 'web3' ? config.skills.web3 : config.skills.os;
|
|
98
|
+
if (active && !categoryArray.includes(mapping.name)) {
|
|
99
|
+
categoryArray.push(mapping.name);
|
|
100
|
+
}
|
|
101
|
+
else if (!active) {
|
|
102
|
+
const index = categoryArray.indexOf(mapping.name);
|
|
103
|
+
if (index !== -1) {
|
|
104
|
+
categoryArray.splice(index, 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
(0, parser_1.saveConfig)(config);
|
|
108
|
+
}
|
|
46
109
|
}
|
|
47
110
|
function isSkillActive(skillName) {
|
|
48
111
|
const disabled = getDisabledSkills();
|
|
49
112
|
return !disabled.includes(skillName);
|
|
50
113
|
}
|
|
114
|
+
function syncAllSkillsToConfig() {
|
|
115
|
+
const config = (0, parser_1.loadConfig)();
|
|
116
|
+
if (!config.skills)
|
|
117
|
+
config.skills = { web3: [], os: [] };
|
|
118
|
+
const activeWeb3 = new Set();
|
|
119
|
+
const activeOs = new Set();
|
|
120
|
+
for (const [skillName, mapping] of Object.entries(reverseSkillMapping)) {
|
|
121
|
+
if (isSkillActive(skillName)) {
|
|
122
|
+
if (mapping.category === 'web3') {
|
|
123
|
+
activeWeb3.add(mapping.name);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
activeOs.add(mapping.name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Check if arrays are different to avoid unnecessary saves
|
|
131
|
+
const currentWeb3 = config.skills.web3 || [];
|
|
132
|
+
const currentOs = config.skills.os || [];
|
|
133
|
+
const web3Changed = currentWeb3.length !== activeWeb3.size || !currentWeb3.every(s => activeWeb3.has(s));
|
|
134
|
+
const osChanged = currentOs.length !== activeOs.size || !currentOs.every(s => activeOs.has(s));
|
|
135
|
+
if (web3Changed || osChanged) {
|
|
136
|
+
config.skills.web3 = Array.from(activeWeb3);
|
|
137
|
+
config.skills.os = Array.from(activeOs);
|
|
138
|
+
(0, parser_1.saveConfig)(config);
|
|
139
|
+
console.log('[SkillManager] Automatically synchronized active skills to config.yaml');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -10,70 +10,75 @@ exports.getUserTokens = getUserTokens;
|
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const os_1 = __importDefault(require("os"));
|
|
13
|
-
const
|
|
13
|
+
const yaml_1 = __importDefault(require("yaml"));
|
|
14
|
+
const config_1 = require("../web3/config");
|
|
15
|
+
const tokens_1 = require("../web3/utils/tokens");
|
|
16
|
+
const WHITELIST_FILE_PATH = path_1.default.join(os_1.default.homedir(), '.nyxora', 'user_whitelist.yaml');
|
|
14
17
|
function getUserWhitelist() {
|
|
15
18
|
try {
|
|
16
19
|
if (!fs_1.default.existsSync(WHITELIST_FILE_PATH)) {
|
|
20
|
+
// Try migrating from JSON if YAML doesn't exist yet
|
|
21
|
+
const oldJsonPath = path_1.default.join(os_1.default.homedir(), '.nyxora', 'user_whitelist.json');
|
|
22
|
+
if (fs_1.default.existsSync(oldJsonPath)) {
|
|
23
|
+
const data = fs_1.default.readFileSync(oldJsonPath, 'utf-8');
|
|
24
|
+
const parsed = JSON.parse(data);
|
|
25
|
+
fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(parsed), 'utf-8');
|
|
26
|
+
// Rename old file so it doesn't get used again
|
|
27
|
+
fs_1.default.renameSync(oldJsonPath, `${oldJsonPath}.bak`);
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
17
30
|
return {};
|
|
18
31
|
}
|
|
19
32
|
const data = fs_1.default.readFileSync(WHITELIST_FILE_PATH, 'utf-8');
|
|
20
|
-
|
|
21
|
-
// Auto-migrate legacy format
|
|
22
|
-
let migrated = false;
|
|
23
|
-
for (const addr in parsed) {
|
|
24
|
-
if (parsed[addr] && !Array.isArray(parsed[addr])) {
|
|
25
|
-
const newArray = [];
|
|
26
|
-
for (const chain in parsed[addr]) {
|
|
27
|
-
const tokens = parsed[addr][chain];
|
|
28
|
-
if (Array.isArray(tokens)) {
|
|
29
|
-
for (const t of tokens) {
|
|
30
|
-
newArray.push({
|
|
31
|
-
chainName: chain,
|
|
32
|
-
address: typeof t === 'string' ? t.toLowerCase() : (t.address || '').toLowerCase(),
|
|
33
|
-
source: 'manual',
|
|
34
|
-
lastSeen: Date.now()
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
parsed[addr] = newArray;
|
|
40
|
-
migrated = true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (migrated) {
|
|
44
|
-
fs_1.default.writeFileSync(WHITELIST_FILE_PATH, JSON.stringify(parsed, null, 2), 'utf-8');
|
|
45
|
-
}
|
|
46
|
-
return parsed;
|
|
33
|
+
return yaml_1.default.parse(data) || {};
|
|
47
34
|
}
|
|
48
35
|
catch (err) {
|
|
49
|
-
console.error('[Whitelist] Error reading user_whitelist.
|
|
36
|
+
console.error('[Whitelist] Error reading user_whitelist.yaml', err);
|
|
50
37
|
return {};
|
|
51
38
|
}
|
|
52
39
|
}
|
|
53
|
-
function saveTokenToWhitelist(walletAddress, chainName, tokenAddress, source = 'manual') {
|
|
40
|
+
async function saveTokenToWhitelist(walletAddress, chainName, tokenAddress, source = 'manual', symbol, decimals) {
|
|
54
41
|
try {
|
|
55
42
|
const whitelist = getUserWhitelist();
|
|
56
43
|
const addr = walletAddress.toLowerCase();
|
|
57
44
|
const tokenAddr = tokenAddress.toLowerCase();
|
|
45
|
+
// Auto-fetch metadata if not provided
|
|
46
|
+
if (!symbol || decimals === undefined) {
|
|
47
|
+
try {
|
|
48
|
+
const client = (0, config_1.getPublicClient)(chainName);
|
|
49
|
+
const metadata = await (0, tokens_1.getTokenMetadata)(client, tokenAddr);
|
|
50
|
+
symbol = metadata.symbol;
|
|
51
|
+
decimals = metadata.decimals;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
console.warn(`[Whitelist] Could not fetch metadata for ${tokenAddr} on ${chainName}`, err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
58
57
|
if (!whitelist[addr])
|
|
59
58
|
whitelist[addr] = [];
|
|
60
59
|
const existingIndex = whitelist[addr].findIndex(t => t.chainName === chainName && t.address === tokenAddr);
|
|
61
60
|
if (existingIndex >= 0) {
|
|
62
61
|
whitelist[addr][existingIndex].lastSeen = Date.now();
|
|
62
|
+
if (symbol)
|
|
63
|
+
whitelist[addr][existingIndex].symbol = symbol;
|
|
64
|
+
if (decimals !== undefined)
|
|
65
|
+
whitelist[addr][existingIndex].decimals = decimals;
|
|
63
66
|
}
|
|
64
67
|
else {
|
|
65
68
|
whitelist[addr].push({
|
|
66
69
|
chainName,
|
|
67
70
|
address: tokenAddr,
|
|
71
|
+
symbol,
|
|
72
|
+
decimals,
|
|
68
73
|
source,
|
|
69
74
|
lastSeen: Date.now()
|
|
70
75
|
});
|
|
71
|
-
console.log(`[Whitelist] Added ${tokenAddr} to ${chainName} for ${addr} via ${source}`);
|
|
76
|
+
console.log(`[Whitelist] Added ${symbol || tokenAddr} to ${chainName} for ${addr} via ${source}`);
|
|
72
77
|
}
|
|
73
|
-
fs_1.default.writeFileSync(WHITELIST_FILE_PATH,
|
|
78
|
+
fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(whitelist), 'utf-8');
|
|
74
79
|
}
|
|
75
80
|
catch (err) {
|
|
76
|
-
console.error('[Whitelist] Error saving token to user_whitelist.
|
|
81
|
+
console.error('[Whitelist] Error saving token to user_whitelist.yaml', err);
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
function removeTokenFromWhitelist(walletAddress, chainName, tokenAddress) {
|
|
@@ -86,12 +91,12 @@ function removeTokenFromWhitelist(walletAddress, chainName, tokenAddress) {
|
|
|
86
91
|
const initialLength = whitelist[addr].length;
|
|
87
92
|
whitelist[addr] = whitelist[addr].filter(t => !(t.chainName === chainName && t.address === tokenAddr));
|
|
88
93
|
if (whitelist[addr].length !== initialLength) {
|
|
89
|
-
fs_1.default.writeFileSync(WHITELIST_FILE_PATH,
|
|
90
|
-
console.log(`[Whitelist] Removed
|
|
94
|
+
fs_1.default.writeFileSync(WHITELIST_FILE_PATH, yaml_1.default.stringify(whitelist), 'utf-8');
|
|
95
|
+
console.log(`[Whitelist] Removed token ${tokenAddr} on ${chainName} for ${addr}`);
|
|
91
96
|
}
|
|
92
97
|
}
|
|
93
98
|
catch (err) {
|
|
94
|
-
console.error('[Whitelist] Error removing token from user_whitelist.
|
|
99
|
+
console.error('[Whitelist] Error removing token from user_whitelist.yaml', err);
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
102
|
function getUserTokens(walletAddress, chainName) {
|