@babylonlabs-io/ts-sdk 0.71.0 → 0.71.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 (94) hide show
  1. package/dist/PeginManager-Bn8FxM35.cjs +2 -0
  2. package/dist/{PeginManager-BIKAnZ-X.cjs.map → PeginManager-Bn8FxM35.cjs.map} +1 -1
  3. package/dist/{PeginManager-Byvmccdd.js → PeginManager-C3ojNTlv.js} +6 -6
  4. package/dist/{PeginManager-Byvmccdd.js.map → PeginManager-C3ojNTlv.js.map} +1 -1
  5. package/dist/{buildAndBroadcastReclaim-C0TdwO2M.js → buildAndBroadcastReclaim-YvWDRAN8.js} +492 -489
  6. package/dist/{buildAndBroadcastReclaim-C0TdwO2M.js.map → buildAndBroadcastReclaim-YvWDRAN8.js.map} +1 -1
  7. package/dist/buildAndBroadcastReclaim-zfjDI5__.cjs +2 -0
  8. package/dist/{buildAndBroadcastReclaim-kRx7-o2y.cjs.map → buildAndBroadcastReclaim-zfjDI5__.cjs.map} +1 -1
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.js +227 -226
  11. package/dist/{mempoolApi-CRNMOsbC.js → mempoolApi-CKD_IbrM.js} +32 -33
  12. package/dist/mempoolApi-CKD_IbrM.js.map +1 -0
  13. package/dist/mempoolApi-DronQ5xn.cjs +2 -0
  14. package/dist/mempoolApi-DronQ5xn.cjs.map +1 -0
  15. package/dist/noPayout-DpGRzidi.js +353 -0
  16. package/dist/noPayout-DpGRzidi.js.map +1 -0
  17. package/dist/noPayout-zEifyecV.cjs +2 -0
  18. package/dist/noPayout-zEifyecV.cjs.map +1 -0
  19. package/dist/{primeVpAuth-tEXx9TGx.cjs → primeVpAuth-Cjq6qFHq.cjs} +2 -2
  20. package/dist/primeVpAuth-Cjq6qFHq.cjs.map +1 -0
  21. package/dist/{primeVpAuth-D1qbOkhb.js → primeVpAuth-tLRjdHwZ.js} +2 -2
  22. package/dist/primeVpAuth-tLRjdHwZ.js.map +1 -0
  23. package/dist/{signPsbtsWithFallback-BjXdnI0X.js → signPsbtsWithFallback-CCFTzE0o.js} +3 -3
  24. package/dist/{signPsbtsWithFallback-BjXdnI0X.js.map → signPsbtsWithFallback-CCFTzE0o.js.map} +1 -1
  25. package/dist/{signPsbtsWithFallback-zOQWokmk.cjs → signPsbtsWithFallback-CrkvpSJz.cjs} +2 -2
  26. package/dist/{signPsbtsWithFallback-zOQWokmk.cjs.map → signPsbtsWithFallback-CrkvpSJz.cjs.map} +1 -1
  27. package/dist/signing-BJ3X66b-.cjs +2 -0
  28. package/dist/signing-BJ3X66b-.cjs.map +1 -0
  29. package/dist/signing-Cm-McVR2.js +22 -0
  30. package/dist/signing-Cm-McVR2.js.map +1 -0
  31. package/dist/tbv/core/clients/eth/types.d.ts +0 -2
  32. package/dist/tbv/core/clients/eth/types.d.ts.map +1 -1
  33. package/dist/tbv/core/clients/eth/vault-registry-reader.d.ts.map +1 -1
  34. package/dist/tbv/core/clients/index.cjs +1 -1
  35. package/dist/tbv/core/clients/index.js +2 -2
  36. package/dist/tbv/core/clients/mempool/mempoolApi.d.ts +9 -1
  37. package/dist/tbv/core/clients/mempool/mempoolApi.d.ts.map +1 -1
  38. package/dist/tbv/core/index.cjs +1 -1
  39. package/dist/tbv/core/index.js +222 -221
  40. package/dist/tbv/core/managers/index.cjs +1 -1
  41. package/dist/tbv/core/managers/index.js +2 -2
  42. package/dist/tbv/core/primitives/index.cjs +1 -1
  43. package/dist/tbv/core/primitives/index.d.ts +2 -0
  44. package/dist/tbv/core/primitives/index.d.ts.map +1 -1
  45. package/dist/tbv/core/primitives/index.js +23 -22
  46. package/dist/tbv/core/primitives/psbt/__tests__/finalizeScriptPathWithSignatures.test.d.ts +11 -0
  47. package/dist/tbv/core/primitives/psbt/__tests__/finalizeScriptPathWithSignatures.test.d.ts.map +1 -0
  48. package/dist/tbv/core/primitives/psbt/finalizeScriptPathWithSignatures.d.ts +52 -0
  49. package/dist/tbv/core/primitives/psbt/finalizeScriptPathWithSignatures.d.ts.map +1 -0
  50. package/dist/tbv/core/primitives/psbt/reclaim.d.ts +23 -1
  51. package/dist/tbv/core/primitives/psbt/reclaim.d.ts.map +1 -1
  52. package/dist/tbv/core/services/index.cjs +1 -1
  53. package/dist/tbv/core/services/index.js +2 -2
  54. package/dist/tbv/core/services/reclaim/__tests__/buildAndBroadcastReclaim.walletPsbt.test.d.ts +14 -0
  55. package/dist/tbv/core/services/reclaim/__tests__/buildAndBroadcastReclaim.walletPsbt.test.d.ts.map +1 -0
  56. package/dist/tbv/core/services/reclaim/buildAndBroadcastReclaim.d.ts +14 -2
  57. package/dist/tbv/core/services/reclaim/buildAndBroadcastReclaim.d.ts.map +1 -1
  58. package/dist/tbv/core/services/refund/buildAndBroadcastRefund.d.ts.map +1 -1
  59. package/dist/tbv/core/utils/index.cjs +1 -1
  60. package/dist/tbv/core/utils/index.js +13 -13
  61. package/dist/tbv/index.cjs +1 -1
  62. package/dist/tbv/index.js +222 -221
  63. package/dist/toRefundInputs-CY9u84U9.cjs +2 -0
  64. package/dist/{toRefundInputs-Bdtz8p27.cjs.map → toRefundInputs-CY9u84U9.cjs.map} +1 -1
  65. package/dist/{toRefundInputs-CgXMcHrk.js → toRefundInputs-Df1vi23S.js} +5 -5
  66. package/dist/{toRefundInputs-CgXMcHrk.js.map → toRefundInputs-Df1vi23S.js.map} +1 -1
  67. package/dist/{verifyScriptPathSchnorrSignature-mzYCzaSy.cjs → verifyScriptPathSchnorrSignature-BfNUSgA7.cjs} +2 -2
  68. package/dist/{verifyScriptPathSchnorrSignature-mzYCzaSy.cjs.map → verifyScriptPathSchnorrSignature-BfNUSgA7.cjs.map} +1 -1
  69. package/dist/{verifyScriptPathSchnorrSignature-ZAZfFJ6S.js → verifyScriptPathSchnorrSignature-Ciq_xZhw.js} +5 -4
  70. package/dist/{verifyScriptPathSchnorrSignature-ZAZfFJ6S.js.map → verifyScriptPathSchnorrSignature-Ciq_xZhw.js.map} +1 -1
  71. package/dist/waitForTransactionReceiptSmartAware-DpQ69ySe.cjs +2 -0
  72. package/dist/waitForTransactionReceiptSmartAware-DpQ69ySe.cjs.map +1 -0
  73. package/dist/{waitForTransactionReceiptSmartAware-B6oYRdFO.js → waitForTransactionReceiptSmartAware-z3Xfmo44.js} +42 -47
  74. package/dist/waitForTransactionReceiptSmartAware-z3Xfmo44.js.map +1 -0
  75. package/package.json +4 -3
  76. package/dist/PeginManager-BIKAnZ-X.cjs +0 -2
  77. package/dist/buildAndBroadcastReclaim-kRx7-o2y.cjs +0 -2
  78. package/dist/mempoolApi-BQllf-ji.cjs +0 -2
  79. package/dist/mempoolApi-BQllf-ji.cjs.map +0 -1
  80. package/dist/mempoolApi-CRNMOsbC.js.map +0 -1
  81. package/dist/noPayout--lxo7qe4.js +0 -300
  82. package/dist/noPayout--lxo7qe4.js.map +0 -1
  83. package/dist/noPayout-CegnEwfp.cjs +0 -2
  84. package/dist/noPayout-CegnEwfp.cjs.map +0 -1
  85. package/dist/primeVpAuth-D1qbOkhb.js.map +0 -1
  86. package/dist/primeVpAuth-tEXx9TGx.cjs.map +0 -1
  87. package/dist/signing-Bnsro0hE.cjs +0 -2
  88. package/dist/signing-Bnsro0hE.cjs.map +0 -1
  89. package/dist/signing-DaLvGwQe.js +0 -16
  90. package/dist/signing-DaLvGwQe.js.map +0 -1
  91. package/dist/toRefundInputs-Bdtz8p27.cjs +0 -2
  92. package/dist/waitForTransactionReceiptSmartAware-B6oYRdFO.js.map +0 -1
  93. package/dist/waitForTransactionReceiptSmartAware-Ct7LPwPI.cjs +0 -2
  94. package/dist/waitForTransactionReceiptSmartAware-Ct7LPwPI.cjs.map +0 -1
