nyxora 1.7.2 → 1.7.3
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/CHANGELOG.md +17 -1
- package/README.md +1 -0
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/config/parser.ts +9 -2
- package/packages/core/src/web3/skills/swapToken.ts +42 -14
- package/packages/core/src/web3/utils/routers.ts +102 -0
- package/packages/dashboard/dist/assets/index-C1Guh5O8.js +300 -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-cGPka4s1.js +0 -295
package/CHANGELOG.md
CHANGED
|
@@ -5,11 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [1.7.
|
|
8
|
+
## [1.7.3] - Unreleased
|
|
9
|
+
|
|
10
|
+
### Web3 Routing & Integrations
|
|
11
|
+
- **Multi-Router Selection (DeFi)**: Added a dynamic Router dropdown to the Dashboard UI, allowing users to forcefully route transactions through specific protocols natively. Supported routers include `1inch`, `CowSwap (MEV-Protected)`, `Li.Fi`, `Relay`, `Uniswap V2`, `Uniswap V3`, and `PancakeSwap`. This integration heavily relies on deep aggregator proxying (bypassing the need for complex V2/V3 ABI calldata overhead) to ensure 100% smooth, anti-fail execution without requiring additional API keys.
|
|
12
|
+
|
|
13
|
+
### Security & Polish
|
|
14
|
+
- **Dashboard:** Redacted the sensitive Nyxora Auth Token from appearing in the Gateway Logs component on the frontend to prevent visual leakage during screen sharing or screenshots.
|
|
15
|
+
|
|
16
|
+
## [1.7.2] - 2026-06-04
|
|
9
17
|
|
|
10
18
|
### UI/UX Enhancements
|
|
11
19
|
- **Google Workspace Logout**: Users can now easily disconnect their Google Workspace accounts directly from the Dashboard (OS Skills tab). This triggers a secure token purge from both the OS Keyring and local storage, ensuring privacy and seamless account switching.
|
|
12
20
|
|
|
21
|
+
### Cloud-Native Deployment
|
|
22
|
+
- **Official Docker & GHCR Support**: Added comprehensive Docker containerization support (`Dockerfile`) and automated publishing pipelines to GitHub Container Registry (GHCR).
|
|
23
|
+
- **Docker Documentation**: Added a dedicated `DOCKER.md` guide explaining how to pull, interactively configure, and run the Nyxora daemon via Docker with isolated Volume storage (`/root/.nyxora`).
|
|
24
|
+
|
|
25
|
+
### Documentation & Compliance
|
|
26
|
+
- **Legal Infrastructure**: Added standard `Privacy Policy` (`privacy.md`) and `Terms of Service` (`terms.md`) to the VitePress documentation to prepare for official Google OAuth App Verification.
|
|
27
|
+
- **Enterprise Roadmap Evolution**: Updated the documentation roadmap to reflect our "Nyxora Next Update" vision, outlining future plans for a Rust-Native Signer, Idempotent Policy Engine, Multi-VM Architecture, and Google App Verification.
|
|
28
|
+
|
|
13
29
|
## [1.7.1]
|
|
14
30
|
|
|
15
31
|
### CLI Enhancements
|
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ It operates under an institutional-grade **Cryptographically Bound Human-in-the-
|
|
|
40
40
|
* **Zero-Click Multi-Session**: Instantly create isolated chat sessions with smart auto-naming triggered by your first prompt, exactly like ChatGPT.
|
|
41
41
|
* **Dynamic Trending Tokens**: Live top 5 crypto assets feed directly injected into the dashboard, completely clickable for instant AI market analysis.
|
|
42
42
|
* **Premium Utility-Centric UI**: A sleek, dark-themed dashboard built for high readability and professional Web3 execution, featuring Pseudo-Generative UI widgets (`<BalanceWidget>`, `<MarketWidget>`, `<SwapWidget>`).
|
|
43
|
+
* **Context Overrides Defaults (NLP Intelligence)**: The Dashboard configuration (default chain & router) acts only as a safety net. If you issue an explicit command via Telegram (e.g., *"Swap 10 USDC to USDT on Arbitrum using Li.Fi"*), the NLP engine dynamically bypasses the default settings and executes exactly what you asked for, ensuring maximum flexibility.
|
|
43
44
|
* **Deep Personalization**: Feed the agent custom rules via `user.md` and define its core persona via `IDENTITY.md`.
|
|
44
45
|
|
|
45
46
|
---
|
package/package.json
CHANGED
|
@@ -39,7 +39,9 @@ export async function saveApiKeys(newKeys: Record<string, string>): Promise<void
|
|
|
39
39
|
export interface NyxoraConfig {
|
|
40
40
|
agent: {
|
|
41
41
|
name: string;
|
|
42
|
+
description: string;
|
|
42
43
|
default_chain: string;
|
|
44
|
+
default_router?: string;
|
|
43
45
|
};
|
|
44
46
|
llm: {
|
|
45
47
|
provider: 'openai' | 'anthropic' | 'ollama' | 'gemini' | 'openrouter';
|
|
@@ -136,7 +138,7 @@ export function loadConfig(): NyxoraConfig {
|
|
|
136
138
|
|
|
137
139
|
// Merge with defaults
|
|
138
140
|
return {
|
|
139
|
-
agent: parsed.agent || { name: 'Nyxora-Default', default_chain: 'base' },
|
|
141
|
+
agent: parsed.agent || { name: 'Nyxora-Default', description: 'An autonomous agent running on your local machine.', default_chain: 'base', default_router: 'auto' },
|
|
140
142
|
llm: parsed.llm || {
|
|
141
143
|
provider: 'openai',
|
|
142
144
|
model: 'gpt-4o-mini',
|
|
@@ -165,7 +167,12 @@ export function loadConfig(): NyxoraConfig {
|
|
|
165
167
|
console.error('[Config] Failed to load config.yaml. Using default configuration.', error);
|
|
166
168
|
}
|
|
167
169
|
return {
|
|
168
|
-
agent: {
|
|
170
|
+
agent: {
|
|
171
|
+
name: "Nyxora-Default",
|
|
172
|
+
description: "An autonomous agent running on your local machine.",
|
|
173
|
+
default_chain: "ethereum",
|
|
174
|
+
default_router: "auto"
|
|
175
|
+
},
|
|
169
176
|
llm: {
|
|
170
177
|
provider: 'openai',
|
|
171
178
|
model: 'gpt-4o-mini',
|
|
@@ -3,7 +3,8 @@ import { getPublicClient, getAddress, ChainName, SUPPORTED_CHAIN_NAMES } from '.
|
|
|
3
3
|
import { txManager } from '../../agent/transactionManager';
|
|
4
4
|
import { resolveToken, ERC20_ABI } from '../utils/tokens';
|
|
5
5
|
import { saveTokenToWhitelist } from '../../utils/userWhitelistManager';
|
|
6
|
-
import
|
|
6
|
+
import { loadConfig } from '../../config/parser';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
7
8
|
|
|
8
9
|
const CHAIN_IDS: Record<string, number> = {
|
|
9
10
|
ethereum: 1,
|
|
@@ -15,7 +16,7 @@ const CHAIN_IDS: Record<string, number> = {
|
|
|
15
16
|
polygon: 137,
|
|
16
17
|
};
|
|
17
18
|
|
|
18
|
-
async function getLifiQuote(fromChainId: number, toChainId: number, fromToken: string, toToken: string, amountWei: string, userAddress: string, slippage: number) {
|
|
19
|
+
async function getLifiQuote(fromChainId: number, toChainId: number, fromToken: string, toToken: string, amountWei: string, userAddress: string, slippage: number, providerName?: string) {
|
|
19
20
|
const url = new URL('https://li.quest/v1/quote');
|
|
20
21
|
url.searchParams.append('fromChain', fromChainId.toString());
|
|
21
22
|
url.searchParams.append('toChain', toChainId.toString());
|
|
@@ -24,6 +25,21 @@ async function getLifiQuote(fromChainId: number, toChainId: number, fromToken: s
|
|
|
24
25
|
url.searchParams.append('fromAmount', amountWei);
|
|
25
26
|
url.searchParams.append('fromAddress', userAddress);
|
|
26
27
|
url.searchParams.append('slippage', slippage.toString());
|
|
28
|
+
|
|
29
|
+
// Specific Exchange forcing (Native-feel integration via Aggregator constraint)
|
|
30
|
+
if (providerName && providerName !== 'lifi' && providerName !== 'auto') {
|
|
31
|
+
// Map our internal names to Li.Fi exchange names
|
|
32
|
+
const exchangeMap: Record<string, string> = {
|
|
33
|
+
'uniswap_v2': 'uniswap_v2',
|
|
34
|
+
'uniswap_v3': 'uniswap_v3',
|
|
35
|
+
'pancakeswap': 'pancakeswap',
|
|
36
|
+
'1inch': 'oneinch',
|
|
37
|
+
'cowswap': 'cowswap'
|
|
38
|
+
};
|
|
39
|
+
if (exchangeMap[providerName]) {
|
|
40
|
+
url.searchParams.append('allowExchanges', exchangeMap[providerName]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
27
43
|
|
|
28
44
|
const res = await fetch(url.toString());
|
|
29
45
|
if (!res.ok) {
|
|
@@ -65,7 +81,7 @@ export async function prepareSwapToken(
|
|
|
65
81
|
toToken: string,
|
|
66
82
|
amountStr: string,
|
|
67
83
|
mode: "auto" | "manual" = "auto",
|
|
68
|
-
providerName: "lifi" | "relay" = "
|
|
84
|
+
providerName: "auto" | "lifi" | "relay" | "uniswap_v2" | "uniswap_v3" | "pancakeswap" | "1inch" | "cowswap" = "auto",
|
|
69
85
|
slippagePercent: number = 0.5
|
|
70
86
|
): Promise<string> {
|
|
71
87
|
try {
|
|
@@ -101,7 +117,17 @@ export async function prepareSwapToken(
|
|
|
101
117
|
let approvalAddress: string | null = null;
|
|
102
118
|
let expectedOutputStr = "";
|
|
103
119
|
|
|
104
|
-
|
|
120
|
+
// If auto, read from global configuration set by Dashboard UI
|
|
121
|
+
let actualProvider = mode === "auto" ? "auto" : providerName;
|
|
122
|
+
if (actualProvider === "auto") {
|
|
123
|
+
try {
|
|
124
|
+
const config = loadConfig();
|
|
125
|
+
actualProvider = (config.agent.default_router as any) || "lifi";
|
|
126
|
+
if (actualProvider === "auto") actualProvider = "lifi"; // strict fallback
|
|
127
|
+
} catch (e) {
|
|
128
|
+
actualProvider = "lifi";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
105
131
|
const isTestnet = chainId === 11155111;
|
|
106
132
|
|
|
107
133
|
// --- SEPOLIA TESTNET MOCK ---
|
|
@@ -121,14 +147,7 @@ export async function prepareSwapToken(
|
|
|
121
147
|
}
|
|
122
148
|
// --- END MOCK ---
|
|
123
149
|
|
|
124
|
-
if (actualProvider === "
|
|
125
|
-
const quote = await getLifiQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, userAddress, slippagePercent / 100);
|
|
126
|
-
txRequest = quote.transactionRequest;
|
|
127
|
-
approvalAddress = quote.estimate.approvalAddress;
|
|
128
|
-
|
|
129
|
-
const toDecimals = quote.action.toToken.decimals;
|
|
130
|
-
expectedOutputStr = formatUnits(BigInt(quote.estimate.toAmount), toDecimals);
|
|
131
|
-
} else if (actualProvider === "relay") {
|
|
150
|
+
if (actualProvider === "relay") {
|
|
132
151
|
const relayQuote = await getRelayQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, userAddress);
|
|
133
152
|
if (!relayQuote.steps || relayQuote.steps.length === 0) throw new Error("No route found by Relay.");
|
|
134
153
|
|
|
@@ -144,6 +163,15 @@ export async function prepareSwapToken(
|
|
|
144
163
|
}
|
|
145
164
|
|
|
146
165
|
expectedOutputStr = relayQuote.details?.currencyOut?.amountFormatted || "Unknown";
|
|
166
|
+
} else {
|
|
167
|
+
// Use Li.Fi for lifi, 1inch, cowswap, uniswap_v2, uniswap_v3, pancakeswap
|
|
168
|
+
// We mapped the allowExchanges inside getLifiQuote
|
|
169
|
+
const quote = await getLifiQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, userAddress, slippagePercent / 100, actualProvider);
|
|
170
|
+
txRequest = quote.transactionRequest;
|
|
171
|
+
approvalAddress = quote.estimate.approvalAddress;
|
|
172
|
+
|
|
173
|
+
const toDecimals = quote.action.toToken.decimals;
|
|
174
|
+
expectedOutputStr = formatUnits(BigInt(quote.estimate.toAmount), toDecimals);
|
|
147
175
|
}
|
|
148
176
|
|
|
149
177
|
// Check allowance early so we know if we need to auto-approve
|
|
@@ -250,11 +278,11 @@ export const swapTokenToolDefinition = {
|
|
|
250
278
|
mode: {
|
|
251
279
|
type: "string",
|
|
252
280
|
enum: ["auto", "manual"],
|
|
253
|
-
description: "auto uses
|
|
281
|
+
description: "auto uses default router. manual uses the specified provider."
|
|
254
282
|
},
|
|
255
283
|
providerName: {
|
|
256
284
|
type: "string",
|
|
257
|
-
enum: ["lifi", "relay"],
|
|
285
|
+
enum: ["auto", "lifi", "relay", "uniswap_v2", "uniswap_v3", "pancakeswap", "1inch", "cowswap"],
|
|
258
286
|
description: "Used if mode is manual."
|
|
259
287
|
}
|
|
260
288
|
},
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export const ROUTER_ADDRESSES: Record<string, Record<string, string>> = {
|
|
2
|
+
uniswap_v2: {
|
|
3
|
+
ethereum: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
|
|
4
|
+
},
|
|
5
|
+
uniswap_v3: {
|
|
6
|
+
ethereum: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
|
|
7
|
+
base: '0x2626664c2603336E57B271c5C0b26F421741e481',
|
|
8
|
+
arbitrum: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
|
|
9
|
+
optimism: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
|
|
10
|
+
polygon: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45'
|
|
11
|
+
},
|
|
12
|
+
pancakeswap: {
|
|
13
|
+
bsc: '0x10ED43C718714eb63d5aA57B78B54704E256024E'
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const UNISWAP_V2_ROUTER_ABI = [
|
|
18
|
+
{
|
|
19
|
+
"inputs": [
|
|
20
|
+
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
|
|
21
|
+
{ "internalType": "uint256", "name": "amountOutMin", "type": "uint256" },
|
|
22
|
+
{ "internalType": "address[]", "name": "path", "type": "address[]" },
|
|
23
|
+
{ "internalType": "address", "name": "to", "type": "address" },
|
|
24
|
+
{ "internalType": "uint256", "name": "deadline", "type": "uint256" }
|
|
25
|
+
],
|
|
26
|
+
"name": "swapExactTokensForTokens",
|
|
27
|
+
"outputs": [{ "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }],
|
|
28
|
+
"stateMutability": "nonpayable",
|
|
29
|
+
"type": "function"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"inputs": [
|
|
33
|
+
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
|
|
34
|
+
{ "internalType": "address[]", "name": "path", "type": "address[]" }
|
|
35
|
+
],
|
|
36
|
+
"name": "getAmountsOut",
|
|
37
|
+
"outputs": [{ "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }],
|
|
38
|
+
"stateMutability": "view",
|
|
39
|
+
"type": "function"
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const UNISWAP_V3_ROUTER_ABI = [
|
|
44
|
+
{
|
|
45
|
+
"inputs": [
|
|
46
|
+
{
|
|
47
|
+
"components": [
|
|
48
|
+
{ "internalType": "address", "name": "tokenIn", "type": "address" },
|
|
49
|
+
{ "internalType": "address", "name": "tokenOut", "type": "address" },
|
|
50
|
+
{ "internalType": "uint24", "name": "fee", "type": "uint24" },
|
|
51
|
+
{ "internalType": "address", "name": "recipient", "type": "address" },
|
|
52
|
+
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
|
|
53
|
+
{ "internalType": "uint256", "name": "amountOutMinimum", "type": "uint256" },
|
|
54
|
+
{ "internalType": "uint160", "name": "sqrtPriceLimitX96", "type": "uint160" }
|
|
55
|
+
],
|
|
56
|
+
"internalType": "struct IV3SwapRouter.ExactInputSingleParams",
|
|
57
|
+
"name": "params",
|
|
58
|
+
"type": "tuple"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"name": "exactInputSingle",
|
|
62
|
+
"outputs": [{ "internalType": "uint256", "name": "amountOut", "type": "uint256" }],
|
|
63
|
+
"stateMutability": "payable",
|
|
64
|
+
"type": "function"
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
export const UNISWAP_V3_QUOTER_ABI = [
|
|
69
|
+
{
|
|
70
|
+
"inputs": [
|
|
71
|
+
{
|
|
72
|
+
"components": [
|
|
73
|
+
{ "internalType": "address", "name": "tokenIn", "type": "address" },
|
|
74
|
+
{ "internalType": "address", "name": "tokenOut", "type": "address" },
|
|
75
|
+
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
|
|
76
|
+
{ "internalType": "uint24", "name": "fee", "type": "uint24" },
|
|
77
|
+
{ "internalType": "uint160", "name": "sqrtPriceLimitX96", "type": "uint160" }
|
|
78
|
+
],
|
|
79
|
+
"internalType": "struct IQuoterV2.QuoteExactInputSingleParams",
|
|
80
|
+
"name": "params",
|
|
81
|
+
"type": "tuple"
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"name": "quoteExactInputSingle",
|
|
85
|
+
"outputs": [
|
|
86
|
+
{ "internalType": "uint256", "name": "amountOut", "type": "uint256" },
|
|
87
|
+
{ "internalType": "uint160", "name": "sqrtPriceX96After", "type": "uint160" },
|
|
88
|
+
{ "internalType": "uint32", "name": "initializedTicksCrossed", "type": "uint32" },
|
|
89
|
+
{ "internalType": "uint256", "name": "gasEstimate", "type": "uint256" }
|
|
90
|
+
],
|
|
91
|
+
"stateMutability": "nonpayable",
|
|
92
|
+
"type": "function"
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
export const QUOTER_ADDRESSES: Record<string, string> = {
|
|
97
|
+
ethereum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
98
|
+
base: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a',
|
|
99
|
+
arbitrum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
100
|
+
optimism: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
101
|
+
polygon: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e'
|
|
102
|
+
};
|