nyxora 1.1.8 → 1.2.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.
@@ -14,6 +14,9 @@ const getBalance_1 = require("../web3/skills/getBalance");
14
14
  const transfer_1 = require("../web3/skills/transfer");
15
15
  const getPrice_1 = require("../web3/skills/getPrice");
16
16
  const swapToken_1 = require("../web3/skills/swapToken");
17
+ const bridgeToken_1 = require("../web3/skills/bridgeToken");
18
+ const mintNft_1 = require("../web3/skills/mintNft");
19
+ const customTx_1 = require("../web3/skills/customTx");
17
20
  const paths_1 = require("../config/paths");
18
21
  exports.logger = new logger_1.Logger();
19
22
  let currentKeyIndex = 0;
@@ -140,7 +143,15 @@ async function processUserInput(input, role = 'user') {
140
143
  model: config.llm.model,
141
144
  temperature: config.llm.temperature,
142
145
  messages: messages,
143
- tools: [getBalance_1.getBalanceToolDefinition, transfer_1.transferToolDefinition, getPrice_1.getPriceToolDefinition, swapToken_1.swapTokenToolDefinition],
146
+ tools: [
147
+ getBalance_1.getBalanceToolDefinition,
148
+ transfer_1.transferToolDefinition,
149
+ getPrice_1.getPriceToolDefinition,
150
+ swapToken_1.swapTokenToolDefinition,
151
+ bridgeToken_1.bridgeTokenToolDefinition,
152
+ mintNft_1.mintNftToolDefinition,
153
+ customTx_1.customTxToolDefinition
154
+ ],
144
155
  tool_choice: "auto",
145
156
  });
146
157
  const responseMessage = response.choices[0].message;