@@ -0,0 +1,2 @@
1
+ "use strict";const I=require("bitcoinjs-lib"),p=require("buffer"),h=require("./fundPeginTransaction-BLD8FRL9.cjs");function E(t){const{numInputs:e,numOutputs:n,feeRate:a}=t;if(!Number.isInteger(e)||e<0)throw new Error(`computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${e}`);if(!Number.isInteger(n)||n<1)throw new Error(`computePeginBaseFeeSats: numOutputs must be a positive integer, got ${n}`);const s=e*h.P2TR_INPUT_SIZE+n*h.MAX_NON_LEGACY_OUTPUT_SIZE+h.TX_BUFFER_SIZE_OVERHEAD;return BigInt(Math.ceil(s*a))+BigInt(h.rateBasedTxBufferFee(a))}function w(t){return BigInt(Math.ceil(h.MAX_NON_LEGACY_OUTPUT_SIZE*t))}function T(t){const{totalInputValue:e,peginAmount:n,baseFee:a,changeOutputFee:s}=t,i=e-n-a;if(i<0n)throw new Error(`applyChangeOutputPolicy: insufficient funds (need ${n+a} sats, have ${e})`);const c=i-s;return c>h.DUST_THRESHOLD?{fee:a+s,changeAmount:c,emitChangeOutput:!0}:{fee:a+i,changeAmount:0n,emitChangeOutput:!1}}function P(t){const{numInputs:e,numOutputs:n,totalBalance:a,feeRate:s}=t;if(a<=0n)return null;const i=E({numInputs:e,numOutputs:n,feeRate:s}),c=a-i;return c>0n?c:0n}function O(t){const e=new Set;for(const n of t){const a=`${n.txid.toLowerCase()}:${n.vout}`;if(e.has(a))throw new Error(`Duplicate UTXO detected: ${n.txid}:${n.vout}. This indicates a data integrity issue with the UTXO source.`);e.add(a)}}function A(t,e,n,a){if(!Number.isInteger(a)||a<1)throw new Error(`Invalid numOutputs: expected a positive integer, got ${a}`);if(t.length===0)throw new Error("Insufficient funds: no UTXOs available");O(t);const s=t.filter(f=>{const r=p.Buffer.from(f.scriptPubKey,"hex");return!!I.script.decompile(r)});if(s.length===0)throw new Error("Insufficient funds: no valid UTXOs available (all have invalid scripts)");const i=[...s].sort((f,r)=>r.value-f.value),c=[];let l=0n,u=0n;for(const f of i){c.push(f),l+=BigInt(f.value);const r=E({numInputs:c.length,numOutputs:a,feeRate:n}),o=w(n);if(l<e+r){u=r;continue}const S=T({totalInputValue:l,peginAmount:e,baseFee:r,changeOutputFee:o});return{selectedUTXOs:c,totalValue:l,fee:S.fee,changeAmount:S.changeAmount}}throw new Error(`Insufficient funds: need ${e+u} sats (${e} pegin + ${u} fee), have ${l} sats`)}function F(t){return t>h.DUST_THRESHOLD}function U(){return h.BTC_DUST_SAT}var d=(t=>(t.P2PKH="P2PKH",t.P2SH="P2SH",t.P2WPKH="P2WPKH",t.P2WSH="P2WSH",t.P2TR="P2TR",t.UNKNOWN="UNKNOWN",t))(d||{});function _(t){const e=t.length;return e===25&&t[0]===118&&t[1]===169&&t[2]===20&&t[23]===136&&t[24]===172?"P2PKH":e===23&&t[0]===169&&t[1]===20&&t[22]===135?"P2SH":e===22&&t[0]===0&&t[1]===20?"P2WPKH":e===34&&t[0]===0&&t[1]===32?"P2WSH":e===34&&t[0]===81&&t[1]===32?"P2TR":"UNKNOWN"}function R(t,e){const n=p.Buffer.from(t.scriptPubKey,"hex"),a=_(n);switch(a){case d.P2WPKH:return{witnessUtxo:{script:n,value:t.value}};case d.P2WSH:{if(!t.witnessScript)throw new Error("Missing witnessScript for P2WSH input");return{witnessUtxo:{script:n,value:t.value},witnessScript:p.Buffer.from(t.witnessScript,"hex")}}case d.P2TR:{if(e&&e.length!==32)throw new Error(`Invalid tapInternalKey length: expected 32 bytes, got ${e.length}`);return{witnessUtxo:{script:n,value:t.value},...e&&{tapInternalKey:e}}}default:throw new Error(`Unsupported script type: ${a}`)}}const v={1:"https://safe-transaction-mainnet.safe.global",11155111:"https://safe-transaction-sepolia.safe.global"},$=5e3,C=14400*1e3,D=1e4,H="/api/v1/multisig-transactions",N=404,L=500,x=2,m="0xef0100",B=20,X=m.length+x*B;function W(t){return t.length===X&&t.toLowerCase().startsWith(m)}async function M(t){const{publicClient:e,walletAddress:n,hash:a,confirmations:s,timeout:i,safePollTimeoutMs:c=C,safePollIntervalMs:l=$}=t,u=await e.getCode({address:n});if(!(u!==void 0&&u!=="0x"&&!W(u)))return e.waitForTransactionReceipt({hash:a,confirmations:s,timeout:i});const r=await e.getChainId(),o=await b({chainId:r,publicClient:e,safeTxHash:a,pollIntervalMs:l,timeoutMs:c});return o.kind==="not-a-proposal"?e.waitForTransactionReceipt({hash:a,confirmations:s,timeout:i}):e.waitForTransactionReceipt({hash:o.transactionHash,confirmations:s})}async function V(t,e){return await t.getTransaction({hash:e}).catch(()=>null)!==null}async function b({chainId:t,publicClient:e,safeTxHash:n,pollIntervalMs:a,timeoutMs:s}){const i=v[t];if(!i)throw new Error(`Safe Transaction Service not configured for chainId ${t}. Connected wallet appears to be a smart-contract account, but this chain is not in the supported list. Either connect an EOA or extend SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`);const c=`${i}${H}/${n}/`,l=Date.now()+s;for(;Date.now()<l;){const u=new AbortController,f=setTimeout(()=>u.abort(),D);let r;try{r=await fetch(c,{signal:u.signal})}catch(o){console.warn(`Safe Transaction Service request failed (will retry in ${a}ms): `+(o instanceof Error?o.message:String(o))),await g(a);continue}finally{clearTimeout(f)}if(r.ok){const o=await r.json();if(o.isExecuted){if(o.isSuccessful===!1)throw new Error(`Safe transaction ${n} was executed on chain but reverted. Check the Safe queue UI for details.`);if(o.transactionHash)return{kind:"executed",transactionHash:o.transactionHash}}}else if(r.status===N){if(await V(e,n))return console.warn(`${n} is a real transaction, not a Safe proposal — the connected wallet submits its own transactions despite reporting contract bytecode. Waiting for its receipt directly.`),{kind:"not-a-proposal"}}else if(r.status>=L)console.warn(`Safe Transaction Service returned ${r.status} for ${n}; retrying in ${a}ms.`);else throw new Error(`Safe Transaction Service returned ${r.status} for ${n}.`);await g(a)}throw new Error(`Timed out after ${s}ms waiting for Safe transaction ${n} to reach quorum and execute. The proposal is still pending in the Safe queue — co-signers must sign and execute it before the dApp can proceed.`)}function g(t){return new Promise(e=>{setTimeout(e,t)})}exports.BitcoinScriptType=d;exports.applyChangeOutputPolicy=T;exports.computeChangeOutputFeeSats=w;exports.computeMaxDeposit=P;exports.computePeginBaseFeeSats=E;exports.getDustThreshold=U;exports.getPsbtInputFields=R;exports.getScriptType=_;exports.selectUtxosForPegin=A;exports.shouldAddChangeOutput=F;exports.waitForTransactionReceiptSmartAware=M;
2
+ //# sourceMappingURL=waitForTransactionReceiptSmartAware-DpQ69ySe.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitForTransactionReceiptSmartAware-DpQ69ySe.cjs","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts","../src/tbv/core/utils/eth/waitForTransactionReceiptSmartAware.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n","/**\n * Smart-account-aware wrapper around viem's `waitForTransactionReceipt`.\n *\n * Externally Owned Accounts (EOAs) — wallets controlled by a single private\n * key, e.g. MetaMask or a hardware wallet. `eth_sendTransaction` returns a real\n * Ethereum tx hash, which viem can poll directly. This wrapper delegates\n * unchanged for them.\n *\n * Smart-contract accounts (e.g. Safe multisigs) — the wallet address is a\n * deployed contract that decides whether to accept a transaction. WalletConnect's\n * `eth_sendTransaction` returns a `safeTxHash` (an EIP-712 hash of the\n * *proposal*) rather than a real tx hash, and the proposal is held in Safe's\n * off-chain Transaction Service until quorum signs and executes it. We poll\n * that service for the proposal until execution, then wait for receipt on the\n * real Ethereum tx hash exposed in the service's response.\n *\n * The two are told apart by `eth_getCode`, but NOT by \"empty vs non-empty\":\n * an EIP-7702 delegated EOA reports `0xef0100 ‖ <delegate address>` and is\n * still an EOA — it signs and submits its own transactions, so\n * `eth_sendTransaction` returns a real tx hash. MetaMask upgrades accounts to\n * smart accounts this way by default, and treating one as a Safe means polling\n * the Transaction Service for a hash that is not a `safeTxHash`: a permanent\n * 404 that reads as \"proposal not yet indexed\" and hangs for the full poll\n * budget on a transaction that already succeeded. Delegation designators are\n * therefore classified as EOAs. As a backstop for any wallet that behaves this\n * way in future, a 404 from the Transaction Service is also checked against the\n * node: if it knows the hash as a real transaction, we were never waiting on a\n * proposal and switch to waiting for that transaction's receipt.\n *\n * @module utils/eth\n */\n\nimport type { Address, Hash, PublicClient, TransactionReceipt } from \"viem\";\n\n/**\n * Chains where the Safe Transaction Service is supported by this utility.\n * Extend the map as more Safe-enabled chains are needed.\n */\nconst SAFE_TX_SERVICE_BASE_URLS: Record<number, string> = {\n 1: \"https://safe-transaction-mainnet.safe.global\",\n 11155111: \"https://safe-transaction-sepolia.safe.global\",\n};\n\nconst DEFAULT_SAFE_POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_SAFE_POLL_TIMEOUT_MS = 4 * 60 * 60 * 1_000;\nconst SAFE_TX_SERVICE_FETCH_TIMEOUT_MS = 10_000;\n\n/** Safe Transaction Service route for one multisig-transaction proposal. */\nconst SAFE_MULTISIG_TX_PATH = \"/api/v1/multisig-transactions\";\n/** The service answers 404 both for an unindexed proposal and an unknown Safe. */\nconst SAFE_TX_SERVICE_NOT_FOUND = 404;\n/** Statuses at or above this are server-side, so worth retrying. */\nconst SAFE_TX_SERVICE_SERVER_ERROR = 500;\n\n/** Two hex characters encode one byte in an `eth_getCode` result. */\nconst HEX_CHARS_PER_BYTE = 2;\n\n/**\n * EIP-7702 delegation designator: an EOA that has delegated its code to a\n * contract reports exactly `0xef0100 ‖ <20-byte delegate address>` from\n * `eth_getCode` (EIP-7702 §\"Delegation Designation\"). Both the prefix and the\n * exact length are checked so ordinary contract bytecode that merely happens to\n * start with these bytes is not mistaken for a delegation.\n */\nconst EIP_7702_DELEGATION_PREFIX = \"0xef0100\";\nconst EIP_7702_DELEGATION_ADDRESS_BYTES = 20;\nconst EIP_7702_DELEGATION_CODE_LENGTH =\n EIP_7702_DELEGATION_PREFIX.length +\n HEX_CHARS_PER_BYTE * EIP_7702_DELEGATION_ADDRESS_BYTES;\n\n/**\n * True when `eth_getCode` reports an EIP-7702 delegation designator rather than\n * a deployed contract. Such an account is still an EOA for our purposes: it\n * signs and submits its own transactions, so the hash we hold is a real tx hash.\n */\nfunction isEip7702DelegatedEoa(code: string): boolean {\n return (\n code.length === EIP_7702_DELEGATION_CODE_LENGTH &&\n code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)\n );\n}\n\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n /**\n * Forwarded to viem verbatim.\n *\n * MUST NOT be used as a finality/reorg gate. viem fetches the receipt once\n * and then only compares block numbers against that cached copy — it never\n * re-checks that the receipt's block is still canonical, so it resolves\n * happily for a transaction that has been reorged out. Setting this buys a\n * delay, not a guarantee. For peg-in registration finality use\n * `waitForPeginRegistrationDepth`, which re-reads live contract state on\n * every poll.\n */\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path, and on the\n * fallback where a supposed smart account turns out to submit its own\n * transactions — that is the EOA case too, however we arrived at it.\n *\n * Ignored only while waiting on a genuine Safe proposal, whose budget is\n * safePollTimeoutMs. It is not forwarded to the receipt wait that follows a\n * proposal's execution either: viem's own default applies there (180s as of\n * viem 2.38.2), so that wait is bounded too — a sufficiently slow node still\n * raises WaitForTransactionReceiptTimeoutError, just on viem's uniform bound\n * rather than on a caller's shorter one.\n */\n timeout?: number;\n /** Total budget for waiting on Safe quorum + execution. Default 4h. */\n safePollTimeoutMs?: number;\n /** Poll cadence against the Safe Transaction Service. Default 5s. */\n safePollIntervalMs?: number;\n}\n\nexport async function waitForTransactionReceiptSmartAware(\n params: WaitForTransactionReceiptSmartAwareParams,\n): Promise<TransactionReceipt> {\n const {\n publicClient,\n walletAddress,\n hash,\n confirmations,\n timeout,\n safePollTimeoutMs = DEFAULT_SAFE_POLL_TIMEOUT_MS,\n safePollIntervalMs = DEFAULT_SAFE_POLL_INTERVAL_MS,\n } = params;\n\n const code = await publicClient.getCode({ address: walletAddress });\n // An EIP-7702 delegated EOA has non-empty code but still submits its own\n // transactions, so it belongs on the EOA path — see the module docblock.\n const isSmartAccount =\n code !== undefined && code !== \"0x\" && !isEip7702DelegatedEoa(code);\n\n if (!isSmartAccount) {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n const chainId = await publicClient.getChainId();\n const outcome = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n publicClient,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n // The wallet turned out to submit its own transactions, so this is the EOA\n // case after all — including the caller's `timeout`, which only ever meant to\n // be ignored while waiting on a genuine Safe proposal.\n if (outcome.kind === \"not-a-proposal\") {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n return publicClient.waitForTransactionReceipt({\n hash: outcome.transactionHash,\n confirmations,\n });\n}\n\n/**\n * How the Safe path ended. The two outcomes are not interchangeable, so they are\n * distinguished rather than both collapsing to a bare hash: `executed` is a real\n * Safe proposal that reached quorum, while `not-a-proposal` means the wallet was\n * never a Safe and we are really on the EOA path — which decides whether the\n * caller's `timeout` applies to the receipt wait that follows.\n */\ntype SafePollOutcome =\n | { kind: \"executed\"; transactionHash: Hash }\n | { kind: \"not-a-proposal\" };\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\n/**\n * Decide whether a 404 from the Transaction Service means \"proposal not indexed\n * yet\" or \"this hash was never a proposal at all\".\n *\n * The Safe indexer cannot answer that: a genuine Safe and its freshly-submitted\n * proposal are both absent from it for a while, so its 404 proves nothing. The\n * NODE can. A `safeTxHash` is an EIP-712 digest of a proposal and is never a\n * transaction on chain, so if the node knows the hash as a real transaction then\n * this was never a proposal and we are on the wrong path entirely.\n *\n * Returns true only on that positive identification. Anything else — the node\n * has not seen it yet, or the lookup fails — is inconclusive and leaves the\n * caller polling, which is the correct behaviour for a Safe that is simply\n * still being indexed.\n */\nasync function hashIsRealTransaction(\n publicClient: PublicClient,\n hash: Hash,\n): Promise<boolean> {\n const transaction = await publicClient\n .getTransaction({ hash })\n .catch(() => null);\n return transaction !== null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n publicClient,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n publicClient: PublicClient;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<SafePollOutcome> {\n const baseUrl = SAFE_TX_SERVICE_BASE_URLS[chainId];\n if (!baseUrl) {\n throw new Error(\n `Safe Transaction Service not configured for chainId ${chainId}. ` +\n `Connected wallet appears to be a smart-contract account, but this ` +\n `chain is not in the supported list. Either connect an EOA or extend ` +\n `SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`,\n );\n }\n\n const url = `${baseUrl}${SAFE_MULTISIG_TX_PATH}/${safeTxHash}/`;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const controller = new AbortController();\n const fetchTimeoutId = setTimeout(\n () => controller.abort(),\n SAFE_TX_SERVICE_FETCH_TIMEOUT_MS,\n );\n\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (err) {\n // Transient failure (AbortError on per-request timeout, DNS hiccup,\n // connection reset, etc.). Log and continue to the next poll iteration\n // instead of consuming the entire safePollTimeoutMs budget on one blip.\n // The outer `while (Date.now() < deadline)` is what enforces the overall\n // budget; this catch deliberately preserves it.\n console.warn(\n `Safe Transaction Service request failed (will retry in ${pollIntervalMs}ms): ` +\n (err instanceof Error ? err.message : String(err)),\n );\n await sleep(pollIntervalMs);\n continue;\n } finally {\n clearTimeout(fetchTimeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as SafeMultisigTransaction;\n if (data.isExecuted) {\n if (data.isSuccessful === false) {\n throw new Error(\n `Safe transaction ${safeTxHash} was executed on chain but reverted. ` +\n `Check the Safe queue UI for details.`,\n );\n }\n if (data.transactionHash) {\n return { kind: \"executed\", transactionHash: data.transactionHash };\n }\n }\n } else if (response.status === SAFE_TX_SERVICE_NOT_FOUND) {\n // Usually \"proposal not indexed yet\", so keep polling. But the very same\n // 404 appears when the hash is not a safeTxHash at all — the case that\n // used to burn the whole budget on an already-mined transaction. Ask the\n // node: if it knows this hash, it is a real transaction, so stop treating\n // it as a proposal and let the caller wait on it directly.\n if (await hashIsRealTransaction(publicClient, safeTxHash)) {\n console.warn(\n `${safeTxHash} is a real transaction, not a Safe proposal — the ` +\n `connected wallet submits its own transactions despite reporting ` +\n `contract bytecode. Waiting for its receipt directly.`,\n );\n return { kind: \"not-a-proposal\" };\n }\n } else if (response.status >= SAFE_TX_SERVICE_SERVER_ERROR) {\n // Transient server error — same treatment as a hung connection: log and retry.\n console.warn(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}; retrying in ${pollIntervalMs}ms.`,\n );\n } else {\n // Other 4xx (403, 410, etc.) is likely permanent — surface immediately.\n throw new Error(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}.`,\n );\n }\n\n await sleep(pollIntervalMs);\n }\n\n throw new Error(\n `Timed out after ${timeoutMs}ms waiting for Safe transaction ${safeTxHash} ` +\n `to reach quorum and execute. The proposal is still pending in the Safe ` +\n `queue — co-signers must sign and execute it before the dApp can proceed.`,\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type","SAFE_TX_SERVICE_BASE_URLS","DEFAULT_SAFE_POLL_INTERVAL_MS","DEFAULT_SAFE_POLL_TIMEOUT_MS","SAFE_TX_SERVICE_FETCH_TIMEOUT_MS","SAFE_MULTISIG_TX_PATH","SAFE_TX_SERVICE_NOT_FOUND","SAFE_TX_SERVICE_SERVER_ERROR","HEX_CHARS_PER_BYTE","EIP_7702_DELEGATION_PREFIX","EIP_7702_DELEGATION_ADDRESS_BYTES","EIP_7702_DELEGATION_CODE_LENGTH","isEip7702DelegatedEoa","code","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","chainId","outcome","pollSafeTransactionServiceUntilExecuted","hashIsRealTransaction","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { script as T, Transaction as _ } from "bitcoinjs-lib";
1
+ import { script as _ } from "bitcoinjs-lib";
2
2
  import { Buffer as d } from "buffer";
