@babylonlabs-io/ts-sdk 0.40.0 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{PayoutManager-B5bovfkD.cjs → PayoutManager-BwYlPF2C.cjs} +2 -2
- package/dist/PayoutManager-BwYlPF2C.cjs.map +1 -0
- package/dist/{PayoutManager-DChODEOJ.js → PayoutManager-CXDccwDN.js} +2 -2
- package/dist/PayoutManager-CXDccwDN.js.map +1 -0
- package/dist/PeginManager-BOuzU8Zo.cjs +2 -0
- package/dist/PeginManager-BOuzU8Zo.cjs.map +1 -0
- package/dist/{PeginManager-D9ZZ8wx2.js → PeginManager-Bsou6AQV.js} +44 -47
- package/dist/PeginManager-Bsou6AQV.js.map +1 -0
- package/dist/{assertPsbtUnsignedTxMatches-r1svclbd.cjs → assertPsbtUnsignedTxMatches-CABhEADu.cjs} +2 -2
- package/dist/assertPsbtUnsignedTxMatches-CABhEADu.cjs.map +1 -0
- package/dist/{assertPsbtUnsignedTxMatches-CzVv57QF.js → assertPsbtUnsignedTxMatches-GHobJP-d.js} +2 -2
- package/dist/assertPsbtUnsignedTxMatches-GHobJP-d.js.map +1 -0
- package/dist/bitcoin-B5aNKtsk.js.map +1 -1
- package/dist/bitcoin-CHfKAhcI.cjs.map +1 -1
- package/dist/{buildAndBroadcastRefund-fIHDHiFh.js → buildAndBroadcastRefund-37Bs7-V1.js} +4 -4
- package/dist/{buildAndBroadcastRefund-fIHDHiFh.js.map → buildAndBroadcastRefund-37Bs7-V1.js.map} +1 -1
- package/dist/{buildAndBroadcastRefund-Cj8JDI7F.cjs → buildAndBroadcastRefund-DbcNEsOv.cjs} +2 -2
- package/dist/{buildAndBroadcastRefund-Cj8JDI7F.cjs.map → buildAndBroadcastRefund-DbcNEsOv.cjs.map} +1 -1
- package/dist/{challengeAssert-DLEmAhT0.js → challengeAssert-ChqnvtRg.js} +2 -2
- package/dist/{challengeAssert-DLEmAhT0.js.map → challengeAssert-ChqnvtRg.js.map} +1 -1
- package/dist/{challengeAssert-rpDaS3fH.cjs → challengeAssert-Cmj_OG6V.cjs} +2 -2
- package/dist/{challengeAssert-rpDaS3fH.cjs.map → challengeAssert-Cmj_OG6V.cjs.map} +1 -1
- package/dist/{fundPeginTransaction-t-6TsHAY.js → fundPeginTransaction-C11tYf6I.js} +32 -33
- package/dist/fundPeginTransaction-C11tYf6I.js.map +1 -0
- package/dist/fundPeginTransaction-C8qsXxNV.cjs +2 -0
- package/dist/fundPeginTransaction-C8qsXxNV.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +9 -9
- package/dist/{noPayout-CS2wnluA.js → noPayout-BtP-R-b-.js} +2 -2
- package/dist/{noPayout-CS2wnluA.js.map → noPayout-BtP-R-b-.js.map} +1 -1
- package/dist/{noPayout-B06Z9RTe.cjs → noPayout-DliaHuc6.cjs} +2 -2
- package/dist/{noPayout-B06Z9RTe.cjs.map → noPayout-DliaHuc6.cjs.map} +1 -1
- package/dist/{reservation-hjXStM03.cjs → reservation-Cwf2u4vu.cjs} +2 -2
- package/dist/{reservation-hjXStM03.cjs.map → reservation-Cwf2u4vu.cjs.map} +1 -1
- package/dist/{reservation-CB-4FBPk.js → reservation-DNOGLBt4.js} +2 -2
- package/dist/{reservation-CB-4FBPk.js.map → reservation-DNOGLBt4.js.map} +1 -1
- package/dist/tbv/core/clients/vault-provider/auth/serverIdentity.d.ts +0 -7
- package/dist/tbv/core/clients/vault-provider/auth/serverIdentity.d.ts.map +1 -1
- package/dist/tbv/core/index.cjs +1 -1
- package/dist/tbv/core/index.js +9 -9
- package/dist/tbv/core/managers/PeginManager.d.ts.map +1 -1
- package/dist/tbv/core/managers/index.cjs +1 -1
- package/dist/tbv/core/managers/index.js +2 -2
- package/dist/tbv/core/managers/pegin/assertAuthAnchorOpReturn.d.ts +0 -17
- package/dist/tbv/core/managers/pegin/assertAuthAnchorOpReturn.d.ts.map +1 -1
- package/dist/tbv/core/managers/pegin/index.d.ts +1 -1
- package/dist/tbv/core/managers/pegin/index.d.ts.map +1 -1
- package/dist/tbv/core/primitives/index.cjs +1 -1
- package/dist/tbv/core/primitives/index.js +3 -3
- package/dist/tbv/core/primitives/psbt/constants.d.ts +0 -4
- package/dist/tbv/core/primitives/psbt/constants.d.ts.map +1 -1
- package/dist/tbv/core/primitives/utils/bitcoin.d.ts +0 -5
- package/dist/tbv/core/primitives/utils/bitcoin.d.ts.map +1 -1
- package/dist/tbv/core/services/index.cjs +1 -1
- package/dist/tbv/core/services/index.js +2 -2
- package/dist/tbv/core/utils/fee/constants.d.ts +8 -12
- package/dist/tbv/core/utils/fee/constants.d.ts.map +1 -1
- package/dist/tbv/core/utils/index.cjs +1 -1
- package/dist/tbv/core/utils/index.js +3 -3
- package/dist/tbv/index.cjs +1 -1
- package/dist/tbv/index.js +9 -9
- package/dist/tbv/integrations/aave/index.cjs +1 -1
- package/dist/tbv/integrations/aave/index.cjs.map +1 -1
- package/dist/tbv/integrations/aave/index.js +265 -270
- package/dist/tbv/integrations/aave/index.js.map +1 -1
- package/dist/tbv/integrations/aave/utils/cascadeSimulation.d.ts +9 -0
- package/dist/tbv/integrations/aave/utils/cascadeSimulation.d.ts.map +1 -1
- package/dist/tbv/integrations/aave/utils/optimalOrder.d.ts +20 -3
- package/dist/tbv/integrations/aave/utils/optimalOrder.d.ts.map +1 -1
- package/dist/types-0bvDGR4x.js.map +1 -1
- package/dist/types-Be3sAYzr.cjs.map +1 -1
- package/dist/{waitForTransactionReceiptSmartAware-tv1mtSIY.cjs → waitForTransactionReceiptSmartAware-BFMQFEzj.cjs} +2 -2
- package/dist/{waitForTransactionReceiptSmartAware-tv1mtSIY.cjs.map → waitForTransactionReceiptSmartAware-BFMQFEzj.cjs.map} +1 -1
- package/dist/{waitForTransactionReceiptSmartAware-CmgFXFza.js → waitForTransactionReceiptSmartAware-Dt5VcMK0.js} +2 -2
- package/dist/{waitForTransactionReceiptSmartAware-CmgFXFza.js.map → waitForTransactionReceiptSmartAware-Dt5VcMK0.js.map} +1 -1
- package/package.json +1 -1
- package/dist/PayoutManager-B5bovfkD.cjs.map +0 -1
- package/dist/PayoutManager-DChODEOJ.js.map +0 -1
- package/dist/PeginManager-D9ZZ8wx2.js.map +0 -1
- package/dist/PeginManager-UqbOj2oV.cjs +0 -2
- package/dist/PeginManager-UqbOj2oV.cjs.map +0 -1
- package/dist/assertPsbtUnsignedTxMatches-CzVv57QF.js.map +0 -1
- package/dist/assertPsbtUnsignedTxMatches-r1svclbd.cjs.map +0 -1
- package/dist/fundPeginTransaction-BBE3wTjR.cjs +0 -2
- package/dist/fundPeginTransaction-BBE3wTjR.cjs.map +0 -1
- package/dist/fundPeginTransaction-t-6TsHAY.js.map +0 -1
- package/dist/tbv/core/primitives/psbt/index.d.ts +0 -34
- package/dist/tbv/core/primitives/psbt/index.d.ts.map +0 -1
- package/dist/tbv/core/primitives/scripts/index.d.ts +0 -10
- package/dist/tbv/core/primitives/scripts/index.d.ts.map +0 -1
- package/dist/tbv/core/primitives/utils/index.d.ts +0 -9
- package/dist/tbv/core/primitives/utils/index.d.ts.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const m=require("bitcoinjs-lib"),g=require("buffer"),f=require("./fundPeginTransaction-
|
|
2
|
-
//# sourceMappingURL=waitForTransactionReceiptSmartAware-
|
|
1
|
+
"use strict";const m=require("bitcoinjs-lib"),g=require("buffer"),f=require("./fundPeginTransaction-C8qsXxNV.cjs");function p(e){const{numInputs:t,numOutputs:n,feeRate:r}=e;if(!Number.isInteger(t)||t<0)throw new Error(`computePeginBaseFeeSats: numInputs must be a non-negative integer, got ${t}`);if(!Number.isInteger(n)||n<1)throw new Error(`computePeginBaseFeeSats: numOutputs must be a positive integer, got ${n}`);const s=t*f.P2TR_INPUT_SIZE+n*f.MAX_NON_LEGACY_OUTPUT_SIZE+f.TX_BUFFER_SIZE_OVERHEAD;return BigInt(Math.ceil(s*r))+BigInt(f.rateBasedTxBufferFee(r))}function T(e){return BigInt(Math.ceil(f.MAX_NON_LEGACY_OUTPUT_SIZE*e))}function E(e){const{totalInputValue:t,peginAmount:n,baseFee:r,changeOutputFee:s}=e,c=t-n-r;if(c<0n)throw new Error(`applyChangeOutputPolicy: insufficient funds (need ${n+r} sats, have ${t})`);const i=c-s;return i>f.DUST_THRESHOLD?{fee:r+s,changeAmount:i,emitChangeOutput:!0}:{fee:r+c,changeAmount:0n,emitChangeOutput:!1}}function I(e){const{numInputs:t,numOutputs:n,totalBalance:r,feeRate:s}=e;if(r<=0n)return null;const c=p({numInputs:t,numOutputs:n,feeRate:s}),i=r-c;return i>0n?i:0n}function P(e){const t=new Set;for(const n of e){const r=`${n.txid.toLowerCase()}:${n.vout}`;if(t.has(r))throw new Error(`Duplicate UTXO detected: ${n.txid}:${n.vout}. This indicates a data integrity issue with the UTXO source.`);t.add(r)}}function O(e,t,n,r){if(!Number.isInteger(r)||r<1)throw new Error(`Invalid numOutputs: expected a positive integer, got ${r}`);if(e.length===0)throw new Error("Insufficient funds: no UTXOs available");P(e);const s=e.filter(o=>{const a=g.Buffer.from(o.scriptPubKey,"hex");return!!m.script.decompile(a)});if(s.length===0)throw new Error("Insufficient funds: no valid UTXOs available (all have invalid scripts)");const c=[...s].sort((o,a)=>a.value-o.value),i=[];let u=0n,l=0n;for(const o of c){i.push(o),u+=BigInt(o.value);const a=p({numInputs:i.length,numOutputs:r,feeRate:n}),d=T(n);if(u<t+a){l=a;continue}const w=E({totalInputValue:u,peginAmount:t,baseFee:a,changeOutputFee:d});return{selectedUTXOs:i,totalValue:u,fee:w.fee,changeAmount:w.changeAmount}}throw new Error(`Insufficient funds: need ${t+l} sats (${t} pegin + ${l} fee), have ${u} sats`)}function U(e){return e>f.DUST_THRESHOLD}function F(){return f.BTC_DUST_SAT}function v(e){const t=e.startsWith("0x")?e.slice(2):e;return`0x${m.Transaction.fromHex(t).getId()}`}var h=(e=>(e.P2PKH="P2PKH",e.P2SH="P2SH",e.P2WPKH="P2WPKH",e.P2WSH="P2WSH",e.P2TR="P2TR",e.UNKNOWN="UNKNOWN",e))(h||{});function _(e){const t=e.length;return t===25&&e[0]===118&&e[1]===169&&e[2]===20&&e[23]===136&&e[24]===172?"P2PKH":t===23&&e[0]===169&&e[1]===20&&e[22]===135?"P2SH":t===22&&e[0]===0&&e[1]===20?"P2WPKH":t===34&&e[0]===0&&e[1]===32?"P2WSH":t===34&&e[0]===81&&e[1]===32?"P2TR":"UNKNOWN"}function A(e,t){const n=g.Buffer.from(e.scriptPubKey,"hex"),r=_(n);switch(r){case h.P2WPKH:return{witnessUtxo:{script:n,value:e.value}};case h.P2WSH:{if(!e.witnessScript)throw new Error("Missing witnessScript for P2WSH input");return{witnessUtxo:{script:n,value:e.value},witnessScript:g.Buffer.from(e.witnessScript,"hex")}}case h.P2TR:{if(t&&t.length!==32)throw new Error(`Invalid tapInternalKey length: expected 32 bytes, got ${t.length}`);return{witnessUtxo:{script:n,value:e.value},...t&&{tapInternalKey:t}}}default:throw new Error(`Unsupported script type: ${r}`)}}const $={1:"https://safe-transaction-mainnet.safe.global",11155111:"https://safe-transaction-sepolia.safe.global"},x=5e3,H=14400*1e3,C=1e4;async function R(e){const{publicClient:t,walletAddress:n,hash:r,confirmations:s,timeout:c,safePollTimeoutMs:i=H,safePollIntervalMs:u=x}=e,l=await t.getCode({address:n});if(!(l!==void 0&&l!=="0x"))return t.waitForTransactionReceipt({hash:r,confirmations:s,timeout:c});const a=await t.getChainId(),d=await B({chainId:a,safeTxHash:r,pollIntervalMs:u,timeoutMs:i});return t.waitForTransactionReceipt({hash:d,confirmations:s})}async function B({chainId:e,safeTxHash:t,pollIntervalMs:n,timeoutMs:r}){const s=$[e];if(!s)throw new Error(`Safe Transaction Service not configured for chainId ${e}. 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=`${s}/api/v1/multisig-transactions/${t}/`,i=Date.now()+r;for(;Date.now()<i;){const u=new AbortController,l=setTimeout(()=>u.abort(),C);let o;try{o=await fetch(c,{signal:u.signal})}catch(a){console.warn(`Safe Transaction Service request failed (will retry in ${n}ms): `+(a instanceof Error?a.message:String(a))),await S(n);continue}finally{clearTimeout(l)}if(o.ok){const a=await o.json();if(a.isExecuted){if(a.isSuccessful===!1)throw new Error(`Safe transaction ${t} was executed on chain but reverted. Check the Safe queue UI for details.`);if(a.transactionHash)return a.transactionHash}}else if(o.status!==404)if(o.status>=500)console.warn(`Safe Transaction Service returned ${o.status} for ${t}; retrying in ${n}ms.`);else throw new Error(`Safe Transaction Service returned ${o.status} for ${t}.`);await S(n)}throw new Error(`Timed out after ${r}ms waiting for Safe transaction ${t} 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 S(e){return new Promise(t=>{setTimeout(t,e)})}exports.BitcoinScriptType=h;exports.applyChangeOutputPolicy=E;exports.calculateBtcTxHash=v;exports.computeChangeOutputFeeSats=T;exports.computeMaxDeposit=I;exports.computePeginBaseFeeSats=p;exports.getDustThreshold=F;exports.getPsbtInputFields=A;exports.getScriptType=_;exports.selectUtxosForPegin=O;exports.shouldAddChangeOutput=U;exports.waitForTransactionReceiptSmartAware=R;
|
|
2
|
+
//# sourceMappingURL=waitForTransactionReceiptSmartAware-BFMQFEzj.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waitForTransactionReceiptSmartAware-tv1mtSIY.cjs","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.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 Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\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 detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\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 * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} 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\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\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 const isSmartAccount = code !== undefined && code !== \"0x\";\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 realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\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}/api/v1/multisig-transactions/${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 data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\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","calculateBtcTxHash","txHex","cleanHex","Transaction","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","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"waitForTransactionReceiptSmartAware-BFMQFEzj.cjs","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.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 Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\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 detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\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 * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} 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\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\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 const isSmartAccount = code !== undefined && code !== \"0x\";\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 realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\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}/api/v1/multisig-transactions/${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 data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\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","calculateBtcTxHash","txHex","cleanHex","Transaction","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","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { script as E, Transaction as T } from "bitcoinjs-lib";
|
|
2
2
|
import { Buffer as d } from "buffer";
|
|
3
|
-
import { D as p, M as m, r as I, P as _, T as P, B as v } from "./fundPeginTransaction-
|
|
3
|
+
import { D as p, M as m, r as I, P as _, T as P, B as v } from "./fundPeginTransaction-C11tYf6I.js";
|
|
4
4
|
function S(t) {
|
|
5
5
|
const { numInputs: e, numOutputs: n, feeRate: a } = t;
|
|
6
6
|
if (!Number.isInteger(e) || e < 0)
|
|
@@ -262,4 +262,4 @@ export {
|
|
|
262
262
|
L as s,
|
|
263
263
|
q as w
|
|
264
264
|
};
|
|
265
|
-
//# sourceMappingURL=waitForTransactionReceiptSmartAware-
|
|
265
|
+
//# sourceMappingURL=waitForTransactionReceiptSmartAware-Dt5VcMK0.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waitForTransactionReceiptSmartAware-CmgFXFza.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.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 Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\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 detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\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 * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} 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\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\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 const isSmartAccount = code !== undefined && code !== \"0x\";\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 realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\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}/api/v1/multisig-transactions/${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 data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\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","calculateBtcTxHash","txHex","cleanHex","Transaction","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","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"waitForTransactionReceiptSmartAware-Dt5VcMK0.js","sources":["../src/tbv/core/utils/fee/peginFeeMath.ts","../src/tbv/core/utils/utxo/selectUtxos.ts","../src/tbv/core/utils/transaction/btcTxHash.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 Transaction Hash Utilities\n *\n * Provides utilities for calculating Bitcoin transaction hashes in a way that matches\n * the contract's BtcUtils.hashBtcTx() implementation.\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Hex } from \"viem\";\n\n/**\n * Calculate Bitcoin transaction hash\n *\n * This matches the contract's BtcUtils.hashBtcTx() implementation:\n * 1. Double SHA256 the transaction bytes\n * 2. Reverse the byte order (Bitcoin convention)\n *\n * The resulting hash is used as the unique vault identifier in the BTCVaultRegistry contract.\n *\n * @param txHex - Transaction hex (with or without 0x prefix)\n * @returns The transaction hash as Hex (with 0x prefix)\n */\nexport function calculateBtcTxHash(txHex: string): Hex {\n // Remove 0x prefix if present\n const cleanHex = txHex.startsWith(\"0x\") ? txHex.slice(2) : txHex;\n\n // Use bitcoinjs-lib to calculate transaction ID (already does double SHA256 + reverse)\n const tx = Transaction.fromHex(cleanHex);\n const txid = tx.getId();\n\n // Return with 0x prefix to match Ethereum hex format\n return `0x${txid}` as Hex;\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 detects an EOA\n * via `eth_getCode` returning empty bytecode and delegates unchanged.\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 * @module utils/eth\n */\n\nimport type {\n Address,\n Hash,\n PublicClient,\n TransactionReceipt,\n} 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\nexport interface WaitForTransactionReceiptSmartAwareParams {\n publicClient: PublicClient;\n walletAddress: Address;\n hash: Hash;\n confirmations?: number;\n /**\n * Forwarded to viem on the EOA (externally owned account) path.\n * Ignored on the smart-account path — see safePollTimeoutMs.\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 const isSmartAccount = code !== undefined && code !== \"0x\";\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 realTxHash = await pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash: hash,\n pollIntervalMs: safePollIntervalMs,\n timeoutMs: safePollTimeoutMs,\n });\n\n return publicClient.waitForTransactionReceipt({\n hash: realTxHash,\n confirmations,\n });\n}\n\ninterface SafeMultisigTransaction {\n isExecuted: boolean;\n isSuccessful: boolean | null;\n transactionHash: Hash | null;\n}\n\nasync function pollSafeTransactionServiceUntilExecuted({\n chainId,\n safeTxHash,\n pollIntervalMs,\n timeoutMs,\n}: {\n chainId: number;\n safeTxHash: Hash;\n pollIntervalMs: number;\n timeoutMs: number;\n}): Promise<Hash> {\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}/api/v1/multisig-transactions/${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 data.transactionHash;\n }\n }\n } else if (response.status === 404) {\n // Proposal not yet indexed — keep polling silently.\n } else if (response.status >= 500) {\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","calculateBtcTxHash","txHex","cleanHex","Transaction","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","waitForTransactionReceiptSmartAware","publicClient","walletAddress","hash","confirmations","timeout","safePollTimeoutMs","safePollIntervalMs","code","chainId","realTxHash","pollSafeTransactionServiceUntilExecuted","safeTxHash","pollIntervalMs","timeoutMs","baseUrl","url","deadline","controller","fetchTimeoutId","response","err","sleep","data","ms","resolve"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PayoutManager-B5bovfkD.cjs","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/managers/PayoutManager.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Best-effort reader for the auth-anchor OP_RETURN payload at `vout` of\n * a funded Pre-PegIn transaction.\n *\n * Returns the 32-byte payload as lowercase hex (no `0x` prefix) if the\n * output at `vout` is exactly `OP_RETURN || PUSH32 || <32 bytes>` with\n * a zero value. Returns `undefined` for any structural mismatch —\n * missing output, wrong script shape, non-zero value — so legacy\n * non-auth-anchored Pre-PegIns parse as \"no anchor\" rather than\n * raising.\n *\n * Used by the refund flow to reconstruct the unfunded WASM template\n * with the same output shape as the on-chain funded transaction.\n * Assertion semantics (compare against an expected value, throw on\n * mismatch) live in {@link assertAuthAnchorOpReturn}.\n */\nexport function readAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vout: number,\n): string | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n // Best-effort: unparseable hex is also \"no extractable anchor\".\n // The same hex flows into the refund PSBT primitive immediately\n // after, where Transaction.fromHex will surface a real parse error.\n return undefined;\n }\n\n if (tx.outs.length <= vout) return undefined;\n\n const output = tx.outs[vout];\n const script = output.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n return undefined;\n }\n if (output.value !== 0) return undefined;\n\n return script.slice(2).toString(\"hex\").toLowerCase();\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../shared/wallets\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\nimport {\n assertPsbtUnsignedTxMatches,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(\n transactions: SignPayoutParams[],\n ): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","extractPayoutSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PayoutManager-DChODEOJ.js","sources":["../src/tbv/core/managers/pegin/assertAuthAnchorOpReturn.ts","../src/tbv/core/services/htlc/index.ts","../src/tbv/core/managers/PayoutManager.ts"],"sourcesContent":["/**\n * Structural verifier for the auth-anchor OP_RETURN in a funded\n * Pre-PegIn transaction.\n *\n * @module managers/pegin/assertAuthAnchorOpReturn\n */\n\nimport * as bitcoin from \"bitcoinjs-lib\";\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/** OP_RETURN opcode. */\nconst OP_RETURN = 0x6a;\n/** Push-32-bytes opcode (raw push, not OP_PUSHDATA1). */\nconst OP_PUSH32 = 0x20;\n/** Encoded length of a standard OP_RETURN script with a 32-byte payload. */\nconst OP_RETURN_PUSH32_SCRIPT_LEN = 1 + 1 + 32;\n\n/**\n * Verify the broadcast Pre-PegIn carries the expected OP_RETURN\n * commitment to the auth anchor.\n *\n * The OP_RETURN sits at `vout = vaultCount` (right after the per-vault\n * HTLC outputs and before the depositor-claim/change outputs) and\n * pushes the 32-byte `SHA256(authAnchor)`. The script encoding is\n * exactly `OP_RETURN || PUSH32 || <32 bytes>` (34 bytes). A\n * non-conformant WASM build that omitted the OP_RETURN, swapped its\n * position, or changed its push payload would let the depositor\n * obtain a valid bearer token for a Pre-PegIn whose on-chain\n * commitment doesn't actually bind the anchor — degrading the auth\n * from on-chain-bound to a shared secret. Fail closed.\n *\n * @throws If the OP_RETURN is missing, mis-located, mis-encoded, or\n * pushes a payload other than `expectedAuthAnchorHashHex`.\n */\nexport function assertAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vaultCount: number,\n expectedAuthAnchorHashHex: string,\n): void {\n const cleanHex = stripHexPrefix(fundedPrePeginTxHex);\n const tx = bitcoin.Transaction.fromHex(cleanHex);\n\n if (tx.outs.length <= vaultCount) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN missing: tx has ${tx.outs.length} ` +\n `outputs, expected at least ${vaultCount + 1} (vault outputs + OP_RETURN)`,\n );\n }\n\n const opReturnOutput = tx.outs[vaultCount];\n const script = opReturnOutput.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has unexpected ` +\n `script encoding (got ${script.length}-byte script with prefix ` +\n `0x${script.slice(0, Math.min(2, script.length)).toString(\"hex\")}; ` +\n `expected ${OP_RETURN_PUSH32_SCRIPT_LEN}-byte OP_RETURN + PUSH32 layout)`,\n );\n }\n\n const pushedHex = script.slice(2).toString(\"hex\").toLowerCase();\n if (pushedHex !== expectedAuthAnchorHashHex.toLowerCase()) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN payload mismatch at vout ${vaultCount}: ` +\n `tx pushes ${pushedHex}, expected ${expectedAuthAnchorHashHex}`,\n );\n }\n\n if (opReturnOutput.value !== 0) {\n throw new Error(\n `Pre-PegIn auth-anchor OP_RETURN at vout ${vaultCount} has non-zero ` +\n `value ${opReturnOutput.value}; OP_RETURN outputs must be 0-value`,\n );\n }\n}\n\n/**\n * Best-effort reader for the auth-anchor OP_RETURN payload at `vout` of\n * a funded Pre-PegIn transaction.\n *\n * Returns the 32-byte payload as lowercase hex (no `0x` prefix) if the\n * output at `vout` is exactly `OP_RETURN || PUSH32 || <32 bytes>` with\n * a zero value. Returns `undefined` for any structural mismatch —\n * missing output, wrong script shape, non-zero value — so legacy\n * non-auth-anchored Pre-PegIns parse as \"no anchor\" rather than\n * raising.\n *\n * Used by the refund flow to reconstruct the unfunded WASM template\n * with the same output shape as the on-chain funded transaction.\n * Assertion semantics (compare against an expected value, throw on\n * mismatch) live in {@link assertAuthAnchorOpReturn}.\n */\nexport function readAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n vout: number,\n): string | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n // Best-effort: unparseable hex is also \"no extractable anchor\".\n // The same hex flows into the refund PSBT primitive immediately\n // after, where Transaction.fromHex will surface a real parse error.\n return undefined;\n }\n\n if (tx.outs.length <= vout) return undefined;\n\n const output = tx.outs[vout];\n const script = output.script;\n if (\n script.length !== OP_RETURN_PUSH32_SCRIPT_LEN ||\n script[0] !== OP_RETURN ||\n script[1] !== OP_PUSH32\n ) {\n return undefined;\n }\n if (output.value !== 0) return undefined;\n\n return script.slice(2).toString(\"hex\").toLowerCase();\n}\n\n/**\n * Scan a funded Pre-PegIn transaction for its auth-anchor commitment\n * (an `OP_RETURN || PUSH32 || <32 bytes>` output with value 0).\n *\n * Returns `{ vout, hash }` when exactly one such output is found.\n * Returns `undefined` when:\n * - the hex is unparseable,\n * - no matching output exists (legacy non-auth-anchored Pre-PegIn),\n * - more than one matching output exists (ambiguous / malformed).\n *\n * Used by the refund orchestrator to (a) locate the on-chain anchor\n * regardless of how many HTLCs preceded it and (b) detect multi-vault\n * funded transactions structurally: the single-vault refund path\n * reconstructs only one hashlock and expects the anchor at vout 1, so\n * any other vout signals a layout this call cannot safely refund.\n */\nexport function findAuthAnchorOpReturn(\n fundedPrePeginTxHex: string,\n): { vout: number; hash: string } | undefined {\n let tx: bitcoin.Transaction;\n try {\n tx = bitcoin.Transaction.fromHex(stripHexPrefix(fundedPrePeginTxHex));\n } catch {\n return undefined;\n }\n\n const hits: { vout: number; hash: string }[] = [];\n for (let i = 0; i < tx.outs.length; i++) {\n const output = tx.outs[i];\n const script = output.script;\n if (\n script.length === OP_RETURN_PUSH32_SCRIPT_LEN &&\n script[0] === OP_RETURN &&\n script[1] === OP_PUSH32 &&\n output.value === 0\n ) {\n hits.push({\n vout: i,\n hash: script.slice(2).toString(\"hex\").toLowerCase(),\n });\n }\n }\n\n return hits.length === 1 ? hits[0] : undefined;\n}\n","/**\n * HTLC Secret / Hashlock Utilities\n *\n * Pure functions for computing and validating SHA-256 hashlocks used in the\n * vault deposit protocol's HTLC (Hash Time Lock Contract).\n *\n * The SDK does NOT generate secrets — that is the caller's responsibility.\n * Today callers use `crypto.getRandomValues(32)`; when the `deriveContextHash`\n * wallet API ships, callers will use `wallet.deriveContextHash(\"babylon-btc-vault\", ctx)`.\n * These utilities work identically regardless of how the secret was produced.\n *\n * On-chain contract validation (BTCVaultRegistry.activateVaultWithSecret):\n * if (sha256(abi.encodePacked(s)) != hashlock) revert InvalidSecret();\n *\n * @module htlc\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport type { Hex } from \"viem\";\n\n/** Expected hex length for a 0x-prefixed bytes32 value. */\nconst HEX_BYTES32_LENGTH = 66; // \"0x\" + 64 hex chars\n\n/**\n * Decode a 0x-prefixed hex string to bytes, with strict validation.\n * @throws if the input is not a valid 0x-prefixed hex string\n */\nfunction hexToBytes(hex: Hex): Uint8Array {\n if (!hex.startsWith(\"0x\") && !hex.startsWith(\"0X\")) {\n throw new Error(\"Expected 0x-prefixed hex string\");\n }\n const clean = hex.slice(2);\n if (clean.length % 2 !== 0) {\n throw new Error(`Hex string has odd length: ${clean.length}`);\n }\n if (!/^[0-9a-fA-F]*$/.test(clean)) {\n throw new Error(\"Hex string contains non-hex characters\");\n }\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Encode a Uint8Array as a 0x-prefixed lowercase hex string.\n */\nfunction bytesToHex(bytes: Uint8Array): Hex {\n return `0x${Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")}`;\n}\n\n/**\n * Validate that a value is a 0x-prefixed bytes32 (exactly 32 bytes).\n * @throws if the value is not exactly 32 bytes\n */\nfunction assertBytes32(value: Hex, label: string): void {\n if (value.length !== HEX_BYTES32_LENGTH) {\n throw new Error(\n `${label} must be exactly 32 bytes (${HEX_BYTES32_LENGTH} hex chars with 0x prefix), got ${value.length}`,\n );\n }\n}\n\n/**\n * Compute the SHA-256 hashlock from a secret preimage.\n *\n * Matches the on-chain validation: `sha256(abi.encodePacked(s))` where `s` is a `bytes32`.\n * `abi.encodePacked(bytes32)` is just the raw 32 bytes — no ABI padding.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @returns 0x-prefixed bytes32 SHA-256 hash\n * @throws if secret is not exactly 32 bytes\n */\nexport function computeHashlock(secret: Hex): Hex {\n assertBytes32(secret, \"Secret\");\n const secretBytes = hexToBytes(secret);\n const hash = sha256(secretBytes);\n return bytesToHex(hash);\n}\n\n/**\n * Validate that a secret's SHA-256 hash matches the expected hashlock.\n *\n * Use this for client-side pre-validation before sending the activation\n * transaction to avoid wasting gas on a contract revert.\n *\n * @param secret - 0x-prefixed bytes32 secret (66 hex chars)\n * @param hashlock - 0x-prefixed bytes32 expected hashlock from the vault\n * @returns true if SHA-256(secret) matches the hashlock\n * @throws if secret or hashlock is not exactly 32 bytes\n */\nexport function validateSecretAgainstHashlock(\n secret: Hex,\n hashlock: Hex,\n): boolean {\n assertBytes32(secret, \"Secret\");\n assertBytes32(hashlock, \"Hashlock\");\n // Validate hashlock is valid hex (secret is validated inside computeHashlock)\n hexToBytes(hashlock);\n\n const computed = computeHashlock(secret);\n return computed.toLowerCase() === hashlock.toLowerCase();\n}\n","/**\n * Payout Manager\n *\n * High-level manager that orchestrates the payout signing flow by coordinating\n * SDK primitives ({@link buildPayoutPsbt}, {@link extractPayoutSignature})\n * with a user-provided Bitcoin wallet.\n *\n * The Payout transaction references the Assert transaction (input 1).\n *\n * @see {@link PeginManager} - For Steps 1–4 of the peg-in flow\n * @see {@link buildPayoutPsbt} - Lower-level primitive for custom implementations\n * @see {@link extractPayoutSignature} - Extract signatures from signed PSBTs\n *\n * @module managers/PayoutManager\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../shared/wallets\";\nimport { createTaprootScriptPathSignOptions } from \"../utils/signing\";\nimport {\n assertPsbtUnsignedTxMatches,\n buildPayoutPsbt,\n extractPayoutSignature,\n validateWalletPubkey,\n type Network,\n} from \"../primitives\";\n\n/**\n * Configuration for the PayoutManager.\n */\nexport interface PayoutManagerConfig {\n /**\n * Bitcoin network to use for transactions.\n */\n network: Network;\n\n /**\n * Bitcoin wallet for signing payout transactions.\n */\n btcWallet: BitcoinWallet;\n}\n\n/**\n * Base parameters shared by both payout transaction types.\n */\ninterface SignPayoutBaseParams {\n /**\n * Peg-in transaction hex.\n * The original transaction that created the vault output being spent.\n */\n peginTxHex: string;\n\n /**\n * Vault provider's BTC public key (x-only, 64-char hex).\n */\n vaultProviderBtcPubkey: string;\n\n /**\n * Vault keeper BTC public keys (x-only, 64-char hex).\n */\n vaultKeeperBtcPubkeys: string[];\n\n /**\n * Universal challenger BTC public keys (x-only, 64-char hex).\n */\n universalChallengerBtcPubkeys: string[];\n\n /**\n * CSV timelock in blocks for the PegIn output.\n */\n timelockPegin: number;\n\n /**\n * Depositor's BTC public key (x-only, 64-char hex). This MUST be the\n * key registered on-chain for the vault — typically read from\n * `BTCVaultRegistry.getBtcVaultBasicInfo(...).depositorBtcPubKey`.\n *\n * Required: omitting it would degrade `validateWalletPubkey` to a\n * self-comparison, allowing the wrong wallet to produce a signature\n * over a script tree that doesn't match the on-chain UTXO.\n */\n depositorBtcPubkey: string;\n\n /**\n * The on-chain registered depositor payout scriptPubKey (hex, with or without 0x prefix).\n * Used to validate that the VP-provided payout transaction actually pays to the\n * correct depositor payout address before signing.\n */\n registeredPayoutScriptPubKey: string;\n\n /**\n * The claimer's x-only BTC public key for this payout (64-char hex, no prefix).\n * Forwarded to {@link buildPayoutPsbt} for per-role output validation.\n */\n claimerBtcPubkey: string;\n\n /**\n * VP commission in basis points (`1..=9999`). Forwarded to {@link buildPayoutPsbt}.\n */\n commissionBps: number;\n}\n\n/**\n * Parameters for signing a Payout transaction.\n *\n * Payout is used in the challenge path after Assert, when the claimer proves validity.\n * Input 1 references the Assert transaction.\n */\nexport interface SignPayoutParams extends SignPayoutBaseParams {\n /**\n * Payout transaction hex (unsigned).\n * This is the transaction from the vault provider that needs depositor signature.\n */\n payoutTxHex: string;\n\n /**\n * Assert transaction hex.\n * Payout input 1 references Assert output 0.\n */\n assertTxHex: string;\n}\n\n/**\n * Result of signing a payout transaction.\n */\nexport interface PayoutSignatureResult {\n /**\n * 64-byte Schnorr signature (128 hex characters).\n */\n signature: string;\n\n /**\n * Depositor's BTC public key used for signing.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * High-level manager for payout transaction signing.\n *\n * @remarks\n * After registering your peg-in on Ethereum (Step 3), the vault provider prepares\n * claim/payout transaction pairs. You must sign each payout transaction using this\n * manager and submit the signatures to the vault provider's RPC API.\n *\n * **What happens internally:**\n * 1. Validates your wallet's public key matches the vault's depositor\n * 2. Builds an unsigned PSBT with taproot script path spend info\n * 3. Signs input 0 (the vault UTXO) with your wallet\n * 4. Extracts the 64-byte Schnorr signature\n *\n * **Note:** The payout transaction has 2 inputs. PayoutManager only signs input 0\n * (from the peg-in tx). Input 1 (from the assert tx) is signed by the vault provider.\n *\n * @see {@link PeginManager} - For the complete peg-in flow context\n * @see {@link buildPayoutPsbt} - Lower-level primitive used internally\n * @see {@link extractPayoutSignature} - Signature extraction primitive\n */\nexport class PayoutManager {\n private readonly config: PayoutManagerConfig;\n\n /**\n * Creates a new PayoutManager instance.\n *\n * @param config - Manager configuration including wallet\n */\n constructor(config: PayoutManagerConfig) {\n this.config = config;\n }\n\n /**\n * Signs a Payout transaction and extracts the Schnorr signature.\n *\n * Flow:\n * 1. Vault provider submits Claim transaction\n * 2. Claimer submits Assert transaction to prove validity\n * 3. Payout can be executed (references Assert tx)\n *\n * This method orchestrates the following steps:\n * 1. Get wallet's public key and convert to x-only format\n * 2. Validate wallet pubkey matches on-chain depositor pubkey (if provided)\n * 3. Build unsigned PSBT using primitives\n * 4. Sign PSBT via btcWallet.signPsbt()\n * 5. Extract 64-byte Schnorr signature using primitives\n *\n * The returned signature can be submitted to the vault provider API.\n *\n * @param params - Payout signing parameters\n * @returns Signature result with 64-byte Schnorr signature and depositor pubkey\n * @throws Error if wallet pubkey doesn't match depositor pubkey\n * @throws Error if wallet operations fail or signature extraction fails\n */\n async signPayoutTransaction(\n params: SignPayoutParams,\n ): Promise<PayoutSignatureResult> {\n // Validate wallet pubkey matches depositor and get both formats\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n params.depositorBtcPubkey,\n );\n\n // Build unsigned PSBT for Payout (uses Assert tx). Per-role output\n // validation happens inside buildPayoutPsbt against the resolved input\n // values.\n const payoutPsbt = await buildPayoutPsbt({\n payoutTxHex: params.payoutTxHex,\n peginTxHex: params.peginTxHex,\n assertTxHex: params.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: params.universalChallengerBtcPubkeys,\n timelockPegin: params.timelockPegin,\n network: this.config.network,\n claimerBtcPubkey: params.claimerBtcPubkey,\n registeredPayoutScriptPubKey: params.registeredPayoutScriptPubKey,\n commissionBps: params.commissionBps,\n });\n\n // Sign PSBT via wallet (Taproot script-path spend, input 0 only)\n const signedPsbtHex = await this.config.btcWallet.signPsbt(\n payoutPsbt.psbtHex,\n createTaprootScriptPathSignOptions(walletPubkeyRaw, 1),\n );\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: payoutPsbt.psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Extract Schnorr signature\n const signature = extractPayoutSignature(signedPsbtHex, depositorPubkey);\n\n return {\n signature,\n depositorBtcPubkey: depositorPubkey,\n };\n }\n\n /**\n * Gets the configured Bitcoin network.\n *\n * @returns The Bitcoin network (mainnet, testnet, signet, regtest)\n */\n getNetwork(): Network {\n return this.config.network;\n }\n\n /**\n * Checks if the wallet supports batch signing (signPsbts).\n *\n * @returns true if batch signing is supported\n */\n supportsBatchSigning(): boolean {\n return typeof this.config.btcWallet.signPsbts === \"function\";\n }\n\n /**\n * Batch signs multiple payout transactions (1 per claimer).\n * This allows signing all transactions with a single wallet interaction.\n *\n * @param transactions - Array of payout params to sign\n * @returns Array of signature results matching input order\n * @throws Error if wallet doesn't support batch signing\n * @throws Error if any signing operation fails\n */\n async signPayoutTransactionsBatch(\n transactions: SignPayoutParams[],\n ): Promise<\n Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }>\n > {\n if (!this.supportsBatchSigning()) {\n throw new Error(\n \"Wallet does not support batch signing (signPsbts method not available)\",\n );\n }\n\n // Get wallet pubkey once\n const walletPubkeyRaw = await this.config.btcWallet.getPublicKeyHex();\n\n // Build all PSBTs (1 per claimer)\n const psbtsToSign: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const depositorPubkeys: string[] = [];\n\n for (const tx of transactions) {\n // Validate wallet pubkey matches depositor\n const { depositorPubkey } = validateWalletPubkey(\n walletPubkeyRaw,\n tx.depositorBtcPubkey,\n );\n depositorPubkeys.push(depositorPubkey);\n\n // Build Payout PSBT (output validation runs inside buildPayoutPsbt\n // against resolved input values).\n const payoutPsbt = await buildPayoutPsbt({\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: tx.peginTxHex,\n assertTxHex: tx.assertTxHex,\n depositorBtcPubkey: depositorPubkey,\n vaultProviderBtcPubkey: tx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: tx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: tx.universalChallengerBtcPubkeys,\n timelockPegin: tx.timelockPegin,\n network: this.config.network,\n claimerBtcPubkey: tx.claimerBtcPubkey,\n registeredPayoutScriptPubKey: tx.registeredPayoutScriptPubKey,\n commissionBps: tx.commissionBps,\n });\n psbtsToSign.push(payoutPsbt.psbtHex);\n signOptions.push(createTaprootScriptPathSignOptions(walletPubkeyRaw, 1));\n }\n\n // Batch sign all PSBTs with single wallet interaction\n const signedPsbts = await this.config.btcWallet.signPsbts!(\n psbtsToSign,\n signOptions,\n );\n\n // Validate that wallet returned the expected number of signed PSBTs\n if (signedPsbts.length !== transactions.length) {\n throw new Error(\n `Expected ${transactions.length} signed PSBTs but received ${signedPsbts.length}`,\n );\n }\n\n // Extract signatures from signed PSBTs\n const results: Array<{\n payoutSignature: string;\n depositorBtcPubkey: string;\n }> = [];\n\n for (let i = 0; i < transactions.length; i++) {\n const depositorPubkey = depositorPubkeys[i];\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtsToSign[i],\n returnedPsbtHex: signedPsbts[i],\n });\n const payoutSignature = extractPayoutSignature(\n signedPsbts[i],\n depositorPubkey,\n );\n\n results.push({\n payoutSignature,\n depositorBtcPubkey: depositorPubkey,\n });\n }\n\n return results;\n }\n\n}\n"],"names":["OP_RETURN","OP_PUSH32","OP_RETURN_PUSH32_SCRIPT_LEN","assertAuthAnchorOpReturn","fundedPrePeginTxHex","vaultCount","expectedAuthAnchorHashHex","cleanHex","stripHexPrefix","tx","bitcoin","opReturnOutput","script","pushedHex","findAuthAnchorOpReturn","hits","i","output","HEX_BYTES32_LENGTH","hexToBytes","hex","clean","bytes","bytesToHex","b","assertBytes32","value","label","computeHashlock","secret","secretBytes","hash","sha256","validateSecretAgainstHashlock","hashlock","PayoutManager","config","__publicField","params","walletPubkeyRaw","depositorPubkey","validateWalletPubkey","payoutPsbt","buildPayoutPsbt","signedPsbtHex","createTaprootScriptPathSignOptions","assertPsbtUnsignedTxMatches","extractPayoutSignature","transactions","psbtsToSign","signOptions","depositorPubkeys","signedPsbts","results","payoutSignature"],"mappings":"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"}
|