2026-02-26 11:19:47 +01:00
|
|
|
// 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<WalletAdapter> {
|
|
|
|
|
// 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: "",
|
|
|
|
|
|
2026-03-09 15:28:18 +01:00
|
|
|
getRelayPrivateKey(_uri: string): Uint8Array {
|
2026-02-26 11:19:47 +01:00
|
|
|
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<any> {
|
|
|
|
|
throw new Error("signTransaction not implemented in test wallet");
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Main ----
|
|
|
|
|
|
|
|
|
|
export async function runWalletMode(options: {
|
|
|
|
|
relay: string;
|
|
|
|
|
uri: string;
|
|
|
|
|
privateKey?: string;
|
|
|
|
|
}): Promise<void> {
|
|
|
|
|
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(() => {});
|
|
|
|
|
}
|