nyxora 1.6.2 → 1.6.4

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +22 -12
  3. package/SECURITY.md +25 -21
  4. package/assets/raw-diagram.png +0 -0
  5. package/assets/security-flow.png +0 -0
  6. package/bin/nyxora.mjs +236 -0
  7. package/launcher.js +8 -3
  8. package/launcher.ts +28 -1
  9. package/package.json +11 -8
  10. package/packages/core/package.json +4 -4
  11. package/packages/core/src/agent/reasoning.ts +10 -8
  12. package/packages/core/src/config/parser.ts +2 -1
  13. package/packages/core/src/gateway/cli.ts +2 -64
  14. package/packages/core/src/gateway/server.ts +89 -8
  15. package/packages/core/src/gateway/setup-cli.ts +7 -0
  16. package/packages/core/src/gateway/setup.ts +52 -28
  17. package/packages/core/src/gateway/telegram.ts +147 -89
  18. package/packages/core/src/memory/logger.ts +83 -20
  19. package/packages/core/src/system/pluginManager.ts +48 -34
  20. package/packages/core/src/utils/state.ts +15 -2
  21. package/packages/core/src/web3/config.ts +18 -3
  22. package/packages/core/src/web3/skills/marketAnalysis.ts +43 -17
  23. package/packages/core/src/web3/skills/swapToken.ts +9 -1
  24. package/packages/dashboard/dist/assets/index-CfIids2e.js +170 -0
  25. package/packages/dashboard/dist/assets/index-POJM-7Fd.css +1 -0
  26. package/packages/dashboard/dist/favicon.svg +1 -1
  27. package/packages/dashboard/dist/index.html +2 -2
  28. package/packages/dashboard/package.json +7 -7
  29. package/packages/dashboard/public/favicon.svg +1 -1
  30. package/packages/dashboard/src/App.tsx +224 -167
  31. package/packages/dashboard/src/Settings.tsx +55 -0
  32. package/packages/dashboard/src/Skills.tsx +8 -1
  33. package/packages/dashboard/src/index.css +146 -35
  34. package/packages/policy/package.json +1 -1
  35. package/packages/policy/src/server.ts +21 -28
  36. package/packages/signer/package.json +1 -1
  37. package/packages/signer/src/server.ts +40 -13
  38. package/test-db.ts +3 -0
  39. package/bin/nyxora.js +0 -13
  40. package/packages/dashboard/dist/assets/index-BK4qmIy6.js +0 -200
  41. package/packages/dashboard/dist/assets/index-C1m4ohce.css +0 -1
  42. package/packages/dashboard/package-lock.json +0 -2748
  43. package/packages/dashboard/src/Memory.tsx +0 -110
@@ -3,44 +3,70 @@ import { resolveToken } from '../utils/tokens';
3
3
 
