nyxora 1.1.8 → 1.3.0
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 +7 -3
- package/SECURITY.md +6 -4
- package/dist/agent/limitOrderManager.js +177 -0
- package/dist/agent/reasoning.js +93 -40
- package/dist/gateway/server.js +21 -6
- package/dist/gateway/telegram.js +19 -2
- package/dist/web3/skills/bridgeToken.js +216 -0
- package/dist/web3/skills/checkPortfolio.js +154 -0
- package/dist/web3/skills/checkSecurity.js +67 -0
- package/dist/web3/skills/createWallet.js +34 -0
- package/dist/web3/skills/customTx.js +93 -0
- package/dist/web3/skills/getBalance.js +42 -5
- package/dist/web3/skills/marketAnalysis.js +71 -0
- package/dist/web3/skills/mintNft.js +122 -0
- package/dist/web3/skills/swapToken.js +191 -51
- package/dist/web3/skills/transfer.js +95 -17
- package/dist/web3/utils/tokens.js +110 -0
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.marketAnalysisToolDefinition = void 0;
|
|
4
|
+
exports.analyzeMarket = analyzeMarket;
|
|
5
|
+
const tokens_1 = require("../utils/tokens");
|
|
6
|
+
async function analyzeMarket(chainName, tokenAddressOrSymbol) {
|
|
7
|
+
try {
|
|
8
|
+
let tokenAddress = tokenAddressOrSymbol;
|
|
9
|
+
try {
|
|
10
|
+
tokenAddress = (0, tokens_1.resolveToken)(tokenAddressOrSymbol, chainName);
|
|
11
|
+
if (tokenAddress === "0x0000000000000000000000000000000000000000") {
|
|
12
|
+
// For native token, we should pass WETH / wrapped version to Dexscreener usually,
|
|
13
|
+
// because native token itself doesn't have a pair on most DEXes directly.
|
|
14
|
+
tokenAddress = (0, tokens_1.resolveToken)("W" + tokenAddressOrSymbol, chainName); // e.g. WETH, WBNB
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
// If it fails to resolve, assume it's already an address or let DexScreener handle it (though DexScreener needs exact address)
|
|
19
|
+
}
|
|
20
|
+
const url = `https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`;
|
|
21
|
+
const res = await fetch(url);
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
throw new Error(`DexScreener API Error: ${res.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
const data = await res.json();
|
|
26
|
+
if (!data.pairs || data.pairs.length === 0) {
|
|
27
|
+
return `No market data found for token ${tokenAddressOrSymbol} on DexScreener.`;
|
|
28
|
+
}
|
|
29
|
+
// Filter pairs by chain if possible, Dexscreener chain IDs are strings like 'ethereum', 'bsc', 'base', 'arbitrum', 'optimism'
|
|
30
|
+
let pair = data.pairs.find((p) => p.chainId === chainName);
|
|
31
|
+
if (!pair) {
|
|
32
|
+
pair = data.pairs[0]; // Fallback to the most liquid pair anywhere
|
|
33
|
+
}
|
|
34
|
+
let report = `📈 **Market Analysis for ${pair.baseToken.name} (${pair.baseToken.symbol})** on ${pair.chainId.toUpperCase()}\n\n`;
|
|
35
|
+
report += `**Price:** $${pair.priceUsd}\n`;
|
|
36
|
+
report += `**Liquidity (USD):** $${Number(pair.liquidity?.usd || 0).toLocaleString()}\n`;
|
|
37
|
+
report += `**FDV:** $${Number(pair.fdv || 0).toLocaleString()}\n`;
|
|
38
|
+
report += `**24h Volume:** $${Number(pair.volume?.h24 || 0).toLocaleString()}\n\n`;
|
|
39
|
+
report += `**Price Change:**\n`;
|
|
40
|
+
report += `- 5m: ${pair.priceChange?.m5}% \n`;
|
|
41
|
+
report += `- 1h: ${pair.priceChange?.h1}% \n`;
|
|
42
|
+
report += `- 24h: ${pair.priceChange?.h24}% \n\n`;
|
|
43
|
+
report += `**DEX:** ${pair.dexId} (${pair.url})\n`;
|
|
44
|
+
return report;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return `Failed to analyze market: ${error.message}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.marketAnalysisToolDefinition = {
|
|
51
|
+
type: "function",
|
|
52
|
+
function: {
|
|
53
|
+
name: "analyze_market",
|
|
54
|
+
description: "Fetches live market data (Price, Liquidity, Volume, FDV, Price Change) for a token using DexScreener.",
|
|
55
|
+
parameters: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
chainName: {
|
|
59
|
+
type: "string",
|
|
60
|
+
enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
|
|
61
|
+
description: "The blockchain network",
|
|
62
|
+
},
|
|
63
|
+
tokenAddressOrSymbol: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "The token symbol (e.g. USDC, PEPE) or contract address to analyze.",
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
required: ["chainName", "tokenAddressOrSymbol"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mintNftToolDefinition = void 0;
|
|
4
|
+
exports.prepareMintNft = prepareMintNft;
|
|
5
|
+
exports.executeMintNft = executeMintNft;
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const config_1 = require("../config");
|
|
8
|
+
const transactionManager_1 = require("../../agent/transactionManager");
|
|
9
|
+
async function prepareMintNft(chainName, contractAddress, functionSignature, argsStr, valueEth = "0") {
|
|
10
|
+
try {
|
|
11
|
+
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
12
|
+
const walletClient = (0, config_1.getWalletClient)(chainName);
|
|
13
|
+
const account = walletClient.account;
|
|
14
|
+
// Parse arguments from JSON or comma separated
|
|
15
|
+
let parsedArgs = [];
|
|
16
|
+
try {
|
|
17
|
+
parsedArgs = JSON.parse(argsStr);
|
|
18
|
+
if (!Array.isArray(parsedArgs)) {
|
|
19
|
+
parsedArgs = [parsedArgs];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
if (argsStr.includes(",")) {
|
|
24
|
+
parsedArgs = argsStr.split(",").map(i => i.trim());
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
parsedArgs = [argsStr.trim()];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
let cleanSig = functionSignature.trim();
|
|
31
|
+
if (!cleanSig.startsWith("function ")) {
|
|
32
|
+
cleanSig = `function ${cleanSig}`;
|
|
33
|
+
}
|
|
34
|
+
// ensure it's payable or nonpayable properly. Viem parseAbi requires it to be payable if we send value
|
|
35
|
+
if (valueEth !== "0" && !cleanSig.includes("payable")) {
|
|
36
|
+
cleanSig = `${cleanSig} payable`;
|
|
37
|
+
}
|
|
38
|
+
const abi = (0, viem_1.parseAbi)([cleanSig]);
|
|
39
|
+
// Extract function name
|
|
40
|
+
const match = cleanSig.match(/function\s+([a-zA-Z0-9_]+)\s*\(/);
|
|
41
|
+
if (!match)
|
|
42
|
+
throw new Error("Invalid function signature format. Use e.g. 'mint(uint256)'");
|
|
43
|
+
const functionName = match[1];
|
|
44
|
+
const valueWei = (0, viem_1.parseEther)(valueEth);
|
|
45
|
+
const { request } = await publicClient.simulateContract({
|
|
46
|
+
account,
|
|
47
|
+
address: contractAddress,
|
|
48
|
+
abi,
|
|
49
|
+
functionName,
|
|
50
|
+
args: parsedArgs,
|
|
51
|
+
value: valueWei,
|
|
52
|
+
});
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
const gasEstimate = request.gas || 100000n;
|
|
55
|
+
const tx = transactionManager_1.txManager.createPendingTransaction('mint', chainName, {
|
|
56
|
+
contractAddress,
|
|
57
|
+
abi,
|
|
58
|
+
functionName,
|
|
59
|
+
parsedArgs,
|
|
60
|
+
valueWei: valueWei.toString(),
|
|
61
|
+
gasEstimate: gasEstimate.toString()
|
|
62
|
+
});
|
|
63
|
+
return `TRANSACTION_PENDING: Simulated NFT Minting successfully. Estimated gas units: ${gasEstimate}. Transaction ID: ${tx.id}. Wait for user to approve.`;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return `Simulation failed! Cannot prepare mint. Error: ${error.message}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function executeMintNft(chainName, params) {
|
|
70
|
+
try {
|
|
71
|
+
const client = (0, config_1.getWalletClient)(chainName);
|
|
72
|
+
const { contractAddress, abi, functionName, parsedArgs, valueWei } = params;
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
const hash = await client.writeContract({
|
|
75
|
+
account: client.account,
|
|
76
|
+
chain: client.chain,
|
|
77
|
+
address: contractAddress,
|
|
78
|
+
abi,
|
|
79
|
+
functionName,
|
|
80
|
+
args: parsedArgs,
|
|
81
|
+
value: BigInt(valueWei),
|
|
82
|
+
});
|
|
83
|
+
return `Mint successful. Tx Hash: ${hash}`;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return `Failed to execute mint: ${error.message}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.mintNftToolDefinition = {
|
|
90
|
+
type: "function",
|
|
91
|
+
function: {
|
|
92
|
+
name: "mint_nft",
|
|
93
|
+
description: "Mint an NFT by calling a specific smart contract function. Automatically simulates the transaction first.",
|
|
94
|
+
parameters: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
chainName: {
|
|
98
|
+
type: "string",
|
|
99
|
+
enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
|
|
100
|
+
description: "The blockchain network",
|
|
101
|
+
},
|
|
102
|
+
contractAddress: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "The NFT smart contract address (0x...)",
|
|
105
|
+
},
|
|
106
|
+
functionSignature: {
|
|
107
|
+
type: "string",
|
|
108
|
+
description: "The function signature to call, e.g., 'mint(uint256)' or 'claim(address,uint256)'",
|
|
109
|
+
},
|
|
110
|
+
argsStr: {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "A JSON array of arguments for the function, e.g., '[1]' or '[\"0x123...\", 2]'",
|
|
113
|
+
},
|
|
114
|
+
valueEth: {
|
|
115
|
+
type: "string",
|
|
116
|
+
description: "The amount of native ETH/BNB to send as payment for the mint. Use '0' if it's a free mint.",
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
required: ["chainName", "contractAddress", "functionSignature", "argsStr", "valueEth"],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
@@ -1,77 +1,217 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.swapTokenToolDefinition = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.prepareSwapToken = prepareSwapToken;
|
|
5
5
|
exports.executeSwap = executeSwap;
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const config_1 = require("../config");
|
|
8
|
+
const transactionManager_1 = require("../../agent/transactionManager");
|
|
9
|
+
const tokens_1 = require("../utils/tokens");
|
|
10
|
+
const CHAIN_IDS = {
|
|
11
|
+
ethereum: 1,
|
|
12
|
+
base: 8453,
|
|
13
|
+
bsc: 56,
|
|
14
|
+
arbitrum: 42161,
|
|
15
|
+
optimism: 10,
|
|
16
|
+
sepolia: 11155111,
|
|
17
|
+
};
|
|
18
|
+
async function getLifiQuote(fromChainId, toChainId, fromToken, toToken, amountWei, userAddress, slippage) {
|
|
19
|
+
const url = new URL('https://li.quest/v1/quote');
|
|
20
|
+
url.searchParams.append('fromChain', fromChainId.toString());
|
|
21
|
+
url.searchParams.append('toChain', toChainId.toString());
|
|
22
|
+
url.searchParams.append('fromToken', fromToken);
|
|
23
|
+
url.searchParams.append('toToken', toToken);
|
|
24
|
+
url.searchParams.append('fromAmount', amountWei);
|
|
25
|
+
url.searchParams.append('fromAddress', userAddress);
|
|
26
|
+
url.searchParams.append('slippage', slippage.toString());
|
|
27
|
+
const res = await fetch(url.toString());
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const err = await res.json().catch(() => ({}));
|
|
30
|
+
throw new Error(`Li.Fi API Error: ${err.message || res.statusText}`);
|
|
31
|
+
}
|
|
32
|
+
return await res.json();
|
|
33
|
+
}
|
|
34
|
+
async function getRelayQuote(fromChainId, toChainId, fromToken, toToken, amountWei, userAddress) {
|
|
35
|
+
const isTestnet = fromChainId === 11155111 || toChainId === 11155111;
|
|
36
|
+
const baseUrl = isTestnet ? "https://api.testnets.relay.link" : "https://api.relay.link";
|
|
37
|
+
const body = {
|
|
38
|
+
user: userAddress,
|
|
39
|
+
originChainId: fromChainId,
|
|
40
|
+
destinationChainId: toChainId,
|
|
41
|
+
originCurrency: fromToken,
|
|
42
|
+
destinationCurrency: toToken,
|
|
43
|
+
amount: amountWei,
|
|
44
|
+
tradeType: "EXACT_INPUT"
|
|
45
|
+
};
|
|
46
|
+
const res = await fetch(`${baseUrl}/quote`, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify(body)
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
const err = await res.json().catch(() => ({}));
|
|
53
|
+
throw new Error(`Relay API Error: ${err.message || res.statusText}`);
|
|
54
|
+
}
|
|
55
|
+
return await res.json();
|
|
56
|
+
}
|
|
57
|
+
async function prepareSwapToken(chainName, fromToken, toToken, amountStr, mode = "auto", providerName = "lifi", slippagePercent = 0.5) {
|
|
58
|
+
try {
|
|
59
|
+
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
60
|
+
const walletClient = (0, config_1.getWalletClient)(chainName);
|
|
61
|
+
const account = walletClient.account;
|
|
62
|
+
const chainId = CHAIN_IDS[chainName];
|
|
63
|
+
const fromTokenAddress = (0, tokens_1.resolveToken)(fromToken, chainName);
|
|
64
|
+
const toTokenAddress = (0, tokens_1.resolveToken)(toToken, chainName);
|
|
65
|
+
const isNativeIn = fromTokenAddress === "0x0000000000000000000000000000000000000000";
|
|
66
|
+
// Get decimals
|
|
67
|
+
let decimals = 18;
|
|
68
|
+
if (!isNativeIn) {
|
|
69
|
+
decimals = await publicClient.readContract({
|
|
70
|
+
address: fromTokenAddress,
|
|
71
|
+
abi: tokens_1.ERC20_ABI,
|
|
72
|
+
functionName: 'decimals',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const amountWei = (0, viem_1.parseUnits)(amountStr, decimals).toString();
|
|
76
|
+
let txRequest = null;
|
|
77
|
+
let approvalAddress = null;
|
|
78
|
+
let expectedOutputStr = "";
|
|
79
|
+
let actualProvider = mode === "auto" ? "lifi" : providerName;
|
|
80
|
+
const isTestnet = chainId === 11155111;
|
|
81
|
+
// --- SEPOLIA TESTNET MOCK ---
|
|
82
|
+
if (isTestnet) {
|
|
83
|
+
const mockGasLimit = "150000";
|
|
84
|
+
expectedOutputStr = "MOCK_TEST_AMOUNT";
|
|
85
|
+
const tx = transactionManager_1.txManager.createPendingTransaction('swap', chainName, {
|
|
86
|
+
txRequest: { to: fromTokenAddress, data: "0x", value: amountWei, gasLimit: mockGasLimit },
|
|
87
|
+
needsApprove: false,
|
|
88
|
+
fromTokenAddress,
|
|
89
|
+
approvalAddress: null,
|
|
90
|
+
amountWei
|
|
91
|
+
});
|
|
92
|
+
return `TRANSACTION_PENDING: Swap simulated via TESTNET_MOCK. Expected Output: ~${expectedOutputStr} ${toToken.toUpperCase()}. Gas est: ${mockGasLimit}. Transaction ID: ${tx.id}. Wait for user to approve.`;
|
|
93
|
+
}
|
|
94
|
+
// --- END MOCK ---
|
|
95
|
+
if (actualProvider === "lifi") {
|
|
96
|
+
const quote = await getLifiQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, account.address, slippagePercent / 100);
|
|
97
|
+
txRequest = quote.transactionRequest;
|
|
98
|
+
approvalAddress = quote.estimate.approvalAddress;
|
|
99
|
+
const toDecimals = quote.action.toToken.decimals;
|
|
100
|
+
expectedOutputStr = (0, viem_1.formatUnits)(BigInt(quote.estimate.toAmount), toDecimals);
|
|
101
|
+
}
|
|
102
|
+
else if (actualProvider === "relay") {
|
|
103
|
+
const relayQuote = await getRelayQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, account.address);
|
|
104
|
+
if (!relayQuote.steps || relayQuote.steps.length === 0)
|
|
105
|
+
throw new Error("No route found by Relay.");
|
|
106
|
+
// Relay returns steps. We need to find the main transaction step.
|
|
107
|
+
const txStep = relayQuote.steps.find((s) => s.id === "execute");
|
|
108
|
+
if (!txStep || !txStep.items || txStep.items.length === 0)
|
|
109
|
+
throw new Error("Relay steps invalid.");
|
|
110
|
+
const item = txStep.items[0];
|
|
111
|
+
txRequest = item.data;
|
|
112
|
+
// Usually Relay route approval to `txRequest.to` if it's not native
|
|
113
|
+
if (!isNativeIn && txRequest.to.toLowerCase() !== fromTokenAddress.toLowerCase()) {
|
|
114
|
+
approvalAddress = txRequest.to;
|
|
115
|
+
}
|
|
116
|
+
expectedOutputStr = relayQuote.details?.currencyOut?.amountFormatted || "Unknown";
|
|
117
|
+
}
|
|
118
|
+
// Check allowance early so we know if we need to auto-approve
|
|
119
|
+
let needsApprove = false;
|
|
120
|
+
if (!isNativeIn && approvalAddress && approvalAddress !== "0x0000000000000000000000000000000000000000") {
|
|
121
|
+
const allowance = await publicClient.readContract({
|
|
122
|
+
address: fromTokenAddress,
|
|
123
|
+
abi: tokens_1.ERC20_ABI,
|
|
124
|
+
functionName: 'allowance',
|
|
125
|
+
args: [account.address, approvalAddress],
|
|
126
|
+
});
|
|
127
|
+
if (allowance < BigInt(amountWei)) {
|
|
128
|
+
needsApprove = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const tx = transactionManager_1.txManager.createPendingTransaction('swap', chainName, {
|
|
132
|
+
txRequest,
|
|
133
|
+
needsApprove,
|
|
134
|
+
fromTokenAddress,
|
|
135
|
+
approvalAddress,
|
|
136
|
+
amountWei
|
|
137
|
+
});
|
|
138
|
+
return `TRANSACTION_PENDING: Swap simulated via ${actualProvider.toUpperCase()}. Expected Output: ~${expectedOutputStr} ${toToken.toUpperCase()}. ${needsApprove ? '(Auto-Approve required) ' : ''}Gas est: ${txRequest.gasLimit || 'auto'}. Transaction ID: ${tx.id}. Wait for user to approve.`;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return `Simulation failed! Cannot prepare swap. Error: ${error.message}`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function executeSwap(chainName, params) {
|
|
145
|
+
try {
|
|
146
|
+
const client = (0, config_1.getWalletClient)(chainName);
|
|
147
|
+
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
148
|
+
const { txRequest, needsApprove, fromTokenAddress, approvalAddress, amountWei } = params;
|
|
149
|
+
if (needsApprove) {
|
|
150
|
+
// Auto-Approve Transaction
|
|
151
|
+
const approveHash = await client.writeContract({
|
|
152
|
+
account: client.account,
|
|
153
|
+
chain: client.chain,
|
|
154
|
+
address: fromTokenAddress,
|
|
155
|
+
abi: tokens_1.ERC20_ABI,
|
|
156
|
+
functionName: 'approve',
|
|
157
|
+
// Max Uint256
|
|
158
|
+
args: [approvalAddress, 115792089237316195423570985008687907853269984665640564039457584007913129639935n],
|
|
159
|
+
});
|
|
160
|
+
await publicClient.waitForTransactionReceipt({ hash: approveHash });
|
|
161
|
+
}
|
|
162
|
+
// Execute Swap
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
const txHash = await client.sendTransaction({
|
|
165
|
+
account: client.account,
|
|
166
|
+
chain: client.chain,
|
|
167
|
+
to: txRequest.to,
|
|
168
|
+
data: txRequest.data,
|
|
169
|
+
value: txRequest.value ? BigInt(txRequest.value) : 0n,
|
|
170
|
+
gas: txRequest.gasLimit ? (BigInt(txRequest.gasLimit) * 12n / 10n) : undefined // 20% buffer
|
|
171
|
+
});
|
|
172
|
+
return `Swap successful. Tx Hash: ${txHash}`;
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return `Failed to execute swap: ${error.message}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
6
178
|
exports.swapTokenToolDefinition = {
|
|
7
179
|
type: "function",
|
|
8
180
|
function: {
|
|
9
181
|
name: "swap_token",
|
|
10
|
-
description: "Executes a decentralized token swap (DEX) to exchange one cryptocurrency for another.",
|
|
182
|
+
description: "Executes a decentralized token swap (DEX) to exchange one cryptocurrency for another using Li.Fi or Relay. Automatically simulates the swap to fetch quotes.",
|
|
11
183
|
parameters: {
|
|
12
184
|
type: "object",
|
|
13
185
|
properties: {
|
|
14
186
|
chainName: {
|
|
15
187
|
type: "string",
|
|
16
|
-
|
|
188
|
+
enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
|
|
189
|
+
description: "The blockchain network",
|
|
17
190
|
},
|
|
18
191
|
fromToken: {
|
|
19
192
|
type: "string",
|
|
20
|
-
description: "The token symbol to sell
|
|
193
|
+
description: "The token symbol to sell (e.g., 'ETH', 'USDC')",
|
|
21
194
|
},
|
|
22
195
|
toToken: {
|
|
23
196
|
type: "string",
|
|
24
|
-
description: "The token symbol to buy
|
|
197
|
+
description: "The token symbol to buy (e.g., 'USDC', 'UNI')",
|
|
25
198
|
},
|
|
26
|
-
|
|
27
|
-
type: "
|
|
199
|
+
amountStr: {
|
|
200
|
+
type: "string",
|
|
28
201
|
description: "The amount of fromToken to swap",
|
|
29
202
|
},
|
|
203
|
+
mode: {
|
|
204
|
+
type: "string",
|
|
205
|
+
enum: ["auto", "manual"],
|
|
206
|
+
description: "auto uses lifi. manual uses the specified provider."
|
|
207
|
+
},
|
|
208
|
+
providerName: {
|
|
209
|
+
type: "string",
|
|
210
|
+
enum: ["lifi", "relay"],
|
|
211
|
+
description: "Used if mode is manual."
|
|
212
|
+
}
|
|
30
213
|
},
|
|
31
|
-
required: ["chainName", "fromToken", "toToken", "
|
|
214
|
+
required: ["chainName", "fromToken", "toToken", "amountStr"],
|
|
32
215
|
},
|
|
33
216
|
},
|
|
34
217
|
};
|
|
35
|
-
const transactionManager_1 = require("../../agent/transactionManager");
|
|
36
|
-
async function swapToken(chainName, fromToken, toToken, amount) {
|
|
37
|
-
const tx = transactionManager_1.txManager.createPendingTransaction('swap', chainName, { fromToken, toToken, amount });
|
|
38
|
-
return `TRANSACTION_PENDING: I have prepared the token swap. Transaction ID: ${tx.id}. Wait for user to approve.`;
|
|
39
|
-
}
|
|
40
|
-
async function executeSwap(chainName, fromToken, toToken, amount) {
|
|
41
|
-
try {
|
|
42
|
-
// Generate simulated exchange rate for testnet/demo purposes
|
|
43
|
-
let rate = 1.0;
|
|
44
|
-
// Simple mock rates based on common pairs
|
|
45
|
-
if (fromToken.toUpperCase() === 'ETH' && toToken.toUpperCase() === 'USDC')
|
|
46
|
-
rate = 3200;
|
|
47
|
-
if (fromToken.toUpperCase() === 'USDC' && toToken.toUpperCase() === 'ETH')
|
|
48
|
-
rate = 1 / 3200;
|
|
49
|
-
if (fromToken.toUpperCase() === 'ETH' && toToken.toUpperCase() === 'LINK')
|
|
50
|
-
rate = 200;
|
|
51
|
-
if (fromToken.toUpperCase() === 'SOL' && toToken.toUpperCase() === 'USDC')
|
|
52
|
-
rate = 150;
|
|
53
|
-
// Add a slight random variance (±1%) to simulate live market slippage
|
|
54
|
-
const variance = 1 + (Math.random() * 0.02 - 0.01);
|
|
55
|
-
const finalRate = rate * variance;
|
|
56
|
-
const toAmount = amount * finalRate;
|
|
57
|
-
// Simulate transaction execution delay
|
|
58
|
-
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
59
|
-
// Generate mock transaction hash
|
|
60
|
-
const txHash = '0x' + Array.from({ length: 64 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
|
|
61
|
-
const swapResult = {
|
|
62
|
-
chain: chainName,
|
|
63
|
-
fromToken: fromToken.toUpperCase(),
|
|
64
|
-
toToken: toToken.toUpperCase(),
|
|
65
|
-
fromAmount: amount.toFixed(4),
|
|
66
|
-
toAmount: toAmount.toFixed(4),
|
|
67
|
-
exchangeRate: finalRate.toFixed(6),
|
|
68
|
-
gasFee: (Math.random() * 0.005).toFixed(4) + ' ETH',
|
|
69
|
-
txHash: txHash,
|
|
70
|
-
status: 'SUCCESS'
|
|
71
|
-
};
|
|
72
|
-
return JSON.stringify(swapResult);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
return JSON.stringify({ error: `Failed to execute swap: ${error.message}` });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,35 +1,109 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.transferToolDefinition = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.prepareTransfer = prepareTransfer;
|
|
5
5
|
exports.executeTransfer = executeTransfer;
|
|
6
6
|
const viem_1 = require("viem");
|
|
7
7
|
const config_1 = require("../config");
|
|
8
8
|
const transactionManager_1 = require("../../agent/transactionManager");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const tokens_1 = require("../utils/tokens");
|
|
10
|
+
async function prepareTransfer(chainName, toAddress, amountStr, token) {
|
|
11
|
+
try {
|
|
12
|
+
const publicClient = (0, config_1.getPublicClient)(chainName);
|
|
13
|
+
const walletClient = (0, config_1.getWalletClient)(chainName);
|
|
14
|
+
const account = walletClient.account;
|
|
15
|
+
let tokenAddress = "0x0000000000000000000000000000000000000000";
|
|
16
|
+
let isNative = true;
|
|
17
|
+
let decimals = 18;
|
|
18
|
+
let symbol = "ETH/BNB"; // Generic fallback for native
|
|
19
|
+
if (token) {
|
|
20
|
+
tokenAddress = (0, tokens_1.resolveToken)(token, chainName);
|
|
21
|
+
isNative = tokenAddress === "0x0000000000000000000000000000000000000000";
|
|
22
|
+
}
|
|
23
|
+
let gasEstimate = 0n;
|
|
24
|
+
if (isNative) {
|
|
25
|
+
// Simulate Native Transfer
|
|
26
|
+
const value = (0, viem_1.parseEther)(amountStr);
|
|
27
|
+
gasEstimate = await publicClient.estimateGas({
|
|
28
|
+
account,
|
|
29
|
+
to: toAddress,
|
|
30
|
+
value,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// Simulate ERC-20 Transfer
|
|
35
|
+
decimals = await publicClient.readContract({
|
|
36
|
+
address: tokenAddress,
|
|
37
|
+
abi: tokens_1.ERC20_ABI,
|
|
38
|
+
functionName: 'decimals',
|
|
39
|
+
});
|
|
40
|
+
symbol = await publicClient.readContract({
|
|
41
|
+
address: tokenAddress,
|
|
42
|
+
abi: tokens_1.ERC20_ABI,
|
|
43
|
+
functionName: 'symbol',
|
|
44
|
+
}).catch(() => token || "TOKEN");
|
|
45
|
+
const value = (0, viem_1.parseUnits)(amountStr, decimals);
|
|
46
|
+
const { request } = await publicClient.simulateContract({
|
|
47
|
+
account,
|
|
48
|
+
address: tokenAddress,
|
|
49
|
+
abi: tokens_1.ERC20_ABI,
|
|
50
|
+
functionName: 'transfer',
|
|
51
|
+
args: [toAddress, value],
|
|
52
|
+
});
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
gasEstimate = request.gas || 50000n;
|
|
55
|
+
}
|
|
56
|
+
const tx = transactionManager_1.txManager.createPendingTransaction('transfer', chainName, {
|
|
57
|
+
toAddress,
|
|
58
|
+
amountStr,
|
|
59
|
+
tokenAddress,
|
|
60
|
+
isNative,
|
|
61
|
+
decimals,
|
|
62
|
+
gasEstimate: gasEstimate.toString()
|
|
63
|
+
});
|
|
64
|
+
const tokenName = isNative ? "Native Token" : symbol;
|
|
65
|
+
return `TRANSACTION_PENDING: I have prepared the ${tokenName} transfer and simulated it successfully. Estimated gas units: ${gasEstimate}. Transaction ID: ${tx.id}. Wait for user to approve.`;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return `Simulation failed! Cannot prepare transfer. Error: ${error.message}`;
|
|
69
|
+
}
|
|
12
70
|
}
|
|
13
|
-
async function executeTransfer(chainName,
|
|
71
|
+
async function executeTransfer(chainName, params) {
|
|
14
72
|
try {
|
|
15
73
|
const client = (0, config_1.getWalletClient)(chainName);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
74
|
+
const { toAddress, amountStr, tokenAddress, isNative, decimals } = params;
|
|
75
|
+
let hash;
|
|
76
|
+
if (isNative) {
|
|
77
|
+
hash = await client.sendTransaction({
|
|
78
|
+
account: client.account,
|
|
79
|
+
chain: client.chain,
|
|
80
|
+
to: toAddress,
|
|
81
|
+
value: (0, viem_1.parseEther)(amountStr),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const value = (0, viem_1.parseUnits)(amountStr, decimals);
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
hash = await client.writeContract({
|
|
88
|
+
account: client.account,
|
|
89
|
+
chain: client.chain,
|
|
90
|
+
address: tokenAddress,
|
|
91
|
+
abi: tokens_1.ERC20_ABI,
|
|
92
|
+
functionName: 'transfer',
|
|
93
|
+
args: [toAddress, value],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
22
96
|
return `Transaction successful. Hash: ${hash}`;
|
|
23
97
|
}
|
|
24
98
|
catch (error) {
|
|
25
|
-
return `Failed to transfer: ${error.message}`;
|
|
99
|
+
return `Failed to execute transfer: ${error.message}`;
|
|
26
100
|
}
|
|
27
101
|
}
|
|
28
102
|
exports.transferToolDefinition = {
|
|
29
103
|
type: "function",
|
|
30
104
|
function: {
|
|
31
|
-
name: "
|
|
32
|
-
description: "Transfer native tokens (ETH, BNB, etc.) from the agent's wallet to a destination address.",
|
|
105
|
+
name: "transfer_token",
|
|
106
|
+
description: "Transfer native tokens (ETH, BNB, etc.) or ERC-20 tokens from the agent's wallet to a destination address. Automatically performs on-chain simulation before requesting user approval.",
|
|
33
107
|
parameters: {
|
|
34
108
|
type: "object",
|
|
35
109
|
properties: {
|
|
@@ -42,12 +116,16 @@ exports.transferToolDefinition = {
|
|
|
42
116
|
type: "string",
|
|
43
117
|
description: "The destination 0x... wallet address."
|
|
44
118
|
},
|
|
45
|
-
|
|
119
|
+
amountStr: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "The amount of tokens to send (e.g. '0.01')."
|
|
122
|
+
},
|
|
123
|
+
token: {
|
|
46
124
|
type: "string",
|
|
47
|
-
description: "The
|
|
125
|
+
description: "Optional. The token symbol (e.g. USDC, USDT) or contract address (0x...). If omitted, sends the native coin."
|
|
48
126
|
}
|
|
49
127
|
},
|
|
50
|
-
required: ["chainName", "toAddress", "
|
|
128
|
+
required: ["chainName", "toAddress", "amountStr"]
|
|
51
129
|
}
|
|
52
130
|
}
|
|
53
131
|
};
|