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
|
@@ -50,8 +50,8 @@ async function fetchMainnetBestRoute(fromChain, toChain, fromToken, toToken, amo
|
|
|
50
50
|
if (isProviderHealthy('openocean')) {
|
|
51
51
|
promises.push(fetchOpenOcean(fromChain, toChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance, keys.openocean_key));
|
|
52
52
|
}
|
|
53
|
-
// Provider 6: KyberSwap (
|
|
54
|
-
if (isProviderHealthy('kyberswap')) {
|
|
53
|
+
// Provider 6: KyberSwap (Same-chain ONLY)
|
|
54
|
+
if (!isCrossChain && isProviderHealthy('kyberswap')) {
|
|
55
55
|
promises.push(fetchKyberSwap(fromChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance));
|
|
56
56
|
}
|
|
57
57
|
// Execute all healthy providers concurrently with a 4s timeout
|
|
@@ -4,6 +4,9 @@ exports.routeTransaction = routeTransaction;
|
|
|
4
4
|
const aggregatorMainnet_1 = require("./aggregatorMainnet");
|
|
5
5
|
const aggregatorTestnet_1 = require("./aggregatorTestnet");
|
|
6
6
|
async function routeTransaction(fromChain, toChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance = "auto") {
|
|
7
|
+
if (!fromChain || !toChain) {
|
|
8
|
+
throw new Error("Missing source or destination chain in routing.");
|
|
9
|
+
}
|
|
7
10
|
// Auto-correct: If one is testnet and the other is mainnet, assume they meant testnet
|
|
8
11
|
if (fromChain.includes('sepolia') && !toChain.includes('sepolia')) {
|
|
9
12
|
if (toChain === 'base')
|
|
@@ -11,6 +11,10 @@ const tokens_1 = require("../utils/tokens");
|
|
|
11
11
|
const defiRouter_1 = require("../aggregator/defiRouter");
|
|
12
12
|
async function prepareBridgeToken(fromChain, toChain, tokenSymbol, amountStr, mode = "auto", providerName = "auto", slippagePercent) {
|
|
13
13
|
try {
|
|
14
|
+
if (!fromChain || !toChain)
|
|
15
|
+
throw new Error("Source or destination chain not provided by AI.");
|
|
16
|
+
if (!amountStr)
|
|
17
|
+
throw new Error("Bridge amount not provided by AI.");
|
|
14
18
|
const userAddress = await (0, vaultClient_1.getAddress)();
|
|
15
19
|
// Auto-correct: If one is testnet and the other is mainnet, assume they meant testnet
|
|
16
20
|
if (fromChain.includes('sepolia') && !toChain.includes('sepolia')) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkRegistryStatusToolDefinition = void 0;
|
|
3
4
|
exports.checkRegistryStatus = checkRegistryStatus;
|
|
4
5
|
const config_1 = require("../config");
|
|
5
6
|
const config_2 = require("../config");
|
|
@@ -77,3 +78,15 @@ async function checkRegistryStatus() {
|
|
|
77
78
|
return { isActive: true, reason: 'Registry check skipped due to network error.' };
|
|
78
79
|
}
|
|
79
80
|
}
|
|
81
|
+
exports.checkRegistryStatusToolDefinition = {
|
|
82
|
+
type: "function",
|
|
83
|
+
function: {
|
|
84
|
+
name: "check_registry_status",
|
|
85
|
+
description: "Internal Security Middleware: Checks the On-Chain Kill Switch Registry before executing any transaction. If the agent is deactivated, the transaction will be strictly blocked for safety.",
|
|
86
|
+
parameters: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {},
|
|
89
|
+
required: []
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
@@ -8,6 +8,8 @@ const transactionManager_1 = require("../../agent/transactionManager");
|
|
|
8
8
|
const vaultClient_1 = require("../utils/vaultClient");
|
|
9
9
|
async function prepareCustomTx(chainName, toAddress, data, valueWei = "0", description = "Custom transaction") {
|
|
10
10
|
try {
|
|
11
|
+
if (!chainName || !toAddress || !data)
|
|
12
|
+
throw new Error("Missing required parameters for custom transaction.");
|
|
11
13
|
const tx = transactionManager_1.txManager.createPendingTransaction('custom', chainName, {
|
|
12
14
|
toAddress,
|
|
13
15
|
data,
|
|
@@ -32,6 +32,8 @@ const AAVE_V3_POOLS = {
|
|
|
32
32
|
};
|
|
33
33
|
async function prepareAaveSupply(chainName, tokenAddressOrSymbol, amountStr) {
|
|
34
34
|
try {
|
|
35
|
+
if (!chainName || !tokenAddressOrSymbol || !amountStr)
|
|
36
|
+
throw new Error("Missing protocol/chain/token parameters for DeFi operation.");
|
|
35
37
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
36
38
|
const userAddress = await (0, config_1.getAddress)();
|
|
37
39
|
const account = userAddress;
|
|
@@ -14,27 +14,32 @@ exports.getPriceToolDefinition = {
|
|
|
14
14
|
coinId: {
|
|
15
15
|
type: "string",
|
|
16
16
|
description: "The CoinGecko ID of the coin (e.g., 'ethereum', 'bitcoin', 'solana')."
|
|
17
|
+
},
|
|
18
|
+
currency: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The fiat currency to convert to (e.g., 'usd', 'idr', 'eur', 'jpy'). Defaults to 'usd'."
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
required: ["coinId"]
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
};
|
|
23
|
-
async function getPrice(coinId) {
|
|
27
|
+
async function getPrice(coinId, currency = 'usd') {
|
|
24
28
|
try {
|
|
25
|
-
const
|
|
29
|
+
const cur = currency.toLowerCase();
|
|
30
|
+
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=${cur}&include_24hr_change=true`;
|
|
26
31
|
const data = await (0, httpClient_1.safeFetchJson)(url);
|
|
27
32
|
if (!data[coinId]) {
|
|
28
33
|
return `❌ Could not find price data for **${coinId.toUpperCase()}**. Please verify the coin name.`;
|
|
29
34
|
}
|
|
30
|
-
const price = data[coinId]
|
|
31
|
-
const change24h = data[coinId]
|
|
35
|
+
const price = data[coinId][cur];
|
|
36
|
+
const change24h = data[coinId][`${cur}_24h_change`];
|
|
32
37
|
const isPositive = change24h >= 0;
|
|
33
38
|
const arrow = isPositive ? '📈 🟩 +' : '📉 🟥 ';
|
|
34
|
-
const priceFormatted = new Intl.NumberFormat('en-US', { style: 'currency', currency:
|
|
39
|
+
const priceFormatted = new Intl.NumberFormat(cur === 'idr' ? 'id-ID' : 'en-US', { style: 'currency', currency: cur.toUpperCase() }).format(price);
|
|
35
40
|
const changeFormatted = change24h ? change24h.toFixed(2) : '0.00';
|
|
36
41
|
return `💰 **Price Update for ${coinId.toUpperCase()}**\n\n` +
|
|
37
|
-
`- **Price (
|
|
42
|
+
`- **Price (${cur.toUpperCase()})**: \`${priceFormatted}\`\n` +
|
|
38
43
|
`- **24h Change**: ${arrow}${changeFormatted}%`;
|
|
39
44
|
}
|
|
40
45
|
catch (error) {
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.manageCustomTokensDefinition = void 0;
|
|
7
4
|
exports.executeManageCustomTokens = executeManageCustomTokens;
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const paths_1 = require("../../config/paths");
|
|
10
5
|
const config_1 = require("../config");
|
|
6
|
+
const userWhitelistManager_1 = require("../../utils/userWhitelistManager");
|
|
7
|
+
const vaultClient_1 = require("../utils/vaultClient");
|
|
11
8
|
exports.manageCustomTokensDefinition = {
|
|
12
9
|
type: 'function',
|
|
13
10
|
function: {
|
|
14
11
|
name: 'manage_custom_tokens',
|
|
15
|
-
description: 'Add or remove a custom ERC-20 token (like a memecoin or specific token) from the user\'s local portfolio watcher.',
|
|
12
|
+
description: 'Add or remove a custom ERC-20 token (like a memecoin or specific token) from the user\'s local portfolio watcher and swap whitelist.',
|
|
16
13
|
parameters: {
|
|
17
14
|
type: 'object',
|
|
18
15
|
properties: {
|
|
@@ -32,50 +29,39 @@ exports.manageCustomTokensDefinition = {
|
|
|
32
29
|
},
|
|
33
30
|
contract_address: {
|
|
34
31
|
type: 'string',
|
|
35
|
-
description: 'The smart contract address of the token.
|
|
32
|
+
description: 'The smart contract address of the token. Required for "add" action or "remove" action.'
|
|
36
33
|
}
|
|
37
34
|
},
|
|
38
|
-
required: ['action', 'chain_name', 'symbol']
|
|
35
|
+
required: ['action', 'chain_name', 'symbol', 'contract_address']
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
};
|
|
42
39
|
async function executeManageCustomTokens(args) {
|
|
43
40
|
const { action, chain_name, symbol, contract_address } = args;
|
|
44
|
-
if (!
|
|
45
|
-
|
|
41
|
+
if (!chain_name) {
|
|
42
|
+
throw new Error("Chain name is required.");
|
|
46
43
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (fs_1.default.existsSync(customTokensPath)) {
|
|
50
|
-
try {
|
|
51
|
-
const data = fs_1.default.readFileSync(customTokensPath, 'utf8');
|
|
52
|
-
customTokens = JSON.parse(data);
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
console.error('Error parsing custom_tokens.json', e);
|
|
56
|
-
}
|
|
44
|
+
if (!symbol) {
|
|
45
|
+
throw new Error("Token symbol is required.");
|
|
57
46
|
}
|
|
58
|
-
if (!
|
|
59
|
-
|
|
47
|
+
if (!config_1.SUPPORTED_CHAIN_NAMES.includes(chain_name)) {
|
|
48
|
+
return `Error: Unsupported chain ${chain_name}.`;
|
|
60
49
|
}
|
|
61
50
|
const upperSymbol = symbol.toUpperCase();
|
|
51
|
+
const userAddress = await (0, vaultClient_1.getAddress)();
|
|
62
52
|
if (action === 'add') {
|
|
63
53
|
if (!contract_address || !contract_address.startsWith('0x')) {
|
|
64
54
|
return `Error: Invalid or missing contract_address.`;
|
|
65
55
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return `Successfully added custom token ${upperSymbol} to the ${chain_name} portfolio tracker.`;
|
|
56
|
+
await (0, userWhitelistManager_1.saveTokenToWhitelist)(userAddress, chain_name, contract_address, 'manual', upperSymbol);
|
|
57
|
+
return `Successfully added custom token ${upperSymbol} to the ${chain_name} portfolio tracker and swap whitelist.`;
|
|
69
58
|
}
|
|
70
59
|
else if (action === 'remove') {
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
fs_1.default.writeFileSync(customTokensPath, JSON.stringify(customTokens, null, 2));
|
|
74
|
-
return `Successfully removed custom token ${upperSymbol} from the ${chain_name} portfolio tracker.`;
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
return `Warning: Token ${upperSymbol} was not found in the custom portfolio tracker for ${chain_name}.`;
|
|
60
|
+
if (!contract_address || !contract_address.startsWith('0x')) {
|
|
61
|
+
return `Error: Invalid or missing contract_address for removal.`;
|
|
78
62
|
}
|
|
63
|
+
(0, userWhitelistManager_1.removeTokenFromWhitelist)(userAddress, chain_name, contract_address);
|
|
64
|
+
return `Successfully removed custom token ${upperSymbol} from the ${chain_name} whitelist.`;
|
|
79
65
|
}
|
|
80
66
|
return `Error: Invalid action.`;
|
|
81
67
|
}
|
|
@@ -56,7 +56,7 @@ async function fetchDexData(query, isCa, chainName) {
|
|
|
56
56
|
if (data && data.pairs && data.pairs.length > 0) {
|
|
57
57
|
let pairs = data.pairs;
|
|
58
58
|
if (chainName) {
|
|
59
|
-
pairs = pairs.filter((p) => p.chainId
|
|
59
|
+
pairs = pairs.filter((p) => p.chainId?.toLowerCase() === chainName?.toLowerCase());
|
|
60
60
|
if (pairs.length === 0)
|
|
61
61
|
pairs = data.pairs;
|
|
62
62
|
}
|
|
@@ -95,6 +95,8 @@ async function fetchCexMomentum(symbol, currentP) {
|
|
|
95
95
|
}
|
|
96
96
|
async function analyzeMarket(chainName, tokenAddressOrSymbol) {
|
|
97
97
|
try {
|
|
98
|
+
if (!tokenAddressOrSymbol)
|
|
99
|
+
throw new Error("Token symbol is invalid.");
|
|
98
100
|
const cleanInput = tokenAddressOrSymbol.replace('$', '').toLowerCase();
|
|
99
101
|
const isAddress = cleanInput.startsWith('0x') && cleanInput.length === 42;
|
|
100
102
|
let officialSymbol = cleanInput.toUpperCase();
|
|
@@ -8,6 +8,8 @@ const config_1 = require("../config");
|
|
|
8
8
|
const transactionManager_1 = require("../../agent/transactionManager");
|
|
9
9
|
async function prepareMintNft(chainName, contractAddress, functionSignature, argsStr, valueEth = "0") {
|
|
10
10
|
try {
|
|
11
|
+
if (!chainName || !contractAddress || !functionSignature)
|
|
12
|
+
throw new Error("Missing required parameters to mint NFT.");
|
|
11
13
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
12
14
|
const userAddress = await (0, config_1.getAddress)();
|
|
13
15
|
const account = userAddress;
|
|
@@ -52,6 +52,8 @@ const POSITION_MANAGERS = {
|
|
|
52
52
|
const parser_1 = require("../../config/parser");
|
|
53
53
|
async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1AddressOrSymbol, amount0Str, amount1Str, feeTier, tickLower, tickUpper, slippagePercent) {
|
|
54
54
|
try {
|
|
55
|
+
if (!chainName || !token0AddressOrSymbol || !token1AddressOrSymbol || !amount0Str || !amount1Str)
|
|
56
|
+
throw new Error("Missing protocol/chain/token parameters for DeFi operation.");
|
|
55
57
|
let actualSlippage = slippagePercent;
|
|
56
58
|
if (actualSlippage === undefined || actualSlippage === null || actualSlippage === "auto") {
|
|
57
59
|
try {
|
|
@@ -8,6 +8,8 @@ const transactionManager_1 = require("../../agent/transactionManager");
|
|
|
8
8
|
const tokens_1 = require("../utils/tokens");
|
|
9
9
|
async function prepareRevokeApproval(chainName, tokenAddressOrSymbol, spenderAddress) {
|
|
10
10
|
try {
|
|
11
|
+
if (!chainName || !tokenAddressOrSymbol || !spenderAddress)
|
|
12
|
+
throw new Error("Missing required parameters for revoking approval.");
|
|
11
13
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
12
14
|
const userAddress = await (0, config_1.getAddress)();
|
|
13
15
|
const account = userAddress;
|
|
@@ -12,15 +12,17 @@ const userWhitelistManager_1 = require("../../utils/userWhitelistManager");
|
|
|
12
12
|
const defiRouter_1 = require("../aggregator/defiRouter");
|
|
13
13
|
async function prepareSwapToken(chainName, fromToken, toToken, amountStr, mode = "auto", providerName = "auto", slippagePercent) {
|
|
14
14
|
try {
|
|
15
|
+
if (!chainName || !fromToken || !toToken || !amountStr)
|
|
16
|
+
throw new Error("Missing required parameters for swap (chain, tokens, or amount).");
|
|
15
17
|
const userAddress = await (0, vaultClient_1.getAddress)();
|
|
16
18
|
const fromTokenAddress = (0, tokens_1.resolveToken)(fromToken, chainName);
|
|
17
19
|
const toTokenAddress = (0, tokens_1.resolveToken)(toToken, chainName);
|
|
18
20
|
const isNativeIn = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
19
21
|
// Auto-save to Degen Whitelist
|
|
20
22
|
if (!isNativeIn)
|
|
21
|
-
(0, userWhitelistManager_1.saveTokenToWhitelist)(userAddress, chainName, fromTokenAddress, 'swap');
|
|
23
|
+
await (0, userWhitelistManager_1.saveTokenToWhitelist)(userAddress, chainName, fromTokenAddress, 'swap');
|
|
22
24
|
if (toTokenAddress !== "0x0000000000000000000000000000000000000000") {
|
|
23
|
-
(0, userWhitelistManager_1.saveTokenToWhitelist)(userAddress, chainName, toTokenAddress, 'swap');
|
|
25
|
+
await (0, userWhitelistManager_1.saveTokenToWhitelist)(userAddress, chainName, toTokenAddress, 'swap');
|
|
24
26
|
}
|
|
25
27
|
// Default to 18 decimals for formatting input, though Aggregator handles this if we pass raw
|
|
26
28
|
const amountWei = (0, viem_1.parseUnits)(amountStr, 18).toString();
|
|
@@ -10,6 +10,8 @@ const tokens_1 = require("../utils/tokens");
|
|
|
10
10
|
const vaultClient_1 = require("../utils/vaultClient");
|
|
11
11
|
async function prepareTransfer(chainName, toAddress, amountStr, token) {
|
|
12
12
|
try {
|
|
13
|
+
if (!chainName || !toAddress || !amountStr)
|
|
14
|
+
throw new Error("Missing required parameters for transfer (chain, recipient, or amount).");
|
|
13
15
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
14
16
|
const userAddress = await (0, config_1.getAddress)();
|
|
15
17
|
const account = userAddress;
|
|
@@ -25,6 +25,8 @@ const VAULT_ABI = [
|
|
|
25
25
|
];
|
|
26
26
|
async function prepareVaultDeposit(chainName, protocol, vaultAddress, tokenAddressOrSymbol, amountStr) {
|
|
27
27
|
try {
|
|
28
|
+
if (!chainName || !vaultAddress || !tokenAddressOrSymbol || !amountStr)
|
|
29
|
+
throw new Error("Missing protocol/chain/token parameters for DeFi operation.");
|
|
28
30
|
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
29
31
|
const userAddress = await (0, config_1.getAddress)();
|
|
30
32
|
const account = userAddress;
|
|
@@ -5,6 +5,7 @@ exports.resolveToken = resolveToken;
|
|
|
5
5
|
exports.getTokenMetadata = getTokenMetadata;
|
|
6
6
|
exports.invalidateTokenCache = invalidateTokenCache;
|
|
7
7
|
exports.preloadTokenMetadataAndAllowance = preloadTokenMetadataAndAllowance;
|
|
8
|
+
const userWhitelistManager_1 = require("../../utils/userWhitelistManager");
|
|
8
9
|
exports.ERC20_ABI = [
|
|
9
10
|
{
|
|
10
11
|
type: 'function',
|
|
@@ -76,7 +77,7 @@ exports.TOKEN_MAP = {
|
|
|
76
77
|
ETH: "0x0000000000000000000000000000000000000000",
|
|
77
78
|
WETH: "0x4200000000000000000000000000000000000006",
|
|
78
79
|
USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
79
|
-
USDT: "
|
|
80
|
+
USDT: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2"
|
|
80
81
|
},
|
|
81
82
|
optimism: {
|
|
82
83
|
ETH: "0x0000000000000000000000000000000000000000",
|
|
@@ -126,6 +127,13 @@ function resolveToken(tokenSymbolOrAddress, chainName) {
|
|
|
126
127
|
if (chainTokens && chainTokens[symbolUpper]) {
|
|
127
128
|
return chainTokens[symbolUpper];
|
|
128
129
|
}
|
|
130
|
+
const whitelistData = (0, userWhitelistManager_1.getUserWhitelist)();
|
|
131
|
+
for (const addr in whitelistData) {
|
|
132
|
+
const tokens = whitelistData[addr];
|
|
133
|
+
const match = tokens.find(t => t.chainName === chainName && t.symbol === symbolUpper);
|
|
134
|
+
if (match)
|
|
135
|
+
return match.address;
|
|
136
|
+
}
|
|
129
137
|
throw new Error(`Token "${tokenSymbolOrAddress}" pada chain ${chainName} tidak ditemukan. Silakan gunakan alamat kontrak langsung (0x...).`);
|
|
130
138
|
}
|
|
131
139
|
// Bounded LRU Cache to prevent RAM bloat from fake tokens (OOM protection)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nyxora",
|
|
3
|
-
"version": "26.6.
|
|
3
|
+
"version": "26.6.21",
|
|
4
4
|
"description": "Your Personal Web3 Assistant",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web3",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"stop": "node ./bin/nyxora.mjs stop",
|
|
43
43
|
"restart": "node ./bin/nyxora.mjs restart",
|
|
44
44
|
"dashboard": "node ./bin/nyxora.mjs dashboard",
|
|
45
|
+
"chat": "node ./bin/nyxora.mjs chat",
|
|
45
46
|
"setup": "node ./bin/nyxora.mjs setup",
|
|
46
47
|
"set-key": "node ./bin/nyxora.mjs set-key",
|
|
47
48
|
"doctor": "node ./bin/nyxora.mjs doctor",
|
|
@@ -370,6 +370,17 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
370
370
|
continue;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
if (!isSkillActive(toolName)) {
|
|
374
|
+
console.warn(pc.red(`[Security] Blocked illegal execution of disabled skill: ${toolName}`));
|
|
375
|
+
result = `[System Error] Access denied: Skill '${toolName}' is currently disabled by the user.`;
|
|
376
|
+
logger.addEntry({
|
|
377
|
+
role: "tool",
|
|
378
|
+
tool_call_id: toolCall.id,
|
|
379
|
+
content: result
|
|
380
|
+
}, sessionId);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
373
384
|
try {
|
|
374
385
|
switch (toolName) {
|
|
375
386
|
case 'get_balance': {
|
|
@@ -382,7 +393,7 @@ export async function processUserInput(input: string, role: 'user' | 'system' =
|
|
|
382
393
|
break;
|
|
383
394
|
}
|
|
384
395
|
case 'get_price': {
|
|
385
|
-
result = await getPrice(args.coinId);
|
|
396
|
+
result = await getPrice(args.coinId, args.currency);
|
|
386
397
|
break;
|
|
387
398
|
}
|
|
388
399
|
case 'swap_token': {
|
|
@@ -1,7 +1,70 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import yaml from 'yaml';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import crypto from 'crypto';
|
|
4
6
|
import { getPath } from './paths';
|
|
7
|
+
let cachedEncryptionKey: Buffer | null = null;
|
|
8
|
+
function getEncryptionKeySync(): Buffer {
|
|
9
|
+
if (cachedEncryptionKey) return cachedEncryptionKey;
|
|
10
|
+
let masterKeyRaw = process.env.NYXORA_MASTER_KEY;
|
|
11
|
+
if (!masterKeyRaw) {
|
|
12
|
+
try {
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const output = execSync(`node -e "require('@napi-rs/keyring').Entry.prototype.getPassword.call(new (require('@napi-rs/keyring').Entry)('nyxora', 'config_master')).then(console.log).catch(()=>console.log(''))"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
15
|
+
const pk = output?.trim();
|
|
16
|
+
if (pk) masterKeyRaw = pk;
|
|
17
|
+
} catch (e) {
|
|
18
|
+
// Ignore
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (!masterKeyRaw) {
|
|
22
|
+
try {
|
|
23
|
+
const masterKeyPath = path.join(os.homedir(), '.nyxora', 'auth', 'master.key');
|
|
24
|
+
if (fs.existsSync(masterKeyPath)) {
|
|
25
|
+
masterKeyRaw = fs.readFileSync(masterKeyPath, 'utf8').trim();
|
|
26
|
+
} else {
|
|
27
|
+
masterKeyRaw = crypto.randomBytes(32).toString('hex');
|
|
28
|
+
try { fs.writeFileSync(masterKeyPath, masterKeyRaw, { mode: 0o600 }); } catch (e) {}
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {
|
|
31
|
+
masterKeyRaw = 'default_fallback_nyxora_key';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
cachedEncryptionKey = crypto.createHash('sha256').update(masterKeyRaw).digest();
|
|
35
|
+
return cachedEncryptionKey;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function encryptDataSync(text: string): string {
|
|
39
|
+
if (!text || text.startsWith('ENC:')) return text;
|
|
40
|
+
const iv = crypto.randomBytes(12);
|
|
41
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', getEncryptionKeySync(), iv);
|
|
42
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
43
|
+
encrypted += cipher.final('hex');
|
|
44
|
+
const authTag = cipher.getAuthTag().toString('hex');
|
|
45
|
+
return `ENC:${iv.toString('hex')}:${authTag}:${encrypted}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function decryptDataSync(encryptedText: string): string {
|
|
49
|
+
if (!encryptedText) return encryptedText;
|
|
50
|
+
if (!encryptedText.startsWith('ENC:')) {
|
|
51
|
+
return encryptedText;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const parts = encryptedText.split(':');
|
|
55
|
+
if (parts.length < 4) return encryptedText;
|
|
56
|
+
const iv = Buffer.from(parts[1], 'hex');
|
|
57
|
+
const authTag = Buffer.from(parts[2], 'hex');
|
|
58
|
+
const encrypted = parts.slice(3).join(':');
|
|
59
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', getEncryptionKeySync(), iv);
|
|
60
|
+
decipher.setAuthTag(authTag);
|
|
61
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
62
|
+
decrypted += decipher.final('utf8');
|
|
63
|
+
return decrypted;
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return encryptedText; // return raw if decryption fails
|
|
66
|
+
}
|
|
67
|
+
}
|
|
5
68
|
|
|
6
69
|
export function loadRpcConfig(): Record<string, string | string[]> {
|
|
7
70
|
const rpcPath = getPath('rpc_key.yaml');
|
|
@@ -26,7 +89,7 @@ export function saveRpcConfig(rpcUrls: Record<string, string | string[]>): void
|
|
|
26
89
|
|
|
27
90
|
export async function loadApiKeys(): Promise<Record<string, string>> {
|
|
28
91
|
const config = loadConfig();
|
|
29
|
-
return config.credentials || {};
|
|
92
|
+
return (config.credentials as Record<string, string>) || {};
|
|
30
93
|
}
|
|
31
94
|
|
|
32
95
|
export async function saveApiKeys(newKeys: Record<string, string>): Promise<void> {
|
|
@@ -96,8 +159,27 @@ export function loadConfig(): NyxoraConfig {
|
|
|
96
159
|
const file = fs.readFileSync(configPath, 'utf8');
|
|
97
160
|
const parsed = yaml.parse(file) as Partial<NyxoraConfig>;
|
|
98
161
|
|
|
99
|
-
// Auto-migration logic: move llm.credentials to root credentials
|
|
100
162
|
let needsSave = false;
|
|
163
|
+
|
|
164
|
+
// Decrypt credentials
|
|
165
|
+
if (parsed.credentials) {
|
|
166
|
+
for (const key in parsed.credentials) {
|
|
167
|
+
if (parsed.credentials[key]) {
|
|
168
|
+
if (parsed.credentials[key]!.startsWith('ENC:')) needsSave = true;
|
|
169
|
+
parsed.credentials[key] = decryptDataSync(parsed.credentials[key]!);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (parsed.integrations?.telegram?.bot_token) {
|
|
174
|
+
if (parsed.integrations.telegram.bot_token.startsWith('ENC:')) needsSave = true;
|
|
175
|
+
parsed.integrations.telegram.bot_token = decryptDataSync(parsed.integrations.telegram.bot_token);
|
|
176
|
+
}
|
|
177
|
+
if (parsed.web3?.explorer_api_key) {
|
|
178
|
+
if (parsed.web3.explorer_api_key.startsWith('ENC:')) needsSave = true;
|
|
179
|
+
parsed.web3.explorer_api_key = decryptDataSync(parsed.web3.explorer_api_key);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Auto-migration logic: move llm.credentials to root credentials
|
|
101
183
|
if (parsed.llm && (parsed.llm as any).credentials) {
|
|
102
184
|
if (!parsed.credentials) {
|
|
103
185
|
parsed.credentials = {};
|
|
@@ -121,7 +203,7 @@ export function loadConfig(): NyxoraConfig {
|
|
|
121
203
|
// Auto-migration logic: move permissions to policy.yaml
|
|
122
204
|
const policyPath = getPath('policy.yaml');
|
|
123
205
|
if (!fs.existsSync(policyPath)) {
|
|
124
|
-
const defaultPolicy = `
|
|
206
|
+
const defaultPolicy = `auto_approve_limit_usd: 0\ncustom_llm_rules: []\n`;
|
|
125
207
|
fs.writeFileSync(policyPath, defaultPolicy, 'utf8');
|
|
126
208
|
console.log('[Config] Created default policy.yaml.');
|
|
127
209
|
}
|
|
@@ -132,9 +214,8 @@ export function loadConfig(): NyxoraConfig {
|
|
|
132
214
|
|
|
133
215
|
if (needsSave) {
|
|
134
216
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.log('[Config] Auto-migrated llm.credentials to root credentials.');
|
|
217
|
+
saveConfig(parsed as NyxoraConfig);
|
|
218
|
+
console.log('[Config] Auto-migrated config file safely.');
|
|
138
219
|
} catch (e) {
|
|
139
220
|
console.error('[Config] Failed to auto-migrate config file', e);
|
|
140
221
|
}
|
|
@@ -142,7 +223,8 @@ export function loadConfig(): NyxoraConfig {
|
|
|
142
223
|
|
|
143
224
|
|
|
144
225
|
|
|
145
|
-
|
|
226
|
+
|
|
227
|
+
const validatedConfig: NyxoraConfig = {
|
|
146
228
|
agent: parsed.agent || { name: 'Nyxora-Default', description: 'Your Personal Web3 Assistant.', default_chain: 'base', default_router: 'auto', default_slippage: 'auto' },
|
|
147
229
|
llm: parsed.llm || {
|
|
148
230
|
provider: 'openai',
|
|
@@ -159,11 +241,16 @@ export function loadConfig(): NyxoraConfig {
|
|
|
159
241
|
web3: { ...parsed.web3, rpc_urls: rpcUrls },
|
|
160
242
|
integrations: parsed.integrations || {
|
|
161
243
|
telegram: { enabled: false }
|
|
162
|
-
}
|
|
163
|
-
|
|
244
|
+
},
|
|
245
|
+
skills: parsed.skills
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
return validatedConfig;
|
|
164
249
|
} catch (error: any) {
|
|
165
250
|
if (error.code === 'ENOENT') {
|
|
166
251
|
console.log('[Config] No config.yaml found. Using default configuration.');
|
|
252
|
+
} else if (error.name === 'YAMLError' || error.message?.includes('YAML')) {
|
|
253
|
+
console.warn('[Parser] YAML Parse Error:', error.message);
|
|
167
254
|
} else {
|
|
168
255
|
console.error('[Config] Failed to load config.yaml. Using default configuration.', error);
|
|
169
256
|
}
|
|
@@ -202,9 +289,32 @@ export function saveConfig(newConfig: NyxoraConfig): void {
|
|
|
202
289
|
if (configToSave.web3 && configToSave.web3.rpc_urls) {
|
|
203
290
|
delete configToSave.web3.rpc_urls;
|
|
204
291
|
}
|
|
292
|
+
|
|
293
|
+
// Keys are no longer encrypted before saving. They are stored in plain text.
|
|
294
|
+
|
|
205
295
|
const yamlStr = yaml.stringify(configToSave);
|
|
206
296
|
fs.writeFileSync(configPath, yamlStr, 'utf8');
|
|
207
297
|
} catch (error) {
|
|
208
298
|
console.error('Failed to save config.yaml', error);
|
|
209
299
|
}
|
|
210
300
|
}
|
|
301
|
+
|
|
302
|
+
export interface PolicyConfig {
|
|
303
|
+
max_usd_per_tx?: number;
|
|
304
|
+
whitelist_only?: boolean;
|
|
305
|
+
require_approval?: boolean;
|
|
306
|
+
auto_approve_limit_usd?: number;
|
|
307
|
+
custom_llm_rules?: string[];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function loadPolicyConfig(): PolicyConfig {
|
|
311
|
+
const policyPath = getPath('policy.yaml');
|
|
312
|
+
if (fs.existsSync(policyPath)) {
|
|
313
|
+
try {
|
|
314
|
+
return yaml.parse(fs.readFileSync(policyPath, 'utf8')) || {};
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error('[Config] Failed to parse policy.yaml', e);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return {};
|
|
320
|
+
}
|