nyxora 26.6.11-2 → 26.6.13
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/packages/core/src/agent/bridgeWatcher.js +34 -0
- package/dist/packages/core/src/agent/reasoning.js +44 -11
- package/dist/packages/core/src/agent/transactionManager.js +47 -0
- package/dist/packages/core/src/gateway/server.js +28 -1
- package/dist/packages/core/src/gateway/telegram.js +34 -1
- package/dist/packages/core/src/memory/logger.js +82 -0
- package/dist/packages/core/src/system/skills/analyzeDocument.js +15 -12
- package/dist/packages/core/src/system/skills/generateExcel.js +6 -8
- package/dist/packages/core/src/web3/aggregator/aggregatorMainnet.js +10 -3
- package/dist/packages/core/src/web3/aggregator/aggregatorTestnet.js +25 -11
- package/dist/packages/core/src/web3/eventListener.js +102 -0
- package/dist/packages/core/src/web3/skills/bridgeToken.js +1 -1
- package/dist/packages/core/src/web3/skills/createLimitOrder.js +48 -0
- 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/package.json +8 -3
- package/packages/core/package.json +4 -2
- package/packages/core/src/agent/bridgeWatcher.ts +39 -0
- package/packages/core/src/agent/reasoning.ts +54 -13
- package/packages/core/src/agent/transactionManager.ts +66 -0
- package/packages/core/src/gateway/server.ts +30 -1
- package/packages/core/src/gateway/telegram.ts +31 -1
- package/packages/core/src/memory/logger.ts +113 -0
- package/packages/core/src/system/skills/analyzeDocument.ts +15 -14
- package/packages/core/src/system/skills/generateExcel.ts +6 -10
- package/packages/core/src/web3/aggregator/aggregatorMainnet.ts +12 -3
- package/packages/core/src/web3/aggregator/aggregatorTestnet.ts +35 -13
- package/packages/core/src/web3/eventListener.ts +103 -0
- package/packages/core/src/web3/skills/bridgeToken.ts +1 -1
- package/packages/core/src/web3/skills/createLimitOrder.ts +56 -0
- 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 +13 -0
- 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/packages/dashboard/dist/assets/index-CCgm_N9M.js +0 -13
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.
|
|
@@ -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
|
+
}
|
|
@@ -31,6 +31,7 @@ const defiLending_1 = require("../web3/skills/defiLending");
|
|
|
31
31
|
const yieldVault_1 = require("../web3/skills/yieldVault");
|
|
32
32
|
const provideLiquidity_1 = require("../web3/skills/provideLiquidity");
|
|
33
33
|
const getTxHistory_1 = require("../web3/skills/getTxHistory");
|
|
34
|
+
const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
|
|
34
35
|
const updateProfile_1 = require("./updateProfile");
|
|
35
36
|
const updateSecurityPolicy_1 = require("../system/skills/updateSecurityPolicy");
|
|
36
37
|
const analyzeDocument_1 = require("../system/skills/analyzeDocument");
|
|
@@ -122,26 +123,35 @@ async function executeWithRetry(requestBuilder, maxRetries = 3) {
|
|
|
122
123
|
function getSystemPrompt() {
|
|
123
124
|
const config = (0, parser_1.loadConfig)();
|
|
124
125
|
const currentDateTime = new Date().toLocaleString('en-US', { timeZoneName: 'short' });
|
|
125
|
-
let basePrompt = `
|
|
126
|
+
let basePrompt = `[CORE DIRECTIVES]
|
|
127
|
+
You are an autonomous Web3 agent operating on EVM chains.
|
|
126
128
|
You are equipped with a native wallet.
|
|
127
129
|
The current real-world date and time is: ${currentDateTime}. Use this for any time-related questions.
|
|
130
|
+
Default Chain: ${config.agent.default_chain}
|
|
128
131
|
|
|
132
|
+
CRITICAL: You MUST use a Chain of Thought approach for every response. You must enclose your internal reasoning steps within <think>...</think> XML tags BEFORE taking any action or providing a final response. This allows you to plan your tool usage, recall the rules, and avoid hallucinations.
|
|
133
|
+
IMPORTANT: The <think> block is strictly for your internal hidden monologue. NEVER put your final answer, conversational text, or questions to the user inside the <think> block. The actual response that the user will see MUST be written OUTSIDE and AFTER the </think> tag.
|
|
134
|
+
|
|
135
|
+
[EXECUTION WORKFLOW]
|
|
129
136
|
CRITICAL RULE 1: NEVER expose internal JSON tool calls to the user. Always parse them and explain the outcome naturally.
|
|
130
137
|
CRITICAL RULE 2: STRICT LANGUAGE MATCHING. You MUST strictly reply in the exact same language as the user's LATEST prompt.
|
|
131
|
-
CRITICAL RULE 3: FORMATTING & CONCISENESS.
|
|
138
|
+
CRITICAL RULE 3: FORMATTING & CONCISENESS. Provide concise analytical summaries of data rather than just dumping raw markdown tables. Be analytical but brief. Use commas for thousands.
|
|
132
139
|
CRITICAL RULE 4: TOOL PRIORITIZATION. Web3 tasks must use Web3 Skills exclusively. OS Skills (search, browse) are fallbacks only. Use get_my_address to show wallet address, and check_portfolio to show balances.
|
|
133
140
|
CRITICAL RULE 5: DEFAULT CHAIN HANDLING. Default to: ${config.agent.default_chain} unless specified. If overridden, confirm the chain politely. For 2-chain txs (bridge), default source to ${config.agent.default_chain}.
|
|
134
|
-
CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
|
|
135
|
-
CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
|
|
136
141
|
CRITICAL RULE 8: CONDITIONAL PARALLEL EXECUTION. Parallel tool execution is ONLY allowed if there are zero data dependencies between them.
|
|
137
|
-
CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
|
|
138
142
|
CRITICAL RULE 10: PLANNING & RISK DISCLOSURE. For high-level instructions (e.g. "Get yield"), formulate a plan and briefly disclose major risks (smart contract risk, impermanent loss) before asking for approval.
|
|
139
|
-
CRITICAL RULE 11:
|
|
140
|
-
CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
|
|
143
|
+
CRITICAL RULE 11: ADAPTIVE RESPONSE RULE. You must process Web3 data (portfolio, price) and provide a concise, to-the-point analysis based on the context. Do not use useless filler greetings/closings. Provide deep analysis only if the data requires it to protect the user's portfolio.
|
|
141
144
|
CRITICAL RULE 13: WALLET CONTEXT CACHING. Portfolio data in chat history is potentially stale. Do not use cached data for transactional planning; refresh the balance via tools first.
|
|
142
145
|
CRITICAL RULE 14: TRANSACTION EXECUTION. For ALL state-changing transactions (swap, bridge, transfer, stake), do NOT ask for verbal confirmation. Execute the tool IMMEDIATELY. The tool itself will trigger a secure popup in the user's dashboard UI for final approval.
|
|
146
|
+
CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answer exists in recent verified context and freshness is not strictly required. Use history to save latency.
|
|
147
|
+
|
|
148
|
+
[ANTI-HALLUCINATION PROTOCOL]
|
|
149
|
+
CRITICAL RULE 6: NETWORK SAFETY VALIDATION. If a request implies cross-chain or mainnet/testnet mixing, or the token symbol is ambiguous (USDC vs USDC.e), YOU MUST NOT GUESS. Ask for confirmation.
|
|
150
|
+
CRITICAL RULE 7: TOOL CONFIDENCE & HALUCINATION PREVENTION. NEVER fabricate blockchain data. If a tool fails or data is missing, state it explicitly. Do not estimate balances, prices, APY, or gas.
|
|
151
|
+
CRITICAL RULE 9: DEFI CONFIGURATION FALLBACK. If a tool fails due to Rate Limits, Unauthorized, or Missing API Keys, instruct the user to visit the "DeFi Configuration 🔑" menu in the dashboard.
|
|
152
|
+
CRITICAL RULE 12: SMART SLIPPAGE AWARENESS. For low-liquidity assets, warn the user that default slippage might not be enough. NEVER invent specific slippage percentage numbers.
|
|
143
153
|
CRITICAL RULE 16: CAPABILITY HONESTY. NEVER claim a capability not available through installed tools. If asked for an unsupported action, state honestly that the skill is missing.
|
|
144
|
-
CRITICAL RULE
|
|
154
|
+
CRITICAL RULE 19: MARKET CONFIDENCE SCORE. When analyzing market data, token security, or preparing trades, you MUST explicitly declare a 'Confidence Score (0-100%)' INSIDE your <think> block. If your score is below 40%, you must firmly WARN the user and advise against the trade in your final response.`;
|
|
145
155
|
// Read IDENTITY.md for core AI persona
|
|
146
156
|
try {
|
|
147
157
|
const identityMdPath = (0, paths_1.getPath)('IDENTITY.md');
|
|
@@ -188,6 +198,23 @@ CRITICAL RULE 17: MINIMIZE UNNECESSARY TOOL CALLS. Do not call tools if the answ
|
|
|
188
198
|
catch (error) {
|
|
189
199
|
// Ignore db errors if not initialized
|
|
190
200
|
}
|
|
201
|
+
// V3: Inject Personalized Risk Profile
|
|
202
|
+
try {
|
|
203
|
+
const profile = exports.logger.getUserProfile();
|
|
204
|
+
if (profile) {
|
|
205
|
+
basePrompt += `\n\n--- [USER_PERSONA] RISK PROFILE & PREFERENCES ---\n`;
|
|
206
|
+
basePrompt += `Risk Level: ${profile.risk_level}\n`;
|
|
207
|
+
basePrompt += `Max Slippage Tolerance: ${profile.max_slippage}%\n`;
|
|
208
|
+
basePrompt += `Avoid Memecoins: ${profile.avoid_memecoins ? 'YES' : 'NO'}\n`;
|
|
209
|
+
if (profile.custom_rules) {
|
|
210
|
+
basePrompt += `Custom Rules: ${profile.custom_rules}\n`;
|
|
211
|
+
}
|
|
212
|
+
basePrompt += `CRITICAL: You MUST adhere to these risk parameters when advising the user or executing tools. If a requested action violates these parameters (e.g., buying a high-risk memecoin when 'Avoid Memecoins' is YES), you MUST warn the user and refuse execution unless they explicitly override.\n`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
// Ignore if db not ready
|
|
217
|
+
}
|
|
191
218
|
return basePrompt;
|
|
192
219
|
}
|
|
193
220
|
async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
@@ -223,7 +250,7 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
223
250
|
const hasGoogleKeyword = /email|gmail|calendar|sheet|doc|form|event/i.test(lowerInput);
|
|
224
251
|
let tools = [];
|
|
225
252
|
if ((0, skillManager_1.isSkillActive)('web3')) {
|
|
226
|
-
tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition);
|
|
253
|
+
tools.push(getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition, bridgeToken_1.bridgeTokenToolDefinition, mintNft_1.mintNftToolDefinition, customTx_1.customTxToolDefinition, checkSecurity_1.checkSecurityToolDefinition, marketAnalysis_1.marketAnalysisToolDefinition, checkPortfolio_1.checkPortfolioToolDefinition, checkAddress_1.checkAddressToolDefinition, getMyAddress_1.getMyAddressToolDefinition, manageCustomTokens_1.manageCustomTokensDefinition, revokeApprovals_1.revokeApprovalToolDefinition, defiLending_1.aaveSupplyToolDefinition, yieldVault_1.vaultDepositToolDefinition, provideLiquidity_1.provideLiquidityToolDefinition, getTxHistory_1.getTxHistoryToolDefinition, createLimitOrder_1.createLimitOrderToolDefinition);
|
|
227
254
|
}
|
|
228
255
|
const SYSTEM_TOOLS = [updateProfile_1.updateProfileToolDefinition, updateSecurityPolicy_1.updateSecurityPolicyToolDefinition, analyzeDocument_1.analyzeDocumentToolDefinition, readFile_1.readLocalFileToolDefinition, writeFile_1.writeLocalFileToolDefinition, generateExcel_1.generateExcelToolDefinition, executeShell_1.runTerminalCommandToolDefinition, browseWeb_1.browseWebsiteToolDefinition, searchWeb_1.searchWebToolDefinition, installSkill_1.installExternalSkillToolDefinition, editFile_1.editLocalFileToolDefinition, gitManager_1.gitManagerToolDefinition, xManager_1.xManagerToolDefinition, notionWorkspace_1.notionWorkspaceToolDefinition, audioTranscribe_1.audioTranscribeToolDefinition, summarizeText_1.summarizeTextToolDefinition];
|
|
229
256
|
const GOOGLE_TOOLS = [googleWorkspace_1.readGmailInboxToolDefinition, googleWorkspace_1.listCalendarEventsToolDefinition, googleWorkspace_1.appendRowToSheetsToolDefinition, googleWorkspace_1.readGoogleDocsToolDefinition, googleWorkspace_1.readGoogleFormResponsesToolDefinition];
|
|
@@ -261,9 +288,11 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
261
288
|
if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
|
|
262
289
|
let canFastReturnAll = true;
|
|
263
290
|
let accumulatedResults = [];
|
|
291
|
+
// Enabled fastReturnTools to eliminate 2nd LLM latency for transaction popups
|
|
264
292
|
const fastReturnTools = [
|
|
265
|
-
'
|
|
266
|
-
'
|
|
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'
|
|
267
296
|
];
|
|
268
297
|
for (const _toolCall of responseMessage.tool_calls) {
|
|
269
298
|
const toolCall = _toolCall;
|
|
@@ -377,6 +406,10 @@ async function processUserInput(input, role = 'user', onProgress, sessionId) {
|
|
|
377
406
|
result = await (0, getTxHistory_1.getTxHistory)(args.chainName, args.address, args.days);
|
|
378
407
|
break;
|
|
379
408
|
}
|
|
409
|
+
case 'create_limit_order': {
|
|
410
|
+
result = await (0, createLimitOrder_1.createLimitOrder)(args.tokenSymbol, args.tokenAddress, args.triggerCondition, args.triggerPriceUsd, args.action, args.amountUsd, args.slippageTolerance);
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
380
413
|
case 'update_profile': {
|
|
381
414
|
result = (0, updateProfile_1.updateProfile)(args.content, args.mode);
|
|
382
415
|
break;
|
|
@@ -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();
|
|
@@ -51,6 +51,7 @@ const yieldVault_1 = require("../web3/skills/yieldVault");
|
|
|
51
51
|
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
|
+
const createLimitOrder_1 = require("../web3/skills/createLimitOrder");
|
|
54
55
|
// System Skills
|
|
55
56
|
const browseWeb_1 = require("../system/skills/browseWeb");
|
|
56
57
|
const executeShell_1 = require("../system/skills/executeShell");
|
|
@@ -69,6 +70,8 @@ const analyzeDocument_1 = require("../system/skills/analyzeDocument");
|
|
|
69
70
|
const searchWeb_1 = require("../system/skills/searchWeb");
|
|
70
71
|
const googleWorkspace_1 = require("../system/skills/googleWorkspace");
|
|
71
72
|
const telegram_1 = require("./telegram");
|
|
73
|
+
const bridgeWatcher_1 = require("../agent/bridgeWatcher");
|
|
74
|
+
const eventListener_1 = require("../web3/eventListener");
|
|
72
75
|
const googleAuthModule_1 = require("./googleAuthModule");
|
|
73
76
|
const legalGenerator_1 = require("./legalGenerator");
|
|
74
77
|
const episodic_1 = require("../memory/episodic");
|
|
@@ -309,7 +312,8 @@ app.get('/api/skills', (req, res) => {
|
|
|
309
312
|
revokeApprovals_2.revokeApprovalToolDefinition,
|
|
310
313
|
yieldVault_1.vaultDepositToolDefinition,
|
|
311
314
|
provideLiquidity_1.provideLiquidityToolDefinition,
|
|
312
|
-
getTxHistory_1.getTxHistoryToolDefinition
|
|
315
|
+
getTxHistory_1.getTxHistoryToolDefinition,
|
|
316
|
+
createLimitOrder_1.createLimitOrderToolDefinition
|
|
313
317
|
];
|
|
314
318
|
const skillsWithStatus = allSkills.map(skill => ({
|
|
315
319
|
...skill,
|
|
@@ -778,6 +782,25 @@ app.delete('/api/memory/:id', (req, res) => {
|
|
|
778
782
|
res.status(500).json({ error: error.message });
|
|
779
783
|
}
|
|
780
784
|
});
|
|
785
|
+
// --- User Persona / Risk Profile Endpoints (V3) ---
|
|
786
|
+
app.get('/api/profile', (req, res) => {
|
|
787
|
+
try {
|
|
788
|
+
const profile = reasoning_1.logger.getUserProfile();
|
|
789
|
+
res.json(profile || { risk_level: 'Moderate', max_slippage: 1.0, avoid_memecoins: false, custom_rules: '' });
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
res.status(500).json({ error: error.message });
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
app.post('/api/profile', (req, res) => {
|
|
796
|
+
try {
|
|
797
|
+
reasoning_1.logger.updateUserProfile(req.body);
|
|
798
|
+
res.json({ success: true });
|
|
799
|
+
}
|
|
800
|
+
catch (error) {
|
|
801
|
+
res.status(500).json({ error: error.message });
|
|
802
|
+
}
|
|
803
|
+
});
|
|
781
804
|
// Fallback for React Router (Single Page Application)
|
|
782
805
|
app.use((req, res, next) => {
|
|
783
806
|
if (req.method === 'GET' && !req.path.startsWith('/api')) {
|
|
@@ -827,6 +850,10 @@ function startServer() {
|
|
|
827
850
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
828
851
|
// Start the Telegram bot listener
|
|
829
852
|
(0, telegram_1.startTelegramBot)();
|
|
853
|
+
// Start Asynchronous Bridge Watcher
|
|
854
|
+
(0, bridgeWatcher_1.startBridgeWatcher)();
|
|
855
|
+
// Start Event Listener for Limit Orders (V3)
|
|
856
|
+
eventListener_1.eventListener.start();
|
|
830
857
|
});
|
|
831
858
|
server.on('error', (e) => {
|
|
832
859
|
if (e.code === 'EADDRINUSE') {
|
|
@@ -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 = '';
|
|
@@ -201,6 +206,15 @@ function startTelegramBot() {
|
|
|
201
206
|
else if (tx.type === 'revokeApproval') {
|
|
202
207
|
result = await (0, revokeApprovals_1.executeRevokeApproval)(tx.chainName, tx.details, true);
|
|
203
208
|
}
|
|
209
|
+
else if (tx.type === 'limit_order') {
|
|
210
|
+
const success = reasoning_1.logger.activateLimitOrder(tx.details.orderId);
|
|
211
|
+
if (success) {
|
|
212
|
+
result = `Limit Order ${tx.details.orderId} is now ACTIVE. The Event-Driven Engine is monitoring the market.`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
throw new Error(`Failed to activate Limit Order. ID not found in database.`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
204
218
|
transactionManager_1.txManager.updateStatus(txId, 'executed', result);
|
|
205
219
|
// Pass session history to formatTransactionSuccess to detect language
|
|
206
220
|
const sessionId = ctx.chat?.id.toString() || 'default';
|
|
@@ -248,3 +262,22 @@ function startTelegramBot() {
|
|
|
248
262
|
console.error('[Telegram] Failed to initialize bot:', error);
|
|
249
263
|
}
|
|
250
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
|
+
}
|
|
@@ -53,6 +53,34 @@ class Logger {
|
|
|
53
53
|
// Phase 1: SQLite Index Optimization
|
|
54
54
|
this.db.exec(`
|
|
55
55
|
CREATE INDEX IF NOT EXISTS idx_session_id ON messages(session_id);
|
|
56
|
+
`);
|
|
57
|
+
// V3: Limit Orders & Event-Driven Engine
|
|
58
|
+
this.db.exec(`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS limit_orders (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
token_address TEXT NOT NULL,
|
|
62
|
+
token_symbol TEXT NOT NULL,
|
|
63
|
+
trigger_condition TEXT NOT NULL,
|
|
64
|
+
trigger_price_usd REAL NOT NULL,
|
|
65
|
+
action TEXT NOT NULL,
|
|
66
|
+
amount_usd REAL NOT NULL,
|
|
67
|
+
slippage_tolerance REAL DEFAULT 5.0,
|
|
68
|
+
status TEXT DEFAULT 'ACTIVE',
|
|
69
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
70
|
+
expires_at DATETIME,
|
|
71
|
+
tx_hash TEXT
|
|
72
|
+
)
|
|
73
|
+
`);
|
|
74
|
+
// V3: Personalized Risk Profile
|
|
75
|
+
this.db.exec(`
|
|
76
|
+
CREATE TABLE IF NOT EXISTS user_profiles (
|
|
77
|
+
id TEXT PRIMARY KEY,
|
|
78
|
+
risk_level TEXT DEFAULT 'Moderate',
|
|
79
|
+
max_slippage REAL DEFAULT 1.0,
|
|
80
|
+
avoid_memecoins BOOLEAN DEFAULT 0,
|
|
81
|
+
custom_rules TEXT,
|
|
82
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
83
|
+
)
|
|
56
84
|
`);
|
|
57
85
|
// Ensure session_id exists for older DBs
|
|
58
86
|
try {
|
|
@@ -193,6 +221,60 @@ class Logger {
|
|
|
193
221
|
console.error('[Nyxora Memory] Error closing database:', e);
|
|
194
222
|
}
|
|
195
223
|
}
|
|
224
|
+
// V3: User Persona & Risk Profile
|
|
225
|
+
getUserProfile() {
|
|
226
|
+
try {
|
|
227
|
+
const row = this.db.prepare('SELECT * FROM user_profiles WHERE id = ?').get('default');
|
|
228
|
+
if (row) {
|
|
229
|
+
return {
|
|
230
|
+
id: row.id,
|
|
231
|
+
risk_level: row.risk_level,
|
|
232
|
+
max_slippage: row.max_slippage,
|
|
233
|
+
avoid_memecoins: Boolean(row.avoid_memecoins),
|
|
234
|
+
custom_rules: row.custom_rules
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
updateUserProfile(profile) {
|
|
244
|
+
const existing = this.getUserProfile() || {
|
|
245
|
+
id: 'default',
|
|
246
|
+
risk_level: 'Moderate',
|
|
247
|
+
max_slippage: 1.0,
|
|
248
|
+
avoid_memecoins: false,
|
|
249
|
+
custom_rules: null
|
|
250
|
+
};
|
|
251
|
+
const updated = { ...existing, ...profile };
|
|
252
|
+
this.db.prepare(`
|
|
253
|
+
INSERT INTO user_profiles (id, risk_level, max_slippage, avoid_memecoins, custom_rules, updated_at)
|
|
254
|
+
VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
255
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
256
|
+
risk_level = excluded.risk_level,
|
|
257
|
+
max_slippage = excluded.max_slippage,
|
|
258
|
+
avoid_memecoins = excluded.avoid_memecoins,
|
|
259
|
+
custom_rules = excluded.custom_rules,
|
|
260
|
+
updated_at = excluded.updated_at
|
|
261
|
+
`).run('default', updated.risk_level, updated.max_slippage, updated.avoid_memecoins ? 1 : 0, updated.custom_rules);
|
|
262
|
+
}
|
|
263
|
+
// V3: Limit Orders
|
|
264
|
+
createLimitOrder(order) {
|
|
265
|
+
const id = crypto_1.default.randomUUID();
|
|
266
|
+
this.db.prepare(`
|
|
267
|
+
INSERT INTO limit_orders (
|
|
268
|
+
id, token_address, token_symbol, trigger_condition, trigger_price_usd, action, amount_usd, slippage_tolerance, status
|
|
269
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
270
|
+
`).run(id, order.token_address, order.token_symbol, order.trigger_condition, order.trigger_price_usd, order.action, order.amount_usd, order.slippage_tolerance || 5.0, 'PENDING_APPROVAL' // Requires user approval in Dashboard/Telegram
|
|
271
|
+
);
|
|
272
|
+
return id;
|
|
273
|
+
}
|
|
274
|
+
activateLimitOrder(orderId) {
|
|
275
|
+
const result = this.db.prepare(`UPDATE limit_orders SET status = 'ACTIVE' WHERE id = ?`).run(orderId);
|
|
276
|
+
return result.changes > 0;
|
|
277
|
+
}
|
|
196
278
|
}
|
|
197
279
|
exports.Logger = Logger;
|
|
198
280
|
exports.logger = new Logger();
|
|
@@ -91,22 +91,25 @@ async function analyzeDocument(filePath) {
|
|
|
91
91
|
return text;
|
|
92
92
|
}
|
|
93
93
|
if (ext === '.xlsx' || ext === '.csv') {
|
|
94
|
-
|
|
95
|
-
const workbook = new ExcelJS.Workbook();
|
|
94
|
+
let text = `--- Spreadsheet Data from ${path_1.default.basename(absolutePath)} ---\n`;
|
|
96
95
|
if (ext === '.csv') {
|
|
97
|
-
await
|
|
96
|
+
const { parse } = await Promise.resolve().then(() => __importStar(require('csv-parse/sync')));
|
|
97
|
+
const content = fs_1.default.readFileSync(absolutePath, 'utf8');
|
|
98
|
+
const records = parse(content, { skip_empty_lines: true });
|
|
99
|
+
records.forEach((row) => {
|
|
100
|
+
text += row.join(',') + '\n';
|
|
101
|
+
});
|
|
98
102
|
}
|
|
99
103
|
else {
|
|
100
|
-
await
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
text += values.join(',') + '\n';
|
|
104
|
+
const readXlsxFile = (await Promise.resolve().then(() => __importStar(require('read-excel-file/node')))).default;
|
|
105
|
+
const sheets = await readXlsxFile(absolutePath);
|
|
106
|
+
sheets.forEach((s) => {
|
|
107
|
+
text += `\n[Sheet: ${s.sheet}]\n`;
|
|
108
|
+
s.data.forEach((row) => {
|
|
109
|
+
text += row.map(cell => cell === null ? '' : String(cell)).join(',') + '\n';
|
|
110
|
+
});
|
|
108
111
|
});
|
|
109
|
-
}
|
|
112
|
+
}
|
|
110
113
|
if (text.length > 20000) {
|
|
111
114
|
text = text.substring(0, 20000) + "... [Content Truncated]";
|
|
112
115
|
}
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.generateExcelToolDefinition = void 0;
|
|
7
7
|
exports.generateExcelFile = generateExcelFile;
|
|
8
|
-
const
|
|
8
|
+
const node_1 = __importDefault(require("write-excel-file/node"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
async function generateExcelFile(data, filePath) {
|
|
@@ -19,14 +19,12 @@ async function generateExcelFile(data, filePath) {
|
|
|
19
19
|
if (!reportData || reportData.length === 0) {
|
|
20
20
|
reportData = [{ Message: 'No data available' }];
|
|
21
21
|
}
|
|
22
|
-
const workbook = new exceljs_1.default.Workbook();
|
|
23
|
-
const worksheet = workbook.addWorksheet('Report');
|
|
24
22
|
const headers = Object.keys(reportData[0]);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
await
|
|
23
|
+
const excelData = [
|
|
24
|
+
headers.map(h => ({ value: h, fontWeight: 'bold' })),
|
|
25
|
+
...reportData.map(row => headers.map(h => ({ value: String(row[h] || '') })))
|
|
26
|
+
];
|
|
27
|
+
await (0, node_1.default)(excelData).toFile(absolutePath);
|
|
30
28
|
return `Success: Excel file generated at ${absolutePath}`;
|
|
31
29
|
}
|
|
32
30
|
catch (error) {
|
|
@@ -167,12 +167,17 @@ async function fetchLifi(fromChain, toChain, fromToken, toToken, amount, address
|
|
|
167
167
|
}
|
|
168
168
|
async function fetchRelay(fromChain, toChain, fromToken, toToken, amount, address, slippage, key) {
|
|
169
169
|
try {
|
|
170
|
+
// Relay API strictly requires the zero address for Native ETH instead of 0xeeee...
|
|
171
|
+
const relayOriginCurrency = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
172
|
+
? '0x0000000000000000000000000000000000000000' : fromToken;
|
|
173
|
+
const relayDestCurrency = toToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
174
|
+
? '0x0000000000000000000000000000000000000000' : toToken;
|
|
170
175
|
const payload = {
|
|
171
176
|
user: address,
|
|
172
177
|
originChainId: CHAIN_IDS[fromChain].toString(),
|
|
173
178
|
destinationChainId: CHAIN_IDS[toChain].toString(),
|
|
174
|
-
originCurrency:
|
|
175
|
-
destinationCurrency:
|
|
179
|
+
originCurrency: relayOriginCurrency,
|
|
180
|
+
destinationCurrency: relayDestCurrency,
|
|
176
181
|
recipient: address,
|
|
177
182
|
tradeType: 'EXACT_INPUT',
|
|
178
183
|
amount: amount,
|
|
@@ -233,6 +238,8 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
|
|
|
233
238
|
if (!buildRes.ok)
|
|
234
239
|
throw new Error(await buildRes.text());
|
|
235
240
|
const buildData = await buildRes.json();
|
|
241
|
+
const isNative = fromToken.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ||
|
|
242
|
+
fromToken === '0x0000000000000000000000000000000000000000';
|
|
236
243
|
return {
|
|
237
244
|
provider: 'KyberSwap',
|
|
238
245
|
expectedOutput: (Number(routeData.data.routeSummary.amountOut) / 1e18).toString(),
|
|
@@ -241,7 +248,7 @@ async function fetchKyberSwap(fromChain, fromToken, toToken, amount, address, sl
|
|
|
241
248
|
txPayload: {
|
|
242
249
|
to: buildData.data.routerAddress,
|
|
243
250
|
data: buildData.data.data,
|
|
244
|
-
value:
|
|
251
|
+
value: isNative ? amount : "0" // FIXED: Mengirim jumlah asli dalam WEI jika Native ETH, atau 0 jika ERC20
|
|
245
252
|
},
|
|
246
253
|
rawQuote: buildData.data
|
|
247
254
|
};
|