3
- import { D as p, M as S, r as I, P, T as A, B as O } from "./fundPeginTransaction-B2wyjqp0.js";
3
+ import { D as p, M as S, r as T, P as I, T as P, B as A } from "./fundPeginTransaction-B2wyjqp0.js";
4
4
  function g(t) {
5
5
  const { numInputs: e, numOutputs: n, feeRate: a } = t;
6
6
  if (!Number.isInteger(e) || e < 0)
@@ -11,13 +11,13 @@ function g(t) {
11
11
  throw new Error(
12
12
  `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${n}`
13
13
  );
14
- const r = e * P + n * S + A;
15
- return BigInt(Math.ceil(r * a)) + BigInt(I(a));
14
+ const r = e * I + n * S + P;
15
+ return BigInt(Math.ceil(r * a)) + BigInt(T(a));
16
16
  }
17
- function F(t) {
17
+ function O(t) {
18
18
  return BigInt(Math.ceil(S * t));
19
19
  }
20
- function R(t) {
20
+ function F(t) {
21
21
  const { totalInputValue: e, peginAmount: n, baseFee: a, changeOutputFee: r } = t, i = e - n - a;
22
22
  if (i < 0n)
23
23
  throw new Error(
@@ -34,13 +34,13 @@ function R(t) {
34
34
  emitChangeOutput: !1
35
35
  };
36
36
  }
37
- function Y(t) {
37
+ function q(t) {
38
38
  const { numInputs: e, numOutputs: n, totalBalance: a, feeRate: r } = t;
39
39
  if (a <= 0n) return null;
40
40
  const i = g({ numInputs: e, numOutputs: n, feeRate: r }), c = a - i;
41
41
  return c > 0n ? c : 0n;
42
42
  }
43
- function U(t) {
43
+ function R(t) {
44
44
  const e = /* @__PURE__ */ new Set();
45
45
  for (const n of t) {
46
46
  const a = `${n.txid.toLowerCase()}:${n.vout}`;
@@ -51,17 +51,17 @@ function U(t) {
51
51
  e.add(a);
52
52
  }
53
53
  }
54
- function Z(t, e, n, a) {
54
+ function Y(t, e, n, a) {
55
55
  if (!Number.isInteger(a) || a < 1)
56
56
  throw new Error(
57
57
  `Invalid numOutputs: expected a positive integer, got ${a}`
58
58
  );
59
59
  if (t.length === 0)
60
60
  throw new Error("Insufficient funds: no UTXOs available");
61
- U(t);
61
+ R(t);
62
62
  const r = t.filter((f) => {
63
63
  const s = d.from(f.scriptPubKey, "hex");
64
- return !!T.decompile(s);
64
+ return !!_.decompile(s);
65
65
  });
66
66
  if (r.length === 0)
67
67
  throw new Error(
@@ -75,12 +75,12 @@ function Z(t, e, n, a) {
75
75
  numInputs: c.length,
76
76
  numOutputs: a,
77
77
  feeRate: n
78
- }), o = F(n);
78
+ }), o = O(n);
79
79
  if (l < e + s) {
80
80
  u = s;
81
81
  continue;
82
82
  }
83
- const E = R({
83
+ const E = F({
84
84
  totalInputValue: l,
85
85
  peginAmount: e,
86
86
  baseFee: s,
@@ -97,18 +97,14 @@ function Z(t, e, n, a) {
97
97
  `Insufficient funds: need ${e + u} sats (${e} pegin + ${u} fee), have ${l} sats`
98
98
  );
99
99
  }
100
- function y(t) {
100
+ function Z(t) {
101
101
  return t > p;
102
102
  }
103
- function j() {
104
- return O;
105
- }
106
- function z(t) {
107
- const e = t.startsWith("0x") ? t.slice(2) : t;
108
- return `0x${_.fromHex(e).getId()}`;
103
+ function y() {
104
+ return A;
109
105
  }
110
106
  var h = /* @__PURE__ */ ((t) => (t.P2PKH = "P2PKH", t.P2SH = "P2SH", t.P2WPKH = "P2WPKH", t.P2WSH = "P2WSH", t.P2TR = "P2TR", t.UNKNOWN = "UNKNOWN", t))(h || {});
111
- function $(t) {
107
+ function U(t) {
112
108
  const e = t.length;
113
109
  return e === 25 && t[0] === 118 && // OP_DUP
114
110
  t[1] === 169 && // OP_HASH160
@@ -121,8 +117,8 @@ function $(t) {
121
117
  t[1] === 32 ? "P2WSH" : e === 34 && t[0] === 81 && // OP_1
122
118
  t[1] === 32 ? "P2TR" : "UNKNOWN";
123
119
  }
124
- function J(t, e) {
125
- const n = d.from(t.scriptPubKey, "hex"), a = $(n);
120
+ function j(t, e) {
121
+ const n = d.from(t.scriptPubKey, "hex"), a = U(n);
126
122
  switch (a) {
127
123
  case h.P2WPKH:
128
124
  return {
@@ -163,27 +159,27 @@ function J(t, e) {
163
159
  const v = {
164
160
  1: "https://safe-transaction-mainnet.safe.global",
165
161
  11155111: "https://safe-transaction-sepolia.safe.global"
166
- }, x = 5e3, H = 14400 * 1e3, C = 1e4, D = "/api/v1/multisig-transactions", N = 404, L = 500, B = 2, m = "0xef0100", W = 20, X = m.length + B * W;
167
- function V(t) {
168
- return t.length === X && t.toLowerCase().startsWith(m);
162
+ }, $ = 5e3, C = 14400 * 1e3, H = 1e4, D = "/api/v1/multisig-transactions", N = 404, x = 500, L = 2, m = "0xef0100", B = 20, W = m.length + L * B;
163
+ function X(t) {
164
+ return t.length === W && t.toLowerCase().startsWith(m);
169
165
  }
170
- async function Q(t) {
166
+ async function z(t) {
171
167
  const {
172
168
  publicClient: e,
173
169
  walletAddress: n,
174
170
  hash: a,
175
171
  confirmations: r,
176
172
  timeout: i,
177
- safePollTimeoutMs: c = H,
178
- safePollIntervalMs: l = x
173
+ safePollTimeoutMs: c = C,
174
+ safePollIntervalMs: l = $
179
175
  } = t, u = await e.getCode({ address: n });
180
- if (!(u !== void 0 && u !== "0x" && !V(u)))
176
+ if (!(u !== void 0 && u !== "0x" && !X(u)))
181
177
  return e.waitForTransactionReceipt({
182
178
  hash: a,
183
179
  confirmations: r,
184
180
  timeout: i
185
181
  });
186
- const s = await e.getChainId(), o = await b({
182
+ const s = await e.getChainId(), o = await M({
187
183
  chainId: s,
188
184
  publicClient: e,
189
185
  safeTxHash: a,
@@ -199,10 +195,10 @@ async function Q(t) {
199
195
  confirmations: r
200
196
  });
201
197
  }
202
- async function M(t, e) {
198
+ async function V(t, e) {
203
199
  return await t.getTransaction({ hash: e }).catch(() => null) !== null;
204
200
  }
205
- async function b({
201
+ async function M({
206
202
  chainId: t,
207
203
  publicClient: e,
208
204
  safeTxHash: n,
@@ -218,7 +214,7 @@ async function b({
218
214
  for (; Date.now() < l; ) {
219
215
  const u = new AbortController(), f = setTimeout(
220
216
  () => u.abort(),
221
- C
217
+ H
222
218
  );
223
219
  let s;
224
220
  try {
@@ -242,11 +238,11 @@ async function b({
242
238
  return { kind: "executed", transactionHash: o.transactionHash };
243
239
  }
244
240
  } else if (s.status === N) {
245
- if (await M(e, n))
241
+ if (await V(e, n))
246
242
  return console.warn(
247
243
  `${n} is a real transaction, not a Safe proposal — the connected wallet submits its own transactions despite reporting contract bytecode. Waiting for its receipt directly.`
248
244
  ), { kind: "not-a-proposal" };
249
- } else if (s.status >= L)
245
+ } else if (s.status >= x)
250
246
  console.warn(
251
247
  `Safe Transaction Service returned ${s.status} for ${n}; retrying in ${a}ms.`
252
248
  );
@@ -267,16 +263,15 @@ function w(t) {
267
263
  }
268
264
  export {
269
265
  h as B,
270
- R as a,
271
- Y as b,
272
- F as c,
266
+ F as a,
267
+ q as b,
268
+ O as c,
273
269
  g as d,
274
- y as e,
275
- z as f,
276
- j as g,
277
- $ as h,
278
- J as i,
279
- Z as s,
280
- Q as w
270
+ Z as e,
271
+ U as f,
272
+ y as g,
273
+ j as h,
274
+ Y as s,
275
+ z as w
281
276
  };
282
- //# sourceMappingURL=waitForTransactionReceiptSmartAware-B6oYRdFO.js.map
277
+ //# sourceMappingURL=waitForTransactionReceiptSmartAware-z3Xfmo44.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waitForTransactionReceiptSmartAware-z3Xfmo44.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/btc/scriptType.ts","../src/tbv/core/utils/btc/psbtInputFields.ts","../src/tbv/core/utils/eth/waitForTransactionReceiptSmartAware.ts"],"sourcesContent":["/**\n * Pre-PegIn fee math primitives used by both UTXO selection and\n * transaction funding so they make bit-identical decisions about base\n * fee, change-output fee, and whether to emit change at all.\n *\n * Dust handling matches the wallet-side check in\n * `babylon-vault crates/btc-wallet-remote/src/client.rs` (dust-change\n * rejection): a change output is emitted only when the post-fee residual\n * exceeds DUST_THRESHOLD (546 sats). Broader fee-estimation behaviors\n * (output sizing, safety margins) are NOT cross-stack guarantees — see\n * JS-vs-Rust parity fixtures in `__tests__/peginFeeMath.test.ts` for the\n * invariants we pin.\n */\n\nimport {\n DUST_THRESHOLD,\n MAX_NON_LEGACY_OUTPUT_SIZE,\n P2TR_INPUT_SIZE,\n rateBasedTxBufferFee,\n TX_BUFFER_SIZE_OVERHEAD,\n} from \"./constants\";\n\nexport interface ComputeBaseFeeParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction (HTLC vault outputs +\n * CPFP anchor + optional auth-anchor OP_RETURN). Excludes the change\n * output — `applyChangeOutputPolicy` adds the change-output fee\n * separately.\n */\n numOutputs: number;\n feeRate: number;\n}\n\n/**\n * Compute the base fee (sats) for a Pre-PegIn transaction with no change\n * output, including the low-fee-rate buffer.\n *\n * Used as the starting point by `applyChangeOutputPolicy`, which then\n * decides whether to add the incremental change-output fee.\n */\nexport function computePeginBaseFeeSats(\n params: ComputeBaseFeeParams,\n): bigint {\n const { numInputs, numOutputs, feeRate } = params;\n if (!Number.isInteger(numInputs) || numInputs < 0) {\n throw new Error(\n `computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${numInputs}`,\n );\n }\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `computePeginBaseFeeSats: numOutputs must be a positive integer, got ${numOutputs}`,\n );\n }\n const txVsize =\n numInputs * P2TR_INPUT_SIZE +\n numOutputs * MAX_NON_LEGACY_OUTPUT_SIZE +\n TX_BUFFER_SIZE_OVERHEAD;\n return (\n BigInt(Math.ceil(txVsize * feeRate)) +\n BigInt(rateBasedTxBufferFee(feeRate))\n );\n}\n\n/**\n * Incremental fee (sats) for adding one P2TR-sized change output at the\n * given fee rate. Does NOT include the low-fee-rate buffer — that is part\n * of the base fee, paid once per transaction.\n */\nexport function computeChangeOutputFeeSats(feeRate: number): bigint {\n return BigInt(Math.ceil(MAX_NON_LEGACY_OUTPUT_SIZE * feeRate));\n}\n\nexport interface ApplyChangeOutputPolicyParams {\n totalInputValue: bigint;\n peginAmount: bigint;\n baseFee: bigint;\n changeOutputFee: bigint;\n}\n\nexport interface ChangeOutputPolicyResult {\n /** Final transaction fee (sats). */\n fee: bigint;\n /**\n * Final change amount (sats). 0n when no change output is emitted.\n * When `emitChangeOutput` is false, the would-be change is paid to\n * miners as part of `fee` — i.e. it is dust by policy.\n */\n changeAmount: bigint;\n /** Whether the funded transaction must include a change output. */\n emitChangeOutput: boolean;\n}\n\n/**\n * Apply the change-output dust policy: emit a change output iff the\n * post-change-output-fee residual strictly exceeds DUST_THRESHOLD.\n *\n * Returns `{ fee, changeAmount, emitChangeOutput }` so the selector and\n * funder both end up with the same fee and same change decision for the\n * same inputs.\n *\n * Inputs:\n * - `totalInputValue`: sum of selected UTXO values\n * - `peginAmount`: amount being pegged in\n * - `baseFee`: fee assuming no change output (from `computePeginBaseFeeSats`)\n * - `changeOutputFee`: incremental fee for adding one change output\n * (from `computeChangeOutputFeeSats`)\n *\n * @throws If `totalInputValue < peginAmount + baseFee` (insufficient funds\n * even before considering change). Callers that need to surface\n * \"insufficient funds\" with their own error wording should check the\n * precondition themselves before invoking this.\n */\nexport function applyChangeOutputPolicy(\n params: ApplyChangeOutputPolicyParams,\n): ChangeOutputPolicyResult {\n const { totalInputValue, peginAmount, baseFee, changeOutputFee } = params;\n\n const residualBeforeChange = totalInputValue - peginAmount - baseFee;\n if (residualBeforeChange < 0n) {\n throw new Error(\n `applyChangeOutputPolicy: insufficient funds (need ${peginAmount + baseFee} sats, have ${totalInputValue})`,\n );\n }\n\n const residualWithChangeOutput = residualBeforeChange - changeOutputFee;\n if (residualWithChangeOutput > DUST_THRESHOLD) {\n return {\n fee: baseFee + changeOutputFee,\n changeAmount: residualWithChangeOutput,\n emitChangeOutput: true,\n };\n }\n\n // Dust-revert: the would-be change is below (or equal to) the dust\n // threshold once the change-output fee is paid, so we omit the change\n // output and let the residual go to miners. The reported `fee` is the\n // ACTUAL on-wire fee — `baseFee + residualBeforeChange` — not just\n // `baseFee`, otherwise fee displays would under-report by up to\n // (changeOutputFee + DUST_THRESHOLD) sats whenever dust gets absorbed.\n return {\n fee: baseFee + residualBeforeChange,\n changeAmount: 0n,\n emitChangeOutput: false,\n };\n}\n\nexport interface ComputeMaxDepositParams {\n numInputs: number;\n /**\n * Number of outputs in the unfunded transaction. Use the worst-case\n * count for the use case being budgeted (e.g. max-batch with\n * auth-anchor) — `computeMaxDeposit` is intentionally an UPPER BOUND\n * and assumes no change output.\n */\n numOutputs: number;\n totalBalance: bigint;\n feeRate: number;\n}\n\n/**\n * Compute the maximum depositable amount (sats) given a fixed-cost\n * sweep: every UTXO is spent, no change output is emitted, fee is the\n * base fee for the requested input/output count.\n *\n * Returns null when `totalBalance <= 0n`. Returns 0n if the base fee\n * alone exceeds the balance.\n */\nexport function computeMaxDeposit(\n params: ComputeMaxDepositParams,\n): bigint | null {\n const { numInputs, numOutputs, totalBalance, feeRate } = params;\n if (totalBalance <= 0n) return null;\n const fee = computePeginBaseFeeSats({ numInputs, numOutputs, feeRate });\n const max = totalBalance - fee;\n return max > 0n ? max : 0n;\n}\n","/**\n * UTXO selection utilities for peg-in transactions.\n * Follows btc-staking-ts methodology with iterative fee calculation.\n */\n\nimport { script as bitcoinScript } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { BTC_DUST_SAT, DUST_THRESHOLD } from \"../fee/constants\";\nimport {\n applyChangeOutputPolicy,\n computeChangeOutputFeeSats,\n computePeginBaseFeeSats,\n} from \"../fee/peginFeeMath\";\n\n/**\n * Unspent Transaction Output (UTXO) for funding peg-in transactions.\n */\nexport interface UTXO {\n /**\n * Transaction ID of the UTXO (64-char hex without 0x prefix).\n */\n txid: string;\n\n /**\n * Output index within the transaction.\n */\n vout: number;\n\n /**\n * Value in satoshis.\n */\n value: number;\n\n /**\n * Script public key hex.\n */\n scriptPubKey: string;\n}\n\nexport interface UTXOSelectionResult {\n selectedUTXOs: UTXO[];\n totalValue: bigint;\n fee: bigint;\n changeAmount: bigint;\n}\n\n/**\n * Assert that no two UTXOs share the same txid:vout outpoint.\n * Duplicates from a buggy or compromised UTXO source would produce\n * an invalid Bitcoin transaction that double-spends the same outpoint.\n */\nfunction assertNoDuplicateUtxos(utxos: UTXO[]): void {\n const seen = new Set<string>();\n for (const utxo of utxos) {\n const key = `${utxo.txid.toLowerCase()}:${utxo.vout}`;\n if (seen.has(key)) {\n throw new Error(\n `Duplicate UTXO detected: ${utxo.txid}:${utxo.vout}. ` +\n `This indicates a data integrity issue with the UTXO source.`,\n );\n }\n seen.add(key);\n }\n}\n\n/**\n * Selects UTXOs to fund a peg-in transaction with iterative fee calculation.\n *\n * This function implements the btc-staking-ts approach:\n * 1. Filter UTXOs for script validity (no minimum value filter)\n * 2. Sort by value (largest first) to minimize number of inputs\n * 3. Iteratively add UTXOs and recalculate fee until we have enough\n *\n * The fee recalculation is critical because:\n * - Each UTXO added increases transaction size → increases fee\n * - More fee needed might require another UTXO\n * - Change output detection affects fee (adds output size if needed)\n *\n * @param availableUTXOs - All available UTXOs from wallet\n * @param peginAmount - Amount to peg in (satoshis)\n * @param feeRate - Fee rate (sat/vbyte)\n * @param numOutputs - Number of outputs in the unfunded transaction (HTLC + CPFP anchor, before change)\n * @returns Selected UTXOs, total value, calculated fee, and change amount\n * @throws Error if insufficient funds or no valid UTXOs\n */\nexport function selectUtxosForPegin(\n availableUTXOs: UTXO[],\n peginAmount: bigint,\n feeRate: number,\n numOutputs: number,\n): UTXOSelectionResult {\n if (!Number.isInteger(numOutputs) || numOutputs < 1) {\n throw new Error(\n `Invalid numOutputs: expected a positive integer, got ${numOutputs}`,\n );\n }\n\n if (availableUTXOs.length === 0) {\n throw new Error(\"Insufficient funds: no UTXOs available\");\n }\n\n assertNoDuplicateUtxos(availableUTXOs);\n\n // Filter for script validity ONLY (matching btc-staking-ts approach)\n // No minimum value filter - we accept any UTXO with valid script\n const validUTXOs = availableUTXOs.filter((utxo) => {\n const script = Buffer.from(utxo.scriptPubKey, \"hex\");\n const decompiledScript = bitcoinScript.decompile(script);\n return !!decompiledScript;\n });\n\n if (validUTXOs.length === 0) {\n throw new Error(\n \"Insufficient funds: no valid UTXOs available (all have invalid scripts)\",\n );\n }\n\n // Sort by value: HIGHEST to LOWEST (use big UTXOs first)\n // Use spread to avoid mutating the original array\n const sortedUTXOs = [...validUTXOs].sort((a, b) => b.value - a.value);\n\n const selectedUTXOs: UTXO[] = [];\n let accumulatedValue = 0n;\n let estimatedFee = 0n;\n\n // Iteratively select UTXOs, recalculating the fee through the shared\n // `applyChangeOutputPolicy` helper so the selector and the funder\n // agree on (fee, change output emission, change amount) for the same\n // inputs. Without that, the funder can omit a change output the\n // selector charged for — silent depositor overpayment at the dust\n // boundary.\n for (const utxo of sortedUTXOs) {\n selectedUTXOs.push(utxo);\n accumulatedValue += BigInt(utxo.value);\n\n const baseFee = computePeginBaseFeeSats({\n numInputs: selectedUTXOs.length,\n numOutputs,\n feeRate,\n });\n const changeOutputFee = computeChangeOutputFeeSats(feeRate);\n\n if (accumulatedValue < peginAmount + baseFee) {\n estimatedFee = baseFee;\n continue;\n }\n\n const policy = applyChangeOutputPolicy({\n totalInputValue: accumulatedValue,\n peginAmount,\n baseFee,\n changeOutputFee,\n });\n\n return {\n selectedUTXOs,\n totalValue: accumulatedValue,\n fee: policy.fee,\n changeAmount: policy.changeAmount,\n };\n }\n\n // If we get here, we don't have enough funds\n throw new Error(\n `Insufficient funds: need ${peginAmount + estimatedFee} sats (${peginAmount} pegin + ${estimatedFee} fee), have ${accumulatedValue} sats`,\n );\n}\n\n/**\n * Checks if change amount is above dust threshold.\n *\n * @param changeAmount - Change amount in satoshis\n * @returns true if change should be added as output, false if it should go to miners\n */\nexport function shouldAddChangeOutput(changeAmount: bigint): boolean {\n return changeAmount > DUST_THRESHOLD;\n}\n\n/**\n * Gets the dust threshold value.\n *\n * @returns Dust threshold in satoshis\n */\nexport function getDustThreshold(): number {\n return BTC_DUST_SAT;\n}\n","/**\n * Bitcoin Script Type Detection\n *\n * Utilities to detect Bitcoin script types for proper PSBT input construction.\n *\n * @module utils/btc/scriptType\n */\n\n/**\n * Bitcoin script types.\n */\nexport enum BitcoinScriptType {\n P2PKH = \"P2PKH\",\n P2SH = \"P2SH\",\n P2WPKH = \"P2WPKH\",\n P2WSH = \"P2WSH\",\n P2TR = \"P2TR\",\n UNKNOWN = \"UNKNOWN\",\n}\n\n/**\n * Detect the type of a Bitcoin script.\n *\n * @param scriptPubKey - The script public key buffer\n * @returns The detected script type\n *\n * @example\n * ```typescript\n * const scriptType = getScriptType(Buffer.from(scriptPubKeyHex, 'hex'));\n * if (scriptType === BitcoinScriptType.P2TR) {\n * // Handle Taproot input\n * }\n * ```\n */\nexport function getScriptType(scriptPubKey: Buffer): BitcoinScriptType {\n const length = scriptPubKey.length;\n\n // P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG (25 bytes)\n if (\n length === 25 &&\n scriptPubKey[0] === 0x76 && // OP_DUP\n scriptPubKey[1] === 0xa9 && // OP_HASH160\n scriptPubKey[2] === 0x14 && // Push 20 bytes\n scriptPubKey[23] === 0x88 && // OP_EQUALVERIFY\n scriptPubKey[24] === 0xac // OP_CHECKSIG\n ) {\n return BitcoinScriptType.P2PKH;\n }\n\n // P2SH: OP_HASH160 <20 bytes> OP_EQUAL (23 bytes)\n if (\n length === 23 &&\n scriptPubKey[0] === 0xa9 && // OP_HASH160\n scriptPubKey[1] === 0x14 && // Push 20 bytes\n scriptPubKey[22] === 0x87 // OP_EQUAL\n ) {\n return BitcoinScriptType.P2SH;\n }\n\n // P2WPKH: OP_0 <20 bytes> (22 bytes)\n if (\n length === 22 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x14 // Push 20 bytes\n ) {\n return BitcoinScriptType.P2WPKH;\n }\n\n // P2WSH: OP_0 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x00 && // OP_0\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2WSH;\n }\n\n // P2TR (Taproot): OP_1 <32 bytes> (34 bytes)\n if (\n length === 34 &&\n scriptPubKey[0] === 0x51 && // OP_1\n scriptPubKey[1] === 0x20 // Push 32 bytes\n ) {\n return BitcoinScriptType.P2TR;\n }\n\n return BitcoinScriptType.UNKNOWN;\n}\n\n","/**\n * PSBT Input Field Construction\n *\n * Constructs the correct PSBT input fields for a given UTXO based on its script type.\n *\n * @module utils/btc/psbtInputFields\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { BitcoinScriptType, getScriptType } from \"./scriptType\";\n\n/**\n * PSBT input fields for supported script types (P2TR, P2WPKH, P2WSH).\n */\nexport interface PsbtInputFields {\n witnessUtxo?: {\n script: Buffer;\n value: number;\n };\n witnessScript?: Buffer;\n tapInternalKey?: Buffer;\n}\n\n/**\n * UTXO information for PSBT construction.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n */\nexport interface UtxoForPsbt {\n /** Transaction ID of the UTXO */\n txid: string;\n /** Output index (vout) of the UTXO */\n vout: number;\n /** Value of the UTXO in satoshis */\n value: number;\n /** ScriptPubKey of the UTXO (hex string) */\n scriptPubKey: string;\n /** Witness script (required for P2WSH) */\n witnessScript?: string;\n}\n\n/**\n * Get PSBT input fields for a given UTXO based on its script type.\n *\n * Only supports Taproot (P2TR) and native SegWit (P2WPKH, P2WSH) script types.\n *\n * @param utxo - The unspent transaction output to process\n * @param publicKeyNoCoord - The x-only public key (32 bytes) for Taproot signing\n * @returns PSBT input fields object containing the necessary data\n * @throws Error if required input data is missing or unsupported script type\n */\nexport function getPsbtInputFields(\n utxo: UtxoForPsbt,\n publicKeyNoCoord?: Buffer,\n): PsbtInputFields {\n const scriptPubKey = Buffer.from(utxo.scriptPubKey, \"hex\");\n const type = getScriptType(scriptPubKey);\n\n switch (type) {\n case BitcoinScriptType.P2WPKH: {\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n };\n }\n\n case BitcoinScriptType.P2WSH: {\n if (!utxo.witnessScript) {\n throw new Error(\"Missing witnessScript for P2WSH input\");\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n witnessScript: Buffer.from(utxo.witnessScript, \"hex\"),\n };\n }\n\n case BitcoinScriptType.P2TR: {\n if (publicKeyNoCoord && publicKeyNoCoord.length !== 32) {\n throw new Error(\n `Invalid tapInternalKey length: expected 32 bytes, got ${publicKeyNoCoord.length}`,\n );\n }\n return {\n witnessUtxo: {\n script: scriptPubKey,\n value: utxo.value,\n },\n // tapInternalKey is needed for Taproot signing\n ...(publicKeyNoCoord && { tapInternalKey: publicKeyNoCoord }),\n };\n }\n\n default:\n throw new Error(`Unsupported script type: ${type}`);\n }\n}\n\n","/**\n * Smart-account-aware wrapper around viem's `waitForTransactionReceipt`.\n *\n * Externally Owned Accounts (EOAs) — wallets controlled by a single private\n * key, e.g. MetaMask or a hardware wallet. `eth_sendTransaction` returns a real\n * Ethereum tx hash, which viem can poll directly. This wrapper delegates\n * unchanged for them.\n *\n * Smart-contract accounts (e.g. Safe multisigs) — the wallet address is a\n * deployed contract that decides whether to accept a transaction. WalletConnect's\n * `eth_sendTransaction` returns a `safeTxHash` (an EIP-712 hash of the\n * *proposal*) rather than a real tx hash, and the proposal is held in Safe's\n * off-chain Transaction Service until quorum signs and executes it. We poll\n * that service for the proposal until execution, then wait for receipt on the\n * real Ethereum tx hash exposed in the service's response.\n *\n * The two are told apart by `eth_getCode`, but NOT by \"empty vs non-empty\":\n * an EIP-7702 delegated EOA reports `0xef0100 ‖ <delegate address>` and is\n * still an EOA — it signs and submits its own transactions, so\n * `eth_sendTransaction` returns a real tx hash. MetaMask upgrades accounts to\n * smart accounts this way by default, and treating one as a Safe means polling\n * the Transaction Service for a hash that is not a `safeTxHash`: a permanent\n * 404 that reads as \"proposal not yet indexed\" and hangs for the full poll\n * budget on a transaction that already succeeded. Delegation designators are\n * therefore classified as EOAs. As a backstop for any wallet that behaves this\n * way in future, a 404 from the Transaction Service is also checked against the\n * node: if it knows the hash as a real transaction, we were never waiting on a\n * proposal and switch to waiting for that transaction's receipt.\n *\n * @module utils/eth\n */\n\nimport type { Address, Hash, PublicClient, TransactionReceipt } from \"viem\";\n\n/**\n * Chains where the Safe Transaction Service is supported by this utility.\n * Extend the map as more Safe-enabled chains are needed.\n */\nconst SAFE_TX_SERVICE_BASE_URLS: Record<number, string> = {\n 1: \"https://safe-transaction-mainnet.safe.global\",\n 11155111: \"https://safe-transaction-sepolia.safe.global\",\n};\n\nconst DEFAULT_SAFE_POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_SAFE_POLL_TIMEOUT_MS = 4 * 60 * 60 * 1_000;\nconst SAFE_TX_SERVICE_FETCH_TIMEOUT_MS = 10_000;\n\n/** Safe Transaction Service route for one multisig-transaction proposal. */\nconst SAFE_MULTISIG_TX_PATH = \"/api/v1/multisig-transactions\";\n/** The service answers 404 both for an unindexed proposal and an unknown Safe. */\nconst SAFE_TX_SERVICE_NOT_FOUND = 404;\n/** Statuses at or above this are server-side, so worth retrying. */\nconst SAFE_TX_SERVICE_SERVER_ERROR = 500;\n\n/** Two hex characters encode one byte in an `eth_getCode` result. */\nconst HEX_CHARS_PER_BYTE = 2;\n\n/**\n * EIP-7702 delegation designator: an EOA that has delegated its code to a\n * contract reports exactly `0xef0100 ‖ <20-byte delegate address>` from\n * `eth_getCode` (EIP-7702 §\"Delegation Designation\"). Both the prefix and the\n * exact length are checked so ordinary contract bytecode that merely happens to\n * start with these bytes is not mistaken for a delegation.\n */\nconst EIP_7702_DELEGATION_PREFIX = \"0xef0100\";\nconst EIP_7702_DELEGATION_ADDRESS_BYTES = 20;\nconst EIP_7702_DELEGATION_CODE_LENGTH =\n EIP_7702_DELEGATION_PREFIX.length +\n HEX_CHARS_PER_BYTE * EIP_7702_DELEGATION_ADDRESS_BYTES;\n\n/**\n * True when `eth_getCode` reports an EIP-7702 delegation designator rather than\n * a deployed contract. Such an account is still an EOA for our purposes: it\n * signs and submits its own transactions, so the hash we hold is a real tx hash.\n */\nfunction isEip7702DelegatedEoa(code: string): boolean {\n return (\n code.length === EIP_7702_DELEGATION_CODE_LENGTH &&\n code.toLowerCase().startsWith(EIP_7702_DELEGATION_PREFIX)\n );\n}\n\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n /**\n * Forwarded to viem verbatim.\n *\n * MUST NOT be used as a finality/reorg gate. viem fetches the receipt once\n * and then only compares block numbers against that cached copy — it never\n * re-checks that the receipt's block is still canonical, so it resolves\n * happily for a transaction that has been reorged out. Setting this buys a\n * delay, not a guarantee. For peg-in registration finality use\n * `waitForPeginRegistrationDepth`, which re-reads live contract state on\n * every poll.\n */\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path, and on the\n * fallback where a supposed smart account turns out to submit its own\n * transactions — that is the EOA case too, however we arrived at it.\n *\n * Ignored only while waiting on a genuine Safe proposal, whose budget is\n * safePollTimeoutMs. It is not forwarded to the receipt wait that follows a\n * proposal's execution either: viem's own default applies there (180s as of\n * viem 2.38.2), so that wait is bounded too — a sufficiently slow node still\n * raises WaitForTransactionReceiptTimeoutError, just on viem's uniform bound\n * rather than on a caller's shorter one.\n */\n timeout?: number;\n /** Total budget for waiting on Safe quorum + execution. Default 4h. */\n safePollTimeoutMs?: number;\n /** Poll cadence against the Safe Transaction Service. Default 5s. */\n safePollIntervalMs?: number;\n}\n\nexport async function waitForTransactionReceiptSmartAware(\n params: WaitForTransactionReceiptSmartAwareParams,\n): Promise<TransactionReceipt> {\n const {\n publicClient,\n walletAddress,\n hash,\n confirmations,\n timeout,\n safePollTimeoutMs = DEFAULT_SAFE_POLL_TIMEOUT_MS,\n safePollIntervalMs = DEFAULT_SAFE_POLL_INTERVAL_MS,\n } = params;\n\n const code = await publicClient.getCode({ address: walletAddress });\n // An EIP-7702 delegated EOA has non-empty code but still submits its own\n // transactions, so it belongs on the EOA path — see the module docblock.\n const isSmartAccount =\n code !== undefined && code !== \"0x\" && !isEip7702DelegatedEoa(code);\n\n if (!isSmartAccount) {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n const chainId = await publicClient.getChainId();\n const outcome = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n publicClient,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n // The wallet turned out to submit its own transactions, so this is the EOA\n // case after all — including the caller's `timeout`, which only ever meant to\n // be ignored while waiting on a genuine Safe proposal.\n if (outcome.kind === \"not-a-proposal\") {\n return publicClient.waitForTransactionReceipt({\n hash,\n confirmations,\n timeout,\n });\n }\n\n return publicClient.waitForTransactionReceipt({\n hash: outcome.transactionHash,\n confirmations,\n });\n}\n\n/**\n * How the Safe path ended. The two outcomes are not interchangeable, so they are\n * distinguished rather than both collapsing to a bare hash: `executed` is a real\n * Safe proposal that reached quorum, while `not-a-proposal` means the wallet was\n * never a Safe and we are really on the EOA path — which decides whether the\n * caller's `timeout` applies to the receipt wait that follows.\n */\ntype SafePollOutcome =\n | { kind: \"executed\"; transactionHash: Hash }\n | { kind: \"not-a-proposal\" };\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\n/**\n * Decide whether a 404 from the Transaction Service means \"proposal not indexed\n * yet\" or \"this hash was never a proposal at all\".\n *\n * The Safe indexer cannot answer that: a genuine Safe and its freshly-submitted\n * proposal are both absent from it for a while, so its 404 proves nothing. The\n * NODE can. A `safeTxHash` is an EIP-712 digest of a proposal and is never a\n * transaction on chain, so if the node knows the hash as a real transaction then\n * this was never a proposal and we are on the wrong path entirely.\n *\n * Returns true only on that positive identification. Anything else — the node\n * has not seen it yet, or the lookup fails — is inconclusive and leaves the\n * caller polling, which is the correct behaviour for a Safe that is simply\n * still being indexed.\n */\nasync function hashIsRealTransaction(\n publicClient: PublicClient,\n hash: Hash,\n): Promise<boolean> {\n const transaction = await publicClient\n .getTransaction({ hash })\n .catch(() => null);\n return transaction !== null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n publicClient,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n publicClient: PublicClient;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<SafePollOutcome> {\n const baseUrl = SAFE_TX_SERVICE_BASE_URLS[chainId];\n if (!baseUrl) {\n throw new Error(\n `Safe Transaction Service not configured for chainId ${chainId}. ` +\n `Connected wallet appears to be a smart-contract account, but this ` +\n `chain is not in the supported list. Either connect an EOA or extend ` +\n `SAFE_TX_SERVICE_BASE_URLS in waitForTransactionReceiptSmartAware.ts.`,\n );\n }\n\n const url = `${baseUrl}${SAFE_MULTISIG_TX_PATH}/${safeTxHash}/`;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const controller = new AbortController();\n const fetchTimeoutId = setTimeout(\n () => controller.abort(),\n SAFE_TX_SERVICE_FETCH_TIMEOUT_MS,\n );\n\n let response: Response;\n try {\n response = await fetch(url, { signal: controller.signal });\n } catch (err) {\n // Transient failure (AbortError on per-request timeout, DNS hiccup,\n // connection reset, etc.). Log and continue to the next poll iteration\n // instead of consuming the entire safePollTimeoutMs budget on one blip.\n // The outer `while (Date.now() < deadline)` is what enforces the overall\n // budget; this catch deliberately preserves it.\n console.warn(\n `Safe Transaction Service request failed (will retry in ${pollIntervalMs}ms): ` +\n (err instanceof Error ? err.message : String(err)),\n );\n await sleep(pollIntervalMs);\n continue;\n } finally {\n clearTimeout(fetchTimeoutId);\n }\n\n if (response.ok) {\n const data = (await response.json()) as SafeMultisigTransaction;\n if (data.isExecuted) {\n if (data.isSuccessful === false) {\n throw new Error(\n `Safe transaction ${safeTxHash} was executed on chain but reverted. ` +\n `Check the Safe queue UI for details.`,\n );\n }\n if (data.transactionHash) {\n return { kind: \"executed\", transactionHash: data.transactionHash };\n }\n }\n } else if (response.status === SAFE_TX_SERVICE_NOT_FOUND) {\n // Usually \"proposal not indexed yet\", so keep polling. But the very same\n // 404 appears when the hash is not a safeTxHash at all — the case that\n // used to burn the whole budget on an already-mined transaction. Ask the\n // node: if it knows this hash, it is a real transaction, so stop treating\n // it as a proposal and let the caller wait on it directly.\n if (await hashIsRealTransaction(publicClient, safeTxHash)) {\n console.warn(\n `${safeTxHash} is a real transaction, not a Safe proposal — the ` +\n `connected wallet submits its own transactions despite reporting ` +\n `contract bytecode. Waiting for its receipt directly.`,\n );\n return { kind: \"not-a-proposal\" };\n }\n } else if (response.status >= SAFE_TX_SERVICE_SERVER_ERROR) {\n // Transient server error — same treatment as a hung connection: log and retry.\n console.warn(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}; retrying in ${pollIntervalMs}ms.`,\n );\n } else {\n // Other 4xx (403, 410, etc.) is likely permanent — surface immediately.\n throw new Error(\n `Safe Transaction Service returned ${response.status} for ${safeTxHash}.`,\n );\n }\n\n await sleep(pollIntervalMs);\n }\n\n throw new Error(\n `Timed out after ${timeoutMs}ms waiting for Safe transaction ${safeTxHash} ` +\n `to reach quorum and execute. The proposal is still pending in the Safe ` +\n `queue — co-signers must sign and execute it before the dApp can proceed.`,\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n"],"names":["computePeginBaseFeeSats","params","numInputs","numOutputs","feeRate","txVsize","P2TR_INPUT_SIZE","MAX_NON_LEGACY_OUTPUT_SIZE","TX_BUFFER_SIZE_OVERHEAD","rateBasedTxBufferFee","computeChangeOutputFeeSats","applyChangeOutputPolicy","totalInputValue","peginAmount","baseFee","changeOutputFee","residualBeforeChange","residualWithChangeOutput","DUST_THRESHOLD","computeMaxDeposit","totalBalance","fee","max","assertNoDuplicateUtxos","utxos","seen","utxo","key","selectUtxosForPegin","availableUTXOs","validUTXOs","script","Buffer","bitcoinScript","sortedUTXOs","a","b","selectedUTXOs","accumulatedValue","estimatedFee","policy","shouldAddChangeOutput","changeAmount","getDustThreshold","BTC_DUST_SAT","BitcoinScriptType","getScriptType","scriptPubKey","length","getPsbtInputFields","publicKeyNoCoord","type","SAFE_TX_SERVICE_BASE_URLS","DEFAULT_SAFE_POLL_INTERVAL_MS","DEFAULT_SAFE_POLL_TIMEOUT_MS","SAFE_TX_SERVICE_FETCH_TIMEOUT_MS","SAFE_MULTISIG_TX_PATH","SAFE_TX_SERVICE_NOT_FOUND","SAFE_TX_SERVICE_SERVER_ERROR","HEX_CHARS_PER_BYTE","EIP_7702_DELEGATION_PREFIX","EIP_7702_DELEGATION_ADDRESS_BYTES","EIP_7702_DELEGATION_CODE_LENGTH","isEip7702DelegatedEoa","code","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","chainId","outcome","pollSafeTransactionServiceUntilExecuted","hashIsRealTransaction","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonlabs-io/ts-sdk",
3
- "version": "0.71.0",
3
+ "version": "0.71.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -106,7 +106,7 @@
106
106
  "license": "ISC",
107
107
  "description": "TypeScript SDK for Babylon protocol integrations",
108
108
  "dependencies": {
109
- "@babylonlabs-io/babylon-tbv-rust-wasm": "0.17.0",
109
+ "@babylonlabs-io/babylon-tbv-rust-wasm": "0.17.1",
110
110
  "@noble/hashes": "2.0.1",
111
111
  "buffer": "6.0.3"
112
112
  },
@@ -141,7 +141,8 @@
141
141
  "format:fix": "prettier --write \"src/**/*.ts\"",
142
142
  "lint": "eslint .",
143
143
  "lint:fix": "eslint . --fix",
144
- "test": "vitest run",
144
+ "test": "pnpm build && pnpm test:built",
145
+ "test:built": "vitest run && node --test tests/wasm-facade.node.mjs",
145
146
  "test:watch": "vitest",
146
147
  "test:ui": "vitest --ui",
147
148
  "test:coverage": "vitest run --coverage",
@@ -1,2 +0,0 @@
1
- "use strict";var De=Object.defineProperty;var We=(s,e,t)=>e in s?De(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var T=(s,e,t)=>We(s,typeof e!="symbol"?e+"":e,t);const b=require("./sha2-DsrLC4NM.cjs"),K=require("bitcoinjs-lib"),W=require("buffer"),F=require("viem"),O=require("./BTCVaultRegistry.abi-z7d_eP22.cjs"),V=require("./errors-CGcNP0rV.cjs"),X=require("./tbv/core/wasm/index.cjs"),M=require("./verifyScriptPathSchnorrSignature-mzYCzaSy.cjs"),L=require("./verifyKeyPathSchnorrSignature-BedSbH2D.cjs"),h=require("./bitcoin-D-O2GjbC.cjs"),ee=require("./validation-C6XTfZak.cjs"),Ne=require("./signing-Bnsro0hE.cjs"),ie=require("./parseFundingOutpoints-BkgGihaY.cjs"),I=require("./signPsbtsWithFallback-zOQWokmk.cjs"),R=require("./waitForTransactionReceiptSmartAware-Ct7LPwPI.cjs"),he=require("./fundPeginTransaction-BLD8FRL9.cjs"),q=require("./constants-Bh38W9mq.cjs"),$=require("./mempoolApi-BQllf-ji.cjs"),Ue=require("@bitcoin-js/tiny-secp256k1-asmjs");function xe(s){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const t in s)if(t!=="default"){const o=Object.getOwnPropertyDescriptor(s,t);Object.defineProperty(e,t,o.get?o:{enumerable:!0,get:()=>s[t]})}}return e.default=s,Object.freeze(e)}const we=xe(K),Le=xe(Ue),G=BigInt(2**32-1),de=BigInt(32);function Ke(s,e=!1){return e?{h:Number(s&G),l:Number(s>>de&G)}:{h:Number(s>>de&G)|0,l:Number(s&G)|0}}function Xe(s,e=!1){const t=s.length;let o=new Uint32Array(t),i=new Uint32Array(t);for(let r=0;r<t;r++){const{h:n,l:a}=Ke(s[r],e);[o[r],i[r]]=[n,a]}return[o,i]}const qe=(s,e,t)=>s<<t|e>>>32-t,Ge=(s,e,t)=>e<<t|s>>>32-t,Ye=(s,e,t)=>e<<t-32|s>>>64-t,ze=(s,e,t)=>s<<t-32|e>>>64-t,je=64;function ye(s){const e=s.prepeginTxid.toLowerCase();if(!/^[0-9a-f]+$/.test(e)||e.length!==je)throw new Error(`buildDepositTerms: prepeginTxid must be 64 hex chars, got "${s.prepeginTxid}"`);if(s.peginAmounts.length===0)throw new Error("buildDepositTerms: at least one pegin amount is required");if(s.timelockPegin<=0||s.timelockAssert<=0||s.timelockRefund<=0)throw new Error("buildDepositTerms: timelocks must be positive");if(!Number.isInteger(s.maxAcceptableCommissionBps)||s.maxAcceptableCommissionBps<0||s.maxAcceptableCommissionBps>=q.MAX_VP_COMMISSION_BPS_EXCLUSIVE)throw new Error(`buildDepositTerms: maxAcceptableCommissionBps must be an integer in [0, ${q.MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${s.maxAcceptableCommissionBps}`);const t=BigInt(q.BPS_DENOMINATOR),o=s.peginAmounts.map((i,r)=>({htlcVout:r,vaultProviderBtcPubkey:s.vaultProviderBtcPubkey,peginAmount:i,commissionFee:i*BigInt(s.maxAcceptableCommissionBps)/t,depositorClaimValue:s.depositorClaimValue,peginMaxFee:s.peginMaxFee}));return{vaultCoreVersion:s.vaultCoreVersion,protocolFeeRate:s.protocolFeeRate,timelockPegin:s.timelockPegin,timelockAssert:s.timelockAssert,timelockRefund:s.timelockRefund,prepeginTxid:e,prepeginMaxFee:s.prepeginMaxFee,vaultKeeperBtcPubkeys:[...s.vaultKeeperBtcPubkeys],universalChallengerBtcPubkeys:[...s.universalChallengerBtcPubkeys],vaults:o}}const Z=25,ce=9999;function Q(s){if(!Number.isInteger(s)||s<0||s>=q.MAX_VP_COMMISSION_BPS_EXCLUSIVE)throw new Error(`Quoted commissionBps must be an integer in [0, ${q.MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${s}`);return Math.min(s+Z,ce)}const Qe=/^0x[0-9a-f]+$/i,Ze=/^[0-9a-f]+$/i,Je=/^[A-Za-z0-9+/]+={0,2}$/;function D(s){if(typeof s!="string"||s.length===0)throw new Error("BTC wallet returned empty public key");return h.processPublicKeyToXOnly(s).toLowerCase()}function et(s){if(typeof s!="string"||s.length===0)throw new Error("BTC wallet returned empty BIP-322 signature");if(s.startsWith("0x")||s.startsWith("0X")){if(!Qe.test(s)||s.length<4||s.length%2!==0)throw new Error("BTC wallet returned malformed hex BIP-322 signature");return s.toLowerCase()}if(Ze.test(s)){if(s.length%2!==0)throw new Error("BTC wallet returned malformed hex BIP-322 signature");return`0x${s.toLowerCase()}`}if(!Je.test(s)||s.length%4!==0)throw new Error("BTC wallet returned malformed base64 BIP-322 signature");const e=W.Buffer.from(s,"base64");if(e.length===0||e.toString("base64")!==s)throw new Error("BTC wallet returned malformed base64 BIP-322 signature");return`0x${e.toString("hex")}`}async function ke(s){const{wallet:e,depositTerms:t,fundedPrePeginTxHex:o,depositorBtcPubkey:i}=s,r=typeof e.approveDepositTerms=="function"?e.approveDepositTerms:void 0;if(!r&&!t)return;const n=o.startsWith("0x")?o.slice(2):o,c=K.Transaction.fromHex(n).getId();if(t&&t.prepeginTxid.replace(/^0x/,"").toLowerCase()!==c)throw new Error(`Deposit terms do not match the transaction being broadcast: terms are for prepeginTxid ${t.prepeginTxid}, but this transaction is ${c}.`);if(!r)return;if(!t)throw new Error("This wallet requires approved deposit terms before signing the Pre-PegIn transaction, but none were provided. Pass PreparePeginResult.depositTerms (fresh flows) or a resume rebuild.");if(typeof e.deriveContextHash!="function")throw new Error("A deposit-approval wallet must also implement deriveContextHash, but this one does not.");if(typeof e.holdsApprovedDepositTerms=="function"){let m=!1;try{m=await e.holdsApprovedDepositTerms(t)}catch{}if(m)return}typeof e.validateDepositTerms=="function"&&await e.validateDepositTerms(t);const d=ie.parseFundingOutpointsFromTx(o);(await ie.deriveVaultRoot({deriveContextHash:e.deriveContextHash.bind(e)},{depositorBtcPubkey:h.hexToUint8Array(D(i)),fundingOutpoints:d})).fill(0),await r.call(e,t)}const tt=Uint8Array.from([7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8]),Te=Uint8Array.from(new Array(16).fill(0).map((s,e)=>e)),st=Te.map(s=>(9*s+5)%16),Ee=(()=>{const t=[[Te],[st]];for(let o=0;o<4;o++)for(let i of t)i.push(i[o].map(r=>tt[r]));return t})(),ve=Ee[0],Se=Ee[1],Be=[[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8],[12,13,11,15,6,9,9,7,12,15,11,13,7,8,7,7],[13,15,14,11,7,7,6,8,13,14,13,12,5,5,6,9],[14,11,12,14,8,6,5,5,15,12,15,14,9,9,8,6],[15,12,13,13,9,5,8,6,14,11,12,11,8,6,5,5]].map(s=>Uint8Array.from(s)),ot=ve.map((s,e)=>s.map(t=>Be[e][t])),rt=Se.map((s,e)=>s.map(t=>Be[e][t])),nt=Uint32Array.from([0,1518500249,1859775393,2400959708,2840853838]),it=Uint32Array.from([1352829926,1548603684,1836072691,2053994217,0]);function pe(s,e,t,o){return s===0?e^t^o:s===1?e&t|~e&o:s===2?(e|~t)^o:s===3?e&o|t&~o:e^(t|~o)}const Y=new Uint32Array(16);class at extends b.HashMD{constructor(){super(64,20,8,!0);T(this,"h0",1732584193);T(this,"h1",-271733879);T(this,"h2",-1732584194);T(this,"h3",271733878);T(this,"h4",-1009589776)}get(){const{h0:t,h1:o,h2:i,h3:r,h4:n}=this;return[t,o,i,r,n]}set(t,o,i,r,n){this.h0=t|0,this.h1=o|0,this.h2=i|0,this.h3=r|0,this.h4=n|0}process(t,o){for(let l=0;l<16;l++,o+=4)Y[l]=t.getUint32(o,!0);let i=this.h0|0,r=i,n=this.h1|0,a=n,c=this.h2|0,d=c,p=this.h3|0,m=p,x=this.h4|0,w=x;for(let l=0;l<5;l++){const v=4-l,E=nt[l],f=it[l],B=ve[l],A=Se[l],g=ot[l],y=rt[l];for(let u=0;u<16;u++){const k=b.rotl(i+pe(l,n,c,p)+Y[B[u]]+E,g[u])+x|0;i=x,x=p,p=b.rotl(c,10)|0,c=n,n=k}for(let u=0;u<16;u++){const k=b.rotl(r+pe(v,a,d,m)+Y[A[u]]+f,y[u])+w|0;r=w,w=m,m=b.rotl(d,10)|0,d=a,a=k}}this.set(this.h1+c+m|0,this.h2+p+w|0,this.h3+x+r|0,this.h4+i+a|0,this.h0+n+d|0)}roundClean(){b.clean(Y)}destroy(){this.destroyed=!0,b.clean(this.buffer),this.set(0,0,0,0,0)}}const ct=b.createHasher(()=>new at),lt=BigInt(0),U=BigInt(1),ut=BigInt(2),ht=BigInt(7),dt=BigInt(256),pt=BigInt(113),_e=[],Ae=[],Ce=[];for(let s=0,e=U,t=1,o=0;s<24;s++){[t,o]=[o,(2*t+3*o)%5],_e.push(2*(5*o+t)),Ae.push((s+1)*(s+2)/2%64);let i=lt;for(let r=0;r<7;r++)e=(e<<U^(e>>ht)*pt)%dt,e&ut&&(i^=U<<(U<<BigInt(r))-U);Ce.push(i)}const Ie=Xe(Ce,!0),ft=Ie[0],gt=Ie[1],fe=(s,e,t)=>t>32?Ye(s,e,t):qe(s,e,t),ge=(s,e,t)=>t>32?ze(s,e,t):Ge(s,e,t);function mt(s,e=24){const t=new Uint32Array(10);for(let o=24-e;o<24;o++){for(let n=0;n<10;n++)t[n]=s[n]^s[n+10]^s[n+20]^s[n+30]^s[n+40];for(let n=0;n<10;n+=2){const a=(n+8)%10,c=(n+2)%10,d=t[c],p=t[c+1],m=fe(d,p,1)^t[a],x=ge(d,p,1)^t[a+1];for(let w=0;w<50;w+=10)s[n+w]^=m,s[n+w+1]^=x}let i=s[2],r=s[3];for(let n=0;n<24;n++){const a=Ae[n],c=fe(i,r,a),d=ge(i,r,a),p=_e[n];i=s[p],r=s[p+1],s[p]=c,s[p+1]=d}for(let n=0;n<50;n+=10){for(let a=0;a<10;a++)t[a]=s[n+a];for(let a=0;a<10;a++)s[n+a]^=~t[(a+2)%10]&t[(a+4)%10]}s[0]^=ft[o],s[1]^=gt[o]}b.clean(t)}class le{constructor(e,t,o,i=!1,r=24){T(this,"state");T(this,"pos",0);T(this,"posOut",0);T(this,"finished",!1);T(this,"state32");T(this,"destroyed",!1);T(this,"blockLen");T(this,"suffix");T(this,"outputLen");T(this,"enableXOF",!1);T(this,"rounds");if(this.blockLen=e,this.suffix=t,this.outputLen=o,this.enableXOF=i,this.rounds=r,b.anumber(o,"outputLen"),!(0<e&&e<200))throw new Error("only keccak-f1600 function is supported");this.state=new Uint8Array(200),this.state32=b.u32(this.state)}clone(){return this._cloneInto()}keccak(){b.swap32IfBE(this.state32),mt(this.state32,this.rounds),b.swap32IfBE(this.state32),this.posOut=0,this.pos=0}update(e){b.aexists(this),b.abytes(e);const{blockLen:t,state:o}=this,i=e.length;for(let r=0;r<i;){const n=Math.min(t-this.pos,i-r);for(let a=0;a<n;a++)o[this.pos++]^=e[r++];this.pos===t&&this.keccak()}return this}finish(){if(this.finished)return;this.finished=!0;const{state:e,suffix:t,pos:o,blockLen:i}=this;e[o]^=t,(t&128)!==0&&o===i-1&&this.keccak(),e[i-1]^=128,this.keccak()}writeInto(e){b.aexists(this,!1),b.abytes(e),this.finish();const t=this.state,{blockLen:o}=this;for(let i=0,r=e.length;i<r;){this.posOut>=o&&this.keccak();const n=Math.min(o-this.posOut,r-i);e.set(t.subarray(this.posOut,this.posOut+n),i),this.posOut+=n,i+=n}return e}xofInto(e){if(!this.enableXOF)throw new Error("XOF is not possible for this instance");return this.writeInto(e)}xof(e){return b.anumber(e),this.xofInto(new Uint8Array(e))}digestInto(e){if(b.aoutput(e,this),this.finished)throw new Error("digest() was already called");return this.writeInto(e),this.destroy(),e}digest(){return this.digestInto(new Uint8Array(this.outputLen))}destroy(){this.destroyed=!0,b.clean(this.state)}_cloneInto(e){const{blockLen:t,suffix:o,outputLen:i,rounds:r,enableXOF:n}=this;return e||(e=new le(t,o,i,n,r)),e.state32.set(this.state32),e.pos=this.pos,e.posOut=this.posOut,e.finished=this.finished,e.rounds=r,e.suffix=o,e.outputLen=i,e.enableXOF=n,e.destroyed=this.destroyed,e}}const Pt=(s,e,t,o={})=>b.createHasher(()=>new le(e,s,t),o),bt=Pt(1,136,32),me=64,_=20,ae=4,He=2,xt=0,wt=1,z=[64,64],yt=s=>Array.from(s).map(e=>e.toString(16).padStart(2,"0")).join("");function ue(s){return ct(b.sha256(s))}function Re(s){return(1<<s)-1}function kt(s){let e=1;for(;e*e<s+1;)e++;return Math.max(e,2)}function Tt(s){const e=ae,t=s*Re(e);return{d:e,n:s,checksum_radix:kt(t)}}function te(s,e){const t=[];let o=e;for(;o>0;)t.push(o&255),o>>>=8;const i=new Uint8Array(s.length+t.length);i.set(s);for(let r=0;r<t.length;r++)i[s.length+r]=t[r];return ue(i)}function se(s,e){let t=s;for(let o=0;o<e;o++)t=ue(t);return t}function Et(s,e){const t=Re(e.d),o=e.checksum_radix-1,i=Math.floor(e.n*t/e.checksum_radix),r=[];for(let p=0;p<e.n;p++){const m=te(s,p+He),x=se(m,t);r.push(Array.from(x))}const n=te(s,xt),a=se(n,o),c=te(s,wt),d=se(c,i);return{config:e,message_terminals:r,checksum_major_terminal:Array.from(d),checksum_minor_terminal:Array.from(a)}}async function Oe(s){try{if(s.length!==me)throw new Error(`WOTS seed must be exactly ${me} bytes, got ${s.length}`);const e=[];for(let t=0;t<z.length;t++){const o=z[t],i=Tt(o),r=new Uint8Array(s.length+1);r.set(s),r[s.length]=t;const n=ue(r);try{const a=Et(n,i);if(a.config.d!==ae)throw new Error(`Block ${t}: expected d=${ae}, got d=${a.config.d}`);if(a.config.n!==o)throw new Error(`Block ${t}: expected n=${o}, got n=${a.config.n}`);if(a.message_terminals.length!==o)throw new Error(`Block ${t}: expected ${o} message terminals, got ${a.message_terminals.length}`);for(let c=0;c<a.message_terminals.length;c++)if(a.message_terminals[c].length!==_)throw new Error(`Block ${t} terminal ${c}: expected ${_} bytes, got ${a.message_terminals[c].length}`);if(a.checksum_minor_terminal.length!==_)throw new Error(`Block ${t} checksum_minor: expected ${_} bytes`);if(a.checksum_major_terminal.length!==_)throw new Error(`Block ${t} checksum_major: expected ${_} bytes`);e.push(a)}finally{r.fill(0),n.fill(0)}}if(e.length!==z.length)throw new Error(`Expected ${z.length} blocks, got ${e.length}`);return e}finally{s.fill(0)}}function oe(s,e,t){if(s.length!==_)throw new Error(`Block ${e} ${t}: expected ${_} bytes, got ${s.length}`);for(let o=0;o<s.length;o++){const i=s[o];if(!Number.isInteger(i)||i<0||i>255)throw new Error(`Block ${e} ${t}[${o}]: invalid byte value ${i}`)}}function $e(s){if(s.length===0)throw new Error("Public keys array must not be empty");for(let r=0;r<s.length;r++){const n=s[r];oe(n.checksum_minor_terminal,r,"checksum_minor_terminal"),oe(n.checksum_major_terminal,r,"checksum_major_terminal");for(let a=0;a<n.message_terminals.length;a++)oe(n.message_terminals[a],r,`message_terminal[${a}]`)}let e=0;for(const r of s)e+=He+r.message_terminals.length;const t=new Uint8Array(e*_);let o=0;for(const r of s){t.set(r.checksum_minor_terminal,o),o+=_,t.set(r.checksum_major_terminal,o),o+=_;for(const n of r.message_terminals)t.set(n,o),o+=_}const i=bt(t);return`0x${yt(i)}`}async function Ve(s,e){const t=[],o=[],i=[],r=[];try{for(let n=0;n<e;n++){const a=await X.expandWotsSeed(s,n);try{const d=await Oe(a);t.push(d),o.push($e(d))}finally{a.fill(0)}const c=await X.expandHashlockSecret(s,n);try{const d=h.uint8ArrayToHex(c);i.push(d),r.push(I.computeHashlock(h.ensureHexPrefix(d)).slice(2))}finally{c.fill(0)}}}finally{s.fill(0)}return{perVaultWotsKeys:t,wotsPkHashes:o,htlcSecretHexes:i,hashlocks:r}}const vt=1,St=2,Bt=33,_t=2,At=3,Ct=1,j=64,It=0,re=1,Pe=/^[0-9a-f]{64}$/i,Ht=/^(?:[0-9a-f]{2})+$/;function Rt(s){const e=s.slice(2);if(!Ht.test(e))throw new Error("proof of possession witness is not even-length lowercase hex");return L.decodeWitnessStack(Uint8Array.from(W.Buffer.from(e,"hex")),"proof of possession witness")}function Ot(s,e,t){var i;const o=Rt(t);if(o.length===vt){const[r]=o;let n,a;if(r.length===j)n=r,a=It;else if(r.length===j+1&&r[j]===re)n=r.subarray(0,j),a=re;else throw new Error("proof of possession witness item must be a 64-byte Schnorr signature (or 65 bytes ending in 0x01)");if(!Pe.test(e))throw new Error(`depositor public key must be bare 64-char x-only hex, got "${e}"`);const c=Uint8Array.from(W.Buffer.from(e,"hex"));if(!$.verifyBip322Simple(s,c,n,a))throw new Error("proof of possession signature does not verify against the depositor key");return{kind:"p2tr-verified"}}if(o.length===St){if(!Pe.test(e))throw new Error(`depositor public key must be bare 64-char x-only hex, got "${e}"`);const[r,n]=o;if(n.length!==Bt||n[0]!==_t&&n[0]!==At)throw new Error(`proof of possession P2WPKH witness item 1 is not a compressed public key (${n.length} bytes, prefix 0x${((i=n[0])==null?void 0:i.toString(16))??"none"})`);if(!Le.isPointCompressed(n))throw new Error("proof of possession P2WPKH witness pubkey is not a valid secp256k1 point");const a=W.Buffer.from(n.subarray(Ct)).toString("hex");if(a!==e.toLowerCase())throw new Error(`proof of possession witness pubkey does not match the depositor key: witness carries ${a}, expected ${e}`);if(r.length<$.P2WPKH_ENCODED_SIG_MIN||r.length>$.P2WPKH_ENCODED_SIG_MAX)throw new Error(`proof of possession signature is ${r.length} bytes; vault ingestion accepts only 71/72-byte DER signatures with a sighash byte — try a different account or a Taproot address`);if(r[r.length-1]!==re)throw new Error(`proof of possession P2WPKH signature must end in a SIGHASH_ALL (0x01) byte, got 0x${r[r.length-1].toString(16)}`);if(!$.verifyBip322P2wpkhSimple(s,n,r))throw new Error("proof of possession signature does not verify against the depositor key");return{kind:"p2wpkh-verified"}}throw new Error(`proof of possession witness has ${o.length} items; expected 1 (P2TR) or 2 (P2WPKH)`)}const Me=0,ne="00".repeat(32),$t="00".repeat(32);function Vt(s,e){const t={bitcoin:"bc",testnet:"tb",signet:"tb",regtest:"bcrt"};try{const o=we.address.fromBech32(s);return o.prefix===t[e]&&o.version===0&&o.data.length===20}catch{return!1}}function Mt(s,e,t,o){const i=t==null?void 0:t[`${s}:${e}`];return i?Promise.resolve({txid:s,vout:e,value:i.value,scriptPubKey:i.scriptPubKey}):$.getUtxoInfo(s,e,o)}const be=12e4;class Ft{constructor(e){T(this,"config");this.config=e}async preparePegin(e){if(e.amounts.length===0)throw new Error("amounts must contain at least one entry");const t=await this.config.btcWallet.getPublicKeyHex(),o=D(t);if(I.supportsDepositApproval(this.config.btcWallet)){const f=(await I.requireChangeAddress(this.config.btcWallet)).trim();if(e.changeAddress.trim()!==f)throw new Error(`Pre-PegIn changeAddress "${e.changeAddress}" is not the approval wallet's change address ("${f}"). Refusing to build a tx the signing device would reject.`)}else if(!h.isAddressFromPublicKey(e.changeAddress,t,this.config.btcNetwork))throw new Error(`Pre-PegIn changeAddress "${e.changeAddress}" is not derived from the connected wallet's public key. Refusing to build a tx that would send change to an address the signing key doesn't control.`);const i=await this.prepareSizing(o,e);if(I.supportsDepositApproval(this.config.btcWallet)){const{validateDepositTerms:f}=this.config.btcWallet;typeof f=="function"&&await f.call(this.config.btcWallet,this.buildPeginDepositTerms({params:e,prepeginTxid:$t,prepeginMaxFee:i.fee,depositorClaimValue:i.depositorClaimValue,peginMaxFee:i.minPeginFee}))}const r=i.selectedUTXOs.map(f=>({txid:h.hexToUint8Array(f.txid),vout:f.vout})),n=await ie.deriveVaultRoot(this.config.btcWallet,{depositorBtcPubkey:h.hexToUint8Array(o),fundingOutpoints:r});let a,c;try{const f=await X.expandAuthAnchor(n);try{a=h.uint8ArrayToHex(f),c=h.uint8ArrayToHex(b.sha256(f))}finally{f.fill(0)}}catch(f){throw n.fill(0),f}const d=await Ve(n,e.amounts.length),{perVaultWotsKeys:p,wotsPkHashes:m,htlcSecretHexes:x,hashlocks:w}=d,l=await this.preparePeginCommit({depositorBtcPubkeyRaw:t,depositorBtcPubkey:o,hashlocks:w,authAnchorHash:c,sizing:i,params:e});for(let f=0;f<l.perVault.length;f++)if(l.perVault[f].htlcVout!==f)throw new Error(`Internal invariant violation: htlcVout/index mismatch at vault ${f} (expected ${f}, got ${l.perVault[f].htlcVout})`);I.assertAuthAnchorOpReturn(l.fundedPrePeginTxHex,e.amounts.length,c);const{depositTerms:v,...E}=l;return{transaction:{...E,selectedUTXOs:i.selectedUTXOs,fee:i.fee,changeAmount:i.changeAmount},depositorBtcPubkey:o,derivedSecrets:{perVaultWotsKeys:p,wotsPkHashes:m,htlcSecretHexes:x,authAnchorHex:a},depositTerms:v}}async prepareSizing(e,t){const o=t.amounts.map(()=>ne),i=t.vaultKeeperBtcPubkeys.length,r=await M.buildPrePeginPsbt({vaultCoreVersion:t.vaultCoreVersion,depositorPubkey:e,vaultProviderPubkey:h.stripHexPrefix(t.vaultProviderBtcPubkey),vaultKeeperPubkeys:t.vaultKeeperBtcPubkeys.map(h.stripHexPrefix),universalChallengerPubkeys:t.universalChallengerBtcPubkeys.map(h.stripHexPrefix),hashlocks:o,timelockRefund:t.timelockRefund,pegInAmounts:t.amounts,feeRate:t.protocolFeeRate,minPeginFeeRate:t.minPeginFeeRate,numLocalChallengers:i,councilQuorum:t.councilQuorum,councilSize:t.councilSize,network:this.config.btcNetwork,authAnchorHash:ne}),n=R.selectUtxosForPegin([...t.availableUTXOs],r.totalOutputValue,t.mempoolFeeRate,he.peginOutputCount(r.htlcValues.length,!0));return{selectedUTXOs:n.selectedUTXOs,fee:n.fee,changeAmount:n.changeAmount,depositorClaimValue:r.depositorClaimValue,minPeginFee:r.minPeginFee}}buildPeginDepositTerms(e){const{params:t}=e;return ye({vaultCoreVersion:t.vaultCoreVersion,protocolFeeRate:t.protocolFeeRate,timelockPegin:t.timelockPegin,timelockAssert:t.timelockAssert,timelockRefund:t.timelockRefund,prepeginTxid:e.prepeginTxid,prepeginMaxFee:e.prepeginMaxFee,vaultProviderBtcPubkey:h.stripHexPrefix(t.vaultProviderBtcPubkey),vaultKeeperBtcPubkeys:t.vaultKeeperBtcPubkeys.map(h.stripHexPrefix),universalChallengerBtcPubkeys:t.universalChallengerBtcPubkeys.map(h.stripHexPrefix),maxAcceptableCommissionBps:Q(t.commissionBps),peginAmounts:t.amounts,depositorClaimValue:e.depositorClaimValue,peginMaxFee:e.peginMaxFee})}async preparePeginCommit(e){const{depositorBtcPubkeyRaw:t,depositorBtcPubkey:o,hashlocks:i,authAnchorHash:r,sizing:n,params:a}=e,c=ne.toLowerCase();for(let P=0;P<i.length;P++)if(i[P].toLowerCase()===c)throw new Error(`preparePeginCommit refusing to build with sizing-pass placeholder hashlock at vault ${P} — internal substitution bug`);if(r.toLowerCase()===c)throw new Error("preparePeginCommit refusing to build with sizing-pass placeholder auth-anchor hash — internal substitution bug");const d=h.stripHexPrefix(a.vaultProviderBtcPubkey),p=a.vaultKeeperBtcPubkeys.map(h.stripHexPrefix),m=a.universalChallengerBtcPubkeys.map(h.stripHexPrefix),x=p.length,w={vaultCoreVersion:a.vaultCoreVersion,depositorPubkey:o,vaultProviderPubkey:d,vaultKeeperPubkeys:p,universalChallengerPubkeys:m,hashlocks:i,timelockRefund:a.timelockRefund,pegInAmounts:a.amounts,feeRate:a.protocolFeeRate,minPeginFeeRate:a.minPeginFeeRate,numLocalChallengers:x,councilQuorum:a.councilQuorum,councilSize:a.councilSize,network:this.config.btcNetwork,authAnchorHash:r},l=await M.buildPrePeginPsbt(w);if(l.depositorClaimValue!==n.depositorClaimValue||l.minPeginFee!==n.minPeginFee)throw new Error(`Pre-PegIn sizing/commit divergence: depositorClaimValue ${n.depositorClaimValue} -> ${l.depositorClaimValue}, minPeginFee ${n.minPeginFee} -> ${l.minPeginFee}. The provisional deposit terms validated before derivation would not match the terms sent for approval; refusing to continue.`);const v=h.getNetwork(this.config.btcNetwork),E=he.fundPeginTransaction({unfundedTxHex:l.psbtHex,selectedUTXOs:n.selectedUTXOs,changeAddress:a.changeAddress,changeAmount:n.changeAmount,network:v}),f=n.selectedUTXOs.reduce((P,S)=>P+BigInt(S.value),0n)-l.totalOutputValue-n.changeAmount;if(f!==n.fee)throw new Error(`Pre-PegIn funded fee ${f} does not match the sizing-pass fee ${n.fee}; refusing to publish a deposit-terms fee bound the funded transaction does not pay.`);const B=h.stripHexPrefix(R.calculateBtcTxHash(E)),A=[],g=[],y=[];for(let P=0;P<i.length;P++){const S=await M.buildPeginTxFromFundedPrePegin({prePeginParams:w,timelockPegin:a.timelockPegin,fundedPrePeginTxHex:E,htlcVout:P}),H=await L.buildPeginInputPsbt({vaultCoreVersion:a.vaultCoreVersion,peginTxHex:S.txHex,fundedPrePeginTxHex:E,depositorPubkey:o,vaultProviderPubkey:d,vaultKeeperPubkeys:p,universalChallengerPubkeys:m,hashlock:i[P],timelockRefund:a.timelockRefund,network:this.config.btcNetwork});A.push(S),g.push(H.psbtHex),y.push(Ne.createTaprootScriptPathSignOptions(t,1))}const u=this.buildPeginDepositTerms({params:a,prepeginTxid:B,prepeginMaxFee:n.fee,depositorClaimValue:l.depositorClaimValue,peginMaxFee:l.minPeginFee});I.supportsDepositApproval(this.config.btcWallet)&&await this.config.btcWallet.approveDepositTerms(u);const k=await I.signPsbtsWithFallback(this.config.btcWallet,g,y),C=[];for(let P=0;P<k.length;P++){M.assertPsbtUnsignedTxMatches({requestedPsbtHex:g[P],returnedPsbtHex:k[P]});const S=L.extractPeginInputSignature(k[P],o);M.assertScriptPathSchnorrSignature({requestedPsbtHex:g[P],signatureHex:S,signerXOnlyPubkeyHex:o,inputIndex:0});const H=L.finalizePeginInputPsbt(k[P]);C.push({htlcVout:P,htlcValue:l.htlcValues[P],peginTxHex:H,peginTxid:A[P].txid,peginInputSignature:S,vaultScriptPubKey:A[P].vaultScriptPubKey})}return{fundedPrePeginTxHex:E,prePeginTxid:B,perVault:C,depositTerms:u}}async signAndBroadcast(e){const{fundedPrePeginTxHex:t,depositorBtcPubkey:o}=e,i=t.startsWith("0x")?t.slice(2):t,r=K.Transaction.fromHex(i);if(r.ins.length===0)throw new Error("Transaction has no inputs");const n=new K.Psbt;n.setVersion(r.version),n.setLocktime(r.locktime);const a=W.Buffer.from(D(o),"hex"),c=this.config.mempoolApiUrl,d=r.ins.map(g=>{const y=W.Buffer.from(g.hash).reverse().toString("hex"),u=g.index;return Mt(y,u,e.localPrevouts,c).then(k=>({input:g,utxoData:k,txid:y,vout:u}))}),p=await Promise.all(d),m=p.reduce((g,y)=>g+BigInt(y.utxoData.value),0n),x=r.outs.reduce((g,y)=>g+BigInt(y.value),0n);if(m<x)throw new Error(`UTXO value mismatch: total input value (${m} sat) is less than total output value (${x} sat). This may indicate the mempool API returned manipulated UTXO data.`);const w=m-x;if(w>ee.MAX_REASONABLE_FEE_SATS)throw new Error(`Implied transaction fee (${w} sat) exceeds maximum reasonable fee (${ee.MAX_REASONABLE_FEE_SATS} sat). This may indicate manipulated UTXO data.`);for(const{input:g,utxoData:y,txid:u,vout:k}of p){const C=R.getPsbtInputFields({value:y.value,scriptPubKey:y.scriptPubKey},a);n.addInput({hash:g.hash,index:g.index,sequence:g.sequence,...C})}for(const g of r.outs)n.addOutput({script:g.script,value:g.value});await ke({wallet:this.config.btcWallet,depositTerms:e.depositTerms,fundedPrePeginTxHex:t,depositorBtcPubkey:o});const l=n.toHex(),v=await this.config.btcWallet.signPsbt(l);M.assertPsbtUnsignedTxMatches({requestedPsbtHex:l,returnedPsbtHex:v});const E=L.assertReturnedKeyPathSignatures({requestedPsbtHex:l,returnedPsbtHex:v});if(I.supportsDepositApproval(this.config.btcWallet)&&E!==n.data.inputs.length)throw new Error(`Key-path verification covered ${E} of ${n.data.inputs.length} Pre-PegIn inputs; an approval wallet signs every input key-path, so the unverified ones must not be broadcast.`);const f=K.Psbt.fromHex(v);try{f.finalizeAllInputs()}catch(g){if(!f.data.inputs.every(u=>u.finalScriptWitness||u.finalScriptSig))throw new Error(`PSBT finalization failed and wallet did not auto-finalize: ${g}`)}const B=f.extractTransaction().toHex();return await $.pushTx(B,c)}async registerPeginOnChain(e){const{unsignedPrePeginTx:t,depositorSignedPeginTx:o,vaultProvider:i,hashlock:r,htlcVout:n,depositorPayoutBtcAddress:a,depositorWotsPkHash:c,popSignature:d}=e;if(!this.config.ethWallet.account)throw new Error("Ethereum wallet account not found");const p=this.config.ethWallet.account.address;if(!F.isAddressEqual(d.depositorEthAddress,p))throw new Error(`Proof of possession was signed for ${d.depositorEthAddress} but the Ethereum wallet is currently connected to ${p}. Reconnect the original account or call signProofOfPossession() again.`);const m=await this.assertPopMatchesBtcWallet(d),x=d.btcPopSignature,w=h.ensureHexPrefix(d.depositorBtcPubkey),l=h.ensureHexPrefix(t),v=h.ensureHexPrefix(o),E=a??await this.config.btcWallet.getAddress(),f=this.resolvePayoutScriptPubKey(m,E),B=R.calculateBtcTxHash(v),A=await X.deriveVaultId(h.stripHexPrefix(B),h.stripHexPrefix(p)),g=h.ensureHexPrefix(A);if(await this.checkVaultExists(g))throw new Error(`Vault already exists (ID: ${g}, peginTxHash: ${B}). Vault IDs are derived from the pegin transaction hash and depositor address. To create a new vault, use different UTXOs or a different amount to generate a unique transaction.`);const u=this.config.publicClient;let k;try{k=await u.readContract({address:this.config.vaultContracts.btcVaultRegistry,abi:O.BTCVaultRegistryABI,functionName:"getPegInFee",args:[i]})}catch(N){throw new Error("Failed to query pegin fee from the contract. Please check your network connection and that the contract address is correct.",{cause:N})}const C=await this.resolveMaxAcceptableCommissionBps(i,e.quotedCommissionBps),P=F.encodeFunctionData({abi:O.BTCVaultRegistryABI,functionName:"submitPeginRequest",args:[p,w,x,l,v,i,C,r,n,f,c]});let S;try{S=await u.estimateGas({to:this.config.vaultContracts.btcVaultRegistry,data:P,value:k,account:this.config.ethWallet.account.address})}catch(N){V.handleContractError(N)}let H;try{H=await this.config.ethWallet.sendTransaction({to:this.config.vaultContracts.btcVaultRegistry,data:P,value:k,account:this.config.ethWallet.account,chain:this.config.ethChain,gas:S})}catch(N){V.handleContractError(N)}const J=await R.waitForTransactionReceiptSmartAware({publicClient:u,walletAddress:this.config.ethWallet.account.address,hash:H,timeout:be});return J.status==="reverted"&&V.handleContractError(new Error(`Transaction reverted. Hash: ${J.transactionHash}. Check the transaction on block explorer for details.`)),{ethTxHash:J.transactionHash,vaultId:g,peginTxHash:B}}async registerPeginBatchOnChain(e){const{vaultProvider:t,unsignedPrePeginTx:o,requests:i,popSignature:r}=e;if(i.length===0)throw new Error("Batch pegin requires at least one request");if(!this.config.ethWallet.account)throw new Error("Ethereum wallet account not found");const n=this.config.ethWallet.account.address;if(!F.isAddressEqual(r.depositorEthAddress,n))throw new Error(`Proof of possession was signed for ${r.depositorEthAddress} but the Ethereum wallet is currently connected to ${n}. Reconnect the original account or call signProofOfPossession() again.`);const a=await this.assertPopMatchesBtcWallet(r),c=r.btcPopSignature,d=i.map(u=>this.resolvePayoutScriptPubKey(a,u.depositorPayoutBtcAddress)),p=[];for(const u of i){const k=h.ensureHexPrefix(u.depositorSignedPeginTx),C=R.calculateBtcTxHash(k),P=await X.deriveVaultId(h.stripHexPrefix(C),h.stripHexPrefix(n)),S=h.ensureHexPrefix(P);if(await this.checkVaultExists(S))throw new Error(`Vault already exists (ID: ${S}, peginTxHash: ${C}). To create a new vault, use different UTXOs or a different amount.`);p.push({vaultId:S,peginTxHash:C})}const m=this.config.publicClient;let x;try{x=await m.readContract({address:this.config.vaultContracts.btcVaultRegistry,abi:O.BTCVaultRegistryABI,functionName:"getPegInFee",args:[t]})}catch(u){throw new Error("Failed to query pegin fee from the contract. Please check your network connection and that the contract address is correct.",{cause:u})}const w=x*BigInt(i.length),l=await this.resolveMaxAcceptableCommissionBps(t,e.quotedCommissionBps),v=h.ensureHexPrefix(r.depositorBtcPubkey),E=h.ensureHexPrefix(o),f=i.map((u,k)=>({depositorBtcPubKey:v,btcPopSignature:c,unsignedPrePeginTx:E,depositorSignedPeginTx:h.ensureHexPrefix(u.depositorSignedPeginTx),hashlock:u.hashlock,htlcVout:u.htlcVout,referralCode:Me,depositorPayoutBtcAddress:d[k],depositorWotsPkHash:u.depositorWotsPkHash})),B=F.encodeFunctionData({abi:O.BTCVaultRegistryABI,functionName:"submitPeginRequestBatch",args:[n,t,l,f]});let A;try{A=await m.estimateGas({to:this.config.vaultContracts.btcVaultRegistry,data:B,value:w,account:this.config.ethWallet.account.address})}catch(u){V.handleContractError(u)}let g;try{g=await this.config.ethWallet.sendTransaction({to:this.config.vaultContracts.btcVaultRegistry,data:B,value:w,account:this.config.ethWallet.account,chain:this.config.ethChain,gas:A})}catch(u){V.handleContractError(u)}const y=await R.waitForTransactionReceiptSmartAware({publicClient:m,walletAddress:this.config.ethWallet.account.address,hash:g,timeout:be});return y.status==="reverted"&&V.handleContractError(new Error(`Batch transaction reverted. Hash: ${y.transactionHash}. Check the transaction on block explorer for details.`)),{ethTxHash:y.transactionHash,vaults:p}}async resolveMaxAcceptableCommissionBps(e,t){if(t===void 0&&I.supportsDepositApproval(this.config.btcWallet))throw new Error("quotedCommissionBps is required when the wallet approved deposit terms: the registration ceiling must anchor to the approved quote.");let o;try{o=await new $.ViemVaultRegistryReader(this.config.publicClient,this.config.vaultContracts.btcVaultRegistry).getVaultProviderCommission(e)}catch(i){throw new Error("Failed to query vault provider commission from the contract. Please check your network connection and that the contract address is correct.",{cause:i})}if(t!==void 0){if(o>t+Z)throw new Error(`Vault provider commission changed since quote: quoted ${t} bps, chain currently reports ${o} bps (allowed drift ${Z} bps). Please refresh to see the new commission and try again.`);return Q(t)}return Q(o)}async checkVaultExists(e){return(await this.config.publicClient.readContract({address:this.config.vaultContracts.btcVaultRegistry,abi:O.BTCVaultRegistryABI,functionName:"getBtcVaultBasicInfo",args:[e]})).depositor!==F.zeroAddress}resolvePayoutScriptPubKey(e,t){if(!h.isAddressFromPublicKey(t,e,this.config.btcNetwork))throw h.stripHexPrefix(e).length===ee.X_ONLY_PUBKEY_HEX_LEN&&Vt(t,this.config.btcNetwork)?new Error(`BTC payout address "${t}" is a P2WPKH (Native SegWit) address, but the connected wallet only exposes an x-only public key. P2WPKH validation requires a compressed key with known y-parity. Use a P2TR (Taproot) payout address instead.`):new Error(`BTC payout address "${t}" is not derived from the connected wallet's public key. The payout sink must be controlled by the same key that signs the pegin; refusing to register a mismatched address.`);const o=h.getNetwork(this.config.btcNetwork);try{return`0x${we.address.toOutputScript(t,o).toString("hex")}`}catch{throw new Error(`Invalid BTC payout address: "${t}". Please provide a valid Bitcoin address for the ${this.config.btcNetwork} network.`)}}async signProofOfPossession(){if(!this.config.ethWallet.account)throw new Error("Ethereum wallet account not found");const e=this.config.ethWallet.account.address,t=D(await this.config.btcWallet.getPublicKeyHex()),o=this.config.vaultContracts.btcVaultRegistry,i=`${e.toLowerCase()}:${this.config.ethChain.id}:pegin:${o.toLowerCase()}`,r=await this.config.btcWallet.signMessage(i,"bip322-simple"),n=et(r);return Ot(new TextEncoder().encode(i),t,n),{btcPopSignature:n,depositorEthAddress:e,depositorBtcPubkey:t}}async assertPopMatchesBtcWallet(e){const t=await this.config.btcWallet.getPublicKeyHex(),o=D(t),i=D(e.depositorBtcPubkey);if(o!==i)throw new Error(`Proof of possession was signed with BTC pubkey ${i} but the BTC wallet is currently connected to ${o}. Reconnect the original wallet or call signProofOfPossession() again.`);return t}getNetwork(){return this.config.btcNetwork}getVaultContractAddress(){return this.config.vaultContracts.btcVaultRegistry}}const Dt=80,Wt=250,Nt=300,Ut=22,Fe="ab";function Lt(s,e){const t=Fe.repeat(Nt),o=h.stripHexPrefix(s).toLowerCase(),i=e.toString(16).padStart(8,"0"),r=`${o}${i}`,n=t.slice(r.length);return`0x${r}${n}`}function Kt(s,e){const t=o=>`0x${Fe.repeat(o)}`;return{depositorBtcPubKey:t(32),btcPopSignature:t(Dt),unsignedPrePeginTx:t(Wt),depositorSignedPeginTx:Lt(s,e),hashlock:t(32),htlcVout:e,referralCode:Me,depositorPayoutBtcAddress:t(Ut),depositorWotsPkHash:t(32)}}async function Xt(s){const{publicClient:e,btcVaultRegistry:t,depositorEthAddress:o,vaultProvider:i,batchSize:r}=s;if(r<=0)throw new Error(`estimateSubmitPeginRequestBatchGas requires batchSize >= 1 (received ${r})`);const a=await e.readContract({address:t,abi:O.BTCVaultRegistryABI,functionName:"getPegInFee",args:[i]})*BigInt(r),c=Array.from({length:r},(p,m)=>Kt(o,m)),d=F.encodeFunctionData({abi:O.BTCVaultRegistryABI,functionName:"submitPeginRequestBatch",args:[o,i,ce,c]});return e.estimateGas({to:t,data:d,value:a,account:o})}exports.COMMISSION_BPS_HEADROOM=Z;exports.MAX_ACCEPTABLE_COMMISSION_BPS_CAP=ce;exports.PeginManager=Ft;exports.buildDepositTerms=ye;exports.capMaxAcceptableCommissionBps=Q;exports.computeWotsBlockPublicKeysHash=$e;exports.deriveWotsBlocksFromSeed=Oe;exports.ensurePrePeginTermsApproval=ke;exports.estimateSubmitPeginRequestBatchGas=Xt;exports.expandPerVaultSecrets=Ve;
2
- //# sourceMappingURL=PeginManager-BIKAnZ-X.cjs.map