codecrypto-cli 1.0.13 → 1.0.15

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.
@@ -0,0 +1,139 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import * as os from 'os';
7
+ import { ethers } from 'ethers';
8
+
9
+ interface Account {
10
+ address: string;
11
+ private_key: string;
12
+ }
13
+
14
+ interface KeysFile {
15
+ mnemonic: string;
16
+ accounts: Account[];
17
+ }
18
+
19
+ export const generateKeysCommand = new Command('generate-keys')
20
+ .description('Generate Foundry keys file with mnemonic and derived accounts')
21
+ .option('-m, --mnemonic <mnemonic>', 'Mnemonic phrase (if not provided, a random one will be generated)')
22
+ .option('-n, --count <number>', 'Number of accounts to generate', '10')
23
+ .option('-o, --output <path>', 'Output file path', '~/.foundry/keys.json')
24
+ .action(async (options) => {
25
+ console.log(chalk.blue('\nšŸ”‘ CodeCrypto Foundry Keys Generator\n'));
26
+
27
+ try {
28
+ // Parse count
29
+ const accountCount = parseInt(options.count, 10);
30
+ if (isNaN(accountCount) || accountCount < 1 || accountCount > 100) {
31
+ console.error(chalk.red('āŒ Error: Account count must be between 1 and 100'));
32
+ process.exit(1);
33
+ }
34
+
35
+ // Generate or use provided mnemonic
36
+ let mnemonic: string;
37
+ if (options.mnemonic) {
38
+ mnemonic = options.mnemonic.trim();
39
+ // Validate mnemonic
40
+ if (!ethers.Mnemonic.isValidMnemonic(mnemonic)) {
41
+ console.error(chalk.red('āŒ Error: Invalid mnemonic phrase'));
42
+ process.exit(1);
43
+ }
44
+ } else {
45
+ // Generate random mnemonic
46
+ const spinner = ora('Generating random mnemonic...').start();
47
+ mnemonic = ethers.Mnemonic.entropyToPhrase(ethers.randomBytes(16));
48
+ spinner.succeed('Random mnemonic generated');
49
+ }
50
+
51
+ // Derive accounts
52
+ const accountsSpinner = ora(`Deriving ${accountCount} account(s) from mnemonic...`).start();
53
+ const accounts: Account[] = [];
54
+
55
+ try {
56
+ for (let i = 0; i < accountCount; i++) {
57
+ // Use HDNodeWallet.fromPhrase with the full derivation path
58
+ // This creates a wallet directly from mnemonic at the specified path
59
+ const wallet = ethers.HDNodeWallet.fromPhrase(mnemonic, `m/44'/60'/0'/0/${i}`);
60
+ accounts.push({
61
+ address: wallet.address,
62
+ private_key: wallet.privateKey,
63
+ });
64
+ }
65
+
66
+ accountsSpinner.succeed(`Generated ${accountCount} account(s)`);
67
+ } catch (error: any) {
68
+ accountsSpinner.fail('Failed to derive accounts');
69
+ console.error(chalk.red(`āŒ Error: ${error.message}`));
70
+ process.exit(1);
71
+ }
72
+
73
+ // Prepare output data
74
+ const keysData: KeysFile = {
75
+ mnemonic,
76
+ accounts,
77
+ };
78
+
79
+ // Resolve output path
80
+ let outputPath = options.output;
81
+ if (outputPath.startsWith('~')) {
82
+ outputPath = path.join(os.homedir(), outputPath.slice(1));
83
+ }
84
+ outputPath = path.resolve(outputPath);
85
+
86
+ // Create directory if it doesn't exist
87
+ const outputDir = path.dirname(outputPath);
88
+ if (!fs.existsSync(outputDir)) {
89
+ const dirSpinner = ora(`Creating directory: ${outputDir}`).start();
90
+ try {
91
+ fs.mkdirSync(outputDir, { recursive: true });
92
+ dirSpinner.succeed(`Directory created: ${outputDir}`);
93
+ } catch (error: any) {
94
+ dirSpinner.fail('Failed to create directory');
95
+ console.error(chalk.red(`āŒ Error: ${error.message}`));
96
+ process.exit(1);
97
+ }
98
+ }
99
+
100
+ // Write file
101
+ const writeSpinner = ora(`Writing keys to ${outputPath}...`).start();
102
+ try {
103
+ fs.writeFileSync(outputPath, JSON.stringify(keysData, null, 2), 'utf-8');
104
+ writeSpinner.succeed(`Keys saved to ${outputPath}`);
105
+ } catch (error: any) {
106
+ writeSpinner.fail('Failed to write file');
107
+ console.error(chalk.red(`āŒ Error: ${error.message}`));
108
+ process.exit(1);
109
+ }
110
+
111
+ // Display summary
112
+ console.log(chalk.green('\nāœ… Keys generated successfully!\n'));
113
+ console.log(chalk.gray('Summary:'));
114
+ console.log(chalk.white(` Mnemonic: ${chalk.cyan(mnemonic)}`));
115
+ console.log(chalk.white(` Accounts: ${chalk.cyan(accountCount)}`));
116
+ console.log(chalk.white(` Output: ${chalk.cyan(outputPath)}\n`));
117
+
118
+ // Display first few accounts
119
+ console.log(chalk.gray('First 3 accounts:'));
120
+ accounts.slice(0, 3).forEach((account, index) => {
121
+ console.log(chalk.white(` ${index + 1}. ${chalk.cyan(account.address)}`));
122
+ console.log(chalk.gray(` Private Key: ${chalk.dim(account.private_key)}`));
123
+ });
124
+
125
+ if (accountCount > 3) {
126
+ console.log(chalk.gray(` ... and ${accountCount - 3} more account(s)\n`));
127
+ } else {
128
+ console.log('');
129
+ }
130
+
131
+ console.log(chalk.yellow('āš ļø Warning: Keep your mnemonic and private keys secure!'));
132
+ console.log(chalk.yellow(' Never share them or commit them to version control.\n'));
133
+
134
+ } catch (error: any) {
135
+ console.error(chalk.red(`\nāŒ Error: ${error.message}`));
136
+ process.exit(1);
137
+ }
138
+ });
139
+
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import { deployCommand } from './commands/deploy';
8
8
  import { deployScCommand } from './commands/deploy-sc';
9
9
  import { authCommand } from './commands/auth';
10
10
  import { doctorCommand } from './commands/doctor';
11
+ import { generateKeysCommand } from './commands/generate-keys';
11
12
 
12
13
  // Cargar variables de entorno desde ~/.codecrypto/.env si existe
13
14
  function loadEnvFile() {
@@ -55,5 +56,6 @@ program.addCommand(deployCommand);
55
56
  program.addCommand(deployScCommand);
56
57
  program.addCommand(authCommand);
57
58
  program.addCommand(doctorCommand);
59
+ program.addCommand(generateKeysCommand);
58
60
 
59
61
  program.parse(process.argv);