@babylonlabs-io/ts-sdk 0.48.1 → 0.48.2

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.
Files changed (62) hide show
  1. package/dist/{PayoutManager-BbemBIo9.js → PayoutManager-D02AePm4.js} +78 -65
  2. package/dist/PayoutManager-D02AePm4.js.map +1 -0
  3. package/dist/PayoutManager-DD1audlx.cjs +2 -0
  4. package/dist/PayoutManager-DD1audlx.cjs.map +1 -0
  5. package/dist/{PeginManager-BMO6R9I9.js → PeginManager-6seoi9mV.js} +217 -198
  6. package/dist/PeginManager-6seoi9mV.js.map +1 -0
  7. package/dist/PeginManager-CHZieoEQ.cjs +2 -0
  8. package/dist/PeginManager-CHZieoEQ.cjs.map +1 -0
  9. package/dist/buildAndBroadcastRefund-Bi07LxuY.cjs +2 -0
  10. package/dist/buildAndBroadcastRefund-Bi07LxuY.cjs.map +1 -0
  11. package/dist/{buildAndBroadcastRefund-hM9Wo0VZ.js → buildAndBroadcastRefund-DXHs6unL.js} +448 -422
  12. package/dist/buildAndBroadcastRefund-DXHs6unL.js.map +1 -0
  13. package/dist/{challengeAssert-1fy_EzAi.js → challengeAssert-ChvLypwc.js} +7 -7
  14. package/dist/{challengeAssert-1fy_EzAi.js.map → challengeAssert-ChvLypwc.js.map} +1 -1
  15. package/dist/challengeAssert-Culc7DoS.cjs +2 -0
  16. package/dist/{challengeAssert-DEw-z3n9.cjs.map → challengeAssert-Culc7DoS.cjs.map} +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.js +170 -169
  19. package/dist/{noPayout-BwSaoU7w.js → noPayout-CA4-x5vZ.js} +2 -2
  20. package/dist/{noPayout-BwSaoU7w.js.map → noPayout-CA4-x5vZ.js.map} +1 -1
  21. package/dist/noPayout-CJ_Acpl_.cjs +2 -0
  22. package/dist/{noPayout-B2Xd40nk.cjs.map → noPayout-CJ_Acpl_.cjs.map} +1 -1
  23. package/dist/tbv/core/index.cjs +1 -1
  24. package/dist/tbv/core/index.js +168 -167
  25. package/dist/tbv/core/managers/PayoutManager.d.ts.map +1 -1
  26. package/dist/tbv/core/managers/PeginManager.d.ts.map +1 -1
  27. package/dist/tbv/core/managers/index.cjs +1 -1
  28. package/dist/tbv/core/managers/index.js +2 -2
  29. package/dist/tbv/core/primitives/index.cjs +1 -1
  30. package/dist/tbv/core/primitives/index.d.ts +8 -7
  31. package/dist/tbv/core/primitives/index.d.ts.map +1 -1
  32. package/dist/tbv/core/primitives/index.js +30 -29
  33. package/dist/tbv/core/primitives/psbt/__tests__/verifyScriptPathSchnorrSignature.test.d.ts +14 -0
  34. package/dist/tbv/core/primitives/psbt/__tests__/verifyScriptPathSchnorrSignature.test.d.ts.map +1 -0
  35. package/dist/tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature.d.ts +53 -0
  36. package/dist/tbv/core/primitives/psbt/verifyScriptPathSchnorrSignature.d.ts.map +1 -0
  37. package/dist/tbv/core/services/deposit/signDepositorGraph.d.ts.map +1 -1
  38. package/dist/tbv/core/services/index.cjs +1 -1
  39. package/dist/tbv/core/services/index.js +2 -2
  40. package/dist/tbv/core/services/refund/buildAndBroadcastRefund.d.ts.map +1 -1
  41. package/dist/tbv/index.cjs +1 -1
  42. package/dist/tbv/index.js +168 -167
  43. package/dist/verifyScriptPathSchnorrSignature-D43cncKJ.cjs +2 -0
  44. package/dist/verifyScriptPathSchnorrSignature-D43cncKJ.cjs.map +1 -0
  45. package/dist/verifyScriptPathSchnorrSignature-DFJAEleY.js +563 -0
  46. package/dist/verifyScriptPathSchnorrSignature-DFJAEleY.js.map +1 -0
  47. package/package.json +1 -1
  48. package/dist/PayoutManager-BLpgkfOS.cjs +0 -2
  49. package/dist/PayoutManager-BLpgkfOS.cjs.map +0 -1
  50. package/dist/PayoutManager-BbemBIo9.js.map +0 -1
  51. package/dist/PeginManager-BMO6R9I9.js.map +0 -1
  52. package/dist/PeginManager-CfkjDMy7.cjs +0 -2
  53. package/dist/PeginManager-CfkjDMy7.cjs.map +0 -1
  54. package/dist/assertPsbtUnsignedTxMatches-BHyBdtxs.js +0 -481
  55. package/dist/assertPsbtUnsignedTxMatches-BHyBdtxs.js.map +0 -1
  56. package/dist/assertPsbtUnsignedTxMatches-s9H0Qqkl.cjs +0 -2
  57. package/dist/assertPsbtUnsignedTxMatches-s9H0Qqkl.cjs.map +0 -1
  58. package/dist/buildAndBroadcastRefund-CLvF5ETe.cjs +0 -2
  59. package/dist/buildAndBroadcastRefund-CLvF5ETe.cjs.map +0 -1
  60. package/dist/buildAndBroadcastRefund-hM9Wo0VZ.js.map +0 -1
  61. package/dist/challengeAssert-DEw-z3n9.cjs +0 -2
  62. package/dist/noPayout-B2Xd40nk.cjs +0 -2
