nyxora 1.7.1 → 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/.dockerignore +12 -0
- package/CHANGELOG.md +21 -0
- package/DOCKER.md +68 -0
- package/Dockerfile +43 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/config/parser.ts +15 -4
- package/packages/core/src/gateway/googleAuthModule.ts +19 -0
- package/packages/core/src/gateway/server.ts +9 -3
- 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 +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/policy/src/server.ts +2 -2
- package/packages/signer/package.json +2 -2
- package/packages/dashboard/dist/assets/index-Dc3Tu0Te.js +0 -295
package/.dockerignore
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +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.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
|
|
17
|
+
|
|
18
|
+
### UI/UX Enhancements
|
|
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.
|
|
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
|
+
|
|
8
29
|
## [1.7.1]
|
|
9
30
|
|
|
10
31
|
### CLI Enhancements
|
package/DOCKER.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# 🐳 Nyxora Docker Guide
|
|
2
|
+
|
|
3
|
+
This document provides a comprehensive guide to installing, configuring interactively, and running the Nyxora Agent fully containerized via Docker. This approach ensures Nyxora can run seamlessly across different environments (Linux, macOS, Windows) without requiring a local Node.js installation.
|
|
4
|
+
|
|
5
|
+
## 🛠 1. Get the Docker Image
|
|
6
|
+
|
|
7
|
+
You have two options to obtain the Nyxora Docker image:
|
|
8
|
+
|
|
9
|
+
### Option A: Pull Pre-built Image (Recommended & Fastest)
|
|
10
|
+
Nyxora officially publishes Docker images via GitHub Container Registry (GHCR) upon every release.
|
|
11
|
+
```bash
|
|
12
|
+
docker pull ghcr.io/nyxoraai/nyxora:latest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Option B: Build Locally
|
|
16
|
+
If you are developing or prefer to compile it yourself, run this in the root directory:
|
|
17
|
+
```bash
|
|
18
|
+
docker build -t ghcr.io/nyxoraai/nyxora:latest .
|
|
19
|
+
```
|
|
20
|
+
> **Note:** The initial build takes time as it compiles C++/Rust system modules (`isolated-vm`, `libsecret`).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## ⚙️ 2. Interactive Setup (Secure & Isolated)
|
|
25
|
+
Nyxora requires an initial configuration (API Keys, Web3 Wallet, etc.). Run the command below to launch the **Interactive Setup Wizard** securely inside Docker.
|
|
26
|
+
|
|
27
|
+
This command mounts a volume and saves your configurations safely to a `.nyxora_docker` folder in your computer's Home directory, ensuring your existing local Nyxora installation remains untouched.
|
|
28
|
+
```bash
|
|
29
|
+
docker run -it --rm -v ~/.nyxora_docker:/root/.nyxora ghcr.io/nyxoraai/nyxora:latest npx ts-node -T packages/core/src/gateway/setup-cli.ts
|
|
30
|
+
```
|
|
31
|
+
*Complete the setup by answering the prompts in your terminal. Once you see "Setup Successful!", this temporary container will automatically delete itself.*
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🚀 3. Start Nyxora (Background Daemon)
|
|
36
|
+
Now that the setup is complete, it's time to start the main architecture (Core API, Policy Engine, and Signer Vault) as a non-stop background daemon:
|
|
37
|
+
```bash
|
|
38
|
+
docker run -d --name nyxora-daemon -p 3000:3000 -p 3001:3001 -v ~/.nyxora_docker:/root/.nyxora ghcr.io/nyxoraai/nyxora:latest
|
|
39
|
+
```
|
|
40
|
+
*(This command will output a long container ID indicating that the daemon is successfully running).*
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🔑 4. Retrieve the Auth Token
|
|
45
|
+
For security reasons, the Web Dashboard is locked behind a randomly generated runtime token upon every boot. Retrieve this token from the Docker logs:
|
|
46
|
+
```bash
|
|
47
|
+
docker logs nyxora-daemon
|
|
48
|
+
```
|
|
49
|
+
Look for a line that says: `[Launcher] Generated Internal Auth Token: <LONG_TOKEN_CODE>`. Please copy that token code.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 💻 5. Access the Web Dashboard
|
|
54
|
+
Open your preferred web browser, and access the following URL (replace `<LONG_TOKEN_CODE>` with the token you copied in Step 4):
|
|
55
|
+
```text
|
|
56
|
+
http://localhost:3000/?token=<LONG_TOKEN_CODE>
|
|
57
|
+
```
|
|
58
|
+
Congratulations! Your Nyxora Agent is now fully operational.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🧰 Docker Management Reference
|
|
63
|
+
Here are some useful commands for managing your Nyxora daemon in the future:
|
|
64
|
+
|
|
65
|
+
* **Stop Nyxora:** `docker stop nyxora-daemon`
|
|
66
|
+
* **Start Nyxora again:** `docker start nyxora-daemon`
|
|
67
|
+
* **Monitor real-time AI logs:** `docker logs -f nyxora-daemon` (Press `Ctrl+C` to exit the stream).
|
|
68
|
+
* **Remove the container (e.g., to reset or upgrade):** `docker rm -f nyxora-daemon`
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
FROM node:22-bookworm-slim
|
|
2
|
+
|
|
3
|
+
# Set working directory
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Set Production Environment
|
|
7
|
+
# Install native dependencies required for isolated-vm, keyring, etc.
|
|
8
|
+
RUN apt-get update && apt-get install -y \
|
|
9
|
+
python3 \
|
|
10
|
+
make \
|
|
11
|
+
g++ \
|
|
12
|
+
libsecret-1-dev \
|
|
13
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
# Copy package metadata first to leverage Docker caching
|
|
16
|
+
COPY package*.json ./
|
|
17
|
+
COPY packages/core/package*.json ./packages/core/
|
|
18
|
+
COPY packages/dashboard/package*.json ./packages/dashboard/
|
|
19
|
+
COPY packages/mcp-server/package*.json ./packages/mcp-server/
|
|
20
|
+
COPY packages/policy/package*.json ./packages/policy/
|
|
21
|
+
COPY packages/signer/package*.json ./packages/signer/
|
|
22
|
+
|
|
23
|
+
# Install dependencies
|
|
24
|
+
RUN npm install
|
|
25
|
+
|
|
26
|
+
# Copy the rest of the application code
|
|
27
|
+
COPY . .
|
|
28
|
+
|
|
29
|
+
# Build the dashboard (frontend)
|
|
30
|
+
RUN npm run build --workspace=dashboard
|
|
31
|
+
|
|
32
|
+
# Set Production Environment now that build is done
|
|
33
|
+
ENV NODE_ENV=production
|
|
34
|
+
|
|
35
|
+
# Expose the ports used by Core/Dashboard and Policy Engine
|
|
36
|
+
EXPOSE 3000
|
|
37
|
+
EXPOSE 3001
|
|
38
|
+
|
|
39
|
+
# PERSISTENT STORAGE: Protect Nyxora's memory and configuration
|
|
40
|
+
VOLUME ["/root/.nyxora"]
|
|
41
|
+
|
|
42
|
+
# Start the daemon in the foreground
|
|
43
|
+
CMD ["npx", "ts-node", "-T", "launcher.ts"]
|
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',
|
|
@@ -158,10 +160,19 @@ export function loadConfig(): NyxoraConfig {
|
|
|
158
160
|
system: { allow_shell_execution: false, allow_file_write: false }
|
|
159
161
|
}
|
|
160
162
|
} as NyxoraConfig;
|
|
161
|
-
} catch (error) {
|
|
162
|
-
|
|
163
|
+
} catch (error: any) {
|
|
164
|
+
if (error.code === 'ENOENT') {
|
|
165
|
+
console.log('[Config] No config.yaml found. Using default configuration.');
|
|
166
|
+
} else {
|
|
167
|
+
console.error('[Config] Failed to load config.yaml. Using default configuration.', error);
|
|
168
|
+
}
|
|
163
169
|
return {
|
|
164
|
-
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
|
+
},
|
|
165
176
|
llm: {
|
|
166
177
|
provider: 'openai',
|
|
167
178
|
model: 'gpt-4o-mini',
|
|
@@ -145,6 +145,25 @@ export async function getAccessToken(): Promise<string | null> {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
export async function logoutGoogle(): Promise<boolean> {
|
|
149
|
+
try {
|
|
150
|
+
const { Entry } = require('@napi-rs/keyring');
|
|
151
|
+
const entry = new Entry('nyxora', 'google_refresh_token');
|
|
152
|
+
await entry.deletePassword();
|
|
153
|
+
} catch (e) {}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
if (fs.existsSync(FALLBACK_TOKEN_PATH)) {
|
|
157
|
+
fs.unlinkSync(FALLBACK_TOKEN_PATH);
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {}
|
|
160
|
+
|
|
161
|
+
accessToken = null;
|
|
162
|
+
tokenExpiry = 0;
|
|
163
|
+
console.log('[Google Auth] Successfully logged out.');
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
148
167
|
// ---- Secure Storage for Refresh Token ----
|
|
149
168
|
|
|
150
169
|
async function saveRefreshToken(token: string) {
|
|
@@ -44,7 +44,7 @@ import { readGmailInboxToolDefinition, listCalendarEventsToolDefinition, appendR
|
|
|
44
44
|
|
|
45
45
|
import { startTelegramBot } from './telegram';
|
|
46
46
|
import { formatTransactionSuccess, formatTransactionError } from '../utils/formatter';
|
|
47
|
-
import { initGoogleAuth, getAuthUrl, processCallback, isAuthenticated } from './googleAuthModule';
|
|
47
|
+
import { initGoogleAuth, getAuthUrl, processCallback, isAuthenticated, logoutGoogle } from './googleAuthModule';
|
|
48
48
|
|
|
49
49
|
// Initialize Google Auth
|
|
50
50
|
initGoogleAuth();
|
|
@@ -291,7 +291,13 @@ app.get('/api/auth/google/callback', async (req, res) => {
|
|
|
291
291
|
});
|
|
292
292
|
|
|
293
293
|
app.get('/api/auth/google/status', async (req, res) => {
|
|
294
|
-
|
|
294
|
+
const connected = await isAuthenticated();
|
|
295
|
+
res.json({ connected });
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
app.delete('/api/auth/google', async (req, res) => {
|
|
299
|
+
const success = await logoutGoogle();
|
|
300
|
+
res.json({ success });
|
|
295
301
|
});
|
|
296
302
|
|
|
297
303
|
app.get('/api/transactions', (req, res) => {
|
|
@@ -405,7 +411,7 @@ export function startServer() {
|
|
|
405
411
|
limitOrderManager.startMonitor();
|
|
406
412
|
|
|
407
413
|
const PORT = Number(process.env.PORT || 3000);
|
|
408
|
-
app.listen(PORT, '
|
|
414
|
+
app.listen(PORT, '0.0.0.0', () => {
|
|
409
415
|
console.log(`🤖 Nyxora API Server running on port ${PORT}`);
|
|
410
416
|
|
|
411
417
|
// Start the Telegram bot listener
|
|
@@ -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
|
+
};
|