@@ -162,7 +173,7 @@ async function processUserInput(input, role = 'user') {
162
173
  const toolCall = _toolCall;
163
174
  if (toolCall.function.name === 'get_balance') {
164
175
  const args = JSON.parse(toolCall.function.arguments);
165
- const balanceResult = await (0, getBalance_1.getBalance)(args.chainName, args.address);
176
+ const balanceResult = await (0, getBalance_1.getBalance)(args.chainName, args.address, args.token);
166
177
  exports.logger.addEntry({
167
178
  role: 'tool',
168
179
  tool_call_id: toolCall.id,
@@ -170,9 +181,9 @@ async function processUserInput(input, role = 'user') {
170
181
  content: balanceResult,
171
182
  });
172
183
  }
173
- else if (toolCall.function.name === 'transfer_native') {
184
+ else if (toolCall.function.name === 'transfer_token' || toolCall.function.name === 'transfer_native') {
174
185
  const args = JSON.parse(toolCall.function.arguments);
175
- const transferResult = await (0, transfer_1.transferNative)(args.chainName, args.toAddress, args.amountEth);
186
+ const transferResult = await (0, transfer_1.prepareTransfer)(args.chainName, args.toAddress, args.amountStr || args.amountEth, args.token);
176
187
  exports.logger.addEntry({
177
188
  role: 'tool',
178
189
  tool_call_id: toolCall.id,
@@ -192,7 +203,7 @@ async function processUserInput(input, role = 'user') {
192
203
  }
193
204
  else if (toolCall.function.name === 'swap_token') {
194
205
  const args = JSON.parse(toolCall.function.arguments);
195
- const swapResult = await (0, swapToken_1.swapToken)(args.chainName, args.fromToken, args.toToken, args.amount);
206
+ const swapResult = await (0, swapToken_1.prepareSwapToken)(args.chainName, args.fromToken, args.toToken, args.amountStr || args.amount, args.mode, args.providerName);
196
207
  exports.logger.addEntry({
197
208
  role: 'tool',
198
209
  tool_call_id: toolCall.id,
@@ -200,6 +211,36 @@ async function processUserInput(input, role = 'user') {
200
211
  content: swapResult,
201
212
  });
202
213
  }
214
+ else if (toolCall.function.name === 'bridge_token') {
215
+ const args = JSON.parse(toolCall.function.arguments);
216
+ const bridgeResult = await (0, bridgeToken_1.prepareBridgeToken)(args.fromChainName, args.toChainName, args.fromToken, args.toToken, args.amountStr, args.mode, args.providerName);
217
+ exports.logger.addEntry({
218
+ role: 'tool',
219
+ tool_call_id: toolCall.id,
220
+ name: toolCall.function.name,
221
+ content: bridgeResult,
222
+ });
223
+ }
224
+ else if (toolCall.function.name === 'mint_nft') {
225
+ const args = JSON.parse(toolCall.function.arguments);
226
+ const mintResult = await (0, mintNft_1.prepareMintNft)(args.chainName, args.contractAddress, args.functionSignature, args.argsStr, args.valueEth);
227
+ exports.logger.addEntry({
228
+ role: 'tool',
229
+ tool_call_id: toolCall.id,
230
+ name: toolCall.function.name,
231
+ content: mintResult,
232
+ });
233
+ }
234
+ else if (toolCall.function.name === 'custom_tx') {
235
+ const args = JSON.parse(toolCall.function.arguments);
236
+ const customResult = await (0, customTx_1.prepareCustomTx)(args.chainName, args.toAddress, args.dataHex, args.valueEth, args.gasLimitStr);
237
+ exports.logger.addEntry({
238
+ role: 'tool',
239
+ tool_call_id: toolCall.id,
240
+ name: toolCall.function.name,
241
+ content: customResult,
242
+ });
243
+ }
203
244
  }
204
245
  // Second call to get the final answer after tool execution
205
246
  const secondMessages = [
@@ -15,9 +15,10 @@ const transactionManager_1 = require("../agent/transactionManager");
15
15
  const transfer_1 = require("../web3/skills/transfer");
16
16
  const swapToken_1 = require("../web3/skills/swapToken");
17
17
  const getBalance_1 = require("../web3/skills/getBalance");
18
- const transfer_2 = require("../web3/skills/transfer");
19
18
  const getPrice_1 = require("../web3/skills/getPrice");
20
- const swapToken_2 = require("../web3/skills/swapToken");
19
+ const bridgeToken_1 = require("../web3/skills/bridgeToken");
20
+ const mintNft_1 = require("../web3/skills/mintNft");
21
+ const customTx_1 = require("../web3/skills/customTx");
21
22
  const telegram_1 = require("./telegram");
22
23
  const formatter_1 = require("../utils/formatter");
23
24
  // Intercept console.log and console.error
@@ -94,9 +95,12 @@ app.get('/api/logs', (req, res) => {
94
95
  app.get('/api/skills', (req, res) => {
95
96
  res.json([
96
97
  getBalance_1.getBalanceToolDefinition,
97
- transfer_2.transferToolDefinition,
98
+ transfer_1.transferToolDefinition,
98
99
  getPrice_1.getPriceToolDefinition,
99
- swapToken_2.swapTokenToolDefinition
100
+ swapToken_1.swapTokenToolDefinition,
101
+ bridgeToken_1.bridgeTokenToolDefinition,
102
+ mintNft_1.mintNftToolDefinition,
103
+ customTx_1.customTxToolDefinition
100
104
  ]);
101
105
  });
102
106
  app.get('/api/transactions', (req, res) => {
@@ -110,10 +114,19 @@ app.post('/api/transactions/:id/approve', async (req, res) => {
110
114
  try {
111
115
  let result = '';
112
116
  if (tx.type === 'transfer') {
113
- result = await (0, transfer_1.executeTransfer)(tx.chainName, tx.details.toAddress, tx.details.amountEth);
117
+ result = await (0, transfer_1.executeTransfer)(tx.chainName, tx.details);
114
118
  }
115
119
  else if (tx.type === 'swap') {
116
- result = await (0, swapToken_1.executeSwap)(tx.chainName, tx.details.fromToken, tx.details.toToken, tx.details.amount);
120
+ result = await (0, swapToken_1.executeSwap)(tx.chainName, tx.details);
121
+ }
122
+ else if (tx.type === 'bridge') {
123
+ result = await (0, bridgeToken_1.executeBridge)(tx.chainName, tx.details);
124
+ }
125
+ else if (tx.type === 'mint') {
126
+ result = await (0, mintNft_1.executeMintNft)(tx.chainName, tx.details);
127
+ }
128
+ else if (tx.type === 'custom') {
129
+ result = await (0, customTx_1.executeCustomTx)(tx.chainName, tx.details);
117
130
  }
118
131
  transactionManager_1.txManager.updateStatus(id, 'executed', result);
119
132
  // Add programmatic beautiful message directly to chat
@@ -10,6 +10,9 @@ const parser_1 = require("../config/parser");
10
10
  const transactionManager_1 = require("../agent/transactionManager");
11
11
  const transfer_1 = require("../web3/skills/transfer");
12
12
  const swapToken_1 = require("../web3/skills/swapToken");
13
+ const bridgeToken_1 = require("../web3/skills/bridgeToken");
14
+ const mintNft_1 = require("../web3/skills/mintNft");
15
+ const customTx_1 = require("../web3/skills/customTx");
13
16
  const formatter_1 = require("../utils/formatter");
14
17
  function startTelegramBot() {
15
18
  const config = (0, parser_1.loadConfig)();
@@ -73,10 +76,19 @@ function startTelegramBot() {
73
76
  try {
74
77
  let result = '';
75
78
  if (tx.type === 'transfer') {
76
- result = await (0, transfer_1.executeTransfer)(tx.chainName, tx.details.toAddress, tx.details.amountEth);
79
+ result = await (0, transfer_1.executeTransfer)(tx.chainName, tx.details);
77
80
  }
78
81
  else if (tx.type === 'swap') {
79
- result = await (0, swapToken_1.executeSwap)(tx.chainName, tx.details.fromToken, tx.details.toToken, tx.details.amount);
82
+ result = await (0, swapToken_1.executeSwap)(tx.chainName, tx.details);
83
+ }
84
+ else if (tx.type === 'bridge') {
85
+ result = await (0, bridgeToken_1.executeBridge)(tx.chainName, tx.details);
86
+ }
87
+ else if (tx.type === 'mint') {
88
+ result = await (0, mintNft_1.executeMintNft)(tx.chainName, tx.details);
89
+ }
90
+ else if (tx.type === 'custom') {
91
+ result = await (0, customTx_1.executeCustomTx)(tx.chainName, tx.details);
80
92
  }
81
93
  transactionManager_1.txManager.updateStatus(txId, 'executed', result);
82
94
  const prettyMsg = (0, formatter_1.formatTransactionSuccess)(tx, result);
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bridgeTokenToolDefinition = void 0;
4
+ exports.prepareBridgeToken = prepareBridgeToken;
5
+ exports.executeBridge = executeBridge;
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 body = {
36
+ user: userAddress,
37
+ originChainId: fromChainId,
38
+ destinationChainId: toChainId,
39
+ originCurrency: fromToken,
40
+ destinationCurrency: toToken,
41
+ amount: amountWei,
42
+ tradeType: "EXACT_INPUT"
43
+ };
44
+ const res = await fetch("https://api.relay.link/quote", {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ body: JSON.stringify(body)
48
+ });
49
+ if (!res.ok) {
50
+ const err = await res.json().catch(() => ({}));
51
+ throw new Error(`Relay API Error: ${err.message || res.statusText}`);
52
+ }
53
+ return await res.json();
54
+ }
55
+ async function prepareBridgeToken(fromChainName, toChainName, fromToken, toToken, amountStr, mode = "auto", providerName = "lifi", slippagePercent = 0.5) {
56
+ try {
57
+ const publicClient = (0, config_1.getPublicClient)(fromChainName);
58
+ const walletClient = (0, config_1.getWalletClient)(fromChainName);
59
+ const account = walletClient.account;
60
+ const fromChainId = CHAIN_IDS[fromChainName];
61
+ const toChainId = CHAIN_IDS[toChainName];
62
+ const fromTokenAddress = (0, tokens_1.resolveToken)(fromToken, fromChainName);
63
+ const toTokenAddress = (0, tokens_1.resolveToken)(toToken, toChainName);
64
+ const isNativeIn = fromTokenAddress === "0x0000000000000000000000000000000000000000";
65
+ // Get decimals
66
+ let decimals = 18;
67
+ if (!isNativeIn) {
68
+ decimals = await publicClient.readContract({
69
+ address: fromTokenAddress,
70
+ abi: tokens_1.ERC20_ABI,
71
+ functionName: 'decimals',
72
+ });
73
+ }
74
+ const amountWei = (0, viem_1.parseUnits)(amountStr, decimals).toString();
75
+ let txRequest = null;
76
+ let approvalAddress = null;
77
+ let expectedOutputStr = "";
78
+ const actualProvider = mode === "auto" ? "lifi" : providerName;
79
+ if (actualProvider === "lifi") {
80
+ const quote = await getLifiQuote(fromChainId, toChainId, fromTokenAddress, toTokenAddress, amountWei, account.address, slippagePercent / 100);
81
+ txRequest = quote.transactionRequest;
82
+ approvalAddress = quote.estimate.approvalAddress;
83
+ const toDecimals = quote.action.toToken.decimals;
84
+ expectedOutputStr = (0, viem_1.formatUnits)(BigInt(quote.estimate.toAmount), toDecimals);
85
+ }
86
+ else if (actualProvider === "relay") {
87
+ const relayQuote = await getRelayQuote(fromChainId, toChainId, fromTokenAddress, toTokenAddress, amountWei, account.address);
88
+ if (!relayQuote.steps || relayQuote.steps.length === 0)
89
+ throw new Error("No route found by Relay.");
90
+ const txStep = relayQuote.steps.find((s) => s.id === "execute");
91
+ if (!txStep || !txStep.items || txStep.items.length === 0)
92
+ throw new Error("Relay steps invalid.");
93
+ const item = txStep.items[0];
94
+ txRequest = item.data;
95
+ if (!isNativeIn && txRequest.to.toLowerCase() !== fromTokenAddress.toLowerCase()) {
96
+ approvalAddress = txRequest.to;
97
+ }
98
+ expectedOutputStr = relayQuote.details?.currencyOut?.amountFormatted || "Unknown";
99
+ }
100
+ let needsApprove = false;
101
+ if (!isNativeIn && approvalAddress && approvalAddress !== "0x0000000000000000000000000000000000000000") {
102
+ const allowance = await publicClient.readContract({
103
+ address: fromTokenAddress,
104
+ abi: tokens_1.ERC20_ABI,
105
+ functionName: 'allowance',
106
+ args: [account.address, approvalAddress],
107
+ });
108
+ if (allowance < BigInt(amountWei)) {
109
+ needsApprove = true;
110
+ }
111
+ }
112
+ const tx = transactionManager_1.txManager.createPendingTransaction('bridge', fromChainName, {
113
+ txRequest,
114
+ needsApprove,
115
+ fromTokenAddress,
116
+ approvalAddress,
117
+ amountWei
118
+ });
119
+ return `TRANSACTION_PENDING: Bridge simulated via ${actualProvider.toUpperCase()}. Expected Output on ${toChainName}: ~${expectedOutputStr} ${toToken.toUpperCase()}. ${needsApprove ? '(Auto-Approve required) ' : ''}Gas est: ${txRequest.gasLimit || 'auto'}. Transaction ID: ${tx.id}. Wait for user to approve.`;
120
+ }
121
+ catch (error) {
122
+ return `Simulation failed! Cannot prepare bridge. Error: ${error.message}`;
123
+ }
124
+ }
125
+ async function executeBridge(chainName, params) {
126
+ try {
127
+ const client = (0, config_1.getWalletClient)(chainName);
128
+ const publicClient = (0, config_1.getPublicClient)(chainName);
129
+ const { txRequest, needsApprove, fromTokenAddress, approvalAddress, amountWei } = params;
130
+ if (needsApprove) {
131
+ const approveHash = await client.writeContract({
132
+ account: client.account,
133
+ chain: client.chain,
134
+ address: fromTokenAddress,
135
+ abi: tokens_1.ERC20_ABI,
136
+ functionName: 'approve',
137
+ args: [approvalAddress, 115792089237316195423570985008687907853269984665640564039457584007913129639935n],
138
+ });
139
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
140
+ }
141
+ const txHash = await client.sendTransaction({
142
+ account: client.account,
143
+ chain: client.chain,
144
+ to: txRequest.to,
145
+ data: txRequest.data,
146
+ value: txRequest.value ? BigInt(txRequest.value) : 0n,
147
+ gas: txRequest.gasLimit ? (BigInt(txRequest.gasLimit) * 12n / 10n) : undefined
148
+ });
149
+ return `Bridge successful. Tx Hash: ${txHash}`;
150
+ }
151
+ catch (error) {
152
+ return `Failed to execute bridge: ${error.message}`;
153
+ }
154
+ }
155
+ exports.bridgeTokenToolDefinition = {
156
+ type: "function",
157
+ function: {
158
+ name: "bridge_token",
159
+ description: "Executes a cross-chain token bridge from one network to another using Li.Fi or Relay. Automatically simulates to fetch quotes.",
160
+ parameters: {
161
+ type: "object",
162
+ properties: {
163
+ fromChainName: {
164
+ type: "string",
165
+ enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
166
+ description: "The source blockchain network",
167
+ },
168
+ toChainName: {
169
+ type: "string",
170
+ enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
171
+ description: "The destination blockchain network",
172
+ },
173
+ fromToken: {
174
+ type: "string",
175
+ description: "The token symbol to sell on source chain (e.g., 'ETH', 'USDC')",
176
+ },
177
+ toToken: {
178
+ type: "string",
179
+ description: "The token symbol to buy on destination chain (e.g., 'USDC', 'UNI')",
180
+ },
181
+ amountStr: {
182
+ type: "string",
183
+ description: "The amount of fromToken to bridge",
184
+ },
185
+ mode: {
186
+ type: "string",
187
+ enum: ["auto", "manual"],
188
+ description: "auto uses lifi. manual uses the specified provider."
189
+ },
190
+ providerName: {
191
+ type: "string",
192
+ enum: ["lifi", "relay"],
193
+ description: "Used if mode is manual."
194
+ }
195
+ },
196
+ required: ["fromChainName", "toChainName", "fromToken", "toToken", "amountStr"],
197
+ },
198
+ },
199
+ };
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.customTxToolDefinition = void 0;
4
+ exports.prepareCustomTx = prepareCustomTx;
5
+ exports.executeCustomTx = executeCustomTx;
6
+ const viem_1 = require("viem");
7
+ const config_1 = require("../config");
8
+ const transactionManager_1 = require("../../agent/transactionManager");
9
+ async function prepareCustomTx(chainName, toAddress, dataHex, valueEth = "0", gasLimitStr) {
10
+ try {
11
+ const publicClient = (0, config_1.getPublicClient)(chainName);
12
+ const walletClient = (0, config_1.getWalletClient)(chainName);
13
+ const account = walletClient.account;
14
+ if (!dataHex.startsWith("0x")) {
15
+ throw new Error("Data must start with 0x (Hex format)");
16
+ }
17
+ const valueWei = (0, viem_1.parseEther)(valueEth);
18
+ let gasEstimate = 0n;
19
+ if (gasLimitStr) {
20
+ gasEstimate = BigInt(gasLimitStr);
21
+ }
22
+ else {
23
+ gasEstimate = await publicClient.estimateGas({
24
+ account,
25
+ to: toAddress,
26
+ data: dataHex,
27
+ value: valueWei,
28
+ });
29
+ }
30
+ const tx = transactionManager_1.txManager.createPendingTransaction('custom', chainName, {
31
+ toAddress,
32
+ dataHex,
33
+ valueWei: valueWei.toString(),
34
+ gasEstimate: gasEstimate.toString()
35
+ });
36
+ return `TRANSACTION_PENDING: Simulated Custom Transaction successfully. Estimated gas units: ${gasEstimate}. Transaction ID: ${tx.id}. Wait for user to approve.`;
37
+ }
38
+ catch (error) {
39
+ return `Simulation failed! Cannot prepare custom tx. Error: ${error.message}`;
40
+ }
41
+ }
42
+ async function executeCustomTx(chainName, params) {
43
+ try {
44
+ const client = (0, config_1.getWalletClient)(chainName);
45
+ const { toAddress, dataHex, valueWei, gasEstimate } = params;
46
+ const hash = await client.sendTransaction({
47
+ account: client.account,
48
+ chain: client.chain,
49
+ to: toAddress,
50
+ data: dataHex,
51
+ value: BigInt(valueWei),
52
+ gas: gasEstimate ? BigInt(gasEstimate) * 12n / 10n : undefined, // 20% buffer
53
+ });
54
+ return `Custom transaction successful. Tx Hash: ${hash}`;
55
+ }
56
+ catch (error) {
57
+ return `Failed to execute custom transaction: ${error.message}`;
58
+ }
59
+ }
60
+ exports.customTxToolDefinition = {
61
+ type: "function",
62
+ function: {
63
+ name: "custom_tx",
64
+ description: "Executes a raw custom transaction with calldata (hex) on a specific blockchain network. Automatically simulates the execution.",
65
+ parameters: {
66
+ type: "object",
67
+ properties: {
68
+ chainName: {
69
+ type: "string",
70
+ enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
71
+ description: "The blockchain network",
72
+ },
73
+ toAddress: {
74
+ type: "string",
75
+ description: "The destination contract or wallet address (0x...)",
76
+ },
77
+ dataHex: {
78
+ type: "string",
79
+ description: "The raw calldata payload in hex format (starting with 0x)",
80
+ },
81
+ valueEth: {
82
+ type: "string",
83
+ description: "The amount of native ETH/BNB to attach. Default is '0'.",
84
+ },
85
+ gasLimitStr: {
86
+ type: "string",
87
+ description: "Optional custom gas limit as a string. If omitted, the node will estimate it.",
88
+ }
89
+ },
90
+ required: ["chainName", "toAddress", "dataHex", "valueEth"],
91
+ },
92
+ },
93
+ };
@@ -37,7 +37,8 @@ exports.getBalanceToolDefinition = void 0;
37
37
  exports.getBalance = getBalance;
38
38
  const viem_1 = require("viem");
39
39
  const config_1 = require("../config");
40
- async function getBalance(chainName, address) {
40
+ const tokens_1 = require("../utils/tokens");
41
+ async function getBalance(chainName, address, token) {
41
42
  try {
42
43
  const client = (0, config_1.getPublicClient)(chainName);
43
44
  let targetAddress = address;
@@ -48,9 +49,41 @@ async function getBalance(chainName, address) {
48
49
  if (!targetAddress) {
49
50
  throw new Error('Address is required but could not be resolved from private key.');
50
51
  }
51
- const balanceWei = await client.getBalance({ address: targetAddress });
52
- const balanceEth = (0, viem_1.formatEther)(balanceWei);
53
- return `${balanceEth} on ${chainName}`;
52
+ if (token) {
53
+ const tokenAddress = (0, tokens_1.resolveToken)(token, chainName);
54
+ if (tokenAddress === "0x0000000000000000000000000000000000000000") {
55
+ const balanceWei = await client.getBalance({ address: targetAddress });
56
+ const balanceEth = (0, viem_1.formatEther)(balanceWei);
57
+ return `${balanceEth} on ${chainName}`;
58
+ }
59
+ else {
60
+ const [balanceWei, decimals, symbol] = await Promise.all([
61
+ client.readContract({
62
+ address: tokenAddress,
63
+ abi: tokens_1.ERC20_ABI,
64
+ functionName: 'balanceOf',
65
+ args: [targetAddress],
66
+ }),
67
+ client.readContract({
68
+ address: tokenAddress,
69
+ abi: tokens_1.ERC20_ABI,
70
+ functionName: 'decimals',
71
+ }),
72
+ client.readContract({
73
+ address: tokenAddress,
74
+ abi: tokens_1.ERC20_ABI,
75
+ functionName: 'symbol',
76
+ }).catch(() => token)
77
+ ]);
78
+ const balanceFormatted = (0, viem_1.formatUnits)(balanceWei, decimals);
79
+ return `${balanceFormatted} ${symbol} on ${chainName}`;
80
+ }
81
+ }
82
+ else {
83
+ const balanceWei = await client.getBalance({ address: targetAddress });
84
+ const balanceEth = (0, viem_1.formatEther)(balanceWei);
85
+ return `${balanceEth} on ${chainName}`;
86
+ }
54
87
  }
55
88
  catch (error) {
56
89
  return `Failed to get balance: ${error.message}`;
@@ -60,7 +93,7 @@ exports.getBalanceToolDefinition = {
60
93
  type: "function",
61
94
  function: {
62
95
  name: "get_balance",
63
- description: "Get the native token balance (ETH, BNB, etc) of a wallet address on a specific chain. If address is omitted, it returns the balance of the agent's own wallet.",
96
+ description: "Get the native or ERC-20 token balance of a wallet address on a specific chain. If address is omitted, it returns the balance of the agent's own wallet.",
64
97
  parameters: {
65
98
  type: "object",
66
99
  properties: {
@@ -72,6 +105,10 @@ exports.getBalanceToolDefinition = {
72
105
  address: {
73
106
  type: "string",
74
107
  description: "Optional. The 0x... address of the wallet. If not provided, it uses the agent's wallet."
108
+ },
109
+ token: {
110
+ type: "string",
111
+ description: "Optional. The token symbol (e.g. USDC, USDT, WETH) or contract address (0x...) to check. If omitted, checks the native coin (ETH/BNB)."
75
112
  }
76
113
  },
77
114
  required: ["chainName"]
@@ -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,200 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.swapTokenToolDefinition = void 0;
4
- exports.swapToken = swapToken;
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 body = {
36
+ user: userAddress,
37
+ originChainId: fromChainId,
38
+ destinationChainId: toChainId,
39
+ originCurrency: fromToken,
40
+ destinationCurrency: toToken,
41
+ amount: amountWei,
42
+ tradeType: "EXACT_INPUT"
43
+ };
44
+ const res = await fetch("https://api.relay.link/quote", {
45
+ method: "POST",
46
+ headers: { "Content-Type": "application/json" },
47
+ body: JSON.stringify(body)
48
+ });
49
+ if (!res.ok) {
50
+ const err = await res.json().catch(() => ({}));
51
+ throw new Error(`Relay API Error: ${err.message || res.statusText}`);
52
+ }
53
+ return await res.json();
54
+ }
55
+ async function prepareSwapToken(chainName, fromToken, toToken, amountStr, mode = "auto", providerName = "lifi", slippagePercent = 0.5) {
56
+ try {
57
+ const publicClient = (0, config_1.getPublicClient)(chainName);
58
+ const walletClient = (0, config_1.getWalletClient)(chainName);
59
+ const account = walletClient.account;
60
+ const chainId = CHAIN_IDS[chainName];
61
+ const fromTokenAddress = (0, tokens_1.resolveToken)(fromToken, chainName);
62
+ const toTokenAddress = (0, tokens_1.resolveToken)(toToken, chainName);
63
+ const isNativeIn = fromTokenAddress === "0x0000000000000000000000000000000000000000";
64
+ // Get decimals
65
+ let decimals = 18;
66
+ if (!isNativeIn) {
67
+ decimals = await publicClient.readContract({
68
+ address: fromTokenAddress,
69
+ abi: tokens_1.ERC20_ABI,
70
+ functionName: 'decimals',
71
+ });
72
+ }
73
+ const amountWei = (0, viem_1.parseUnits)(amountStr, decimals).toString();
74
+ let txRequest = null;
75
+ let approvalAddress = null;
76
+ let expectedOutputStr = "";
77
+ const actualProvider = mode === "auto" ? "lifi" : providerName;
78
+ if (actualProvider === "lifi") {
79
+ const quote = await getLifiQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, account.address, slippagePercent / 100);
80
+ txRequest = quote.transactionRequest;
81
+ approvalAddress = quote.estimate.approvalAddress;
82
+ const toDecimals = quote.action.toToken.decimals;
83
+ expectedOutputStr = (0, viem_1.formatUnits)(BigInt(quote.estimate.toAmount), toDecimals);
84
+ }
85
+ else if (actualProvider === "relay") {
86
+ const relayQuote = await getRelayQuote(chainId, chainId, fromTokenAddress, toTokenAddress, amountWei, account.address);
87
+ if (!relayQuote.steps || relayQuote.steps.length === 0)
88
+ throw new Error("No route found by Relay.");
89
+ // Relay returns steps. We need to find the main transaction step.
90
+ const txStep = relayQuote.steps.find((s) => s.id === "execute");
91
+ if (!txStep || !txStep.items || txStep.items.length === 0)
92
+ throw new Error("Relay steps invalid.");
93
+ const item = txStep.items[0];
94
+ txRequest = item.data;
95
+ // Usually Relay route approval to `txRequest.to` if it's not native
96
+ if (!isNativeIn && txRequest.to.toLowerCase() !== fromTokenAddress.toLowerCase()) {
97
+ approvalAddress = txRequest.to;
98
+ }
99
+ expectedOutputStr = relayQuote.details?.currencyOut?.amountFormatted || "Unknown";
100
+ }
101
+ // Check allowance early so we know if we need to auto-approve
102
+ let needsApprove = false;
103
+ if (!isNativeIn && approvalAddress && approvalAddress !== "0x0000000000000000000000000000000000000000") {
104
+ const allowance = await publicClient.readContract({
105
+ address: fromTokenAddress,
106
+ abi: tokens_1.ERC20_ABI,
107
+ functionName: 'allowance',
108
+ args: [account.address, approvalAddress],
109
+ });
110
+ if (allowance < BigInt(amountWei)) {
111
+ needsApprove = true;
112
+ }
113
+ }
114
+ const tx = transactionManager_1.txManager.createPendingTransaction('swap', chainName, {
115
+ txRequest,
116
+ needsApprove,
117
+ fromTokenAddress,
118
+ approvalAddress,
119
+ amountWei
120
+ });
121
+ 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.`;
122
+ }
123
+ catch (error) {
124
+ return `Simulation failed! Cannot prepare swap. Error: ${error.message}`;
125
+ }
126
+ }
127
+ async function executeSwap(chainName, params) {
128
+ try {
129
+ const client = (0, config_1.getWalletClient)(chainName);
130
+ const publicClient = (0, config_1.getPublicClient)(chainName);
131
+ const { txRequest, needsApprove, fromTokenAddress, approvalAddress, amountWei } = params;
132
+ if (needsApprove) {
133
+ // Auto-Approve Transaction
134
+ const approveHash = await client.writeContract({
135
+ account: client.account,
136
+ chain: client.chain,
137
+ address: fromTokenAddress,
138
+ abi: tokens_1.ERC20_ABI,
139
+ functionName: 'approve',
140
+ // Max Uint256
141
+ args: [approvalAddress, 115792089237316195423570985008687907853269984665640564039457584007913129639935n],
142
+ });
143
+ await publicClient.waitForTransactionReceipt({ hash: approveHash });
144
+ }
145
+ // Execute Swap
146
+ // @ts-ignore
147
+ const txHash = await client.sendTransaction({
148
+ account: client.account,
149
+ chain: client.chain,
150
+ to: txRequest.to,
151
+ data: txRequest.data,
152
+ value: txRequest.value ? BigInt(txRequest.value) : 0n,
153
+ gas: txRequest.gasLimit ? (BigInt(txRequest.gasLimit) * 12n / 10n) : undefined // 20% buffer
154
+ });
155
+ return `Swap successful. Tx Hash: ${txHash}`;
156
+ }
157
+ catch (error) {
158
+ return `Failed to execute swap: ${error.message}`;
159
+ }
160
+ }
6
161
  exports.swapTokenToolDefinition = {
7
162
  type: "function",
8
163
  function: {
9
164
  name: "swap_token",
10
- description: "Executes a decentralized token swap (DEX) to exchange one cryptocurrency for another.",
165
+ 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
166
  parameters: {
12
167
  type: "object",
13
168
  properties: {
14
169
  chainName: {
15
170
  type: "string",
16
- description: "The blockchain network (e.g., 'ethereum', 'base', 'arbitrum', 'sepolia')",
171
+ enum: ["ethereum", "base", "bsc", "arbitrum", "optimism", "sepolia"],
172
+ description: "The blockchain network",
17
173
  },
18
174
  fromToken: {
19
175
  type: "string",
20
- description: "The token symbol to sell/swap from (e.g., 'ETH', 'USDC')",
176
+ description: "The token symbol to sell (e.g., 'ETH', 'USDC')",
21
177
  },
22
178
  toToken: {
23
179
  type: "string",
24
- description: "The token symbol to buy/swap to (e.g., 'USDC', 'UNI')",
180
+ description: "The token symbol to buy (e.g., 'USDC', 'UNI')",
25
181
  },
26
- amount: {
27
- type: "number",
182
+ amountStr: {
183
+ type: "string",
28
184
  description: "The amount of fromToken to swap",
29
185
  },
186
+ mode: {
187
+ type: "string",
188
+ enum: ["auto", "manual"],
189
+ description: "auto uses lifi. manual uses the specified provider."
190
+ },
191
+ providerName: {
192
+ type: "string",
193
+ enum: ["lifi", "relay"],
194
+ description: "Used if mode is manual."
195
+ }
30
196
  },
31
- required: ["chainName", "fromToken", "toToken", "amount"],
197
+ required: ["chainName", "fromToken", "toToken", "amountStr"],
32
198
  },
33
199
  },
34
200
  };
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.transferNative = transferNative;
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
- async function transferNative(chainName, toAddress, amountEth) {
10
- const tx = transactionManager_1.txManager.createPendingTransaction('transfer', chainName, { toAddress, amountEth });
11
- return `TRANSACTION_PENDING: I have prepared the transfer. Transaction ID: ${tx.id}. Wait for user to approve.`;
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, toAddress, amountEth) {
71
+ async function executeTransfer(chainName, params) {
14
72
  try {
15
73
  const client = (0, config_1.getWalletClient)(chainName);
16
- const hash = await client.sendTransaction({
17
- account: client.account,
18
- chain: client.chain,
19
- to: toAddress,
20
- value: (0, viem_1.parseEther)(amountEth),
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: "transfer_native",
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
- amountEth: {
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 amount of tokens to send in ETH units (e.g. '0.01')."
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", "amountEth"]
128
+ required: ["chainName", "toAddress", "amountStr"]
51
129
  }
52
130
  }
53
131
  };
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TOKEN_MAP = exports.ERC20_ABI = void 0;
4
+ exports.resolveToken = resolveToken;
5
+ exports.ERC20_ABI = [
6
+ {
7
+ type: 'function',
8
+ name: 'balanceOf',
9
+ inputs: [{ name: 'account', type: 'address' }],
10
+ outputs: [{ type: 'uint256' }],
11
+ stateMutability: 'view',
12
+ },
13
+ {
14
+ type: 'function',
15
+ name: 'transfer',
16
+ inputs: [
17
+ { name: 'to', type: 'address' },
18
+ { name: 'value', type: 'uint256' }
19
+ ],
20
+ outputs: [{ type: 'bool' }],
21
+ stateMutability: 'nonpayable',
22
+ },
23
+ {
24
+ type: 'function',
25
+ name: 'approve',
26
+ inputs: [
27
+ { name: 'spender', type: 'address' },
28
+ { name: 'value', type: 'uint256' }
29
+ ],
30
+ outputs: [{ type: 'bool' }],
31
+ stateMutability: 'nonpayable',
32
+ },
33
+ {
34
+ type: 'function',
35
+ name: 'allowance',
36
+ inputs: [
37
+ { name: 'owner', type: 'address' },
38
+ { name: 'spender', type: 'address' }
39
+ ],
40
+ outputs: [{ type: 'uint256' }],
41
+ stateMutability: 'view',
42
+ },
43
+ {
44
+ type: 'function',
45
+ name: 'decimals',
46
+ inputs: [],
47
+ outputs: [{ type: 'uint8' }],
48
+ stateMutability: 'view',
49
+ },
50
+ {
51
+ type: 'function',
52
+ name: 'symbol',
53
+ inputs: [],
54
+ outputs: [{ type: 'string' }],
55
+ stateMutability: 'view',
56
+ }
57
+ ];
58
+ exports.TOKEN_MAP = {
59
+ ethereum: {
60
+ ETH: "0x0000000000000000000000000000000000000000",
61
+ WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
62
+ USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
63
+ USDT: "0xdAC17F958D2ee523a2206206994597C13D831ec7"
64
+ },
65
+ arbitrum: {
66
+ ETH: "0x0000000000000000000000000000000000000000",
67
+ WETH: "0x82aF49447D8a07e3bd95BD0d56f352415231Cl11",
68
+ USDC: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
69
+ "USDC.E": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
70
+ USDT: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"
71
+ },
72
+ base: {
73
+ ETH: "0x0000000000000000000000000000000000000000",
74
+ WETH: "0x4200000000000000000000000000000000000006",
75
+ USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
76
+ USDT: "0xf55BEC9cbd4732f1F4143f647652e924540d9d64"
77
+ },
78
+ optimism: {
79
+ ETH: "0x0000000000000000000000000000000000000000",
80
+ WETH: "0x4200000000000000000000000000000000000006",
81
+ USDC: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
82
+ USDT: "0x94b008aA00579c1307b0EF2c499aD98a8ce58e58"
83
+ },
84
+ bsc: {
85
+ BNB: "0x0000000000000000000000000000000000000000",
86
+ WBNB: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
87
+ USDC: "0x8AC76a51cc950d9822D68b83fE1Ad97B32CD580d",
88
+ USDT: "0x55d398326f99059fF775485246999027B3197955"
89
+ },
90
+ sepolia: {
91
+ ETH: "0x0000000000000000000000000000000000000000",
92
+ WETH: "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14",
93
+ USDC: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" // Circle Faucet Sepolia USDC
94
+ }
95
+ };
96
+ function resolveToken(tokenSymbolOrAddress, chainName) {
97
+ if (tokenSymbolOrAddress.startsWith("0x") && tokenSymbolOrAddress.length === 42) {
98
+ return tokenSymbolOrAddress;
99
+ }
100
+ const symbolUpper = tokenSymbolOrAddress.toUpperCase().trim();
101
+ if (["ETH", "MATIC", "POL", "BNB", "AVAX", "NATIVE"].includes(symbolUpper)) {
102
+ return "0x0000000000000000000000000000000000000000";
103
+ }
104
+ const chainTokens = exports.TOKEN_MAP[chainName];
105
+ if (chainTokens && chainTokens[symbolUpper]) {
106
+ return chainTokens[symbolUpper];
107
+ }
108
+ throw new Error(`Token "${tokenSymbolOrAddress}" pada chain ${chainName} tidak ditemukan. Silakan gunakan alamat kontrak langsung (0x...).`);
109
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nyxora",
3
- "version": "1.1.8",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "dist/gateway/cli.js",
6
6
  "files": [