* Generate a new wallet with BIP-39 mnemonic, save to disk. * New users get both EVM and Solana keys derived from the same mnemonic. * CRITICAL: Verifies the file was actually written after generation.
()
| 124 | * CRITICAL: Verifies the file was actually written after generation. |
| 125 | */ |
| 126 | async function generateAndSaveWallet(): Promise<{ |
| 127 | key: string; |
| 128 | address: string; |
| 129 | mnemonic: string; |
| 130 | solanaPrivateKeyBytes: Uint8Array; |
| 131 | }> { |
| 132 | // Safety: if a mnemonic file already exists, a Solana wallet was derived from it. |
| 133 | // Generating a new wallet would overwrite the mnemonic and lose Solana funds. |
| 134 | const existingMnemonic = await loadMnemonic(); |
| 135 | if (existingMnemonic) { |
| 136 | throw new Error( |
| 137 | `Mnemonic file exists at ${MNEMONIC_FILE} but wallet.key is missing.\n` + |
| 138 | `Refusing to generate a new wallet to protect existing funds.\n\n` + |
| 139 | `Restore your EVM private key using one of:\n` + |
| 140 | ` Windows: set BLOCKRUN_WALLET_KEY=0x<your_key>\n` + |
| 141 | ` Mac/Linux: export BLOCKRUN_WALLET_KEY=0x<your_key>\n\n` + |
| 142 | `Then run: npx @blockrun/clawrouter`, |
| 143 | ); |
| 144 | } |
| 145 | |
| 146 | const mnemonic = generateWalletMnemonic(); |
| 147 | const derived = deriveAllKeys(mnemonic); |
| 148 | |
| 149 | // Create directory |
| 150 | await mkdir(WALLET_DIR, { recursive: true }); |
| 151 | |
| 152 | // Write wallet key file (EVM private key) |
| 153 | await writeFile(WALLET_FILE, derived.evmPrivateKey + "\n", { mode: 0o600 }); |
| 154 | |
| 155 | // Write mnemonic file |
| 156 | await writeFile(MNEMONIC_FILE, mnemonic + "\n", { mode: 0o600 }); |
| 157 | |
| 158 | // CRITICAL: Verify the file was actually written |
| 159 | try { |
| 160 | const verification = (await readTextFile(WALLET_FILE)).trim(); |
| 161 | if (verification !== derived.evmPrivateKey) { |
| 162 | throw new Error("Wallet file verification failed - content mismatch"); |
| 163 | } |
| 164 | console.log(`[ClawRouter] Wallet saved and verified at ${WALLET_FILE}`); |
| 165 | } catch (err) { |
| 166 | throw new Error( |
| 167 | `Failed to verify wallet file after creation: ${err instanceof Error ? err.message : String(err)}`, |
| 168 | { cause: err }, |
| 169 | ); |
| 170 | } |
| 171 | |
| 172 | // Derive Solana address for display |
| 173 | let solanaAddress: string | undefined; |
| 174 | try { |
| 175 | solanaAddress = await getSolanaAddress(derived.solanaPrivateKeyBytes); |
| 176 | } catch { |
| 177 | // Non-fatal — Solana address display is best-effort |
| 178 | } |
| 179 | |
| 180 | // Print prominent backup reminder after generating a new wallet |
| 181 | console.log(`[ClawRouter]`); |
| 182 | console.log(`[ClawRouter] ════════════════════════════════════════════════`); |
| 183 | console.log(`[ClawRouter] NEW WALLET GENERATED — BACK UP YOUR KEY NOW`); |
no test coverage detected