@darksol/terminal 0.7.2 → 0.8.1
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 +13 -5
- package/package.json +1 -1
- package/skill/SKILL.md +19 -6
- package/src/cli.js +94 -3
- package/src/config/keys.js +8 -0
- package/src/services/casino.js +26 -76
- package/src/services/lifi.js +713 -0
- package/src/trading/index.js +1 -0
- package/src/web/commands.js +222 -8
package/src/trading/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { executeSwap, resolveToken, getTokenInfo } from './swap.js';
|
|
2
2
|
export { snipeToken, watchSnipe } from './snipe.js';
|
|
3
3
|
export { createDCA, listDCA, cancelDCA, runDCA } from './dca.js';
|
|
4
|
+
export { executeLifiSwap, executeLifiBridge, checkBridgeStatus, showSupportedChains } from '../services/lifi.js';
|
package/src/web/commands.js
CHANGED
|
@@ -223,7 +223,7 @@ export async function handleMenuSelect(id, value, item, ws) {
|
|
|
223
223
|
|
|
224
224
|
case 'trade_action':
|
|
225
225
|
if (value === 'swap') {
|
|
226
|
-
ws.sendMenu('trade_swap_pair', '◆ Swap Pair', [
|
|
226
|
+
ws.sendMenu('trade_swap_pair', '◆ Swap Pair (via LI.FI — best route across 31 DEXs)', [
|
|
227
227
|
{ value: 'ETH->USDC', label: 'ETH → USDC', desc: 'Most common' },
|
|
228
228
|
{ value: 'USDC->ETH', label: 'USDC → ETH', desc: 'Reverse' },
|
|
229
229
|
{ value: 'ETH->AERO', label: 'ETH → AERO', desc: 'Base ecosystem' },
|
|
@@ -233,6 +233,16 @@ export async function handleMenuSelect(id, value, item, ws) {
|
|
|
233
233
|
]);
|
|
234
234
|
return {};
|
|
235
235
|
}
|
|
236
|
+
if (value === 'bridge') {
|
|
237
|
+
const currentChain = getConfig('chain') || 'base';
|
|
238
|
+
const chains = ['base', 'ethereum', 'arbitrum', 'optimism', 'polygon', 'avalanche', 'bsc', 'zksync', 'scroll', 'linea'];
|
|
239
|
+
ws.sendMenu('bridge_from_chain', '◆ Bridge From Chain', chains.map(c => ({
|
|
240
|
+
value: c,
|
|
241
|
+
label: c === currentChain ? `★ ${c}` : c,
|
|
242
|
+
desc: c === currentChain ? 'current' : '',
|
|
243
|
+
})));
|
|
244
|
+
return {};
|
|
245
|
+
}
|
|
236
246
|
if (value === 'snipe') {
|
|
237
247
|
ws.sendPrompt('trade_snipe_token', 'Token contract (0x...):', {});
|
|
238
248
|
return {};
|
|
@@ -280,6 +290,63 @@ export async function handleMenuSelect(id, value, item, ws) {
|
|
|
280
290
|
});
|
|
281
291
|
return {};
|
|
282
292
|
|
|
293
|
+
case 'bridge_from_chain': {
|
|
294
|
+
if (value === 'back') return {};
|
|
295
|
+
const chains = ['base', 'ethereum', 'arbitrum', 'optimism', 'polygon', 'avalanche', 'bsc', 'zksync', 'scroll', 'linea'];
|
|
296
|
+
const destChains = chains.filter(c => c !== value);
|
|
297
|
+
ws.sendMenu('bridge_to_chain', `◆ Bridge To Chain (from ${value})`, destChains.map(c => ({
|
|
298
|
+
value: c,
|
|
299
|
+
label: c,
|
|
300
|
+
desc: '',
|
|
301
|
+
meta: { fromChain: value },
|
|
302
|
+
})));
|
|
303
|
+
return {};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case 'bridge_to_chain': {
|
|
307
|
+
if (value === 'back') return {};
|
|
308
|
+
const fromChain = item?.meta?.fromChain || 'base';
|
|
309
|
+
ws.sendMenu('bridge_token', `◆ Token to Bridge (${fromChain} → ${value})`, [
|
|
310
|
+
{ value: 'ETH', label: 'ETH', desc: 'Native token', meta: { fromChain, toChain: value } },
|
|
311
|
+
{ value: 'USDC', label: 'USDC', desc: 'Stablecoin', meta: { fromChain, toChain: value } },
|
|
312
|
+
{ value: 'USDT', label: 'USDT', desc: 'Tether', meta: { fromChain, toChain: value } },
|
|
313
|
+
{ value: 'custom', label: 'Custom token', desc: 'Enter symbol', meta: { fromChain, toChain: value } },
|
|
314
|
+
{ value: 'back', label: '← Back', desc: '' },
|
|
315
|
+
]);
|
|
316
|
+
return {};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
case 'bridge_token': {
|
|
320
|
+
if (value === 'back') return {};
|
|
321
|
+
if (value === 'custom') {
|
|
322
|
+
ws.sendPrompt('bridge_custom_token', 'Token symbol or address:', item?.meta || {});
|
|
323
|
+
return {};
|
|
324
|
+
}
|
|
325
|
+
const m = item?.meta || {};
|
|
326
|
+
ws.sendMenu('bridge_amount', `◆ Amount (${value} · ${m.fromChain} → ${m.toChain})`, [
|
|
327
|
+
{ value: '0.01', label: `0.01 ${value}`, desc: 'small', meta: { ...m, token: value } },
|
|
328
|
+
{ value: '0.05', label: `0.05 ${value}`, desc: 'standard', meta: { ...m, token: value } },
|
|
329
|
+
{ value: '0.1', label: `0.1 ${value}`, desc: 'medium', meta: { ...m, token: value } },
|
|
330
|
+
{ value: '0.5', label: `0.5 ${value}`, desc: 'large', meta: { ...m, token: value } },
|
|
331
|
+
{ value: '1', label: `1 ${value}`, desc: 'large', meta: { ...m, token: value } },
|
|
332
|
+
{ value: 'custom', label: 'Custom amount', desc: '', meta: { ...m, token: value } },
|
|
333
|
+
]);
|
|
334
|
+
return {};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
case 'bridge_amount': {
|
|
338
|
+
if (value === 'custom') {
|
|
339
|
+
ws.sendPrompt('bridge_custom_amount', `Amount (${item?.meta?.token || 'token'}):`, item?.meta || {});
|
|
340
|
+
return {};
|
|
341
|
+
}
|
|
342
|
+
ws.sendPrompt('bridge_password', `Wallet password (bridge ${value} ${item?.meta?.token || ''} · ${item?.meta?.fromChain || ''} → ${item?.meta?.toChain || ''}):`, {
|
|
343
|
+
...(item?.meta || {}),
|
|
344
|
+
amount: value,
|
|
345
|
+
mask: true,
|
|
346
|
+
});
|
|
347
|
+
return {};
|
|
348
|
+
}
|
|
349
|
+
|
|
283
350
|
case 'send_token':
|
|
284
351
|
if (value === 'back') return {};
|
|
285
352
|
if (value === 'custom') {
|
|
@@ -536,6 +603,66 @@ export async function handlePromptResponse(id, value, meta, ws) {
|
|
|
536
603
|
return {};
|
|
537
604
|
}
|
|
538
605
|
|
|
606
|
+
if (id === 'bridge_custom_token') {
|
|
607
|
+
if (!value) { ws.sendLine(` ${ANSI.red}✗ Cancelled${ANSI.reset}`); ws.sendLine(''); return {}; }
|
|
608
|
+
const m = meta || {};
|
|
609
|
+
ws.sendMenu('bridge_amount', `◆ Amount (${value} · ${m.fromChain} → ${m.toChain})`, [
|
|
610
|
+
{ value: '0.01', label: `0.01 ${value}`, desc: 'small', meta: { ...m, token: value.trim() } },
|
|
611
|
+
{ value: '0.05', label: `0.05 ${value}`, desc: 'standard', meta: { ...m, token: value.trim() } },
|
|
612
|
+
{ value: '0.1', label: `0.1 ${value}`, desc: 'medium', meta: { ...m, token: value.trim() } },
|
|
613
|
+
{ value: '0.5', label: `0.5 ${value}`, desc: 'large', meta: { ...m, token: value.trim() } },
|
|
614
|
+
{ value: '1', label: `1 ${value}`, desc: 'large', meta: { ...m, token: value.trim() } },
|
|
615
|
+
{ value: 'custom', label: 'Custom amount', desc: '', meta: { ...m, token: value.trim() } },
|
|
616
|
+
]);
|
|
617
|
+
return {};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (id === 'bridge_custom_amount') {
|
|
621
|
+
const n = Number(value);
|
|
622
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
623
|
+
ws.sendLine(` ${ANSI.red}✗ Invalid amount${ANSI.reset}`);
|
|
624
|
+
ws.sendLine('');
|
|
625
|
+
return {};
|
|
626
|
+
}
|
|
627
|
+
ws.sendPrompt('bridge_password', `Wallet password (bridge ${value} ${meta?.token || ''} · ${meta?.fromChain || ''} → ${meta?.toChain || ''}):`, {
|
|
628
|
+
...meta,
|
|
629
|
+
amount: String(value),
|
|
630
|
+
mask: true,
|
|
631
|
+
});
|
|
632
|
+
return {};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (id === 'bridge_password') {
|
|
636
|
+
if (!meta?.fromChain || !meta?.toChain || !meta?.token || !meta?.amount || !value) {
|
|
637
|
+
ws.sendLine(` ${ANSI.red}✗ Missing bridge details${ANSI.reset}`);
|
|
638
|
+
ws.sendLine('');
|
|
639
|
+
return {};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
ws.sendLine('');
|
|
643
|
+
ws.sendLine(` ${ANSI.dim}Bridging ${meta.amount} ${meta.token} from ${meta.fromChain} → ${meta.toChain} via LI.FI...${ANSI.reset}`);
|
|
644
|
+
ws.sendLine('');
|
|
645
|
+
|
|
646
|
+
try {
|
|
647
|
+
const { executeLifiBridge } = await import('../services/lifi.js');
|
|
648
|
+
await executeLifiBridge({
|
|
649
|
+
fromChain: meta.fromChain,
|
|
650
|
+
toChain: meta.toChain,
|
|
651
|
+
token: meta.token,
|
|
652
|
+
amount: meta.amount,
|
|
653
|
+
slippage: parseFloat(getConfig('slippage') || 0.5),
|
|
654
|
+
wallet: getConfig('activeWallet'),
|
|
655
|
+
password: value,
|
|
656
|
+
confirm: true,
|
|
657
|
+
});
|
|
658
|
+
ws.sendLine(` ${ANSI.green}✓ Bridge flow completed (check terminal output for receipt)${ANSI.reset}`);
|
|
659
|
+
} catch (err) {
|
|
660
|
+
ws.sendLine(` ${ANSI.red}✗ Bridge failed: ${err.message}${ANSI.reset}`);
|
|
661
|
+
}
|
|
662
|
+
ws.sendLine('');
|
|
663
|
+
return {};
|
|
664
|
+
}
|
|
665
|
+
|
|
539
666
|
if (id === 'trade_swap_password') {
|
|
540
667
|
if (!meta?.tokenIn || !meta?.tokenOut || !meta?.amount || !value) {
|
|
541
668
|
ws.sendLine(` ${ANSI.red}✗ Missing swap details${ANSI.reset}`);
|
|
@@ -543,12 +670,13 @@ export async function handlePromptResponse(id, value, meta, ws) {
|
|
|
543
670
|
return {};
|
|
544
671
|
}
|
|
545
672
|
|
|
546
|
-
ws.sendLine(` ${ANSI.dim}Executing swap ${meta.amount} ${meta.tokenIn} → ${meta.tokenOut}...${ANSI.reset}`);
|
|
673
|
+
ws.sendLine(` ${ANSI.dim}Executing swap ${meta.amount} ${meta.tokenIn} → ${meta.tokenOut} via LI.FI...${ANSI.reset}`);
|
|
547
674
|
ws.sendLine('');
|
|
548
675
|
|
|
549
676
|
try {
|
|
550
|
-
|
|
551
|
-
await
|
|
677
|
+
// Try LI.FI first, fall back to direct Uniswap
|
|
678
|
+
const { executeLifiSwap } = await import('../services/lifi.js');
|
|
679
|
+
const swapOpts = {
|
|
552
680
|
tokenIn: meta.tokenIn,
|
|
553
681
|
tokenOut: meta.tokenOut,
|
|
554
682
|
amount: meta.amount,
|
|
@@ -556,8 +684,18 @@ export async function handlePromptResponse(id, value, meta, ws) {
|
|
|
556
684
|
wallet: getConfig('activeWallet'),
|
|
557
685
|
password: value,
|
|
558
686
|
confirm: true,
|
|
559
|
-
}
|
|
560
|
-
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
const result = await executeLifiSwap(swapOpts);
|
|
690
|
+
if (result?.success) {
|
|
691
|
+
ws.sendLine(` ${ANSI.green}✓ Swap completed via LI.FI${ANSI.reset}`);
|
|
692
|
+
} else if (result?.error !== 'cancelled') {
|
|
693
|
+
// Fallback to direct Uniswap
|
|
694
|
+
ws.sendLine(` ${ANSI.darkGold}LI.FI route unavailable — trying direct Uniswap V3...${ANSI.reset}`);
|
|
695
|
+
const { executeSwap } = await import('../trading/swap.js');
|
|
696
|
+
await executeSwap(swapOpts);
|
|
697
|
+
ws.sendLine(` ${ANSI.green}✓ Swap completed via Uniswap V3${ANSI.reset}`);
|
|
698
|
+
}
|
|
561
699
|
} catch (err) {
|
|
562
700
|
ws.sendLine(` ${ANSI.red}✗ Swap failed: ${err.message}${ANSI.reset}`);
|
|
563
701
|
}
|
|
@@ -686,6 +824,8 @@ export async function handleCommand(cmd, ws) {
|
|
|
686
824
|
return await cmdMarket(args, ws);
|
|
687
825
|
case 'trade':
|
|
688
826
|
return await cmdTrade(args, ws);
|
|
827
|
+
case 'bridge':
|
|
828
|
+
return await cmdBridge(args, ws);
|
|
689
829
|
case 'wallet':
|
|
690
830
|
return await cmdWallet(args, ws);
|
|
691
831
|
case 'mail':
|
|
@@ -719,7 +859,7 @@ export async function handleCommand(cmd, ws) {
|
|
|
719
859
|
return await cmdChatLogs(args, ws);
|
|
720
860
|
default: {
|
|
721
861
|
// Fuzzy: if it looks like natural language, route to AI
|
|
722
|
-
const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca|order|card|prepaid|visa|mastercard|bet|coinflip|flip|dice|slots|hilo|gamble|play|casino)\b/i;
|
|
862
|
+
const nlKeywords = /\b(swap|buy|sell|send|transfer|price|what|how|should|analyze|check|balance|gas|dca|order|card|prepaid|visa|mastercard|bet|coinflip|flip|dice|slots|hilo|gamble|play|casino|bridge|cross-chain|crosschain)\b/i;
|
|
723
863
|
if (nlKeywords.test(cmd)) {
|
|
724
864
|
return await cmdAI(cmd.split(/\s+/), ws);
|
|
725
865
|
}
|
|
@@ -986,7 +1126,8 @@ async function cmdTrade(args, ws) {
|
|
|
986
1126
|
ws.sendLine('');
|
|
987
1127
|
|
|
988
1128
|
ws.sendMenu('trade_action', '◆ Trade Actions', [
|
|
989
|
-
{ value: 'swap', label: '🔄 Swap', desc: '
|
|
1129
|
+
{ value: 'swap', label: '🔄 Swap', desc: 'Token swap via LI.FI (best route across 31 DEXs)' },
|
|
1130
|
+
{ value: 'bridge', label: '🌉 Bridge', desc: 'Cross-chain transfer via LI.FI (60 chains)' },
|
|
990
1131
|
{ value: 'snipe', label: '⚡ Snipe', desc: 'Fast buy by token contract' },
|
|
991
1132
|
{ value: 'watch', label: '👀 Watch Pairs', desc: 'Monitor new pairs (CLI guidance)' },
|
|
992
1133
|
{ value: 'back', label: '← Back', desc: '' },
|
|
@@ -995,6 +1136,79 @@ async function cmdTrade(args, ws) {
|
|
|
995
1136
|
return {};
|
|
996
1137
|
}
|
|
997
1138
|
|
|
1139
|
+
// ══════════════════════════════════════════════════
|
|
1140
|
+
// BRIDGE (LI.FI cross-chain)
|
|
1141
|
+
// ══════════════════════════════════════════════════
|
|
1142
|
+
async function cmdBridge(args, ws) {
|
|
1143
|
+
const sub = (args[0] || '').toLowerCase();
|
|
1144
|
+
|
|
1145
|
+
if (sub === 'status' && args[1]) {
|
|
1146
|
+
// Check bridge transfer status
|
|
1147
|
+
ws.sendLine(` ${ANSI.dim}Checking bridge status...${ANSI.reset}`);
|
|
1148
|
+
try {
|
|
1149
|
+
const { checkBridgeStatus } = await import('../services/lifi.js');
|
|
1150
|
+
await checkBridgeStatus(args[1], {
|
|
1151
|
+
fromChain: args.find((a, i) => args[i - 1] === '--from'),
|
|
1152
|
+
toChain: args.find((a, i) => args[i - 1] === '--to'),
|
|
1153
|
+
});
|
|
1154
|
+
} catch (err) {
|
|
1155
|
+
ws.sendLine(` ${ANSI.red}✗ ${err.message}${ANSI.reset}`);
|
|
1156
|
+
}
|
|
1157
|
+
ws.sendLine('');
|
|
1158
|
+
return {};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (sub === 'chains') {
|
|
1162
|
+
ws.sendLine(` ${ANSI.dim}Fetching supported chains...${ANSI.reset}`);
|
|
1163
|
+
try {
|
|
1164
|
+
const { getChains } = await import('../services/lifi.js');
|
|
1165
|
+
const chains = await getChains();
|
|
1166
|
+
ws.sendLine('');
|
|
1167
|
+
ws.sendLine(`${ANSI.gold} ◆ LI.FI SUPPORTED CHAINS (${chains.length})${ANSI.reset}`);
|
|
1168
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
1169
|
+
|
|
1170
|
+
const evm = chains.filter(c => c.chainType === 'EVM').sort((a, b) => a.name.localeCompare(b.name));
|
|
1171
|
+
const other = chains.filter(c => c.chainType !== 'EVM');
|
|
1172
|
+
|
|
1173
|
+
for (const c of evm.slice(0, 30)) {
|
|
1174
|
+
ws.sendLine(` ${ANSI.green}●${ANSI.reset} ${ANSI.white}${c.name.padEnd(22)}${ANSI.reset} ${ANSI.dim}id:${c.id}${ANSI.reset}`);
|
|
1175
|
+
}
|
|
1176
|
+
if (evm.length > 30) {
|
|
1177
|
+
ws.sendLine(` ${ANSI.dim}...and ${evm.length - 30} more EVM chains${ANSI.reset}`);
|
|
1178
|
+
}
|
|
1179
|
+
if (other.length) {
|
|
1180
|
+
ws.sendLine('');
|
|
1181
|
+
for (const c of other) {
|
|
1182
|
+
ws.sendLine(` ${ANSI.blue}●${ANSI.reset} ${ANSI.white}${c.name.padEnd(22)}${ANSI.reset} ${ANSI.dim}${c.chainType}${ANSI.reset}`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
ws.sendLine('');
|
|
1186
|
+
} catch (err) {
|
|
1187
|
+
ws.sendLine(` ${ANSI.red}✗ ${err.message}${ANSI.reset}`);
|
|
1188
|
+
ws.sendLine('');
|
|
1189
|
+
}
|
|
1190
|
+
return {};
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Default: show bridge menu
|
|
1194
|
+
const currentChain = getConfig('chain') || 'base';
|
|
1195
|
+
|
|
1196
|
+
ws.sendLine(`${ANSI.gold} ◆ CROSS-CHAIN BRIDGE (LI.FI)${ANSI.reset}`);
|
|
1197
|
+
ws.sendLine(`${ANSI.dim} ${'─'.repeat(50)}${ANSI.reset}`);
|
|
1198
|
+
ws.sendLine(` ${ANSI.white}Move tokens between 60+ chains with optimal routing.${ANSI.reset}`);
|
|
1199
|
+
ws.sendLine(` ${ANSI.dim}Aggregates 27 bridges for best rates.${ANSI.reset}`);
|
|
1200
|
+
ws.sendLine('');
|
|
1201
|
+
|
|
1202
|
+
const chains = ['base', 'ethereum', 'arbitrum', 'optimism', 'polygon', 'avalanche', 'bsc', 'zksync', 'scroll', 'linea'];
|
|
1203
|
+
ws.sendMenu('bridge_from_chain', '◆ Bridge From Chain', chains.map(c => ({
|
|
1204
|
+
value: c,
|
|
1205
|
+
label: c === currentChain ? `★ ${c}` : c,
|
|
1206
|
+
desc: c === currentChain ? 'current chain' : '',
|
|
1207
|
+
})));
|
|
1208
|
+
|
|
1209
|
+
return {};
|
|
1210
|
+
}
|
|
1211
|
+
|
|
998
1212
|
// ══════════════════════════════════════════════════
|
|
999
1213
|
// WALLET
|
|
1000
1214
|
// ══════════════════════════════════════════════════
|