// Copyright (C) 2026 Whiterun LLC, // This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later. // A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html import chalk from "chalk"; import ora from "ora"; import { WalletConnectionManager, type WalletAdapter, DerivationPath, } from "@wizardconnect/wallet"; import { generateKeyExchangeCredentials, hexToBin } from "@wizardconnect/core"; import { deriveHdPrivateNodeFromSeed, deriveHdPrivateNodeChild, deriveHdPath, deriveHdPublicNode, encodeHdPublicKey, secp256k1, } from "@bitauth/libauth"; // ---- Build WalletAdapter ---- async function buildAdapter( relayPrivateKey: Uint8Array, ): Promise { // Use relay private key as HD seed for deterministic derivation const hdMaster = deriveHdPrivateNodeFromSeed(relayPrivateKey); const hdMain = deriveHdPath(hdMaster, "m/0") as any; const hdChange = deriveHdPath(hdMaster, "m/1") as any; const hdDefi = deriveHdPath(hdMaster, "m/7") as any; return { walletName: "wiz-test CLI wallet", walletIcon: "", getRelayPrivateKey(): Uint8Array { return relayPrivateKey; }, getPublicKey(path: DerivationPath, index: bigint): Uint8Array { const hdChain = (path as number) === 1 ? hdChange : (path as number) === 7 ? hdDefi : hdMain; const child = deriveHdPrivateNodeChild(hdChain, Number(index)); const pubKey = secp256k1.derivePublicKeyCompressed(child.privateKey); if (typeof pubKey === "string") throw new Error(`secp256k1: ${pubKey}`); return pubKey; }, getXpub(path: DerivationPath): string { const hdChain = (path as number) === 1 ? hdChange : (path as number) === 7 ? hdDefi : hdMain; const result = encodeHdPublicKey({ node: deriveHdPublicNode(hdChain), network: "mainnet", }); if (typeof result === "string") throw new Error(`encodeHdPublicKey: ${result}`); return result.hdPublicKey; }, async signTransaction(_request): Promise { throw new Error("signTransaction not implemented in test wallet"); }, }; } // ---- Main ---- export async function runWalletMode(options: { relay: string; uri: string; privateKey?: string; }): Promise { console.log(chalk.bold("\nwiz-test wallet mode")); console.log(chalk.dim(`relay: ${options.relay}`)); console.log(chalk.dim(`uri: ${options.uri}`)); console.log(); // Derive relay private key let relayPrivKey: Uint8Array; if (options.privateKey) { if (options.privateKey.length !== 64) { console.error(chalk.red("--private-key must be 64 hex chars")); process.exit(1); } relayPrivKey = hexToBin(options.privateKey); } else { const creds = generateKeyExchangeCredentials(); relayPrivKey = hexToBin(creds.privateKey); console.log(chalk.dim(`generated relay key: ${creds.privateKey}`)); } const buildSpinner = ora("Building test wallet adapter...").start(); const adapter = await buildAdapter(relayPrivKey); buildSpinner.succeed("Wallet adapter ready"); const manager = new WalletConnectionManager(adapter); manager.on("connectionStatusChanged", (connectionId, status) => { const s = status.status; if (s === "connected") { console.log(chalk.green(`connection ${connectionId}: connected`)); } else if (s === "reconnecting") { console.log(chalk.yellow(`connection ${connectionId}: reconnecting...`)); } else if (s === "disconnected") { console.log(chalk.red(`connection ${connectionId}: disconnected`)); } }); manager.on("pendingSignRequest", (request) => { console.log( chalk.yellow("← sign_request") + chalk.dim( ` conn=${request.connectionId} seq=${request.request.sequence}`, ), ); console.log(chalk.dim(" (auto-rejecting — test wallet does not sign)")); manager .sendSignError( request.connectionId, request.request.sequence, "Test wallet cannot sign", ) .catch(() => {}); }); console.log(chalk.dim("\nConnecting to dapp...")); const connectionId = manager.connect(options.uri); console.log(chalk.dim(`connection id: ${connectionId}`)); console.log(chalk.dim("Press Ctrl+C to quit\n")); process.on("SIGINT", () => { console.log(chalk.yellow("\nShutting down...")); manager.disconnectAll(); process.exit(0); }); await new Promise(() => {}); }