Skip to main content
Glama
generate-seed.ts9.97 kB
#!/usr/bin/env node import * as bip39 from 'bip39'; import { Command } from 'commander'; import chalk from 'chalk'; import { createHash, randomBytes } from 'crypto'; const program = new Command(); program .name('generate-seed') .description('Generate a BIP39 mnemonic and derive seed for Midnight wallet') .version('1.0.0') .option('-w, --words <number>', 'number of words in mnemonic (12 or 24)', '24') .option('-p, --password <string>', 'optional password for additional security', '') .option('-f, --format <string>', 'seed format: "full" (64 bytes) or "compact" (32 bytes)', 'full') .option('-e, --entropy <hex>', 'use provided hex entropy (16, 20, 24, 28, or 32 bytes) to generate mnemonic') .option('-s, --seed <hex>', 'treat the provided hex as seed, verify by converting to mnemonic and back') .option('-m, --mnemonic <string>', 'use provided mnemonic to generate seed') .option('-M, --midnight-seed <hex>', 'Midnight wallet seed: generate a compatible BIP39 mnemonic for a Midnight wallet seed') .parse(process.argv); const options = program.opts(); // Handle Midnight wallet specific seed (seed = entropy in Midnight's case) if (options.midnightSeed) { try { const midnightSeed = options.midnightSeed; // Validate seed length if (midnightSeed.length !== 64) { console.error(chalk.red('Error: Midnight seed must be exactly 32 bytes (64 hex characters)')); process.exit(1); } // Convert Midnight seed to BIP39 mnemonic by using it as entropy const seedBuffer = Buffer.from(midnightSeed, 'hex'); const mnemonic = bip39.entropyToMnemonic(seedBuffer); console.log(chalk.green('\n=== Midnight Wallet Seed Conversion ===\n')); console.log(chalk.yellow('Midnight Wallet Seed (hex):')); console.log(chalk.white(midnightSeed)); console.log('\n' + chalk.yellow('Compatible BIP39 Mnemonic:')); console.log(chalk.white(mnemonic)); console.log('\n' + chalk.cyan('Important Notes:')); console.log(chalk.cyan('1. In Midnight, your seed is used as entropy to generate the BIP39 mnemonic')); console.log(chalk.cyan('2. This mnemonic can be used in any BIP39-compatible wallet')); console.log(chalk.cyan('3. Save both your seed AND mnemonic for complete wallet recovery')); console.log(chalk.green('\n=== Keep this information secure! ===\n')); process.exit(0); } catch (error) { console.error(chalk.red(`Error processing Midnight seed: ${error.message}`)); process.exit(1); } } // If mnemonic is provided, use it to generate seed if (options.mnemonic) { try { const mnemonic = options.mnemonic.trim(); // Validate mnemonic if (!bip39.validateMnemonic(mnemonic)) { console.error(chalk.red('Error: Invalid BIP39 mnemonic')); process.exit(1); } // Get the entropy that generated this mnemonic const entropy = Buffer.from(bip39.mnemonicToEntropy(mnemonic), 'hex'); console.log(chalk.green('\n=== Midnight Wallet Seed Generator (From Mnemonic) ===\n')); console.log(chalk.yellow('Provided Mnemonic:')); console.log(chalk.white(mnemonic)); console.log('\n' + chalk.yellow('Entropy that generated this mnemonic (hex):')); console.log(chalk.white(entropy.toString('hex'))); if (options.password) { console.log('\n' + chalk.yellow('Note: This seed was generated with a password. You will need this password to recreate the seed from the mnemonic.')); } console.log(chalk.green('\n=== Keep this information secure! ===\n')); process.exit(0); } catch (error) { console.error(chalk.red(`Error processing mnemonic: ${error.message}`)); process.exit(1); } } // New: If seed is provided, treat as seed and verify conversion if (options.seed) { try { const seedHex = options.seed; // Validate seed length if (seedHex.length !== 64) { console.error(chalk.red('Error: Seed must be exactly 32 bytes (64 hex characters)')); process.exit(1); } // Generate mnemonic directly from the seed (this is not standard BIP39, but works for verification) // For verification, we treat the seed as entropy to convert to mnemonic const seedAsEntropy = Buffer.from(seedHex, 'hex'); const mnemonic = bip39.entropyToMnemonic(seedAsEntropy); // Now derive a seed from this mnemonic const derivedSeed = bip39.mnemonicToSeedSync(mnemonic, options.password); // Get a compact seed if requested const compactDerivedSeed = createHash('sha256').update(derivedSeed).digest(); // Print the results console.log(chalk.green('\n=== Midnight Wallet Seed Verification ===\n')); console.log(chalk.yellow('Input Seed/Entropy (hex):')); console.log(chalk.white(seedHex)); console.log('\n' + chalk.yellow('Corresponding BIP39 Mnemonic:')); console.log(chalk.white(mnemonic)); // Show both formats for the derived seed console.log('\n' + chalk.yellow('Derived Full Seed (hex):')); console.log(chalk.white(derivedSeed.toString('hex'))); console.log('\n' + chalk.yellow('Derived Compact Seed (hex):')); console.log(chalk.white(compactDerivedSeed.toString('hex'))); // Check if the derived compact seed matches the input const seedsMatch = seedHex === compactDerivedSeed.toString('hex'); console.log('\n' + chalk.yellow('Verification Result:')); if (seedsMatch) { console.log(chalk.green('✓ Success: Original seed and derived compact seed match!')); } else { console.log(chalk.red('✗ Warning: Original seed and derived compact seed do not match!')); console.log(chalk.yellow('This is EXPECTED when using a seed as initial entropy. In BIP39:')); console.log(chalk.yellow('1. Entropy → Mnemonic → Seed is a one-way process')); console.log(chalk.yellow('2. Your input was treated as entropy to generate a mnemonic')); console.log(chalk.yellow('3. The mnemonic then derives a different seed value')); console.log(chalk.yellow('4. The mnemonic is what should be used for wallet compatibility')); } if (options.password) { console.log('\n' + chalk.yellow('Note: A password was used. The derived seeds will only match the original if the same password was used to create it.')); } console.log(chalk.green('\n=== Keep this information secure! ===\n')); process.exit(0); } catch (error) { console.error(chalk.red(`Error processing seed: ${error.message}`)); process.exit(1); } } // If entropy is provided, use it to generate mnemonic if (options.entropy) { try { // Check if the hex entropy is valid const entropyBuffer = Buffer.from(options.entropy, 'hex'); const validLengths = [16, 20, 24, 28, 32]; // Valid entropy lengths in bytes const entropyLengthBytes = entropyBuffer.length; if (!validLengths.includes(entropyLengthBytes)) { console.error(chalk.red(`Error: Entropy must be 16, 20, 24, 28, or 32 bytes (${validLengths.map(l => l*2).join(', ')} hex characters)`)); process.exit(1); } // Generate the mnemonic from entropy const mnemonic = bip39.entropyToMnemonic(entropyBuffer); console.log(chalk.green('\n=== Midnight Wallet Seed Generator (From Entropy) ===\n')); console.log(chalk.yellow('Provided Entropy (hex):')); console.log(chalk.white(entropyBuffer.toString('hex'))); console.log('\n' + chalk.yellow('Generated BIP39 Mnemonic:')); console.log(chalk.white(mnemonic)); if (options.password) { console.log('\n' + chalk.yellow('Note: This seed was generated with a password. You will need this password to recreate the seed from the mnemonic.')); } console.log(chalk.green('\n=== Keep this information secure! ===\n')); process.exit(0); } catch (error) { console.error(chalk.red(`Error processing entropy: ${error.message}`)); process.exit(1); } } // Standard flow (generating new entropy) if no entropy was provided // Validate word count const wordCount = parseInt(options.words); if (wordCount !== 12 && wordCount !== 24) { console.error(chalk.red('Error: Word count must be either 12 or 24')); process.exit(1); } // Validate format if (options.format !== 'full' && options.format !== 'compact') { console.error(chalk.red('Error: Format must be either "full" or "compact"')); process.exit(1); } // Generate strength based on word count (128 bits for 12 words, 256 bits for 24 words) const strength = wordCount === 12 ? 128 : 256; // Generate random entropy const entropyBytes = strength / 8; const entropy = randomBytes(entropyBytes); // Generate mnemonic from entropy const mnemonic = bip39.entropyToMnemonic(entropy); // Generate a secure API key const apiKey = randomBytes(32).toString('hex'); console.log(chalk.green('\n=== Midnight Wallet Seed Generator ===\n')); console.log(chalk.yellow('Midnight Seed (hex):')); console.log(chalk.white(entropy.toString('hex'))); console.log('\n' + chalk.yellow('BIP39 Mnemonic:')); console.log(chalk.white(mnemonic)); console.log('\n' + chalk.cyan('Important Note for Midnight Wallet:')); console.log(chalk.cyan('For Midnight, your wallet seed is the entropy value shown above')); if (options.password) { console.log('\n' + chalk.yellow('Note: This seed was generated with a password. You will need this password to recreate the seed from the mnemonic.')); } // console.log('\n' + chalk.green('=== API Authentication Setup ===')); // console.log(chalk.yellow('Secure API Key (for .env file):')); // console.log(chalk.white(apiKey)); // console.log('\n' + chalk.cyan('To use this API key:')); // console.log(chalk.cyan('1. Add it to your .env file: API_KEY=' + apiKey)); // console.log(chalk.cyan('2. Include it in API requests with the x-api-key header')); // console.log(chalk.cyan('3. Or pass it as a query parameter: ?api_key=' + apiKey)); console.log(chalk.green('\n=== Keep this information secure! ===\n'));

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evilpixi/pixi-midnight-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server