clawncher 0.1.8 → 0.1.11
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 +5 -0
- package/dist/cli.js +365 -5
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -34,6 +34,11 @@ Deploy a new token on Base with Uniswap V4 pool:
|
|
|
34
34
|
# Basic deployment
|
|
35
35
|
clawncher deploy --name "My Token" --symbol MYTKN
|
|
36
36
|
|
|
37
|
+
# Pay the mandatory 1,000,000 $CLAWNCH launch burn (required to deploy while
|
|
38
|
+
# burn enforcement is on). Your wallet must hold >= 1,000,000 $CLAWNCH; the CLI
|
|
39
|
+
# burns it to the dead address, then deploys — in one command.
|
|
40
|
+
clawncher deploy --name "My Token" --symbol MYTKN --burn
|
|
41
|
+
|
|
37
42
|
# With image and description
|
|
38
43
|
clawncher deploy --name "My Token" --symbol MYTKN \
|
|
39
44
|
--image "https://example.com/logo.png" \
|
package/dist/cli.js
CHANGED
|
@@ -9,13 +9,29 @@ import chalk from 'chalk';
|
|
|
9
9
|
import ora from 'ora';
|
|
10
10
|
import Table from 'cli-table3';
|
|
11
11
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
12
|
-
import { join } from 'path';
|
|
12
|
+
import { join, dirname } from 'path';
|
|
13
13
|
import { homedir } from 'os';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
14
15
|
import { createWalletClient, createPublicClient, http, formatEther, parseEther, } from 'viem';
|
|
15
16
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
16
17
|
import { base, baseSepolia } from 'viem/chains';
|
|
17
|
-
import { ClawnchReader, ClawnchClient, ClawncherClaimer, ClawnchPortfolio, ClawnchWatcher, ClawnchSwapper, ClawnchLiquidity, ClawnchApiDeployer, WayfinderClient, getAddresses, NATIVE_TOKEN_ADDRESS, WAYFINDER_CHAIN_NAMES, ClawnchFeeLockerABI, HerdIntelligence, HerdAuth, } from '@clawnch/clawncher-sdk';
|
|
18
|
-
|
|
18
|
+
import { ClawnchReader, ClawnchClient, ClawncherClaimer, ClawnchPortfolio, ClawnchWatcher, ClawnchSwapper, ClawnchLiquidity, ClawnchApiDeployer, WayfinderClient, BankrFeeClaimer, getAddresses, NATIVE_TOKEN_ADDRESS, WAYFINDER_CHAIN_NAMES, ClawnchFeeLockerABI, HerdIntelligence, HerdAuth, } from '@clawnch/clawncher-sdk';
|
|
19
|
+
// Read VERSION from package.json at runtime instead of hardcoding it.
|
|
20
|
+
// The previous hardcoded constant drifted from the published package
|
|
21
|
+
// version on every release and showed users an inaccurate banner.
|
|
22
|
+
// dist/cli.js → ../package.json (package root).
|
|
23
|
+
const VERSION = (() => {
|
|
24
|
+
try {
|
|
25
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
26
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
27
|
+
return pkg.version || '0.0.0';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// package.json missing / unreadable — return a sentinel that's
|
|
31
|
+
// clearly wrong so the bug is visible instead of silently stale.
|
|
32
|
+
return '0.0.0';
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
19
35
|
// ============================================================================
|
|
20
36
|
// UI Toolkit
|
|
21
37
|
// ============================================================================
|
|
@@ -329,7 +345,8 @@ program
|
|
|
329
345
|
.option('--dev-buy-recipient <address>', 'Dev buy recipient')
|
|
330
346
|
.option('--pair <token>', 'Paired token: "weth" (default) or "usdc"', 'weth')
|
|
331
347
|
.option('--no-vanity', 'Disable vanity address')
|
|
332
|
-
.option('--bypass-rate-limit', 'Burn 10,000 $CLAWNCH to skip
|
|
348
|
+
.option('--bypass-rate-limit', 'Burn 10,000 $CLAWNCH to skip 24h per-agent cooldown')
|
|
349
|
+
.option('--burn', 'Pay the mandatory 1,000,000 $CLAWNCH launch burn (required when burn enforcement is on)')
|
|
333
350
|
.option('--network <network>', 'Network (mainnet/sepolia)', 'mainnet')
|
|
334
351
|
.option('--private-key <key>', 'Private key')
|
|
335
352
|
.option('--rpc <url>', 'RPC URL')
|
|
@@ -354,6 +371,7 @@ program
|
|
|
354
371
|
name: opts.name,
|
|
355
372
|
symbol: opts.symbol,
|
|
356
373
|
bypassRateLimit: opts.bypassRateLimit || false,
|
|
374
|
+
burn: opts.burn || false,
|
|
357
375
|
};
|
|
358
376
|
if (opts.image)
|
|
359
377
|
request.image = opts.image;
|
|
@@ -391,7 +409,9 @@ program
|
|
|
391
409
|
recipient: opts.devBuyRecipient ? validateAddress(opts.devBuyRecipient, 'dev buy recipient') : account.address,
|
|
392
410
|
};
|
|
393
411
|
}
|
|
394
|
-
spinner.text =
|
|
412
|
+
spinner.text = opts.burn
|
|
413
|
+
? `Burning 1,000,000 $CLAWNCH + deploying ${opts.symbol} on ${network}...`
|
|
414
|
+
: `Deploying ${opts.symbol} on ${network}...`;
|
|
395
415
|
const result = await deployer.deploy(request);
|
|
396
416
|
spinner.succeed('Token deployed');
|
|
397
417
|
if (opts.json) {
|
|
@@ -421,6 +441,9 @@ program
|
|
|
421
441
|
}
|
|
422
442
|
catch (err) {
|
|
423
443
|
spinner.stop();
|
|
444
|
+
if (!opts.burn && err instanceof Error && /burn/i.test(err.message)) {
|
|
445
|
+
console.error(`\n ${c.muted('This launch path requires a 1,000,000 $CLAWNCH burn. Re-run with')} ${c.highlight('--burn')} ${c.muted('to burn + deploy in one step.')}\n`);
|
|
446
|
+
}
|
|
424
447
|
handleError(err);
|
|
425
448
|
}
|
|
426
449
|
});
|
|
@@ -873,6 +896,191 @@ fees
|
|
|
873
896
|
}
|
|
874
897
|
});
|
|
875
898
|
// ============================================================================
|
|
899
|
+
// Bankr Fee Commands (Doppler protocol)
|
|
900
|
+
// ============================================================================
|
|
901
|
+
fees
|
|
902
|
+
.command('bankr-check')
|
|
903
|
+
.description('Check claimable Bankr/Doppler fees for a wallet')
|
|
904
|
+
.argument('<wallet>', 'Wallet address')
|
|
905
|
+
.option('-d, --days <days>', 'Lookback period in days (1-90)', '30')
|
|
906
|
+
.option('--json', 'Output as JSON')
|
|
907
|
+
.action(async (wallet, opts) => {
|
|
908
|
+
const walletAddr = validateAddress(wallet, 'wallet');
|
|
909
|
+
const spinner = ora('Checking Bankr fees...').start();
|
|
910
|
+
try {
|
|
911
|
+
const days = parseInt(opts.days, 10);
|
|
912
|
+
const dashboard = await BankrFeeClaimer.getCreatorFees(walletAddr, days);
|
|
913
|
+
spinner.stop();
|
|
914
|
+
if (opts.json) {
|
|
915
|
+
console.log(JSON.stringify(dashboard, null, 2));
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
console.log();
|
|
919
|
+
console.log(sectionHeader('Bankr Fee Dashboard'));
|
|
920
|
+
console.log(kv('Wallet', fmtAddr(wallet)));
|
|
921
|
+
console.log(kv('Period', c.value(`${dashboard.days} days`)));
|
|
922
|
+
console.log();
|
|
923
|
+
console.log(kv('Claimable WETH', c.accent(dashboard.totals.claimableWeth + ' WETH')));
|
|
924
|
+
console.log(kv('Claimed WETH', c.muted(dashboard.totals.claimedWeth + ' WETH')));
|
|
925
|
+
console.log(kv('Claim Count', c.value(dashboard.totals.claimCount.toString())));
|
|
926
|
+
console.log();
|
|
927
|
+
if (!dashboard.tokens || dashboard.tokens.length === 0) {
|
|
928
|
+
console.log(c.muted(' No Bankr tokens with fees found'));
|
|
929
|
+
console.log();
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
console.log(styledTable(['Symbol', 'Token', 'Claimable WETH', 'Claimed WETH'], dashboard.tokens.map((t) => {
|
|
933
|
+
const wethIsToken0 = t.token0Label === 'WETH';
|
|
934
|
+
const claimableWeth = wethIsToken0 ? t.claimable.token0 : t.claimable.token1;
|
|
935
|
+
const claimedWeth = wethIsToken0 ? t.claimed.token0 : t.claimed.token1;
|
|
936
|
+
return [
|
|
937
|
+
c.highlight(t.symbol),
|
|
938
|
+
fmtAddr(t.tokenAddress, true),
|
|
939
|
+
c.accent(claimableWeth),
|
|
940
|
+
c.muted(claimedWeth),
|
|
941
|
+
];
|
|
942
|
+
})));
|
|
943
|
+
console.log();
|
|
944
|
+
}
|
|
945
|
+
catch (err) {
|
|
946
|
+
spinner.stop();
|
|
947
|
+
handleError(err);
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
fees
|
|
951
|
+
.command('bankr-claim')
|
|
952
|
+
.description('Claim Bankr/Doppler fees for a specific token')
|
|
953
|
+
.argument('<token>', 'Token contract address')
|
|
954
|
+
.option('--private-key <key>', 'Private key (or use CLAWNCHER_PRIVATE_KEY env)')
|
|
955
|
+
.option('--rpc <url>', 'Custom RPC URL')
|
|
956
|
+
.option('--json', 'Output as JSON')
|
|
957
|
+
.action(async (token, opts) => {
|
|
958
|
+
const spinner = ora('Preparing Bankr fee claim...').start();
|
|
959
|
+
try {
|
|
960
|
+
const tokenAddress = validateAddress(token, 'token');
|
|
961
|
+
const privateKey = await getPrivateKeyOrWallet(opts);
|
|
962
|
+
const rpcUrl = opts.rpc || 'https://mainnet.base.org';
|
|
963
|
+
const account = privateKeyToAccount(privateKey);
|
|
964
|
+
const wallet = createWalletClient({
|
|
965
|
+
account,
|
|
966
|
+
chain: base,
|
|
967
|
+
transport: http(rpcUrl),
|
|
968
|
+
});
|
|
969
|
+
const publicClient = createPublicClient({
|
|
970
|
+
chain: base,
|
|
971
|
+
transport: http(rpcUrl),
|
|
972
|
+
});
|
|
973
|
+
// First, look up this token's fee data
|
|
974
|
+
spinner.text = 'Looking up token fees...';
|
|
975
|
+
const dashboard = await BankrFeeClaimer.getCreatorFees(account.address);
|
|
976
|
+
const tokenData = dashboard.tokens?.find((t) => t.tokenAddress.toLowerCase() === tokenAddress.toLowerCase());
|
|
977
|
+
if (!tokenData) {
|
|
978
|
+
spinner.stop();
|
|
979
|
+
console.log();
|
|
980
|
+
console.log(c.error(' No Bankr fees found for this token'));
|
|
981
|
+
console.log(c.muted(' Make sure this is a Bankr-deployed token and you are the creator'));
|
|
982
|
+
console.log();
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (!tokenData.initializer || !tokenData.poolId) {
|
|
986
|
+
spinner.stop();
|
|
987
|
+
console.log();
|
|
988
|
+
console.log(c.error(' Missing initializer or poolId — cannot claim'));
|
|
989
|
+
console.log();
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
const wethIsToken0 = tokenData.token0Label === 'WETH';
|
|
993
|
+
const claimableWeth = wethIsToken0 ? tokenData.claimable.token0 : tokenData.claimable.token1;
|
|
994
|
+
spinner.text = `Claiming ${tokenData.symbol} fees (${claimableWeth} WETH)...`;
|
|
995
|
+
const claimer = new BankrFeeClaimer({ wallet, publicClient });
|
|
996
|
+
const result = await claimer.claimToken(tokenData);
|
|
997
|
+
spinner.stop();
|
|
998
|
+
if (opts.json) {
|
|
999
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
console.log();
|
|
1003
|
+
console.log(sectionHeader('Bankr Fee Claim'));
|
|
1004
|
+
console.log(kv('Token', `${c.highlight(tokenData.symbol)} ${fmtAddr(tokenAddress, true)}`));
|
|
1005
|
+
console.log(kv('Caller', fmtAddr(account.address)));
|
|
1006
|
+
console.log();
|
|
1007
|
+
if (result.success) {
|
|
1008
|
+
console.log(` ${c.success('\u2713')} ${c.label('Claimed')} ${c.accent(claimableWeth + ' WETH')}`);
|
|
1009
|
+
console.log(` ${c.muted('tx:')} ${c.dim(result.txHash)}`);
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.log(` ${c.error('\u2717')} ${c.label('Failed')} ${c.error(result.error || 'Unknown error')}`);
|
|
1013
|
+
}
|
|
1014
|
+
console.log();
|
|
1015
|
+
}
|
|
1016
|
+
catch (err) {
|
|
1017
|
+
spinner.stop();
|
|
1018
|
+
handleError(err);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
fees
|
|
1022
|
+
.command('bankr-claim-all')
|
|
1023
|
+
.description('Claim Bankr/Doppler fees for all tokens with claimable fees')
|
|
1024
|
+
.option('--private-key <key>', 'Private key (or use CLAWNCHER_PRIVATE_KEY env)')
|
|
1025
|
+
.option('--rpc <url>', 'Custom RPC URL')
|
|
1026
|
+
.option('--json', 'Output as JSON')
|
|
1027
|
+
.action(async (opts) => {
|
|
1028
|
+
const spinner = ora('Scanning for claimable Bankr fees...').start();
|
|
1029
|
+
try {
|
|
1030
|
+
const privateKey = await getPrivateKeyOrWallet(opts);
|
|
1031
|
+
const rpcUrl = opts.rpc || 'https://mainnet.base.org';
|
|
1032
|
+
const account = privateKeyToAccount(privateKey);
|
|
1033
|
+
const wallet = createWalletClient({
|
|
1034
|
+
account,
|
|
1035
|
+
chain: base,
|
|
1036
|
+
transport: http(rpcUrl),
|
|
1037
|
+
});
|
|
1038
|
+
const publicClient = createPublicClient({
|
|
1039
|
+
chain: base,
|
|
1040
|
+
transport: http(rpcUrl),
|
|
1041
|
+
});
|
|
1042
|
+
const claimer = new BankrFeeClaimer({ wallet, publicClient });
|
|
1043
|
+
const result = await claimer.claimAll({
|
|
1044
|
+
onProgress: (progress) => {
|
|
1045
|
+
spinner.text = `Scanning launches... ${progress.scanned}/${progress.total}`;
|
|
1046
|
+
},
|
|
1047
|
+
onClaim: (claimResult, index, total) => {
|
|
1048
|
+
const icon = claimResult.success ? c.success('\u2713') : c.error('\u2717');
|
|
1049
|
+
spinner.text = `${icon} ${claimResult.symbol} (${index + 1}/${total})`;
|
|
1050
|
+
},
|
|
1051
|
+
});
|
|
1052
|
+
spinner.stop();
|
|
1053
|
+
if (opts.json) {
|
|
1054
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
console.log();
|
|
1058
|
+
console.log(sectionHeader('Bankr Batch Claim Results'));
|
|
1059
|
+
console.log(kv('Wallet', fmtAddr(account.address)));
|
|
1060
|
+
console.log(kv('WETH Estimate', c.accent(result.totalWethEstimate + ' WETH')));
|
|
1061
|
+
console.log();
|
|
1062
|
+
if (result.results.length === 0) {
|
|
1063
|
+
console.log(c.muted(' No claimable Bankr fees found'));
|
|
1064
|
+
console.log();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
for (const r of result.results) {
|
|
1068
|
+
const icon = r.success ? c.success('\u2713') : c.error('\u2717');
|
|
1069
|
+
const status = r.success
|
|
1070
|
+
? c.dim(r.txHash.slice(0, 14) + '...')
|
|
1071
|
+
: c.error(r.error || 'failed');
|
|
1072
|
+
console.log(` ${icon} ${c.highlight(r.symbol.padEnd(10))} ${fmtAddr(r.tokenAddress, true)} ${c.dim('\u2500')} ${status}`);
|
|
1073
|
+
}
|
|
1074
|
+
console.log();
|
|
1075
|
+
console.log(c.dim(` ${result.successCount}/${result.results.length} tokens claimed successfully`));
|
|
1076
|
+
console.log();
|
|
1077
|
+
}
|
|
1078
|
+
catch (err) {
|
|
1079
|
+
spinner.stop();
|
|
1080
|
+
handleError(err);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
// ============================================================================
|
|
876
1084
|
// Portfolio Command
|
|
877
1085
|
// ============================================================================
|
|
878
1086
|
program
|
|
@@ -1120,6 +1328,158 @@ program
|
|
|
1120
1328
|
}
|
|
1121
1329
|
});
|
|
1122
1330
|
// ============================================================================
|
|
1331
|
+
// Connect Command (Telegram Bot Pairing)
|
|
1332
|
+
// ============================================================================
|
|
1333
|
+
program
|
|
1334
|
+
.command('connect')
|
|
1335
|
+
.description('Connect to a wallet set up via the Telegram bot (@OpenClawnchBot)')
|
|
1336
|
+
.argument('<code>', 'Pairing code from the Telegram bot (e.g. CLAW-A7X9)')
|
|
1337
|
+
.option('--api-url <url>', 'Clawnch API URL')
|
|
1338
|
+
.option('--json', 'Output as JSON')
|
|
1339
|
+
.action(async (code, opts) => {
|
|
1340
|
+
const spinner = ora('Connecting...').start();
|
|
1341
|
+
try {
|
|
1342
|
+
const baseUrl = (opts.apiUrl || getClawnchApiUrl()).replace(/\/$/, '');
|
|
1343
|
+
// Strip ALL whitespace (not just leading/trailing) — paste from the
|
|
1344
|
+
// Telegram bot occasionally includes a non-breaking space or a stray
|
|
1345
|
+
// newline between the prefix and the random suffix. The previous
|
|
1346
|
+
// `.trim()` only handled outer whitespace, which made codes that
|
|
1347
|
+
// looked right to the user fail the server-side `^CLAW-[A-Z0-9]{4}$`
|
|
1348
|
+
// regex with no useful feedback.
|
|
1349
|
+
const normalizedCode = code.replace(/\s+/g, '').toUpperCase();
|
|
1350
|
+
// Exchange pairing code for encrypted credentials
|
|
1351
|
+
spinner.text = 'Exchanging pairing code...';
|
|
1352
|
+
const res = await fetch(`${baseUrl}/api/pair/exchange`, {
|
|
1353
|
+
method: 'POST',
|
|
1354
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1355
|
+
body: JSON.stringify({ code: normalizedCode }),
|
|
1356
|
+
});
|
|
1357
|
+
if (!res.ok) {
|
|
1358
|
+
const err = await res.json().catch(() => ({ error: 'Exchange failed' }));
|
|
1359
|
+
spinner.fail(`Failed: ${err.error || 'Unknown error'}`);
|
|
1360
|
+
// Echo the exact string we sent. Codes are single-use + 5-minute TTL
|
|
1361
|
+
// so this isn't a secret leak by the time the user sees the error,
|
|
1362
|
+
// and it's the only way to debug "I pasted the right thing"
|
|
1363
|
+
// mismatches caused by invisible characters surviving the trim.
|
|
1364
|
+
// JSON.stringify reveals invisible chars as escape sequences.
|
|
1365
|
+
console.log(c.muted(` Sent code: ${JSON.stringify(normalizedCode)}`));
|
|
1366
|
+
console.log(c.muted(` Expected format: CLAW-XXXX (4 alphanumeric chars, uppercase)`));
|
|
1367
|
+
if (err.suggestion)
|
|
1368
|
+
console.log(c.muted(` ${err.suggestion}`));
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
}
|
|
1371
|
+
const data = await res.json();
|
|
1372
|
+
spinner.text = 'Decrypting wallet...';
|
|
1373
|
+
// Ask for PIN to decrypt the private key
|
|
1374
|
+
const { createInterface } = await import('readline');
|
|
1375
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1376
|
+
const pin = await new Promise((resolve) => {
|
|
1377
|
+
// Use stderr for the prompt so it doesn't interfere with JSON output
|
|
1378
|
+
process.stderr.write(c.label('\n Enter the PIN you set in the Telegram bot: '));
|
|
1379
|
+
rl.question('', (answer) => {
|
|
1380
|
+
rl.close();
|
|
1381
|
+
resolve(answer.trim());
|
|
1382
|
+
});
|
|
1383
|
+
});
|
|
1384
|
+
if (!pin) {
|
|
1385
|
+
spinner.fail('PIN is required to decrypt the wallet.');
|
|
1386
|
+
process.exit(1);
|
|
1387
|
+
}
|
|
1388
|
+
// Decrypt private key
|
|
1389
|
+
const { scryptSync, createDecipheriv } = await import('crypto');
|
|
1390
|
+
const salt = Buffer.from(data.salt, 'hex');
|
|
1391
|
+
const iv = Buffer.from(data.iv, 'hex');
|
|
1392
|
+
const authTag = Buffer.from(data.authTag, 'hex');
|
|
1393
|
+
const ciphertext = Buffer.from(data.encryptedKey, 'hex');
|
|
1394
|
+
let privateKey;
|
|
1395
|
+
try {
|
|
1396
|
+
// Match the bot's scrypt params (N=2^14, r=8, p=1)
|
|
1397
|
+
const key = scryptSync(pin, salt, 32, { N: 2 ** 14, r: 8, p: 1 });
|
|
1398
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
1399
|
+
decipher.setAuthTag(authTag);
|
|
1400
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1401
|
+
privateKey = decrypted.toString('utf8');
|
|
1402
|
+
}
|
|
1403
|
+
catch {
|
|
1404
|
+
spinner.fail('Wrong PIN. Could not decrypt the wallet.');
|
|
1405
|
+
process.exit(1);
|
|
1406
|
+
}
|
|
1407
|
+
// Verify it's a valid private key
|
|
1408
|
+
if (!privateKey.startsWith('0x') || privateKey.length !== 66) {
|
|
1409
|
+
spinner.fail('Decrypted data is not a valid private key. Try again with /start in the bot.');
|
|
1410
|
+
process.exit(1);
|
|
1411
|
+
}
|
|
1412
|
+
// Import the wallet using the existing encrypted wallet system
|
|
1413
|
+
// Re-encrypt with the CLI's stronger scrypt params for local storage
|
|
1414
|
+
const { importFromPrivateKey, setActiveWallet, listWallets } = await import('./wallet.js');
|
|
1415
|
+
const walletName = data.agentName
|
|
1416
|
+
? data.agentName.toLowerCase().replace(/[^a-z0-9-_]/g, '-')
|
|
1417
|
+
: `tg-${data.wallet.slice(2, 8).toLowerCase()}`;
|
|
1418
|
+
// Check if wallet name already exists
|
|
1419
|
+
const existing = listWallets();
|
|
1420
|
+
let finalName = walletName;
|
|
1421
|
+
if (existing.some((w) => w.name === walletName)) {
|
|
1422
|
+
finalName = `${walletName}-${Date.now().toString(36).slice(-4)}`;
|
|
1423
|
+
}
|
|
1424
|
+
// Prompt for a local password (stronger than PIN, for CLI encryption)
|
|
1425
|
+
const rl2 = createInterface({ input: process.stdin, output: process.stdout });
|
|
1426
|
+
const password = await new Promise((resolve) => {
|
|
1427
|
+
process.stderr.write(c.label(' Choose a password for local wallet encryption (8+ chars): '));
|
|
1428
|
+
rl2.question('', (answer) => {
|
|
1429
|
+
rl2.close();
|
|
1430
|
+
resolve(answer.trim());
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
if (password.length < 8) {
|
|
1434
|
+
spinner.fail('Password must be at least 8 characters.');
|
|
1435
|
+
process.exit(1);
|
|
1436
|
+
}
|
|
1437
|
+
spinner.text = 'Importing wallet...';
|
|
1438
|
+
// Import with the CLI's wallet system (uses scrypt N=2^18)
|
|
1439
|
+
importFromPrivateKey(finalName, privateKey, password);
|
|
1440
|
+
setActiveWallet(finalName);
|
|
1441
|
+
// Save agent config if present
|
|
1442
|
+
const config = loadConfig();
|
|
1443
|
+
config.network = data.network || 'mainnet';
|
|
1444
|
+
if (!config.privateKey) {
|
|
1445
|
+
// Don't overwrite existing raw key, but set network
|
|
1446
|
+
}
|
|
1447
|
+
saveConfig(config);
|
|
1448
|
+
spinner.succeed('Connected!');
|
|
1449
|
+
if (opts.json) {
|
|
1450
|
+
console.log(JSON.stringify({
|
|
1451
|
+
wallet: data.wallet,
|
|
1452
|
+
walletName: finalName,
|
|
1453
|
+
agentName: data.agentName,
|
|
1454
|
+
hasApiKey: !!data.agentApiKey,
|
|
1455
|
+
network: data.network,
|
|
1456
|
+
}, null, 2));
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
console.log();
|
|
1460
|
+
console.log(kv('Wallet', c.value(data.wallet)));
|
|
1461
|
+
console.log(kv('Wallet name', c.accent(finalName)));
|
|
1462
|
+
console.log(kv('Network', networkBadge(data.network || 'mainnet')));
|
|
1463
|
+
if (data.agentName) {
|
|
1464
|
+
console.log(kv('Agent', c.value(data.agentName)));
|
|
1465
|
+
}
|
|
1466
|
+
if (data.agentApiKey) {
|
|
1467
|
+
console.log(kv('API Key', c.accent(data.agentApiKey)));
|
|
1468
|
+
console.log();
|
|
1469
|
+
console.log(c.warn(' Save your API key — you\'ll need it for verified deploys.'));
|
|
1470
|
+
}
|
|
1471
|
+
console.log();
|
|
1472
|
+
console.log(c.muted(' Your wallet is encrypted and stored locally.'));
|
|
1473
|
+
console.log(c.muted(' Next: clawncher deploy --verified --api-key <key>'));
|
|
1474
|
+
console.log();
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
catch (err) {
|
|
1478
|
+
spinner.stop();
|
|
1479
|
+
handleError(err);
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
// ============================================================================
|
|
1123
1483
|
// Tokens Command (API)
|
|
1124
1484
|
// ============================================================================
|
|
1125
1485
|
program
|