nyxora 26.6.12 → 26.6.14
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 +3 -3
- package/dist/launcher.js +11 -3
- package/dist/packages/core/src/agent/bridgeWatcher.js +34 -0
- package/dist/packages/core/src/agent/reasoning.js +6 -2
- package/dist/packages/core/src/agent/transactionManager.js +47 -0
- package/dist/packages/core/src/config/parser.js +42 -3
- package/dist/packages/core/src/gateway/server.js +23 -0
- package/dist/packages/core/src/gateway/setup.js +1 -0
- package/dist/packages/core/src/gateway/telegram.js +25 -1
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +3 -1
- package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +19 -10
- package/dist/packages/core/src/web3/skills/bridgeToken.js +1 -1
- package/dist/packages/core/src/web3/skills/customTx.js +1 -1
- package/dist/packages/core/src/web3/skills/defiLending.js +2 -2
- package/dist/packages/core/src/web3/skills/mintNft.js +1 -1
- package/dist/packages/core/src/web3/skills/nativeOpBridge.js +70 -0
- package/dist/packages/core/src/web3/skills/provideLiquidity.js +3 -3
- package/dist/packages/core/src/web3/skills/revokeApprovals.js +1 -1
- package/dist/packages/core/src/web3/skills/swapToken.js +1 -1
- package/dist/packages/core/src/web3/skills/transfer.js +1 -1
- package/dist/packages/core/src/web3/skills/yieldVault.js +2 -2
- package/launcher.ts +10 -3
- package/package.json +5 -2
- package/packages/core/package.json +1 -1
- package/packages/core/src/agent/bridgeWatcher.ts +39 -0
- package/packages/core/src/agent/reasoning.ts +6 -2
- package/packages/core/src/agent/transactionManager.ts +66 -0
- package/packages/core/src/config/parser.ts +42 -3
- package/packages/core/src/gateway/server.ts +25 -1
- package/packages/core/src/gateway/setup.ts +2 -1
- package/packages/core/src/gateway/telegram.ts +24 -1
- package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +4 -1
- package/packages/core/src/web3/aggregator/aggregatorTestnet.ts +28 -12
- package/packages/core/src/web3/skills/bridgeToken.ts +1 -1
- package/packages/core/src/web3/skills/customTx.ts +1 -1
- package/packages/core/src/web3/skills/defiLending.ts +2 -2
- package/packages/core/src/web3/skills/mintNft.ts +1 -1
- package/packages/core/src/web3/skills/nativeOpBridge.ts +83 -0
- package/packages/core/src/web3/skills/provideLiquidity.ts +3 -3
- package/packages/core/src/web3/skills/revokeApprovals.ts +1 -1
- package/packages/core/src/web3/skills/swapToken.ts +1 -1
- package/packages/core/src/web3/skills/transfer.ts +1 -1
- package/packages/core/src/web3/skills/yieldVault.ts +2 -2
- package/packages/dashboard/dist/assets/{index-BhKhEfi_.js → index-BKkezv4e.js} +3 -3
- package/packages/dashboard/dist/index.html +1 -1
- 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/README.md
CHANGED
|
@@ -13,14 +13,14 @@ Nyxora is a **secure, non-custodial runtime infrastructure for autonomous onchai
|
|
|
13
13
|
|
|
14
14
|
**Nyxora now natively supports the Model Context Protocol (MCP)**. You can transform your external AI agents (like Claude Desktop and Cursor) into secure Web3 actors that execute swaps and fetch balances using Nyxora's secure signer vault. [View the MCP Integration Guide](https://nyxoraai.github.io/Nyxora/guide/mcp-integration)
|
|
15
15
|
|
|
16
|
-
It operates under
|
|
16
|
+
It operates under a **Zero-Trust, Defense-in-Depth Cryptographically Bound Human-in-the-Loop** execution model, ensuring that Remote AIs (LLMs) never have unilateral access to your funds.
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
## 🔥 Key Features
|
|
21
21
|
|
|
22
22
|
### Advanced Security Architecture
|
|
23
|
-
* **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by an Arbitrum Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Arbitrum Architecture
|
|
23
|
+
* **🛡️ On-Chain AI Kill-Switch**: Nyxora is governed by an Arbitrum Smart Contract (`NyxoraAgentRegistry`). Users have absolute cryptographic power to instantly paralyze the AI's on-chain execution if compromised, solving the Web3 AI safety dilemma. [Read more about our Arbitrum Architecture](https://nyxoraai.github.io/Nyxora/security/smart-contract)
|
|
24
24
|
* **3-Tier IPC Architecture**: Nyxora is split into isolated processes: **Core** (LLM Runtime), **Policy Engine** (Guardrails on port 3001), and **Signer Vault** (Isolated Key Manager on Unix Sockets).
|
|
25
25
|
* **DeFi Configuration BYOK & UI Masking**: All aggregator and provider API keys are strictly isolated via a Bring Your Own Keys (BYOK) architecture into a heavily guarded `~/.nyxora/defi_keys.yaml` file. The local web Dashboard masks these injected secrets using `***********` and `IS_SET` censorship, completely neutralizing malicious browser extensions from exfiltrating your keys.
|
|
26
26
|
* **Approval Replay Protection (Nonce Guard)**: Transactions requested by the AI are drafted as hashes and signed with a randomized 16-byte Nonce. The `/api/transactions/:id/approve` endpoint strictly enforces Nonce matching to completely eliminate double-spending and Replay Attacks.
|
|
@@ -32,7 +32,7 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
|
|
|
32
32
|
* **Security Scanner**: Nyxora can scan smart contracts via GoPlus Labs to detect Honeypots, Hidden Taxes, and malicious proxy upgrades before you buy.
|
|
33
33
|
* **Advanced DeFi Optimization**: Autonomously supply assets to Aave V3, deposit into Beefy/Yearn Auto-Compounder Vaults, manage Uniswap V3 Liquidity (LP), and instantly revoke infinite approvals to secure your wallet. Features intelligent Transaction Chaining to auto-approve allowances prior to execution.
|
|
34
34
|
* **6-Engine Meta-Aggregator & Anti-MEV**: The core engine interfaces with a powerful 6-Engine Meta-Aggregator (**1inch, 0x, LI.FI, Relay, OpenOcean, and KyberSwap**) to route tokens cross-chain, ensuring absolute maximum liquidity depth.
|
|
35
|
-
* **Adaptive Auto Slippage Protection**: Nyxora enforces a dynamic and adaptive **'auto' slippage** by default to leverage dynamic MEV-protection from these
|
|
35
|
+
* **Adaptive Auto Slippage Protection**: Nyxora enforces a dynamic and adaptive **'auto' slippage** by default to leverage dynamic MEV-protection from these industry-standard aggregators. However, the user retains absolute control to override this dynamically—either globally via the Dashboard Settings or on a per-transaction basis through NLP chat commands (e.g., *"Swap 1 ETH to PEPE with 10% slippage"*).
|
|
36
36
|
|
|
37
37
|
* **Cross-Chain Hybrid Market Scanner**: Real-time asset tracking combining CoinGecko global data with DexScreener on-chain metrics across Ethereum, Base, Solana, BSC, and more.
|
|
38
38
|
* **"Lean Degen" Auto-Whitelist**: Automatically intercepts Contract Addresses (CAs) whenever you check balances or swap tokens, saving them to your localized `user_whitelist.json` for future tracking.
|
package/dist/launcher.js
CHANGED
|
@@ -36,7 +36,15 @@ const spawnService = (name, command, args, env, inheritStdio = false) => {
|
|
|
36
36
|
child = (0, child_process_1.spawn)(command, args, { env, stdio: inheritStdio ? 'inherit' : 'pipe' });
|
|
37
37
|
if (!inheritStdio) {
|
|
38
38
|
child.stdout?.on('data', (data) => process.stdout.write(`[${name}] ${data}`));
|
|
39
|
-
child.stderr?.on('data', (data) =>
|
|
39
|
+
child.stderr?.on('data', (data) => {
|
|
40
|
+
const msg = data.toString();
|
|
41
|
+
if (msg.toLowerCase().includes('warn')) {
|
|
42
|
+
process.stderr.write(`[${name}] ${msg}`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
process.stderr.write(`[${name}] ERROR: ${msg}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
40
48
|
}
|
|
41
49
|
child.on('close', async (code) => {
|
|
42
50
|
console.log(`[${name}] Exited with code ${code}`);
|
|
@@ -101,8 +109,8 @@ if (fs_1.default.existsSync(socketPath)) {
|
|
|
101
109
|
const children = [];
|
|
102
110
|
const isCompiled = __filename.endsWith('.js');
|
|
103
111
|
const ext = isCompiled ? '.js' : '.ts';
|
|
104
|
-
const cmd = isCompiled ? 'node' : '
|
|
105
|
-
const baseArgs = isCompiled ? [] : ['
|
|
112
|
+
const cmd = isCompiled ? 'node' : path_1.default.join(__dirname, 'node_modules', '.bin', 'ts-node');
|
|
113
|
+
const baseArgs = isCompiled ? [] : ['-T'];
|
|
106
114
|
const signerPath = path_1.default.join(__dirname, `packages/signer/src/server${ext}`);
|
|
107
115
|
const signer = spawnService('Signer', cmd, [...baseArgs, signerPath], env);
|
|
108
116
|
children.push(signer);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startBridgeWatcher = startBridgeWatcher;
|
|
4
|
+
const transactionManager_1 = require("./transactionManager");
|
|
5
|
+
const telegram_1 = require("../gateway/telegram");
|
|
6
|
+
const parser_1 = require("../config/parser");
|
|
7
|
+
// In a real production environment, this would use the @eth-optimism/sdk
|
|
8
|
+
// to fetch the Merkle Proof and call proveWithdrawalTransaction on L1.
|
|
9
|
+
// For the scope of this architecture prototype, we simulate the Challenge Period
|
|
10
|
+
// watcher by using a time-delay, representing the exact asynchronous behavior.
|
|
11
|
+
const CHALLENGE_PERIOD_MS = 2 * 60 * 1000; // Simulating a 2-minute challenge period for testnet demo
|
|
12
|
+
function startBridgeWatcher() {
|
|
13
|
+
console.log('[Bridge Watcher] Started background daemon for asynchronous L2 withdrawals');
|
|
14
|
+
setInterval(async () => {
|
|
15
|
+
const config = (0, parser_1.loadConfig)();
|
|
16
|
+
const authId = config.integrations?.telegram?.authorized_chat_id;
|
|
17
|
+
if (!authId)
|
|
18
|
+
return;
|
|
19
|
+
const pending = transactionManager_1.txManager.getPendingWithdrawals();
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
for (const w of pending) {
|
|
22
|
+
if (w.status === 'WAITING_FOR_CHALLENGE') {
|
|
23
|
+
if (now - w.createdAt > CHALLENGE_PERIOD_MS) {
|
|
24
|
+
// The simulated challenge period is over. State root is "published".
|
|
25
|
+
console.log(`[Bridge Watcher] Withdrawal ${w.id} is ready for L1 claim!`);
|
|
26
|
+
transactionManager_1.txManager.updateWithdrawalStatus(w.id, 'READY_FOR_CLAIM');
|
|
27
|
+
const amountDisplay = Number(w.amount) / 1e18; // assuming 18 decimals
|
|
28
|
+
const message = `🔔 **Bridge Ready to Claim!**\n\nYour withdrawal of ${amountDisplay} ETH from ${w.l2Chain} to ${w.l1Chain} has completed its challenge period.\n\nShall I execute the Prove & Claim transaction on L1 now?`;
|
|
29
|
+
await (0, telegram_1.sendPushNotification)(authId, message, w.id);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}, 30000); // Check every 30 seconds
|
|
34
|
+
}
|
|
@@ -288,8 +288,12 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
288
288
|
if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
|
|
289
289
|
let canFastReturnAll = true;
|
|
290
290
|
let accumulatedResults = [];
|
|
291
|
-
//
|
|
292
|
-
const fastReturnTools = [
|
|
291
|
+
// Enabled fastReturnTools to eliminate 2nd LLM latency for transaction popups
|
|
292
|
+
const fastReturnTools = [
|
|
293
|
+
'transfer_token', 'transfer_native', 'swap_token', 'bridge_token',
|
|
294
|
+
'mint_nft', 'custom_tx', 'revoke_approval', 'supply_aave',
|
|
295
|
+
'deposit_yield_vault', 'provide_liquidity_v3'
|
|
296
|
+
];
|
|
293
297
|
for (const _toolCall of responseMessage.tool_calls) {
|
|
294
298
|
const toolCall = _toolCall;
|
|
295
299
|
let result = "";
|
|
@@ -5,8 +5,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.txManager = void 0;
|
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
8
10
|
class TransactionManager {
|
|
9
11
|
transactions = new Map();
|
|
12
|
+
withdrawals = new Map();
|
|
13
|
+
dbPath;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.dbPath = path_1.default.join(process.cwd(), '.nyxora_withdrawals.json');
|
|
16
|
+
this.loadWithdrawals();
|
|
17
|
+
}
|
|
18
|
+
loadWithdrawals() {
|
|
19
|
+
if (fs_1.default.existsSync(this.dbPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const data = fs_1.default.readFileSync(this.dbPath, 'utf8');
|
|
22
|
+
const parsed = JSON.parse(data);
|
|
23
|
+
parsed.forEach(w => this.withdrawals.set(w.id, w));
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
console.error("Failed to load withdrawals DB:", e);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
saveWithdrawals() {
|
|
31
|
+
const data = Array.from(this.withdrawals.values());
|
|
32
|
+
fs_1.default.writeFileSync(this.dbPath, JSON.stringify(data, null, 2));
|
|
33
|
+
}
|
|
10
34
|
createPendingTransaction(type, chainName, details) {
|
|
11
35
|
const id = crypto_1.default.randomUUID();
|
|
12
36
|
const nonce = crypto_1.default.randomBytes(16).toString('hex');
|
|
@@ -36,5 +60,28 @@ class TransactionManager {
|
|
|
36
60
|
tx.result = result;
|
|
37
61
|
}
|
|
38
62
|
}
|
|
63
|
+
// --- WITHDRAWAL LOGIC ---
|
|
64
|
+
createPendingWithdrawal(data) {
|
|
65
|
+
const id = crypto_1.default.randomUUID();
|
|
66
|
+
const withdrawal = {
|
|
67
|
+
...data,
|
|
68
|
+
id,
|
|
69
|
+
status: 'WAITING_FOR_CHALLENGE',
|
|
70
|
+
createdAt: Date.now()
|
|
71
|
+
};
|
|
72
|
+
this.withdrawals.set(id, withdrawal);
|
|
73
|
+
this.saveWithdrawals();
|
|
74
|
+
return withdrawal;
|
|
75
|
+
}
|
|
76
|
+
getPendingWithdrawals() {
|
|
77
|
+
return Array.from(this.withdrawals.values()).filter(w => w.status !== 'COMPLETED');
|
|
78
|
+
}
|
|
79
|
+
updateWithdrawalStatus(id, status) {
|
|
80
|
+
const w = this.withdrawals.get(id);
|
|
81
|
+
if (w) {
|
|
82
|
+
w.status = status;
|
|
83
|
+
this.saveWithdrawals();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
39
86
|
}
|
|
40
87
|
exports.txManager = new TransactionManager();
|
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadRpcConfig = loadRpcConfig;
|
|
7
|
+
exports.saveRpcConfig = saveRpcConfig;
|
|
6
8
|
exports.loadApiKeys = loadApiKeys;
|
|
7
9
|
exports.saveApiKeys = saveApiKeys;
|
|
8
10
|
exports.loadConfig = loadConfig;
|
|
@@ -10,6 +12,27 @@ exports.saveConfig = saveConfig;
|
|
|
10
12
|
const fs_1 = __importDefault(require("fs"));
|
|
11
13
|
const yaml_1 = __importDefault(require("yaml"));
|
|
12
14
|
const paths_1 = require("./paths");
|
|
15
|
+
function loadRpcConfig() {
|
|
16
|
+
const rpcPath = (0, paths_1.getPath)('rpc_key.yaml');
|
|
17
|
+
if (fs_1.default.existsSync(rpcPath)) {
|
|
18
|
+
try {
|
|
19
|
+
return yaml_1.default.parse(fs_1.default.readFileSync(rpcPath, 'utf8')) || {};
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
console.error('[Config] Failed to parse rpc_key.yaml', e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
function saveRpcConfig(rpcUrls) {
|
|
28
|
+
const rpcPath = (0, paths_1.getPath)('rpc_key.yaml');
|
|
29
|
+
try {
|
|
30
|
+
fs_1.default.writeFileSync(rpcPath, yaml_1.default.stringify(rpcUrls), 'utf8');
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error('Failed to save rpc_key.yaml', error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
13
36
|
async function loadApiKeys() {
|
|
14
37
|
const config = loadConfig();
|
|
15
38
|
return config.credentials || {};
|
|
@@ -23,6 +46,8 @@ async function saveApiKeys(newKeys) {
|
|
|
23
46
|
}
|
|
24
47
|
function loadConfig() {
|
|
25
48
|
const configPath = (0, paths_1.getPath)('config.yaml');
|
|
49
|
+
const rpcPath = (0, paths_1.getPath)('rpc_key.yaml');
|
|
50
|
+
let rpcUrls = loadRpcConfig();
|
|
26
51
|
try {
|
|
27
52
|
const file = fs_1.default.readFileSync(configPath, 'utf8');
|
|
28
53
|
const parsed = yaml_1.default.parse(file);
|
|
@@ -41,6 +66,16 @@ function loadConfig() {
|
|
|
41
66
|
delete parsed.llm.credentials;
|
|
42
67
|
needsSave = true;
|
|
43
68
|
}
|
|
69
|
+
// Auto-migration logic: move web3.rpc_urls to rpc_key.yaml
|
|
70
|
+
if (parsed.web3 && parsed.web3.rpc_urls && Object.keys(parsed.web3.rpc_urls).length > 0) {
|
|
71
|
+
if (!fs_1.default.existsSync(rpcPath)) {
|
|
72
|
+
rpcUrls = parsed.web3.rpc_urls;
|
|
73
|
+
saveRpcConfig(rpcUrls);
|
|
74
|
+
console.log('[Config] Auto-migrated web3.rpc_urls to rpc_key.yaml.');
|
|
75
|
+
}
|
|
76
|
+
delete parsed.web3.rpc_urls;
|
|
77
|
+
needsSave = true;
|
|
78
|
+
}
|
|
44
79
|
if (needsSave) {
|
|
45
80
|
try {
|
|
46
81
|
const yamlStr = yaml_1.default.stringify(parsed);
|
|
@@ -65,7 +100,7 @@ function loadConfig() {
|
|
|
65
100
|
},
|
|
66
101
|
credentials: parsed.credentials || {},
|
|
67
102
|
memory: parsed.memory || { type: 'file', path: './memory.json' },
|
|
68
|
-
web3: parsed.web3
|
|
103
|
+
web3: { ...parsed.web3, rpc_urls: rpcUrls },
|
|
69
104
|
integrations: parsed.integrations || {
|
|
70
105
|
telegram: { enabled: false }
|
|
71
106
|
},
|
|
@@ -102,7 +137,7 @@ function loadConfig() {
|
|
|
102
137
|
},
|
|
103
138
|
credentials: {},
|
|
104
139
|
memory: { type: 'file', path: './memory.json' },
|
|
105
|
-
web3: { rpc_urls:
|
|
140
|
+
web3: { rpc_urls: rpcUrls },
|
|
106
141
|
integrations: {
|
|
107
142
|
telegram: { enabled: false }
|
|
108
143
|
},
|
|
@@ -116,7 +151,11 @@ function loadConfig() {
|
|
|
116
151
|
function saveConfig(newConfig) {
|
|
117
152
|
const configPath = (0, paths_1.getPath)('config.yaml');
|
|
118
153
|
try {
|
|
119
|
-
const
|
|
154
|
+
const configToSave = JSON.parse(JSON.stringify(newConfig));
|
|
155
|
+
if (configToSave.web3 && configToSave.web3.rpc_urls) {
|
|
156
|
+
delete configToSave.web3.rpc_urls;
|
|
157
|
+
}
|
|
158
|
+
const yamlStr = yaml_1.default.stringify(configToSave);
|
|
120
159
|
fs_1.default.writeFileSync(configPath, yamlStr, 'utf8');
|
|
121
160
|
}
|
|
122
161
|
catch (error) {
|
|
@@ -70,6 +70,7 @@ const analyzeDocument_1 = require("../system/skills/analyzeDocument");
|
|
|
70
70
|
const searchWeb_1 = require("../system/skills/searchWeb");
|
|
71
71
|
const googleWorkspace_1 = require("../system/skills/googleWorkspace");
|
|
72
72
|
const telegram_1 = require("./telegram");
|
|
73
|
+
const bridgeWatcher_1 = require("../agent/bridgeWatcher");
|
|
73
74
|
const eventListener_1 = require("../web3/eventListener");
|
|
74
75
|
const googleAuthModule_1 = require("./googleAuthModule");
|
|
75
76
|
const legalGenerator_1 = require("./legalGenerator");
|
|
@@ -263,6 +264,26 @@ app.post('/api/config', (req, res) => {
|
|
|
263
264
|
res.status(500).json({ error: error.message });
|
|
264
265
|
}
|
|
265
266
|
});
|
|
267
|
+
app.get('/api/rpc', (req, res) => {
|
|
268
|
+
try {
|
|
269
|
+
const rpcConfig = (0, parser_1.loadRpcConfig)();
|
|
270
|
+
res.json(rpcConfig);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
res.status(500).json({ error: error.message });
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
app.post('/api/rpc', (req, res) => {
|
|
277
|
+
try {
|
|
278
|
+
const currentRpc = (0, parser_1.loadRpcConfig)();
|
|
279
|
+
const newRpc = { ...currentRpc, ...req.body };
|
|
280
|
+
(0, parser_1.saveRpcConfig)(newRpc);
|
|
281
|
+
res.json({ success: true });
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
res.status(500).json({ error: error.message });
|
|
285
|
+
}
|
|
286
|
+
});
|
|
266
287
|
app.get('/api/defi-keys', (req, res) => {
|
|
267
288
|
try {
|
|
268
289
|
const keys = (0, defiConfigManager_1.loadDefiKeys)();
|
|
@@ -849,6 +870,8 @@ function startServer() {
|
|
|
849
870
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
850
871
|
// Start the Telegram bot listener
|
|
851
872
|
(0, telegram_1.startTelegramBot)();
|
|
873
|
+
// Start Asynchronous Bridge Watcher
|
|
874
|
+
(0, bridgeWatcher_1.startBridgeWatcher)();
|
|
852
875
|
// Start Event Listener for Limit Orders (V3)
|
|
853
876
|
eventListener_1.eventListener.start();
|
|
854
877
|
});
|
|
@@ -424,6 +424,7 @@ Provider: ${config.llm.provider}`;
|
|
|
424
424
|
config.integrations.telegram.authorized_chat_id = authorizedChatId;
|
|
425
425
|
}
|
|
426
426
|
(0, parser_1.saveConfig)(config);
|
|
427
|
+
(0, parser_1.saveRpcConfig)({});
|
|
427
428
|
// Sync disabled_skills.json based on user selection
|
|
428
429
|
const allWeb3Skills = [
|
|
429
430
|
'transfer', 'swapToken', 'bridgeToken', 'customTx', 'mintNft',
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.formatToTelegramHTML = formatToTelegramHTML;
|
|
7
7
|
exports.startTelegramBot = startTelegramBot;
|
|
8
|
+
exports.sendPushNotification = sendPushNotification;
|
|
8
9
|
const telegraf_1 = require("telegraf");
|
|
9
10
|
const reasoning_1 = require("../agent/reasoning");
|
|
10
11
|
const parser_1 = require("../config/parser");
|
|
@@ -18,6 +19,7 @@ const executeDefi_1 = require("../web3/skills/executeDefi");
|
|
|
18
19
|
const revokeApprovals_1 = require("../web3/skills/revokeApprovals");
|
|
19
20
|
const formatter_1 = require("../utils/formatter");
|
|
20
21
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
22
|
+
let globalBotInstance = null;
|
|
21
23
|
function formatToTelegramHTML(text) {
|
|
22
24
|
if (!text)
|
|
23
25
|
return "";
|
|
@@ -32,9 +34,11 @@ function formatToTelegramHTML(text) {
|
|
|
32
34
|
// Convert code blocks and inline code
|
|
33
35
|
html = html.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
|
|
34
36
|
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
35
|
-
// Strip <thought> blocks completely for user-friendly output
|
|
37
|
+
// Strip <thought> and <think> blocks completely for user-friendly output
|
|
36
38
|
html = html.replace(/<thought>[\s\S]*?<\/thought>\n?/g, '');
|
|
37
39
|
html = html.replace(/<thought>[\s\S]*?<\/thought>\n?/g, '');
|
|
40
|
+
html = html.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
|
|
41
|
+
html = html.replace(/<think>[\s\S]*?<\/think>\n?/g, '');
|
|
38
42
|
// Transform Markdown Tables to <pre> monospaced blocks so they don't break on mobile
|
|
39
43
|
const tableRegex = /(?:\|.*\|(?:\n|$))+/g;
|
|
40
44
|
html = html.replace(tableRegex, (match) => {
|
|
@@ -51,6 +55,7 @@ function startTelegramBot() {
|
|
|
51
55
|
}
|
|
52
56
|
try {
|
|
53
57
|
const bot = new telegraf_1.Telegraf(token);
|
|
58
|
+
globalBotInstance = bot;
|
|
54
59
|
// Pairing state variables
|
|
55
60
|
const isPaired = !!config.integrations?.telegram?.authorized_chat_id;
|
|
56
61
|
let generatedPin = '';
|
|
@@ -257,3 +262,22 @@ function startTelegramBot() {
|
|
|
257
262
|
console.error('[Telegram] Failed to initialize bot:', error);
|
|
258
263
|
}
|
|
259
264
|
}
|
|
265
|
+
async function sendPushNotification(chatId, message, withdrawalId) {
|
|
266
|
+
if (!globalBotInstance)
|
|
267
|
+
return;
|
|
268
|
+
try {
|
|
269
|
+
let extraParams = { parse_mode: 'HTML' };
|
|
270
|
+
if (withdrawalId) {
|
|
271
|
+
extraParams = {
|
|
272
|
+
...extraParams,
|
|
273
|
+
...telegraf_1.Markup.inlineKeyboard([
|
|
274
|
+
[telegraf_1.Markup.button.callback(`✅ Approve Claim`, `claim_${withdrawalId}`)]
|
|
275
|
+
])
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
await globalBotInstance.telegram.sendMessage(chatId, formatToTelegramHTML(message), extraParams);
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
console.error('[Telegram] Failed to send push notification:', error);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -238,6 +238,8 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
|
|
|
238
238
|
if (!buildRes.ok)
|
|
239
239
|
throw new Error(await buildRes.text());
|
|
240
240
|
const buildData = await buildRes.json();
|
|
241
|
+
const isNative = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ||
|
|
242
|
+
fromToken === '0x0000000000000000000000000000000000000000';
|
|
241
243
|
return {
|
|
242
244
|
provider: 'KyberSwap',
|
|
243
245
|
expectedOutput: (Number(routeData.data.routeSummary.amountOut) / 1e18).toString(),
|
|
@@ -246,7 +248,7 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
|
|
|
246
248
|
txPayload: {
|
|
247
249
|
to: buildData.data.routerAddress,
|
|
248
250
|
data: buildData.data.data,
|
|
249
|
-
value:
|
|
251
|
+
value: isNative ? amount : "0" // FIXED: Mengirim jumlah asli dalam WEI jika Native ETH, atau 0 jika ERC20
|
|
250
252
|
},
|
|
251
253
|
rawQuote: buildData.data
|
|
252
254
|
};
|
|
@@ -3,21 +3,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fetchTestnetBestRoute = fetchTestnetBestRoute;
|
|
4
4
|
const httpClient_1 = require("../../utils/httpClient");
|
|
5
5
|
const viem_1 = require("viem");
|
|
6
|
+
const nativeOpBridge_1 = require("../skills/nativeOpBridge");
|
|
6
7
|
async function fetchTestnetBestRoute(fromChain, toChain, fromToken, toToken, amountInWei, userAddress, slippageTolerance = "auto") {
|
|
7
8
|
const promises = [];
|
|
8
|
-
// Routing Logic
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
promises.push(
|
|
9
|
+
// Routing Logic Hierarchy
|
|
10
|
+
const isOpStack = toChain === 'optimism_sepolia' || toChain === 'base_sepolia' ||
|
|
11
|
+
fromChain === 'optimism_sepolia' || fromChain === 'base_sepolia';
|
|
12
|
+
const isArbitrum = toChain === 'arbitrum_sepolia' || fromChain === 'arbitrum_sepolia';
|
|
13
|
+
if (isOpStack) {
|
|
14
|
+
// Primary: Universal OP Stack
|
|
15
|
+
promises.push((0, nativeOpBridge_1.fetchNativeOpBridgeTestnet)(fromChain, toChain, fromToken, toToken, amountInWei, userAddress)
|
|
16
|
+
.catch(e => { console.warn('Native OP failed:', e.message); return null; }));
|
|
17
|
+
// Fallback 1: Relay Testnet (Only supports Base Sepolia natively via Relay endpoint)
|
|
18
|
+
if (toChain === 'base_sepolia' || fromChain === 'base_sepolia') {
|
|
19
|
+
promises.push(fetchRelayTestnet(fromChain, toChain, fromToken, toToken, amountInWei, userAddress)
|
|
20
|
+
.catch(e => { console.warn('Relay Fallback failed:', e.message); return null; }));
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
|
-
else if (
|
|
17
|
-
|
|
23
|
+
else if (isArbitrum) {
|
|
24
|
+
// Fallback 2: Official Arbitrum Bridge
|
|
25
|
+
promises.push(fetchArbitrumBridgeTestnet(fromChain, toChain, fromToken, toToken, amountInWei, userAddress)
|
|
26
|
+
.catch(e => { console.warn('Arbitrum Bridge failed:', e.message); return null; }));
|
|
18
27
|
}
|
|
19
28
|
else {
|
|
20
|
-
throw new Error(`[Testnet Meta-Aggregator] Unsupported testnet route from ${fromChain} to ${toChain}
|
|
29
|
+
throw new Error(`[Testnet Meta-Aggregator] Unsupported testnet route from ${fromChain} to ${toChain}.`);
|
|
21
30
|
}
|
|
22
31
|
const results = await Promise.allSettled(promises);
|
|
23
32
|
let bestQuote = null;
|
|
@@ -52,7 +52,7 @@ async function prepareBridgeToken(fromChain, toChain, tokenSymbol, amountStr, mo
|
|
|
52
52
|
txData: route.txPayload,
|
|
53
53
|
rawQuote: route.rawQuote
|
|
54
54
|
});
|
|
55
|
-
return
|
|
55
|
+
return `⏳ **Bridge queued:** ${amountStr} ${tokenSymbol} | ${fromChain.toUpperCase()} ➡️ ${toChain.toUpperCase()} | Via ${route.provider} | Approve below.`;
|
|
56
56
|
}
|
|
57
57
|
catch (error) {
|
|
58
58
|
console.error("BRIDGE TOKEN ERROR:", error);
|
|
@@ -14,7 +14,7 @@ async function prepareCustomTx(chainName, toAddress, data, valueWei = "0", descr
|
|
|
14
14
|
valueWei,
|
|
15
15
|
description
|
|
16
16
|
});
|
|
17
|
-
return
|
|
17
|
+
return `⏳ **Custom Tx queued:** ${description} | ${chainName.toUpperCase()} | Approve below.`;
|
|
18
18
|
}
|
|
19
19
|
catch (error) {
|
|
20
20
|
return `Failed to prepare custom tx: ${error.message}`;
|
|
@@ -62,7 +62,7 @@ async function prepareAaveSupply(chainName, tokenAddressOrSymbol, amountStr) {
|
|
|
62
62
|
symbol: metadata.symbol,
|
|
63
63
|
gasEstimate: "60000"
|
|
64
64
|
});
|
|
65
|
-
return
|
|
65
|
+
return `⏳ **Approve queued:** ${metadata.symbol} | For: Aave V3 | ${chainName.toUpperCase()} | Approve below.`;
|
|
66
66
|
}
|
|
67
67
|
// 2. Simulate Supply
|
|
68
68
|
let gasEstimate = 0n;
|
|
@@ -87,7 +87,7 @@ async function prepareAaveSupply(chainName, tokenAddressOrSymbol, amountStr) {
|
|
|
87
87
|
symbol: metadata.symbol,
|
|
88
88
|
gasEstimate: gasEstimate.toString()
|
|
89
89
|
});
|
|
90
|
-
return
|
|
90
|
+
return `⏳ **Aave Supply queued:** ${amountStr} ${metadata.symbol} | ${chainName.toUpperCase()} | Approve below.`;
|
|
91
91
|
}
|
|
92
92
|
catch (error) {
|
|
93
93
|
return `Failed to prepare Aave supply: ${error.message}`;
|
|
@@ -60,7 +60,7 @@ async function prepareMintNft(chainName, contractAddress, functionSignature, arg
|
|
|
60
60
|
valueWei: valueWei.toString(),
|
|
61
61
|
gasEstimate: gasEstimate.toString()
|
|
62
62
|
});
|
|
63
|
-
return
|
|
63
|
+
return `⏳ **Mint NFT queued:** ${contractAddress} | ${chainName.toUpperCase()} | ⚠️ Verify Contract | Approve below.`;
|
|
64
64
|
}
|
|
65
65
|
catch (error) {
|
|
66
66
|
return `Simulation failed! Cannot prepare mint. Error: ${error.message}`;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchNativeOpBridgeTestnet = fetchNativeOpBridgeTestnet;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
const OP_L1_PORTAL_MAP = {
|
|
6
|
+
'base_sepolia': '0xfd0bf71f60660e2f608ed56e1659c450eb113120',
|
|
7
|
+
'optimism_sepolia': '0xfbb0621e0b23b5478b630bd55a5f21f67730b0f1'
|
|
8
|
+
};
|
|
9
|
+
const L2_STANDARD_BRIDGE = '0x4200000000000000000000000000000000000010';
|
|
10
|
+
async function fetchNativeOpBridgeTestnet(fromChain, toChain, fromToken, toToken, amount, address) {
|
|
11
|
+
const isL1toL2 = fromChain === 'sepolia' && OP_L1_PORTAL_MAP[toChain];
|
|
12
|
+
const isL2toL1 = toChain === 'sepolia' && OP_L1_PORTAL_MAP[fromChain];
|
|
13
|
+
if (!isL1toL2 && !isL2toL1) {
|
|
14
|
+
throw new Error(`[Native OP Bridge] Unsupported route from ${fromChain} to ${toChain}`);
|
|
15
|
+
}
|
|
16
|
+
// Ensure it's Native ETH for simplicity
|
|
17
|
+
const isNative = fromToken.toLowerCase() === 'eth' ||
|
|
18
|
+
fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ||
|
|
19
|
+
fromToken === '0x0000000000000000000000000000000000000000';
|
|
20
|
+
if (!isNative) {
|
|
21
|
+
throw new Error(`[Native OP Bridge] Only Native ETH bridging is supported natively in this version.`);
|
|
22
|
+
}
|
|
23
|
+
if (isL1toL2) {
|
|
24
|
+
// Deposit (L1 -> L2)
|
|
25
|
+
const portalAddress = OP_L1_PORTAL_MAP[toChain];
|
|
26
|
+
const bridgeEthAbi = (0, viem_1.parseAbi)(['function bridgeETHTo(address _to, uint32 _minGasLimit, bytes _extraData) payable']);
|
|
27
|
+
const callData = (0, viem_1.encodeFunctionData)({
|
|
28
|
+
abi: bridgeEthAbi,
|
|
29
|
+
functionName: 'bridgeETHTo',
|
|
30
|
+
args: [address, 200000, '0x']
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
provider: 'Native OP Stack Bridge (L1->L2)',
|
|
34
|
+
txPayload: {
|
|
35
|
+
to: portalAddress,
|
|
36
|
+
data: callData,
|
|
37
|
+
value: amount
|
|
38
|
+
},
|
|
39
|
+
expectedOutput: amount,
|
|
40
|
+
expectedOutputRaw: amount,
|
|
41
|
+
gasCostUsd: 0,
|
|
42
|
+
rawQuote: { note: 'Direct L1->L2 Deposit via OP Portal. Funds will arrive instantly on L2.' }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Withdrawal (L2 -> L1)
|
|
47
|
+
const withdrawEthAbi = (0, viem_1.parseAbi)(['function bridgeETHTo(address _to, uint32 _minGasLimit, bytes _extraData) payable']);
|
|
48
|
+
const callData = (0, viem_1.encodeFunctionData)({
|
|
49
|
+
abi: withdrawEthAbi,
|
|
50
|
+
functionName: 'bridgeETHTo',
|
|
51
|
+
args: [address, 200000, '0x']
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
provider: 'Native OP Stack Bridge (L2->L1)',
|
|
55
|
+
txPayload: {
|
|
56
|
+
to: L2_STANDARD_BRIDGE,
|
|
57
|
+
data: callData,
|
|
58
|
+
value: amount
|
|
59
|
+
},
|
|
60
|
+
expectedOutput: amount,
|
|
61
|
+
expectedOutputRaw: amount,
|
|
62
|
+
gasCostUsd: 0,
|
|
63
|
+
rawQuote: {
|
|
64
|
+
note: 'Direct L2->L1 Withdrawal. WARNING: Requires 7-day challenge period.',
|
|
65
|
+
isAsyncWithdrawal: true,
|
|
66
|
+
l1PortalAddress: OP_L1_PORTAL_MAP[fromChain]
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -111,11 +111,11 @@ async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1A
|
|
|
111
111
|
});
|
|
112
112
|
if (allowance0 < amount0Wei) {
|
|
113
113
|
const tx = transactionManager_1.txManager.createPendingTransaction('approve', chainName, { spenderAddress: positionManagerAddress, tokenAddress: token0, amountStr: amount0, symbol: meta0.symbol, gasEstimate: "60000" });
|
|
114
|
-
return
|
|
114
|
+
return `⏳ **Approve queued:** ${meta0.symbol} | For: Uniswap V3 | ${chainName.toUpperCase()} | Approve below.`;
|
|
115
115
|
}
|
|
116
116
|
if (allowance1 < amount1Wei) {
|
|
117
117
|
const tx = transactionManager_1.txManager.createPendingTransaction('approve', chainName, { spenderAddress: positionManagerAddress, tokenAddress: token1, amountStr: amount1, symbol: meta1.symbol, gasEstimate: "60000" });
|
|
118
|
-
return
|
|
118
|
+
return `⏳ **Approve queued:** ${meta1.symbol} | For: Uniswap V3 | ${chainName.toUpperCase()} | Approve below.`;
|
|
119
119
|
}
|
|
120
120
|
// 2. Simulate Mint
|
|
121
121
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1200); // 20 mins
|
|
@@ -148,7 +148,7 @@ async function prepareProvideLiquidity(chainName, token0AddressOrSymbol, token1A
|
|
|
148
148
|
positionManagerAddress, token0, token1, amount0, amount1, tickLower, tickUpper,
|
|
149
149
|
gasEstimate: gasEstimate.toString()
|
|
150
150
|
});
|
|
151
|
-
return
|
|
151
|
+
return `⏳ **Add Liquidity queued:** ${amount0} ${meta0.symbol} & ${amount1} ${meta1.symbol} | ${chainName.toUpperCase()} | Approve below.`;
|
|
152
152
|
}
|
|
153
153
|
catch (error) {
|
|
154
154
|
return `Failed to prepare liquidity provision: ${error.message}`;
|
|
@@ -39,7 +39,7 @@ async function prepareRevokeApproval(chainName, tokenAddressOrSymbol, spenderAdd
|
|
|
39
39
|
symbol,
|
|
40
40
|
gasEstimate: gasEstimate.toString()
|
|
41
41
|
});
|
|
42
|
-
return
|
|
42
|
+
return `⏳ **Revoke queued:** ${symbol} | Spender: ${spenderAddress} | ${chainName.toUpperCase()} | Approve below.`;
|
|
43
43
|
}
|
|
44
44
|
catch (error) {
|
|
45
45
|
return `Failed to prepare revoke approval: ${error.message}`;
|
|
@@ -38,7 +38,7 @@ async function prepareSwapToken(chainName, fromToken, toToken, amountStr, mode =
|
|
|
38
38
|
txData: route.txPayload,
|
|
39
39
|
rawQuote: route.rawQuote
|
|
40
40
|
});
|
|
41
|
-
return
|
|
41
|
+
return `⏳ **Swap queued:** ${amountStr} ${fromToken} ➡️ ${route.expectedOutput} ${toToken} | ${chainName.toUpperCase()} | Via ${route.provider} | Approve below.`;
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
44
|
return `Failed to prepare swap: ${error.message}`;
|
|
@@ -66,7 +66,7 @@ async function prepareTransfer(chainName, toAddress, amountStr, token) {
|
|
|
66
66
|
gasEstimate: gasEstimate.toString()
|
|
67
67
|
});
|
|
68
68
|
const tokenName = isNative ? "Native Token" : symbol;
|
|
69
|
-
return
|
|
69
|
+
return `⏳ **Transfer queued:** ${amountStr} ${tokenName} | ${chainName.toUpperCase()} ➡️ ${toAddress} | Approve below.`;
|
|
70
70
|
}
|
|
71
71
|
catch (error) {
|
|
72
72
|
return `Simulation failed! Cannot prepare transfer. Error: ${error.message}`;
|
|
@@ -50,7 +50,7 @@ async function prepareVaultDeposit(chainName, protocol, vaultAddress, tokenAddre
|
|
|
50
50
|
symbol: metadata.symbol,
|
|
51
51
|
gasEstimate: "60000"
|
|
52
52
|
});
|
|
53
|
-
return
|
|
53
|
+
return `⏳ **Approve queued:** ${metadata.symbol} | For: ${protocol.toUpperCase()} Vault | ${chainName.toUpperCase()} | Approve below.`;
|
|
54
54
|
}
|
|
55
55
|
// 2. Simulate Deposit
|
|
56
56
|
let gasEstimate = 0n;
|
|
@@ -76,7 +76,7 @@ async function prepareVaultDeposit(chainName, protocol, vaultAddress, tokenAddre
|
|
|
76
76
|
protocol,
|
|
77
77
|
gasEstimate: gasEstimate.toString()
|
|
78
78
|
});
|
|
79
|
-
return
|
|
79
|
+
return `⏳ **Vault Deposit queued:** ${amountStr} ${metadata.symbol} | ${protocol.toUpperCase()} | ${chainName.toUpperCase()} | Approve below.`;
|
|
80
80
|
}
|
|
81
81
|
catch (error) {
|
|
82
82
|
return `Failed to prepare Vault deposit: ${error.message}`;
|