@babylonlabs-io/ts-sdk 0.74.0 → 0.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PeginManager-E-UHA1un.cjs → PeginManager-DEByBX5R.cjs} +2 -2
- package/dist/{PeginManager-E-UHA1un.cjs.map → PeginManager-DEByBX5R.cjs.map} +1 -1
- package/dist/{PeginManager-C9RPOd0L.js → PeginManager-DNimKlmw.js} +11 -11
- package/dist/{PeginManager-C9RPOd0L.js.map → PeginManager-DNimKlmw.js.map} +1 -1
- package/dist/{bip322Verify-D6sBBUHE.js → bip322Verify-DSco3Yhl.js} +2 -2
- package/dist/{bip322Verify-D6sBBUHE.js.map → bip322Verify-DSco3Yhl.js.map} +1 -1
- package/dist/buildAndBroadcastReclaim-Cqc6Jiqk.cjs +2 -0
- package/dist/{buildAndBroadcastReclaim-U-PyFfDg.cjs.map → buildAndBroadcastReclaim-Cqc6Jiqk.cjs.map} +1 -1
- package/dist/{buildAndBroadcastReclaim-BXclIcu_.js → buildAndBroadcastReclaim-DCGcupg7.js} +6 -6
- package/dist/{buildAndBroadcastReclaim-BXclIcu_.js.map → buildAndBroadcastReclaim-DCGcupg7.js.map} +1 -1
- package/dist/constants-BTZdyOuk.cjs +2 -0
- package/dist/constants-BTZdyOuk.cjs.map +1 -0
- package/dist/constants-D5vORbQE.js +29 -0
- package/dist/constants-D5vORbQE.js.map +1 -0
- package/dist/fundPeginTransaction-54-Rwb2k.cjs +2 -0
- package/dist/fundPeginTransaction-54-Rwb2k.cjs.map +1 -0
- package/dist/fundPeginTransaction-CwhRbCQC.js +88 -0
- package/dist/fundPeginTransaction-CwhRbCQC.js.map +1 -0
- package/dist/{index-Cs7_Evtk.cjs → index-CK1Xvq4K.cjs} +2 -2
- package/dist/{index-Cs7_Evtk.cjs.map → index-CK1Xvq4K.cjs.map} +1 -1
- package/dist/{index-LgmJhdEN.js → index-DQ5bfoT3.js} +2 -2
- package/dist/{index-LgmJhdEN.js.map → index-DQ5bfoT3.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +15 -15
- package/dist/noPayout-4RZB2g1e.cjs +2 -0
- package/dist/noPayout-4RZB2g1e.cjs.map +1 -0
- package/dist/noPayout-DiGr4eas.js +361 -0
- package/dist/noPayout-DiGr4eas.js.map +1 -0
- package/dist/{parseFundingOutpoints-B67CoXpF.js → parseFundingOutpoints-BQWvINfm.js} +2 -2
- package/dist/{parseFundingOutpoints-B67CoXpF.js.map → parseFundingOutpoints-BQWvINfm.js.map} +1 -1
- package/dist/pegin-registration-client-CXK3B_D1.js +882 -0
- package/dist/pegin-registration-client-CXK3B_D1.js.map +1 -0
- package/dist/pegin-registration-client-DLsMNm8Z.cjs +2 -0
- package/dist/pegin-registration-client-DLsMNm8Z.cjs.map +1 -0
- package/dist/{primeVpAuth-DBHohVKM.cjs → primeVpAuth-BOGKniTl.cjs} +2 -2
- package/dist/{primeVpAuth-DBHohVKM.cjs.map → primeVpAuth-BOGKniTl.cjs.map} +1 -1
- package/dist/{primeVpAuth-DvaD45q_.js → primeVpAuth-DdfHSC53.js} +4 -4
- package/dist/{primeVpAuth-DvaD45q_.js.map → primeVpAuth-DdfHSC53.js.map} +1 -1
- package/dist/{psbtInputFields-BIxBpPj2.cjs → psbtInputFields-BrV_ZfL_.cjs} +2 -2
- package/dist/{psbtInputFields-BIxBpPj2.cjs.map → psbtInputFields-BrV_ZfL_.cjs.map} +1 -1
- package/dist/{psbtInputFields-B3e05Xe6.js → psbtInputFields-CCVyaQMi.js} +2 -2
- package/dist/{psbtInputFields-B3e05Xe6.js.map → psbtInputFields-CCVyaQMi.js.map} +1 -1
- package/dist/secp256k1-D-H9jmd7.js +1766 -0
- package/dist/secp256k1-D-H9jmd7.js.map +1 -0
- package/dist/secp256k1-DDHQ1_za.cjs +2 -0
- package/dist/secp256k1-DDHQ1_za.cjs.map +1 -0
- package/dist/{sha2-CrlWM5S8.js → sha2-CLpQd0Rd.js} +8 -8
- package/dist/{sha2-CrlWM5S8.js.map → sha2-CLpQd0Rd.js.map} +1 -1
- package/dist/{signPsbtsWithFallback-DMSpIQWH.cjs → signPsbtsWithFallback-DtwzL5AA.cjs} +2 -2
- package/dist/{signPsbtsWithFallback-DMSpIQWH.cjs.map → signPsbtsWithFallback-DtwzL5AA.cjs.map} +1 -1
- package/dist/{signPsbtsWithFallback-0VBALhjS.js → signPsbtsWithFallback-f7QLp0q8.js} +4 -4
- package/dist/{signPsbtsWithFallback-0VBALhjS.js.map → signPsbtsWithFallback-f7QLp0q8.js.map} +1 -1
- package/dist/tbv/core/clients/eth/index.cjs +1 -1
- package/dist/tbv/core/clients/eth/index.js +3 -3
- package/dist/tbv/core/clients/index.cjs +1 -1
- package/dist/tbv/core/clients/index.js +5 -5
- package/dist/tbv/core/clients/vault-provider/status/index.cjs +1 -1
- package/dist/tbv/core/clients/vault-provider/status/index.js +1 -1
- package/dist/tbv/core/index.cjs +1 -1
- package/dist/tbv/core/index.js +15 -15
- package/dist/tbv/core/managers/index.cjs +1 -1
- package/dist/tbv/core/managers/index.js +2 -2
- package/dist/tbv/core/primitives/index.cjs +1 -1
- package/dist/tbv/core/primitives/index.js +4 -4
- package/dist/tbv/core/primitives/psbt/__tests__/assertPeginTxShape.test.d.ts +5 -6
- package/dist/tbv/core/primitives/psbt/__tests__/assertPeginTxShape.test.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/__tests__/assertWasmPeginSizing.test.d.ts +2 -2
- package/dist/tbv/core/primitives/psbt/assertWasmPeginSizing.d.ts +36 -14
- package/dist/tbv/core/primitives/psbt/assertWasmPeginSizing.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/constants.d.ts +39 -0
- package/dist/tbv/core/primitives/psbt/constants.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/pegin.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/peginInput.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/refund.d.ts.map +1 -1
- package/dist/tbv/core/services/index.cjs +1 -1
- package/dist/tbv/core/services/index.js +2 -2
- package/dist/tbv/core/utils/fee/constants.d.ts +4 -4
- package/dist/tbv/core/utils/index.cjs +1 -1
- package/dist/tbv/core/utils/index.js +2 -2
- package/dist/tbv/core/utils/transaction/fundPeginTransaction.d.ts.map +1 -1
- package/dist/tbv/core/vault-secrets/index.js +1 -1
- package/dist/tbv/index.cjs +1 -1
- package/dist/tbv/index.js +15 -15
- package/dist/test/setup.browser.d.ts +2 -0
- package/dist/test/setup.browser.d.ts.map +1 -0
- package/dist/testing/index.js +1 -1
- package/dist/{toRefundInputs-C3oIN0aW.js → toRefundInputs-BNSXIymn.js} +7 -7
- package/dist/{toRefundInputs-C3oIN0aW.js.map → toRefundInputs-BNSXIymn.js.map} +1 -1
- package/dist/{toRefundInputs-BwG6T0PP.cjs → toRefundInputs-meTyor7r.cjs} +2 -2
- package/dist/{toRefundInputs-BwG6T0PP.cjs.map → toRefundInputs-meTyor7r.cjs.map} +1 -1
- package/dist/{types-DSlcXtog.cjs → types-BzSOthHH.cjs} +2 -2
- package/dist/{types-DSlcXtog.cjs.map → types-BzSOthHH.cjs.map} +1 -1
- package/dist/{types-CPDLZm3l.js → types-C5DyrC8F.js} +2 -2
- package/dist/{types-CPDLZm3l.js.map → types-C5DyrC8F.js.map} +1 -1
- package/dist/verifyKeyPathSchnorrSignature-BVtNwgK6.js +360 -0
- package/dist/verifyKeyPathSchnorrSignature-BVtNwgK6.js.map +1 -0
- package/dist/verifyKeyPathSchnorrSignature-BjB8Znly.cjs +2 -0
- package/dist/verifyKeyPathSchnorrSignature-BjB8Znly.cjs.map +1 -0
- package/dist/verifyScriptPathSchnorrSignature-D_epga4j.js +1140 -0
- package/dist/verifyScriptPathSchnorrSignature-D_epga4j.js.map +1 -0
- package/dist/verifyScriptPathSchnorrSignature-DaRqvEgW.cjs +2 -0
- package/dist/verifyScriptPathSchnorrSignature-DaRqvEgW.cjs.map +1 -0
- package/package.json +4 -1
- package/dist/buildAndBroadcastReclaim-U-PyFfDg.cjs +0 -2
- package/dist/constants-Bh38W9mq.cjs +0 -2
- package/dist/constants-Bh38W9mq.cjs.map +0 -1
- package/dist/constants-CPzppOdq.js +0 -17
- package/dist/constants-CPzppOdq.js.map +0 -1
- package/dist/fundPeginTransaction-B2wyjqp0.js +0 -83
- package/dist/fundPeginTransaction-B2wyjqp0.js.map +0 -1
- package/dist/fundPeginTransaction-BLD8FRL9.cjs +0 -2
- package/dist/fundPeginTransaction-BLD8FRL9.cjs.map +0 -1
- package/dist/noPayout-DpGRzidi.js +0 -353
- package/dist/noPayout-DpGRzidi.js.map +0 -1
- package/dist/noPayout-zEifyecV.cjs +0 -2
- package/dist/noPayout-zEifyecV.cjs.map +0 -1
- package/dist/pegin-registration-client-6vLk2itL.cjs +0 -2
- package/dist/pegin-registration-client-6vLk2itL.cjs.map +0 -1
- package/dist/pegin-registration-client-BdRiR8br.js +0 -2635
- package/dist/pegin-registration-client-BdRiR8br.js.map +0 -1
- package/dist/vaultCoreVersion-DYfw3qed.cjs +0 -2
- package/dist/vaultCoreVersion-DYfw3qed.cjs.map +0 -1
- package/dist/vaultCoreVersion-D_fP9P3C.js +0 -10
- package/dist/vaultCoreVersion-D_fP9P3C.js.map +0 -1
- package/dist/verifyKeyPathSchnorrSignature-BedSbH2D.cjs +0 -2
- package/dist/verifyKeyPathSchnorrSignature-BedSbH2D.cjs.map +0 -1
- package/dist/verifyKeyPathSchnorrSignature-BrgUJmV0.js +0 -372
- package/dist/verifyKeyPathSchnorrSignature-BrgUJmV0.js.map +0 -1
- package/dist/verifyScriptPathSchnorrSignature-BfNUSgA7.cjs +0 -2
- package/dist/verifyScriptPathSchnorrSignature-BfNUSgA7.cjs.map +0 -1
- package/dist/verifyScriptPathSchnorrSignature-Ciq_xZhw.js +0 -951
- package/dist/verifyScriptPathSchnorrSignature-Ciq_xZhw.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"psbtInputFields-B3e05Xe6.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"psbtInputFields-CCVyaQMi.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type"],"mappings":"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"}
|