import { existsSync, readFileSync } from "fs"; import path from "path"; import { generateTemplateIdentifier } from "@xo-cash/engine"; import { hexToBin, lockingBytecodeToCashAddress } from "@bitauth/libauth"; import { bold, dim } from "../cli-utils.js"; import type { CommandDependencies } from "./types.js"; /** * Prints the help message for the receive command */ export const printReceiveHelp = () => { console.log( ` ${bold("Usage:")} xo-cli receive [role-identifier] ${bold("Description:")} Generate a single-use receiving address from a template. ${bold("Arguments:")} ${dim("Path to the template JSON file")} ${dim("The output identifier within the template (e.g. 'receiveOutput')")} [role-identifier] ${dim("The role identifier (e.g. 'receiver'). Auto-selects the first role if omitted.")} ${bold("Options:")} -h --help ${dim("Show this help message")} `); }; /** * Command which creates a single-use address/lockingScript for a given template and role. * @param deps - The command dependencies. * @param args - Positional args after the command name, e.g. ["template.json", "receiveOutput", "receiver"]. * @param options - Parsed option flags. */ export const handleReceiveCommand = async (deps: CommandDependencies, args: string[], options: Record): Promise => { const templateFile = args[0]; const outputIdentifier = args[1]; const roleIdentifier = args[2]; deps.verboseLogger(`Receive args - template: ${templateFile}, output: ${outputIdentifier}, role: ${roleIdentifier}`); if (!templateFile || !outputIdentifier) { deps.verboseLogger("Missing required arguments"); printReceiveHelp(); return; } // Resolve and read the template file const templatePath = path.resolve(`${process.cwd()}/${templateFile}`); deps.verboseLogger(`Template path: ${templatePath}`); if (!existsSync(templatePath)) { console.error(`Template file does not exist: ${templatePath}`); printReceiveHelp(); return; } const template = readFileSync(templatePath, "utf8"); const templateIdentifier = generateTemplateIdentifier(JSON.parse(template)); deps.verboseLogger(`Template identifier: ${templateIdentifier}`); // Generate the locking bytecode (returned as a hex string) const lockingBytecodeHex = await deps.app.engine.generateLockingBytecode( templateIdentifier, outputIdentifier, roleIdentifier, ); deps.verboseLogger(`Locking bytecode hex: ${lockingBytecodeHex}`); // Convert the locking bytecode to a BCH cash address const result = lockingBytecodeToCashAddress({ bytecode: hexToBin(lockingBytecodeHex), prefix: 'bitcoincash' }); if (typeof result === 'string') { console.error(`Failed to encode address: ${result}`); return; } console.log(result.address); };