4
4
  export async function analyzeMarket(chainName: ChainName, tokenAddressOrSymbol: string): Promise<string> {
5
5
  try {
6
- let tokenAddress = tokenAddressOrSymbol;
6
+ const cleanSymbol = tokenAddressOrSymbol.replace('$', '').toLowerCase();
7
+
8
+ // 1. Primary Engine: CoinGecko Global
7
9
  try {
8
- tokenAddress = resolveToken(tokenAddressOrSymbol, chainName);
9
- if (tokenAddress === "0x0000000000000000000000000000000000000000") {
10
- // For native token, we should pass WETH / wrapped version to Dexscreener usually,
11
- // because native token itself doesn't have a pair on most DEXes directly.
12
- tokenAddress = resolveToken("W" + tokenAddressOrSymbol, chainName); // e.g. WETH, WBNB
10
+ const cgSearchRes = await fetch(`https://api.coingecko.com/api/v3/search?query=${cleanSymbol}`);
11
+ if (cgSearchRes.ok) {
12
+ const searchData = await cgSearchRes.json();
13
+ const foundCoin = searchData.coins?.find((c: any) => c.symbol.toLowerCase() === cleanSymbol || c.id === cleanSymbol);
14
+
15
+ if (foundCoin) {
16
+ const cgCoinRes = await fetch(`https://api.coingecko.com/api/v3/coins/${foundCoin.id}`);
17
+ if (cgCoinRes.ok) {
18
+ const coinData = await cgCoinRes.json();
19
+ let report = `📈 **Market Analysis for ${coinData.name} (${coinData.symbol.toUpperCase()})** [Global via CoinGecko]\n\n`;
20
+ report += `**Price:** $${coinData.market_data?.current_price?.usd || 0}\n`;
21
+ report += `**Market Cap:** $${Number(coinData.market_data?.market_cap?.usd || 0).toLocaleString()}\n`;
22
+ report += `**24h Volume:** $${Number(coinData.market_data?.total_volume?.usd || 0).toLocaleString()}\n\n`;
23
+ report += `**Price Change:**\n`;
24
+ report += `- 1h: ${coinData.market_data?.price_change_percentage_1h_in_currency?.usd?.toFixed(2) || 0}% \n`;
25
+ report += `- 24h: ${coinData.market_data?.price_change_percentage_24h?.toFixed(2) || 0}% \n`;
26
+ report += `- 7d: ${coinData.market_data?.price_change_percentage_7d?.toFixed(2) || 0}% \n\n`;
27
+ report += `**Rank:** #${coinData.market_cap_rank || 'N/A'}\n`;
28
+ return report;
29
+ }
30
+ }
13
31
  }
14
32
  } catch (e) {
15
- // If it fails to resolve, assume it's already an address or let DexScreener handle it (though DexScreener needs exact address)
33
+ console.warn("CoinGecko analysis failed, falling back to DexScreener...", e);
16
34
  }
17
35
 
18
- const url = `https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`;
19
- const res = await fetch(url);
36
+ // 2. Fallback Engine: DexScreener Cross-Chain Search
37
+ let query = tokenAddressOrSymbol;
38
+ try {
39
+ const resolved = resolveToken(tokenAddressOrSymbol, chainName);
40
+ if (resolved !== "0x0000000000000000000000000000000000000000") {
41
+ query = resolved; // Use exact address if resolved locally
42
+ }
43
+ } catch (e) {}
44
+
45
+ const dexSearchUrl = `https://api.dexscreener.com/latest/dex/search?q=${query}`;
46
+ const res = await fetch(dexSearchUrl);
20
47
  if (!res.ok) {
21
48
  throw new Error(`DexScreener API Error: ${res.statusText}`);
22
49
  }
23
50
 
24
51
  const data = await res.json();
25
52
  if (!data.pairs || data.pairs.length === 0) {
26
- return `No market data found for token ${tokenAddressOrSymbol} on DexScreener.`;
53
+ return `No market data found for '${tokenAddressOrSymbol}' on CoinGecko or DexScreener across any chain.`;
27
54
  }
28
55
 
29
- // Filter pairs by chain if possible, Dexscreener chain IDs are strings like 'ethereum', 'bsc', 'base', 'arbitrum', 'optimism'
30
56
  let pair = data.pairs.find((p: any) => p.chainId === chainName);
31
57
  if (!pair) {
32
- pair = data.pairs[0]; // Fallback to the most liquid pair anywhere
58
+ pair = data.pairs[0];
33
59
  }
34
60
 
35
- let report = `📈 **Market Analysis for ${pair.baseToken.name} (${pair.baseToken.symbol})** on ${pair.chainId.toUpperCase()}\n\n`;
61
+ let report = `📈 **Market Analysis for ${pair.baseToken.name} (${pair.baseToken.symbol})** on ${pair.chainId.toUpperCase()} [Cross-Chain Fallback: DexScreener]\n\n`;
36
62
  report += `**Price:** $${pair.priceUsd}\n`;
37
63
  report += `**Liquidity (USD):** $${Number(pair.liquidity?.usd || 0).toLocaleString()}\n`;
38
64
  report += `**FDV:** $${Number(pair.fdv || 0).toLocaleString()}\n`;
39
65
  report += `**24h Volume:** $${Number(pair.volume?.h24 || 0).toLocaleString()}\n\n`;
40
66
  report += `**Price Change:**\n`;
41
- report += `- 5m: ${pair.priceChange?.m5}% \n`;
42
- report += `- 1h: ${pair.priceChange?.h1}% \n`;
43
- report += `- 24h: ${pair.priceChange?.h24}% \n\n`;
67
+ report += `- 5m: ${pair.priceChange?.m5 || 0}% \n`;
68
+ report += `- 1h: ${pair.priceChange?.h1 || 0}% \n`;
69
+ report += `- 24h: ${pair.priceChange?.h24 || 0}% \n\n`;
44
70
  report += `**DEX:** ${pair.dexId} (${pair.url})\n`;
45
71
 
46
72
  return report;
@@ -53,7 +79,7 @@ export const marketAnalysisToolDefinition = {
53
79
  type: "function",
54
80
  function: {
55
81
  name: "analyze_market",
56
- description: "Fetches live market data (Price, Liquidity, Volume, FDV, Price Change) for a token using DexScreener.",
82
+ description: "Fetches live market data (Price, Liquidity, Volume, FDV, Price Change) globally across all chains using CoinGecko and DexScreener.",
57
83
  parameters: {
58
84
  type: "object",
59
85
  properties: {
@@ -2,6 +2,7 @@ import { parseUnits, formatUnits } from 'viem';
2
2
  import { getPublicClient, getAddress, ChainName } from '../config';
3
3
  import { txManager } from '../../agent/transactionManager';
4
4
  import { resolveToken, ERC20_ABI } from '../utils/tokens';
5
+ import crypto from 'crypto';
5
6
 
6
7
  const CHAIN_IDS: Record<ChainName, number> = {
7
8
  ethereum: 1,
@@ -169,7 +170,7 @@ export async function executeSwap(chainName: ChainName, params: any, autoApprove
169
170
  const { txRequest, needsApprove, fromTokenAddress, approvalAddress, amountWei } = params;
170
171
  const token = process.env.INTERNAL_AUTH_TOKEN;
171
172
 
172
- const payload = {
173
+ const payload: any = {
173
174
  type: 'swap',
174
175
  chainName,
175
176
  autoApprove,
@@ -182,6 +183,13 @@ export async function executeSwap(chainName: ChainName, params: any, autoApprove
182
183
  }
183
184
  };
184
185
 
186
+ if (autoApprove && token) {
187
+ // Generate internal HMAC signature for autoApprove bypass
188
+ // using the transaction chainName as a quick deterministic unique string
189
+ // In a real scenario, use a specific txId or nonce.
190
+ payload.internalSignature = crypto.createHmac('sha256', token).update(chainName + amountWei).digest('hex');
191
+ }
192
+
185
193
  const res = await fetch('http://127.0.0.1:3001/request-tx', {
186
194
  method: 'POST',
187
195
  headers: {