"use strict";
/**
 * Transaction builder for VRSC payments.
 * Builds and signs standard payment transactions offline using @bitgo/utxo-lib.
 *
 * Supports:
 * - R-address (P2PKH) inputs and outputs
 * - i-address (P2ID / identity) inputs and outputs
 * - Mixed inputs (spend from both R and i address in one TX)
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildP2IDScript = buildP2IDScript;
exports.isIAddress = isIAddress;
exports.selectUtxos = selectUtxos;
exports.buildPayment = buildPayment;
exports.buildMultiPayment = buildMultiPayment;
exports.wifToAddress = wifToAddress;
exports.wifToPubkey = wifToPubkey;
// @ts-ignore - VerusCoin fork, no TS declarations
const utxolib = __importStar(require("@bitgo/utxo-lib"));
const keypair_js_1 = require("../identity/keypair.js");
const DEFAULT_FEE = 10000; // 0.0001 VRSC in satoshis
const SATS_PER_COIN = 100000000;
// i-address version byte (same on mainnet and testnet)
const I_ADDRESS_VERSION = 0x66;
/**
 * Build a P2ID (pay-to-identity) output script for an i-address.
 *
 * Verus identity output script format (36 bytes):
 *   PUSH(5) [COptCCParams: v4, EVAL_IDENTITY, m=0, n=0]
 *   OP_CHECKCRYPTOCONDITION
 *   PUSH(27) [COptCCParams: v4, EVAL_IDENTITY, m=0, n=1, dests=1, type=ID, <20-byte hash>]
 *   OP_DROP
 */
function buildP2IDScript(iAddress) {
    const decoded = utxolib.address.fromBase58Check(iAddress);
    if (decoded.version !== I_ADDRESS_VERSION) {
        throw new Error(`Not an i-address (version ${decoded.version.toString(16)}, expected ${I_ADDRESS_VERSION.toString(16)}): ${iAddress}`);
    }
    const hash = decoded.hash; // 20-byte identity hash
    return Buffer.concat([
        Buffer.from([
            0x05, // PUSH 5 bytes
            0x04, 0x03, 0x00, 0x00, 0x00, // COptCCParams: version=4, evalcode=3 (EVAL_IDENTITY), m=0, n=0, flags=0
            0xcc, // OP_CHECKCRYPTOCONDITION
            0x1b, // PUSH 27 bytes
            0x04, 0x03, 0x00, 0x01, 0x01, // COptCCParams: version=4, evalcode=3, m=0, n=1, numDests=1
            0x15, // PUSH 21 bytes (1 type byte + 20 hash bytes)
            0x04, // destination type = 4 (identity)
        ]),
        hash, // 20-byte identity hash
        Buffer.from([0x75]), // OP_DROP
    ]);
}
/**
 * Check if an address is an i-address (identity address).
 */
function isIAddress(address) {
    try {
        const decoded = utxolib.address.fromBase58Check(address);
        return decoded.version === I_ADDRESS_VERSION;
    }
    catch {
        return false;
    }
}
/**
 * Build an output script for any address (R-address or i-address).
 */
function toOutputScript(address, networkObj) {
    if (isIAddress(address)) {
        return buildP2IDScript(address);
    }
    return utxolib.address.toOutputScript(address, networkObj);
}
/**
 * Select UTXOs to cover the target amount (greedy algorithm).
 * Prefers larger UTXOs to minimize inputs.
 */