@@ -1,2 +0,0 @@
1
- "use strict";const b=require("@babylonlabs-io/babylon-tbv-rust-wasm"),I=require("./fundPeginTransaction-DuMwnytD.cjs"),w=require("buffer"),v=require("bitcoinjs-lib"),c=require("./bitcoin-CHfKAhcI.cjs");async function X(e,t){const n=t.pegInAmounts.length;if(e.htlcValues.length!==n)throw new Error(`WASM Pre-PegIn returned ${e.htlcValues.length} HTLC value(s), expected ${n} (one per requested deposit).`);if(e.peginAmounts.length!==n||e.htlcScriptPubKeys.length!==n||e.htlcAddresses.length!==n)throw new Error(`WASM Pre-PegIn returned mismatched array lengths (htlcValues=${e.htlcValues.length}, peginAmounts=${e.peginAmounts.length}, htlcScriptPubKeys=${e.htlcScriptPubKeys.length}, htlcAddresses=${e.htlcAddresses.length}); expected ${n} each.`);if(e.depositorClaimValue<=0n)throw new Error(`WASM Pre-PegIn returned non-positive depositorClaimValue ${e.depositorClaimValue}; expected > 0.`);const r=await b.computeMinClaimValue(t.numLocalChallengers,t.universalChallengerPubkeys.length,t.councilQuorum,t.councilSize,t.feeRate);if(e.depositorClaimValue!==r)throw new Error(`WASM Pre-PegIn depositorClaimValue ${e.depositorClaimValue} does not match the independently computed minimum claim value ${r} (numLocalChallengers=${t.numLocalChallengers}, numUniversalChallengers=${t.universalChallengerPubkeys.length}, councilQuorum=${t.councilQuorum}, councilSize=${t.councilSize}, feeRate=${t.feeRate}).`);const i=t.minPeginFeeRate*I.MAX_REASONABLE_PEGIN_VBYTES;for(let o=0;o<n;o++){const u=t.pegInAmounts[o],s=e.peginAmounts[o],a=e.htlcValues[o];if(s!==u)throw new Error(`WASM Pre-PegIn peginAmount[${o}] ${s} does not match the requested amount ${u}; refusing to build a tx whose recorded amount differs from the depositor's request.`);if(s<=0n)throw new Error(`WASM Pre-PegIn peginAmount[${o}] is non-positive (${s}); expected > 0.`);if(a<=0n)throw new Error(`WASM Pre-PegIn htlcValue[${o}] is non-positive (${a}); expected > 0.`);const l=a-s-e.depositorClaimValue;if(l<=0n)throw new Error(`WASM Pre-PegIn htlcValue[${o}] ${a} does not strictly cover peginAmount ${s} + depositorClaimValue ${e.depositorClaimValue} + a PegIn fee (implied fee ${l}).`);if(l>i)throw new Error(`WASM Pre-PegIn implied PegIn fee for HTLC[${o}] (${l} sat) exceeds the plausibility cap ${i} sat (minPeginFeeRate=${t.minPeginFeeRate} × ${I.MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${a} appears grossly inflated.`)}}function z(e,t,n){if(e.length<t.length)throw new Error(`Encoded Pre-PegIn tx has ${e.length} output(s), fewer than the ${t.length} HTLC output(s) the cross-check validated.`);for(let r=0;r<t.length;r++){const i=BigInt(e[r].value);if(i!==t[r])throw new Error(`Encoded Pre-PegIn HTLC output[${r}] value ${i} does not match the cross-checked htlcValue ${t[r]}; the funded/signed tx would not pay the validated amount.`);const o=e[r].script.toString("hex").toLowerCase(),u=n[r].toLowerCase();if(o!==u)throw new Error(`Encoded Pre-PegIn HTLC output[${r}] scriptPubKey ${o} does not match the cross-checked htlcScriptPubKey ${u}.`)}}const V=64,D=/^[0-9a-fA-F]+$/;async function Y(e){const t=H(e.authAnchorHash),n=await b.createPrePeginTransaction({depositorPubkey:e.depositorPubkey,vaultProviderPubkey:e.vaultProviderPubkey,vaultKeeperPubkeys:e.vaultKeeperPubkeys,universalChallengerPubkeys:e.universalChallengerPubkeys,hashlocks:[...e.hashlocks],timelockRefund:e.timelockRefund,pegInAmounts:[...e.pegInAmounts],feeRate:e.feeRate,minPeginFeeRate:e.minPeginFeeRate,numLocalChallengers:e.numLocalChallengers,councilQuorum:e.councilQuorum,councilSize:e.councilSize,network:e.network,authAnchorHash:t});await X(n,e);const r=I.parseUnfundedWasmTransaction(n.txHex);z(r.outputs,n.htlcValues,n.htlcScriptPubKeys);const i=r.outputs.reduce((u,s)=>u+BigInt(s.value),0n),o=t!==void 0?n.htlcValues.length:null;return{psbtHex:n.txHex,totalOutputValue:i,htlcValues:n.htlcValues,htlcScriptPubKeys:n.htlcScriptPubKeys,htlcAddresses:n.htlcAddresses,peginAmounts:n.peginAmounts,depositorClaimValue:n.depositorClaimValue,authAnchorVout:o}}function H(e){if(e===void 0)return;const t=e.startsWith("0x")||e.startsWith("0X")?e.slice(2):e;if(t.length!==V||!D.test(t))throw new Error(`authAnchorHash must be 32-byte hex (${V} chars, no 0x prefix); got length ${t.length}`);return t.toLowerCase()}async function G(e){const t=await b.buildPeginTxFromPrePegin({depositorPubkey:e.prePeginParams.depositorPubkey,vaultProviderPubkey:e.prePeginParams.vaultProviderPubkey,vaultKeeperPubkeys:e.prePeginParams.vaultKeeperPubkeys,universalChallengerPubkeys:e.prePeginParams.universalChallengerPubkeys,hashlocks:[...e.prePeginParams.hashlocks],timelockRefund:e.prePeginParams.timelockRefund,pegInAmounts:[...e.prePeginParams.pegInAmounts],feeRate:e.prePeginParams.feeRate,minPeginFeeRate:e.prePeginParams.minPeginFeeRate,numLocalChallengers:e.prePeginParams.numLocalChallengers,councilQuorum:e.prePeginParams.councilQuorum,councilSize:e.prePeginParams.councilSize,network:e.prePeginParams.network,authAnchorHash:H(e.prePeginParams.authAnchorHash)},e.timelockPegin,e.fundedPrePeginTxHex,e.htlcVout);return{txHex:t.txHex,txid:t.txid,vaultScriptPubKey:t.vaultScriptPubKey,vaultValue:t.vaultValue}}async function W(e){const t=await b.createPayoutConnector({depositor:e.depositor,vaultProvider:e.vaultProvider,vaultKeepers:e.vaultKeepers,universalChallengers:e.universalChallengers,timelockPegin:e.timelockPegin},e.network);return{payoutScript:t.payoutScript,taprootScriptHash:t.taprootScriptHash,scriptPubKey:t.scriptPubKey,address:t.address,payoutControlBlock:t.payoutControlBlock}}const Q=2,A=0,k=0,B=546,Z=3,R=2,C=1e4,q=3,N=10,L=100;async function j(e){const t=c.stripHexPrefix(e.payoutTxHex),n=c.stripHexPrefix(e.peginTxHex),r=c.stripHexPrefix(e.assertTxHex),i=await W({depositor:e.depositorBtcPubkey,vaultProvider:e.vaultProviderBtcPubkey,vaultKeepers:e.vaultKeeperBtcPubkeys,universalChallengers:e.universalChallengerBtcPubkeys,timelockPegin:e.timelockPegin,network:e.network}),o=c.hexToUint8Array(i.payoutScript),u=c.hexToUint8Array(i.payoutControlBlock),s=v.Transaction.fromHex(t),a=v.Transaction.fromHex(n),l=v.Transaction.fromHex(r),y=new v.Psbt;if(y.setVersion(s.version),y.setLocktime(s.locktime),s.ins.length!==2)throw new Error(`Payout transaction must have exactly 2 inputs, got ${s.ins.length}`);const d=s.ins[0],h=s.ins[1],P=c.uint8ArrayToHex(new Uint8Array(d.hash).slice().reverse()),f=a.getId();if(P!==f||d.index!==A)throw new Error(`Input 0 must spend PegIn:${A}. Expected ${f}:${A}, got ${P}:${d.index}`);const $=c.uint8ArrayToHex(new Uint8Array(h.hash).slice().reverse()),m=l.getId();if($!==m||h.index!==k)throw new Error(`Input 1 must spend Assert:${k}. Expected ${m}:${k}, got ${$}:${h.index}`);const g=a.outs[d.index];if(!g)throw new Error(`Previous output not found for input 0 (txid: ${P}, index: ${d.index})`);const x=l.outs[h.index];if(!x)throw new Error(`Previous output not found for input 1 (txid: ${$}, index: ${h.index})`);J({payoutTx:s,peginValueSats:g.value,claimerBtcPubkey:e.claimerBtcPubkey,vaultProviderBtcPubkey:e.vaultProviderBtcPubkey,depositorBtcPubkey:e.depositorBtcPubkey,vaultKeeperBtcPubkeys:e.vaultKeeperBtcPubkeys,registeredPayoutScriptPubKey:e.registeredPayoutScriptPubKey,commissionBps:e.commissionBps});const S=g.value+x.value;let T=0;for(const E of s.outs)T+=E.value;if(T>S)throw new Error(`Payout outputs (${T} sats) exceed inputs (${S} sats); invalid transaction.`);const O=S-T,U=Math.floor(S*N/L);if(O>U)throw new Error(`Payout implicit fee ${O} sats exceeds the safety cap of ${U} sats (${N}/${L} of inputs=${S}); refusing to sign payout.`);y.addInput({hash:d.hash,index:d.index,sequence:d.sequence,witnessUtxo:{script:g.script,value:g.value},tapLeafScript:[{leafVersion:c.TAPSCRIPT_LEAF_VERSION,script:w.Buffer.from(o),controlBlock:w.Buffer.from(u)}],tapInternalKey:w.Buffer.from(b.tapInternalPubkey)}),y.addInput({hash:h.hash,index:h.index,sequence:h.sequence,witnessUtxo:{script:x.script,value:x.value}});for(const E of s.outs)y.addOutput({script:E.script,value:E.value});return{psbtHex:y.toHex()}}function J(e){const{payoutTx:t,peginValueSats:n,claimerBtcPubkey:r,vaultProviderBtcPubkey:i,depositorBtcPubkey:o,vaultKeeperBtcPubkeys:u,registeredPayoutScriptPubKey:s,commissionBps:a}=e;if(!c.isValidHex(s))throw new Error("Invalid registeredPayoutScriptPubKey: not valid hex");const l=c.stripHexPrefix(r).toLowerCase(),y=c.stripHexPrefix(i).toLowerCase(),d=c.stripHexPrefix(o).toLowerCase(),h=u.map(x=>c.stripHexPrefix(x).toLowerCase());let P,f,$;if(l===y)P="vp-claimer",f=Z,$=c.stripHexPrefix(s);else if(l===d)P="depositor-as-claimer",f=R,$=c.stripHexPrefix(s);else if(h.includes(l))P="vk-claimer",f=R,$=c.stripHexPrefix(c.deriveBip86ScriptPubKeyHex(l));else throw new Error(`Unknown claimer pubkey ${l}: not VP, depositor, or a registered vault keeper`);if(t.outs.length!==f)throw new Error(`Payout transaction has ${t.outs.length} output(s), expected exactly ${f} for role ${P}.`);const m=w.Buffer.from($,"hex");if(!t.outs[0].script.equals(m))throw new Error(`Payout transaction output 0 does not pay the expected scriptPubKey for role ${P}`);const g=f-1;if(t.outs[g].value!==B)throw new Error(`Payout CPFP anchor (out ${g}) value ${t.outs[g].value} sats must equal ${B} sats`);if(P==="vp-claimer"){if(!Number.isInteger(a)||a<0||a>=C)throw new Error(`commissionBps must be an integer in [0, ${C}), got ${a}`);const x=Math.floor(n*a/C);if(t.outs[1].value>x)throw new Error(`Payout VP commission (out 1) value ${t.outs[1].value} sats exceeds cap ${x} sats (${a} bps of peginValue=${n})`)}}function ee(e,t,n=0){const r=v.Psbt.fromHex(e);if(n>=r.data.inputs.length)throw new Error(`Input index ${n} out of range (${r.data.inputs.length} inputs)`);const i=r.data.inputs[n];if(i.tapScriptSig&&i.tapScriptSig.length>0){const o=c.hexToUint8Array(t);for(const u of i.tapScriptSig)if(u.pubkey.equals(w.Buffer.from(o)))return K(u.signature,n);throw new Error(`No signature found for depositor pubkey: ${t} at input ${n}`)}if(i.finalScriptWitness&&i.finalScriptWitness.length>0){const o=te(i.finalScriptWitness);if(o.length!==q)throw new Error(`Unexpected finalized witness stack size at input ${n}: expected ${q} items (signature, script, controlBlock), got ${o.length}`);return K(o[0],n)}throw new Error(`No tapScriptSig or finalScriptWitness found in signed PSBT at input ${n}`)}function K(e,t){if(e.length===64)return c.uint8ArrayToHex(new Uint8Array(e));throw e.length===65?new Error(`Unexpected sighash byte 0x${e[64].toString(16).padStart(2,"0")} at input ${t}. Expected implicit SIGHASH_DEFAULT as a 64-byte signature.`):new Error(`Unexpected signature length at input ${t}: ${e.length}`)}function te(e){const t=[];let n=0;const r=u=>{if(n+u>e.length)throw new Error(`Malformed witness data: need ${u} byte(s) at offset ${n}, only ${e.length-n} remaining`)},i=()=>{r(1);const u=e[n++];if(u<253)return u;if(u===253){r(2);const s=(e[n]|e[n+1]<<8)>>>0;return n+=2,s}if(u===254){r(4);const s=(e[n]|e[n+1]<<8|e[n+2]<<16|e[n+3]<<24)>>>0;return n+=4,s}throw new Error(`Malformed witness data: 8-byte varint (0xff) not supported at offset ${n-1}`)},o=i();for(let u=0;u<o;u++){const s=i();r(s),t.push(w.Buffer.from(e.subarray(n,n+s))),n+=s}if(n!==e.length)throw new Error(`Malformed witness data: ${e.length-n} trailing byte(s) after parsing ${o} item(s)`);return t}class p extends Error{constructor(t){super(`Wallet returned a PSBT for a different transaction: ${t}`),this.name="PsbtSubstitutionError"}}function M(e,t){try{return v.Psbt.fromHex(t)}catch(n){const r=n instanceof Error?n.message:String(n);throw new Error(`Failed to parse ${e} PSBT: ${r}`)}}const ne=8;function _(e){return`${e.toString("hex").slice(0,ne)}…`}function F(e){const t=w.Buffer.from(e).reverse();return _(t)}function re(e){const t=M("requested",e.requestedPsbtHex),n=M("returned",e.returnedPsbtHex);if(t.version!==n.version)throw new p(`tx version differs (requested=${t.version}, returned=${n.version})`);if(t.locktime!==n.locktime)throw new p(`tx locktime differs (requested=${t.locktime}, returned=${n.locktime})`);if(t.txInputs.length!==n.txInputs.length)throw new p(`input count differs (requested=${t.txInputs.length}, returned=${n.txInputs.length})`);if(t.txOutputs.length!==n.txOutputs.length)throw new p(`output count differs (requested=${t.txOutputs.length}, returned=${n.txOutputs.length})`);for(let r=0;r<t.txInputs.length;r++){const i=t.txInputs[r],o=n.txInputs[r];if(!i.hash.equals(o.hash))throw new p(`input ${r} prevout txid differs (requested=${F(i.hash)}, returned=${F(o.hash)})`);if(i.index!==o.index)throw new p(`input ${r} prevout vout differs (requested=${i.index}, returned=${o.index})`);if(i.sequence!==o.sequence)throw new p(`input ${r} sequence differs (requested=${i.sequence}, returned=${o.sequence})`)}for(let r=0;r<t.txOutputs.length;r++){const i=t.txOutputs[r],o=n.txOutputs[r];if(!i.script.equals(o.script))throw new p(`output ${r} scriptPubKey differs (requested=${_(i.script)}, returned=${_(o.script)})`);if(i.value!==o.value)throw new p(`output ${r} value differs (requested=${i.value}, returned=${o.value})`)}}exports.ASSERT_PAYOUT_OUTPUT_INDEX=k;exports.DEPOSITOR_PAYOUT_INPUT_COUNT=Q;exports.PEGIN_VAULT_OUTPUT_INDEX=A;exports.PsbtSubstitutionError=p;exports.assertPsbtUnsignedTxMatches=re;exports.buildPayoutPsbt=j;exports.buildPeginTxFromFundedPrePegin=G;exports.buildPrePeginPsbt=Y;exports.createPayoutScript=W;exports.extractPayoutSignature=ee;exports.normalizeAuthAnchorHash=H;
2
- //# sourceMappingURL=assertPsbtUnsignedTxMatches-s9H0Qqkl.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"assertPsbtUnsignedTxMatches-s9H0Qqkl.cjs","sources":["../src/tbv/core/primitives/psbt/assertWasmPeginSizing.ts","../src/tbv/core/primitives/psbt/pegin.ts","../src/tbv/core/primitives/scripts/payout.ts","../src/tbv/core/primitives/psbt/constants.ts","../src/tbv/core/primitives/psbt/payout.ts","../src/tbv/core/primitives/psbt/assertPsbtUnsignedTxMatches.ts"],"sourcesContent":["/**\n * Cross-check the values WASM returns from `createPrePeginTransaction`\n * against independently-known expectations before they feed a signed\n * Bitcoin transaction or the on-chain PegIn registration.\n *\n * CLAUDE.md critical path #1: the Rust/WASM layer computes\n * `htlcValue = peginAmount + depositorClaimValue + minPeginFee` internally\n * and JS receives the outputs with no runtime validation. A doctored or\n * buggy binary that returns a different `peginAmount`, an out-of-formula\n * `htlcValue`, or a wrong `depositorClaimValue` would otherwise be committed\n * verbatim — taxing the depositor or starving the downstream tx graph of fees.\n *\n * @module primitives/psbt/assertWasmPeginSizing\n */\n\nimport {\n computeMinClaimValue,\n type PrePeginResult,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport { MAX_REASONABLE_PEGIN_VBYTES } from \"../../utils/fee/constants\";\nimport type { ParsedOutput } from \"../../utils/transaction/fundPeginTransaction\";\n\nimport type { PrePeginParams } from \"./pegin\";\n\n/**\n * Assert the WASM Pre-PegIn sizing result is internally consistent and\n * matches what the caller requested.\n *\n * The strong checks are pure-JS and fully independent of the WASM binary:\n * the per-HTLC `peginAmount` must equal the requested amount, array lengths\n * must match, and every value must be positive. The implied PegIn fee\n * (`htlcValue - peginAmount - depositorClaimValue`) is bounded by\n * plausibility rather than recomputed exactly, because JS↔Rust vbyte parity\n * is not a cross-stack guarantee (see {@link MAX_REASONABLE_PEGIN_VBYTES}).\n * The `depositorClaimValue` cross-check against `computeMinClaimValue` is a\n * WASM-vs-WASM consistency check (a different entry point), not an\n * independent one.\n *\n * @param result - The result returned by `createPrePeginTransaction`.\n * @param params - The parameters that were passed to build it.\n * @throws If any value is missing, non-positive, mismatched against the\n * request, or outside the protocol formula / plausibility bounds.\n */\nexport async function assertWasmPeginSizing(\n result: PrePeginResult,\n params: PrePeginParams,\n): Promise<void> {\n const expectedCount = params.pegInAmounts.length;\n\n // Count: every parallel array must carry exactly one entry per requested\n // deposit, otherwise the per-HTLC indexing downstream is meaningless.\n if (result.htlcValues.length !== expectedCount) {\n throw new Error(\n `WASM Pre-PegIn returned ${result.htlcValues.length} HTLC value(s), ` +\n `expected ${expectedCount} (one per requested deposit).`,\n );\n }\n if (\n result.peginAmounts.length !== expectedCount ||\n result.htlcScriptPubKeys.length !== expectedCount ||\n result.htlcAddresses.length !== expectedCount\n ) {\n throw new Error(\n `WASM Pre-PegIn returned mismatched array lengths ` +\n `(htlcValues=${result.htlcValues.length}, ` +\n `peginAmounts=${result.peginAmounts.length}, ` +\n `htlcScriptPubKeys=${result.htlcScriptPubKeys.length}, ` +\n `htlcAddresses=${result.htlcAddresses.length}); ` +\n `expected ${expectedCount} each.`,\n );\n }\n\n // depositorClaimValue: positivity + WASM-vs-WASM consistency. Sized by the\n // tx-graph `feeRate` (see PrePeginParams.feeRate), so the standalone\n // `computeMinClaimValue` must reproduce the constructor's internal value.\n if (result.depositorClaimValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn returned non-positive depositorClaimValue ` +\n `${result.depositorClaimValue}; expected > 0.`,\n );\n }\n const expectedClaimValue = await computeMinClaimValue(\n params.numLocalChallengers,\n params.universalChallengerPubkeys.length,\n params.councilQuorum,\n params.councilSize,\n params.feeRate,\n );\n if (result.depositorClaimValue !== expectedClaimValue) {\n throw new Error(\n `WASM Pre-PegIn depositorClaimValue ${result.depositorClaimValue} does ` +\n `not match the independently computed minimum claim value ` +\n `${expectedClaimValue} (numLocalChallengers=${params.numLocalChallengers}, ` +\n `numUniversalChallengers=${params.universalChallengerPubkeys.length}, ` +\n `councilQuorum=${params.councilQuorum}, councilSize=${params.councilSize}, ` +\n `feeRate=${params.feeRate}).`,\n );\n }\n\n const maxImpliedFee = params.minPeginFeeRate * MAX_REASONABLE_PEGIN_VBYTES;\n\n for (let i = 0; i < expectedCount; i++) {\n const requested = params.pegInAmounts[i];\n const peginAmount = result.peginAmounts[i];\n const htlcValue = result.htlcValues[i];\n\n // Amount echo (strongest, fully independent): the recorded pegin amount\n // must equal exactly what the caller requested. A mismatch is the\n // WASM-tax attack — the contract would record a doctored amount while the\n // depositor's wallet funds the original, and the difference is a\n // WASM-controlled tax.\n if (peginAmount !== requested) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] ${peginAmount} does not match the ` +\n `requested amount ${requested}; refusing to build a tx whose ` +\n `recorded amount differs from the depositor's request.`,\n );\n }\n if (peginAmount <= 0n) {\n throw new Error(\n `WASM Pre-PegIn peginAmount[${i}] is non-positive (${peginAmount}); ` +\n `expected > 0.`,\n );\n }\n if (htlcValue <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] is non-positive (${htlcValue}); ` +\n `expected > 0.`,\n );\n }\n\n // Formula: htlcValue = peginAmount + depositorClaimValue + minPeginFee.\n // The implied fee must be strictly positive (the HTLC must reserve a real\n // PegIn fee) and within the plausibility bound.\n const impliedFee = htlcValue - peginAmount - result.depositorClaimValue;\n if (impliedFee <= 0n) {\n throw new Error(\n `WASM Pre-PegIn htlcValue[${i}] ${htlcValue} does not strictly cover ` +\n `peginAmount ${peginAmount} + depositorClaimValue ` +\n `${result.depositorClaimValue} + a PegIn fee (implied fee ` +\n `${impliedFee}).`,\n );\n }\n if (impliedFee > maxImpliedFee) {\n throw new Error(\n `WASM Pre-PegIn implied PegIn fee for HTLC[${i}] (${impliedFee} sat) ` +\n `exceeds the plausibility cap ${maxImpliedFee} sat ` +\n `(minPeginFeeRate=${params.minPeginFeeRate} × ` +\n `${MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${htlcValue} ` +\n `appears grossly inflated.`,\n );\n }\n }\n}\n\n/**\n * Bind the validated metadata to the bytes that actually get funded and\n * signed.\n *\n * `assertWasmPeginSizing` proves the WASM *metadata* (`htlcValues`,\n * `htlcScriptPubKeys`) matches the request and the protocol formula — but the\n * transaction the depositor funds and signs is `result.txHex`. If the encoded\n * tx carried a different HTLC output value or script than the metadata, the\n * depositor would fund a transaction whose real outputs differ from the values\n * that were cross-checked. This closes that final link: the encoded HTLC\n * outputs must equal the validated metadata.\n *\n * The WASM lays out HTLC outputs first (vouts `0..N-1`), then the optional\n * auth-anchor OP_RETURN, then the CPFP anchor — so we only compare the first\n * `htlcValues.length` outputs.\n *\n * @param outputs - Outputs parsed from the unfunded Pre-PegIn tx hex.\n * @param htlcValues - The (already value-validated) per-HTLC values.\n * @param htlcScriptPubKeys - The per-HTLC scriptPubKeys (hex).\n * @throws If the encoded outputs are too few, or any HTLC output's value or\n * scriptPubKey disagrees with the validated metadata.\n */\nexport function assertEncodedHtlcOutputsMatch(\n outputs: readonly ParsedOutput[],\n htlcValues: readonly bigint[],\n htlcScriptPubKeys: readonly string[],\n): void {\n if (outputs.length < htlcValues.length) {\n throw new Error(\n `Encoded Pre-PegIn tx has ${outputs.length} output(s), fewer than the ` +\n `${htlcValues.length} HTLC output(s) the cross-check validated.`,\n );\n }\n\n for (let i = 0; i < htlcValues.length; i++) {\n const encodedValue = BigInt(outputs[i].value);\n if (encodedValue !== htlcValues[i]) {\n throw new Error(\n `Encoded Pre-PegIn HTLC output[${i}] value ${encodedValue} does not ` +\n `match the cross-checked htlcValue ${htlcValues[i]}; the funded/signed ` +\n `tx would not pay the validated amount.`,\n );\n }\n\n const encodedScript = outputs[i].script.toString(\"hex\").toLowerCase();\n const expectedScript = htlcScriptPubKeys[i].toLowerCase();\n if (encodedScript !== expectedScript) {\n throw new Error(\n `Encoded Pre-PegIn HTLC output[${i}] scriptPubKey ${encodedScript} does ` +\n `not match the cross-checked htlcScriptPubKey ${expectedScript}.`,\n );\n }\n }\n}\n","/**\n * Pre-PegIn PSBT Builder Primitive\n *\n * This module provides pure functions for building unfunded Pre-PegIn transactions\n * and deriving PegIn transactions from them, using the WASM implementation from\n * @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * Pre-PegIn Flow:\n * 1. buildPrePeginPsbt() — creates unfunded Pre-PegIn tx (HTLC output)\n * 2. [caller funds Pre-PegIn tx and computes txid]\n * 3. buildPeginTxFromFundedPrePegin() — derives PegIn tx spending the HTLC\n * 4. buildPeginInputPsbt() — PSBT for depositor to sign PegIn HTLC leaf 0 input\n *\n * @module primitives/psbt/pegin\n */\n\nimport {\n buildPeginTxFromPrePegin,\n createPrePeginTransaction,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport { parseUnfundedWasmTransaction } from \"../../utils/transaction/fundPeginTransaction\";\n\nimport {\n assertEncodedHtlcOutputsMatch,\n assertWasmPeginSizing,\n} from \"./assertWasmPeginSizing\";\n\n/**\n * Parameters for building an unfunded Pre-PegIn PSBT\n */\nexport interface PrePeginParams {\n /** Depositor's BTC public key (x-only, 64-char hex without 0x prefix) */\n depositorPubkey: string;\n /** Vault provider's BTC public key (x-only, 64-char hex) */\n vaultProviderPubkey: string;\n /** Array of vault keeper BTC public keys (x-only, 64-char hex) */\n vaultKeeperPubkeys: string[];\n /** Array of universal challenger BTC public keys (x-only, 64-char hex) */\n universalChallengerPubkeys: string[];\n /** SHA256 hash commitment(s) (64 hex chars = 32 bytes each) */\n hashlocks: readonly string[];\n /** CSV timelock in blocks for the HTLC refund path */\n timelockRefund: number;\n /** Amounts to peg in (satoshis), one per deposit */\n pegInAmounts: readonly bigint[];\n /** TX-graph fee rate in sat/vB from contract offchain params; sizes the depositor claim value */\n feeRate: bigint;\n /** Minimum PegIn fee rate in sat/vB from contract offchain params; sizes the PegIn tx fee */\n minPeginFeeRate: bigint;\n /** Number of local challengers (from contract params) */\n numLocalChallengers: number;\n /** M in M-of-N council multisig (from contract params) */\n councilQuorum: number;\n /** N in M-of-N council multisig (from contract params) */\n councilSize: number;\n /** Bitcoin network */\n network: Network;\n /**\n * Optional 32-byte `SHA256(auth_anchor)` commitment (64-char hex, no\n * `0x` prefix). If provided, the Pre-PegIn tx will include an\n * `OP_RETURN <PUSH32 authAnchorHash>` output at vout =\n * `hashlocks.length`, binding the depositor's bearer-token\n * `auth_anchor` preimage to this Pre-PegIn.\n */\n authAnchorHash?: string;\n}\n\n/**\n * Byte length of an `auth_anchor_hash` commitment when encoded as a\n * lowercase hex string (32 bytes → 64 hex chars).\n */\nconst AUTH_ANCHOR_HASH_HEX_LEN = 64;\n\nconst HEX_PATTERN = /^[0-9a-fA-F]+$/;\n\n/**\n * Result of building an unfunded Pre-PegIn transaction\n */\nexport interface PrePeginPsbtResult {\n /**\n * Unfunded transaction hex (no inputs, HTLC outputs + optional\n * auth-anchor OP_RETURN + CPFP anchor).\n *\n * The caller is responsible for:\n * - Selecting UTXOs covering totalOutputValue + network fees\n * - Funding the transaction (add inputs and change output)\n * - Calling buildPeginTxFromFundedPrePegin() with the funded tx hex\n */\n psbtHex: string;\n /** Sum of all unfunded outputs — use this for UTXO selection */\n totalOutputValue: bigint;\n /** HTLC output values in satoshis, one per deposit (each includes peginAmount + depositorClaimValue + minPeginFee) */\n htlcValues: readonly bigint[];\n /** HTLC output scriptPubKeys (hex encoded), one per deposit */\n htlcScriptPubKeys: readonly string[];\n /** HTLC Taproot addresses, one per deposit */\n htlcAddresses: readonly string[];\n /** Pegin amounts in satoshis, one per deposit */\n peginAmounts: readonly bigint[];\n /** Depositor claim value computed by WASM from contract parameters */\n depositorClaimValue: bigint;\n /**\n * Vout index of the auth-anchor `OP_RETURN` output if one was\n * included (i.e. `authAnchorHash` was provided), or `null` if not.\n * Always equals `htlcValues.length` when present.\n */\n authAnchorVout: number | null;\n}\n\n/**\n * Parameters for building the PegIn transaction from a funded Pre-PegIn tx\n */\nexport interface BuildPeginTxParams {\n /** Same PrePeginParams used to create the Pre-PegIn transaction */\n prePeginParams: PrePeginParams;\n /** CSV timelock in blocks for the PegIn vault output */\n timelockPegin: number;\n /** Hex-encoded funded Pre-PegIn transaction */\n fundedPrePeginTxHex: string;\n /** Index of the HTLC output to spend */\n htlcVout: number;\n}\n\n/**\n * Result of building the PegIn transaction\n */\nexport interface PeginTxResult {\n /** PegIn transaction hex (1 input spending HTLC, 1 vault output) */\n txHex: string;\n /** PegIn transaction ID */\n txid: string;\n /** Vault output scriptPubKey (hex encoded) */\n vaultScriptPubKey: string;\n /** Vault output value in satoshis */\n vaultValue: bigint;\n}\n\n/**\n * Build unfunded Pre-PegIn transaction using WASM.\n *\n * Creates a Bitcoin transaction template with no inputs, an HTLC output, and a\n * CPFP anchor output. The HTLC value is computed internally from the contract\n * parameters — the caller does not need to compute depositorClaimValue separately.\n *\n * @param params - Pre-PegIn parameters\n * @returns Unfunded Pre-PegIn transaction details with HTLC output information\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPrePeginPsbt(\n params: PrePeginParams,\n): Promise<PrePeginPsbtResult> {\n const authAnchorHash = normalizeAuthAnchorHash(params.authAnchorHash);\n\n const result = await createPrePeginTransaction({\n depositorPubkey: params.depositorPubkey,\n vaultProviderPubkey: params.vaultProviderPubkey,\n vaultKeeperPubkeys: params.vaultKeeperPubkeys,\n universalChallengerPubkeys: params.universalChallengerPubkeys,\n hashlocks: [...params.hashlocks],\n timelockRefund: params.timelockRefund,\n pegInAmounts: [...params.pegInAmounts],\n feeRate: params.feeRate,\n minPeginFeeRate: params.minPeginFeeRate,\n numLocalChallengers: params.numLocalChallengers,\n councilQuorum: params.councilQuorum,\n councilSize: params.councilSize,\n network: params.network,\n authAnchorHash,\n });\n\n // CLAUDE.md critical path #1: the WASM outputs reach JS with no runtime\n // validation. Cross-check every value-bearing field against the request\n // and the protocol formula before it can feed a signed tx or the on-chain\n // PegIn registration. Both the sizing and commit passes route through here.\n await assertWasmPeginSizing(result, params);\n\n // Parse the unfunded tx to sum all output values\n // (HTLCs + optional OP_RETURN + CPFP anchor). This is the amount\n // UTXOs must cover before adding network fees.\n const parsed = parseUnfundedWasmTransaction(result.txHex);\n\n // Bind the validated metadata to the bytes that get funded and signed:\n // the encoded HTLC outputs must carry exactly the values/scripts the\n // cross-check above validated. Otherwise a tx whose real outputs differ\n // from the checked metadata could still be funded and signed.\n assertEncodedHtlcOutputsMatch(\n parsed.outputs,\n result.htlcValues,\n result.htlcScriptPubKeys,\n );\n\n const totalOutputValue = parsed.outputs.reduce(\n (sum, o) => sum + BigInt(o.value),\n 0n,\n );\n\n // The WASM places the OP_RETURN commitment immediately after the\n // HTLC outputs when authAnchorHash is provided.\n const authAnchorVout =\n authAnchorHash !== undefined ? result.htlcValues.length : null;\n\n return {\n psbtHex: result.txHex,\n totalOutputValue,\n htlcValues: result.htlcValues,\n htlcScriptPubKeys: result.htlcScriptPubKeys,\n htlcAddresses: result.htlcAddresses,\n peginAmounts: result.peginAmounts,\n depositorClaimValue: result.depositorClaimValue,\n authAnchorVout,\n };\n}\n\n/**\n * Validate and normalize an `authAnchorHash` hex string before passing\n * it to the WASM boundary. WASM expects exactly 64 lowercase hex chars.\n */\nexport function normalizeAuthAnchorHash(\n value: string | undefined,\n): string | undefined {\n if (value === undefined) return undefined;\n const cleaned =\n value.startsWith(\"0x\") || value.startsWith(\"0X\") ? value.slice(2) : value;\n if (\n cleaned.length !== AUTH_ANCHOR_HASH_HEX_LEN ||\n !HEX_PATTERN.test(cleaned)\n ) {\n throw new Error(\n `authAnchorHash must be 32-byte hex (${AUTH_ANCHOR_HASH_HEX_LEN} chars, no 0x prefix); got length ${cleaned.length}`,\n );\n }\n return cleaned.toLowerCase();\n}\n\n/**\n * Build the PegIn transaction from a funded Pre-PegIn transaction.\n *\n * The PegIn transaction spends the Pre-PegIn HTLC output at htlcVout via the\n * hashlock + all-party script (leaf 0).\n *\n * @param params - Build parameters including Pre-PegIn params and funded tx hex\n * @returns PegIn transaction details\n * @throws If WASM initialization fails or parameters are invalid\n */\nexport async function buildPeginTxFromFundedPrePegin(\n params: BuildPeginTxParams,\n): Promise<PeginTxResult> {\n // WASM reconstructs the Pre-PegIn template from these params to\n // decode the funded tx. Must pass `authAnchorHash` (normalized\n // identically to buildPrePeginPsbt) so the reconstruction matches\n // the original outputs, including the OP_RETURN at vout =\n // hashlocks.length.\n const result = await buildPeginTxFromPrePegin(\n {\n depositorPubkey: params.prePeginParams.depositorPubkey,\n vaultProviderPubkey: params.prePeginParams.vaultProviderPubkey,\n vaultKeeperPubkeys: params.prePeginParams.vaultKeeperPubkeys,\n universalChallengerPubkeys:\n params.prePeginParams.universalChallengerPubkeys,\n hashlocks: [...params.prePeginParams.hashlocks],\n timelockRefund: params.prePeginParams.timelockRefund,\n pegInAmounts: [...params.prePeginParams.pegInAmounts],\n feeRate: params.prePeginParams.feeRate,\n minPeginFeeRate: params.prePeginParams.minPeginFeeRate,\n numLocalChallengers: params.prePeginParams.numLocalChallengers,\n councilQuorum: params.prePeginParams.councilQuorum,\n councilSize: params.prePeginParams.councilSize,\n network: params.prePeginParams.network,\n authAnchorHash: normalizeAuthAnchorHash(\n params.prePeginParams.authAnchorHash,\n ),\n },\n params.timelockPegin,\n params.fundedPrePeginTxHex,\n params.htlcVout,\n );\n\n return {\n txHex: result.txHex,\n txid: result.txid,\n vaultScriptPubKey: result.vaultScriptPubKey,\n vaultValue: result.vaultValue,\n };\n}\n","/**\n * Payout Script Generator Primitive\n *\n * This module provides pure functions for generating payout scripts and taproot information\n * by wrapping the WASM implementation from @babylonlabs-io/babylon-tbv-rust-wasm.\n *\n * The payout script is used for signing payout transactions in the vault system.\n * It defines the spending conditions for the vault output, enabling the depositor\n * to authorize payouts during the peg-in flow (Step 3).\n *\n * @remarks\n * This is a low-level primitive. For most use cases, prefer using {@link buildPayoutPsbt}\n * which handles script creation internally. For high-level wallet orchestration, use\n * PayoutManager from the managers module.\n *\n * @see {@link buildPayoutPsbt} - Higher-level function that uses this internally\n *\n * @module primitives/scripts/payout\n */\n\nimport {\n createPayoutConnector,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\n/**\n * Parameters for creating a payout script.\n *\n * These parameters define the participants in a vault and are used to generate\n * the taproot script that controls how funds can be spent from the vault.\n */\nexport interface PayoutScriptParams {\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * This is the user depositing BTC into the vault. The depositor must sign\n * payout transactions to authorize fund distribution.\n */\n depositor: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex without 0x prefix).\n *\n * The service provider managing vault operations. Also referred to as\n * \"claimer\" in the WASM layer.\n */\n vaultProvider: string;\n\n /**\n * Array of vault keeper BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * Vault keepers participate in vault operations and script spending conditions.\n */\n vaultKeepers: string[];\n\n /**\n * Array of universal challenger BTC public keys (x-only, 64-char hex without 0x prefix).\n *\n * These parties can challenge the vault under certain conditions.\n */\n universalChallengers: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Bitcoin network for script generation.\n *\n * Must match the network used for all other vault operations to ensure\n * address encoding compatibility.\n */\n network: Network;\n}\n\n/**\n * Result of creating a payout script.\n *\n * Contains all the taproot-related data needed for constructing and signing\n * payout transactions from the vault.\n */\nexport interface PayoutScriptResult {\n /**\n * The payout script hex used in taproot script path spending.\n *\n * This is the raw script bytes that define the spending conditions,\n * encoded as a hexadecimal string. Used when constructing the\n * tapLeafScript for PSBT signing.\n */\n payoutScript: string;\n\n /**\n * The taproot script hash (leaf hash) for the payout script.\n *\n * This is the tagged hash of the script used in taproot tree construction.\n * Required for computing the control block during script path spending.\n */\n taprootScriptHash: string;\n\n /**\n * The full scriptPubKey for the vault output address.\n *\n * This is the complete output script (OP_1 <32-byte-key>) that should be\n * used when creating the vault output in a peg-in transaction.\n */\n scriptPubKey: string;\n\n /**\n * The vault Bitcoin address derived from the script.\n *\n * A human-readable bech32m address (bc1p... for mainnet, tb1p... for testnet/signet)\n * that can be used to receive funds into the vault.\n */\n address: string;\n\n /**\n * Serialized control block for Taproot script path spend (hex encoded).\n *\n * Computed by the Rust WASM PeginPayoutConnector. Used directly in\n * tapLeafScript when building payout PSBTs.\n */\n payoutControlBlock: string;\n}\n\n/**\n * Create payout script and taproot information using WASM.\n *\n * This is a pure function that wraps the Rust WASM implementation.\n * The payout connector generates the necessary taproot scripts and information\n * required for signing payout transactions.\n *\n * @remarks\n * The generated script encodes spending conditions that require signatures from\n * the depositor and vault provider (or liquidators in challenge scenarios).\n * This script is used internally by {@link buildPayoutPsbt}.\n *\n * @param params - Payout script parameters defining vault participants and network\n * @returns Payout script and taproot information for PSBT construction\n *\n * @see {@link buildPayoutPsbt} - Use this for building complete payout PSBTs\n */\nexport async function createPayoutScript(\n params: PayoutScriptParams,\n): Promise<PayoutScriptResult> {\n // Call the WASM wrapper with the correct parameter structure\n const connector = await createPayoutConnector(\n {\n depositor: params.depositor,\n vaultProvider: params.vaultProvider,\n vaultKeepers: params.vaultKeepers,\n universalChallengers: params.universalChallengers,\n timelockPegin: params.timelockPegin,\n },\n params.network,\n );\n\n return {\n payoutScript: connector.payoutScript,\n taprootScriptHash: connector.taprootScriptHash,\n scriptPubKey: connector.scriptPubKey,\n address: connector.address,\n payoutControlBlock: connector.payoutControlBlock,\n };\n}\n","/**\n * Protocol invariants for depositor graph transactions.\n *\n * These indices and counts encode the on-chain vault protocol layout\n * (which output of PegIn/Assert each child transaction spends, and how\n * many inputs each transaction has). Consumed by the PSBT builders and\n * the depositor graph signing service; a drift between copies of these\n * values would silently change validation behaviour.\n *\n * @module primitives/psbt/constants\n * @see btc-vault crates/vault/docs/btc-transactions-spec.md\n */\n\n/**\n * Depositor Payout transaction input count.\n * Input 0: PegIn:0 (signed). Input 1: Assert:0 (in sighash, not signed).\n */\nexport const DEPOSITOR_PAYOUT_INPUT_COUNT = 2;\n\n/** PegIn vault output index spent by the depositor's Payout input 0. */\nexport const PEGIN_VAULT_OUTPUT_INDEX = 0;\n\n/** Assert output index spent by the depositor's Payout input 1 (NOT signed). */\nexport const ASSERT_PAYOUT_OUTPUT_INDEX = 0;\n\n/**\n * Dust amount (sats) for the payout CPFP anchor output. Matches `DUST_AMOUNT`\n * in `btc-vault crates/vault/src/lib.rs`.\n */\nexport const PAYOUT_ANCHOR_DUST_SATS = 546;\n\n/** VP-claimer payout output count: [depositor payout, VP commission, CPFP anchor]. */\nexport const VP_CLAIMER_PAYOUT_OUTPUT_COUNT = 3;\n\n/** Depositor/VK-claimer payout output count: [claimer payout, CPFP anchor]. */\nexport const NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT = 2;\n\n/**\n * Exclusive upper bound on VP commission (bps), and the bps denominator for\n * `floor(peginValue * bps / 10_000)`. Matches `BTCVaultRegistry._validateCommission`\n * (`commissionBps >= 10000` reverts). The minimum is version-locked\n * (`minVpCommissionBps`) and enforced upstream, not here.\n */\nexport const MAX_VP_COMMISSION_BPS_EXCLUSIVE = 10_000;\n","/**\n * Payout PSBT Builder Primitives\n *\n * This module provides pure functions for building unsigned payout PSBTs and extracting\n * Schnorr signatures from signed PSBTs. It uses WASM-generated scripts from the payout\n * connector and bitcoinjs-lib for PSBT construction.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @module primitives/psbt/payout\n */\n\nimport {\n type Network,\n tapInternalPubkey,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Buffer } from \"buffer\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\nimport { createPayoutScript } from \"../scripts/payout\";\nimport {\n TAPSCRIPT_LEAF_VERSION,\n deriveBip86ScriptPubKeyHex,\n hexToUint8Array,\n isValidHex,\n stripHexPrefix,\n uint8ArrayToHex,\n} from \"../utils/bitcoin\";\nimport {\n ASSERT_PAYOUT_OUTPUT_INDEX,\n MAX_VP_COMMISSION_BPS_EXCLUSIVE,\n NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n PAYOUT_ANCHOR_DUST_SATS,\n PEGIN_VAULT_OUTPUT_INDEX,\n VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n} from \"./constants\";\n\n/**\n * Number of items in a Taproot script-path spend witness stack for a\n * single-signature script: [signature, script, controlBlock].\n *\n * The current payout script requires exactly one depositor signature. If the\n * protocol evolves to require multiple signatures in the payout script, this\n * invariant and the finalized-PSBT extraction path must be revisited because\n * the first witness item would no longer necessarily be the depositor's.\n */\nconst TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE = 3;\n\n/**\n * Coarse cap on a payout tx's implicit fee (inputs − outputs), as a fraction\n * of input value — blocks a VP deflating outputs and burning the remainder\n * as miner fee. A backstop only; the per-role structural checks in\n * {@link assertPayoutOutputLayout} are the primary value-diversion guard.\n */\nconst MAX_PAYOUT_FEE_FRACTION_NUMERATOR = 10;\nconst MAX_PAYOUT_FEE_FRACTION_DENOMINATOR = 100;\n\n/**\n * Parameters for building an unsigned Payout PSBT\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface PayoutParams {\n /**\n * Payout transaction hex (unsigned)\n * This is the transaction that needs to be signed by the depositor\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex\n * Payout input 1 references Assert output 0\n */\n assertTxHex: string;\n\n /**\n * Peg-in transaction hex\n * This transaction created the vault output that we're spending\n */\n peginTxHex: string;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex without 0x prefix)\n */\n depositorBtcPubkey: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex)\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex)\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex)\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Bitcoin network\n */\n network: Network;\n\n /**\n * Claimer's x-only BTC public key (64-char hex, no prefix). Drives role\n * inference (VP / depositor-as-claimer / VK-claimer) inside `buildPayoutPsbt`.\n */\n claimerBtcPubkey: string;\n\n /**\n * On-chain registered depositor payout scriptPubKey (hex, 0x optional).\n * Expected outs[0].script for VP- and depositor-claimer roles; unused for\n * VK-claimer (its outs[0].script is derived from `claimerBtcPubkey`).\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * VP commission in basis points (`BTCVaultRegistry.vaultProviderCommissionBps`).\n * Caps the VP-claimer outs[1].value. The protocol minimum is enforced\n * upstream; here only `0 <= bps < 10_000` is checked, for safe cap math.\n */\n commissionBps: number;\n}\n\n/**\n * Result of building an unsigned payout PSBT\n */\nexport interface PayoutPsbtResult {\n /**\n * Unsigned PSBT hex ready for signing\n */\n psbtHex: string;\n}\n\n/**\n * Build unsigned Payout PSBT for depositor to sign.\n *\n * Payout is used in the **challenge path** when the claimer proves validity:\n * 1. Vault provider submits Claim transaction\n * 2. Challenge is raised during challenge period\n * 3. Claimer submits Assert transaction to prove validity\n * 4. Payout can be executed (references Assert tx)\n *\n * Payout transactions have the following structure:\n * - Input 0: from PeginTx output0 (signed by depositor)\n * - Input 1: from Assert output0 (NOT signed by depositor)\n *\n * @param params - Payout parameters\n * @returns Unsigned PSBT ready for depositor to sign\n *\n * @throws If payout transaction does not have exactly 2 inputs\n * @throws If input 0 does not spend PegIn:0 (vault UTXO)\n * @throws If input 1 does not spend Assert:0 (proof output)\n * @throws If previous output is not found for either input\n * @throws If sum of output values exceeds sum of input values (invalid tx)\n * @throws If implicit fee (inputs − outputs) exceeds the configured fraction\n * of total input value — see {@link MAX_PAYOUT_FEE_FRACTION_NUMERATOR}\n * @throws If `claimerBtcPubkey` is not VP, depositor, or a registered VK\n * @throws If payout output count, outs[0] script, outs[last] anchor value, or\n * (VP-claimer) outs[1] commission cap do not match the protocol layout\n * @throws If `commissionBps` is not a non-negative integer below 10_000\n */\nexport async function buildPayoutPsbt(\n params: PayoutParams,\n): Promise<PayoutPsbtResult> {\n // Normalize hex inputs (strip 0x prefix if present)\n const payoutTxHex = stripHexPrefix(params.payoutTxHex);\n const peginTxHex = stripHexPrefix(params.peginTxHex);\n const assertTxHex = stripHexPrefix(params.assertTxHex);\n\n // Get payout script from WASM\n const payoutConnector = await createPayoutScript({\n depositor: params.depositorBtcPubkey,\n vaultProvider: params.vaultProviderBtcPubkey,\n vaultKeepers: params.vaultKeeperBtcPubkeys,\n universalChallengers: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n network: params.network,\n });\n\n const payoutScriptBytes = hexToUint8Array(payoutConnector.payoutScript);\n const controlBlock = hexToUint8Array(payoutConnector.payoutControlBlock);\n\n // Parse transactions\n const payoutTx = Transaction.fromHex(payoutTxHex);\n const peginTx = Transaction.fromHex(peginTxHex);\n const assertTx = Transaction.fromHex(assertTxHex);\n\n // Create PSBT\n const psbt = new Psbt();\n psbt.setVersion(payoutTx.version);\n psbt.setLocktime(payoutTx.locktime);\n\n // PayoutTx has exactly 2 inputs:\n // - Input 0: from PeginTx output0 (signed by depositor using taproot script path)\n // - Input 1: from Assert output0 (signed by claimer/challengers, not depositor)\n //\n // IMPORTANT: For Taproot SIGHASH_DEFAULT (0x00), the sighash commits to ALL inputs'\n // prevouts, not just the one being signed. Therefore, we must include BOTH inputs\n // in the PSBT so the wallet computes the correct sighash that the VP expects.\n\n // Verify payout transaction has expected structure\n if (payoutTx.ins.length !== 2) {\n throw new Error(\n `Payout transaction must have exactly 2 inputs, got ${payoutTx.ins.length}`,\n );\n }\n\n const input0 = payoutTx.ins[0];\n const input1 = payoutTx.ins[1];\n\n // Verify input 0 spends PegIn:0 (the vault UTXO).\n // Both txid AND vout must match the protocol contract — the vout is the\n // input-side anchor that prevents a malicious VP from binding the\n // depositor's signature to a different output of the same parent.\n const input0Txid = uint8ArrayToHex(\n new Uint8Array(input0.hash).slice().reverse(),\n );\n const peginTxid = peginTx.getId();\n\n if (input0Txid !== peginTxid || input0.index !== PEGIN_VAULT_OUTPUT_INDEX) {\n throw new Error(\n `Input 0 must spend PegIn:${PEGIN_VAULT_OUTPUT_INDEX}. ` +\n `Expected ${peginTxid}:${PEGIN_VAULT_OUTPUT_INDEX}, got ${input0Txid}:${input0.index}`,\n );\n }\n\n // Verify input 1 spends Assert:0 (the proof output).\n const input1Txid = uint8ArrayToHex(\n new Uint8Array(input1.hash).slice().reverse(),\n );\n const assertTxid = assertTx.getId();\n\n if (input1Txid !== assertTxid || input1.index !== ASSERT_PAYOUT_OUTPUT_INDEX) {\n throw new Error(\n `Input 1 must spend Assert:${ASSERT_PAYOUT_OUTPUT_INDEX}. ` +\n `Expected ${assertTxid}:${ASSERT_PAYOUT_OUTPUT_INDEX}, got ${input1Txid}:${input1.index}`,\n );\n }\n\n const peginPrevOut = peginTx.outs[input0.index];\n if (!peginPrevOut) {\n throw new Error(\n `Previous output not found for input 0 (txid: ${input0Txid}, index: ${input0.index})`,\n );\n }\n\n const input1PrevOut = assertTx.outs[input1.index];\n if (!input1PrevOut) {\n throw new Error(\n `Previous output not found for input 1 (txid: ${input1Txid}, index: ${input1.index})`,\n );\n }\n\n // Per-role output validation — blocks an extra attacker output or value\n // routed into a non-payout slot.\n assertPayoutOutputLayout({\n payoutTx,\n peginValueSats: peginPrevOut.value,\n claimerBtcPubkey: params.claimerBtcPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n depositorBtcPubkey: params.depositorBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n });\n\n // Bound the implicit fee — blocks a VP deflating output values and burning\n // the difference as miner fee.\n const inputValueSats = peginPrevOut.value + input1PrevOut.value;\n let outputValueSats = 0;\n for (const out of payoutTx.outs) outputValueSats += out.value;\n if (outputValueSats > inputValueSats) {\n throw new Error(\n `Payout outputs (${outputValueSats} sats) exceed inputs ` +\n `(${inputValueSats} sats); invalid transaction.`,\n );\n }\n const implicitFeeSats = inputValueSats - outputValueSats;\n const maxFeeSats = Math.floor(\n (inputValueSats * MAX_PAYOUT_FEE_FRACTION_NUMERATOR) /\n MAX_PAYOUT_FEE_FRACTION_DENOMINATOR,\n );\n if (implicitFeeSats > maxFeeSats) {\n throw new Error(\n `Payout implicit fee ${implicitFeeSats} sats exceeds the safety cap ` +\n `of ${maxFeeSats} sats ` +\n `(${MAX_PAYOUT_FEE_FRACTION_NUMERATOR}/${MAX_PAYOUT_FEE_FRACTION_DENOMINATOR} ` +\n `of inputs=${inputValueSats}); refusing to sign payout.`,\n );\n }\n\n // Input 0: Depositor signs using Taproot script path spend\n // This input includes tapLeafScript for signing\n psbt.addInput({\n hash: input0.hash,\n index: input0.index,\n sequence: input0.sequence,\n witnessUtxo: {\n script: peginPrevOut.script,\n value: peginPrevOut.value,\n },\n tapLeafScript: [\n {\n leafVersion: TAPSCRIPT_LEAF_VERSION,\n script: Buffer.from(payoutScriptBytes),\n controlBlock: Buffer.from(controlBlock),\n },\n ],\n tapInternalKey: Buffer.from(tapInternalPubkey),\n // sighashType omitted - defaults to SIGHASH_DEFAULT (0x00) for Taproot\n });\n\n // Input 1: From Assert transaction (NOT signed by depositor)\n // We include this with witnessUtxo so the sighash is computed correctly,\n // but we do NOT include tapLeafScript since the depositor doesn't sign it.\n psbt.addInput({\n hash: input1.hash,\n index: input1.index,\n sequence: input1.sequence,\n witnessUtxo: {\n script: input1PrevOut.script,\n value: input1PrevOut.value,\n },\n // No tapLeafScript - depositor doesn't sign this input\n });\n\n // Add outputs\n for (const output of payoutTx.outs) {\n psbt.addOutput({\n script: output.script,\n value: output.value,\n });\n }\n\n return {\n psbtHex: psbt.toHex(),\n };\n}\n\n/**\n * Validate a payout transaction's output structure for the claimer's role,\n * keyed on `claimerBtcPubkey`. Pins per role: `outs.length`, `outs[0].script`,\n * `outs[last].value` (anchor dust), and (VP-claimer) `outs[1].value` capped at\n * `floor(peginValue × commissionBps / 10_000)`. Canonical layouts: VP-claimer\n * = [payout, commission, anchor]; depositor/VK-claimer = [payout, anchor].\n *\n * `outs[1].script` and `outs[last].script` are intentionally not pinned: the\n * value pins above bound depositor exposure regardless of where those outputs\n * are sent, so the value pins — not script pins — are load-bearing.\n *\n * @internal Helper invoked by {@link buildPayoutPsbt}.\n */\nfunction assertPayoutOutputLayout(args: {\n payoutTx: Transaction;\n peginValueSats: number;\n claimerBtcPubkey: string;\n vaultProviderBtcPubkey: string;\n depositorBtcPubkey: string;\n vaultKeeperBtcPubkeys: string[];\n registeredPayoutScriptPubKey: string;\n commissionBps: number;\n}): void {\n const {\n payoutTx,\n peginValueSats,\n claimerBtcPubkey,\n vaultProviderBtcPubkey,\n depositorBtcPubkey,\n vaultKeeperBtcPubkeys,\n registeredPayoutScriptPubKey,\n commissionBps,\n } = args;\n\n if (!isValidHex(registeredPayoutScriptPubKey)) {\n throw new Error(\"Invalid registeredPayoutScriptPubKey: not valid hex\");\n }\n\n const claimer = stripHexPrefix(claimerBtcPubkey).toLowerCase();\n const vp = stripHexPrefix(vaultProviderBtcPubkey).toLowerCase();\n const dep = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n const keepers = vaultKeeperBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n\n type Role = \"vp-claimer\" | \"depositor-as-claimer\" | \"vk-claimer\";\n let role: Role;\n let expectedOutCount: number;\n let expectedOut0ScriptHex: string;\n\n if (claimer === vp) {\n role = \"vp-claimer\";\n expectedOutCount = VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n expectedOut0ScriptHex = stripHexPrefix(registeredPayoutScriptPubKey);\n } else if (claimer === dep) {\n role = \"depositor-as-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n expectedOut0ScriptHex = stripHexPrefix(registeredPayoutScriptPubKey);\n } else if (keepers.includes(claimer)) {\n role = \"vk-claimer\";\n expectedOutCount = NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;\n expectedOut0ScriptHex = stripHexPrefix(deriveBip86ScriptPubKeyHex(claimer));\n } else {\n throw new Error(\n `Unknown claimer pubkey ${claimer}: not VP, depositor, or a registered vault keeper`,\n );\n }\n\n if (payoutTx.outs.length !== expectedOutCount) {\n throw new Error(\n `Payout transaction has ${payoutTx.outs.length} output(s), ` +\n `expected exactly ${expectedOutCount} for role ${role}.`,\n );\n }\n\n const expectedOut0Script = Buffer.from(expectedOut0ScriptHex, \"hex\");\n if (!payoutTx.outs[0].script.equals(expectedOut0Script)) {\n throw new Error(\n `Payout transaction output 0 does not pay the expected scriptPubKey for role ${role}`,\n );\n }\n\n const anchorIdx = expectedOutCount - 1;\n if (payoutTx.outs[anchorIdx].value !== PAYOUT_ANCHOR_DUST_SATS) {\n throw new Error(\n `Payout CPFP anchor (out ${anchorIdx}) value ${payoutTx.outs[anchorIdx].value} sats ` +\n `must equal ${PAYOUT_ANCHOR_DUST_SATS} sats`,\n );\n }\n\n if (role === \"vp-claimer\") {\n // Structural guard only — a non-negative integer below the bps\n // denominator, so the cap math `floor(peginValue * bps / 10_000)` is\n // meaningful. The protocol minimum is enforced at the trust boundary\n // (`prepareSigningContext`); a too-low value here is fail-safe.\n if (\n !Number.isInteger(commissionBps) ||\n commissionBps < 0 ||\n commissionBps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n ) {\n throw new Error(\n `commissionBps must be an integer in ` +\n `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${commissionBps}`,\n );\n }\n const maxCommissionSats = Math.floor(\n (peginValueSats * commissionBps) / MAX_VP_COMMISSION_BPS_EXCLUSIVE,\n );\n if (payoutTx.outs[1].value > maxCommissionSats) {\n throw new Error(\n `Payout VP commission (out 1) value ${payoutTx.outs[1].value} sats ` +\n `exceeds cap ${maxCommissionSats} sats ` +\n `(${commissionBps} bps of peginValue=${peginValueSats})`,\n );\n }\n }\n}\n\n/**\n * Extract Schnorr signature from signed payout PSBT.\n *\n * This function supports two cases:\n * 1. Non-finalized PSBT: Extracts from tapScriptSig field\n * 2. Finalized PSBT: Extracts from witness data\n *\n * The signature is returned as a 64-byte hex string (128 hex characters).\n * Payout signatures must use implicit Taproot SIGHASH_DEFAULT, which is\n * encoded by omitting the sighash byte.\n *\n * @param signedPsbtHex - Signed PSBT hex\n * @param depositorPubkey - Depositor's public key (x-only, 64-char hex)\n * @param inputIndex - Input index to extract signature from (default: 0)\n * @returns 64-byte Schnorr signature (128 hex characters, no sighash flag)\n *\n * @throws If no signature is found in the PSBT\n * @throws If the signature has an unexpected length\n */\nexport function extractPayoutSignature(\n signedPsbtHex: string,\n depositorPubkey: string,\n inputIndex = 0,\n): string {\n const signedPsbt = Psbt.fromHex(signedPsbtHex);\n\n if (inputIndex >= signedPsbt.data.inputs.length) {\n throw new Error(\n `Input index ${inputIndex} out of range (${signedPsbt.data.inputs.length} inputs)`,\n );\n }\n\n const input = signedPsbt.data.inputs[inputIndex];\n\n // Case 1: Non-finalized PSBT — extract from tapScriptSig\n if (input.tapScriptSig && input.tapScriptSig.length > 0) {\n const depositorPubkeyBytes = hexToUint8Array(depositorPubkey);\n\n for (const sigEntry of input.tapScriptSig) {\n if (sigEntry.pubkey.equals(Buffer.from(depositorPubkeyBytes))) {\n return extractSchnorrSig(sigEntry.signature, inputIndex);\n }\n }\n\n throw new Error(\n `No signature found for depositor pubkey: ${depositorPubkey} at input ${inputIndex}`,\n );\n }\n\n // Case 2: Finalized PSBT — extract from finalScriptWitness\n // Taproot single-signature script-path witness: [signature, script, controlBlock].\n // Enforce the exact stack size so that if a wallet produces an unexpected\n // finalization (e.g. a multi-signature stack, an annex, or malformed data),\n // we fail loudly instead of silently returning witnessStack[0] which may\n // not be the depositor's signature.\n if (input.finalScriptWitness && input.finalScriptWitness.length > 0) {\n const witnessStack = parseWitnessStack(input.finalScriptWitness);\n if (witnessStack.length !== TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE) {\n throw new Error(\n `Unexpected finalized witness stack size at input ${inputIndex}: ` +\n `expected ${TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE} items (signature, script, controlBlock), ` +\n `got ${witnessStack.length}`,\n );\n }\n return extractSchnorrSig(witnessStack[0], inputIndex);\n }\n\n throw new Error(\n `No tapScriptSig or finalScriptWitness found in signed PSBT at input ${inputIndex}`,\n );\n}\n\n/**\n * Extract and validate a 64-byte Schnorr signature.\n * Rejects 65-byte signatures because the appended sighash byte changes the\n * Taproot message being signed; stripping it would produce an unverifiable\n * SIGHASH_DEFAULT signature.\n * @internal\n */\nfunction extractSchnorrSig(sig: Uint8Array, inputIndex: number): string {\n if (sig.length === 64) {\n return uint8ArrayToHex(new Uint8Array(sig));\n }\n if (sig.length === 65) {\n throw new Error(\n `Unexpected sighash byte 0x${sig[64].toString(16).padStart(2, \"0\")} at input ${inputIndex}. ` +\n \"Expected implicit SIGHASH_DEFAULT as a 64-byte signature.\",\n );\n }\n throw new Error(\n `Unexpected signature length at input ${inputIndex}: ${sig.length}`,\n );\n}\n\n/**\n * Parse a BIP-141 serialized witness stack into individual stack items.\n * Format: [varint item_count] [varint len, data]...\n *\n * Throws on malformed input (truncated buffer, 8-byte varints, or trailing\n * bytes) so callers never receive silently-corrupted witness items.\n * @internal\n */\nfunction parseWitnessStack(witness: Buffer): Buffer[] {\n const items: Buffer[] = [];\n let offset = 0;\n\n const requireBytes = (n: number): void => {\n if (offset + n > witness.length) {\n throw new Error(\n `Malformed witness data: need ${n} byte(s) at offset ${offset}, only ${witness.length - offset} remaining`,\n );\n }\n };\n\n const readVarInt = (): number => {\n requireBytes(1);\n const first = witness[offset++];\n if (first < 0xfd) return first;\n if (first === 0xfd) {\n requireBytes(2);\n const val = (witness[offset] | (witness[offset + 1] << 8)) >>> 0;\n offset += 2;\n return val;\n }\n if (first === 0xfe) {\n requireBytes(4);\n const val =\n (witness[offset] |\n (witness[offset + 1] << 8) |\n (witness[offset + 2] << 16) |\n (witness[offset + 3] << 24)) >>>\n 0;\n offset += 4;\n return val;\n }\n // 0xff — 8-byte varint. Not used for witness sizes in practice and JS\n // numbers cannot represent all 64-bit values exactly, so reject rather\n // than risk silent truncation.\n throw new Error(\n `Malformed witness data: 8-byte varint (0xff) not supported at offset ${offset - 1}`,\n );\n };\n\n const count = readVarInt();\n for (let i = 0; i < count; i++) {\n const len = readVarInt();\n requireBytes(len);\n items.push(Buffer.from(witness.subarray(offset, offset + len)));\n offset += len;\n }\n\n if (offset !== witness.length) {\n throw new Error(\n `Malformed witness data: ${witness.length - offset} trailing byte(s) after parsing ${count} item(s)`,\n );\n }\n\n return items;\n}\n\n","/**\n * Asserts a wallet-returned PSBT encodes the same unsigned transaction\n * as the locally-built PSBT we asked the wallet to sign. Per-input PSBT\n * metadata (witnessUtxo, tapLeafScript, sighashType) is intentionally NOT\n * compared — those fields are committed to the Schnorr sighash and the\n * vault provider's `verify_depositor_signature` rejects mismatches there.\n * This primitive defends the path where a colluding VP would otherwise\n * accept a wallet-substituted signature.\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\n/**\n * Thrown when a wallet-returned PSBT encodes a different unsigned\n * transaction than the one the caller asked the wallet to sign.\n */\nexport class PsbtSubstitutionError extends Error {\n constructor(detail: string) {\n super(\n `Wallet returned a PSBT for a different transaction: ${detail}`,\n );\n this.name = \"PsbtSubstitutionError\";\n }\n}\n\nexport interface AssertPsbtUnsignedTxMatchesParams {\n /** PSBT we built locally and asked the wallet to sign. */\n requestedPsbtHex: string;\n /** PSBT the wallet returned after signing. */\n returnedPsbtHex: string;\n}\n\nfunction parsePsbt(label: \"requested\" | \"returned\", hex: string): Psbt {\n try {\n return Psbt.fromHex(hex);\n } catch (cause) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n throw new Error(`Failed to parse ${label} PSBT: ${reason}`);\n }\n}\n\n/**\n * Length of the hex prefix included in mismatch errors. Short enough that\n * full prevout txids and output scriptPubKeys never reach logs / error\n * trackers, long enough to disambiguate during forensic triage.\n */\nconst REDACTED_HEX_PREFIX_LEN = 8;\n\nfunction redactHex(buf: Buffer): string {\n return `${buf.toString(\"hex\").slice(0, REDACTED_HEX_PREFIX_LEN)}…`;\n}\n\n/**\n * `bitcoinjs-lib` exposes `txInputs[i].hash` in internal little-endian form;\n * a human reading logs expects the big-endian txid an explorer would show.\n * Reverse before truncating so the surfaced prefix matches what an operator\n * can search for.\n */\nfunction redactTxid(internalHash: Buffer): string {\n const reversed = Buffer.from(internalHash).reverse();\n return redactHex(reversed);\n}\n\n/**\n * Compare two PSBTs and throw `PsbtSubstitutionError` unless they encode\n * the same unsigned transaction (version, locktime, inputs, outputs).\n *\n * @throws PsbtSubstitutionError on any mismatch in the unsigned tx\n * @throws Error if either PSBT cannot be parsed\n */\nexport function assertPsbtUnsignedTxMatches(\n params: AssertPsbtUnsignedTxMatchesParams,\n): void {\n const requested = parsePsbt(\"requested\", params.requestedPsbtHex);\n const returned = parsePsbt(\"returned\", params.returnedPsbtHex);\n\n if (requested.version !== returned.version) {\n throw new PsbtSubstitutionError(\n `tx version differs (requested=${requested.version}, returned=${returned.version})`,\n );\n }\n if (requested.locktime !== returned.locktime) {\n throw new PsbtSubstitutionError(\n `tx locktime differs (requested=${requested.locktime}, returned=${returned.locktime})`,\n );\n }\n if (requested.txInputs.length !== returned.txInputs.length) {\n throw new PsbtSubstitutionError(\n `input count differs (requested=${requested.txInputs.length}, returned=${returned.txInputs.length})`,\n );\n }\n if (requested.txOutputs.length !== returned.txOutputs.length) {\n throw new PsbtSubstitutionError(\n `output count differs (requested=${requested.txOutputs.length}, returned=${returned.txOutputs.length})`,\n );\n }\n for (let i = 0; i < requested.txInputs.length; i++) {\n const r = requested.txInputs[i];\n const s = returned.txInputs[i];\n if (!r.hash.equals(s.hash)) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout txid differs (requested=${redactTxid(r.hash)}, returned=${redactTxid(s.hash)})`,\n );\n }\n if (r.index !== s.index) {\n throw new PsbtSubstitutionError(\n `input ${i} prevout vout differs (requested=${r.index}, returned=${s.index})`,\n );\n }\n if (r.sequence !== s.sequence) {\n throw new PsbtSubstitutionError(\n `input ${i} sequence differs (requested=${r.sequence}, returned=${s.sequence})`,\n );\n }\n }\n for (let i = 0; i < requested.txOutputs.length; i++) {\n const r = requested.txOutputs[i];\n const s = returned.txOutputs[i];\n if (!r.script.equals(s.script)) {\n throw new PsbtSubstitutionError(\n `output ${i} scriptPubKey differs (requested=${redactHex(r.script)}, returned=${redactHex(s.script)})`,\n );\n }\n if (r.value !== s.value) {\n throw new PsbtSubstitutionError(\n `output ${i} value differs (requested=${r.value}, returned=${s.value})`,\n );\n }\n }\n}\n"],"names":["assertWasmPeginSizing","result","params","expectedCount","expectedClaimValue","computeMinClaimValue","maxImpliedFee","MAX_REASONABLE_PEGIN_VBYTES","i","requested","peginAmount","htlcValue","impliedFee","assertEncodedHtlcOutputsMatch","outputs","htlcValues","htlcScriptPubKeys","encodedValue","encodedScript","expectedScript","AUTH_ANCHOR_HASH_HEX_LEN","HEX_PATTERN","buildPrePeginPsbt","authAnchorHash","normalizeAuthAnchorHash","createPrePeginTransaction","parsed","parseUnfundedWasmTransaction","totalOutputValue","sum","o","authAnchorVout","value","cleaned","buildPeginTxFromFundedPrePegin","buildPeginTxFromPrePegin","createPayoutScript","connector","createPayoutConnector","DEPOSITOR_PAYOUT_INPUT_COUNT","PEGIN_VAULT_OUTPUT_INDEX","ASSERT_PAYOUT_OUTPUT_INDEX","PAYOUT_ANCHOR_DUST_SATS","VP_CLAIMER_PAYOUT_OUTPUT_COUNT","NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT","MAX_VP_COMMISSION_BPS_EXCLUSIVE","TAPROOT_SINGLE_SIG_WITNESS_STACK_SIZE","MAX_PAYOUT_FEE_FRACTION_NUMERATOR","MAX_PAYOUT_FEE_FRACTION_DENOMINATOR","buildPayoutPsbt","payoutTxHex","stripHexPrefix","peginTxHex","assertTxHex","payoutConnector","payoutScriptBytes","hexToUint8Array","controlBlock","payoutTx","Transaction","peginTx","assertTx","psbt","Psbt","input0","input1","input0Txid","uint8ArrayToHex","peginTxid","input1Txid","assertTxid","peginPrevOut","input1PrevOut","assertPayoutOutputLayout","inputValueSats","outputValueSats","out","implicitFeeSats","maxFeeSats","TAPSCRIPT_LEAF_VERSION","Buffer","tapInternalPubkey","output","args","peginValueSats","claimerBtcPubkey","vaultProviderBtcPubkey","depositorBtcPubkey","vaultKeeperBtcPubkeys","registeredPayoutScriptPubKey","commissionBps","isValidHex","claimer","vp","dep","keepers","k","role","expectedOutCount","expectedOut0ScriptHex","deriveBip86ScriptPubKeyHex","expectedOut0Script","anchorIdx","maxCommissionSats","extractPayoutSignature","signedPsbtHex","depositorPubkey","inputIndex","signedPsbt","input","depositorPubkeyBytes","sigEntry","extractSchnorrSig","witnessStack","parseWitnessStack","sig","witness","items","offset","requireBytes","n","readVarInt","first","val","count","len","PsbtSubstitutionError","detail","parsePsbt","label","hex","cause","reason","REDACTED_HEX_PREFIX_LEN","redactHex","buf","redactTxid","internalHash","reversed","assertPsbtUnsignedTxMatches","returned","r","s"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use strict";var Z=Object.defineProperty;var J=(e,t,r)=>t in e?Z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var A=(e,t,r)=>J(e,typeof t!="symbol"?t+"":t,r);const ee=require("./BTCVaultRegistry.abi-JdeqLz4x.cjs"),h=require("./bitcoin-CHfKAhcI.cjs"),D=require("./PayoutManager-BLpgkfOS.cjs"),P=require("./types-WA0LrDk1.cjs"),_=require("bitcoinjs-lib"),E=require("./assertPsbtUnsignedTxMatches-s9H0Qqkl.cjs"),$=require("./noPayout-B2Xd40nk.cjs"),V=require("./signing-Bnsro0hE.cjs"),L=require("@babylonlabs-io/babylon-tbv-rust-wasm"),te=/^0x[0-9a-fA-F]{64}$/,re=/^0x[0-9a-fA-F]{40}$/,ne=/^0x([0-9a-fA-F]{2})*$/;function C(e,t){if(e.length!==66)throw new Error(`${t} must be 32 bytes (66 hex chars with 0x prefix), got length ${e.length}`);if(!te.test(e))throw new Error(`${t} must contain only hex characters after the 0x prefix`)}function oe(e,t){if(!re.test(e))throw new Error(`${t} must be a 20-byte 0x-prefixed hex address (42 chars)`)}function ie(e,t){if(!ne.test(e))throw new Error(`${t} must be a 0x-prefixed hex string with an even number of hex chars`)}async function ae(e){const{btcVaultRegistryAddress:t,vaultId:r,hashlock:n,activationMetadata:a,writeContract:u,signal:s}=e;s==null||s.throwIfAborted(),oe(t,"btcVaultRegistryAddress"),C(r,"vaultId");const i=h.ensureHexPrefix(e.secret);if(C(i,"secret"),n!==void 0&&(C(n,"hashlock"),!D.validateSecretAgainstHashlock(i,n)))throw new Error("Invalid secret: SHA256(secret) does not match the provided hashlock");return ie(a,"activationMetadata"),u({address:t,abi:ee.BTCVaultRegistryABI,functionName:"activateVaultWithSecret",args:[r,i,a]})}const se=1e4;async function F(e){const{statusReader:t,peginTxid:r,targetStatuses:n,timeoutMs:a,pollIntervalMs:u=se,signal:s}=e,i=Date.now();for(;;){if(s!=null&&s.aborted)throw new Error(`Polling aborted for pegin ${r.slice(0,8)}… (target: ${[...n].join(", ")})`);if(Date.now()-i>=a)throw new Error(`Polling timeout after ${a}ms for pegin ${r.slice(0,8)}… (target: ${[...n].join(", ")})`);try{const o=await t.getPeginStatus({pegin_txid:r},s);if(o.pegin_txid.toLowerCase()!==r.toLowerCase())throw new Error(`getPeginStatus returned status for pegin ${o.pegin_txid.slice(0,8)}…, requested ${r.slice(0,8)}…`);const l=o.status;if(n.has(l)||l===P.DaemonStatus.ACTIVATED)return l;if(l===P.DaemonStatus.EXPIRED||P.VP_TERMINAL_FAILURE_STATUSES.has(l))throw new Error(`Pegin ${r.slice(0,8)}… reached terminal status "${l}" while waiting for ${[...n].join(", ")}`)}catch(o){if(!(o instanceof P.JsonRpcError&&o.code===P.RpcErrorCode.PEGIN_NOT_FOUND))throw o}await new Promise((o,l)=>{const d=()=>{clearTimeout(c),l(new Error(`Polling aborted for pegin ${r.slice(0,8)}… (target: ${[...n].join(", ")})`))},c=setTimeout(()=>{s==null||s.removeEventListener("abort",d),o()},u);s==null||s.addEventListener("abort",d,{once:!0})})}}const ue=300*1e3,le=new Set([P.DaemonStatus.PENDING_DEPOSITOR_WOTS_PK,...P.POST_WOTS_STATUSES]);async function ce(e){const{statusReader:t,wotsSubmitter:r,peginTxid:n,depositorPk:a,wotsPublicKeys:u,timeoutMs:s=ue,signal:i}=e;i==null||i.throwIfAborted();const o=await F({statusReader:t,peginTxid:n,targetStatuses:le,timeoutMs:s,signal:i});P.POST_WOTS_STATUSES.has(o)||(i==null||i.throwIfAborted(),await r.submitDepositorWotsKey({pegin_txid:n,depositor_pk:a,wots_public_keys:u},i))}const X=1,he=1;function de(e,t){const r=h.stripHexPrefix(t).toLowerCase(),a=e.map(u=>h.stripHexPrefix(u).toLowerCase()).filter(u=>u!==r);if(a.length===0)throw new Error("Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)");if(new Set(a).size!==a.length)throw new Error("Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured");return a}function pe(e,t,r){const n=r.map(c=>h.stripHexPrefix(c).toLowerCase()),a=t.filter(c=>n.includes(c));if(a.length>0)throw new Error(`Cannot validate challenger set: vault keepers and universal challengers overlap (${a.join(", ")})`);const u=[...t,...n],s=e.map(c=>h.stripHexPrefix(c.challenger_pubkey).toLowerCase()),i=new Set(s);if(i.size!==s.length)throw new Error("Depositor graph contains duplicate challenger entries in challenger_presign_data");const o=new Set(u),l=u.filter(c=>!i.has(c)),d=s.filter(c=>!o.has(c));if(l.length>0||d.length>0)throw new Error("Depositor graph challenger set does not match expected (local ∪ universal)"+(l.length>0?` (missing: ${l.join(", ")})`:"")+(d.length>0?` (unexpected: ${d.join(", ")})`:""))}function ge(e,t){const r=e.ins[t];return h.uint8ArrayToHex(new Uint8Array(r.hash).slice().reverse())}function R(e,t,r,n,a){const u=e.ins[t];if(u.index!==0)throw new Error(`NoPayout (challenger ${a}) input ${t} expected to spend ${n} vout 0, got vout ${u.index}`);const s=r.getId(),i=ge(e,t);if(i!==s)throw new Error(`NoPayout (challenger ${a}) input ${t} does not reference ${n} (expected txid ${s}, got ${i})`)}async function fe(e,t,r){const n=[],a=[],u=[],s=de(r.vaultKeeperBtcPubkeys,r.depositorBtcPubkey);pe(e.challenger_presign_data,s,r.universalChallengerBtcPubkeys);const i=await E.buildPayoutPsbt({payoutTxHex:e.payout_tx.tx_hex,peginTxHex:r.peginTxHex,assertTxHex:e.assert_tx.tx_hex,depositorBtcPubkey:r.depositorBtcPubkey,vaultProviderBtcPubkey:r.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:r.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:r.universalChallengerBtcPubkeys,timelockPegin:r.timelockPegin,network:r.network,claimerBtcPubkey:r.depositorBtcPubkey,registeredPayoutScriptPubKey:r.registeredPayoutScriptPubKey,commissionBps:he});n.push(i.psbtHex),a.push(V.createTaprootScriptPathSignOptions(t,X));const o=h.stripHexPrefix(r.depositorBtcPubkey),l=_.Transaction.fromHex(h.stripHexPrefix(e.assert_tx.tx_hex));for(const d of e.challenger_presign_data){const c=h.stripHexPrefix(d.challenger_pubkey),p=n.length,b=await Pe({challenger:d,challengerPubkey:c,claimerPubkey:o,localChallengers:s,assertTxParsed:l,ctx:r});n.push(b),a.push(V.createTaprootScriptPathSignOptions(t,X)),u.push({challengerPubkey:c,noPayoutIdx:p})}return{psbtHexes:n,signOptions:a,challengerEntries:u}}async function Pe(e){const{challenger:t,challengerPubkey:r,claimerPubkey:n,localChallengers:a,assertTxParsed:u,ctx:s}=e;$.assertNoPayoutOutputMatchesChallenger(t.nopayout_tx.tx_hex,r,s.network);const i=_.Transaction.fromHex(h.stripHexPrefix(t.nopayout_tx.tx_hex)),o=_.Transaction.fromHex(h.stripHexPrefix(t.challenge_assert_x_tx.tx_hex)),l=_.Transaction.fromHex(h.stripHexPrefix(t.challenge_assert_y_tx.tx_hex));if(i.ins.length!==3)throw new Error(`NoPayout (challenger ${r}) must have exactly 3 inputs, got ${i.ins.length}`);R(i,0,u,"Assert",r),R(i,1,o,"ChallengeAssertX",r),R(i,2,l,"ChallengeAssertY",r);const d=[u.outs[0],o.outs[0],l.outs[0]].map(c=>({script_pubkey:h.uint8ArrayToHex(new Uint8Array(c.script)),value:c.value}));return $.buildNoPayoutPsbt({noPayoutTxHex:t.nopayout_tx.tx_hex,challengerPubkey:r,prevouts:d,connectorParams:{claimer:n,localChallengers:a,universalChallengers:s.universalChallengerBtcPubkeys,timelockAssert:s.timelockAssert,councilMembers:s.councilMembers,councilQuorum:s.councilQuorum}})}function me(e,t,r){E.assertPsbtUnsignedTxMatches(e[0]);const n=E.extractPayoutSignature(e[0].returnedPsbtHex,r),a={};for(const u of t)E.assertPsbtUnsignedTxMatches(e[u.noPayoutIdx]),a[u.challengerPubkey]={nopayout_signature:E.extractPayoutSignature(e[u.noPayoutIdx].returnedPsbtHex,r)};return{payout_signatures:{payout_signature:n},per_challenger:a}}async function be(e,t,r){if(typeof e.signPsbts=="function")return e.signPsbts(t,r);const n=[];for(let a=0;a<t.length;a++)n.push(await e.signPsbt(t[a],r==null?void 0:r[a]));return n}async function q(e){const{depositorGraph:t,btcWallet:r,signingContext:n}=e,a=await r.getPublicKeyHex(),{depositorPubkey:u}=h.validateWalletPubkey(a,h.stripHexPrefix(n.depositorBtcPubkey)),{psbtHexes:s,signOptions:i,challengerEntries:o}=await fe(t,a,n),l=await be(r,s,i);if(l.length!==s.length)throw new Error(`Wallet returned ${l.length} signed PSBTs, expected ${s.length}`);const d=s.map((c,p)=>({requestedPsbtHex:c,returnedPsbtHex:l[p]}));return me(d,o,u)}const ye=1200*1e3,G=new Set([P.DaemonStatus.PENDING_ACKS,P.DaemonStatus.PENDING_ACTIVATION,P.DaemonStatus.ACTIVATED_PENDING_BROADCAST,P.DaemonStatus.ACTIVATED]),xe=new Set([P.DaemonStatus.PENDING_DEPOSITOR_SIGNATURES,...G]);function we(e){return e.map(t=>({claimerPubkeyXOnly:h.processPublicKeyToXOnly(t.claimer_pubkey),payoutTxHex:t.payout_tx.tx_hex,assertTxHex:t.assert_tx.tx_hex}))}function k(e){return h.processPublicKeyToXOnly(e).toLowerCase()}function ve(e,t,r,n){const a=k(n),u=[k(t),...r.map(k)],s=new Set(u);if(s.size!==u.length)throw new Error("Cannot validate claimer set: signing context contains duplicate vault provider or vault keeper key");if(s.has(a))throw new Error("Cannot validate claimer set: depositor key overlaps with vault provider or vault keeper set");const i=e.map(p=>k(p.claimer_pubkey));if(new Set(i).size!==i.length)throw new Error("Presign response contains duplicate claimer entries");const o=i.filter(p=>p!==a),l=new Set(o),d=u.filter(p=>!l.has(p)),c=o.filter(p=>!s.has(p));if(d.length>0||c.length>0)throw new Error("Presign response claimer set does not match expected (vault provider ∪ vault keepers)"+(d.length>0?` (missing: ${d.join(", ")})`:"")+(c.length>0?` (unexpected: ${c.join(", ")})`:""))}function z(e,t){return{payoutTxHex:e.payoutTxHex,peginTxHex:t.peginTxHex,assertTxHex:e.assertTxHex,vaultProviderBtcPubkey:t.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:t.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:t.universalChallengerBtcPubkeys,depositorBtcPubkey:t.depositorBtcPubkey,timelockPegin:t.timelockPegin,registeredPayoutScriptPubKey:t.registeredPayoutScriptPubKey,claimerBtcPubkey:e.claimerPubkeyXOnly,commissionBps:t.commissionBps}}async function Te(e,t,r,n){const a=new D.PayoutManager({network:t.network,btcWallet:e}),u=r.length;n==null||n(0,u);let s;if(a.supportsBatchSigning())s=(await a.signPayoutTransactionsBatch(r.map(l=>z(l,t)))).map(l=>l.payoutSignature);else{s=[];for(let o=0;o<r.length;o++){n==null||n(o,u);const l=await a.signPayoutTransaction(z(r[o],t));s.push(l.signature)}}const i={};for(let o=0;o<r.length;o++)i[r[o].claimerPubkeyXOnly]={payout_signature:s[o]};return n==null||n(u,u),i}async function Ee(e){const{statusReader:t,presignClient:r,btcWallet:n,peginTxid:a,depositorPk:u,signingContext:s,timeoutMs:i=ye,signal:o,onProgress:l}=e,d=await F({statusReader:t,peginTxid:a,targetStatuses:xe,timeoutMs:i,signal:o});if(G.has(d))return;o==null||o.throwIfAborted();const c=await r.requestDepositorPresignTransactions({pegin_txid:a,depositor_pk:u},o);o==null||o.throwIfAborted();const p=k(u);ve(c.txs,s.vaultProviderBtcPubkey,s.vaultKeeperBtcPubkeys,u);const b=c.txs.filter(x=>k(x.claimer_pubkey)!==p),m=we(b),y=await Te(n,s,m,l);o==null||o.throwIfAborted();const w=await q({depositorGraph:c.depositor_graph,btcWallet:n,signingContext:{peginTxHex:s.peginTxHex,depositorBtcPubkey:u,vaultProviderBtcPubkey:s.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:s.vaultKeeperBtcPubkeys,universalChallengerBtcPubkeys:s.universalChallengerBtcPubkeys,timelockPegin:s.timelockPegin,timelockAssert:s.timelockAssert,councilMembers:s.councilMembers,councilQuorum:s.councilQuorum,network:s.network,registeredPayoutScriptPubKey:s.registeredPayoutScriptPubKey}});o==null||o.throwIfAborted();const v={...y};v[h.stripHexPrefix(u)]=w.payout_signatures,await r.submitDepositorPresignatures({pegin_txid:a,depositor_pk:u,signatures:v,depositor_claimer_presignatures:w},o)}function ke(e){return/^[0-9a-fA-F]{64}$/.test(e)}function _e(e){const{amountSats:t,minDeposit:r,maxDeposit:n,btcBalance:a,estimatedFeeSats:u,depositorClaimValue:s}=e;return!(t<=0n||t<r||n&&n>0n&&t>n||u==null||s==null||t+u+s>a)}function Se(e,t,r){return e<=0n?{valid:!1,error:"Deposit amount must be greater than zero"}:e<t?{valid:!1,error:`Minimum deposit is ${h.formatSatoshisToBtc(t)} BTC`}:r&&r>0n&&e>r?{valid:!1,error:`Maximum deposit is ${h.formatSatoshisToBtc(r)} BTC`}:{valid:!0}}function Ae(e){const{amount:t,effectiveRemaining:r}=e;return r===null?{valid:!0}:r===0n?{valid:!1,error:"Supply cap reached — deposits temporarily paused"}:t>r?{valid:!1,error:`Vault size exceeds remaining capacity (${h.formatSatoshisToBtc(r)} BTC)`}:{valid:!0}}function Ce(e,t){if(!e||e.length===0)return{valid:!1,error:"At least one vault provider must be selected"};const r=t.map(a=>a.toLowerCase());return e.filter(a=>!r.includes(a.toLowerCase())).length>0?{valid:!1,error:"Invalid vault provider selected"}:{valid:!0}}function W(e,t,r){if(!e||e.length===0)return{valid:!1,error:"At least one vault amount required"};for(let n=0;n<e.length;n++){const a=e[n];if(a<=0n)return{valid:!1,error:`Vault ${n+1} amount must be positive`};if(t&&a<t)return{valid:!1,error:`Vault ${n+1} amount ${h.formatSatoshisToBtc(a)} BTC is below minimum deposit ${h.formatSatoshisToBtc(t)} BTC`};if(r&&a>r)return{valid:!1,error:`Vault ${n+1} amount ${h.formatSatoshisToBtc(a)} BTC exceeds maximum deposit ${h.formatSatoshisToBtc(r)} BTC`}}return{valid:!0}}function Q(e){const t=h.stripHexPrefix(e);return ke(t)?{valid:!0}:{valid:!1,error:"Invalid pubkey format: must be 64 hex characters (32-byte x-only public key, no 0x prefix)"}}function Re(e){if(!e||e.length===0)throw new Error("No vault keepers available. The system requires at least one vault keeper to create a deposit.")}function Be(e){if(!e||e.length===0)throw new Error("No universal challengers available. The system requires at least one universal challenger to create a deposit.")}function $e(e){if(e.length===0)throw new Error("No spendable UTXOs available")}function Ve(e){const{vaultAmounts:t,confirmedUTXOs:r,vaultProviderBtcPubkey:n,vaultKeeperBtcPubkeys:a,universalChallengerBtcPubkeys:u,minDeposit:s,maxDeposit:i}=e,o=W(t,s,i);if(!o.valid)throw new Error(o.error);const l=Q(n);if(!l.valid)throw new Error(l.error);Re(a),Be(u),$e(r)}async function Ie(e){const{vaultRegistryReader:t,vaultKeeperReader:r,universalChallengerReader:n,vaultProviderEthAddress:a,applicationEntryPoint:u,expectedVaultProviderBtcPubkey:s,expectedVaultKeeperBtcPubkeys:i,expectedUniversalChallengerBtcPubkeys:o}=e,[l,d,c]=await Promise.all([t.getVaultProviderBtcPubKey(a),r.getCurrentVaultKeepersVersion(u),n.getLatestUniversalChallengersVersion()]),[p,b]=await Promise.all([r.getVaultKeepersByVersion(u,d),n.getUniversalChallengersByVersion(c)]),m=f=>h.processPublicKeyToXOnly(f).toLowerCase(),y=f=>f.map(m).sort();if(m(s)!==l)throw new Error(`Vault provider BTC pubkey indexer hint does not match BTCVaultRegistry for ${a}. Refresh and try again.`);const v=y(i),x=y(p.map(f=>f.btcPubKey));if(v.length!==x.length||v.some((f,g)=>f!==x[g]))throw new Error(`Vault keeper BTC pubkeys (v${d}) indexer set does not match ApplicationRegistry on-chain set. Refresh and try again.`);const S=y(o),T=y(b.map(f=>f.btcPubKey));if(S.length!==T.length||S.some((f,g)=>f!==T[g]))throw new Error(`Universal challenger BTC pubkeys (v${c}) indexer set does not match ProtocolParams on-chain set. Refresh and try again.`);return{vaultProviderBtcPubkeyXOnly:l,vaultKeeperBtcPubkeysSorted:x,universalChallengerBtcPubkeysSorted:T,expectedAppVaultKeepersVersion:d,expectedUniversalChallengersVersion:c}}class M extends Error{constructor(t){super(t),this.name="RegisteredVaultVersionMismatchError"}}function He(e){return e instanceof M||e instanceof Error&&e.name==="RegisteredVaultVersionMismatchError"}async function Ne(e){const{vaultRegistryReader:t,vaultIds:r,expectedOffchainParamsVersion:n,expectedAppVaultKeepersVersion:a,expectedUniversalChallengersVersion:u}=e,s=await t.getProtocolInfoBatch(r),i=[];if(s.forEach((o,l)=>{const d=r[l];o.offchainParamsVersion!==n&&i.push(`vault ${d}: offchainParams expected v${n}, got v${o.offchainParamsVersion}`),o.appVaultKeepersVersion!==a&&i.push(`vault ${d}: appVaultKeepers expected v${a}, got v${o.appVaultKeepersVersion}`),o.universalChallengersVersion!==u&&i.push(`vault ${d}: universalChallengers expected v${u}, got v${o.universalChallengersVersion}`)}),i.length>0)throw new M(`Aborting BTC broadcast: signer-set or offchain-params versions changed during registration (${i.join("; ")}). The Pre-PegIn was not broadcast; the registered ETH vault will time out per protocol rules.`)}var K=(e=>(e.CLAIM_EVENT_RECEIVED="ClaimEventReceived",e.CLAIM_BROADCAST="ClaimBroadcast",e.ASSERT_BROADCAST="AssertBroadcast",e.PAYOUT_BROADCAST="PayoutBroadcast",e.PAYOUT_BLOCKED="PayoutBlocked",e))(K||{});const Oe=new Set(["PayoutBroadcast","PayoutBlocked"]);function De(e){return Object.values(K).includes(e)}function Fe(e){return!!e&&Oe.has(e)}class j extends Error{constructor(r,n){super(`Refund not yet mature (BIP68 not final): ${n.message}`);A(this,"vaultId");A(this,"cause");this.name="BIP68NotMatureError",this.vaultId=r,this.cause=n}}const Me=/^0x[0-9a-fA-F]{64}$/,Ke=/^(?:0x)?(?:[0-9a-fA-F]{2})+$/,Y=/^(?:0x)?(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{66})$/,U=160,I=2e3,H=10n,N=100n;function Ue(e){if(!Number.isFinite(e)||e<=0)throw new Error(`feeRateSatsVb must be a positive finite number, got ${e}`);return BigInt(Math.ceil(e*U))}const Le=1,Xe=/non-BIP68-final/i;function O(e,t){if(e.length!==66)throw new Error(`${t} must be 32 bytes (66 hex chars with 0x prefix), got length ${e.length}`);if(!Me.test(e))throw new Error(`${t} must contain only hex characters after the 0x prefix`)}function B(e,t){if(!Number.isInteger(e)||e<0)throw new Error(`${t} must be a non-negative integer, got ${e}`)}function ze(e){if(O(e.hashlock,"hashlock"),!Number.isInteger(e.htlcVout)||e.htlcVout<0)throw new Error(`htlcVout must be a non-negative integer, got ${e.htlcVout}`);if(!Array.isArray(e.batch)||e.batch.length===0)throw new Error("batch must be a non-empty array of HTLC entries");if(e.htlcVout>=e.batch.length)throw new Error(`htlcVout ${e.htlcVout} is out of range for batch of size ${e.batch.length}`);for(let r=0;r<e.batch.length;r++){const n=e.batch[r];if(O(n.hashlock,`batch[${r}].hashlock`),!Number.isInteger(n.htlcVout)||n.htlcVout!==r)throw new Error(`batch[${r}].htlcVout must equal ${r} (contiguous vout-ordered vector), got ${n.htlcVout}`);if(typeof n.amount!="bigint"||n.amount<=0n)throw new Error(`batch[${r}].amount must be a positive bigint, got ${n.amount}`)}const t=e.batch[e.htlcVout];if(t.hashlock.toLowerCase()!==e.hashlock.toLowerCase())throw new Error(`batch[${e.htlcVout}].hashlock (${t.hashlock}) does not match target hashlock (${e.hashlock})`);if(t.amount!==e.amount)throw new Error(`batch[${e.htlcVout}].amount (${t.amount}) does not match target amount (${e.amount})`);if(B(e.offchainParamsVersion,"offchainParamsVersion"),B(e.appVaultKeepersVersion,"appVaultKeepersVersion"),B(e.universalChallengersVersion,"universalChallengersVersion"),typeof e.unsignedPrePeginTxHex!="string"||e.unsignedPrePeginTxHex.length===0)throw new Error("unsignedPrePeginTxHex must be a non-empty hex string");if(!Ke.test(e.unsignedPrePeginTxHex))throw new Error("unsignedPrePeginTxHex must be a hex byte string (optional 0x prefix, even length)");if(!e.depositorBtcPubkey||!Y.test(e.depositorBtcPubkey))throw new Error("depositorBtcPubkey must be 32 or 33 bytes of hex (optional 0x prefix)");if(typeof e.amount!="bigint"||e.amount<=0n)throw new Error(`amount must be a positive bigint, got ${e.amount}`)}function qe(e){if(!e.vaultProviderPubkey||!Y.test(e.vaultProviderPubkey))throw new Error("vaultProviderPubkey must be 32 or 33 bytes of hex");if(e.vaultKeeperPubkeys.length===0)throw new Error("vaultKeeperPubkeys must be non-empty");if(e.universalChallengerPubkeys.length===0)throw new Error("universalChallengerPubkeys must be non-empty");if(!Number.isInteger(e.timelockRefund)||e.timelockRefund<=0)throw new Error(`timelockRefund must be a positive integer, got ${e.timelockRefund}`);if(typeof e.feeRate!="bigint"||e.feeRate<=0n)throw new Error(`protocol feeRate must be a positive bigint, got ${e.feeRate}`);if(typeof e.minPeginFeeRate!="bigint"||e.minPeginFeeRate<=0n)throw new Error(`minPeginFeeRate must be a positive bigint, got ${e.minPeginFeeRate}`);if(!Number.isInteger(e.numLocalChallengers)||e.numLocalChallengers<0)throw new Error("numLocalChallengers must be a non-negative integer");if(!Number.isInteger(e.councilQuorum)||!Number.isInteger(e.councilSize)||e.councilQuorum<=0||e.councilSize<=0||e.councilQuorum>e.councilSize)throw new Error(`councilQuorum (${e.councilQuorum}) must be in [1, councilSize=${e.councilSize}]`)}async function Ge(e,t){const r=await L.computeMinClaimValue(t.numLocalChallengers,t.universalChallengerPubkeys.length,t.councilQuorum,t.councilSize,t.feeRate),n=await L.computeMinPeginFee(t.vaultKeeperPubkeys.length,t.universalChallengerPubkeys.length,t.minPeginFeeRate),a=r+n;return e.map((u,s)=>{const i=u.amount-a;if(i<=0n)throw new Error(`Re-derived peginAmount for batch[${s}] is non-positive (${i}): HTLC value ${u.amount} does not exceed depositorClaimValue ${r} + minPeginFee ${n}. Refusing to build a refund from an inconsistent (amount, protocol params) pair.`);return i})}function We(e){const t=_.Psbt.fromHex(e);try{t.finalizeAllInputs()}catch(r){const n=r instanceof Error?r.message:String(r);if(!n.includes("already finalized"))throw new Error(`Failed to finalize refund PSBT: ${n}`)}return t.extractTransaction().toHex()}async function Qe(e){const{vaultId:t,readVault:r,readPrePeginContext:n,feeRate:a,signPsbt:u,broadcastTx:s,signal:i}=e;i==null||i.throwIfAborted(),O(t,"vaultId");const o=await r();ze(o),i==null||i.throwIfAborted();const l=await n(o);if(qe(l),i==null||i.throwIfAborted(),!Number.isFinite(a)||a<=0)throw new Error(`feeRate must be a positive number, got ${a}`);if(a>I)throw new Error(`feeRate ${a} sat/vB exceeds refund safety cap ${I} sat/vB; refusing to sign refund.`);const d=BigInt(Math.ceil(a*U)),c=o.amount*H/N;if(d>c)throw new Error(`Refund fee ${d} sats exceeds the per-vault safety cap of ${c} sats (${H}/${N} of vault.amount=${o.amount}); refusing to sign refund.`);i==null||i.throwIfAborted();const p=h.processPublicKeyToXOnly(o.depositorBtcPubkey),b=h.stripHexPrefix(o.unsignedPrePeginTxHex),m=D.findAuthAnchorOpReturn(b);if(m!==void 0&&m.vout!==o.batch.length)throw new Error(`Auth-anchor OP_RETURN at vout ${m.vout} does not match batch size (${o.batch.length} HTLC outputs expect the anchor at vout ${o.batch.length}). Refund refused — sibling HTLC vector is incomplete.`);const y=m==null?void 0:m.hash;let w;try{w=_.Transaction.fromHex(b)}catch(g){throw new Error(`Failed to parse funded Pre-PegIn transaction hex: ${g instanceof Error?g.message:String(g)}`)}if(w.outs.length<o.batch.length)throw new Error(`Funded Pre-PegIn tx has ${w.outs.length} outputs but batch requires at least ${o.batch.length} HTLC outputs. Refund refused — funded tx shape disagrees with sibling vector.`);const v=await Ge(o.batch,l);i==null||i.throwIfAborted();const{psbtHex:x}=await $.buildRefundPsbt({prePeginParams:{depositorPubkey:p,vaultProviderPubkey:h.stripHexPrefix(l.vaultProviderPubkey),vaultKeeperPubkeys:l.vaultKeeperPubkeys.map(h.stripHexPrefix),universalChallengerPubkeys:l.universalChallengerPubkeys.map(h.stripHexPrefix),hashlocks:o.batch.map(g=>h.stripHexPrefix(g.hashlock)),timelockRefund:l.timelockRefund,pegInAmounts:v,feeRate:l.feeRate,minPeginFeeRate:l.minPeginFeeRate,numLocalChallengers:l.numLocalChallengers,councilQuorum:l.councilQuorum,councilSize:l.councilSize,network:l.network,authAnchorHash:y},fundedPrePeginTxHex:b,htlcVout:o.htlcVout,refundFee:d,hashlock:h.stripHexPrefix(o.hashlock)});i==null||i.throwIfAborted();const S=V.createTaprootScriptPathSignOptions(o.depositorBtcPubkey,Le),T=await u(x,S);E.assertPsbtUnsignedTxMatches({requestedPsbtHex:x,returnedPsbtHex:T});const f=We(T);i==null||i.throwIfAborted();try{return await s(f)}catch(g){throw g instanceof Error&&Xe.test(g.message)?new j(t,g):g}}exports.BIP68NotMatureError=j;exports.ClaimerPegoutStatusValue=K;exports.REFUND_MAX_FEE_FRACTION_DENOMINATOR=N;exports.REFUND_MAX_FEE_FRACTION_NUMERATOR=H;exports.REFUND_MAX_FEE_RATE_SATS_VB=I;exports.REFUND_VSIZE=U;exports.RegisteredVaultVersionMismatchError=M;exports.activateVault=ae;exports.buildAndBroadcastRefund=Qe;exports.estimateRefundFeeSats=Ue;exports.isDepositAmountValid=_e;exports.isPegoutTerminalStatus=Fe;exports.isRecognizedPegoutStatus=De;exports.isRegisteredVaultVersionMismatchError=He;exports.runDepositorPresignFlow=Ee;exports.signDepositorGraph=q;exports.submitWotsPublicKey=ce;exports.validateDepositAmount=Se;exports.validateMultiVaultDepositInputs=Ve;exports.validateOnChainParticipantKeys=Ie;exports.validateProviderSelection=Ce;exports.validateRemainingCapacity=Ae;exports.validateVaultAmounts=W;exports.validateVaultProviderPubkey=Q;exports.verifyRegisteredVaultVersions=Ne;exports.waitForPeginStatus=F;
2
- //# sourceMappingURL=buildAndBroadcastRefund-CLvF5ETe.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"buildAndBroadcastRefund-CLvF5ETe.cjs","sources":["../src/tbv/core/services/activation/activateVault.ts","../src/tbv/core/services/deposit/waitForPeginStatus.ts","../src/tbv/core/services/deposit/submitWotsPublicKey.ts","../src/tbv/core/services/deposit/signDepositorGraph.ts","../src/tbv/core/services/deposit/runDepositorPresignFlow.ts","../src/tbv/core/services/deposit/validation.ts","../src/tbv/core/services/deposit/validateOnChainParticipantKeys.ts","../src/tbv/core/services/deposit/verifyRegisteredVaultVersions.ts","../src/tbv/core/services/pegout/state.ts","../src/tbv/core/services/refund/errors.ts","../src/tbv/core/services/refund/buildAndBroadcastRefund.ts"],"sourcesContent":["/**\n * Vault activation — reveal HTLC secret on Ethereum to move the vault from\n * Verified to Active. The on-chain contract validates `sha256(s) == hashlock`\n * and the activation deadline; this function pre-validates inputs (including\n * an optional hashlock check) and delegates the actual contract write to an\n * injected callback so the SDK stays transport-agnostic.\n *\n * @module services/activation\n */\n\nimport type { Abi, Address, Hash, Hex } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { ensureHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport { validateSecretAgainstHashlock } from \"../htlc\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\nconst ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;\n// ETH calldata convention: 0x prefix REQUIRED, even number of hex chars, may\n// be empty (\"0x\"). Named distinctly from the BTC-hex regex in\n// buildAndBroadcastRefund.ts (which allows an optional prefix and requires\n// non-empty) to make the convention explicit at the call site.\nconst ETH_HEX_BYTES_RE = /^0x([0-9a-fA-F]{2})*$/;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\nfunction assertAddress(value: string, label: string): void {\n if (!ADDRESS_HEX_RE.test(value)) {\n throw new Error(\n `${label} must be a 20-byte 0x-prefixed hex address (42 chars)`,\n );\n }\n}\n\nfunction assertHexBytes(value: string, label: string): void {\n if (!ETH_HEX_BYTES_RE.test(value)) {\n throw new Error(\n `${label} must be a 0x-prefixed hex string with an even number of hex chars`,\n );\n }\n}\n\n/**\n * A single ETH contract-write call. The SDK assembles these; the caller\n * executes them via viem, wagmi, a wallet provider, or any other transport.\n */\nexport interface EthContractWriteCall {\n address: Address;\n abi: Abi;\n functionName: string;\n args: readonly unknown[];\n}\n\n/**\n * Minimum shape the SDK requires from any contract-write result. Callers may\n * return richer objects (e.g. including the receipt) — the SDK propagates\n * them unchanged via the generic parameter on {@link EthContractWriter}.\n */\nexport interface EthContractWriteResult {\n transactionHash: Hash;\n}\n\n/**\n * Caller-provided contract writer. The generic `R` lets callers return any\n * transport-specific result shape (e.g. `{ transactionHash, receipt }`);\n * the SDK forwards that shape back through `activateVault`.\n */\nexport type EthContractWriter<R extends EthContractWriteResult = EthContractWriteResult> = (\n call: EthContractWriteCall,\n) => Promise<R>;\n\nexport interface ActivateVaultInput<\n R extends EthContractWriteResult = EthContractWriteResult,\n> {\n /** BTCVaultRegistry contract address (env-specific). */\n btcVaultRegistryAddress: Address;\n /** Vault ID (bytes32, 0x-prefixed). */\n vaultId: Hex;\n /**\n * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n * `0X` prefix is normalised before validation.\n */\n secret: string;\n /**\n * Optional hashlock for client-side pre-validation. When provided, the SDK\n * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n */\n hashlock?: Hex;\n /**\n * Activation metadata passed through to the contract. Required to keep\n * the \"empty metadata\" convention explicit at the call site — pass `\"0x\"`\n * (empty bytes) when no metadata is needed. Must be a 0x-prefixed hex\n * string with an even number of hex chars.\n */\n activationMetadata: Hex;\n /** Caller-provided write callback — see {@link EthContractWriter}. */\n writeContract: EthContractWriter<R>;\n /**\n * Optional abort signal. Checked before validation runs; since validation\n * is fully synchronous, cancellation between validation and the write is\n * not observable and callers should rely on the transport's own\n * cancellation support for that window.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Reveal the HTLC secret on Ethereum and activate the vault.\n *\n * Validates inputs, optionally pre-checks the secret against the expected\n * hashlock, and delegates the contract write to `writeContract`. Returns\n * whatever the writer returns so callers can keep richer transport-specific\n * metadata (e.g. viem receipts) end-to-end.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n * or if `sha256(secret) != hashlock`\n * @throws `Error` if `activationMetadata` is not a 0x-prefixed hex byte\n * string (must have an even number of hex chars). Pass `\"0x\"` for\n * empty metadata.\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVault<\n R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultInput<R>): Promise<R> {\n const {\n btcVaultRegistryAddress,\n vaultId,\n hashlock,\n activationMetadata,\n writeContract,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n\n assertAddress(btcVaultRegistryAddress, \"btcVaultRegistryAddress\");\n assertBytes32(vaultId, \"vaultId\");\n\n const normalizedSecret = ensureHexPrefix(input.secret);\n assertBytes32(normalizedSecret, \"secret\");\n\n if (hashlock !== undefined) {\n assertBytes32(hashlock, \"hashlock\");\n if (!validateSecretAgainstHashlock(normalizedSecret, hashlock)) {\n throw new Error(\n \"Invalid secret: SHA256(secret) does not match the provided hashlock\",\n );\n }\n }\n\n assertHexBytes(activationMetadata, \"activationMetadata\");\n\n return writeContract({\n address: btcVaultRegistryAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"activateVaultWithSecret\",\n args: [vaultId, normalizedSecret, activationMetadata],\n });\n}\n","/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * Pure polling utility with no framework dependencies (no localStorage, no React).\n * Handles \"PegIn not found\" as transient (VP hasn't ingested yet).\n */\n\nimport { JsonRpcError } from \"../../clients/vault-provider/json-rpc-client\";\nimport {\n DaemonStatus,\n RpcErrorCode,\n VP_TERMINAL_FAILURE_STATUSES,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader } from \"./interfaces\";\n\n/** Default polling interval (10 seconds). */\nconst DEFAULT_POLL_INTERVAL_MS = 10_000;\n\nexport interface WaitForPeginStatusParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Set of acceptable statuses — polling stops when the VP reports one of these */\n targetStatuses: ReadonlySet<DaemonStatus>;\n /** Maximum time to wait in milliseconds */\n timeoutMs: number;\n /** Polling interval in milliseconds (default: 10s) */\n pollIntervalMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Poll `getPeginStatus` until the VP reaches one of the target statuses.\n *\n * @returns The DaemonStatus that matched one of the targets, OR\n * `DaemonStatus.ACTIVATED` if the VP raced past the requested target into the\n * happy-path terminal (success-via-overshoot — the goal is satisfied).\n * @throws Error on timeout, abort, non-transient RPC error, or any terminal status (`Expired` + `VP_TERMINAL_FAILURE_STATUSES`) not in `targetStatuses`.\n */\nexport async function waitForPeginStatus(\n params: WaitForPeginStatusParams,\n): Promise<DaemonStatus> {\n const {\n statusReader,\n peginTxid,\n targetStatuses,\n timeoutMs,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n signal,\n } = params;\n\n const startTime = Date.now();\n\n while (true) {\n if (signal?.aborted) {\n throw new Error(\n `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n throw new Error(\n `Polling timeout after ${timeoutMs}ms for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n try {\n const response = await statusReader.getPeginStatus(\n { pegin_txid: peginTxid },\n signal,\n );\n\n // Reject responses echoing a different pegin txid.\n if (response.pegin_txid.toLowerCase() !== peginTxid.toLowerCase()) {\n throw new Error(\n `getPeginStatus returned status for pegin ${response.pegin_txid.slice(0, 8)}…, requested ${peginTxid.slice(0, 8)}…`,\n );\n }\n\n const status = response.status as DaemonStatus;\n if (targetStatuses.has(status)) {\n return status;\n }\n // Happy-path overshoot: VP raced past the requested target to ACTIVATED.\n // The caller's goal (reach some earlier state) is satisfied — return\n // success rather than time out waiting for a state the VP already left.\n if (status === DaemonStatus.ACTIVATED) {\n return status;\n }\n // EXPIRED is included — depositor has no path forward once VP marks the pegin Expired.\n if (\n status === DaemonStatus.EXPIRED ||\n VP_TERMINAL_FAILURE_STATUSES.has(status)\n ) {\n throw new Error(\n `Pegin ${peginTxid.slice(0, 8)}… reached terminal status \"${status}\" while waiting for ${[...targetStatuses].join(\", \")}`,\n );\n }\n } catch (error) {\n // \"PegIn not found\" is transient — VP hasn't ingested the pegin yet.\n const isNotFound =\n error instanceof JsonRpcError &&\n error.code === RpcErrorCode.PEGIN_NOT_FOUND;\n if (!isNotFound) {\n throw error;\n }\n }\n\n // Wait before next poll, with abort support\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(\n new Error(\n `Polling aborted for pegin ${peginTxid.slice(0, 8)}… (target: ${[...targetStatuses].join(\", \")})`,\n ),\n );\n };\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, pollIntervalMs);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","/**\n * Submit pre-derived WOTS public keys to the vault provider.\n *\n * Polls `getPeginStatus` until the VP reaches `PendingDepositorWotsPK`,\n * then submits the keys. If the VP has already moved past WOTS step\n * (e.g., resume flow), submission is skipped.\n *\n * The caller is responsible for deriving WOTS keys externally using\n * `expandWotsSeed` + `deriveWotsBlocksFromSeed` from the SDK's\n * `tbv/core/vault-secrets` and `tbv/core/wots` modules respectively.\n */\n\nimport {\n DaemonStatus,\n POST_WOTS_STATUSES,\n type WotsBlockPublicKey,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader, WotsKeySubmitter } from \"./interfaces\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n/** Maximum time to wait for VP to reach PendingDepositorWotsPK (5 min). */\nconst STATUS_POLL_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** All statuses we accept — either ready for submission or already past it. */\nconst TARGET_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_WOTS_PK,\n ...POST_WOTS_STATUSES,\n]);\n\nexport interface SubmitWotsPublicKeyParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the WOTS key submission interface */\n wotsSubmitter: WotsKeySubmitter;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Pre-derived WOTS block public keys (one per assert block) */\n wotsPublicKeys: WotsBlockPublicKey[];\n /** Maximum time to wait for VP to be ready (default: 5 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Submit WOTS public keys to the vault provider.\n *\n * @throws Error on timeout, abort, or RPC error\n */\nexport async function submitWotsPublicKey(\n params: SubmitWotsPublicKeyParams,\n): Promise<void> {\n const {\n statusReader,\n wotsSubmitter,\n peginTxid,\n depositorPk,\n wotsPublicKeys,\n timeoutMs = STATUS_POLL_TIMEOUT_MS,\n signal,\n } = params;\n\n signal?.throwIfAborted();\n\n // Wait until VP has ingested the pegin and is ready for the WOTS key.\n const status = await waitForPeginStatus({\n statusReader,\n peginTxid,\n targetStatuses: TARGET_STATUSES,\n timeoutMs,\n signal,\n });\n\n // Key was already submitted in a previous session (e.g. resume flow)\n if (POST_WOTS_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n await wotsSubmitter.submitDepositorWotsKey(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n wots_public_keys: wotsPublicKeys,\n },\n signal,\n );\n}\n","/**\n * Depositor Graph Signing Service\n *\n * Signs the depositor's own graph transactions (Payout, NoPayout per challenger)\n * for the depositor-as-claimer flow.\n *\n * Both PSBTs are constructed locally from authoritative on-chain connector\n * parameters and the VP-advertised transaction hexes (which are themselves\n * cross-checked against on-chain or protocol-defined sinks). Building PSBTs\n * locally is essential: every field that enters the Taproot sighash\n * (witnessUtxo, tapLeafScript, controlBlock, tapInternalKey) must come from\n * trusted sources, otherwise a malicious VP could substitute metadata that\n * makes the depositor's signature valid for a different spend.\n *\n * Transaction counts: 1 Payout + N NoPayout = 1 + N total PSBTs.\n *\n * @see btc-vault docs/pegin.md - \"Automatic Graph Creation & Presigning\"\n * @see btc-vault crates/vault/src/transactions/nopayout.rs - NoPayout structure\n */\n\nimport { type Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport type { BitcoinWallet, SignPsbtOptions } from \"../../../../shared/wallets/interfaces\";\nimport type {\n DepositorAsClaimerPresignatures,\n DepositorGraphTransactions,\n DepositorPreSigsPerChallenger,\n PresignDataPerChallenger,\n} from \"../../clients/vault-provider/types\";\nimport {\n assertPsbtUnsignedTxMatches,\n type AssertPsbtUnsignedTxMatchesParams,\n} from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport {\n assertNoPayoutOutputMatchesChallenger,\n buildNoPayoutPsbt,\n} from \"../../primitives/psbt/noPayout\";\nimport {\n buildPayoutPsbt,\n extractPayoutSignature,\n} from \"../../primitives/psbt/payout\";\nimport {\n stripHexPrefix,\n uint8ArrayToHex,\n validateWalletPubkey,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\n/**\n * The depositor signs exactly one input (index 0) per payout/nopayout PSBT.\n * Used to construct SignPsbtOptions for wallet.signPsbt(). PSBTs may carry\n * additional inputs (the payout PSBT includes the assert prevout; the nopayout\n * PSBT includes the two ChallengeAssert prevouts) so the Taproot SIGHASH_DEFAULT\n * sighash commits to all prevouts, but those inputs are not signed by the\n * depositor.\n */\nconst DEPOSITOR_SIGNED_INPUT_COUNT = 1;\n\n/**\n * commissionBps placeholder for the depositor-as-claimer path — `buildPayoutPsbt`\n * only consults it under the VP-claimer role, so any in-range value is inert.\n */\nconst DEPOSITOR_PATH_UNUSED_COMMISSION_BPS = 1;\n\n/** Tracks which indices in the flat PSBT array belong to which challenger */\ninterface ChallengerEntry {\n challengerPubkey: string;\n noPayoutIdx: number;\n}\n\n/** Result of the collect phase - flat PSBT array with index mapping */\ninterface CollectedDepositorGraphPsbts {\n psbtHexes: string[];\n signOptions: SignPsbtOptions[];\n challengerEntries: ChallengerEntry[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Compute the local-challenger set for the depositor-as-claimer flow.\n *\n * Per btc-vault `crates/vault/src/tx_graph/graph.rs:144-150` (introduced in\n * PR #1092 / commit 3133b698, 2026-02-18):\n * Depositor-as-claimer: LocalChallengers = VKs only (VP excluded)\n *\n * Note: the docstring at `crates/vault/src/lib.rs:332` still says\n * `{VaultProvider, VaultKeepers} - {Claimer}` — that wording is stale and\n * predates the depositor-as-claimer special case. This function follows the\n * actual implementation, not the stale docstring.\n *\n * The protocol guarantees the depositor is not a vault keeper\n * (`TxGraphParams::validate` enforces it), so the depositor filter here is\n * defense-in-depth; it surfaces a clear error if a misconfigured context\n * ever violates the invariant.\n */\nfunction deriveLocalChallengers(\n vaultKeeperBtcPubkeys: string[],\n depositorBtcPubkey: string,\n): string[] {\n const depositor = stripHexPrefix(depositorBtcPubkey).toLowerCase();\n const vks = vaultKeeperBtcPubkeys.map((k) => stripHexPrefix(k).toLowerCase());\n const filtered = vks.filter((k) => k !== depositor);\n if (filtered.length === 0) {\n throw new Error(\n \"Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)\",\n );\n }\n if (new Set(filtered).size !== filtered.length) {\n throw new Error(\n \"Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured\",\n );\n }\n return filtered;\n}\n\n/**\n * Reject VP-supplied `challenger_presign_data` whose pubkey set does not\n * exactly equal `localChallengers ∪ universalChallengers`.\n *\n * The daemon's `challenger_presign_data` contains one entry per challenger\n * in `Challengers::all_sorted() = local + universal` (per\n * btc-vault `crates/vault/src/tx_graph/graph.rs:438-458`). For the\n * depositor-as-claimer flow this is `VKs + UCs`.\n *\n * Threat model: a malicious or buggy VP could omit, duplicate, or inject\n * unrelated entries. Missing entries → depositor activates with incomplete\n * recovery material (omitted challenger later becomes unenforceable).\n * Duplicates or extras → wallet signs PSBTs for challengers the protocol\n * doesn't recognize, handing the VP signatures it shouldn't have.\n */\nfunction assertChallengerSetMatchesExpected(\n challengerPresignData: PresignDataPerChallenger[],\n localChallengers: string[],\n universalChallengerBtcPubkeys: string[],\n): void {\n const universal = universalChallengerBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n // Protocol guarantee: local and universal sets are disjoint. Reject\n // overlap so the depositor doesn't sign for an ambiguous challenger role.\n const overlap = localChallengers.filter((k) => universal.includes(k));\n if (overlap.length > 0) {\n throw new Error(\n `Cannot validate challenger set: vault keepers and universal challengers overlap (${overlap.join(\", \")})`,\n );\n }\n const expected = [...localChallengers, ...universal];\n\n const suppliedList = challengerPresignData.map((c) =>\n stripHexPrefix(c.challenger_pubkey).toLowerCase(),\n );\n const suppliedSet = new Set(suppliedList);\n if (suppliedSet.size !== suppliedList.length) {\n throw new Error(\n \"Depositor graph contains duplicate challenger entries in challenger_presign_data\",\n );\n }\n const expectedSet = new Set(expected);\n const missing = expected.filter((c) => !suppliedSet.has(c));\n const extra = suppliedList.filter((c) => !expectedSet.has(c));\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `Depositor graph challenger set does not match expected (local ∪ universal)` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n );\n }\n}\n\n/**\n * Read the txid that the given input references in the unsigned tx, in display\n * (big-endian) hex order. bitcoinjs-lib stores `input.hash` in internal\n * little-endian byte order, which is the reverse of how txids are normally\n * displayed.\n */\nfunction readInputTxid(tx: Transaction, inputIndex: number): string {\n const input = tx.ins[inputIndex];\n return uint8ArrayToHex(new Uint8Array(input.hash).slice().reverse());\n}\n\n/**\n * Verify the noPayout transaction's input at `inputIndex` references the\n * given parent transaction at vout 0 (per nopayout.rs the layout is fixed:\n * Assert:0, ChallengeAssertX:0, ChallengeAssertY:0).\n */\nfunction assertInputReferencesParent(\n noPayoutTx: Transaction,\n inputIndex: number,\n parentTx: Transaction,\n parentLabel: string,\n challengerPubkey: string,\n): void {\n const input = noPayoutTx.ins[inputIndex];\n if (input.index !== 0) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} expected to spend ${parentLabel} vout 0, got vout ${input.index}`,\n );\n }\n const parentTxid = parentTx.getId();\n const inputTxid = readInputTxid(noPayoutTx, inputIndex);\n if (inputTxid !== parentTxid) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) input ${inputIndex} does not reference ${parentLabel} (expected txid ${parentTxid}, got ${inputTxid})`,\n );\n }\n}\n\n// ============================================================================\n// Collect phase\n// ============================================================================\n\n/**\n * Build the depositor's payout PSBT and per-challenger NoPayout PSBTs locally\n * from authoritative connector params.\n *\n * Layout of returned arrays: [Payout, NoPayout_0, NoPayout_1, ...]\n */\nasync function collectDepositorGraphPsbts(\n depositorGraph: DepositorGraphTransactions,\n walletPublicKey: string,\n ctx: DepositorGraphSigningContext,\n): Promise<CollectedDepositorGraphPsbts> {\n const psbtHexes: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const challengerEntries: ChallengerEntry[] = [];\n\n // 1. Fail-fast on a malformed VP response BEFORE doing any PSBT-build\n // work that would be wasted if the challenger set is wrong.\n const localChallengers = deriveLocalChallengers(\n ctx.vaultKeeperBtcPubkeys,\n ctx.depositorBtcPubkey,\n );\n assertChallengerSetMatchesExpected(\n depositorGraph.challenger_presign_data,\n localChallengers,\n ctx.universalChallengerBtcPubkeys,\n );\n\n // 2. Build the payout PSBT locally — every sighash-relevant field is\n // derived from trusted on-chain connector params, not from the VP.\n // buildPayoutPsbt also runs the per-role output validation.\n const builtPayout = await buildPayoutPsbt({\n payoutTxHex: depositorGraph.payout_tx.tx_hex,\n peginTxHex: ctx.peginTxHex,\n assertTxHex: depositorGraph.assert_tx.tx_hex,\n depositorBtcPubkey: ctx.depositorBtcPubkey,\n vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: ctx.universalChallengerBtcPubkeys,\n timelockPegin: ctx.timelockPegin,\n network: ctx.network,\n claimerBtcPubkey: ctx.depositorBtcPubkey,\n registeredPayoutScriptPubKey: ctx.registeredPayoutScriptPubKey,\n commissionBps: DEPOSITOR_PATH_UNUSED_COMMISSION_BPS,\n });\n psbtHexes.push(builtPayout.psbtHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n // 3. Per-challenger: build the NoPayout PSBT locally too.\n const claimerPubkey = stripHexPrefix(ctx.depositorBtcPubkey);\n const assertTxParsed = Transaction.fromHex(\n stripHexPrefix(depositorGraph.assert_tx.tx_hex),\n );\n\n for (const challenger of depositorGraph.challenger_presign_data) {\n const challengerPubkey = stripHexPrefix(challenger.challenger_pubkey);\n\n const noPayoutIdx = psbtHexes.length;\n const noPayoutHex = await buildLocalNoPayoutPsbt({\n challenger,\n challengerPubkey,\n claimerPubkey,\n localChallengers,\n assertTxParsed,\n ctx,\n });\n psbtHexes.push(noPayoutHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n challengerEntries.push({\n challengerPubkey,\n noPayoutIdx,\n });\n }\n\n return { psbtHexes, signOptions, challengerEntries };\n}\n\ninterface BuildLocalNoPayoutPsbtParams {\n challenger: PresignDataPerChallenger;\n challengerPubkey: string;\n claimerPubkey: string;\n localChallengers: string[];\n assertTxParsed: Transaction;\n ctx: DepositorGraphSigningContext;\n}\n\n/**\n * Build a single NoPayout PSBT for one challenger from authoritative\n * inputs. Validates the VP-supplied parent transactions match what the\n * NoPayout transaction commits to via input txids, and asserts the output\n * pays to the protocol-defined challenger sink before returning.\n *\n * NoPayout transaction layout (per\n * btc-vault crates/vault/src/transactions/nopayout.rs):\n * - 3 inputs (fixed order):\n * - Input 0: Assert tx output 0 (depositor signs - NoPayout path)\n * - Input 1: ChallengeAssertX tx output 0 (with timelock)\n * - Input 2: ChallengeAssertY tx output 0 (with timelock)\n * - 1 output: BIP-86 P2TR to the challenger\n */\nasync function buildLocalNoPayoutPsbt(\n params: BuildLocalNoPayoutPsbtParams,\n): Promise<string> {\n const {\n challenger,\n challengerPubkey,\n claimerPubkey,\n localChallengers,\n assertTxParsed,\n ctx,\n } = params;\n\n // Pin the output sink before doing any sighash-relevant work.\n assertNoPayoutOutputMatchesChallenger(\n challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n ctx.network,\n );\n\n // Parse the NoPayout tx and the two ChallengeAssert parents.\n const noPayoutTx = Transaction.fromHex(\n stripHexPrefix(challenger.nopayout_tx.tx_hex),\n );\n const challengeAssertXTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_x_tx.tx_hex),\n );\n const challengeAssertYTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_y_tx.tx_hex),\n );\n\n if (noPayoutTx.ins.length !== 3) {\n throw new Error(\n `NoPayout (challenger ${challengerPubkey}) must have exactly 3 inputs, got ${noPayoutTx.ins.length}`,\n );\n }\n\n // Pin every input's parent. Each parent's outs[0] is the authoritative\n // prevout - because we verified the parent's txid matches what the NoPayout\n // tx commits to, the parent cannot be substituted without changing the\n // NoPayout txid.\n assertInputReferencesParent(\n noPayoutTx,\n 0,\n assertTxParsed,\n \"Assert\",\n challengerPubkey,\n );\n assertInputReferencesParent(\n noPayoutTx,\n 1,\n challengeAssertXTx,\n \"ChallengeAssertX\",\n challengerPubkey,\n );\n assertInputReferencesParent(\n noPayoutTx,\n 2,\n challengeAssertYTx,\n \"ChallengeAssertY\",\n challengerPubkey,\n );\n\n const prevouts = [\n assertTxParsed.outs[0],\n challengeAssertXTx.outs[0],\n challengeAssertYTx.outs[0],\n ].map((out) => ({\n script_pubkey: uint8ArrayToHex(new Uint8Array(out.script)),\n value: out.value,\n }));\n\n return buildNoPayoutPsbt({\n noPayoutTxHex: challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n prevouts,\n connectorParams: {\n claimer: claimerPubkey,\n localChallengers,\n universalChallengers: ctx.universalChallengerBtcPubkeys,\n timelockAssert: ctx.timelockAssert,\n councilMembers: ctx.councilMembers,\n councilQuorum: ctx.councilQuorum,\n },\n });\n}\n\n// ============================================================================\n// Extract phase\n// ============================================================================\n\n/** A pair of a locally-built PSBT and the wallet-returned PSBT for it. */\ntype PsbtPair = AssertPsbtUnsignedTxMatchesParams;\n\n/**\n * Extract all signatures from signed PSBTs and assemble into presignatures.\n * Each pair is asserted to encode the same unsigned tx before its signature\n * is extracted — defends against a wallet that returns a signature for a\n * substituted transaction.\n */\nfunction extractDepositorGraphSignatures(\n psbtPairs: PsbtPair[],\n challengerEntries: ChallengerEntry[],\n depositorPubkey: string,\n): DepositorAsClaimerPresignatures {\n // Positional invariant: psbtPairs[0] is the payout PSBT; per-challenger\n // nopayouts live at indices recorded in `challengerEntries[].noPayoutIdx`.\n // Set up by `collectDepositorGraphPsbts` (payout pushed first, then each\n // nopayout). A future refactor that reorders the array would silently\n // extract the wrong signature for the wrong slot — Critical Path #3.\n assertPsbtUnsignedTxMatches(psbtPairs[0]);\n const payoutSignature = extractPayoutSignature(\n psbtPairs[0].returnedPsbtHex,\n depositorPubkey,\n );\n\n const perChallenger: Record<string, DepositorPreSigsPerChallenger> = {};\n for (const entry of challengerEntries) {\n assertPsbtUnsignedTxMatches(psbtPairs[entry.noPayoutIdx]);\n perChallenger[entry.challengerPubkey] = {\n nopayout_signature: extractPayoutSignature(\n psbtPairs[entry.noPayoutIdx].returnedPsbtHex,\n depositorPubkey,\n ),\n };\n }\n\n return {\n payout_signatures: {\n payout_signature: payoutSignature,\n },\n per_challenger: perChallenger,\n };\n}\n\n/**\n * Sign multiple PSBTs, using batch signing when the wallet supports it.\n * Falls back to sequential `signPsbt` calls for wallets without `signPsbts`.\n */\nasync function signPsbtsWithFallback(\n wallet: BitcoinWallet,\n psbtHexes: string[],\n options?: SignPsbtOptions[],\n): Promise<string[]> {\n if (typeof wallet.signPsbts === \"function\") {\n return wallet.signPsbts(psbtHexes, options);\n }\n\n const signed: string[] = [];\n for (let i = 0; i < psbtHexes.length; i++) {\n signed.push(await wallet.signPsbt(psbtHexes[i], options?.[i]));\n }\n return signed;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Authoritative inputs required to construct the depositor's Payout AND every\n * per-challenger NoPayout PSBT locally. Every field here must come from\n * trusted on-chain sources, not from the vault provider response. They feed\n * directly into the Taproot sighash.\n */\nexport interface DepositorGraphSigningContext {\n /** Raw pegin BTC transaction hex (provides the depositor's signed prevout) */\n peginTxHex: string;\n /** Depositor's BTC public key (x-only, 64-char hex, no 0x prefix) */\n depositorBtcPubkey: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Pegin CSV timelock from the locked offchain params version (blocks) */\n timelockPegin: number;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Sourced from the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n */\n timelockAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix). Sourced from\n * the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).securityCouncilKeys`.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold. Sourced from the on-chain ProtocolParams\n * contract via `ViemProtocolParamsReader.getOffchainParamsByVersion(...).councilQuorum`.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /**\n * On-chain registered depositor payout scriptPubKey (hex, with or without\n * 0x prefix). Used to assert the VP-advertised payout transaction pays to\n * the depositor's registered address before the wallet produces a signature.\n */\n registeredPayoutScriptPubKey: string;\n}\n\nexport interface SignDepositorGraphParams {\n /** The depositor graph from VP response */\n depositorGraph: DepositorGraphTransactions;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** Authoritative inputs used to rebuild every PSBT locally */\n signingContext: DepositorGraphSigningContext;\n}\n\n/**\n * Sign all depositor graph transactions and assemble into presignatures.\n *\n * Flow:\n * 1. Build payout + per-challenger nopayout PSBTs locally\n * 2. Batch sign via wallet.signPsbts() if available, else sequential signPsbt()\n * 3. Extract Schnorr signatures from each signed PSBT\n * 4. Assemble into DepositorAsClaimerPresignatures\n */\nexport async function signDepositorGraph(\n params: SignDepositorGraphParams,\n): Promise<DepositorAsClaimerPresignatures> {\n const { depositorGraph, btcWallet, signingContext } = params;\n\n const walletPublicKey = await btcWallet.getPublicKeyHex();\n // Fail fast if the connected wallet doesn't match the on-chain registered\n // depositor key — otherwise extractPayoutSignature later fails after\n // multiple wallet popups with an opaque \"no signature found\" error.\n const { depositorPubkey } = validateWalletPubkey(\n walletPublicKey,\n stripHexPrefix(signingContext.depositorBtcPubkey),\n );\n\n // 1. Build all PSBTs locally\n const { psbtHexes, signOptions, challengerEntries } =\n await collectDepositorGraphPsbts(\n depositorGraph,\n walletPublicKey,\n signingContext,\n );\n\n // 2. Sign all PSBTs (batch when supported, sequential fallback for mobile)\n const signedPsbtHexes = await signPsbtsWithFallback(\n btcWallet,\n psbtHexes,\n signOptions,\n );\n\n if (signedPsbtHexes.length !== psbtHexes.length) {\n throw new Error(\n `Wallet returned ${signedPsbtHexes.length} signed PSBTs, expected ${psbtHexes.length}`,\n );\n }\n\n // 3. Pair requested with signed and extract signatures\n const psbtPairs: PsbtPair[] = psbtHexes.map((requestedPsbtHex, i) => ({\n requestedPsbtHex,\n returnedPsbtHex: signedPsbtHexes[i],\n }));\n return extractDepositorGraphSignatures(\n psbtPairs,\n challengerEntries,\n depositorPubkey,\n );\n}\n","/**\n * Payout Signing Orchestration\n *\n * Polls VP for `PendingDepositorSignatures`, fetches presign transactions,\n * signs payouts via PayoutManager, signs the depositor graph, and submits\n * all signatures back to the VP.\n *\n * This is the main deposit protocol step between registration and activation.\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport { DaemonStatus } from \"../../clients/vault-provider/types\";\nimport type {\n ClaimerSignatures,\n ClaimerTransactions,\n} from \"../../clients/vault-provider/types\";\nimport { PayoutManager } from \"../../managers/PayoutManager\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport type { PeginStatusReader, PresignClient } from \"./interfaces\";\nimport { signDepositorGraph } from \"./signDepositorGraph\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context required for signing payout transactions.\n * Caller builds this from on-chain data (contract queries, GraphQL, config).\n */\nexport interface PayoutSigningContext {\n /** Raw pegin BTC transaction hex (for PSBT construction) */\n peginTxHex: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Depositor's BTC public key (x-only hex, no prefix) */\n depositorBtcPubkey: string;\n /** Pegin timelock from the locked offchain params version */\n timelockPegin: number;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Source: ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n timelockAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix).\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).securityCouncilKeys`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold.\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).councilQuorum`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /** On-chain registered depositor payout scriptPubKey (hex) */\n registeredPayoutScriptPubKey: string;\n /** VP commission (bps) from `BTCVaultRegistry`; caps the VP-claimer payout commission output. */\n commissionBps: number;\n}\n\nexport interface RunDepositorPresignFlowParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the presign transaction flow interface */\n presignClient: PresignClient;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Signing context built from on-chain data */\n signingContext: PayoutSigningContext;\n /** Maximum polling timeout in milliseconds (default: 20 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Optional progress callback (completed claimers, total claimers) */\n onProgress?: (completed: number, total: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum polling timeout (20 minutes) — VP may take 15-20 min to prepare. */\nconst MAX_POLLING_TIMEOUT_MS = 20 * 60 * 1000;\n\n/** Statuses after payout signatures are submitted — if VP is already here, skip. */\nconst POST_PAYOUT_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_ACKS,\n DaemonStatus.PENDING_ACTIVATION,\n DaemonStatus.ACTIVATED_PENDING_BROADCAST,\n DaemonStatus.ACTIVATED,\n]);\n\nconst TARGET_STATUS: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_SIGNATURES,\n ...POST_PAYOUT_STATUSES,\n]);\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\ninterface PreparedTransaction {\n claimerPubkeyXOnly: string;\n payoutTxHex: string;\n assertTxHex: string;\n}\n\nfunction prepareTransactionsForSigning(\n claimerTransactions: ClaimerTransactions[],\n): PreparedTransaction[] {\n return claimerTransactions.map((tx) => ({\n claimerPubkeyXOnly: processPublicKeyToXOnly(tx.claimer_pubkey),\n payoutTxHex: tx.payout_tx.tx_hex,\n assertTxHex: tx.assert_tx.tx_hex,\n }));\n}\n\n/**\n * Canonical x-only lowercase form, used for all claimer pubkey set-equality\n * comparisons in this module. `processPublicKeyToXOnly` already strips any\n * `0x` prefix; the lowercase here removes case-sensitivity (the VP-response\n * schema validator accepts uppercase hex, and `processPublicKeyToXOnly`\n * preserves the case of already-x-only 64-char input).\n */\nfunction normalizeClaimerPubkey(pubkey: string): string {\n return processPublicKeyToXOnly(pubkey).toLowerCase();\n}\n\n/**\n * Reject VP-supplied `response.txs` whose non-depositor claimer set does not\n * exactly equal `{vaultProviderBtcPubkey} ∪ vaultKeeperBtcPubkeys`.\n *\n * The expected set is derived from on-chain context (sourced by the caller\n * from the registry/contract reads that populate PayoutSigningContext). A\n * malicious or buggy VP could otherwise omit registered vault keepers from\n * the response; the depositor would sign only the supplied subset and submit\n * a partial presignature map. If the VP later disappears, the omitted\n * keepers cannot exercise their payout recovery branch and BTC can lock.\n *\n * The depositor's own claimer entry (if present in `response.txs`) is\n * filtered out before diffing — its Payout PSBT is built locally and signed\n * separately via signDepositorGraph, so its presence in `response.txs` is\n * permitted but not required. Duplicate detection runs on the full supplied\n * list *before* the depositor filter, so a response containing\n * `[VP, VK, depositor, depositor]` is rejected as malformed.\n */\nfunction assertNonDepositorClaimerSetMatches(\n suppliedTxs: ClaimerTransactions[],\n expectedVpPubkey: string,\n expectedVkPubkeys: string[],\n depositorPubkeyXOnly: string,\n): void {\n const depositor = normalizeClaimerPubkey(depositorPubkeyXOnly);\n const expectedList = [\n normalizeClaimerPubkey(expectedVpPubkey),\n ...expectedVkPubkeys.map(normalizeClaimerPubkey),\n ];\n const expected = new Set(expectedList);\n if (expected.size !== expectedList.length) {\n throw new Error(\n \"Cannot validate claimer set: signing context contains duplicate vault provider or vault keeper key\",\n );\n }\n if (expected.has(depositor)) {\n throw new Error(\n \"Cannot validate claimer set: depositor key overlaps with vault provider or vault keeper set\",\n );\n }\n\n const suppliedAll = suppliedTxs.map((tx) =>\n normalizeClaimerPubkey(tx.claimer_pubkey),\n );\n if (new Set(suppliedAll).size !== suppliedAll.length) {\n throw new Error(\n \"Presign response contains duplicate claimer entries\",\n );\n }\n\n const suppliedNonDepositor = suppliedAll.filter((k) => k !== depositor);\n const suppliedSet = new Set(suppliedNonDepositor);\n const missing = expectedList.filter((c) => !suppliedSet.has(c));\n const extra = suppliedNonDepositor.filter((c) => !expected.has(c));\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `Presign response claimer set does not match expected (vault provider ∪ vault keepers)` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n );\n }\n}\n\n/**\n * Build the `SignPayoutParams` for a single claimer. Role/script resolution\n * happens inside `buildPayoutPsbt`; here we only forward the claimer pubkey\n * and the per-vault context fields.\n */\nfunction buildPayoutSigningInput(\n tx: PreparedTransaction,\n context: PayoutSigningContext,\n) {\n return {\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: context.peginTxHex,\n assertTxHex: tx.assertTxHex,\n vaultProviderBtcPubkey: context.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: context.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: context.universalChallengerBtcPubkeys,\n depositorBtcPubkey: context.depositorBtcPubkey,\n timelockPegin: context.timelockPegin,\n registeredPayoutScriptPubKey: context.registeredPayoutScriptPubKey,\n claimerBtcPubkey: tx.claimerPubkeyXOnly,\n commissionBps: context.commissionBps,\n };\n}\n\n/**\n * Sign all payout transactions using PayoutManager.\n * Uses batch signing when wallet supports it, sequential otherwise.\n */\nasync function signPayoutTransactions(\n btcWallet: BitcoinWallet,\n context: PayoutSigningContext,\n transactions: PreparedTransaction[],\n onProgress?: (completed: number, total: number) => void,\n): Promise<Record<string, ClaimerSignatures>> {\n const payoutManager = new PayoutManager({\n network: context.network,\n btcWallet,\n });\n\n const totalClaimers = transactions.length;\n onProgress?.(0, totalClaimers);\n\n let payoutSignatures: string[];\n\n if (payoutManager.supportsBatchSigning()) {\n const results = await payoutManager.signPayoutTransactionsBatch(\n transactions.map((tx) => buildPayoutSigningInput(tx, context)),\n );\n payoutSignatures = results.map((r) => r.payoutSignature);\n } else {\n payoutSignatures = [];\n for (let i = 0; i < transactions.length; i++) {\n onProgress?.(i, totalClaimers);\n const result = await payoutManager.signPayoutTransaction(\n buildPayoutSigningInput(transactions[i], context),\n );\n payoutSignatures.push(result.signature);\n }\n }\n\n const signatures: Record<string, ClaimerSignatures> = {};\n for (let i = 0; i < transactions.length; i++) {\n signatures[transactions[i].claimerPubkeyXOnly] = {\n payout_signature: payoutSignatures[i],\n };\n }\n\n onProgress?.(totalClaimers, totalClaimers);\n return signatures;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Poll for payout transactions, sign them, sign the depositor graph,\n * and submit all signatures to the vault provider.\n *\n * This is the main deposit protocol step between registration and activation.\n *\n * @throws Error on timeout, abort, signing failure, or RPC error\n */\nexport async function runDepositorPresignFlow(\n params: RunDepositorPresignFlowParams,\n): Promise<void> {\n const {\n statusReader,\n presignClient,\n btcWallet,\n peginTxid,\n depositorPk,\n signingContext,\n timeoutMs = MAX_POLLING_TIMEOUT_MS,\n signal,\n onProgress,\n } = params;\n\n // Phase 1: Poll until VP is ready for depositor signatures (or already past)\n const status = await waitForPeginStatus({\n statusReader,\n peginTxid,\n targetStatuses: TARGET_STATUS,\n timeoutMs,\n signal,\n });\n\n // Resume-safe: if VP already moved past payout signing, nothing to do\n if (POST_PAYOUT_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n // Phase 2: Fetch presign transactions\n const response = await presignClient.requestDepositorPresignTransactions(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n },\n signal,\n );\n\n signal?.throwIfAborted();\n\n // Phase 3: Sign VP/VK claimer payout transactions\n // Fail-fast: assert the supplied non-depositor claimer set exactly equals\n // the on-chain-derived {VP} ∪ {VKs} before any wallet prompts run. The\n // depositor's own entry is permitted but not required (its payout is\n // signed separately via signDepositorGraph in Phase 4).\n const depositorPkNormalized = normalizeClaimerPubkey(depositorPk);\n assertNonDepositorClaimerSetMatches(\n response.txs,\n signingContext.vaultProviderBtcPubkey,\n signingContext.vaultKeeperBtcPubkeys,\n depositorPk,\n );\n // Filter out the depositor's own claimer entry — its payout is signed\n // separately via signDepositorGraph (Phase 4) using VP-provided PSBTs.\n // Including it here would cause a redundant wallet signing prompt whose\n // result is discarded when the depositor graph signature overwrites it.\n // Compare on the normalized form so an uppercase-hex depositor entry in\n // the VP response is still filtered out consistently with the assertion.\n const nonDepositorTxs = response.txs.filter(\n (tx) => normalizeClaimerPubkey(tx.claimer_pubkey) !== depositorPkNormalized,\n );\n const preparedTransactions = prepareTransactionsForSigning(nonDepositorTxs);\n const claimerSignatures = await signPayoutTransactions(\n btcWallet,\n signingContext,\n preparedTransactions,\n onProgress,\n );\n\n signal?.throwIfAborted();\n\n // Phase 4: Sign depositor-as-claimer graph. Both Payout and per-challenger\n // NoPayout PSBTs are rebuilt locally inside signDepositorGraph from these\n // authoritative connector params and the on-chain protocol parameters.\n const depositorClaimerPresignatures = await signDepositorGraph({\n depositorGraph: response.depositor_graph,\n btcWallet,\n signingContext: {\n peginTxHex: signingContext.peginTxHex,\n depositorBtcPubkey: depositorPk,\n vaultProviderBtcPubkey: signingContext.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: signingContext.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys:\n signingContext.universalChallengerBtcPubkeys,\n timelockPegin: signingContext.timelockPegin,\n timelockAssert: signingContext.timelockAssert,\n councilMembers: signingContext.councilMembers,\n councilQuorum: signingContext.councilQuorum,\n network: signingContext.network,\n registeredPayoutScriptPubKey: signingContext.registeredPayoutScriptPubKey,\n },\n });\n\n signal?.throwIfAborted();\n\n // Phase 5: Submit all signatures to VP\n // Include depositor's own payout signature in the signatures map\n const allSignatures = { ...claimerSignatures };\n allSignatures[stripHexPrefix(depositorPk)] =\n depositorClaimerPresignatures.payout_signatures;\n\n await presignClient.submitDepositorPresignatures(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n signatures: allSignatures,\n depositor_claimer_presignatures: depositorClaimerPresignatures,\n },\n signal,\n );\n}\n","/**\n * Pure validation functions for deposit operations.\n *\n * All validations return a consistent {@link ValidationResult} format or throw\n * on critical failures (e.g. missing protocol participants).\n *\n * Business rules (single-provider limit, max vault count) and form-flow\n * checks (wallet connected) belong in the consumer layer.\n *\n * @module tbv/core/services/deposit/validation\n */\n\nimport {\n formatSatoshisToBtc,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n warnings?: string[];\n}\n\n/**\n * Parameters for checking if a deposit form is valid.\n */\nexport interface DepositFormValidityParams {\n /** Deposit amount in satoshis */\n amountSats: bigint;\n /** Minimum deposit from protocol params */\n minDeposit: bigint;\n /** Maximum deposit from protocol params (optional) */\n maxDeposit?: bigint;\n /** User's available BTC balance in satoshis */\n btcBalance: bigint;\n /** Estimated transaction fee in satoshis */\n estimatedFeeSats?: bigint;\n /** Depositor claim value in satoshis (required output for challenge transactions) */\n depositorClaimValue?: bigint;\n}\n\nexport interface RemainingCapacityParams {\n /** Requested deposit amount in satoshis */\n amount: bigint;\n /**\n * Effective remaining capacity in satoshis (min of protocol-total and\n * per-address remaining). `null` means no cap applies.\n */\n effectiveRemaining: bigint | null;\n}\n\n/** Narrow structural type for UTXO — avoids importing vault-specific types. */\ninterface UtxoLike {\n txid: string;\n vout: number;\n value: number;\n}\n\n/**\n * Parameters for validating multi-vault deposit flow inputs.\n *\n * Callers must resolve any async loading states before calling — the SDK\n * validates resolved data, not React hook state.\n *\n * Form-flow checks (wallet connected, provider selected) are the caller's\n * responsibility and are NOT performed here.\n */\nexport interface MultiVaultDepositFlowInputs {\n vaultAmounts: bigint[];\n confirmedUTXOs: UtxoLike[];\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: string[];\n universalChallengerBtcPubkeys: string[];\n /** Protocol minimum deposit per vault (satoshis) */\n minDeposit: bigint;\n /** Protocol maximum deposit per vault (satoshis) */\n maxDeposit?: bigint;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction isValidXOnlyHex(hex: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(hex);\n}\n\n// ---------------------------------------------------------------------------\n// Validation functions\n// ---------------------------------------------------------------------------\n\n/**\n * Check if deposit amount is within valid range and affordable.\n *\n * Returns false when fees/claim value are not yet known (still loading),\n * and includes them in the balance check once available.\n */\nexport function isDepositAmountValid(\n params: DepositFormValidityParams,\n): boolean {\n const {\n amountSats,\n minDeposit,\n maxDeposit,\n btcBalance,\n estimatedFeeSats,\n depositorClaimValue,\n } = params;\n\n if (amountSats <= 0n) return false;\n if (amountSats < minDeposit) return false;\n if (maxDeposit && maxDeposit > 0n && amountSats > maxDeposit) return false;\n\n if (estimatedFeeSats == null || depositorClaimValue == null) return false;\n\n const totalRequired = amountSats + estimatedFeeSats + depositorClaimValue;\n if (totalRequired > btcBalance) return false;\n\n return true;\n}\n\n/**\n * Validate deposit amount against minimum and maximum constraints.\n */\nexport function validateDepositAmount(\n amount: bigint,\n minDeposit: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (amount <= 0n) {\n return {\n valid: false,\n error: \"Deposit amount must be greater than zero\",\n };\n }\n\n if (amount < minDeposit) {\n return {\n valid: false,\n error: `Minimum deposit is ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n\n if (maxDeposit && maxDeposit > 0n && amount > maxDeposit) {\n return {\n valid: false,\n error: `Maximum deposit is ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that the requested deposit fits within the effective remaining cap.\n */\nexport function validateRemainingCapacity(\n params: RemainingCapacityParams,\n): ValidationResult {\n const { amount, effectiveRemaining } = params;\n if (effectiveRemaining === null) return { valid: true };\n\n if (effectiveRemaining === 0n) {\n return {\n valid: false,\n error: \"Supply cap reached — deposits temporarily paused\",\n };\n }\n\n if (amount > effectiveRemaining) {\n return {\n valid: false,\n error: `Vault size exceeds remaining capacity (${formatSatoshisToBtc(effectiveRemaining)} BTC)`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that selected providers exist in the available set.\n *\n * Business rules (e.g. single-provider limit) are the caller's responsibility.\n */\nexport function validateProviderSelection(\n selectedProviders: string[],\n availableProviders: string[],\n): ValidationResult {\n if (!selectedProviders || selectedProviders.length === 0) {\n return {\n valid: false,\n error: \"At least one vault provider must be selected\",\n };\n }\n\n const availableProvidersLower = availableProviders.map((p) =>\n p.toLowerCase(),\n );\n const invalidProviders = selectedProviders.filter(\n (p) => !availableProvidersLower.includes(p.toLowerCase()),\n );\n\n if (invalidProviders.length > 0) {\n return {\n valid: false,\n error: \"Invalid vault provider selected\",\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault amounts array for multi-vault deposits.\n * Checks count, positivity, and per-vault min/max protocol limits.\n *\n * Max vault count limits are the caller's responsibility.\n */\nexport function validateVaultAmounts(\n amounts: bigint[],\n minDeposit?: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (!amounts || amounts.length === 0) {\n return {\n valid: false,\n error: \"At least one vault amount required\",\n };\n }\n\n for (let i = 0; i < amounts.length; i++) {\n const amount = amounts[i];\n if (amount <= 0n) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount must be positive`,\n };\n }\n if (minDeposit && amount < minDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC is below minimum deposit ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n if (maxDeposit && amount > maxDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC exceeds maximum deposit ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault provider BTC public key format.\n */\nexport function validateVaultProviderPubkey(pubkey: string): ValidationResult {\n const stripped = stripHexPrefix(pubkey);\n if (!isValidXOnlyHex(stripped)) {\n return {\n valid: false,\n error:\n \"Invalid pubkey format: must be 64 hex characters (32-byte x-only public key, no 0x prefix)\",\n };\n }\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers for multi-vault validation\n// ---------------------------------------------------------------------------\n\nfunction validateVaultKeepers(vaultKeeperBtcPubkeys: string[]): void {\n if (!vaultKeeperBtcPubkeys || vaultKeeperBtcPubkeys.length === 0) {\n throw new Error(\n \"No vault keepers available. The system requires at least one vault keeper to create a deposit.\",\n );\n }\n}\n\nfunction validateUniversalChallengers(\n universalChallengerBtcPubkeys: string[],\n): void {\n if (\n !universalChallengerBtcPubkeys ||\n universalChallengerBtcPubkeys.length === 0\n ) {\n throw new Error(\n \"No universal challengers available. The system requires at least one universal challenger to create a deposit.\",\n );\n }\n}\n\nfunction validateUTXOState(confirmedUTXOs: UtxoLike[]): void {\n if (confirmedUTXOs.length === 0) {\n throw new Error(\"No spendable UTXOs available\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Multi-vault composite validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validate protocol-level multi-vault deposit inputs.\n * Throws an error if any validation fails.\n *\n * Form-flow checks (wallet connections, provider selection) must be\n * performed by the caller before invoking this function.\n */\nexport function validateMultiVaultDepositInputs(\n params: MultiVaultDepositFlowInputs,\n): void {\n const {\n vaultAmounts,\n confirmedUTXOs,\n vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys,\n minDeposit,\n maxDeposit,\n } = params;\n\n const amountsValidation = validateVaultAmounts(\n vaultAmounts,\n minDeposit,\n maxDeposit,\n );\n if (!amountsValidation.valid) {\n throw new Error(amountsValidation.error);\n }\n\n // Vault provider pubkey\n const pubkeyValidation = validateVaultProviderPubkey(vaultProviderBtcPubkey);\n if (!pubkeyValidation.valid) {\n throw new Error(pubkeyValidation.error);\n }\n\n validateVaultKeepers(vaultKeeperBtcPubkeys);\n validateUniversalChallengers(universalChallengerBtcPubkeys);\n validateUTXOState(confirmedUTXOs);\n}\n","import type { Address } from \"viem\";\n\nimport type {\n UniversalChallengerReader,\n VaultKeeperReader,\n VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { processPublicKeyToXOnly } from \"../../primitives/utils/bitcoin\";\n\nexport interface ValidateOnChainParticipantKeysParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultKeeperReader: VaultKeeperReader;\n universalChallengerReader: UniversalChallengerReader;\n vaultProviderEthAddress: Address;\n applicationEntryPoint: Address;\n expectedVaultProviderBtcPubkey: string;\n expectedVaultKeeperBtcPubkeys: string[];\n expectedUniversalChallengerBtcPubkeys: string[];\n}\n\nexport interface ValidatedOnChainParticipantKeys {\n vaultProviderBtcPubkeyXOnly: string;\n vaultKeeperBtcPubkeysSorted: string[];\n universalChallengerBtcPubkeysSorted: string[];\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n}\n\nexport async function validateOnChainParticipantKeys(\n params: ValidateOnChainParticipantKeysParams,\n): Promise<ValidatedOnChainParticipantKeys> {\n const {\n vaultRegistryReader,\n vaultKeeperReader,\n universalChallengerReader,\n vaultProviderEthAddress,\n applicationEntryPoint,\n expectedVaultProviderBtcPubkey,\n expectedVaultKeeperBtcPubkeys,\n expectedUniversalChallengerBtcPubkeys,\n } = params;\n\n const [\n onChainVpKey,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n ] = await Promise.all([\n vaultRegistryReader.getVaultProviderBtcPubKey(vaultProviderEthAddress),\n vaultKeeperReader.getCurrentVaultKeepersVersion(applicationEntryPoint),\n universalChallengerReader.getLatestUniversalChallengersVersion(),\n ]);\n\n const [onChainKeepers, onChainChallengers] = await Promise.all([\n vaultKeeperReader.getVaultKeepersByVersion(\n applicationEntryPoint,\n expectedAppVaultKeepersVersion,\n ),\n universalChallengerReader.getUniversalChallengersByVersion(\n expectedUniversalChallengersVersion,\n ),\n ]);\n\n const canonical = (k: string) => processPublicKeyToXOnly(k).toLowerCase();\n const sortedSet = (keys: string[]) => keys.map(canonical).sort();\n\n const expectedVpKeyXOnly = canonical(expectedVaultProviderBtcPubkey);\n if (expectedVpKeyXOnly !== onChainVpKey) {\n throw new Error(\n `Vault provider BTC pubkey indexer hint does not match BTCVaultRegistry for ${vaultProviderEthAddress}. Refresh and try again.`,\n );\n }\n\n const expectedKeepers = sortedSet(expectedVaultKeeperBtcPubkeys);\n const onChainKeepersSorted = sortedSet(\n onChainKeepers.map((p) => p.btcPubKey),\n );\n if (\n expectedKeepers.length !== onChainKeepersSorted.length ||\n expectedKeepers.some((k, i) => k !== onChainKeepersSorted[i])\n ) {\n throw new Error(\n `Vault keeper BTC pubkeys (v${expectedAppVaultKeepersVersion}) indexer set does not match ApplicationRegistry on-chain set. Refresh and try again.`,\n );\n }\n\n const expectedChallengers = sortedSet(expectedUniversalChallengerBtcPubkeys);\n const onChainChallengersSorted = sortedSet(\n onChainChallengers.map((p) => p.btcPubKey),\n );\n if (\n expectedChallengers.length !== onChainChallengersSorted.length ||\n expectedChallengers.some((k, i) => k !== onChainChallengersSorted[i])\n ) {\n throw new Error(\n `Universal challenger BTC pubkeys (v${expectedUniversalChallengersVersion}) indexer set does not match ProtocolParams on-chain set. Refresh and try again.`,\n );\n }\n\n return {\n vaultProviderBtcPubkeyXOnly: onChainVpKey,\n vaultKeeperBtcPubkeysSorted: onChainKeepersSorted,\n universalChallengerBtcPubkeysSorted: onChainChallengersSorted,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n };\n}\n","import type { Hex } from \"viem\";\n\nimport type { VaultRegistryReader } from \"../../clients/eth/types\";\n\nexport interface VerifyRegisteredVaultVersionsParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultIds: readonly Hex[];\n expectedOffchainParamsVersion: number;\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n}\n\n// Distinct from a transient RPC failure: the orchestrator removes pending\n// pegin entries only when a real mismatch is confirmed on-chain.\nexport class RegisteredVaultVersionMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RegisteredVaultVersionMismatchError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field so the cleanup path stays reliable.\nexport function isRegisteredVaultVersionMismatchError(\n err: unknown,\n): err is RegisteredVaultVersionMismatchError {\n return (\n err instanceof RegisteredVaultVersionMismatchError ||\n (err instanceof Error && err.name === \"RegisteredVaultVersionMismatchError\")\n );\n}\n\nexport async function verifyRegisteredVaultVersions(\n params: VerifyRegisteredVaultVersionsParams,\n): Promise<void> {\n const {\n vaultRegistryReader,\n vaultIds,\n expectedOffchainParamsVersion,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n } = params;\n\n const infos = await vaultRegistryReader.getProtocolInfoBatch(vaultIds);\n\n const mismatches: string[] = [];\n infos.forEach((v, i) => {\n const id = vaultIds[i];\n if (v.offchainParamsVersion !== expectedOffchainParamsVersion) {\n mismatches.push(\n `vault ${id}: offchainParams expected v${expectedOffchainParamsVersion}, got v${v.offchainParamsVersion}`,\n );\n }\n if (v.appVaultKeepersVersion !== expectedAppVaultKeepersVersion) {\n mismatches.push(\n `vault ${id}: appVaultKeepers expected v${expectedAppVaultKeepersVersion}, got v${v.appVaultKeepersVersion}`,\n );\n }\n if (v.universalChallengersVersion !== expectedUniversalChallengersVersion) {\n mismatches.push(\n `vault ${id}: universalChallengers expected v${expectedUniversalChallengersVersion}, got v${v.universalChallengersVersion}`,\n );\n }\n });\n\n if (mismatches.length > 0) {\n throw new RegisteredVaultVersionMismatchError(\n `Aborting BTC broadcast: signer-set or offchain-params versions changed during registration (${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ETH vault will time out per protocol rules.`,\n );\n }\n}\n","/**\n * Pegout state definitions and protocol-level terminal checks.\n *\n * Maps VP-reported pegout statuses from `vaultProvider_batchGetPegoutStatus`\n * to protocol lifecycle states.\n *\n * Lifecycle (pegin-level, see btc-vault mod.rs PegoutStatus):\n * ClaimEventReceived -> ClaimBroadcast -> AssertBroadcast ->\n * PayoutBroadcast (success) | PayoutBlocked (NoPayout / CouncilNoPayout)\n */\n\n/** Claimer-side pegout statuses reported by the VP. */\nexport enum ClaimerPegoutStatusValue {\n CLAIM_EVENT_RECEIVED = \"ClaimEventReceived\",\n CLAIM_BROADCAST = \"ClaimBroadcast\",\n ASSERT_BROADCAST = \"AssertBroadcast\",\n PAYOUT_BROADCAST = \"PayoutBroadcast\",\n PAYOUT_BLOCKED = \"PayoutBlocked\",\n}\n\nconst PEGOUT_TERMINAL_STATUSES = new Set<string>([\n ClaimerPegoutStatusValue.PAYOUT_BROADCAST,\n ClaimerPegoutStatusValue.PAYOUT_BLOCKED,\n]);\n\n/** Whether a claimer status string maps to a known pegout state. */\nexport function isRecognizedPegoutStatus(status: string): boolean {\n return Object.values(ClaimerPegoutStatusValue).includes(\n status as ClaimerPegoutStatusValue,\n );\n}\n\n/**\n * Whether a claimer status is a hard-terminal pegout status\n * (PayoutBroadcast or PayoutBlocked). Soft-terminal conditions (polling\n * thresholds) are a consumer-side concern.\n */\nexport function isPegoutTerminalStatus(\n claimerStatus: string | undefined,\n): boolean {\n return !!claimerStatus && PEGOUT_TERMINAL_STATUSES.has(claimerStatus);\n}\n","/**\n * Domain errors thrown by the refund service.\n *\n * @module services/refund/errors\n */\n\nimport type { Hex } from \"viem\";\n\n/**\n * Thrown when the broadcast transport rejects the refund tx because the CSV\n * timelock has not yet matured (BIP68 non-final). Callers can surface a\n * friendly \"wait until block N\" message; the original transport error is\n * available via {@link cause}.\n */\nexport class BIP68NotMatureError extends Error {\n public readonly vaultId: Hex;\n public override readonly cause: Error;\n\n constructor(vaultId: Hex, cause: Error) {\n super(`Refund not yet mature (BIP68 not final): ${cause.message}`);\n this.name = \"BIP68NotMatureError\";\n this.vaultId = vaultId;\n this.cause = cause;\n }\n}\n","/**\n * Vault refund orchestration — reclaim BTC from an expired Pre-PegIn HTLC via\n * the CSV-timelocked refund script (leaf 1). SDK owns the sequence of:\n * fetch → fee calc → PSBT build → sign → finalize → broadcast. Pre-fetched\n * data (fee rate) is passed by value; the data-flow-dependent reads\n * (`readVault`, `readPrePeginContext(vault)`) and the interactive transports\n * (`signPsbt`, `broadcastTx`) stay as injected callbacks so the caller keeps\n * its transport choice (viem, wagmi, mempool client, etc.) and error decoding.\n *\n * @module services/refund\n */\n\nimport {\n computeMinClaimValue,\n computeMinPeginFee,\n type Network,\n} from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\nimport type { Address, Hex } from \"viem\";\n\nimport type { SignPsbtOptions } from \"../../../../shared/wallets/interfaces/BitcoinWallet\";\nimport { findAuthAnchorOpReturn } from \"../../managers/pegin\";\nimport { assertPsbtUnsignedTxMatches } from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport { buildRefundPsbt } from \"../../primitives/psbt/refund\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\nimport { BIP68NotMatureError } from \"./errors\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\n// BTC raw-hex convention: 0x prefix optional, even number of hex chars, must\n// be non-empty. Named distinctly from the ETH-hex regex in activateVault.ts\n// (which requires a 0x prefix and allows empty \"0x\") to make the convention\n// explicit at the call site.\nconst BTC_HEX_BYTES_RE = /^(?:0x)?(?:[0-9a-fA-F]{2})+$/;\n// Pubkeys are either 32 bytes (x-only, 64 hex chars) or 33 bytes (compressed,\n// 66 hex chars). 65 hex chars is not a valid byte length — reject it here\n// rather than letting the malformed value surface as an opaque PSBT/signing\n// failure later.\nconst PUBKEY_HEX_RE = /^(?:0x)?(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{66})$/;\n// Conservative upper bound for the fixed-shape refund tx (1 P2TR script-path\n// input spending the HTLC refund leaf → 1 P2TR/P2WPKH output). Taproot\n// script-path witness: 64-byte Schnorr sig + refund script + control block.\n// This is protocol-owned knowledge; callers don't parameterise it.\nexport const REFUND_VSIZE = 160;\n\n// Hard upper bound on the per-vbyte fee rate the SDK will sign a refund at.\n// Defense-in-depth: a compromised mempool endpoint can legally return up to\n// 10_000 sat/vB (see mempoolApi.ts `MAX_FEE_RATE`), which on a 160-vbyte\n// refund would burn up to 1.6M sats in miner fees.\n//\n// Sizing: during the April 2024 halving / Runes launch `fastestFee` peaked\n// around 1,800 sat/vB, and `halfHourFee` tracked close to it during the\n// worst of the congestion (~1,000–1,500 sat/vB range — the half-hour\n// bucket converges with the fastest bucket when the queue is deep enough).\n// 2000 leaves ~1.3× margin over that historical extreme so the cap doesn't\n// gate legitimate refunds during a comparable event, while still blocking\n// the obvious malicious case (10_000) by 5×. Small-vault burn is bounded\n// separately by REFUND_MAX_FEE_FRACTION_* below, so the rate cap is free\n// to be set generously here.\nexport const REFUND_MAX_FEE_RATE_SATS_VB = 2000;\n\n// Hard upper bound on the absolute refund fee as a fraction of the vault\n// amount. Protects small vaults where even a moderate fee rate burns a\n// disproportionate share (e.g. on a 100k-sat vault, 500 sat/vB would burn\n// 80%). The fraction cap binds before the rate cap whenever the vault is\n// small. Expressed as numerator/denominator to keep arithmetic in bigint\n// and avoid float-precision drift in the comparison.\nexport const REFUND_MAX_FEE_FRACTION_NUMERATOR = 10n;\nexport const REFUND_MAX_FEE_FRACTION_DENOMINATOR = 100n;\n\n/**\n * Network fee (sats) the SDK will charge for a refund tx at the given\n * sat/vB rate. Mirrors the internal computation in\n * {@link buildAndBroadcastRefund} so callers (e.g. UI fee previews) don't\n * have to duplicate the constant.\n */\nexport function estimateRefundFeeSats(feeRateSatsVb: number): bigint {\n if (!Number.isFinite(feeRateSatsVb) || feeRateSatsVb <= 0) {\n throw new Error(\n `feeRateSatsVb must be a positive finite number, got ${feeRateSatsVb}`,\n );\n }\n return BigInt(Math.ceil(feeRateSatsVb * REFUND_VSIZE));\n}\n// Refund tx has exactly one input — the HTLC output at htlcVout from the\n// Pre-PegIn tx. Used to tell the signer how many sign entries to generate.\n// (Not the taproot leaf index; the leaf is encoded into the PSBT by the\n// WASM PSBT builder based on the refund script path.)\nconst REFUND_INPUT_COUNT = 1;\nconst BIP68_ERROR_RE = /non-BIP68-final/i;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\n/**\n * One vault's per-HTLC binding in a Pre-PegIn batch. Carries the fields\n * needed to reconstruct the WASM `WasmPrePeginTx` template byte-for-byte\n * against the funded transaction.\n */\nexport interface VaultBatchEntry {\n /** SHA-256 hashlock commitment for this vault (bytes32, 0x-prefixed). */\n hashlock: Hex;\n /** HTLC output value in satoshis for this vault. */\n amount: bigint;\n /** Index of this vault's HTLC output in the funded Pre-PegIn tx. */\n htlcVout: number;\n}\n\n/**\n * Authoritative vault fields needed to build a refund. Versioning fields,\n * the hashlock, and htlcVout must come from the on-chain contract (never the\n * indexer). The amount + `unsignedPrePeginTxHex` + `depositorBtcPubkey` can\n * come from the indexer since they are not security-critical for signing\n * (the PSBT builder re-derives the HTLC script from on-chain params).\n *\n * `batch` is the full, vout-ordered HTLC vector for the Pre-PegIn (one\n * entry per sibling vault that shares this funded transaction). For a\n * single-vault deposit this is a length-1 array. For batched deposits\n * (e.g. the Aave split) the orchestrator passes every sibling through\n * so the WASM template matches the funded tx's shape.\n */\nexport interface VaultRefundData {\n hashlock: Hex;\n htlcVout: number;\n offchainParamsVersion: number;\n appVaultKeepersVersion: number;\n universalChallengersVersion: number;\n vaultProvider: Address;\n applicationEntryPoint: Address;\n /** Pre-PegIn HTLC output value in satoshis. */\n amount: bigint;\n /**\n * Funded, pre-witness Pre-PegIn transaction hex. 0x prefix optional.\n * The name mirrors the contract/indexer schema; the bytes are the\n * funded form (refund construction needs real outpoints).\n */\n unsignedPrePeginTxHex: string;\n /** Depositor's BTC public key (x-only or compressed hex; 0x prefix optional). */\n depositorBtcPubkey: string;\n /**\n * Full vout-ordered HTLC vector for the funded Pre-PegIn (one entry\n * per sibling vault, including the target vault). Must satisfy\n * `batch[i].htlcVout === i` for all i, and the target's `htlcVout` /\n * `hashlock` / `amount` must equal `batch[vault.htlcVout]`.\n */\n batch: ReadonlyArray<VaultBatchEntry>;\n}\n\n/**\n * Version-resolved protocol context that parameterises the HTLC's taproot\n * scripts. The *signer-set* fields (`vaultKeeperPubkeys`,\n * `universalChallengerPubkeys`) and the version-locked numeric protocol\n * params **must** be sourced from the on-chain contract at the version\n * pinned in {@link VaultRefundData} — this is the trust boundary.\n * `vaultProviderPubkey` today is sourced from the GraphQL indexer via\n * `fetchVaultProviderById`; the caller is responsible for any additional\n * cross-check it requires. Keeper and challenger pubkey arrays must be\n * pre-sorted the same way the Rust protocol sorts them (canonical for\n * script derivation).\n */\nexport interface RefundPrePeginContext {\n vaultProviderPubkey: string;\n vaultKeeperPubkeys: readonly string[];\n universalChallengerPubkeys: readonly string[];\n timelockRefund: number;\n feeRate: bigint;\n minPeginFeeRate: bigint;\n numLocalChallengers: number;\n councilQuorum: number;\n councilSize: number;\n network: Network;\n}\n\n/** Minimum shape required from a broadcast result. */\nexport interface BtcBroadcastResult {\n txId: string;\n}\n\nexport type BtcBroadcaster<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> = (signedTxHex: string) => Promise<R>;\n\nexport type RefundPsbtSigner = (\n psbtHex: string,\n opts: SignPsbtOptions,\n) => Promise<string>;\n\nexport interface RefundInput<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> {\n vaultId: Hex;\n /**\n * Fetch authoritative on-chain + indexer vault data. The SDK passes no\n * arguments — the caller closes over `vaultId` (or any other context it\n * needs).\n */\n readVault: () => Promise<VaultRefundData>;\n /**\n * Fetch the version-pinned refund context (sorted pubkeys, timelock, etc.)\n * derived from the vault's locked versions.\n */\n readPrePeginContext: (\n vault: VaultRefundData,\n ) => Promise<RefundPrePeginContext>;\n /**\n * Mempool-derived sat/vB fee rate to use for the refund tx (positive\n * number). Caller fetches this before invoking — it does not depend on\n * any value the SDK computes, and folding it into the call keeps the\n * orchestration honest.\n */\n feeRate: number;\n /** BTC wallet signer; receives a PSBT hex + taproot script-path options. */\n signPsbt: RefundPsbtSigner;\n /** Broadcast callback — returns whatever shape the caller needs. */\n broadcastTx: BtcBroadcaster<R>;\n /** Checked at every async boundary. */\n signal?: AbortSignal;\n}\n\nfunction assertNonNegativeInteger(value: number, label: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer, got ${value}`);\n }\n}\n\nfunction validateVaultRefundData(v: VaultRefundData): void {\n assertBytes32(v.hashlock, \"hashlock\");\n if (!Number.isInteger(v.htlcVout) || v.htlcVout < 0) {\n throw new Error(\n `htlcVout must be a non-negative integer, got ${v.htlcVout}`,\n );\n }\n // Batch shape — one entry per sibling HTLC, vout-ordered and\n // contiguous from 0. The reconstructed WASM template uses these\n // arrays directly: any gap, duplicate, or mis-ordering against the\n // funded tx would produce an unspendable refund. The target's\n // (hashlock, amount, htlcVout) must equal the corresponding batch\n // entry so the orchestrator and the caller can't disagree about\n // which output is being refunded.\n if (!Array.isArray(v.batch) || v.batch.length === 0) {\n throw new Error(\"batch must be a non-empty array of HTLC entries\");\n }\n if (v.htlcVout >= v.batch.length) {\n throw new Error(\n `htlcVout ${v.htlcVout} is out of range for batch of size ${v.batch.length}`,\n );\n }\n for (let i = 0; i < v.batch.length; i++) {\n const entry = v.batch[i];\n assertBytes32(entry.hashlock, `batch[${i}].hashlock`);\n if (!Number.isInteger(entry.htlcVout) || entry.htlcVout !== i) {\n throw new Error(\n `batch[${i}].htlcVout must equal ${i} (contiguous vout-ordered vector), got ${entry.htlcVout}`,\n );\n }\n if (typeof entry.amount !== \"bigint\" || entry.amount <= 0n) {\n throw new Error(\n `batch[${i}].amount must be a positive bigint, got ${entry.amount}`,\n );\n }\n }\n const targetEntry = v.batch[v.htlcVout];\n if (targetEntry.hashlock.toLowerCase() !== v.hashlock.toLowerCase()) {\n throw new Error(\n `batch[${v.htlcVout}].hashlock (${targetEntry.hashlock}) does not match target hashlock (${v.hashlock})`,\n );\n }\n if (targetEntry.amount !== v.amount) {\n throw new Error(\n `batch[${v.htlcVout}].amount (${targetEntry.amount}) does not match target amount (${v.amount})`,\n );\n }\n // Version fields flow directly into on-chain script derivation via\n // `readPrePeginContext` — NaN, negative, or non-integer values would\n // silently produce wrong scripts. Guard here as defence in depth even\n // though the caller sources these from bigint on-chain reads.\n assertNonNegativeInteger(v.offchainParamsVersion, \"offchainParamsVersion\");\n assertNonNegativeInteger(v.appVaultKeepersVersion, \"appVaultKeepersVersion\");\n assertNonNegativeInteger(\n v.universalChallengersVersion,\n \"universalChallengersVersion\",\n );\n if (typeof v.unsignedPrePeginTxHex !== \"string\" || v.unsignedPrePeginTxHex.length === 0) {\n throw new Error(\"unsignedPrePeginTxHex must be a non-empty hex string\");\n }\n if (!BTC_HEX_BYTES_RE.test(v.unsignedPrePeginTxHex)) {\n throw new Error(\n \"unsignedPrePeginTxHex must be a hex byte string (optional 0x prefix, even length)\",\n );\n }\n if (!v.depositorBtcPubkey || !PUBKEY_HEX_RE.test(v.depositorBtcPubkey)) {\n throw new Error(\n \"depositorBtcPubkey must be 32 or 33 bytes of hex (optional 0x prefix)\",\n );\n }\n if (typeof v.amount !== \"bigint\" || v.amount <= 0n) {\n throw new Error(`amount must be a positive bigint, got ${v.amount}`);\n }\n}\n\nfunction validateRefundPrePeginContext(c: RefundPrePeginContext): void {\n if (!c.vaultProviderPubkey || !PUBKEY_HEX_RE.test(c.vaultProviderPubkey)) {\n throw new Error(\"vaultProviderPubkey must be 32 or 33 bytes of hex\");\n }\n if (c.vaultKeeperPubkeys.length === 0) {\n throw new Error(\"vaultKeeperPubkeys must be non-empty\");\n }\n if (c.universalChallengerPubkeys.length === 0) {\n throw new Error(\"universalChallengerPubkeys must be non-empty\");\n }\n if (!Number.isInteger(c.timelockRefund) || c.timelockRefund <= 0) {\n throw new Error(\n `timelockRefund must be a positive integer, got ${c.timelockRefund}`,\n );\n }\n if (typeof c.feeRate !== \"bigint\" || c.feeRate <= 0n) {\n throw new Error(\n `protocol feeRate must be a positive bigint, got ${c.feeRate}`,\n );\n }\n if (typeof c.minPeginFeeRate !== \"bigint\" || c.minPeginFeeRate <= 0n) {\n throw new Error(\n `minPeginFeeRate must be a positive bigint, got ${c.minPeginFeeRate}`,\n );\n }\n if (\n !Number.isInteger(c.numLocalChallengers) ||\n c.numLocalChallengers < 0\n ) {\n throw new Error(\"numLocalChallengers must be a non-negative integer\");\n }\n if (\n !Number.isInteger(c.councilQuorum) ||\n !Number.isInteger(c.councilSize) ||\n c.councilQuorum <= 0 ||\n c.councilSize <= 0 ||\n c.councilQuorum > c.councilSize\n ) {\n throw new Error(\n `councilQuorum (${c.councilQuorum}) must be in [1, councilSize=${c.councilSize}]`,\n );\n }\n}\n\n/**\n * Re-derive each HTLC's original peg-in amount from its on-chain HTLC output\n * value, inverting the protocol formula\n * `htlcValue = peginAmount + depositorClaimValue + minPeginFee`.\n *\n * The original peg-in amount is not persisted anywhere — only the HTLC output\n * value (`batch[i].amount`) survives on-chain. WASM refund template\n * reconstruction needs the *peg-in amount*, not the HTLC value: feeding the\n * HTLC value would size the template's HTLC output above what the funded tx\n * carries, and `buildRefundPsbt`'s value cross-check would refuse the refund.\n *\n * `depositorClaimValue` and `minPeginFee` are constant across the batch\n * (fixed by the version-pinned protocol params in {@link ctx}), so they are\n * computed once via the same WASM entry points the peg-in path uses, then\n * subtracted from every entry's value. The subtraction is the inverse of the\n * sizing WASM performs internally; `buildRefundPsbt` then re-binds the result\n * to the funded tx bytes, so a wrong derivation fails closed rather than\n * signing a mis-sized refund.\n */\nexport async function deriveRefundPeginAmounts(\n batch: ReadonlyArray<VaultBatchEntry>,\n ctx: RefundPrePeginContext,\n): Promise<bigint[]> {\n const depositorClaimValue = await computeMinClaimValue(\n ctx.numLocalChallengers,\n ctx.universalChallengerPubkeys.length,\n ctx.councilQuorum,\n ctx.councilSize,\n ctx.feeRate,\n );\n const minPeginFee = await computeMinPeginFee(\n ctx.vaultKeeperPubkeys.length,\n ctx.universalChallengerPubkeys.length,\n ctx.minPeginFeeRate,\n );\n const reserved = depositorClaimValue + minPeginFee;\n\n return batch.map((entry, i) => {\n const peginAmount = entry.amount - reserved;\n if (peginAmount <= 0n) {\n throw new Error(\n `Re-derived peginAmount for batch[${i}] is non-positive ` +\n `(${peginAmount}): HTLC value ${entry.amount} does not exceed ` +\n `depositorClaimValue ${depositorClaimValue} + minPeginFee ` +\n `${minPeginFee}. Refusing to build a refund from an inconsistent ` +\n `(amount, protocol params) pair.`,\n );\n }\n return peginAmount;\n });\n}\n\nfunction finalizeAndExtract(signedPsbtHex: string): string {\n const psbt = Psbt.fromHex(signedPsbtHex);\n try {\n psbt.finalizeAllInputs();\n } catch (e: unknown) {\n // Some wallets (e.g. Keystone) finalize during signPsbt; bitcoinjs then\n // throws \"Input is already finalized\". Treat that case as a no-op.\n const message = e instanceof Error ? e.message : String(e);\n if (!message.includes(\"already finalized\")) {\n throw new Error(`Failed to finalize refund PSBT: ${message}`);\n }\n }\n return psbt.extractTransaction().toHex();\n}\n\n/**\n * Build, sign, and broadcast a refund transaction for an expired vault.\n *\n * Trust boundary: `readVault` must source the hashlock, htlcVout, and\n * versioning fields from the on-chain contract — an indexer-only path\n * leaves the refund flow open to signer-set substitution. The SDK does\n * not enforce this; it is the caller's responsibility.\n *\n * The broadcast transport is expected to surface Bitcoin's `non-BIP68-final`\n * policy rejection as an `Error` whose message contains that string; when\n * it does, the SDK wraps it in {@link BIP68NotMatureError}. All other\n * transport errors propagate unchanged.\n *\n * @returns whatever the injected `broadcastTx` returns (generic pass-through)\n * @throws `Error` if any validation fails\n * @throws {@link BIP68NotMatureError} if the broadcast is rejected because\n * the refund CSV timelock has not yet matured\n * @throws anything `readVault`, `readPrePeginContext`,\n * `signPsbt`, or `broadcastTx` throws\n */\nexport async function buildAndBroadcastRefund<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n>(input: RefundInput<R>): Promise<R> {\n const {\n vaultId,\n readVault,\n readPrePeginContext,\n feeRate,\n signPsbt,\n broadcastTx,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n assertBytes32(vaultId, \"vaultId\");\n\n const vault = await readVault();\n validateVaultRefundData(vault);\n signal?.throwIfAborted();\n\n const ctx = await readPrePeginContext(vault);\n validateRefundPrePeginContext(ctx);\n signal?.throwIfAborted();\n\n if (!Number.isFinite(feeRate) || feeRate <= 0) {\n throw new Error(`feeRate must be a positive number, got ${feeRate}`);\n }\n // Rate cap: fail closed before PSBT construction if the seeded value\n // exceeds the safety ceiling. A compromised mempool API (or upstream\n // proxy / BGP hijack) can otherwise drive `halfHourFee` to the API's\n // 10_000 sat/vB ceiling and burn the refund as miner fee.\n if (feeRate > REFUND_MAX_FEE_RATE_SATS_VB) {\n throw new Error(\n `feeRate ${feeRate} sat/vB exceeds refund safety cap ` +\n `${REFUND_MAX_FEE_RATE_SATS_VB} sat/vB; refusing to sign refund.`,\n );\n }\n const refundFee = BigInt(Math.ceil(feeRate * REFUND_VSIZE));\n // Fraction cap: even within the rate ceiling, refuse to sign if the\n // absolute fee would consume more than the configured percentage of the\n // vault amount. Protects small vaults from disproportionate burn.\n const maxFeeByFraction =\n (vault.amount * REFUND_MAX_FEE_FRACTION_NUMERATOR) /\n REFUND_MAX_FEE_FRACTION_DENOMINATOR;\n if (refundFee > maxFeeByFraction) {\n throw new Error(\n `Refund fee ${refundFee} sats exceeds the per-vault safety cap ` +\n `of ${maxFeeByFraction} sats ` +\n `(${REFUND_MAX_FEE_FRACTION_NUMERATOR}/${REFUND_MAX_FEE_FRACTION_DENOMINATOR} ` +\n `of vault.amount=${vault.amount}); refusing to sign refund.`,\n );\n }\n signal?.throwIfAborted();\n\n // `vault.depositorBtcPubkey` may arrive as wallet-native compressed sec1\n // (33 bytes) because the caller fetches it live from the wallet for\n // signing. WASM script derivation wants x-only (32 bytes), so normalize\n // here; the raw form is kept for the wallet sign call below.\n const xOnlyDepositorPubkey = processPublicKeyToXOnly(\n vault.depositorBtcPubkey,\n );\n\n const cleanFundedPrePeginTxHex = stripHexPrefix(vault.unsignedPrePeginTxHex);\n\n // Production peg-ins (PeginManager) commit an OP_RETURN <PUSH32\n // SHA256(authAnchor)> output at `vout = hashlocks.length`. The\n // reconstructed unfunded template carries `batch.length` HTLC outputs,\n // so the OP_RETURN — when present — must sit at exactly that vout.\n // Legacy non-auth-anchored Pre-PegIns return `undefined` from the\n // finder; the template then has no OP_RETURN either, which is a\n // matching configuration.\n const found = findAuthAnchorOpReturn(cleanFundedPrePeginTxHex);\n if (found !== undefined && found.vout !== vault.batch.length) {\n throw new Error(\n `Auth-anchor OP_RETURN at vout ${found.vout} does not match batch size ` +\n `(${vault.batch.length} HTLC outputs expect the anchor at vout ${vault.batch.length}). ` +\n `Refund refused — sibling HTLC vector is incomplete.`,\n );\n }\n const authAnchorHash = found?.hash;\n\n // Independent structural check on the funded tx: it must carry at\n // least N HTLC outputs (one per batch entry). If the anchor is\n // present we've already pinned its position above, which transitively\n // proves the tx has ≥ N+1 outputs; if the anchor is absent (legacy)\n // we still need ≥ N to spend `htlcVout = N-1`.\n let parsedFundedTx: Transaction;\n try {\n parsedFundedTx = Transaction.fromHex(cleanFundedPrePeginTxHex);\n } catch (e) {\n throw new Error(\n `Failed to parse funded Pre-PegIn transaction hex: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n if (parsedFundedTx.outs.length < vault.batch.length) {\n throw new Error(\n `Funded Pre-PegIn tx has ${parsedFundedTx.outs.length} outputs but batch ` +\n `requires at least ${vault.batch.length} HTLC outputs. ` +\n `Refund refused — funded tx shape disagrees with sibling vector.`,\n );\n }\n\n // Re-derive the original peg-in amounts from the on-chain HTLC values.\n // `batch[i].amount` is the HTLC *output* value, not the peg-in amount the\n // WASM template constructor expects; feeding it verbatim mis-sizes the\n // template. The derivation is bound back to the funded tx bytes by\n // `buildRefundPsbt`'s value cross-check, so an inconsistent result fails\n // closed instead of producing a mis-sized refund.\n const refundPeginAmounts = await deriveRefundPeginAmounts(vault.batch, ctx);\n signal?.throwIfAborted();\n\n const { psbtHex } = await buildRefundPsbt({\n prePeginParams: {\n depositorPubkey: xOnlyDepositorPubkey,\n vaultProviderPubkey: stripHexPrefix(ctx.vaultProviderPubkey),\n vaultKeeperPubkeys: ctx.vaultKeeperPubkeys.map(stripHexPrefix),\n universalChallengerPubkeys:\n ctx.universalChallengerPubkeys.map(stripHexPrefix),\n hashlocks: vault.batch.map((b) => stripHexPrefix(b.hashlock)),\n timelockRefund: ctx.timelockRefund,\n pegInAmounts: refundPeginAmounts,\n feeRate: ctx.feeRate,\n minPeginFeeRate: ctx.minPeginFeeRate,\n numLocalChallengers: ctx.numLocalChallengers,\n councilQuorum: ctx.councilQuorum,\n councilSize: ctx.councilSize,\n network: ctx.network,\n authAnchorHash,\n },\n fundedPrePeginTxHex: cleanFundedPrePeginTxHex,\n htlcVout: vault.htlcVout,\n refundFee,\n // buildRefundPsbt's top-level `hashlock` param is documented as \"no 0x\n // prefix\" and flows into the WASM HTLC connector derivation; a prefixed\n // value would derive the wrong refund script leaf and yield an\n // unspendable PSBT. Match the `hashlocks` array handling above.\n hashlock: stripHexPrefix(vault.hashlock),\n });\n signal?.throwIfAborted();\n\n const signOptions = createTaprootScriptPathSignOptions(\n vault.depositorBtcPubkey,\n REFUND_INPUT_COUNT,\n );\n const signedPsbtHex = await signPsbt(psbtHex, signOptions);\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n const signedTxHex = finalizeAndExtract(signedPsbtHex);\n signal?.throwIfAborted();\n\n try {\n return await broadcastTx(signedTxHex);\n } catch (error) {\n if (error instanceof Error && BIP68_ERROR_RE.test(error.message)) {\n throw new BIP68NotMatureError(vaultId, error);\n }\n throw error;\n }\n}\n"],"names":["BYTES32_HEX_RE","ADDRESS_HEX_RE","ETH_HEX_BYTES_RE","assertBytes32","value","label","assertAddress","assertHexBytes","activateVault","input","btcVaultRegistryAddress","vaultId","hashlock","activationMetadata","writeContract","signal","normalizedSecret","ensureHexPrefix","validateSecretAgainstHashlock","BTCVaultRegistryABI","DEFAULT_POLL_INTERVAL_MS","waitForPeginStatus","params","statusReader","peginTxid","targetStatuses","timeoutMs","pollIntervalMs","startTime","response","status","DaemonStatus","VP_TERMINAL_FAILURE_STATUSES","error","JsonRpcError","RpcErrorCode","resolve","reject","onAbort","timeoutId","STATUS_POLL_TIMEOUT_MS","TARGET_STATUSES","POST_WOTS_STATUSES","submitWotsPublicKey","wotsSubmitter","depositorPk","wotsPublicKeys","DEPOSITOR_SIGNED_INPUT_COUNT","DEPOSITOR_PATH_UNUSED_COMMISSION_BPS","deriveLocalChallengers","vaultKeeperBtcPubkeys","depositorBtcPubkey","depositor","stripHexPrefix","filtered","k","assertChallengerSetMatchesExpected","challengerPresignData","localChallengers","universalChallengerBtcPubkeys","universal","overlap","expected","suppliedList","suppliedSet","expectedSet","missing","extra","readInputTxid","tx","inputIndex","uint8ArrayToHex","assertInputReferencesParent","noPayoutTx","parentTx","parentLabel","challengerPubkey","parentTxid","inputTxid","collectDepositorGraphPsbts","depositorGraph","walletPublicKey","ctx","psbtHexes","signOptions","challengerEntries","builtPayout","buildPayoutPsbt","createTaprootScriptPathSignOptions","claimerPubkey","assertTxParsed","Transaction","challenger","noPayoutIdx","noPayoutHex","buildLocalNoPayoutPsbt","assertNoPayoutOutputMatchesChallenger","challengeAssertXTx","challengeAssertYTx","prevouts","out","buildNoPayoutPsbt","extractDepositorGraphSignatures","psbtPairs","depositorPubkey","assertPsbtUnsignedTxMatches","payoutSignature","extractPayoutSignature","perChallenger","entry","signPsbtsWithFallback","wallet","options","signed","i","signDepositorGraph","btcWallet","signingContext","validateWalletPubkey","signedPsbtHexes","requestedPsbtHex","MAX_POLLING_TIMEOUT_MS","POST_PAYOUT_STATUSES","TARGET_STATUS","prepareTransactionsForSigning","claimerTransactions","processPublicKeyToXOnly","normalizeClaimerPubkey","pubkey","assertNonDepositorClaimerSetMatches","suppliedTxs","expectedVpPubkey","expectedVkPubkeys","depositorPubkeyXOnly","expectedList","suppliedAll","suppliedNonDepositor","c","buildPayoutSigningInput","context","signPayoutTransactions","transactions","onProgress","payoutManager","PayoutManager","totalClaimers","payoutSignatures","r","result","signatures","runDepositorPresignFlow","presignClient","depositorPkNormalized","nonDepositorTxs","preparedTransactions","claimerSignatures","depositorClaimerPresignatures","allSignatures","isValidXOnlyHex","hex","isDepositAmountValid","amountSats","minDeposit","maxDeposit","btcBalance","estimatedFeeSats","depositorClaimValue","validateDepositAmount","amount","formatSatoshisToBtc","validateRemainingCapacity","effectiveRemaining","validateProviderSelection","selectedProviders","availableProviders","availableProvidersLower","p","validateVaultAmounts","amounts","validateVaultProviderPubkey","stripped","validateVaultKeepers","validateUniversalChallengers","validateUTXOState","confirmedUTXOs","validateMultiVaultDepositInputs","vaultAmounts","vaultProviderBtcPubkey","amountsValidation","pubkeyValidation","validateOnChainParticipantKeys","vaultRegistryReader","vaultKeeperReader","universalChallengerReader","vaultProviderEthAddress","applicationEntryPoint","expectedVaultProviderBtcPubkey","expectedVaultKeeperBtcPubkeys","expectedUniversalChallengerBtcPubkeys","onChainVpKey","expectedAppVaultKeepersVersion","expectedUniversalChallengersVersion","onChainKeepers","onChainChallengers","canonical","sortedSet","keys","expectedKeepers","onChainKeepersSorted","expectedChallengers","onChainChallengersSorted","RegisteredVaultVersionMismatchError","message","isRegisteredVaultVersionMismatchError","err","verifyRegisteredVaultVersions","vaultIds","expectedOffchainParamsVersion","infos","mismatches","v","id","ClaimerPegoutStatusValue","PEGOUT_TERMINAL_STATUSES","isRecognizedPegoutStatus","isPegoutTerminalStatus","claimerStatus","BIP68NotMatureError","cause","__publicField","BTC_HEX_BYTES_RE","PUBKEY_HEX_RE","REFUND_VSIZE","REFUND_MAX_FEE_RATE_SATS_VB","REFUND_MAX_FEE_FRACTION_NUMERATOR","REFUND_MAX_FEE_FRACTION_DENOMINATOR","estimateRefundFeeSats","feeRateSatsVb","REFUND_INPUT_COUNT","BIP68_ERROR_RE","assertNonNegativeInteger","validateVaultRefundData","targetEntry","validateRefundPrePeginContext","deriveRefundPeginAmounts","batch","computeMinClaimValue","minPeginFee","computeMinPeginFee","reserved","peginAmount","finalizeAndExtract","signedPsbtHex","psbt","Psbt","e","buildAndBroadcastRefund","readVault","readPrePeginContext","feeRate","signPsbt","broadcastTx","vault","refundFee","maxFeeByFraction","xOnlyDepositorPubkey","cleanFundedPrePeginTxHex","found","findAuthAnchorOpReturn","authAnchorHash","parsedFundedTx","refundPeginAmounts","psbtHex","buildRefundPsbt","b","signedTxHex"],"mappings":"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"}