@darksol/terminal 0.4.0-beta.2 → 0.4.1-beta.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/package.json +3 -2
- package/src/cli.js +13 -1
- package/src/trading/swap.js +56 -4
- package/src/ui/banner.js +4 -2
- package/src/web/commands.js +532 -0
- package/src/web/index.html +49 -0
- package/src/web/server.js +247 -0
- package/src/web/terminal.css +177 -0
- package/src/web/terminal.js +262 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@darksol/terminal",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1-beta.1",
|
|
4
4
|
"description": "DARKSOL Terminal — unified CLI for all DARKSOL services. Market intel, trading, oracle, casino, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,8 @@
|
|
|
43
43
|
"open": "^11.0.0",
|
|
44
44
|
"ora": "^8.1.0",
|
|
45
45
|
"terminal-link": "^3.0.0",
|
|
46
|
-
"update-notifier": "^7.3.1"
|
|
46
|
+
"update-notifier": "^7.3.1",
|
|
47
|
+
"ws": "^8.19.0"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=18.0.0"
|
package/src/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { showHistory } from './wallet/history.js';
|
|
|
9
9
|
import { showGas } from './services/gas.js';
|
|
10
10
|
import { watchPrice, checkPrices } from './services/watch.js';
|
|
11
11
|
import { mailSetup, mailCreate, mailInboxes, mailSend, mailList, mailRead, mailReply, mailForward, mailThreads, mailDelete, mailUse, mailStats, mailStatus } from './services/mail.js';
|
|
12
|
+
import { startWebShell } from './web/server.js';
|
|
12
13
|
import { executeSwap } from './trading/swap.js';
|
|
13
14
|
import { snipeToken, watchSnipe } from './trading/snipe.js';
|
|
14
15
|
import { createDCA, listDCA, cancelDCA, runDCA } from './trading/dca.js';
|
|
@@ -32,7 +33,7 @@ export function cli(argv) {
|
|
|
32
33
|
program
|
|
33
34
|
.name('darksol')
|
|
34
35
|
.description(theme.gold('DARKSOL Terminal') + theme.dim(' — Ghost in the machine with teeth 🌑'))
|
|
35
|
-
.version('0.1
|
|
36
|
+
.version('0.4.1')
|
|
36
37
|
;
|
|
37
38
|
|
|
38
39
|
// ═══════════════════════════════════════
|
|
@@ -398,6 +399,16 @@ export function cli(argv) {
|
|
|
398
399
|
.description('Delete an inbox')
|
|
399
400
|
.action((id) => mailDelete(id));
|
|
400
401
|
|
|
402
|
+
// ═══════════════════════════════════════
|
|
403
|
+
// WEB SHELL
|
|
404
|
+
// ═══════════════════════════════════════
|
|
405
|
+
program
|
|
406
|
+
.command('serve')
|
|
407
|
+
.description('🌐 Launch web terminal in browser')
|
|
408
|
+
.option('-p, --port <port>', 'Server port', '18791')
|
|
409
|
+
.option('--no-open', 'Don\'t auto-open browser')
|
|
410
|
+
.action((opts) => startWebShell(opts));
|
|
411
|
+
|
|
401
412
|
// ═══════════════════════════════════════
|
|
402
413
|
// PORTFOLIO SHORTCUT
|
|
403
414
|
// ═══════════════════════════════════════
|
|
@@ -932,6 +943,7 @@ function showCommandList() {
|
|
|
932
943
|
['mail', 'AgentMail — email for your agent'],
|
|
933
944
|
['facilitator', 'x402 payment facilitator'],
|
|
934
945
|
['skills', 'Agent skill directory'],
|
|
946
|
+
['serve', 'Launch web terminal in browser'],
|
|
935
947
|
['setup', 'Re-run setup wizard'],
|
|
936
948
|
['config', 'Terminal configuration'],
|
|
937
949
|
];
|
package/src/trading/swap.js
CHANGED
|
@@ -56,6 +56,18 @@ const SWAP_ROUTER_ABI = [
|
|
|
56
56
|
'function multicall(uint256 deadline, bytes[] data) external payable returns (bytes[])',
|
|
57
57
|
];
|
|
58
58
|
|
|
59
|
+
// Uniswap V3 Quoter V2 ABI (for getting expected output)
|
|
60
|
+
const QUOTER_ABI = [
|
|
61
|
+
'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96)) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// Quoter V2 addresses per chain
|
|
65
|
+
const QUOTERS = {
|
|
66
|
+
base: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a',
|
|
67
|
+
ethereum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
68
|
+
arbitrum: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
|
|
69
|
+
};
|
|
70
|
+
|
|
59
71
|
// Resolve token symbol to address
|
|
60
72
|
export function resolveToken(symbol, chain) {
|
|
61
73
|
const upper = symbol.toUpperCase();
|
|
@@ -177,7 +189,7 @@ export async function executeSwap(opts = {}) {
|
|
|
177
189
|
return;
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
|
|
192
|
+
let swapSpin = spinner('Executing swap...').start();
|
|
181
193
|
|
|
182
194
|
// Approve if needed (non-native)
|
|
183
195
|
if (!isNativeIn) {
|
|
@@ -191,15 +203,54 @@ export async function executeSwap(opts = {}) {
|
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
// Execute swap
|
|
194
|
-
swapSpin.text = '
|
|
206
|
+
swapSpin.text = 'Getting quote for slippage protection...';
|
|
195
207
|
const swapRouter = new ethers.Contract(router, SWAP_ROUTER_ABI, signer);
|
|
208
|
+
const actualTokenOut = tokenOutAddr === ethers.ZeroAddress ? TOKENS[chain]?.WETH : tokenOutAddr;
|
|
196
209
|
|
|
197
210
|
const deadline = Math.floor(Date.now() / 1000) + 300; // 5 min
|
|
198
|
-
const amountOutMin = 0; // TODO: get quote for proper slippage protection
|
|
199
211
|
|
|
212
|
+
// Get quote from Quoter V2 for proper slippage protection
|
|
213
|
+
let amountOutMin = 0n;
|
|
214
|
+
const quoterAddr = QUOTERS[chain];
|
|
215
|
+
if (quoterAddr) {
|
|
216
|
+
try {
|
|
217
|
+
const quoter = new ethers.Contract(quoterAddr, QUOTER_ABI, provider);
|
|
218
|
+
const quoteResult = await quoter.quoteExactInputSingle.staticCall({
|
|
219
|
+
tokenIn: actualTokenIn,
|
|
220
|
+
tokenOut: actualTokenOut,
|
|
221
|
+
amountIn,
|
|
222
|
+
fee: 3000,
|
|
223
|
+
sqrtPriceLimitX96: 0,
|
|
224
|
+
});
|
|
225
|
+
// staticCall returns [amountOut, sqrtPriceX96After, initializedTicksCrossed, gasEstimate]
|
|
226
|
+
const expectedOut = quoteResult[0];
|
|
227
|
+
// Apply slippage tolerance: minOut = expectedOut * (100 - slippage) / 100
|
|
228
|
+
amountOutMin = (expectedOut * BigInt(Math.floor((100 - maxSlippage) * 100))) / 10000n;
|
|
229
|
+
swapSpin.text = `Quote: ~${ethers.formatUnits(expectedOut, tokenOutInfo.decimals)} ${tokenOutInfo.symbol} (min: ${ethers.formatUnits(amountOutMin, tokenOutInfo.decimals)})`;
|
|
230
|
+
} catch (quoteErr) {
|
|
231
|
+
// If quote fails, warn but allow user to proceed with zero protection
|
|
232
|
+
swapSpin.warn('Could not get quote — no slippage protection');
|
|
233
|
+
warn(`Quote failed: ${quoteErr.message}`);
|
|
234
|
+
const { proceedAnyway } = await inquirer.prompt([{
|
|
235
|
+
type: 'confirm',
|
|
236
|
+
name: 'proceedAnyway',
|
|
237
|
+
message: theme.accent('Proceed WITHOUT slippage protection? (risky)'),
|
|
238
|
+
default: false,
|
|
239
|
+
}]);
|
|
240
|
+
if (!proceedAnyway) {
|
|
241
|
+
warn('Swap cancelled — no slippage protection available');
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
swapSpin = spinner('Sending swap transaction...').start();
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
warn(`No Quoter available for ${chain} — swap will have no slippage protection`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
swapSpin.text = 'Sending swap transaction...';
|
|
200
251
|
const swapParams = {
|
|
201
252
|
tokenIn: actualTokenIn,
|
|
202
|
-
tokenOut:
|
|
253
|
+
tokenOut: actualTokenOut,
|
|
203
254
|
fee: 3000, // 0.3% fee tier
|
|
204
255
|
recipient: address,
|
|
205
256
|
deadline,
|
|
@@ -222,6 +273,7 @@ export async function executeSwap(opts = {}) {
|
|
|
222
273
|
['TX Hash', receipt.hash],
|
|
223
274
|
['Block', receipt.blockNumber.toString()],
|
|
224
275
|
['Gas Used', receipt.gasUsed.toString()],
|
|
276
|
+
['Min Output', amountOutMin > 0n ? `${ethers.formatUnits(amountOutMin, tokenOutInfo.decimals)} ${tokenOutInfo.symbol}` : theme.error('None (unprotected)')],
|
|
225
277
|
['Status', receipt.status === 1 ? theme.success('Success') : theme.error('Failed')],
|
|
226
278
|
]);
|
|
227
279
|
console.log('');
|
package/src/ui/banner.js
CHANGED
|
@@ -26,7 +26,7 @@ export function showBanner(opts = {}) {
|
|
|
26
26
|
);
|
|
27
27
|
console.log(
|
|
28
28
|
theme.dim(' ║ ') +
|
|
29
|
-
theme.subtle(' v0.4.
|
|
29
|
+
theme.subtle(' v0.4.1-beta.1') +
|
|
30
30
|
theme.dim(' ') +
|
|
31
31
|
theme.gold('🌑') +
|
|
32
32
|
theme.dim(' ║')
|
|
@@ -44,7 +44,7 @@ export function showBanner(opts = {}) {
|
|
|
44
44
|
|
|
45
45
|
export function showMiniBanner() {
|
|
46
46
|
console.log('');
|
|
47
|
-
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.4.
|
|
47
|
+
console.log(theme.gold.bold(' 🌑 DARKSOL TERMINAL') + theme.dim(' v0.4.1-beta.1'));
|
|
48
48
|
console.log(theme.dim(' ─────────────────────────────'));
|
|
49
49
|
console.log('');
|
|
50
50
|
}
|
|
@@ -71,3 +71,5 @@ export function showDivider() {
|
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
|
|
75
|
+
|