function selectUtxos(utxos, targetAmount) {
    const targetSatoshis = Math.round(targetAmount * SATS_PER_COIN);
    const sorted = [...utxos].sort((a, b) => b.satoshis - a.satoshis);
    const selected = [];
    let total = 0;
    for (const utxo of sorted) {
        selected.push(utxo);
        total += utxo.satoshis;
        if (total >= targetSatoshis)
            break;
    }
    if (total < targetSatoshis) {
        throw new Error(`Insufficient funds: need ${targetSatoshis} satoshis, have ${total}`);
    }
    return { selected, total };
}
function buildPayment(params) {
    const { wif, toAddress, amount, utxos, fee = DEFAULT_FEE, network = 'verustest', } = params;
    // Validate amount — reject negative/NaN/Infinity/oversized before constructing.
    if (!Number.isFinite(amount) || amount <= 0) {
        throw new Error(`Invalid payment amount: ${amount}`);
    }
    const networkObj = network === 'verustest'
        ? utxolib.networks.verustest
        : utxolib.networks.verus;
    const amountSatoshis = Math.round(amount * SATS_PER_COIN);
    if (!Number.isSafeInteger(amountSatoshis)) {
        throw new Error('Payment amount too large');
    }
    const totalNeeded = amountSatoshis + fee;
    const { selected, total: inputTotal } = selectUtxos(utxos, totalNeeded / SATS_PER_COIN);
    const changeAddress = params.changeAddress || (0, keypair_js_1.keypairFromWIF)(wif, network).address;
    const changeSatoshis = inputTotal - amountSatoshis - fee;
    // Build transaction
    const keyPair = utxolib.ECPair.fromWIF(wif, networkObj);
    const txb = new utxolib.TransactionBuilder(networkObj);
    txb.setVersion(4);
    txb.setVersionGroupId(0x892f2085);
    // Add inputs — use script for i-address UTXOs (identity outputs)
    for (const utxo of selected) {
        if (utxo.script) {
            const scriptBuf = Buffer.from(utxo.script, 'hex');
            txb.addInput(utxo.txid, utxo.vout, 0xffffffff, scriptBuf);
        }
        else {
            txb.addInput(utxo.txid, utxo.vout);
        }
    }
    // Payment output — supports both R-address and i-address destinations
    txb.addOutput(toOutputScript(toAddress, networkObj), amountSatoshis);
    // Change output (vout 1, after the single payment output)
    const hasChange = changeSatoshis > 1000;
    if (hasChange) {
        txb.addOutput(toOutputScript(changeAddress, networkObj), changeSatoshis);
    }
    // Sign all inputs
    for (let i = 0; i < selected.length; i++) {
        txb.sign(i, keyPair, undefined, utxolib.Transaction.SIGHASH_ALL, selected[i].satoshis);
    }
    const tx = txb.build();
    const rawhex = tx.toHex();
    if (!params.returnDetails)
        return rawhex;
    const txid = tx.getId();
    return {
        rawhex,
        spentUtxos: selected.map(u => ({ txid: u.txid, vout: u.vout })),
        changeUtxo: hasChange ? {
            txid,
            vout: 1,
            satoshis: changeSatoshis,
            script: toOutputScript(changeAddress, networkObj).toString('hex'),
            address: changeAddress,
        } : null,
    };
}
function buildMultiPayment(params) {
    const { wif, outputs, utxos, fee = DEFAULT_FEE, network = 'verustest', } = params;
    if (outputs.length === 0)
        throw new Error('At least one output required');
    for (const o of outputs) {
        if (!Number.isFinite(o.amount) || o.amount <= 0) {
            throw new Error(`Invalid output amount: ${o.amount}`);
        }
    }
    const networkObj = network === 'verustest'
        ? utxolib.networks.verustest
        : utxolib.networks.verus;
    const totalOutputSatoshis = outputs.reduce((sum, o) => sum + Math.round(o.amount * SATS_PER_COIN), 0);
    const totalNeeded = totalOutputSatoshis + fee;
    const { selected, total: inputTotal } = selectUtxos(utxos, totalNeeded / SATS_PER_COIN);
    const changeAddress = params.changeAddress || (0, keypair_js_1.keypairFromWIF)(wif, network).address;
    const changeSatoshis = inputTotal - totalOutputSatoshis - fee;
    const keyPair = utxolib.ECPair.fromWIF(wif, networkObj);
    const txb = new utxolib.TransactionBuilder(networkObj);
    txb.setVersion(4);
    txb.setVersionGroupId(0x892f2085);
    // Inputs
    for (const utxo of selected) {
        if (utxo.script) {
            txb.addInput(utxo.txid, utxo.vout, 0xffffffff, Buffer.from(utxo.script, 'hex'));
        }
        else {
            txb.addInput(utxo.txid, utxo.vout);
        }
    }
    // Multiple outputs
    for (const output of outputs) {
        txb.addOutput(toOutputScript(output.address, networkObj), Math.round(output.amount * SATS_PER_COIN));
    }
    // Change
    const hasChange = changeSatoshis > 1000;
    const changeVout = outputs.length; // change output index = after all payment outputs
    if (hasChange) {
        txb.addOutput(toOutputScript(changeAddress, networkObj), changeSatoshis);
    }
    // Sign
    for (let i = 0; i < selected.length; i++) {
        txb.sign(i, keyPair, undefined, utxolib.Transaction.SIGHASH_ALL, selected[i].satoshis);
    }
    const tx = txb.build();
    const rawhex = tx.toHex();
    if (!params.returnDetails)
        return rawhex;
    const txid = tx.getId();
    const changeScript = hasChange ? toOutputScript(changeAddress, networkObj).toString('hex') : '';
    return {
        rawhex,
        spentUtxos: selected.map(u => ({ txid: u.txid, vout: u.vout })),
        changeUtxo: hasChange ? {
            txid,
            vout: changeVout,
            satoshis: changeSatoshis,
            script: changeScript,
            address: changeAddress,
        } : null,
    };
}
function wifToAddress(wif, networkName = 'verustest') {
    return (0, keypair_js_1.keypairFromWIF)(wif, networkName).address;
}
function wifToPubkey(wif, networkName = 'verustest') {
    return (0, keypair_js_1.keypairFromWIF)(wif, networkName).pubkey;
}
//# sourceMappingURL=payment.js.map