@babylonlabs-io/ts-sdk 3.2.0 → 3.3.0
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-DOdsgcUR.cjs → PayoutManager-B1Ev0r7o.cjs} +2 -2
- package/dist/{PayoutManager-DOdsgcUR.cjs.map → PayoutManager-B1Ev0r7o.cjs.map} +1 -1
- package/dist/{PayoutManager-BZ212yml.js → PayoutManager-xEGvTbhL.js} +3 -3
- package/dist/{PayoutManager-BZ212yml.js.map → PayoutManager-xEGvTbhL.js.map} +1 -1
- package/dist/{PeginManager-DcwGom01.js → PeginManager-BJnUgW8C.js} +7 -7
- package/dist/{PeginManager-DcwGom01.js.map → PeginManager-BJnUgW8C.js.map} +1 -1
- package/dist/{PeginManager-BHplS4dM.cjs → PeginManager-Co43rW_x.cjs} +2 -2
- package/dist/{PeginManager-BHplS4dM.cjs.map → PeginManager-Co43rW_x.cjs.map} +1 -1
- package/dist/ProtocolParams.abi-BgEZaQ2I.cjs +2 -0
- package/dist/ProtocolParams.abi-BgEZaQ2I.cjs.map +1 -0
- package/dist/{ProtocolParams.abi-CKOVqFJP.js → ProtocolParams.abi-Cv77fsUe.js} +15 -9
- package/dist/ProtocolParams.abi-Cv77fsUe.js.map +1 -0
- package/dist/{buildAndBroadcastReclaim-CcQ0b1be.js → buildAndBroadcastReclaim-CQANDXVJ.js} +846 -822
- package/dist/buildAndBroadcastReclaim-CQANDXVJ.js.map +1 -0
- package/dist/buildAndBroadcastReclaim-DPk36Uhp.cjs +2 -0
- package/dist/buildAndBroadcastReclaim-DPk36Uhp.cjs.map +1 -0
- package/dist/challengeAssert-B2otuf7y.js +509 -0
- package/dist/challengeAssert-B2otuf7y.js.map +1 -0
- package/dist/challengeAssert-C3eMcM5Y.cjs +2 -0
- package/dist/challengeAssert-C3eMcM5Y.cjs.map +1 -0
- package/dist/constants-CAm10Qwp.cjs +2 -0
- package/dist/constants-CAm10Qwp.cjs.map +1 -0
- package/dist/constants-CvQZuxKv.js +48 -0
- package/dist/constants-CvQZuxKv.js.map +1 -0
- package/dist/{index-CVBtBjQ5.js → index-BRsWXDlv.js} +6 -6
- package/dist/{index-CVBtBjQ5.js.map → index-BRsWXDlv.js.map} +1 -1
- package/dist/{index-DpjYs5yr.cjs → index-C6avwx1s.cjs} +2 -2
- package/dist/{index-DpjYs5yr.cjs.map → index-C6avwx1s.cjs.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +302 -303
- package/dist/index.js.map +1 -1
- package/dist/{pegin-registration-client-BogzOk8B.cjs → pegin-registration-client-CdRyXP3j.cjs} +2 -2
- package/dist/{pegin-registration-client-BogzOk8B.cjs.map → pegin-registration-client-CdRyXP3j.cjs.map} +1 -1
- package/dist/{pegin-registration-client-DvyrUNku.js → pegin-registration-client-Cvjk4ILq.js} +3 -3
- package/dist/{pegin-registration-client-DvyrUNku.js.map → pegin-registration-client-Cvjk4ILq.js.map} +1 -1
- package/dist/{primeVpAuth-BPj2TPdD.cjs → primeVpAuth-CXNXYKrh.cjs} +2 -2
- package/dist/{primeVpAuth-BPj2TPdD.cjs.map → primeVpAuth-CXNXYKrh.cjs.map} +1 -1
- package/dist/{primeVpAuth-CiCdRNGf.js → primeVpAuth-D4UWXQ76.js} +2 -2
- package/dist/{primeVpAuth-CiCdRNGf.js.map → primeVpAuth-D4UWXQ76.js.map} +1 -1
- package/dist/{registration-records-CKLAv3gw.js → registration-records-DJJC2NiO.js} +92 -90
- package/dist/{registration-records-CKLAv3gw.js.map → registration-records-DJJC2NiO.js.map} +1 -1
- package/dist/{registration-records-DFQgBaYi.cjs → registration-records-XQMIKjGV.cjs} +2 -2
- package/dist/{registration-records-DFQgBaYi.cjs.map → registration-records-XQMIKjGV.cjs.map} +1 -1
- package/dist/{signer-set-reader-DFF9MgQN.js → signer-set-reader-CSUe92zE.js} +2 -2
- package/dist/{signer-set-reader-DFF9MgQN.js.map → signer-set-reader-CSUe92zE.js.map} +1 -1
- package/dist/{signer-set-reader-D-CyIYR5.cjs → signer-set-reader-GrFe_rsW.cjs} +2 -2
- package/dist/{signer-set-reader-D-CyIYR5.cjs.map → signer-set-reader-GrFe_rsW.cjs.map} +1 -1
- package/dist/tbv/core/clients/eth/index.cjs +1 -1
- package/dist/tbv/core/clients/eth/index.js +4 -4
- package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts +1 -0
- package/dist/tbv/core/clients/eth/protocol-params-reader.d.ts.map +1 -1
- package/dist/tbv/core/clients/eth/protocol-params-validation.d.ts.map +1 -1
- package/dist/tbv/core/clients/eth/types.d.ts +12 -6
- package/dist/tbv/core/clients/eth/types.d.ts.map +1 -1
- package/dist/tbv/core/clients/index.cjs +1 -1
- package/dist/tbv/core/clients/index.js +6 -6
- package/dist/tbv/core/clients/vault-provider/status/index.cjs +1 -1
- package/dist/tbv/core/clients/vault-provider/status/index.js +1 -1
- package/dist/tbv/core/contracts/abis/ProtocolParams.abi.d.ts +8 -0
- package/dist/tbv/core/contracts/abis/ProtocolParams.abi.d.ts.map +1 -1
- package/dist/tbv/core/contracts/index.cjs +1 -1
- package/dist/tbv/core/contracts/index.js +1 -1
- package/dist/tbv/core/index.cjs +1 -1
- package/dist/tbv/core/index.js +300 -301
- package/dist/tbv/core/index.js.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/primitives/index.cjs +1 -1
- package/dist/tbv/core/primitives/index.js +29 -30
- package/dist/tbv/core/primitives/index.js.map +1 -1
- package/dist/tbv/core/primitives/psbt/__tests__/challengeAssert.test.d.ts +2 -0
- package/dist/tbv/core/primitives/psbt/__tests__/challengeAssert.test.d.ts.map +1 -0
- package/dist/tbv/core/primitives/psbt/__tests__/noPayout.test.d.ts +2 -0
- package/dist/tbv/core/primitives/psbt/__tests__/noPayout.test.d.ts.map +1 -0
- package/dist/tbv/core/primitives/psbt/challengeAssert.d.ts +43 -0
- package/dist/tbv/core/primitives/psbt/challengeAssert.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/constants.d.ts +50 -2
- package/dist/tbv/core/primitives/psbt/constants.d.ts.map +1 -1
- package/dist/tbv/core/primitives/psbt/noPayout.d.ts +42 -0
- package/dist/tbv/core/primitives/psbt/noPayout.d.ts.map +1 -1
- package/dist/tbv/core/services/deposit/__tests__/signDepositorGraph.realGraph.test.d.ts +12 -0
- package/dist/tbv/core/services/deposit/__tests__/signDepositorGraph.realGraph.test.d.ts.map +1 -0
- package/dist/tbv/core/services/deposit/runDepositorPresignFlow.d.ts +7 -0
- package/dist/tbv/core/services/deposit/runDepositorPresignFlow.d.ts.map +1 -1
- package/dist/tbv/core/services/deposit/signDepositorGraph.d.ts +46 -1
- package/dist/tbv/core/services/deposit/signDepositorGraph.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/wasm/__tests__/challengeAssertOutputConnector.test.d.ts +2 -0
- package/dist/tbv/core/wasm/__tests__/challengeAssertOutputConnector.test.d.ts.map +1 -0
- package/dist/tbv/core/wasm/index.cjs +1 -1
- package/dist/tbv/core/wasm/index.cjs.map +1 -1
- package/dist/tbv/core/wasm/index.d.ts +14 -2
- package/dist/tbv/core/wasm/index.d.ts.map +1 -1
- package/dist/tbv/core/wasm/index.js +81 -77
- package/dist/tbv/core/wasm/index.js.map +1 -1
- package/dist/tbv/index.cjs +1 -1
- package/dist/tbv/index.js +300 -301
- package/dist/tbv/index.js.map +1 -1
- package/dist/{toRefundInputs-8DgBT_Bg.cjs → toRefundInputs-DTuWi--u.cjs} +2 -2
- package/dist/{toRefundInputs-8DgBT_Bg.cjs.map → toRefundInputs-DTuWi--u.cjs.map} +1 -1
- package/dist/{toRefundInputs-CCMNAoRx.js → toRefundInputs-Dzw1cteD.js} +5 -5
- package/dist/{toRefundInputs-CCMNAoRx.js.map → toRefundInputs-Dzw1cteD.js.map} +1 -1
- package/dist/types-24A9Qoxb.js +274 -0
- package/dist/types-24A9Qoxb.js.map +1 -0
- package/dist/types-BC2TGzcp.cjs +2 -0
- package/dist/types-BC2TGzcp.cjs.map +1 -0
- package/dist/{verifyKeyPathSchnorrSignature-CQUut2-O.cjs → verifyKeyPathSchnorrSignature-BRTpEH9X.cjs} +2 -2
- package/dist/{verifyKeyPathSchnorrSignature-CQUut2-O.cjs.map → verifyKeyPathSchnorrSignature-BRTpEH9X.cjs.map} +1 -1
- package/dist/{verifyKeyPathSchnorrSignature-DxPbeSdQ.js → verifyKeyPathSchnorrSignature-IIj3Z2se.js} +2 -2
- package/dist/{verifyKeyPathSchnorrSignature-DxPbeSdQ.js.map → verifyKeyPathSchnorrSignature-IIj3Z2se.js.map} +1 -1
- package/dist/{verifyScriptPathSchnorrSignature-D2t1yACC.cjs → verifyScriptPathSchnorrSignature-CahYx5Td.cjs} +2 -2
- package/dist/{verifyScriptPathSchnorrSignature-D2t1yACC.cjs.map → verifyScriptPathSchnorrSignature-CahYx5Td.cjs.map} +1 -1
- package/dist/{verifyScriptPathSchnorrSignature-B3-VkvnK.js → verifyScriptPathSchnorrSignature-owu3BfyT.js} +2 -2
- package/dist/{verifyScriptPathSchnorrSignature-B3-VkvnK.js.map → verifyScriptPathSchnorrSignature-owu3BfyT.js.map} +1 -1
- package/package.json +3 -3
- package/dist/ProtocolParams.abi-CKOVqFJP.js.map +0 -1
- package/dist/ProtocolParams.abi-CjrT0545.cjs +0 -2
- package/dist/ProtocolParams.abi-CjrT0545.cjs.map +0 -1
- package/dist/buildAndBroadcastReclaim-C7bq5wNM.cjs +0 -2
- package/dist/buildAndBroadcastReclaim-C7bq5wNM.cjs.map +0 -1
- package/dist/buildAndBroadcastReclaim-CcQ0b1be.js.map +0 -1
- package/dist/challengeAssert-Cp_GeRg8.js +0 -64
- package/dist/challengeAssert-Cp_GeRg8.js.map +0 -1
- package/dist/challengeAssert-Z7_YULBb.cjs +0 -2
- package/dist/challengeAssert-Z7_YULBb.cjs.map +0 -1
- package/dist/constants-BeYquVd-.js +0 -36
- package/dist/constants-BeYquVd-.js.map +0 -1
- package/dist/constants-BwCg5K6R.cjs +0 -2
- package/dist/constants-BwCg5K6R.cjs.map +0 -1
- package/dist/noPayout-B-vGXJ-V.js +0 -361
- package/dist/noPayout-B-vGXJ-V.js.map +0 -1
- package/dist/noPayout-DOHak0ui.cjs +0 -2
- package/dist/noPayout-DOHak0ui.cjs.map +0 -1
- package/dist/types-Bda75Od0.cjs +0 -2
- package/dist/types-Bda75Od0.cjs.map +0 -1
- package/dist/types-C5jj3Cc-.js +0 -198
- package/dist/types-C5jj3Cc-.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildAndBroadcastReclaim-DPk36Uhp.cjs","sources":["../src/tbv/core/services/activation/activateVault.ts","../src/tbv/core/services/delegated-claim/assertBinding.ts","../src/tbv/core/services/delegated-claim/assertPayoutFeeAndTimelocks.ts","../src/tbv/core/services/delegated-claim/challengerBinding.ts","../src/tbv/core/services/delegated-claim/payoutBinding.ts","../src/tbv/core/services/delegated-claim/payoutInputLeaf.ts","../src/tbv/core/services/delegated-claim/vaultIdBinding.ts","../src/tbv/core/services/delegated-claim/verifyingKeyBinding.ts","../src/tbv/core/services/delegated-claim/readWatchtowerArtifacts.ts","../src/tbv/core/services/delegated-claim/signingRequests.ts","../src/tbv/core/services/delegated-claim/types.ts","../src/tbv/core/services/delegated-claim/walletIdentity.ts","../src/tbv/core/services/delegated-claim/planDelegatedClaimSigning.ts","../src/tbv/core/services/delegated-claim/verifySignatureForRequest.ts","../src/tbv/core/services/delegated-claim/assembleWatchtowerArtifactsFromSignatures.ts","../src/tbv/core/services/delegated-claim/signDelegatedClaimPlan.ts","../src/tbv/core/services/delegated-claim/assembleWatchtowerArtifacts.ts","../src/tbv/core/services/delegated-claim/deriveClaimerWotsKeypair.ts","../src/tbv/core/services/participants/resolveParticipantKeys.ts","../src/tbv/core/services/participants/readStampedParticipantKeys.ts","../src/tbv/core/services/delegated-claim/readDelegatedClaimVaultContext.ts","../src/tbv/core/services/deposit/peginRegistrationDepth.ts","../src/tbv/core/services/deposit/graphFingerprint.ts","../src/tbv/core/services/deposit/signDepositorGraph.ts","../src/tbv/core/services/deposit/waitForPeginStatus.ts","../src/tbv/core/services/deposit/runDepositorPresignFlow.ts","../src/tbv/core/services/deposit/submitWotsPublicKey.ts","../src/tbv/core/services/participants/indexerKeyHint.ts","../src/tbv/core/services/deposit/validateOnChainParticipantKeys.ts","../src/tbv/core/services/deposit/validation.ts","../src/tbv/core/services/deposit/verifyRegisteredParticipantKeys.ts","../src/tbv/core/services/deposit/verifyRegisteredVaultVersions.ts","../src/tbv/core/services/refund/errors.ts","../src/tbv/core/services/refund/buildAndBroadcastRefund.ts","../src/tbv/core/services/reclaim/errors.ts","../src/tbv/core/services/reclaim/buildAndBroadcastReclaim.ts"],"sourcesContent":["/**\n * Vault activation — reveal HTLC secret on Ethereum to move the vault from\n * Verified to Active. The on-chain contract validates `sha256(s) == hashlock`\n * and the activation deadline; this function pre-validates inputs (including\n * an optional hashlock check) and delegates the actual contract write to an\n * injected callback so the SDK stays transport-agnostic.\n *\n * @module services/activation\n */\n\nimport type { Abi, Address, Hash, Hex } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { ensureHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport { validateSecretAgainstHashlock } from \"../htlc\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\nconst ADDRESS_HEX_RE = /^0x[0-9a-fA-F]{40}$/;\n// ETH calldata convention: 0x prefix REQUIRED, even number of hex chars, may\n// be empty (\"0x\"). Named distinctly from the BTC-hex regex in\n// buildAndBroadcastRefund.ts (which allows an optional prefix and requires\n// non-empty) to make the convention explicit at the call site.\nconst ETH_HEX_BYTES_RE = /^0x([0-9a-fA-F]{2})*$/;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\nfunction assertAddress(value: string, label: string): void {\n if (!ADDRESS_HEX_RE.test(value)) {\n throw new Error(\n `${label} must be a 20-byte 0x-prefixed hex address (42 chars)`,\n );\n }\n}\n\nfunction assertHexBytes(value: string, label: string): void {\n if (!ETH_HEX_BYTES_RE.test(value)) {\n throw new Error(\n `${label} must be a 0x-prefixed hex string with an even number of hex chars`,\n );\n }\n}\n\n/**\n * A single ETH contract-write call. The SDK assembles these; the caller\n * executes them via viem, wagmi, a wallet provider, or any other transport.\n */\nexport interface EthContractWriteCall {\n address: Address;\n abi: Abi;\n functionName: string;\n args: readonly unknown[];\n}\n\n/**\n * Minimum shape the SDK requires from any contract-write result. Callers may\n * return richer objects (e.g. including the receipt) — the SDK propagates\n * them unchanged via the generic parameter on {@link EthContractWriter}.\n */\nexport interface EthContractWriteResult {\n transactionHash: Hash;\n}\n\n/**\n * Caller-provided contract writer. The generic `R` lets callers return any\n * transport-specific result shape (e.g. `{ transactionHash, receipt }`);\n * the SDK forwards that shape back through `activateVault`.\n */\nexport type EthContractWriter<R extends EthContractWriteResult = EthContractWriteResult> = (\n call: EthContractWriteCall,\n) => Promise<R>;\n\nexport interface ActivateVaultInput<\n R extends EthContractWriteResult = EthContractWriteResult,\n> {\n /** BTCVaultRegistry contract address (env-specific). */\n btcVaultRegistryAddress: Address;\n /** Vault ID (bytes32, 0x-prefixed). */\n vaultId: Hex;\n /**\n * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n * `0X` prefix is normalised before validation.\n */\n secret: string;\n /**\n * Optional hashlock for client-side pre-validation. When provided, the SDK\n * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n */\n hashlock?: Hex;\n /**\n * Activation metadata passed through to the contract. Required to keep\n * the \"empty metadata\" convention explicit at the call site — pass `\"0x\"`\n * (empty bytes) when no metadata is needed. Must be a 0x-prefixed hex\n * string with an even number of hex chars.\n */\n activationMetadata: Hex;\n /** Caller-provided write callback — see {@link EthContractWriter}. */\n writeContract: EthContractWriter<R>;\n /**\n * Optional abort signal. Checked before validation runs; since validation\n * is fully synchronous, cancellation between validation and the write is\n * not observable and callers should rely on the transport's own\n * cancellation support for that window.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Shared pre-write validation for the two activation entry points and\n * `claimExpiredVault`: address and bytes32 shapes, plus the\n * `sha256(secret) == hashlock` pre-check whenever a hashlock is passed — the\n * last gate before the secret would enter calldata. The hashlock is optional\n * for activation; `claimExpiredVault` rejects a missing one before calling\n * this, so for it the check always runs.\n *\n * @returns the 0x-normalised secret to place in calldata\n */\nfunction validateActivationInputs(input: {\n btcVaultRegistryAddress: Address;\n vaultId: Hex;\n secret: string;\n hashlock?: Hex;\n}): Hex {\n assertAddress(input.btcVaultRegistryAddress, \"btcVaultRegistryAddress\");\n assertBytes32(input.vaultId, \"vaultId\");\n\n const normalizedSecret = ensureHexPrefix(input.secret);\n assertBytes32(normalizedSecret, \"secret\");\n\n if (input.hashlock !== undefined) {\n assertBytes32(input.hashlock, \"hashlock\");\n if (!validateSecretAgainstHashlock(normalizedSecret, input.hashlock)) {\n throw new Error(\n \"Invalid secret: SHA256(secret) does not match the provided hashlock\",\n );\n }\n }\n\n return normalizedSecret;\n}\n\n/**\n * Reveal the HTLC secret on Ethereum and activate the vault.\n *\n * Validates inputs, optionally pre-checks the secret against the expected\n * hashlock, and delegates the contract write to `writeContract`. Returns\n * whatever the writer returns so callers can keep richer transport-specific\n * metadata (e.g. viem receipts) end-to-end.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n * or if `sha256(secret) != hashlock`\n * @throws `Error` if `activationMetadata` is not a 0x-prefixed hex byte\n * string (must have an even number of hex chars). Pass `\"0x\"` for\n * empty metadata.\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVault<\n R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultInput<R>): Promise<R> {\n const {\n btcVaultRegistryAddress,\n vaultId,\n hashlock,\n activationMetadata,\n writeContract,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n\n const normalizedSecret = validateActivationInputs({\n btcVaultRegistryAddress,\n vaultId,\n secret: input.secret,\n hashlock,\n });\n\n assertHexBytes(activationMetadata, \"activationMetadata\");\n\n return writeContract({\n address: btcVaultRegistryAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"activateVaultWithSecret\",\n args: [vaultId, normalizedSecret, activationMetadata],\n });\n}\n\nexport interface ActivateVaultAndRedeemInput<\n R extends EthContractWriteResult = EthContractWriteResult,\n> {\n /** BTCVaultRegistry contract address (env-specific). */\n btcVaultRegistryAddress: Address;\n /** Vault ID (bytes32, 0x-prefixed). */\n vaultId: Hex;\n /**\n * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n * `0X` prefix is normalised before validation.\n */\n secret: string;\n /**\n * Optional hashlock for client-side pre-validation. When provided, the SDK\n * rejects before calling `writeContract` if `sha256(secret) != hashlock`.\n */\n hashlock?: Hex;\n /** Caller-provided write callback — see {@link EthContractWriter}. */\n writeContract: EthContractWriter<R>;\n /**\n * Optional abort signal. Checked before validation runs; since validation\n * is fully synchronous, cancellation between validation and the write is\n * not observable and callers should rely on the transport's own\n * cancellation support for that window.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Depositor escape hatch: reveal the HTLC secret and immediately redeem the\n * vault for the depositor, without any application activation. The contract\n * (`activateVaultWithSecretAndRedeem`) runs the same activation preconditions\n * (Verified status, activation deadline, `sha256(s) == hashlock`) and then\n * marks the vault Redeemed so the vault provider pays the BTC out to the\n * depositor's committed payout address. Used when the normal activation is\n * unavailable (e.g. the application adapter is paused or its activation\n * reverts) but the secret must still be revealed to recover the swept peg-in.\n *\n * Takes no activation metadata — the application entry point is never called.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId` or `secret` is not a valid 32-byte hex\n * @throws `Error` if `hashlock` is provided and is not a valid 32-byte hex,\n * or if `sha256(secret) != hashlock`\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function activateVaultAndRedeem<\n R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ActivateVaultAndRedeemInput<R>): Promise<R> {\n const { btcVaultRegistryAddress, vaultId, hashlock, writeContract, signal } =\n input;\n\n signal?.throwIfAborted();\n\n const normalizedSecret = validateActivationInputs({\n btcVaultRegistryAddress,\n vaultId,\n secret: input.secret,\n hashlock,\n });\n\n return writeContract({\n address: btcVaultRegistryAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"activateVaultWithSecretAndRedeem\",\n args: [vaultId, normalizedSecret],\n });\n}\n\nexport interface ClaimExpiredVaultInput<\n R extends EthContractWriteResult = EthContractWriteResult,\n> {\n /** BTCVaultRegistry contract address (env-specific). */\n btcVaultRegistryAddress: Address;\n /** Vault ID (bytes32, 0x-prefixed). */\n vaultId: Hex;\n /**\n * HTLC secret preimage (bytes32). A missing `0x` prefix or an uppercase\n * `0X` prefix is normalised before validation.\n */\n secret: string;\n /**\n * The vault's on-chain hashlock. Required, unlike the activation entry\n * points: the claim is offered only after the secret is presumed leaked, and\n * the SDK still refuses to place a secret in calldata that does not match\n * the commitment it reveals against.\n */\n hashlock: Hex;\n /** Caller-provided write callback — see {@link EthContractWriter}. */\n writeContract: EthContractWriter<R>;\n /**\n * Optional abort signal. Checked before validation runs; since validation\n * is fully synchronous, cancellation between validation and the write is\n * not observable and callers should rely on the transport's own\n * cancellation support for that window.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Claim an expired vault: reveal the HTLC secret for a vault that reached\n * Verified but expired before activation, moving it to Redeemed so the BTC is\n * claimed out of the PegIn on Bitcoin to the vault provider and depositor keys\n * recorded on chain.\n *\n * This is the only exit for a vault whose Pre-PegIn HTLC was spent by the\n * PegIn after expiry, which removes the depositor's refund path. The contract\n * (`claimExpiredVault`) accepts it from any caller, requires status Expired\n * with `verifiedAt > 0`, rejects it after the vault's `claimExpiredUntil`\n * block, and checks `sha256(s) == hashlock`.\n *\n * Revealing the secret while the HTLC is still unspent lets anyone broadcast\n * the PegIn and race the depositor's own refund. Callers must offer this only\n * once the PegIn has spent the HTLC.\n *\n * @throws `Error` if `btcVaultRegistryAddress` is not a valid 20-byte address\n * @throws `Error` if `vaultId`, `secret` or `hashlock` is not a valid 32-byte\n * hex, or if `sha256(secret) != hashlock`\n * @throws whatever the injected `writeContract` throws\n * @throws `AbortError` / caller-provided abort reason if `signal` aborts\n */\nexport async function claimExpiredVault<\n R extends EthContractWriteResult = EthContractWriteResult,\n>(input: ClaimExpiredVaultInput<R>): Promise<R> {\n const { btcVaultRegistryAddress, vaultId, hashlock, writeContract, signal } =\n input;\n\n signal?.throwIfAborted();\n\n if (typeof hashlock !== \"string\") {\n throw new Error(\"hashlock is required to claim an expired vault\");\n }\n\n const normalizedSecret = validateActivationInputs({\n btcVaultRegistryAddress,\n vaultId,\n secret: input.secret,\n hashlock,\n });\n\n return writeContract({\n address: btcVaultRegistryAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"claimExpiredVault\",\n args: [vaultId, normalizedSecret],\n });\n}\n","/**\n * Binding between the three transactions a delegated claim signs in\n * sequence: Assert must spend Claim:0 and declare it as its prevout, the\n * Payout's Assert-connector input must spend Assert:0 — the Assert that is\n * being signed, not another one the graph might carry — and the Payout's\n * Vault-UTXO input must spend output 0 of the PegIn the Claim spends\n * (btc-vault `payout.rs:66,107` and `claim.rs:120` @ ac4954e7), so the\n * Payout the depositor signs pays out this vault and no other.\n *\n * btc-vault's `check_assert_spends_claim` (`sign.rs:1155-1193` @ ac4954e7)\n * already enforces the Assert half inside `buildClaimPsbt`; it is mirrored\n * here so this export holds for PSBTs built any other way. Nothing covers the\n * Payout half but this. The signatures are collected once and cannot be\n * re-collected, so a pairing mismatch found later leaves a signed Assert the\n * signed Payout can never spend.\n *\n * @module services/delegated-claim/assertBinding\n */\n\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\n\nimport {\n ASSERT_CLAIM_INPUT_INDEX,\n ASSERT_PAYOUT_OUTPUT_INDEX,\n CLAIM_CONNECTOR_OUTPUT_INDEX,\n CLAIM_PEGIN_INPUT_INDEX,\n PAYOUT_ASSERT_INPUT_INDEX,\n PAYOUT_PEGIN_INPUT_INDEX,\n PEGIN_VAULT_OUTPUT_INDEX,\n} from \"../../primitives/psbt/constants\";\n\n/** @experimental */\nexport class AssertBindingError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AssertBindingError\";\n }\n}\n\n/** @experimental */\nexport interface AssertAssertBindsClaimAndPayoutParams {\n claimPsbtBase64: string;\n assertPsbtBase64: string;\n payoutClaimerPsbtBase64: string;\n}\n\nfunction parse(label: string, psbtBase64: string): Psbt {\n try {\n return Psbt.fromBase64(psbtBase64);\n } catch (cause) {\n throw new AssertBindingError(`${label} PSBT cannot be parsed.`, { cause });\n }\n}\n\n/** Internal-order prevout hash of one input, present or not. */\nfunction inputHash(label: string, psbt: Psbt, inputIndex: number): Buffer {\n const input = psbt.txInputs[inputIndex];\n if (!input) {\n throw new AssertBindingError(`${label} PSBT has no input ${inputIndex}.`);\n }\n return input.hash;\n}\n\n/** Internal-order hash of a PSBT's unsigned transaction. */\nfunction unsignedTxHash(psbt: Psbt): Buffer {\n return Transaction.fromBuffer(\n psbt.data.globalMap.unsignedTx.toBuffer(),\n ).getHash();\n}\n\nfunction assertInputSpends(\n label: string,\n psbt: Psbt,\n inputIndex: number,\n expectedHash: Buffer,\n expectedVout: number,\n target: string,\n): void {\n const input = psbt.txInputs[inputIndex];\n if (!input) {\n throw new AssertBindingError(`${label} PSBT has no input ${inputIndex}.`);\n }\n if (!input.hash.equals(expectedHash) || input.index !== expectedVout) {\n throw new AssertBindingError(\n `${label} input ${inputIndex} does not spend ${target}: the graph pairs this ${label} with a different transaction.`,\n );\n }\n}\n\n/**\n * The Assert's declared prevout must be Claim:0 itself, value and script: the\n * SIGHASH_DEFAULT signature commits to both. Mirrors the second half of\n * `check_assert_spends_claim` (`sign.rs:1176-1193` @ ac4954e7).\n */\nfunction assertDeclaresClaimOutput(assert: Psbt, claim: Psbt): void {\n const witnessUtxo = assert.data.inputs[ASSERT_CLAIM_INPUT_INDEX]?.witnessUtxo;\n if (witnessUtxo === undefined) {\n throw new AssertBindingError(\n `Assert input ${ASSERT_CLAIM_INPUT_INDEX} carries no witnessUtxo, so the amount and ` +\n `script its signature commits to cannot be checked; refusing to sign assert.`,\n );\n }\n const claimOutput = claim.txOutputs[CLAIM_CONNECTOR_OUTPUT_INDEX];\n if (!claimOutput) {\n throw new AssertBindingError(\n `Claim PSBT has no output ${CLAIM_CONNECTOR_OUTPUT_INDEX} for the Assert to spend; refusing to sign assert.`,\n );\n }\n if (witnessUtxo.value !== claimOutput.value) {\n throw new AssertBindingError(\n `Assert input ${ASSERT_CLAIM_INPUT_INDEX} declares a prevout of ${witnessUtxo.value} sats ` +\n `but the Claim's output ${CLAIM_CONNECTOR_OUTPUT_INDEX} is worth ${claimOutput.value}; refusing to sign assert.`,\n );\n }\n if (!witnessUtxo.script.equals(claimOutput.script)) {\n throw new AssertBindingError(\n `Assert input ${ASSERT_CLAIM_INPUT_INDEX} declares a prevout script that is not the ` +\n `Claim's output ${CLAIM_CONNECTOR_OUTPUT_INDEX} script; refusing to sign assert.`,\n );\n }\n}\n\n/**\n * @throws {AssertBindingError} When Assert input 0 is not Claim:0 or does not\n * declare Claim:0's value and script as its witnessUtxo, Payout\n * input 1 is not Assert:0, or Payout input 0 is not output 0 of the\n * PegIn the Claim spends.\n * @experimental\n */\nexport function assertAssertBindsClaimAndPayout(\n params: AssertAssertBindsClaimAndPayoutParams,\n): void {\n const claim = parse(\"Claim\", params.claimPsbtBase64);\n const assert = parse(\"Assert\", params.assertPsbtBase64);\n const payout = parse(\"Payout\", params.payoutClaimerPsbtBase64);\n\n assertInputSpends(\n \"Assert\",\n assert,\n ASSERT_CLAIM_INPUT_INDEX,\n unsignedTxHash(claim),\n CLAIM_CONNECTOR_OUTPUT_INDEX,\n \"Claim:0\",\n );\n assertDeclaresClaimOutput(assert, claim);\n assertInputSpends(\n \"Payout\",\n payout,\n PAYOUT_ASSERT_INPUT_INDEX,\n unsignedTxHash(assert),\n ASSERT_PAYOUT_OUTPUT_INDEX,\n \"Assert:0\",\n );\n assertInputSpends(\n \"Payout\",\n payout,\n PAYOUT_PEGIN_INPUT_INDEX,\n inputHash(\"Claim\", claim, CLAIM_PEGIN_INPUT_INDEX),\n PEGIN_VAULT_OUTPUT_INDEX,\n \"this vault's PegIn:0\",\n );\n}\n","/**\n * Claim-side counterpart of the CSV-sequence pins and the implicit-fee band\n * that `primitives/psbt/payout.ts` runs at deposit time on the same Payout\n * transaction: input 0's sequence is `timelock_pegin`, input 1's is\n * `timelock_assert`, and `inputs − outputs` is the miner fee\n * (btc-vault `payout.rs:103-140` @ ac4954e7).\n *\n * The transaction-level checks read the claimer PSBT's unsigned transaction,\n * which is proved byte-equal to the depositor's here. The declared prevout\n * amounts are not part of that transaction — they live in each PSBT's own\n * input map — so they are pinned on both PSBTs.\n *\n * @module services/delegated-claim/assertPayoutFeeAndTimelocks\n */\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\nimport {\n assertPayoutFeeBandDomain,\n assertPayoutFeeInBand,\n type PayoutFeeBandParams,\n} from \"../../primitives/psbt/assertPayoutFeeBand\";\nimport {\n ASSERT_PAYOUT_OUTPUT_INDEX,\n DEPOSITOR_PAYOUT_INPUT_COUNT,\n NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n PAYOUT_ASSERT_INPUT_INDEX,\n PAYOUT_DESTINATION_OUTPUT_INDEX,\n PAYOUT_PEGIN_INPUT_INDEX,\n PEGIN_VAULT_OUTPUT_INDEX,\n} from \"../../primitives/psbt/constants\";\nimport type { DelegatedClaimVaultContext } from \"./types\";\n\nfunction parse(label: string, psbtBase64: string): Psbt {\n try {\n return Psbt.fromBase64(psbtBase64);\n } catch (cause) {\n throw new Error(`${label} PSBT cannot be parsed.`, { cause });\n }\n}\n\n/**\n * Pins both declared prevout amounts of one Payout PSBT.\n *\n * BIP-341's `sha_amounts` commits to every input's amount under\n * SIGHASH_DEFAULT, which is what this path signs with\n * (`primitives/psbt/payout.ts:511`,\n * `primitives/psbt/verifyScriptPathSchnorrSignature.ts:153`), so a misstated\n * Assert:0 amount on input 1 invalidates input 0's signature just as an\n * overstated Vault UTXO does. The builder declares both (btc-vault\n * `psbt.rs:125-128` @ ac4954e7 sets `witness_utxo` on every input), so a\n * missing one is itself a refusal.\n *\n * Deposit time needs no such check: `primitives/psbt/payout.ts:306-312`\n * derives input 1's prevout from the real Assert and writes it at `:520-523`,\n * so it is right by construction there, whereas these PSBTs arrive pre-built\n * from the vault provider's graph. The pin makes the amount the wallet signs\n * over the amount the fee below is measured over.\n */\nfunction assertDeclaredPrevouts(\n label: string,\n payout: Psbt,\n peginValueSats: number,\n assertOutput: { value: number; script: Buffer },\n): void {\n const pins = [\n {\n inputIndex: PAYOUT_PEGIN_INPUT_INDEX,\n expectedSats: peginValueSats,\n source: `the vault's PegIn output ${PEGIN_VAULT_OUTPUT_INDEX}`,\n },\n {\n inputIndex: PAYOUT_ASSERT_INPUT_INDEX,\n expectedSats: assertOutput.value,\n source: `the Assert's output ${ASSERT_PAYOUT_OUTPUT_INDEX}`,\n },\n ];\n for (const { inputIndex, expectedSats, source } of pins) {\n const witnessUtxo = payout.data.inputs[inputIndex]?.witnessUtxo;\n if (witnessUtxo === undefined) {\n throw new Error(\n `${label} Payout input ${inputIndex} carries no witnessUtxo, so the amount ` +\n `its signature commits to cannot be checked; refusing to sign payout.`,\n );\n }\n if (witnessUtxo.value !== expectedSats) {\n throw new Error(\n `${label} Payout input ${inputIndex} declares a prevout of ${witnessUtxo.value} sats ` +\n `but ${source} is worth ${expectedSats}; refusing to sign payout.`,\n );\n }\n }\n // `sha_scriptpubkeys` commits to every input's script too; input 1's is in\n // hand. Input 0's is not pinned (pre-review N46).\n const assertInput = payout.data.inputs[PAYOUT_ASSERT_INPUT_INDEX].witnessUtxo;\n if (!assertInput?.script.equals(assertOutput.script)) {\n throw new Error(\n `${label} Payout input ${PAYOUT_ASSERT_INPUT_INDEX} declares a prevout script that is not ` +\n `the Assert's output ${ASSERT_PAYOUT_OUTPUT_INDEX} script; refusing to sign payout.`,\n );\n }\n}\n\n/**\n * Throws unless the Payout's two input sequences are the vault's stamped CSV\n * timelocks, both PSBTs declare the two prevout amounts the signatures commit\n * to, and its implicit fee lies in the version-locked band.\n *\n * Not part of the public surface: it does not check which outpoints the\n * Payout spends, so it is only meaningful once they are proved elsewhere.\n * `buildBoundPsbtSet` proves them for the claimer PSBT with\n * `assertAssertBindsClaimAndPayout`; the depositor PSBT inherits that proof\n * from the byte-equality of the two unsigned transactions, which this\n * function enforces itself.\n *\n * @throws When the fee-band inputs are out of domain, a PSBT cannot be\n * parsed, the two Payout PSBTs are not the same transaction, the\n * Assert has no output\n * {@link ASSERT_PAYOUT_OUTPUT_INDEX}, the Payout has the wrong input\n * count, a sequence is not the stamped timelock, the Payout has the\n * wrong output count, the outputs exceed the inputs, the fee falls\n * outside the band, or either PSBT declares no prevout on an input or\n * one whose value is not the vault's PegIn output (input 0) or the\n * Assert's output (input 1).\n *\n * @internal\n */\nexport async function assertPayoutFeeAndTimelocks(params: {\n /** The claimer Payout signing PSBT, base64, as the graph produced it. */\n payoutClaimerPsbtBase64: string;\n /** The depositor Payout signing PSBT, base64, proved to be the same transaction. */\n payoutDepositorPsbtBase64: string;\n /** The Assert PSBT of the same graph; its output 0 is Payout input 1's prevout. */\n assertPsbtBase64: string;\n vault: DelegatedClaimVaultContext;\n}): Promise<void> {\n const { vault } = params;\n const feeBandParams: PayoutFeeBandParams = {\n vaultCoreVersion: vault.vaultCoreVersion,\n numVaultKeepers: vault.vaultKeeperBtcPubkeys.length,\n numUniversalChallengers: vault.universalChallengerBtcPubkeys.length,\n councilSize: vault.councilSize,\n protocolFeeRate: vault.protocolFeeRate,\n };\n assertPayoutFeeBandDomain(feeBandParams);\n\n const payoutClaimer = parse(\"Claimer Payout\", params.payoutClaimerPsbtBase64);\n const payoutDepositor = parse(\n \"Depositor Payout\",\n params.payoutDepositorPsbtBase64,\n );\n const assertPsbt = parse(\"Assert\", params.assertPsbtBase64);\n\n // The transaction-level checks below read the claimer PSBT alone, so the two\n // must be one transaction; each keeps its own input map, pinned separately.\n if (\n !payoutDepositor.data.globalMap.unsignedTx\n .toBuffer()\n .equals(payoutClaimer.data.globalMap.unsignedTx.toBuffer())\n ) {\n throw new Error(\n \"Depositor and claimer Payout PSBTs describe different transactions; refusing to sign payout.\",\n );\n }\n\n // The caller's outpoint proof (module doc) covers input 1 spending this\n // Assert's output 0, so that output is what input 1 is worth.\n const assertOutput = assertPsbt.txOutputs[ASSERT_PAYOUT_OUTPUT_INDEX];\n if (assertOutput === undefined) {\n throw new Error(\n `Assert transaction has no output ${ASSERT_PAYOUT_OUTPUT_INDEX}; the ` +\n `Payout's implicit fee cannot be measured.`,\n );\n }\n\n // Transaction-level checks run on the claimer PSBT alone: the depositor's\n // unsigned transaction was checked byte-equal to it above.\n if (payoutClaimer.txInputs.length !== DEPOSITOR_PAYOUT_INPUT_COUNT) {\n throw new Error(\n `Payout transaction must have exactly ${DEPOSITOR_PAYOUT_INPUT_COUNT} ` +\n `inputs, got ${payoutClaimer.txInputs.length}`,\n );\n }\n const peginSequence =\n payoutClaimer.txInputs[PAYOUT_PEGIN_INPUT_INDEX].sequence;\n if (peginSequence !== vault.timelockPegin) {\n throw new Error(\n `Payout input ${PAYOUT_PEGIN_INPUT_INDEX} sequence ${peginSequence} must equal the ` +\n `PegIn CSV timelock ${vault.timelockPegin}; refusing to sign payout.`,\n );\n }\n const assertSequence =\n payoutClaimer.txInputs[PAYOUT_ASSERT_INPUT_INDEX].sequence;\n if (assertSequence !== vault.timelockAssert) {\n throw new Error(\n `Payout input ${PAYOUT_ASSERT_INPUT_INDEX} sequence ${assertSequence} must equal the ` +\n `Assert CSV timelock ${vault.timelockAssert}; refusing to sign payout.`,\n );\n }\n\n // Each PSBT carries its own input map, which the byte-equality of the two\n // unsigned transactions does not cover, so both are pinned.\n assertDeclaredPrevouts(\n \"Claimer\",\n payoutClaimer,\n vault.peginVaultOutputValueSats,\n assertOutput,\n );\n assertDeclaredPrevouts(\n \"Depositor\",\n payoutDepositor,\n vault.peginVaultOutputValueSats,\n assertOutput,\n );\n\n const inputValueSats = vault.peginVaultOutputValueSats + assertOutput.value;\n const outs = payoutClaimer.txOutputs;\n // The precondition for the `out1Len: undefined` below, and what makes\n // `outs[PAYOUT_DESTINATION_OUTPUT_INDEX]` the pinned destination.\n if (outs.length !== NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT) {\n throw new Error(\n `Payout transaction has ${outs.length} output(s), expected exactly ` +\n `${NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT} for a depositor-as-claimer payout.`,\n );\n }\n let outputValueSats = 0;\n for (const out of outs) outputValueSats += out.value;\n if (outputValueSats > inputValueSats) {\n throw new Error(\n `Payout outputs (${outputValueSats} sats) exceed inputs ` +\n `(${inputValueSats} sats); invalid transaction.`,\n );\n }\n\n // `out1Len` is undefined: the claimer layout's second output is the CPFP\n // anchor, not a commission (`assertPayoutPaysRegisteredScript` pins both).\n await assertPayoutFeeInBand(feeBandParams, {\n implicitFeeSats: inputValueSats - outputValueSats,\n out0Len: outs[PAYOUT_DESTINATION_OUTPUT_INDEX].script.length,\n out1Len: undefined,\n });\n}\n","/**\n * Binding between the challenger set a delegated claim signs for and the set\n * the vault actually has on chain.\n *\n * The WronglyChallenged PSBTs arrive keyed by challenger public key, and the\n * vault provider chooses those keys. Nothing downstream notices a short map:\n * the artifacts file verifies against its own graph, so a missing challenger\n * produces a file that looks complete and fails only months later, when that\n * challenger's ChallengeAssert has to be answered inside\n * `timelock_challenge_assert` and the material is not there.\n *\n * The failure is asymmetric, which is why both directions are checked.\n * Missing entries leave the depositor unable to defend the claim; extra\n * entries hand out signatures for keys the protocol does not recognize.\n *\n * This mirrors `assertChallengerSetMatchesExpected` in\n * `services/deposit/signDepositorGraph.ts`, which does the same job at\n * deposit time against the same on-chain sets.\n *\n * @module services/delegated-claim/challengerBinding\n */\n\nimport { deriveLocalChallengers } from \"../../primitives/challengers\";\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/**\n * Thrown when the graph's challengers are not the vault's challengers.\n *\n * @experimental\n */\nexport class ChallengerSetMismatchError extends Error {\n constructor(\n /** Challengers the vault has that the graph left out. */\n readonly missing: string[],\n /** Challengers the graph lists that the vault does not have. */\n readonly unexpected: string[],\n ) {\n super(\n `Transaction graph's challenger set does not match the vault's ` +\n `(local ∪ universal)` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (unexpected.length > 0\n ? ` (unexpected: ${unexpected.join(\", \")})`\n : \"\") +\n `. Refusing to sign an incomplete or padded challenger set.`,\n );\n this.name = \"ChallengerSetMismatchError\";\n }\n}\n\n/**\n * The graph's challenger keys, and the on-chain sets they must equal.\n *\n * @experimental\n */\nexport interface AssertChallengerSetMatchesVaultParams {\n /** Challenger keys the graph produced WronglyChallenged PSBTs for. */\n graphChallengerPubkeys: string[];\n /** Depositor's BTC public key, registered on chain for this vault. */\n depositorBtcPubkey: string;\n /**\n * Vault provider's BTC public key, registered on chain for this vault.\n * The depositor-as-claimer branch does not use it, but the shared\n * derivation takes it, and passing a stand-in would be a lie that the\n * next change to that function could turn into a wrong set.\n */\n vaultProviderBtcPubkey: string;\n /** Vault keepers registered on chain for this vault. */\n vaultKeeperBtcPubkeys: string[];\n /** Universal challengers registered on chain. */\n universalChallengerBtcPubkeys: string[];\n}\n\n/**\n * Throws unless the graph's challenger set equals `local ∪ universal`.\n *\n * The depositor is the claimer here, so the local set is the vault keepers,\n * derived by the same function the deposit path uses rather than restated.\n *\n * @throws {@link ChallengerSetMismatchError} on any missing or extra key, or\n * a plain error when the on-chain sets themselves are unusable.\n *\n * @experimental\n */\nexport function assertChallengerSetMatchesVault(\n params: AssertChallengerSetMatchesVaultParams,\n): void {\n const local = deriveLocalChallengers({\n claimerBtcPubkey: params.depositorBtcPubkey,\n depositorBtcPubkey: params.depositorBtcPubkey,\n vaultProviderBtcPubkey: params.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: params.vaultKeeperBtcPubkeys,\n });\n const universal = params.universalChallengerBtcPubkeys.map(normalizeKey);\n\n // Protocol guarantee: the two sets are disjoint. An overlap makes a\n // challenger's role ambiguous, so it is an error rather than a union.\n const overlap = local.filter((key) => universal.includes(key));\n if (overlap.length > 0) {\n throw new Error(\n `Cannot check the challenger set: vault keepers and universal ` +\n `challengers overlap (${overlap.join(\", \")}).`,\n );\n }\n\n const supplied = params.graphChallengerPubkeys.map(normalizeKey);\n const suppliedSet = new Set(supplied);\n if (suppliedSet.size !== supplied.length) {\n throw new Error(\n \"Transaction graph lists the same challenger more than once.\",\n );\n }\n\n const expected = [...local, ...universal];\n const expectedSet = new Set(expected);\n const missing = expected.filter((key) => !suppliedSet.has(key));\n const unexpected = supplied.filter((key) => !expectedSet.has(key));\n if (missing.length > 0 || unexpected.length > 0) {\n throw new ChallengerSetMismatchError(missing, unexpected);\n }\n}\n\nfunction normalizeKey(pubkey: string): string {\n return stripHexPrefix(pubkey).toLowerCase();\n}\n","/**\n * Binding between a delegated-claim Payout and the destination the vault\n * registered on chain.\n *\n * The Payout transaction is built by the vault provider and moves the whole\n * recovered peg-in. Its outputs are the one thing in this flow that decides\n * where the money lands, and nothing in the artifacts file constrains them:\n * the Rust verification proves the bundled signatures are self-consistent\n * with the same graph that named the outputs. So a graph correctly bound to\n * this vault can still carry a Payout that pays somebody else, and the\n * depositor would sign it in the same prompt as everything else.\n *\n * This is the claim-side counterpart of `assertPayoutOutputLayout` in\n * `primitives/psbt/payout.ts`, which the deposit-time path already runs for\n * the same `depositor-as-claimer` role. It pins the same things: the output\n * count, the payout script, the CPFP anchor's value and destination, and the\n * version and locktime the depositor's signature commits to.\n *\n * @module services/delegated-claim/payoutBinding\n */\n\nimport { Psbt } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { deriveP2trScript } from \"../../clients/eth/payout-script\";\nimport {\n NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,\n PAYOUT_ANCHOR_DUST_SATS,\n PAYOUT_DESTINATION_OUTPUT_INDEX,\n PAYOUT_TX_LOCKTIME,\n PAYOUT_TX_VERSION,\n} from \"../../primitives/psbt/constants\";\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/**\n * Thrown when a Payout does not pay the vault's registered destination.\n *\n * @experimental\n */\nexport class PayoutDestinationError extends Error {\n constructor(\n /** The vault's registered payout scriptPubKey, hex. */\n readonly expectedScriptHex: string,\n /** The scriptPubKey the Payout actually pays, hex. */\n readonly actualScriptHex: string,\n ) {\n super(\n `Payout transaction output ${PAYOUT_DESTINATION_OUTPUT_INDEX} pays ` +\n `${actualScriptHex}, not the vault's registered payout script ` +\n `${expectedScriptHex}. Refusing to sign a payout to another ` +\n `destination.`,\n );\n this.name = \"PayoutDestinationError\";\n }\n}\n\n/**\n * The Payout to check, and the destination it must pay.\n *\n * @experimental\n */\nexport interface AssertPayoutPaysRegisteredScriptParams {\n /** A Payout signing PSBT, base64, as the graph produced it. */\n payoutPsbtBase64: string;\n /**\n * `depositorPayoutScriptPubKey` as the vault registered it on chain, hex.\n * The vault provider does not choose this value, which is the whole point\n * of comparing against it.\n */\n registeredPayoutScriptPubKey: string;\n /**\n * The vault's registered depositor key, x-only hex. The CPFP anchor is the\n * claimer's BIP-86 output, and on this path the claimer is the depositor.\n */\n depositorBtcPubkey: string;\n}\n\n/**\n * Throws unless the Payout pays the vault's registered payout script.\n *\n * Run this on every Payout PSBT before the wallet is prompted. Both the\n * depositor and the claimer PSBT describe the same transaction, so both must\n * pass; checking only one would leave the other free to differ.\n *\n * @throws {@link PayoutDestinationError} when output 0 pays elsewhere, or a\n * plain error when the layout is not the canonical claimer layout or\n * the CPFP anchor is not the depositor's BIP-86 P2TR.\n *\n * @experimental\n */\nexport function assertPayoutPaysRegisteredScript(\n params: AssertPayoutPaysRegisteredScriptParams,\n): void {\n let psbt: Psbt;\n try {\n psbt = Psbt.fromBase64(params.payoutPsbtBase64);\n } catch (cause) {\n throw new Error(\"Payout PSBT cannot be parsed.\", { cause });\n }\n\n const { version, locktime, outs } = readPayoutTx(psbt);\n\n // Version and locktime are part of every sighash the depositor is about to\n // produce, so a deviation means signing a transaction the protocol never\n // constructs.\n if (version !== PAYOUT_TX_VERSION || locktime !== PAYOUT_TX_LOCKTIME) {\n throw new Error(\n `Payout transaction has version ${version} and locktime ${locktime}, ` +\n `expected ${PAYOUT_TX_VERSION} and ${PAYOUT_TX_LOCKTIME}.`,\n );\n }\n\n if (outs.length !== NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT) {\n throw new Error(\n `Payout transaction has ${outs.length} output(s), expected exactly ` +\n `${NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT} for a depositor-as-claimer ` +\n `payout.`,\n );\n }\n\n const expected = stripHexPrefix(\n params.registeredPayoutScriptPubKey,\n ).toLowerCase();\n if (expected.length === 0) {\n throw new Error(\n \"registeredPayoutScriptPubKey is empty; the vault's on-chain payout \" +\n \"script is required to check the Payout destination.\",\n );\n }\n const actual = outs[PAYOUT_DESTINATION_OUTPUT_INDEX].script\n .toString(\"hex\")\n .toLowerCase();\n // Compared as strings: a Buffer round-trip would silently truncate a\n // malformed registered script at its first non-hex pair.\n if (actual !== expected) {\n throw new PayoutDestinationError(expected, actual);\n }\n\n const anchor = outs[NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT - 1];\n if (anchor.value !== PAYOUT_ANCHOR_DUST_SATS) {\n throw new Error(\n `Payout CPFP anchor value ${anchor.value} sats must equal ` +\n `${PAYOUT_ANCHOR_DUST_SATS} sats.`,\n );\n }\n\n // btc-vault builds the anchor from `Bip86KeyConnector::new(assert_connector\n // .claimer)` (`transactions/payout.rs:151-153`, `connectors/mod.rs:237-268`\n // @ ac4954e7), so on this path it is the depositor's own key-path P2TR.\n const expectedAnchor = stripHexPrefix(\n deriveP2trScript(stripHexPrefix(params.depositorBtcPubkey).toLowerCase()),\n );\n const actualAnchor = anchor.script.toString(\"hex\").toLowerCase();\n if (actualAnchor !== expectedAnchor) {\n throw new Error(\n `Payout CPFP anchor pays ${actualAnchor}, not the depositor's BIP-86 P2TR ` +\n `${expectedAnchor}. Refusing to sign a payout whose fee-bump output is ` +\n `spendable by somebody else.`,\n );\n }\n}\n\n/** The unsigned transaction fields this check reads, from the PSBT. */\nfunction readPayoutTx(psbt: Psbt): {\n version: number;\n locktime: number;\n outs: { script: Buffer; value: number }[];\n} {\n return {\n version: psbt.version,\n locktime: psbt.locktime,\n outs: psbt.txOutputs.map((out) => ({\n script: Buffer.from(out.script),\n value: out.value,\n })),\n };\n}\n","/**\n * The depositor Payout PSBT the WASM builder emits carries a `witness_utxo` on\n * every input but taproot metadata on input 0 alone, the one it signs\n * (btc-vault `crates/vault/src/psbt.rs:125-128` sets every prevout, `:130-131`\n * writes the leaf, internal key and merkle root only for the requested inputs,\n * and `transactions/payout.rs:268-274` requests input 0 alone @ ac4954e7).\n * A hardware wallet's intent-bound payout\n * validator reads input 1's leaf to identify the claimer before it signs\n * input 0, so without it the device refuses. The claimer Payout PSBT is the\n * same unsigned transaction and carries exactly that leaf on input 1, so it\n * is copied across here, once, for every wallet: software wallets sign\n * input 0 and ignore input 1's metadata.\n *\n * @module services/delegated-claim/payoutInputLeaf\n */\n\nimport { Psbt } from \"bitcoinjs-lib\";\n\nimport { PAYOUT_ASSERT_INPUT_INDEX } from \"../../primitives/psbt/constants\";\n\n/** @experimental */\nexport class PayoutInputLeafError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PayoutInputLeafError\";\n }\n}\n\n/** @experimental */\nexport interface CopyAssertConnectorLeafParams {\n payoutDepositorPsbtBase64: string;\n payoutClaimerPsbtBase64: string;\n}\n\n/**\n * @returns The depositor Payout PSBT (base64) with input 1's taproot\n * metadata taken from the claimer Payout PSBT.\n * @throws {PayoutInputLeafError} If the two PSBTs are not the same unsigned\n * transaction, the claimer PSBT carries no single input-1 leaf, or\n * the depositor PSBT already carries one.\n * @experimental\n */\nexport function copyAssertConnectorLeaf(\n params: CopyAssertConnectorLeafParams,\n): string {\n const depositor = Psbt.fromBase64(params.payoutDepositorPsbtBase64);\n const claimer = Psbt.fromBase64(params.payoutClaimerPsbtBase64);\n\n const depositorTx = depositor.data.globalMap.unsignedTx.toBuffer();\n const claimerTx = claimer.data.globalMap.unsignedTx.toBuffer();\n if (!depositorTx.equals(claimerTx)) {\n throw new PayoutInputLeafError(\n \"Depositor and claimer Payout PSBTs describe different transactions; the graph is inconsistent.\",\n );\n }\n\n const from = claimer.data.inputs[PAYOUT_ASSERT_INPUT_INDEX];\n const to = depositor.data.inputs[PAYOUT_ASSERT_INPUT_INDEX];\n if (!from || !to) {\n throw new PayoutInputLeafError(\n \"Payout PSBT has no Assert-connector input.\",\n );\n }\n if (!from.tapLeafScript || from.tapLeafScript.length !== 1) {\n throw new PayoutInputLeafError(\n \"Claimer Payout PSBT must carry exactly one leaf on the Assert-connector input.\",\n );\n }\n if (to.tapLeafScript && to.tapLeafScript.length > 0) {\n throw new PayoutInputLeafError(\n \"Depositor Payout PSBT already carries an Assert-connector leaf; refusing to overwrite it.\",\n );\n }\n\n depositor.updateInput(PAYOUT_ASSERT_INPUT_INDEX, {\n tapLeafScript: from.tapLeafScript,\n ...(from.tapInternalKey ? { tapInternalKey: from.tapInternalKey } : {}),\n ...(from.tapMerkleRoot ? { tapMerkleRoot: from.tapMerkleRoot } : {}),\n });\n return depositor.toBase64();\n}\n","/**\n * Binding between a delegated-claim graph and the vault it is claimed for.\n *\n * Neither the vault provider's graph JSON nor an artifacts file is bound to a\n * vault id by construction: the Rust side only hex-normalizes the `vault_id`\n * field it is handed, so a graph for vault B carrying `vault_id` A passes\n * every other check. The binding that does exist is on chain — the vault id\n * is `keccak256(peginTxid, depositorEthAddress)` — and the Claim transaction\n * spends that PegIn output with its first input.\n *\n * Re-deriving the id from that input is therefore the only check that ties a\n * served graph, or a file the SDK did not write, to the vault the depositor\n * means to claim.\n *\n * @module services/delegated-claim/vaultIdBinding\n */\n\nimport { Psbt, Transaction } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { derivePeginVaultId } from \"../../clients/eth/pegin-transaction\";\nimport { CLAIM_PEGIN_INPUT_INDEX } from \"../../primitives/psbt/constants\";\nimport { PEGIN_DEPOSITOR_CLAIM_VOUT } from \"../../primitives/psbt/depositorClaim\";\n\n/**\n * Thrown when a graph does not belong to the vault it is presented for.\n *\n * @experimental\n */\nexport class VaultIdBindingError extends Error {\n constructor(\n readonly expectedVaultId: string,\n readonly derivedVaultId: string,\n readonly peginTxid: string,\n ) {\n super(\n `Transaction graph does not belong to vault ${expectedVaultId}: its ` +\n `Claim spends PegIn ${peginTxid}, which derives vault ` +\n `${derivedVaultId}. Refusing to sign for a graph served for another ` +\n `vault.`,\n );\n this.name = \"VaultIdBindingError\";\n }\n}\n\n/**\n * Display-order txid of the PegIn output the Claim PSBT spends.\n *\n * @throws Unless the Claim has exactly one input and it spends PegIn output 1\n * (CLAUDE.md §3 funding-chain rule).\n */\nexport function peginTxidFromClaimPsbt(claimPsbtBase64: string): string {\n let psbt: Psbt;\n try {\n psbt = Psbt.fromBase64(claimPsbtBase64);\n } catch (cause) {\n throw new Error(\"Claim PSBT cannot be parsed.\", { cause });\n }\n const input = psbt.txInputs[CLAIM_PEGIN_INPUT_INDEX];\n if (!input) {\n throw new Error(\"Claim PSBT carries no PegIn input to bind the vault to.\");\n }\n assertSpendsDepositorClaimOutput(\n \"Claim PSBT\",\n psbt.txInputs.length,\n input.index,\n );\n return displayTxid(input.hash);\n}\n\n/**\n * Display-order txid of the PegIn output a signed Claim transaction spends.\n *\n * @throws Unless the Claim has exactly one input and it spends PegIn output 1.\n */\nexport function peginTxidFromClaimTx(claimTx: Transaction): string {\n const input = claimTx.ins[CLAIM_PEGIN_INPUT_INDEX];\n if (!input) {\n throw new Error(\n \"Artifacts file's claim_tx carries no PegIn input to bind the vault to.\",\n );\n }\n assertSpendsDepositorClaimOutput(\n \"Artifacts file's claim_tx\",\n claimTx.ins.length,\n input.index,\n );\n return displayTxid(input.hash);\n}\n\n/**\n * The depositor Claim is funded by PegIn output 1 alone; the pinned engine\n * checks the same shape (btc-vault `check_depositor_claim_shape`,\n * `transactions/claim.rs:140-158` @ b534ff9e), re-asserted here per CLAUDE.md §1.\n */\nfunction assertSpendsDepositorClaimOutput(\n label: string,\n inputCount: number,\n vout: number,\n): void {\n if (inputCount !== 1 || vout !== PEGIN_DEPOSITOR_CLAIM_VOUT) {\n throw new Error(\n `${label} must have exactly one input spending PegIn output ` +\n `${PEGIN_DEPOSITOR_CLAIM_VOUT}; it has ${inputCount} input(s) and ` +\n `the first spends output ${vout}.`,\n );\n }\n}\n\n/**\n * The three values the on-chain vault id is derived from and compared with.\n *\n * @experimental\n */\nexport interface AssertClaimSpendsVaultParams {\n /** Display-order PegIn txid, from the Claim's first input. */\n peginTxid: string;\n /** Depositor's Ethereum address, the second half of the on-chain id. */\n depositorEthAddress: string;\n /** Vault id the graph or file claims to be for. */\n expectedVaultId: string;\n}\n\n/**\n * Throws unless the Claim's PegIn input derives the expected vault id.\n *\n * Experimental: this API can change in a minor release. Pin the SDK\n * version if you build on it.\n *\n * @throws {@link VaultIdBindingError} when the graph belongs to another vault.\n * @experimental\n */\nexport function assertClaimSpendsVault(\n params: AssertClaimSpendsVaultParams,\n): void {\n const derived = normalizeVaultId(\n derivePeginVaultId(params.peginTxid, params.depositorEthAddress),\n );\n const expected = normalizeVaultId(params.expectedVaultId);\n if (derived !== expected) {\n throw new VaultIdBindingError(expected, derived, params.peginTxid);\n }\n}\n\n/** Lowercase, `0x`-prefixed form of a 32-byte vault id. */\nexport function normalizeVaultId(vaultId: string): string {\n const bare = vaultId.startsWith(\"0x\") ? vaultId.slice(2) : vaultId;\n return `0x${bare.toLowerCase()}`;\n}\n\n/**\n * Bitcoin stores a prevout hash in internal byte order; a txid is the same\n * bytes reversed. Copies before reversing — the buffer belongs to the parsed\n * transaction.\n *\n * @internal\n */\nexport function displayTxid(internalHash: Uint8Array): string {\n return Buffer.from(internalHash).reverse().toString(\"hex\");\n}\n","/**\n * Binding between the Groth16 verifying key a claim runs under and the one\n * the depositor trusts.\n *\n * btc-vault takes the key opaquely and only checks that the bytes parse\n * (`delegated_claim.rs:405-414` @ ac4954e7): \"Must come from a trusted source\n * … never from the vault provider or any artifact the vault provider serves.\n * … a substituted key would let a proof the depositor never authorized pass\n * the pre-Assert check. Compare the VP-served key byte for byte against this\n * trusted value before signing.\" That comparison is what lives here.\n *\n * @module services/delegated-claim/verifyingKeyBinding\n */\n\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\n\n/**\n * The one form two verifying keys can be compared in: lowercase hex, no `0x`.\n *\n * @throws If the value is empty or is not even-length hex — an unparseable\n * key would otherwise compare equal to another unparseable one.\n * @internal\n */\nexport function normalizeVerifyingKeyHex(value: string, label: string): string {\n const body = stripHexPrefix(value);\n if (!/^(?:[0-9a-fA-F]{2})+$/.test(body)) {\n throw new Error(`${label} must be non-empty, even-length hex.`);\n }\n return body.toLowerCase();\n}\n\n/** @internal */\ninterface AssertVerifyingKeyIsTrustedParams {\n /** The key as the vault provider served it. */\n servedVerifyingKeyHex: string;\n /** The key for {@link AssertVerifyingKeyIsTrustedParams.proverCircuitVersion}, from the vault-provers release or the prover service. */\n trustedVerifyingKeyHex: string;\n proverCircuitVersion: number;\n}\n\n/**\n * Throws unless the vault-provider-served verifying key is the trusted one.\n *\n * @returns The trusted key in the form btc-vault decodes it: bare lowercase\n * hex (`hex::decode`, `delegated_claim.rs:569-571` @ ac4954e7,\n * rejects a `0x` prefix).\n * @internal\n */\nexport function assertVerifyingKeyIsTrusted(\n params: AssertVerifyingKeyIsTrustedParams,\n): string {\n const served = normalizeVerifyingKeyHex(\n params.servedVerifyingKeyHex,\n \"source.verifyingKeyHex\",\n );\n const trusted = normalizeVerifyingKeyHex(\n params.trustedVerifyingKeyHex,\n \"trustedVerifyingKeyHex\",\n );\n if (served === trusted) return trusted;\n throw new Error(\n `Vault provider served Groth16 verifying key ${served} but the trusted key for ` +\n `prover circuit version ${params.proverCircuitVersion} is ${trusted}; refusing to ` +\n `sign a claim whose proof would verify under a substituted key.`,\n );\n}\n","/**\n * Reading and checking a watchtower `artifacts.json` that already exists.\n *\n * An artifacts file is the depositor's only path to their funds when the\n * vault provider is gone, and nothing checks it between the day it is written\n * and the day it is used. These functions close that gap: they re-verify\n * every signature in the file against its own embedded graph, and confirm the\n * file belongs to the vault it is about to be claimed for.\n *\n * Run them before using the file to claim, not after a claim has already\n * spent the PegIn UTXO.\n *\n * @module services/delegated-claim/readWatchtowerArtifacts\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport { verifyWatchtowerArtifacts } from \"../../wasm\";\n\nimport type { BabeSessionsState, WatchtowerArtifactsSummary } from \"./types\";\nimport {\n assertClaimSpendsVault,\n normalizeVaultId,\n peginTxidFromClaimTx,\n} from \"./vaultIdBinding\";\nimport { normalizeVerifyingKeyHex } from \"./verifyingKeyBinding\";\n\n/**\n * Graph version the delegated-claim artifacts format exists for. Vaults on\n * graph v1 and v2 predate it and have no artifacts path at all.\n *\n * @experimental\n */\nexport const DELEGATED_CLAIM_TX_GRAPH_VERSION = 3;\n\n/**\n * Thrown when an artifacts file does not describe the vault being claimed.\n *\n * @experimental\n */\nexport class ArtifactsVaultMismatchError extends Error {\n constructor(\n readonly expectedVaultId: string,\n readonly actualVaultId: string,\n ) {\n super(\n `Artifacts belong to vault ${actualVaultId}, not ${expectedVaultId}. ` +\n `Select the artifacts file saved for this vault.`,\n );\n this.name = \"ArtifactsVaultMismatchError\";\n }\n}\n\n/** Raw shape of the fields this module reads out of the file. */\ninterface ArtifactsFileFields {\n vault_core_version?: unknown;\n vault_id?: unknown;\n claim_tx?: unknown;\n prover_circuit_version?: unknown;\n claimable_event_block_number?: unknown;\n verifying_key?: unknown;\n babe_sessions?: unknown;\n}\n\n/** Non-empty, whole-byte hex in either case, as btc-vault's `hex::decode` takes. */\nconst HEX_BYTES = /^(?:[0-9a-fA-F]{2})+$/;\n\n/**\n * Whether the file carries BaBe sessions, checking each entry's shape.\n *\n * An empty or absent map is an unjoined file. For any other map btc-vault\n * requires each value to be an object whose `decryptor_artifacts_hex` is a\n * non-empty hex string (`delegated_claim.rs:482-492` @ b534ff9e); checking\n * it here names the bad entry instead of surfacing an opaque Rust error.\n * Coverage of the graph's challengers is left to the WASM verifier.\n */\nfunction readBabeSessions(value: unknown): BabeSessionsState {\n const sessions = requireRecord(value, \"babe_sessions\");\n const challengerPubkeys = Object.keys(sessions);\n if (challengerPubkeys.length === 0) {\n return { joined: false };\n }\n for (const [challengerPubkey, session] of Object.entries(sessions)) {\n const artifactsHex =\n typeof session === \"object\" && session !== null\n ? (session as { decryptor_artifacts_hex?: unknown })\n .decryptor_artifacts_hex\n : undefined;\n if (typeof artifactsHex !== \"string\" || !HEX_BYTES.test(artifactsHex)) {\n throw new Error(\n `Artifacts file is missing a usable \"babe_sessions\": the entry for ` +\n `${challengerPubkey} carries no non-empty hex \"decryptor_artifacts_hex\".`,\n );\n }\n }\n return { joined: true, challengerPubkeys };\n}\n\n/**\n * Reads the small, self-describing fields of an artifacts file.\n *\n * This parses the whole JSON, so it is bounded by whatever the file carries\n * in `babe_sessions`. Keep those sessions in their own file: a bundle with\n * real sessions runs to hundreds of megabytes per challenger and cannot be\n * parsed in a browser tab.\n *\n * Experimental: this API can change in a minor release. Pin the SDK\n * version if you build on it.\n *\n * @throws If the file is not JSON, or lacks the fields every artifacts file\n * has.\n * @experimental\n */\nexport function summarizeWatchtowerArtifacts(\n artifactsJson: string,\n): WatchtowerArtifactsSummary {\n let parsed: ArtifactsFileFields;\n try {\n parsed = JSON.parse(artifactsJson) as ArtifactsFileFields;\n } catch (cause) {\n throw new Error(\"Artifacts file is not valid JSON.\", { cause });\n }\n\n const vaultId = requireString(parsed.vault_id, \"vault_id\");\n const claimTx = parseClaimTx(requireString(parsed.claim_tx, \"claim_tx\"));\n const proverCircuitVersion = requireSafeInteger(\n parsed.prover_circuit_version,\n \"prover_circuit_version\",\n );\n // Absent on files written before the field existed; the CLI reads an\n // absent value as 0, which means \"not yet known from chain\". Anything\n // present must be a block number: `JSON.parse` has already rounded\n // anything above 2^53, and a negative or fractional value would otherwise\n // reach `BigInt` and throw without naming the field.\n const claimableEventBlockNumber =\n parsed.claimable_event_block_number === undefined\n ? 0n\n : BigInt(\n requireSafeInteger(\n parsed.claimable_event_block_number,\n \"claimable_event_block_number\",\n ),\n );\n\n return {\n vaultCoreVersion:\n parsed.vault_core_version === undefined\n ? undefined\n : requireSafeInteger(parsed.vault_core_version, \"vault_core_version\"),\n vaultId,\n claimTxid: claimTx.getId(),\n peginTxid: peginTxidFromClaimTx(claimTx),\n proverCircuitVersion,\n claimableEventBlockNumber,\n // Required: btc-vault's builder always writes it\n // (`delegated_claim.rs:874` @ b534ff9e), so an absent one is not a file\n // this SDK or the CLI produced.\n verifyingKeyHex: requireString(parsed.verifying_key, \"verifying_key\"),\n babeSessions: readBabeSessions(parsed.babe_sessions),\n };\n}\n\n/**\n * The file to check, and the vault it must belong to.\n *\n * @experimental\n */\nexport interface AssertArtifactsUsableParams {\n artifactsJson: string;\n /** Vault the caller intends to claim, `0x`-prefixed or bare hex. */\n expectedVaultId: string;\n /**\n * Depositor's Ethereum address, used with the file's PegIn txid to\n * re-derive the vault id. Without it the only check would be the file's\n * self-declared `vault_id`.\n */\n depositorEthAddress: string;\n /**\n * The Groth16 verifying key for the vault's `proverCircuitVersion`, from\n * the `vault-provers` release or the prover service — never from the vault\n * provider or anything it serves (btc-vault `delegated_claim.rs:405-414`\n * @ b534ff9e). A file carrying a different key proves nothing at Assert.\n */\n trustedVerifyingKeyHex: string;\n /**\n * The vault's stamped `proverCircuitVersion`, from\n * `DelegatedClaimVaultContext`. btc-vault's verifier never reads the\n * file's own `prover_circuit_version` — `delegated_claim.rs:876` @ b534ff9e\n * only writes it — while `vaultd`'s\n * `crates/vaultd/src/cli/command/watchtower/start_claim.rs:348-352` hands\n * the file's key and version to the prover together, so a wrong version\n * fails there, before Assert.\n */\n expectedProverCircuitVersion: number;\n /**\n * The block of the vault's finalized `VaultClaimableBy` event, from\n * `DelegatedClaimVaultContext.claimableEventBlockNumber`. Unverified by\n * btc-vault like the circuit version, and handed to the prover beside it\n * (`start_claim.rs:349,352` @ b534ff9e), so a wrong block fails there too.\n */\n expectedClaimableEventBlockNumber: bigint;\n /**\n * Graph version to verify under. Defaults to the only version the format\n * exists for. A file that records a different `vault_core_version` is\n * rejected rather than verified under this one.\n */\n txGraphVersion?: number;\n}\n\n/**\n * Verifies an artifacts file and confirms it is the one for this vault.\n *\n * An unjoined file passes, with `babeSessions.joined` false. It cannot\n * answer a challenge, so a caller must not start a claim from it by any\n * route until the sessions are joined in (see {@link BabeSessionsState}).\n *\n * Experimental: this API can change in a minor release. Pin the SDK\n * version if you build on it.\n *\n * @throws {@link ArtifactsVaultMismatchError} when the file names a different\n * vault, {@link VaultIdBindingError} when the graph it carries\n * belongs to another vault whatever the file says, a plain error when\n * its `prover_circuit_version` is not `expectedProverCircuitVersion`,\n * its `claimable_event_block_number` is not\n * `expectedClaimableEventBlockNumber`, its `verifying_key` is not\n * `trustedVerifyingKeyHex`, or a `babe_sessions` entry is not\n * non-empty hex, or a\n * verification error when any bundled signature does not hold\n * against that graph or a non-empty `babe_sessions` does not cover\n * exactly its challengers.\n * @experimental\n */\nexport async function assertArtifactsUsableForVault(\n params: AssertArtifactsUsableParams,\n): Promise<WatchtowerArtifactsSummary> {\n const summary = summarizeWatchtowerArtifacts(params.artifactsJson);\n\n const expected = normalizeVaultId(params.expectedVaultId);\n const actual = normalizeVaultId(summary.vaultId);\n if (expected !== actual) {\n throw new ArtifactsVaultMismatchError(expected, actual);\n }\n\n // `vault_id` is a field the file declares about itself, and the Rust\n // verification only hex-normalizes it. This re-derives the id from the\n // graph the file actually carries, which catches a file whose graph\n // belongs to another vault however it was produced.\n assertClaimSpendsVault({\n peginTxid: summary.peginTxid,\n depositorEthAddress: params.depositorEthAddress,\n expectedVaultId: expected,\n });\n\n // Verifying a file under a version it does not claim yields, at best, an\n // opaque Rust error. The file states its own version, so compare it.\n const resolved = params.txGraphVersion ?? DELEGATED_CLAIM_TX_GRAPH_VERSION;\n if (\n summary.vaultCoreVersion !== undefined &&\n summary.vaultCoreVersion !== resolved\n ) {\n throw new Error(\n `Artifacts record vault core version ${summary.vaultCoreVersion}, ` +\n `but verification was asked for version ${resolved}.`,\n );\n }\n\n // A file assembled before the Ethereum withdrawal was initiated carries\n // zero here. `vaultd vp wt start-claim` would then ask the prover to prove\n // the wrong block and fail before Assert, so a function named \"usable\"\n // refuses it rather than passing the problem downstream. Nothing in the SDK\n // fills the field in later — reassemble the file instead.\n if (summary.claimableEventBlockNumber === 0n) {\n throw new Error(\n \"Artifacts carry no claimable event block number. They were assembled \" +\n \"before the Ethereum withdrawal was initiated; assemble them again.\",\n );\n }\n if (\n summary.claimableEventBlockNumber !==\n params.expectedClaimableEventBlockNumber\n ) {\n throw new Error(\n `Artifacts record claimable event block ${summary.claimableEventBlockNumber} but the ` +\n `vault's finalized VaultClaimableBy event is at block ${params.expectedClaimableEventBlockNumber}; ` +\n `the prover would prove the wrong block, so refusing the file.`,\n );\n }\n\n // Nothing verifies this field: btc-vault only writes it\n // (`delegated_claim.rs:876` @ b534ff9e), and `vaultd` hands it to the prover\n // beside the file's key (`start_claim.rs:348-352` @ b534ff9e), so a wrong\n // one fails only at start-claim, before Assert.\n if (summary.proverCircuitVersion !== params.expectedProverCircuitVersion) {\n throw new Error(\n `Artifacts record prover circuit version ${summary.proverCircuitVersion} but the vault's ` +\n `stamped params say ${params.expectedProverCircuitVersion}; a proof for the wrong circuit ` +\n `fails at start-claim, so refusing the file.`,\n );\n }\n\n // The Rust verification takes the key opaquely, so a substituted one would\n // let a proof the depositor never authorized pass the pre-Assert check\n // (btc-vault `delegated_claim.rs:405-414` @ b534ff9e).\n const fileVerifyingKey = normalizeVerifyingKeyHex(\n summary.verifyingKeyHex,\n 'Artifacts file \"verifying_key\"',\n );\n const trustedVerifyingKey = normalizeVerifyingKeyHex(\n params.trustedVerifyingKeyHex,\n \"trustedVerifyingKeyHex\",\n );\n if (fileVerifyingKey !== trustedVerifyingKey) {\n throw new Error(\n `Artifacts carry Groth16 verifying key ${fileVerifyingKey} but the trusted key for ` +\n `prover circuit version ${params.expectedProverCircuitVersion} is ${trustedVerifyingKey}; ` +\n `refusing a file whose proof would verify under a substituted key.`,\n );\n }\n\n await verifyWatchtowerArtifacts(resolved, params.artifactsJson);\n\n return summary;\n}\n\nfunction parseClaimTx(txHex: string): Transaction {\n try {\n return Transaction.fromHex(txHex);\n } catch (cause) {\n throw new Error(\"Artifacts file carries an unparseable claim_tx.\", {\n cause,\n });\n }\n}\n\nfunction requireString(value: unknown, field: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`Artifacts file is missing a usable \"${field}\".`);\n }\n return value;\n}\n\n/**\n * A count or a block height: never negative, never fractional, and inside\n * the range `JSON.parse` can represent without silently rounding.\n */\nfunction requireSafeInteger(value: unknown, field: string): number {\n if (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 0) {\n throw new Error(`Artifacts file is missing a usable \"${field}\".`);\n }\n return value;\n}\n\n/**\n * A JSON object, not a string or an array — `Object.keys` on either reports\n * index positions, which would pass as challenger public keys.\n */\nfunction requireRecord(value: unknown, field: string): Record<string, unknown> {\n if (value === undefined) return {};\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`Artifacts file is missing a usable \"${field}\".`);\n }\n return value as Record<string, unknown>;\n}\n","/**\n * The ordered list of PSBTs a delegated claim asks the depositor to sign.\n *\n * Order and input indices mirror `btc-vault crates/vault` and are not free\n * to choose: the Payout's Assert connector is input 1 (input 0 is the PegIn\n * UTXO); every other signing input is input 0 of its own transaction. Request\n * ids tie WronglyChallenged entries to their (challenger, gcIndex) pair, so\n * the array position is not load-bearing; the challenger order reflects the\n * caller's Record insertion order. The engine emits that record BTreeMap-\n * sorted by challenger hex (btc-vault `crates/vault/src/wasm/api.rs`,\n * `wasm_build_wrongly_challenged_psbts`), which is what keeps the\n * assembler's positional compare stable.\n *\n * @module services/delegated-claim/signingRequests\n */\n\nimport {\n ASSERT_CLAIM_INPUT_INDEX,\n CLAIM_PEGIN_INPUT_INDEX,\n PAYOUT_ASSERT_INPUT_INDEX,\n PAYOUT_PEGIN_INPUT_INDEX,\n WRONGLY_CHALLENGED_INPUT_INDEX,\n} from \"../../primitives/psbt/constants\";\nimport type { DelegatedClaimSigningRequest } from \"./types\";\n\n/**\n * The five PSBT groups the WASM builders produce, all base64.\n *\n * @experimental\n */\nexport interface DelegatedClaimPsbtSet {\n claim: string;\n assert: string;\n payoutClaimer: string;\n payoutDepositor: string;\n wronglyChallenged: Record<string, string[]>;\n}\n\n/**\n * Stable id of a WronglyChallenged request.\n *\n * @experimental\n */\nexport function wronglyChallengedRequestId(\n challengerPubkey: string,\n gcIndex: number,\n): string {\n return `wronglyChallenged:${challengerPubkey}:${gcIndex}`;\n}\n\n/**\n * @experimental\n */\nexport function buildDelegatedClaimSigningRequests(\n psbts: DelegatedClaimPsbtSet,\n): DelegatedClaimSigningRequest[] {\n const requests: DelegatedClaimSigningRequest[] = [\n {\n id: \"claim\",\n kind: \"claim\",\n psbtBase64: psbts.claim,\n inputIndex: CLAIM_PEGIN_INPUT_INDEX,\n },\n {\n id: \"assert\",\n kind: \"assert\",\n psbtBase64: psbts.assert,\n inputIndex: ASSERT_CLAIM_INPUT_INDEX,\n },\n {\n id: \"payoutClaimer\",\n kind: \"payoutClaimer\",\n psbtBase64: psbts.payoutClaimer,\n inputIndex: PAYOUT_ASSERT_INPUT_INDEX,\n },\n {\n id: \"payoutDepositor\",\n kind: \"payoutDepositor\",\n psbtBase64: psbts.payoutDepositor,\n inputIndex: PAYOUT_PEGIN_INPUT_INDEX,\n },\n ];\n for (const challengerPubkey of Object.keys(psbts.wronglyChallenged)) {\n psbts.wronglyChallenged[challengerPubkey].forEach((psbtBase64, gcIndex) => {\n requests.push({\n id: wronglyChallengedRequestId(challengerPubkey, gcIndex),\n kind: \"wronglyChallenged\",\n psbtBase64,\n inputIndex: WRONGLY_CHALLENGED_INPUT_INDEX,\n });\n });\n }\n return requests;\n}\n","/**\n * Types for the delegated-claim (depositor-as-claimer) artifact flow.\n *\n * @module services/delegated-claim/types\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport type { Hex } from \"viem\";\n\nimport type { OnChainBtcPubkey } from \"../../clients/eth/types\";\n\n/**\n * The small, non-opaque fields of an `artifacts.json` file.\n *\n * @experimental\n */\nexport interface WatchtowerArtifactsSummary {\n /**\n * Vault Core version the file records, absent on files written before the\n * field existed. `assertArtifactsUsableForVault` refuses a file whose\n * value differs from the version it verifies under.\n */\n vaultCoreVersion?: number;\n /** 32-byte on-chain vault id, as the file records it. */\n vaultId: string;\n /** Txid of the fully signed Claim transaction the file carries. */\n claimTxid: string;\n /**\n * Txid of the PegIn output that Claim spends. Together with the depositor's\n * Ethereum address this derives the vault id, which is the only check that\n * binds the file's graph to a vault — `vault_id` itself is self-declared.\n */\n peginTxid: string;\n proverCircuitVersion: number;\n /**\n * Block of the finalized `VaultClaimableBy` event, or `0n` when the file was\n * assembled before the Ethereum withdrawal was initiated. A claim run\n * against zero proves the wrong block and fails before Assert, so\n * `assertArtifactsUsableForVault` refuses it.\n *\n * `bigint`, matching {@link DelegatedClaimVaultContext} and the WASM\n * boundary — a block number is a u64 there.\n */\n claimableEventBlockNumber: bigint;\n /**\n * Groth16 verifying key the file carries (`verifying_key`, btc-vault\n * `delegated_claim.rs:523` @ b534ff9e). Compared with the caller's trusted\n * key by `assertArtifactsUsableForVault`.\n */\n verifyingKeyHex: string;\n babeSessions: BabeSessionsState;\n}\n\n/**\n * Whether the file carries BaBe sessions.\n *\n * An unjoined file (`babe_sessions` empty or absent) is the normal output of\n * a browser assembly, and it cannot defend a claim. Claim, Assert and Payout\n * finalize without the sessions, but answering a `ChallengeAssert` needs\n * them: the watchtower decrypts the hashlock preimage from the joined\n * session (`broadcast_wrongly_challenged.rs:152-183` @ b534ff9e), and an\n * unanswered challenge lets the challenger's NoPayout spend Assert:0, which\n * blocks the depositor's Payout (`nopayout.rs:1-21` @ b534ff9e). Join\n * the sessions in before starting a claim by any route; `vaultd vp wt\n * start-claim` enforces this, a browser-driven claim does not.\n *\n * `joined: true` means a session is present for each listed key, as\n * non-empty hex. The keys are returned as the file writes them. Once\n * `assertArtifactsUsableForVault` resolves, each parses to a graph challenger\n * and every challenger is covered (`validate_babe_sessions`,\n * `delegated_claim.rs:455-503` @ b534ff9e), but the parse accepts either hex\n * case, so a key need not string-equal the graph's lowercase key and one\n * challenger can appear twice. Neither check decodes a session: only the\n * watchtower does, so a present session is not proven usable.\n *\n * @experimental\n */\nexport type BabeSessionsState =\n | { joined: false }\n | { joined: true; challengerPubkeys: string[] };\n\n/** The `babe_sessions` value of an unjoined file. */\nexport const UNJOINED_BABE_SESSIONS_JSON = \"{}\";\n\n/**\n * Inputs the vault provider supplies for artifact assembly.\n *\n * @experimental\n */\nexport interface ClaimerArtifactsSource {\n /** `tx_graph_json` from `requestDepositorClaimerArtifacts`. */\n txGraphJson: string;\n /**\n * `verifying_key_hex` from the same response — the vault provider's own\n * copy, never used as-is.\n *\n * It is the key `pinPegoutProof` later verifies the Groth16 proof against,\n * so a provider that supplies a key of its own choosing makes that\n * verification prove nothing. The planner compares it byte for byte with\n * {@link DelegatedClaimSigningPlan.trustedVerifyingKeyHex} before any\n * wallet prompt and refuses on any difference; only the trusted value\n * reaches the file.\n */\n verifyingKeyHex: string;\n}\n\n/**\n * On-chain facts the assembled artifacts commit to.\n *\n * @experimental\n */\nexport interface DelegatedClaimVaultContext {\n vaultId: Hex;\n /**\n * Depositor's Ethereum address, the one the vault was registered under.\n * With the PegIn txid it re-derives {@link DelegatedClaimVaultContext.vaultId},\n * which is how a vault-provider-served graph is bound to this vault.\n */\n depositorEthAddress: Hex;\n /**\n * The vault's registered depositor BTC key\n * (`getBtcVaultBasicInfo(...).depositorBtcPubKey`, x-only). The claim scripts\n * are bound to it, and the plan's `depositorPublicKey` must equal it.\n */\n depositorBtcPubkey: OnChainBtcPubkey;\n /**\n * `depositorPayoutScriptPubKey` as the vault registered it on chain, hex.\n * The Payout the vault provider builds is checked against this before the\n * wallet signs it.\n */\n registeredPayoutScriptPubKey: string;\n /** Vault provider's BTC public key, registered on chain for this vault. */\n vaultProviderBtcPubkey: string;\n /** Vault keepers registered on chain, the local challengers of this claim. */\n vaultKeeperBtcPubkeys: string[];\n /** Universal challengers registered on chain. */\n universalChallengerBtcPubkeys: string[];\n /** Graph (vault core) version of the vault. Delegated claim requires 3. */\n txGraphVersion: number;\n proverCircuitVersion: number;\n /**\n * Vault Core version from the finalized `PegInSubmitted` event. The builder\n * refuses a graph that records a different one, which is what stops a graph\n * built under other rules from being signed.\n */\n vaultCoreVersion: number;\n /**\n * Block of the finalized `VaultClaimableBy` event.\n *\n * Zero is accepted, because the file is meant to be assembled while the\n * vault provider is still online, which can be long before the withdrawal\n * is initiated. Such a file is not claimable as written: nothing in the SDK\n * fills the field in later, and `assertArtifactsUsableForVault` refuses it.\n * Pass the real block whenever the event has already finalized.\n */\n claimableEventBlockNumber: bigint;\n /**\n * Value of output 0 of the vault's depositor-signed PegIn, in sats, parsed\n * from `VaultData.protocol.depositorSignedPeginTx`. The Payout's input 0\n * spends that output (btc-vault `payout.rs:103-112` @ ac4954e7), so it is\n * one of the two amounts the claim-time fee band is measured over.\n */\n peginVaultOutputValueSats: number;\n /** `feeRate` of the vault's stamped `getOffchainParamsByVersion`, sat/vB. */\n protocolFeeRate: bigint;\n /** `securityCouncilKeys.length` of the same stamped offchain params. */\n councilSize: number;\n /**\n * `timelockAssert` of the stamped params, which btc-vault uses as the PegIn\n * timelock too (vaultd `pegin_babe_setup.rs:794`, `pegin_validation.rs:417`\n * @ ac4954e7) — the CSV sequence of Payout input 0.\n */\n timelockPegin: number;\n /** `timelockAssert` of the stamped params — the CSV sequence of Payout input 1. */\n timelockAssert: number;\n}\n\n/**\n * Which delegated-claim transaction a signing request is for.\n *\n * @experimental\n */\nexport type DelegatedClaimSigningKind =\n | \"claim\"\n | \"assert\"\n | \"payoutClaimer\"\n | \"payoutDepositor\"\n | \"wronglyChallenged\";\n\n/**\n * One PSBT the depositor must sign, with the input to sign and a stable id\n * the signatures are keyed by.\n *\n * @experimental\n */\nexport interface DelegatedClaimSigningRequest {\n /** `kind`, or `wronglyChallenged:<challenger x-only hex>:<gcIndex>`. */\n readonly id: string;\n readonly kind: DelegatedClaimSigningKind;\n readonly psbtBase64: string;\n readonly inputIndex: number;\n}\n\n/**\n * Everything one delegated-claim signing session needs, built once and\n * signed by whichever wallet path fits. Treat as immutable: the assembler\n * rebuilds every PSBT from the graph and byte-compares against this.\n *\n * @experimental\n */\nexport interface DelegatedClaimSigningPlan {\n readonly depositorPublicKey: string;\n readonly btcNetwork: Network;\n readonly source: ClaimerArtifactsSource;\n /**\n * The Groth16 verifying key for {@link DelegatedClaimVaultContext.proverCircuitVersion},\n * obtained from the `vault-provers` release or the prover service — never\n * from the vault provider or anything it serves. btc-vault\n * `delegated_claim.rs:405-414` @ b534ff9e: the builder cannot tell a\n * substituted key from the real one, and a substituted key would let a\n * proof the depositor never authorized pass the pre-Assert check. This is\n * the value written into the file.\n */\n readonly trustedVerifyingKeyHex: string;\n readonly vault: DelegatedClaimVaultContext;\n /** Written into the file as `babe_sessions`; `{}` builds an unjoined file. */\n readonly babeSessionsJson: string;\n readonly requests: readonly DelegatedClaimSigningRequest[];\n}\n\n/**\n * 64-byte Schnorr signatures (hex) keyed by {@link DelegatedClaimSigningRequest.id}.\n *\n * @experimental\n */\nexport type DelegatedClaimSignatures = ReadonlyMap<string, string>;\n","/**\n * Wallet identity validation for delegated claim signing.\n *\n * @module services/delegated-claim/walletIdentity\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport { isAddressFromPublicKey } from \"../../primitives/utils/bitcoin\";\nimport {\n COMPRESSED_PUBKEY_HEX_LEN,\n X_ONLY_PUBKEY_HEX_LEN,\n} from \"../../utils/validation\";\n\n/** Hex length of the SEC1 prefix byte a compressed key carries before its x coordinate. */\nconst COMPRESSED_PREFIX_HEX_LEN =\n COMPRESSED_PUBKEY_HEX_LEN - X_ONLY_PUBKEY_HEX_LEN;\n\n/**\n * The 32-byte x-only form of a compressed or x-only public key, lowercase.\n *\n * @experimental\n */\nexport function xOnlyHex(publicKeyHex: string): string {\n const hex = publicKeyHex.replace(/^0x/, \"\").toLowerCase();\n if (hex.length === COMPRESSED_PUBKEY_HEX_LEN)\n return hex.slice(COMPRESSED_PREFIX_HEX_LEN);\n if (hex.length === X_ONLY_PUBKEY_HEX_LEN) return hex;\n throw new Error(\n `Public key must be 33-byte compressed or 32-byte x-only hex, got ${hex.length / 2} bytes.`,\n );\n}\n\n/**\n * Returns the wallet's signing address, once both it and the wallet's public\n * key are proved to be the vault's depositor.\n *\n * Both halves matter. The public key is what the graph's scripts commit to;\n * the address is what the sign options name the signer by, and a wallet whose\n * reported address and public key belong to different accounts would\n * otherwise sign the whole batch for the wrong account.\n *\n * @throws When the wallet is on a different account than the vault's\n * depositor, or reports an address that key does not control.\n * @experimental\n */\nexport async function assertWalletMatchesDepositor(\n btcWallet: BitcoinWallet,\n depositorPublicKey: string,\n btcNetwork: Network,\n): Promise<string> {\n const walletPublicKey = await btcWallet.getPublicKeyHex();\n if (xOnlyHex(walletPublicKey) !== xOnlyHex(depositorPublicKey)) {\n throw new Error(\n \"Connected wallet does not hold the vault's depositor key. \" +\n \"Select the account that made the deposit, then try again.\",\n );\n }\n\n const signerAddress = await btcWallet.getAddress();\n // The wallet's own key, just proved x-equal to the depositor's: a\n // native-segwit address needs its parity byte, which the on-chain x-only\n // key does not carry (the deposit-time check passes the wallet key too).\n if (!isAddressFromPublicKey(signerAddress, walletPublicKey, btcNetwork)) {\n throw new Error(\n `Connected wallet reports address \"${signerAddress}\", which is not ` +\n \"derived from the vault's depositor key. Select the account that \" +\n \"made the deposit, then try again.\",\n );\n }\n return signerAddress;\n}\n","/**\n * Builds every PSBT a delegated claim signs and binds them to the vault\n * before any wallet prompt, returning an immutable signing plan.\n *\n * Nothing here talks to a wallet. The plan is what `signDelegatedClaimPlan`\n * signs and what `assembleWatchtowerArtifactsFromSignatures` rebuilds and\n * byte-compares against, so the checks run once, here, and the assembler\n * proves the same PSBTs were signed.\n *\n * @module services/delegated-claim/planDelegatedClaimSigning\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport { assertPayoutScriptMatchesPopKey } from \"../../clients/eth/payout-script\";\nimport { ensureHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport {\n buildAssertClaimerPsbt,\n buildClaimPsbt,\n buildPayoutClaimerPsbt,\n buildPayoutDepositorPsbt,\n buildWronglyChallengedPsbts,\n} from \"../../wasm\";\n\nimport { assertAssertBindsClaimAndPayout } from \"./assertBinding\";\nimport { assertPayoutFeeAndTimelocks } from \"./assertPayoutFeeAndTimelocks\";\nimport { assertChallengerSetMatchesVault } from \"./challengerBinding\";\nimport { assertPayoutPaysRegisteredScript } from \"./payoutBinding\";\nimport { copyAssertConnectorLeaf } from \"./payoutInputLeaf\";\nimport { DELEGATED_CLAIM_TX_GRAPH_VERSION } from \"./readWatchtowerArtifacts\";\nimport {\n buildDelegatedClaimSigningRequests,\n type DelegatedClaimPsbtSet,\n} from \"./signingRequests\";\nimport {\n type ClaimerArtifactsSource,\n type DelegatedClaimSigningPlan,\n type DelegatedClaimVaultContext,\n UNJOINED_BABE_SESSIONS_JSON,\n} from \"./types\";\nimport {\n assertClaimSpendsVault,\n peginTxidFromClaimPsbt,\n} from \"./vaultIdBinding\";\nimport { assertVerifyingKeyIsTrusted } from \"./verifyingKeyBinding\";\nimport { xOnlyHex } from \"./walletIdentity\";\n\n/** @experimental */\nexport interface PlanDelegatedClaimSigningParams {\n /**\n * Depositor's BTC public key (compressed or x-only hex). A vault that\n * registered a P2WPKH payout script needs the compressed key here, as at\n * registration: the x-only form cannot derive a P2WPKH script.\n */\n depositorPublicKey: string;\n /** Network the depositor's address is derived on, to check the signer. */\n btcNetwork: Network;\n /** Graph and verifying key as the vault provider returned them. */\n source: ClaimerArtifactsSource;\n /** See {@link DelegatedClaimSigningPlan.trustedVerifyingKeyHex}. */\n trustedVerifyingKeyHex: string;\n vault: DelegatedClaimVaultContext;\n /** See {@link AssembleWatchtowerArtifactsParams.babeSessionsJson}. */\n babeSessionsJson?: string;\n}\n\n/**\n * Build the five PSBT groups from the graph and run every binding check.\n * Shared with the assembler, which rebuilds the same set to prove the plan\n * it is given was not altered.\n *\n * @internal\n * @experimental\n */\nexport async function buildBoundPsbtSet(\n txGraphVersion: number,\n graphJson: string,\n vault: DelegatedClaimVaultContext,\n depositorPublicKey: string,\n): Promise<DelegatedClaimPsbtSet> {\n // The wallet-identity and signature checks compare against this key, so it\n // has to be the chain's — and in a form the signer accepts, which is the\n // compressed or x-only one, never the 65-byte uncompressed key.\n const depositorXOnly = xOnlyHex(depositorPublicKey);\n if (depositorXOnly !== vault.depositorBtcPubkey) {\n throw new Error(\n `Depositor key ${depositorXOnly} is not the vault's registered depositor key ${vault.depositorBtcPubkey}; refusing to plan a claim for another key.`,\n );\n }\n\n // Otherwise the destination the whole claim pays rests on the RPC node's\n // registration log alone. The two forms are this SDK's registration rule\n // (`clients/eth/payout-script.ts`, called from pegin-registration-client.ts:407),\n // not the contract's — it bounds the script's length alone.\n try {\n assertPayoutScriptMatchesPopKey(\n ensureHexPrefix(vault.registeredPayoutScriptPubKey),\n vault.depositorBtcPubkey,\n depositorPublicKey,\n );\n } catch (cause) {\n throw new Error(\n `Registered payout script ${vault.registeredPayoutScriptPubKey} failed the depositor-key ` +\n `derivation check: ${cause instanceof Error ? cause.message : String(cause)}. This SDK ` +\n `registers only P2TR of the depositor's x-only key or P2WPKH of the compressed key (the ` +\n `contract bounds the script's length alone, PeginLogic.sol:237-240 @ c559f5c2); refusing ` +\n `to sign a claim that pays it.`,\n { cause },\n );\n }\n\n // Vaults on graph v1 and v2 have no delegated-claim path at all, so the\n // builders below would be asked for transactions that do not exist.\n if (txGraphVersion !== DELEGATED_CLAIM_TX_GRAPH_VERSION) {\n throw new Error(\n `Graph version ${txGraphVersion} is not the delegated-claim graph version ` +\n `${DELEGATED_CLAIM_TX_GRAPH_VERSION}; vaults on earlier graphs have no delegated-claim path.`,\n );\n }\n\n // One axis under two names (readDelegatedClaimVaultContext): the builders\n // pick the graph model by the first, the fee band by the second.\n if (txGraphVersion !== vault.vaultCoreVersion) {\n throw new Error(\n `Graph version ${txGraphVersion} does not match the vault context's vault core version ${vault.vaultCoreVersion}; they are one axis and must agree.`,\n );\n }\n\n const [claim, assert, payoutClaimer, wronglyChallenged, payoutDepositorRaw] =\n await Promise.all([\n buildClaimPsbt(txGraphVersion, graphJson),\n buildAssertClaimerPsbt(txGraphVersion, graphJson),\n buildPayoutClaimerPsbt(txGraphVersion, graphJson),\n buildWronglyChallengedPsbts(txGraphVersion, graphJson),\n buildPayoutDepositorPsbt(txGraphVersion, graphJson),\n ]);\n\n // The graph arrives from the vault provider and carries no proof that it\n // belongs to this vault. The Claim's first input spends the PegIn output\n // the on-chain vault id is derived from, so that input is the binding.\n assertClaimSpendsVault({\n peginTxid: peginTxidFromClaimPsbt(claim),\n depositorEthAddress: vault.depositorEthAddress,\n expectedVaultId: vault.vaultId,\n });\n\n // The Assert we sign must be the one the Payout's input 1 spends, and\n // must itself spend Claim:0. Checked on the builder's own PSBTs, before\n // the depositor Payout is augmented.\n assertAssertBindsClaimAndPayout({\n claimPsbtBase64: claim,\n assertPsbtBase64: assert,\n payoutClaimerPsbtBase64: payoutClaimer,\n });\n\n // Where the money lands. Both PSBTs describe the same Payout transaction,\n // so both are checked.\n for (const psbtBase64 of [payoutClaimer, payoutDepositorRaw]) {\n assertPayoutPaysRegisteredScript({\n payoutPsbtBase64: psbtBase64,\n registeredPayoutScriptPubKey: vault.registeredPayoutScriptPubKey,\n depositorBtcPubkey: vault.depositorBtcPubkey,\n });\n }\n\n // Byte-compares the two unsigned transactions, so from here the depositor\n // PSBT is proved to describe the same Payout the Assert binding tied to this\n // vault's PegIn and Assert.\n const payoutDepositor = copyAssertConnectorLeaf({\n payoutDepositorPsbtBase64: payoutDepositorRaw,\n payoutClaimerPsbtBase64: payoutClaimer,\n });\n\n // How much of the money reaches it: the CSV sequences the signature commits\n // to, and the fee the difference between inputs and outputs burns. Both\n // PSBTs go in because the declared prevout amounts live in each one's input\n // map, which the byte-equality above does not cover.\n await assertPayoutFeeAndTimelocks({\n payoutClaimerPsbtBase64: payoutClaimer,\n payoutDepositorPsbtBase64: payoutDepositor,\n assertPsbtBase64: assert,\n vault,\n });\n\n // Who can be answered later.\n assertChallengerSetMatchesVault({\n graphChallengerPubkeys: Object.keys(wronglyChallenged),\n depositorBtcPubkey: depositorPublicKey,\n vaultProviderBtcPubkey: vault.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: vault.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: vault.universalChallengerBtcPubkeys,\n });\n\n return { claim, assert, payoutClaimer, payoutDepositor, wronglyChallenged };\n}\n\n/**\n * @throws If the vault provider's verifying key is not the trusted one, if\n * `depositorPublicKey` is not the vault's registered depositor key,\n * the registered payout script is not derived from it,\n * the graph version and the vault context's vault core version\n * disagree, the graph is not version 3, any binding check fails, or\n * the Payout's CSV sequences, declared prevout amounts or implicit fee\n * do not match the vault's stamped timelocks, its PegIn and Assert\n * outputs, and the fee band.\n * @experimental\n */\nexport async function planDelegatedClaimSigning(\n params: PlanDelegatedClaimSigningParams,\n): Promise<DelegatedClaimSigningPlan> {\n // Before anything is built or prompted: a substituted key would let a proof\n // the depositor never authorized pass the pre-Assert check (btc-vault\n // `delegated_claim.rs:405-414` @ ac4954e7).\n assertVerifyingKeyIsTrusted({\n servedVerifyingKeyHex: params.source.verifyingKeyHex,\n trustedVerifyingKeyHex: params.trustedVerifyingKeyHex,\n proverCircuitVersion: params.vault.proverCircuitVersion,\n });\n\n const psbts = await buildBoundPsbtSet(\n params.vault.txGraphVersion,\n params.source.txGraphJson,\n params.vault,\n params.depositorPublicKey,\n );\n return {\n depositorPublicKey: params.depositorPublicKey,\n btcNetwork: params.btcNetwork,\n trustedVerifyingKeyHex: params.trustedVerifyingKeyHex,\n source: params.source,\n vault: params.vault,\n // Omitted means unjoined: the browser cannot hold the real sessions.\n babeSessionsJson:\n params.babeSessionsJson === undefined\n ? UNJOINED_BABE_SESSIONS_JSON\n : params.babeSessionsJson,\n requests: buildDelegatedClaimSigningRequests(psbts),\n };\n}\n","/**\n * Host-side verification of one delegated-claim signature against the\n * request it answers, shared by the signer and the assembler.\n *\n * Critical path 8: a wallet may ignore the untweaked-signer flag, so a\n * signature is only accepted once it verifies against the PSBT that was\n * requested and the depositor's x-only key.\n *\n * @module services/delegated-claim/verifySignatureForRequest\n */\n\nimport { Buffer } from \"buffer\";\n\nimport { assertScriptPathSchnorrSignature } from \"../../primitives\";\n\nimport type { DelegatedClaimSigningPlan, DelegatedClaimSigningRequest } from \"./types\";\nimport { xOnlyHex } from \"./walletIdentity\";\n\n/**\n * @throws If `signatureHex` is not a valid script-path signature by the\n * plan's depositor over `request`'s PSBT and input.\n * @internal\n * @experimental\n */\nexport function assertSignatureForRequest(\n plan: DelegatedClaimSigningPlan,\n request: DelegatedClaimSigningRequest,\n signatureHex: string,\n): void {\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtBase64ToHex(request.psbtBase64),\n signatureHex,\n signerXOnlyPubkeyHex: xOnlyHex(plan.depositorPublicKey),\n inputIndex: request.inputIndex,\n });\n}\n\n// The WASM graph emits and reads PSBTs as base64; wallets and the verifier\n// take them as hex. Re-encode the same bytes; never parse.\n\n/**\n * @internal\n * @experimental\n */\nexport function psbtBase64ToHex(psbtBase64: string): string {\n return Buffer.from(psbtBase64, \"base64\").toString(\"hex\");\n}\n\n/**\n * @internal\n * @experimental\n */\nexport function psbtHexToBase64(psbtHex: string): string {\n return Buffer.from(psbtHex, \"hex\").toString(\"base64\");\n}\n","/**\n * Turns a signed delegated-claim plan into `artifacts.json`.\n *\n * Neither input is trusted as-is. Every PSBT is rebuilt from the graph and\n * byte-compared against the plan, so a caller that altered a request between\n * planning and signing fails here rather than producing a file whose\n * signatures verify against transactions nobody checked; and every signature\n * is verified against the rebuilt PSBT and the depositor key, so the map need\n * not come from `signDelegatedClaimPlan` to be safe.\n *\n * @module services/delegated-claim/assembleWatchtowerArtifactsFromSignatures\n */\n\nimport type { WronglyChallengedSigs } from \"../../wasm\";\nimport { buildWatchtowerArtifacts, finalizeClaimTx } from \"../../wasm\";\n\nimport { buildBoundPsbtSet } from \"./planDelegatedClaimSigning\";\nimport {\n buildDelegatedClaimSigningRequests,\n wronglyChallengedRequestId,\n} from \"./signingRequests\";\nimport type {\n DelegatedClaimSignatures,\n DelegatedClaimSigningPlan,\n DelegatedClaimSigningRequest,\n} from \"./types\";\nimport { assertSignatureForRequest } from \"./verifySignatureForRequest\";\nimport { assertVerifyingKeyIsTrusted } from \"./verifyingKeyBinding\";\n\n/**\n * Thrown when a plan's requests differ from the ones the graph builds now.\n *\n * @experimental\n */\nexport class SigningPlanMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SigningPlanMismatchError\";\n }\n}\n\n/**\n * A signed plan: the plan as it was handed to the signer, and the signatures\n * it produced.\n *\n * @experimental\n */\nexport interface AssembleFromSignaturesParams {\n plan: DelegatedClaimSigningPlan;\n signatures: DelegatedClaimSignatures;\n}\n\n/**\n * Proves the plan's requests are, field for field, the ones the graph builds.\n * Positional: the rebuilt list is canonical, so a reorder reports as an id\n * mismatch at the first position that differs.\n */\nfunction assertPlanMatchesRebuilt(\n given: readonly DelegatedClaimSigningRequest[],\n rebuilt: readonly DelegatedClaimSigningRequest[],\n): void {\n if (given.length !== rebuilt.length) {\n throw new SigningPlanMismatchError(\n `Plan has ${given.length} requests but the graph builds ${rebuilt.length}; the plan was altered after planning.`,\n );\n }\n for (let i = 0; i < rebuilt.length; i++) {\n const expected = rebuilt[i];\n const request = given[i];\n if (request.id !== expected.id) {\n throw new SigningPlanMismatchError(\n `Plan request at position ${i} is \"${request.id}\" but the graph builds \"${expected.id}\" there; the plan was altered after planning.`,\n );\n }\n if (request.kind !== expected.kind) {\n throw new SigningPlanMismatchError(\n `Plan request \"${request.id}\" has kind \"${request.kind}\" but the graph builds \"${expected.kind}\"; the plan was altered after planning.`,\n );\n }\n if (request.inputIndex !== expected.inputIndex) {\n throw new SigningPlanMismatchError(\n `Plan request \"${request.id}\" signs inputIndex ${request.inputIndex} but the graph builds ${expected.inputIndex}; the plan was altered after planning.`,\n );\n }\n if (request.psbtBase64 !== expected.psbtBase64) {\n throw new SigningPlanMismatchError(\n `Plan request \"${request.id}\" carries a psbt that differs from the one the graph builds; the plan was altered after planning.`,\n );\n }\n }\n}\n\n/** One signature per request, no more and no fewer. */\nfunction assertSignaturesCoverRequests(\n requests: readonly DelegatedClaimSigningRequest[],\n signatures: DelegatedClaimSignatures,\n): void {\n const requestIds = new Set(requests.map((request) => request.id));\n const missing = [...requestIds].filter((id) => !signatures.has(id));\n const extra = [...signatures.keys()].filter((id) => !requestIds.has(id));\n if (missing.length === 0 && extra.length === 0) return;\n const quoted = (ids: string[]) => ids.map((id) => `\"${id}\"`).join(\", \");\n throw new Error(\n `Signatures do not cover the plan: ${signatures.size} signatures for ${requests.length} requests` +\n (missing.length > 0 ? `; missing ${quoted(missing)}` : \"\") +\n (extra.length > 0 ? `; extra ${quoted(extra)}` : \"\") +\n \".\",\n );\n}\n\nfunction requireSignature(\n signatures: DelegatedClaimSignatures,\n id: string,\n): string {\n const signatureHex = signatures.get(id);\n // Unreachable after assertSignaturesCoverRequests; kept for type narrowing.\n if (signatureHex === undefined) {\n throw new Error(\n `No signature for request \"${id}\"; the plan was not fully signed.`,\n );\n }\n return signatureHex;\n}\n\n/**\n * Rebuilds the PSBT set from the plan's graph and vault, proves the plan's\n * requests equal it, verifies every signature against the rebuilt request,\n * then routes each signature to its artifacts field. Only the rebuilt set\n * feeds the file; the plan's own PSBT bytes are compared and never used.\n *\n * @throws {SigningPlanMismatchError} If any request's id, kind, input index\n * or PSBT differs from the one the graph builds now.\n * @throws If the plan's vault-provider verifying key is not its trusted one,\n * a request has no signature, a signature has no request, a\n * signature does not verify against its rebuilt request, a binding\n * check on the rebuilt set fails, or the Rust verification of the\n * bundle fails.\n * @experimental\n */\nexport async function assembleWatchtowerArtifactsFromSignatures(\n params: AssembleFromSignaturesParams,\n): Promise<string> {\n const { plan, signatures } = params;\n const { txGraphVersion } = plan.vault;\n const graphJson = plan.source.txGraphJson;\n\n // The plan is a plain object, so the planner's comparison is re-run here on\n // the key that is actually about to be written into the file.\n const trustedVerifyingKeyHex = assertVerifyingKeyIsTrusted({\n servedVerifyingKeyHex: plan.source.verifyingKeyHex,\n trustedVerifyingKeyHex: plan.trustedVerifyingKeyHex,\n proverCircuitVersion: plan.vault.proverCircuitVersion,\n });\n\n const psbts = await buildBoundPsbtSet(\n txGraphVersion,\n graphJson,\n plan.vault,\n plan.depositorPublicKey,\n );\n const rebuilt = buildDelegatedClaimSigningRequests(psbts);\n assertPlanMatchesRebuilt(plan.requests, rebuilt);\n assertSignaturesCoverRequests(rebuilt, signatures);\n // Against the rebuilt request, so a caller-supplied map is held to the same\n // bar as one the signer produced.\n for (const request of rebuilt) {\n assertSignatureForRequest(\n plan,\n request,\n requireSignature(signatures, request.id),\n );\n }\n\n // Keyed off the rebuilt set, so each signature lands on the challenger and\n // garbled-circuit index the graph built its PSBT for.\n const wronglyChallengedSigs: WronglyChallengedSigs = {};\n for (const challengerPubkey of Object.keys(psbts.wronglyChallenged)) {\n wronglyChallengedSigs[challengerPubkey] = psbts.wronglyChallenged[\n challengerPubkey\n ].map((_, gcIndex) =>\n requireSignature(\n signatures,\n wronglyChallengedRequestId(challengerPubkey, gcIndex),\n ),\n );\n }\n\n const signedClaimTxHex = await finalizeClaimTx(\n txGraphVersion,\n graphJson,\n requireSignature(signatures, \"claim\"),\n );\n\n return buildWatchtowerArtifacts({\n txGraphVersion,\n graphJson,\n signedClaimTxHex,\n assertClaimerSigHex: requireSignature(signatures, \"assert\"),\n payoutClaimerSigHex: requireSignature(signatures, \"payoutClaimer\"),\n wronglyChallengedSigs,\n depositorPayoutSigHex: requireSignature(signatures, \"payoutDepositor\"),\n verifyingKeyHex: trustedVerifyingKeyHex,\n claimableEventBlockNumber: plan.vault.claimableEventBlockNumber,\n proverCircuitVersion: plan.vault.proverCircuitVersion,\n vaultIdHex: plan.vault.vaultId,\n babeSessionsJson: plan.babeSessionsJson,\n expectedVaultCoreVersion: plan.vault.vaultCoreVersion,\n });\n}\n","/**\n * Signs a delegated-claim plan with whichever wallet path fits.\n *\n * Software wallets sign the whole plan in one `signPsbts` batch where the\n * wallet supports it; a wallet without `signPsbts` is signed one PSBT at a\n * time by the fallback (the intended behaviour for Utila). Approval-capable\n * wallets (the `DepositTermsApprover` seam) hold a device session that binds\n * some of these signatures to a loaded intent and refuses others while it is\n * loaded, so for them the plan is signed as ordered ceremonies through the\n * {@link DelegatedClaimPsbtSigner} capability, which lets the provider put\n * its own derivation fields on each PSBT; see {@link signWithApprovalWallet}.\n *\n * @module services/delegated-claim/signDelegatedClaimPlan\n */\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets/interfaces\";\nimport type {\n DepositTerms,\n DepositTermsApprover,\n} from \"../../deposit-terms/depositTerms\";\nimport { supportsDepositApproval } from \"../../deposit-terms/depositTerms\";\nimport { signPsbtsWithFallback } from \"../../managers/pegin/signPsbtsWithFallback\";\nimport { uint8ArrayToHex } from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptionsForInput } from \"../../utils/signing\";\nimport type { VaultContextInput } from \"../../vault-secrets\";\nimport { deriveVaultRoot } from \"../../vault-secrets\";\nimport { extractTapScriptSig } from \"../../wasm\";\n\nimport type {\n DelegatedClaimSignatures,\n DelegatedClaimSigningKind,\n DelegatedClaimSigningPlan,\n DelegatedClaimSigningRequest,\n DelegatedClaimVaultContext,\n} from \"./types\";\nimport {\n assertSignatureForRequest,\n psbtBase64ToHex,\n psbtHexToBase64,\n} from \"./verifySignatureForRequest\";\nimport { assertWalletMatchesDepositor, xOnlyHex } from \"./walletIdentity\";\n\n/**\n * The claim-ceremony signing capability an approval wallet must add: its\n * device signs some claim PSBTs with no loaded intent and needs its own\n * derivation fields on them, which only the provider can write. Implementers\n * MUST classify each PSBT themselves from the single requested input and\n * refuse anything that is not one of the claim ceremony's shapes — this is\n * not a second `signPsbt`. The SDK cannot enforce that and does not rely on\n * it: the wallet is proved to be the depositor's before any prompt, and every\n * returned signature is verified against the PSBT that requested it. The SDK\n * probes for the method before it prompts.\n *\n * @experimental\n */\nexport interface DelegatedClaimPsbtSigner {\n signDelegatedClaimPsbt(\n psbtHex: string,\n options?: SignPsbtOptions,\n ): Promise<string>;\n}\n\n/** Probes {@link DelegatedClaimPsbtSigner.signDelegatedClaimPsbt}. */\nfunction supportsDelegatedClaimSigning(\n wallet: BitcoinWallet,\n): wallet is BitcoinWallet & DelegatedClaimPsbtSigner {\n return (\n typeof (wallet as Partial<DelegatedClaimPsbtSigner>)\n .signDelegatedClaimPsbt === \"function\"\n );\n}\n\n/**\n * Options for {@link signDelegatedClaimPlan}.\n *\n * @experimental\n */\nexport interface SignDelegatedClaimPlanOptions {\n /**\n * Required for approval-capable wallets: the terms that load the vault's\n * intent on the device. Resume flows rebuild them from on-chain state.\n */\n depositTerms?: DepositTerms;\n /**\n * Required for approval-capable wallets: the context the vault root derives\n * from; its depositor key must be the vault's registered key, checked before\n * any device I/O.\n */\n vaultContext?: VaultContextInput;\n /**\n * Signatures from an earlier, incomplete run of this same plan. Each is\n * verified against its request before it is reused; only standalone kinds\n * are reused, intent-bound ones are always re-signed. Applies to approval\n * wallets only: a software wallet always re-signs everything and ignores\n * this — one prompt for a wallet with native `signPsbts`, one prompt per\n * PSBT otherwise (the sequential fallback), and a cancel restarts the set.\n */\n resume?: DelegatedClaimSignatures;\n /**\n * Checked before every wallet prompt. The check before either path throws\n * the aborted signal's reason. Inside the approval ceremony, once the map\n * holds a signature — a verified resumed standalone entry counts — a stop is\n * reported as {@link DelegatedClaimSigningIncompleteError} with the reason as\n * `cause`, so the collected signatures survive for `resume`; while the map is\n * still empty the reason is thrown as is.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Thrown when an approval-wallet run stops before it reaches the end.\n *\n * Carries what was collected so a retry can pass it as `resume`. A retry\n * drops the intent-bound signatures and re-signs only those, so a stop in the\n * final intent release after every standalone request was resumed re-runs\n * only the intent-bound part of the ceremony — even though the map it carries\n * is complete by then. Never persist the map as artifacts: only the assembler\n * verifies it as a set.\n *\n * @experimental\n */\nexport class DelegatedClaimSigningIncompleteError extends Error {\n constructor(\n message: string,\n readonly signatures: DelegatedClaimSignatures,\n /**\n * The request the ceremony is to resume from — not necessarily the one\n * that failed: a stop in the intent release or approval is reported\n * against the request it was about to reach. The message says which.\n */\n readonly failedRequestId: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = \"DelegatedClaimSigningIncompleteError\";\n }\n}\n\n/** Which device state a kind signs in: only under this vault's loaded intent, or only without one. */\ntype DeviceSigningState = \"intentBound\" | \"standalone\";\n\n// Exhaustive over the kind union, so adding a kind without classifying it\n// fails to compile instead of silently dropping out of the ceremony.\nconst KIND_DEVICE_STATE: Record<DelegatedClaimSigningKind, DeviceSigningState> =\n {\n assert: \"intentBound\",\n payoutDepositor: \"intentBound\",\n payoutClaimer: \"standalone\",\n claim: \"standalone\",\n wronglyChallenged: \"standalone\",\n };\n/** Which device step a stop happened in, as the incomplete-run message names it. */\ntype CeremonyPhase =\n | \"clearing any loaded intent\"\n | \"releasing the loaded intent\"\n | \"approving the deposit terms\"\n | \"signing\";\n\n/** The order the standalone requests are signed once the intent is released. */\nconst STANDALONE_ORDER: readonly DelegatedClaimSigningKind[] = [\n \"payoutClaimer\",\n \"claim\",\n \"wronglyChallenged\",\n];\n\n/**\n * The plan must come from `planDelegatedClaimSigning`: the signer\n * signs it as given and does not rebuild it. A plan altered in between is\n * rejected at assembly by `assembleWatchtowerArtifactsFromSignatures`, which\n * rebuilds every PSBT from the graph, and a hardware wallet displays what it\n * signs.\n *\n * @returns Signatures keyed by request id, one per request in the plan.\n * @experimental\n */\nexport async function signDelegatedClaimPlan(\n plan: DelegatedClaimSigningPlan,\n wallet: BitcoinWallet,\n opts: SignDelegatedClaimPlanOptions = {},\n): Promise<DelegatedClaimSignatures> {\n opts.signal?.throwIfAborted();\n\n // Pure checks first, so a missing or mismatched input fails before the\n // wallet is asked anything. Then the sign options name the signer by\n // address, so the address must be proved to be this depositor's before any\n // prompt: a wallet on the wrong account would otherwise sign the whole set\n // for the wrong account.\n assertRequestIdsUnique(plan);\n // The planner enforces this, but the plan reaching here is a plain object.\n if (xOnlyHex(plan.depositorPublicKey) !== plan.vault.depositorBtcPubkey) {\n throw new Error(\n `Plan depositor key ${xOnlyHex(plan.depositorPublicKey)} is not the vault's registered depositor key ` +\n `${plan.vault.depositorBtcPubkey}; the plan was not built for this vault.`,\n );\n }\n if (supportsDepositApproval(wallet)) {\n if (!supportsDelegatedClaimSigning(wallet)) {\n throw new Error(\n \"Approval wallet does not implement signDelegatedClaimPsbt; the claim ceremony needs it because the \" +\n \"device signs some claim PSBTs with no loaded intent and needs its own derivation fields on them.\",\n );\n }\n const ceremony = requireApprovalInputs(opts, plan);\n assertTermsMatchVault(ceremony.depositTerms, plan.vault);\n const partition = partitionByDeviceState(plan);\n const signerAddress = await assertWalletMatchesDepositor(\n wallet,\n plan.depositorPublicKey,\n plan.btcNetwork,\n );\n return signWithApprovalWallet(\n plan,\n wallet,\n signerAddress,\n ceremony,\n partition,\n opts,\n );\n }\n const signerAddress = await assertWalletMatchesDepositor(\n wallet,\n plan.depositorPublicKey,\n plan.btcNetwork,\n );\n return signWithBatch(plan, wallet, signerAddress, opts.signal);\n}\n\ninterface DeviceStatePartition {\n intentBound: readonly DelegatedClaimSigningRequest[];\n /** In {@link STANDALONE_ORDER}, then plan order within a kind. */\n standalone: readonly DelegatedClaimSigningRequest[];\n}\n\n// Signatures are keyed by id on both wallet paths, so a repeated id would\n// silently overwrite one.\nfunction assertRequestIdsUnique(plan: DelegatedClaimSigningPlan): void {\n const seen = new Set<string>();\n for (const request of plan.requests) {\n if (seen.has(request.id)) {\n throw new Error(\n `Delegated-claim plan lists request \"${request.id}\" more than once; ` +\n \"its signatures could not be keyed, so nothing is signed.\",\n );\n }\n seen.add(request.id);\n }\n}\n\nfunction partitionByDeviceState(\n plan: DelegatedClaimSigningPlan,\n): DeviceStatePartition {\n const intentBound = plan.requests.filter(\n (r) => KIND_DEVICE_STATE[r.kind] === \"intentBound\",\n );\n const standalone = STANDALONE_ORDER.flatMap((kind) =>\n plan.requests.filter((r) => r.kind === kind),\n );\n const classified = intentBound.length + standalone.length;\n if (classified !== plan.requests.length) {\n throw new Error(\n `Delegated-claim plan has ${plan.requests.length} signing requests but only ${classified} are of a kind ` +\n \"this signer can place in the device ceremony; the plan and the signer disagree on the request kinds.\",\n );\n }\n return { intentBound, standalone };\n}\n\nfunction assertEveryRequestSigned(\n plan: DelegatedClaimSigningPlan,\n signatures: DelegatedClaimSignatures,\n): void {\n if (signatures.size !== plan.requests.length) {\n throw new Error(\n `Delegated-claim signing ended with ${signatures.size} signatures for ${plan.requests.length} requests; ` +\n \"the set is incomplete and must not be treated as signed.\",\n );\n }\n}\n\ninterface ApprovalCeremonyInputs {\n depositTerms: DepositTerms;\n vaultContext: VaultContextInput;\n}\n\nfunction requireApprovalInputs(\n opts: SignDelegatedClaimPlanOptions,\n plan: DelegatedClaimSigningPlan,\n): ApprovalCeremonyInputs {\n if (opts.depositTerms === undefined) {\n throw new Error(\n \"An approval wallet signs Assert and the depositor Payout only under this vault's \" +\n \"loaded intent; pass depositTerms (rebuilt from on-chain state) so it can be approved.\",\n );\n }\n if (opts.vaultContext === undefined) {\n throw new Error(\n \"An approval wallet releases a loaded intent only through the vault-root derivation; \" +\n \"pass vaultContext so that derivation can run before and after the intent-bound signatures.\",\n );\n }\n // Both derivation screens would otherwise run for a context built for\n // another depositor than the one this vault is registered to.\n const contextKey = uint8ArrayToHex(opts.vaultContext.depositorBtcPubkey);\n if (contextKey !== plan.vault.depositorBtcPubkey) {\n throw new Error(\n `vaultContext derives from depositor key ${contextKey} but this plan's vault is registered to ` +\n `${plan.vault.depositorBtcPubkey}; the device would derive for another depositor.`,\n );\n }\n return { depositTerms: opts.depositTerms, vaultContext: opts.vaultContext };\n}\n\n/**\n * Every field the terms and the vault context both carry must agree: the\n * rosters, the vault core version, the two CSV timelocks, the protocol fee\n * rate, and the vault provider key of each group the terms describe.\n * Otherwise the device binds the Assert to another deposit's intent.\n *\n * Exported so a claim-time terms builder can be checked against the context\n * it will be signed with, before any device session.\n *\n * @experimental\n */\nexport function assertTermsMatchVault(\n terms: DepositTerms,\n vault: DelegatedClaimVaultContext,\n): void {\n const norm = (keys: readonly string[]) =>\n keys\n .map((k) => xOnlyHex(k))\n .sort()\n .join(\",\");\n if (\n norm(terms.vaultKeeperBtcPubkeys) !== norm(vault.vaultKeeperBtcPubkeys) ||\n norm(terms.universalChallengerBtcPubkeys) !==\n norm(vault.universalChallengerBtcPubkeys)\n ) {\n throw new Error(\n \"Deposit terms carry different keeper or challenger rosters than the vault context this claim signs for; \" +\n \"the device would bind the Assert to the wrong intent.\",\n );\n }\n if (terms.vaultCoreVersion !== vault.vaultCoreVersion) {\n throw new Error(\n `Deposit terms describe vault core version ${terms.vaultCoreVersion} but the vault context has ` +\n `version ${vault.vaultCoreVersion}; the device would bind the Assert to the wrong intent.`,\n );\n }\n if (terms.timelockPegin !== vault.timelockPegin) {\n throw new Error(\n `Deposit terms describe timelockPegin ${terms.timelockPegin} but the vault context has ` +\n `${vault.timelockPegin}; the device would bind the Assert to the wrong intent.`,\n );\n }\n if (terms.timelockAssert !== vault.timelockAssert) {\n throw new Error(\n `Deposit terms describe timelockAssert ${terms.timelockAssert} but the vault context has ` +\n `${vault.timelockAssert}; the device would bind the Assert to the wrong intent.`,\n );\n }\n if (terms.protocolFeeRate !== vault.protocolFeeRate) {\n throw new Error(\n `Deposit terms describe protocolFeeRate ${terms.protocolFeeRate} but the vault context has ` +\n `${vault.protocolFeeRate}; the device would bind the Assert to the wrong intent.`,\n );\n }\n if (terms.vaults.length === 0) {\n throw new Error(\n \"Deposit terms describe no vault group, so they cannot name the vault context's vault provider; \" +\n \"the device would bind the Assert to the wrong intent.\",\n );\n }\n const vaultProvider = xOnlyHex(vault.vaultProviderBtcPubkey);\n for (const group of terms.vaults) {\n if (xOnlyHex(group.vaultProviderBtcPubkey) !== vaultProvider) {\n throw new Error(\n `Deposit terms group at htlcVout ${group.htlcVout} names a different vault provider than the vault ` +\n \"context this claim signs for; the device would bind the Assert to the wrong intent.\",\n );\n }\n }\n}\n\n/**\n * The device sequence, set by two rules. Firmware: Assert signs only under\n * INTENT_LOADED, and while this vault's intent is loaded a Payout whose input\n * 0 spends its PegIn is routed to the intent-bound validator, which signs\n * the depositor's input 0 only, so the claimer's input 1 is unreachable in\n * that state; Claim and WronglyChallenged sign in any state (app-babylon-vault\n * `sign_psbt_validate.c` dispatcher and routing @ b0c0ac4d). Provider (P3):\n * `LedgerVaultProvider` refuses the claimer Payout while its mirror is\n * intent-loaded, with a typed error saying to derive first. A context\n * derivation is the only release, so the order is derive → approve →\n * intent-bound → derive → standalone. Each public wallet call takes its own\n * device lock, so they are sequenced, never nested. The optional\n * `validateDepositTerms` probe runs before all of it, host-side, so terms the\n * device envelope refuses fail before the first derive screen.\n */\nasync function signWithApprovalWallet(\n plan: DelegatedClaimSigningPlan,\n wallet: BitcoinWallet & DepositTermsApprover & DelegatedClaimPsbtSigner,\n signerAddress: string,\n ceremony: ApprovalCeremonyInputs,\n { intentBound, standalone }: DeviceStatePartition,\n opts: SignDelegatedClaimPlanOptions,\n): Promise<DelegatedClaimSignatures> {\n // Seeded with the verified resumed standalone signatures, so a stop at any\n // point hands them back along with whatever this run adds.\n const signatures = verifiedResumable(plan, opts.resume);\n // The full ceremony order; `queue[position]` is the request the ceremony is\n // at, resumed or not, which is what a stop is reported against.\n const queue = [...intentBound, ...standalone];\n let position = 0;\n // On a resume the map is pre-populated, so the position alone cannot say\n // whether a stop happened in a wallet prompt or in the release/approval\n // around it; this names which. Held in an object so the compiler keeps the\n // value `signOne` writes from its own scope.\n const stop: { phase: CeremonyPhase } = {\n phase: \"clearing any loaded intent\",\n };\n\n const signOne = async (\n request: DelegatedClaimSigningRequest,\n ): Promise<void> => {\n stop.phase = \"signing\";\n opts.signal?.throwIfAborted();\n const signedHex = await wallet.signDelegatedClaimPsbt(\n psbtBase64ToHex(request.psbtBase64),\n createTaprootScriptPathSignOptionsForInput(\n plan.depositorPublicKey,\n request.inputIndex,\n signerAddress,\n ),\n );\n const signatureHex = await extractTapScriptSig(\n psbtHexToBase64(signedHex),\n request.inputIndex,\n );\n assertSignatureForRequest(plan, request, signatureHex);\n signatures.set(request.id, signatureHex);\n };\n\n // The phase is a parameter because the opening derive runs on a device that\n // may hold no intent at all, which the second one never does.\n const releaseIntent = async (phase: CeremonyPhase): Promise<void> => {\n stop.phase = phase;\n opts.signal?.throwIfAborted();\n // deriveVaultRoot validates the wallet's reply; the root itself is not\n // needed here and is zeroed at once.\n const root = await deriveVaultRoot(wallet, ceremony.vaultContext);\n root.fill(0);\n };\n\n // Envelope violations fail here, before the derive costs a physical\n // approval. Validate-only per DepositTermsApprover — no device I/O. Outside\n // the try: a terms refusal is deterministic, so even on a resume it throws\n // as is rather than as a stop a resume retry would repeat.\n if (typeof wallet.validateDepositTerms === \"function\") {\n await wallet.validateDepositTerms(ceremony.depositTerms);\n }\n\n try {\n await releaseIntent(\"clearing any loaded intent\");\n stop.phase = \"approving the deposit terms\";\n opts.signal?.throwIfAborted();\n await wallet.approveDepositTerms(ceremony.depositTerms);\n for (const request of intentBound) {\n await signOne(request);\n position++;\n }\n // Always run, even with every standalone request resumed: it is the only\n // release of the loaded intent, and it precedes the standalone phase, so\n // the first standalone request is what it unblocks.\n await releaseIntent(\"releasing the loaded intent\");\n for (const request of standalone) {\n if (!signatures.has(request.id)) await signOne(request);\n position++;\n }\n } catch (cause) {\n // \"Collected\" means the map is non-empty, resumed entries included. An\n // abort or failure before that surfaces as is; after it, every stop is\n // reported with the map, so the caller keeps what it has. A plan with no\n // standalone phase has nothing to report a release failure against.\n const next = queue[position];\n if (signatures.size === 0 || next === undefined) throw cause;\n const where =\n stop.phase === \"signing\"\n ? `stopped at \"${next.id}\"`\n : `stopped before \"${next.id}\" while ${stop.phase}`;\n throw new DelegatedClaimSigningIncompleteError(\n `Delegated-claim signing ${where}; ${signatures.size} of ${plan.requests.length} signatures were collected.`,\n signatures,\n next.id,\n { cause },\n );\n }\n assertEveryRequestSigned(plan, signatures);\n return signatures;\n}\n\n/**\n * Resumable signatures that verify against their request. Intent-bound kinds\n * are dropped (the device re-signs them under the fresh intent), and so is\n * any signature that fails verification: it is re-signed, not reported.\n */\nfunction verifiedResumable(\n plan: DelegatedClaimSigningPlan,\n resume: DelegatedClaimSignatures | undefined,\n): Map<string, string> {\n const out = new Map<string, string>();\n if (!resume) return out;\n for (const request of plan.requests) {\n const signatureHex = resume.get(request.id);\n if (\n signatureHex === undefined ||\n KIND_DEVICE_STATE[request.kind] === \"intentBound\"\n )\n continue;\n try {\n assertSignatureForRequest(plan, request, signatureHex);\n } catch {\n // Swallowed on purpose: a resumed entry that does not verify is re-signed\n // rather than reported; a malformed PSBT re-raises at fresh-sign time.\n continue;\n }\n out.set(request.id, signatureHex);\n }\n return out;\n}\n\nasync function signWithBatch(\n plan: DelegatedClaimSigningPlan,\n wallet: BitcoinWallet,\n signerAddress: string,\n signal: AbortSignal | undefined,\n): Promise<DelegatedClaimSignatures> {\n const requests = plan.requests;\n const signedPsbtHexes = await signPsbtsWithFallback(\n wallet,\n requests.map((r) => psbtBase64ToHex(r.psbtBase64)),\n requests.map((r) =>\n createTaprootScriptPathSignOptionsForInput(\n plan.depositorPublicKey,\n r.inputIndex,\n signerAddress,\n ),\n ),\n signal,\n );\n const signatures = new Map<string, string>();\n for (let i = 0; i < requests.length; i++) {\n const signatureHex = await extractTapScriptSig(\n psbtHexToBase64(signedPsbtHexes[i]),\n requests[i].inputIndex,\n );\n assertSignatureForRequest(plan, requests[i], signatureHex);\n signatures.set(requests[i].id, signatureHex);\n }\n assertEveryRequestSigned(plan, signatures);\n return signatures;\n}\n","/**\n * Watchtower artifact assembly for the delegated claim.\n *\n * Collects every claimer-side signature the `vaultd vp wt` watchtower CLI\n * will need — in one batched wallet interaction — and bundles them with the\n * vault provider's transaction graph into an `artifacts.json`.\n *\n * Do this while the vault provider is still online and the depositor is\n * still at the keyboard. The signatures are Taproot script-path signatures\n * whose sighashes do not cover witness data, so they stay valid however the\n * claim later plays out: the WOTS values, the Groth16 proof, and the hashlock\n * preimages are all witness-only. That is what lets one signing session\n * authorize a claim that runs months later without the depositor present.\n *\n * @module services/delegated-claim/assembleWatchtowerArtifacts\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport type { DepositTerms } from \"../../deposit-terms/depositTerms\";\nimport type { VaultContextInput } from \"../../vault-secrets\";\n\nimport { assembleWatchtowerArtifactsFromSignatures } from \"./assembleWatchtowerArtifactsFromSignatures\";\nimport { planDelegatedClaimSigning } from \"./planDelegatedClaimSigning\";\nimport { signDelegatedClaimPlan } from \"./signDelegatedClaimPlan\";\nimport type {\n ClaimerArtifactsSource,\n DelegatedClaimVaultContext,\n} from \"./types\";\n\n/**\n * Everything one delegated-claim signing session needs.\n *\n * @experimental\n */\nexport interface AssembleWatchtowerArtifactsParams {\n /** Wallet holding the depositor key the graph was built with. */\n btcWallet: BitcoinWallet;\n /** Depositor's BTC public key (compressed or x-only hex). */\n depositorPublicKey: string;\n /** Network the depositor's address is derived on, to check the signer. */\n btcNetwork: Network;\n /** Graph and verifying key as the vault provider returned them. */\n source: ClaimerArtifactsSource;\n /** See {@link DelegatedClaimSigningPlan.trustedVerifyingKeyHex}. */\n trustedVerifyingKeyHex: string;\n vault: DelegatedClaimVaultContext;\n /**\n * Per-challenger BaBe sessions as `{\"<pk>\": {\"decryptor_artifacts_hex\":\n * \"...\"}}`, passed through into the file unchanged.\n *\n * Omit it to build an unjoined file: real sessions run to hundreds of\n * megabytes per challenger, too large for a browser tab. An unjoined file\n * cannot answer a challenge, so join the sessions in before starting a\n * claim from it (see {@link BabeSessionsState}). A map that is passed must\n * cover exactly the graph's challengers (btc-vault `validate_babe_sessions`).\n */\n babeSessionsJson?: string;\n /**\n * Required for approval-capable wallets (the `DepositTermsApprover` seam):\n * the terms that load this vault's intent on the device. Resume flows\n * rebuild them from on-chain state.\n */\n depositTerms?: DepositTerms;\n /**\n * Required for approval-capable wallets: the context the vault root derives\n * from; its depositor key must be the vault's registered key, checked before\n * any device I/O.\n */\n vaultContext?: VaultContextInput;\n}\n\n/**\n * Signs the delegated-claim set and returns the `artifacts.json` content,\n * ready to write verbatim.\n *\n * Composition of {@link planDelegatedClaimSigning},\n * {@link signDelegatedClaimPlan} and\n * {@link assembleWatchtowerArtifactsFromSignatures}. Software wallets sign\n * in one batched prompt; approval-capable wallets sign as ordered device\n * ceremonies. Every signature is verified against the graph before the\n * file is produced. This one-shot forwards neither `signal` nor `resume`;\n * callers needing those use the split.\n *\n * Experimental: this API can change in a minor release. Pin the SDK\n * version if you build on it.\n *\n * @throws If the vault provider's verifying key is not `trustedVerifyingKeyHex`,\n * if the graph is not version 3, if a binding check fails, if the\n * wallet returns a signature that does not verify, or if the graph's\n * own presignatures are incomplete.\n * @experimental\n */\nexport async function assembleWatchtowerArtifacts(\n params: AssembleWatchtowerArtifactsParams,\n): Promise<string> {\n const plan = await planDelegatedClaimSigning({\n depositorPublicKey: params.depositorPublicKey,\n btcNetwork: params.btcNetwork,\n source: params.source,\n trustedVerifyingKeyHex: params.trustedVerifyingKeyHex,\n vault: params.vault,\n babeSessionsJson: params.babeSessionsJson,\n });\n const signatures = await signDelegatedClaimPlan(plan, params.btcWallet, {\n depositTerms: params.depositTerms,\n vaultContext: params.vaultContext,\n });\n return assembleWatchtowerArtifactsFromSignatures({ plan, signatures });\n}\n","/**\n * WOTS keypair derivation for the delegated claim.\n *\n * The watchtower CLI's `start-claim` needs the depositor's secret WOTS hash\n * chains to commit π₁ into the Assert witness. They are never stored: they\n * are re-derived from the wallet on demand, through the same frozen path\n * that produced the on-chain `depositorWotsPkHash` at deposit time.\n *\n * @module services/delegated-claim/deriveClaimerWotsKeypair\n */\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport { deriveVaultRoot, expandWotsSeed } from \"../../vault-secrets\";\nimport type { VaultContextInput } from \"../../vault-secrets\";\nimport {\n validateWotsKeypairAgainstGraph,\n wotsKeypairFromSeed,\n} from \"../../wasm\";\n\n/**\n * Inputs for re-deriving the depositor's WOTS keypair at claim time.\n *\n * @experimental\n */\nexport interface DeriveClaimerWotsKeypairParams {\n /** Must implement `deriveContextHash` — the only wallet prompt in the claim. */\n btcWallet: BitcoinWallet;\n /** Same context that produced this vault's secrets at deposit time. */\n vaultContext: VaultContextInput;\n /** HTLC output index of this vault within the Pre-PegIn transaction. */\n htlcVout: number;\n /** JSON-serialized TxGraph the keypair must match. */\n txGraphJson: string;\n /** Graph version. Delegated claim requires 3. */\n txGraphVersion: number;\n /**\n * `depositorWotsPkHash` as the vault records it on chain, `0x`-prefixed.\n *\n * This is the only anchor here the vault provider does not supply. The\n * graph check below compares against VP-served bytes, so it cannot tell a\n * wrong `htlcVout`, a wrong wallet account, or expander drift after a\n * WASM re-pin from a correct derivation.\n */\n expectedWotsPkHash: string;\n}\n\n/**\n * The `wots_keypair.json` content and the hash it commits to.\n *\n * @experimental\n */\nexport interface ClaimerWotsKeypair {\n /**\n * Content of `wots_keypair.json`, ready to write verbatim. Secret and\n * single-use: never log it, never persist it beyond the claim, and never\n * reuse it — reuse across claims leaks the WOTS key.\n */\n wotsKeypairJson: string;\n /** `0x`-prefixed hash of the public keys, matching `depositorWotsPkHash`. */\n pkHash: string;\n}\n\n/**\n * Re-derives the depositor's WOTS keypair and checks it against the vault's\n * on-chain commitment and against the graph.\n *\n * The validation is the point of this function, not a formality: an unbound\n * keypair produces an Assert witness no verifier accepts, and that failure\n * would otherwise surface only after the Claim has been broadcast and the\n * PegIn UTXO is already spent. The on-chain hash is checked first, because\n * it is the one value here the vault provider cannot choose.\n *\n * Experimental: this API can change in a minor release. Pin the SDK\n * version if you build on it.\n *\n * @throws If the derivation does not match the vault's on-chain\n * `depositorWotsPkHash`, or the WOTS public keys the graph's Claim\n * commits to.\n * @experimental\n */\nexport async function deriveClaimerWotsKeypair(\n params: DeriveClaimerWotsKeypairParams,\n): Promise<ClaimerWotsKeypair> {\n const root = await deriveVaultRoot(params.btcWallet, params.vaultContext);\n let wotsSeed: Uint8Array;\n try {\n wotsSeed = await expandWotsSeed(root, params.htlcVout);\n } finally {\n root.fill(0);\n }\n\n let derivation;\n try {\n derivation = await wotsKeypairFromSeed(wotsSeed);\n } finally {\n wotsSeed.fill(0);\n }\n\n assertMatchesOnChainHash(derivation.pk_hash, params.expectedWotsPkHash);\n\n await validateWotsKeypairAgainstGraph(\n params.txGraphVersion,\n derivation.keypair,\n params.txGraphJson,\n );\n\n return {\n wotsKeypairJson: JSON.stringify(derivation.keypair),\n pkHash: derivation.pk_hash,\n };\n}\n\n/**\n * Throws unless the derived public-key hash is the one the vault committed\n * to on chain at deposit time.\n */\nfunction assertMatchesOnChainHash(derived: string, onChain: string): void {\n if (normalizeHash(derived) !== normalizeHash(onChain)) {\n throw new Error(\n `Derived WOTS public-key hash ${normalizeHash(derived)} does not match ` +\n `the vault's on-chain depositorWotsPkHash ${normalizeHash(onChain)}. ` +\n `The wallet, the HTLC output index, or the derivation itself is not ` +\n `the one this vault was created with.`,\n );\n }\n}\n\nfunction normalizeHash(hash: string): string {\n const bare = hash.startsWith(\"0x\") ? hash.slice(2) : hash;\n return `0x${bare.toLowerCase()}`;\n}\n","/**\n * RFC-006 participant operation-key resolution.\n *\n * Under RFC-006 an operator's BTC key is no longer fixed at registration: it\n * is an append-only history of *operation* keys, rotated by the operator's\n * cold ETH admin key. Every consumer that needs \"the key this participant\n * signs with\" must resolve it, in one of two modes:\n *\n * - **current** — for a peg-in being built now, and for the VP auth pin.\n * Needs no epoch read; each registry resolves its own genesis fallback, so\n * an operator that never rotated yields its registration key.\n * - **at epochs** — for any vault that already exists. The vault froze its\n * epochs at `submitPeginRequest`, and resolving against them is what keeps\n * an old vault signable after a later rotation.\n *\n * Both modes go through the same `finalize` pass, which is where the\n * rotation-specific safety checks live.\n *\n * @module services/participants/resolveParticipantKeys\n */\n\nimport { assertOnChainBtcPubkey } from \"../../clients/eth/onChainBtcPubkey\";\nimport type {\n KeyEpochs,\n OperationKeyQuery,\n OperationKeyReader,\n RawOperationKeys,\n} from \"../../clients/eth/types\";\nimport type {\n KeyResolutionMode,\n ParticipantKeySet,\n ResolvedParticipant,\n} from \"./types\";\n\n/** Role label used in validation error messages. */\ntype Role = \"vault provider\" | \"vault keeper\" | \"universal challenger\";\n\nfunction resolveOne(\n role: Role,\n rosterEntry: { ethAddress: `0x${string}`; btcPubKey: `0x${string}` },\n rawOperationKey: `0x${string}`,\n): ResolvedParticipant {\n const label = `${role} operation key (admin=${rosterEntry.ethAddress})`;\n const genesisBtcPubkey = assertOnChainBtcPubkey(\n rosterEntry.btcPubKey,\n `${role} roster key (admin=${rosterEntry.ethAddress})`,\n );\n const operationBtcPubkey = assertOnChainBtcPubkey(rawOperationKey, label);\n\n return {\n adminAddress: rosterEntry.ethAddress,\n genesisBtcPubkey,\n operationBtcPubkey,\n rotated: operationBtcPubkey !== genesisBtcPubkey,\n };\n}\n\n/**\n * Assert no two participants resolved to the same operation key.\n *\n * The on-chain guarantees are narrower than \"registration keys are distinct\",\n * and narrower than an earlier revision of this comment claimed. Each registry\n * enforces uniqueness *within its own role*, on rotation as well as\n * registration, and a VP rotation additionally rejects any key held in the\n * current vault-keeper or universal-challenger rosters.\n *\n * What no contract checks is the cross-role direction those rules leave open:\n * a keeper or challenger rotating **onto** the VP's key (the VP-side check runs\n * at VP rotation time and does not run again when a keeper moves), a keeper and\n * a challenger colliding with each other, and keepers of different applications\n * colliding. Any of those would collapse two entries in the sorted script key\n * set and silently build a lock with the wrong participant count, so they are\n * rejected here — before any script is constructed — rather than surfacing as a\n * confusing signature mismatch much later.\n */\nfunction assertDistinctOperationKeys(\n participants: ResolvedParticipant[],\n): void {\n const seen = new Map<string, string>();\n for (const participant of participants) {\n const previousOwner = seen.get(participant.operationBtcPubkey);\n if (previousOwner) {\n throw new Error(\n `Participant operation key collision: ${previousOwner} and ` +\n `${participant.adminAddress} both resolve to operation key ` +\n `${participant.operationBtcPubkey}`,\n );\n }\n seen.set(participant.operationBtcPubkey, participant.adminAddress);\n }\n}\n\nfunction finalize(\n query: OperationKeyQuery,\n raw: RawOperationKeys,\n resolvedAt: KeyResolutionMode,\n): ParticipantKeySet {\n if (\n raw.vaultKeepers.length !== query.vaultKeepers.length ||\n raw.universalChallengers.length !== query.universalChallengers.length\n ) {\n throw new Error(\n `Operation-key resolution returned ${raw.vaultKeepers.length} keeper and ` +\n `${raw.universalChallengers.length} challenger keys for a roster of ` +\n `${query.vaultKeepers.length} keepers and ` +\n `${query.universalChallengers.length} challengers`,\n );\n }\n\n const vaultProvider = resolveOne(\n \"vault provider\",\n {\n ethAddress: query.vaultProviderEthAddress,\n btcPubKey: query.vaultProviderGenesisBtcPubkey,\n },\n raw.vaultProvider,\n );\n\n const vaultKeepers = query.vaultKeepers.map((keeper, i) =>\n resolveOne(\"vault keeper\", keeper, raw.vaultKeepers[i]),\n );\n const universalChallengers = query.universalChallengers.map((challenger, i) =>\n resolveOne(\"universal challenger\", challenger, raw.universalChallengers[i]),\n );\n\n assertDistinctOperationKeys([\n vaultProvider,\n ...vaultKeepers,\n ...universalChallengers,\n ]);\n\n return {\n vaultProvider,\n vaultKeepers,\n universalChallengers,\n // Derived from the pairs, never the reverse — see ParticipantKeySet.\n vaultKeeperOperationKeysSorted: vaultKeepers\n .map((p) => p.operationBtcPubkey as string)\n .sort(),\n universalChallengerOperationKeysSorted: universalChallengers\n .map((p) => p.operationBtcPubkey as string)\n .sort(),\n resolvedAt,\n query,\n };\n}\n\n/**\n * Resolve every participant's *current* operation key.\n *\n * Use for a peg-in being built now. Issues no epoch read, so it never touches\n * the extended `getBtcVaultProtocolInfo` ABI.\n *\n * `blockNumber` pins the resolution to one block, and is required. Pass the\n * same block the rosters in `query` were read at: the roster supplies each\n * participant's address and genesis key, and this call resolves what that\n * participant has rotated to. Reading the two at different blocks can pair a\n * roster member with a key from a different chain state, so there is no\n * correct unpinned call and no default worth having.\n */\nexport async function resolveCurrentParticipantKeys(params: {\n operationKeyReader: OperationKeyReader;\n query: OperationKeyQuery;\n blockNumber: bigint;\n}): Promise<ParticipantKeySet> {\n const raw = await params.operationKeyReader.getCurrentOperationKeys(\n params.query,\n params.blockNumber,\n );\n return finalize(params.query, raw, { mode: \"current\" });\n}\n\n/**\n * Resolve every participant's operation key bonded at a vault's frozen epochs.\n *\n * Use for every existing-vault path: resume, payout signing, refund. The\n * rosters in `query` must be read at the vault's frozen *membership* versions,\n * because those roster keys are the genesis the keeper/challenger getters fall\n * back to.\n */\nexport async function resolveParticipantKeysAtEpochs(params: {\n // Only the epochs getter is used, so a caller holding just that (the\n // claim-time readers) needs no full reader.\n operationKeyReader: Pick<OperationKeyReader, \"getOperationKeysAtEpochs\">;\n query: OperationKeyQuery;\n epochs: KeyEpochs;\n}): Promise<ParticipantKeySet> {\n const raw = await params.operationKeyReader.getOperationKeysAtEpochs(\n params.query,\n params.epochs,\n );\n return finalize(params.query, raw, {\n mode: \"epochs\",\n epochs: params.epochs,\n });\n}\n","/**\n * The participant keys an existing vault's scripts were built with.\n *\n * Rosters at the vault's stamped membership versions, then operation keys at\n * its frozen epochs — the same recipe the vault app runs on every resume\n * (`readStampedVaultContext` in services/vault). Lives in the SDK so the\n * claim-time context and terms builders, which have no app around them, read\n * exactly what the deposit-time flow read.\n *\n * @module services/participants/readStampedParticipantKeys\n */\n\nimport type { Hex } from \"viem\";\n\nimport type {\n OperationKeyReader,\n UniversalChallengerReader,\n VaultData,\n VaultKeeperReader,\n VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { resolveParticipantKeysAtEpochs } from \"./resolveParticipantKeys\";\nimport type { ParticipantKeySet } from \"./types\";\n\n/**\n * The reads this needs, as `Pick`s so a caller can pass the SDK's viem readers\n * or a narrower implementation.\n *\n * @experimental\n */\nexport interface StampedParticipantKeyReaders {\n registryReader: Pick<\n VaultRegistryReader,\n \"getVaultProviderGenesisBtcPubKey\" | \"getVaultKeyEpochs\"\n >;\n vaultKeeperReader: Pick<VaultKeeperReader, \"getVaultKeepersByVersion\">;\n universalChallengerReader: Pick<\n UniversalChallengerReader,\n \"getUniversalChallengersByVersion\"\n >;\n operationKeyReader: Pick<OperationKeyReader, \"getOperationKeysAtEpochs\">;\n}\n\n/** @experimental */\nexport interface ReadStampedParticipantKeysParams {\n vaultId: Hex;\n /** The vault's on-chain record, as `VaultRegistryReader.getVaultData` returns it. */\n vault: VaultData;\n readers: StampedParticipantKeyReaders;\n}\n\n/**\n * @throws When either stamped roster is empty, or key resolution fails\n * (`resolveParticipantKeysAtEpochs`).\n * @experimental\n */\nexport async function readStampedParticipantKeys(\n params: ReadStampedParticipantKeysParams,\n): Promise<ParticipantKeySet> {\n const { basic, protocol } = params.vault;\n const {\n registryReader,\n vaultKeeperReader,\n universalChallengerReader,\n operationKeyReader,\n } = params.readers;\n\n const [\n vaultKeepers,\n universalChallengers,\n vaultProviderGenesisBtcPubkey,\n epochs,\n ] = await Promise.all([\n vaultKeeperReader.getVaultKeepersByVersion(\n basic.applicationEntryPoint,\n protocol.appVaultKeepersVersion,\n ),\n universalChallengerReader.getUniversalChallengersByVersion(\n protocol.universalChallengersVersion,\n ),\n registryReader.getVaultProviderGenesisBtcPubKey(basic.vaultProvider),\n registryReader.getVaultKeyEpochs(params.vaultId),\n ]);\n\n if (vaultKeepers.length === 0) {\n throw new Error(\n `No vault keepers for version ${protocol.appVaultKeepersVersion}`,\n );\n }\n if (universalChallengers.length === 0) {\n throw new Error(\n `No universal challengers for version ${protocol.universalChallengersVersion}`,\n );\n }\n\n return resolveParticipantKeysAtEpochs({\n operationKeyReader,\n query: {\n vaultProviderEthAddress: basic.vaultProvider,\n vaultProviderGenesisBtcPubkey:\n `0x${vaultProviderGenesisBtcPubkey}` as Hex,\n applicationEntryPoint: basic.applicationEntryPoint,\n vaultKeepers,\n universalChallengers,\n },\n epochs,\n });\n}\n","/**\n * The vault facts a delegated claim needs, read from chain months after the\n * deposit, with no vault provider and no indexer in the loop.\n *\n * Every field of {@link DelegatedClaimVaultContext} comes from the registry:\n * the vault record, its `PegInSubmittedV2` registration log, its finalized\n * `VaultClaimableBy` redemption log, the offchain params the vault stamped,\n * and the participant rosters at the versions and epochs the vault froze.\n * Where two of those sources carry the same value they are compared, and a\n * disagreement refuses the read rather than picking one.\n *\n * @module services/delegated-claim/readDelegatedClaimVaultContext\n */\n\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Address, Hex } from \"viem\";\n\nimport {\n VaultClaimableByNotFoundError,\n isVaultClaimableByNotFoundError,\n} from \"../../clients/eth/claimable-event-error\";\nimport { assertOnChainBtcPubkey } from \"../../clients/eth/onChainBtcPubkey\";\nimport {\n calculateBtcTxHash,\n derivePeginVaultId,\n} from \"../../clients/eth/pegin-transaction\";\nimport { deriveTimelockPegin } from \"../../clients/eth/protocol-params-reader\";\nimport {\n assertRegisteredPayoutScriptBounds,\n findRegistrationRecord,\n registrationPrePeginTxHash,\n} from \"../../clients/eth/registration-records\";\nimport {\n OnChainBtcVaultStatus,\n type PeginRegistrationRecord,\n type ProtocolParamsReader,\n type VaultData,\n type VaultRegistryReader,\n type VersionedOffchainParams,\n} from \"../../clients/eth/types\";\nimport { PEGIN_VAULT_OUTPUT_INDEX } from \"../../primitives/psbt/constants\";\nimport { stripHexPrefix } from \"../../primitives/utils/bitcoin\";\nimport {\n readStampedParticipantKeys,\n type StampedParticipantKeyReaders,\n} from \"../participants/readStampedParticipantKeys\";\nimport type { ParticipantKeySet } from \"../participants/types\";\nimport { DELEGATED_CLAIM_TX_GRAPH_VERSION } from \"./readWatchtowerArtifacts\";\nimport type { DelegatedClaimVaultContext } from \"./types\";\nimport { displayTxid, normalizeVaultId } from \"./vaultIdBinding\";\n\n/** @experimental */\nexport interface DelegatedClaimVaultReaders\n extends StampedParticipantKeyReaders {\n registryReader: Pick<\n VaultRegistryReader,\n | \"getVaultData\"\n | \"getVaultKeyEpochs\"\n | \"getVaultProviderGenesisBtcPubKey\"\n | \"getRegistrationRecordsAtBlock\"\n | \"getVaultClaimableBy\"\n >;\n protocolParamsReader: Pick<\n ProtocolParamsReader,\n \"getOffchainParamsByVersion\"\n >;\n}\n\n/** @experimental */\nexport interface ReadDelegatedClaimVaultContextParams {\n vaultId: Hex;\n readers: DelegatedClaimVaultReaders;\n}\n\n/**\n * The context, plus the sibling facts a claim needs that the context does not\n * carry: the on-chain WOTS commitment (`deriveClaimerWotsKeypair.expectedWotsPkHash`),\n * what `buildVaultContextInputForClaim` takes, and every record this read\n * already fetched that `rebuildDepositTermsForClaim` would otherwise fetch\n * again — it takes this whole object rather than a vault id.\n *\n * @experimental\n */\nexport interface DelegatedClaimVaultRead {\n context: DelegatedClaimVaultContext;\n /** The target's registry record, as read here. */\n vault: VaultData;\n /** The target's own `PegInSubmittedV2` log. */\n registrationRecord: PeginRegistrationRecord;\n /** Every log in the target's registration block, the batch's siblings among them. */\n registrationRecords: readonly PeginRegistrationRecord[];\n /** Participant operation keys resolved at the vault's stamped versions and epochs. */\n participantKeys: ParticipantKeySet;\n /** The offchain params snapshot at `protocol.offchainParamsVersion`. */\n offchainParams: VersionedOffchainParams;\n /** `VaultBasicInfo.depositorBtcPubKey` verbatim, for `buildVaultContextInputForClaim`. */\n depositorBtcPubKeyBytes32: Hex;\n depositorWotsPkHash: Hex;\n prePeginTxHash: Hex;\n /**\n * Txid of the vault's depositor-signed PegIn, derived here through the\n * dependency-free parser and cross-checked against both logs. The vault\n * provider indexes a deposit's artifacts and auth tokens by it.\n */\n peginTxHash: Hex;\n /** The vault's registered vault provider, which addresses its VP proxy. */\n vaultProvider: Address;\n htlcVout: number;\n}\n\n/**\n * @throws When the vault's core version is not the delegated-claim graph\n * version; when the depositor-signed PegIn carries no output\n * {@link PEGIN_VAULT_OUTPUT_INDEX}; when the registration log, the\n * redemption log, the vault record and the stamped offchain params\n * disagree on a shared field;\n * when the PegIn does not derive the vault id asked for; when it does\n * not have exactly one input, or that input does not spend the\n * record's Pre-PegIn at its `htlcVout`;\n * when output {@link PEGIN_VAULT_OUTPUT_INDEX} is not\n * worth `basic.amount`; and whatever the readers throw (notably the\n * typed `VaultClaimableByNotFoundError`). The\n * disagreement checks are inconsistent-node guards only: on an honest\n * chain both logs are written from the same contract state as the\n * record (`PeginLogic.sol:379-380`, `RedeemLogic.sol:118-124`) and\n * cannot differ.\n * @experimental\n */\nexport async function readDelegatedClaimVaultContext(\n params: ReadDelegatedClaimVaultContextParams,\n): Promise<DelegatedClaimVaultRead> {\n const { vaultId, readers } = params;\n const vault = await readers.registryReader.getVaultData(vaultId);\n const { basic, protocol } = vault;\n\n // The stamped vault core version is the graph version (one axis). The\n // delegated-claim builders exist for version 3 only, so anything else\n // cannot be claimed by this path.\n if (protocol.vaultCoreVersion !== DELEGATED_CLAIM_TX_GRAPH_VERSION) {\n throw new Error(\n `Vault ${vaultId} is stamped vault core version ${protocol.vaultCoreVersion}; ` +\n `delegated claim requires ${DELEGATED_CLAIM_TX_GRAPH_VERSION}.`,\n );\n }\n const depositorBtcPubkey = assertOnChainBtcPubkey(\n basic.depositorBtcPubKey,\n `getBtcVaultBasicInfo(${vaultId}).depositorBtcPubKey`,\n );\n\n // Every depositor VaultClaimableBy is emitted after the vault is set\n // Redeemed (RedeemLogic.sol redeemForDepositor via _executeRedeemVault;\n // PeginLogic.sol claimExpired), so any other status cannot have the log\n // yet: report the same typed absence without a finalized→createdAt scan.\n if (basic.status !== OnChainBtcVaultStatus.REDEEMED) {\n throw new VaultClaimableByNotFoundError(\n vaultId,\n basic.depositorBtcPubKey,\n basic.createdAt,\n basic.createdAt,\n `the vault's status is ${basic.status}, not Redeemed (${OnChainBtcVaultStatus.REDEEMED}); no scan was made`,\n );\n }\n\n const [participantKeys, records, claimableEvent, offchainParams] =\n await Promise.all([\n readStampedParticipantKeys({ vaultId, vault, readers }),\n readers.registryReader.getRegistrationRecordsAtBlock(basic.createdAt),\n readers.registryReader\n .getVaultClaimableBy(vaultId, basic.depositorBtcPubKey, basic.createdAt)\n .catch((error: unknown) => {\n if (!isVaultClaimableByNotFoundError(error)) throw error;\n // The status gate above already proved the vault is Redeemed, so the\n // reader's generic \"redeem first\" wording would mislead here. Name\n // what is actually left.\n throw new VaultClaimableByNotFoundError(\n vaultId,\n basic.depositorBtcPubKey,\n error.fromBlock,\n error.toBlock,\n `the vault is Redeemed but no VaultClaimableBy authorizes this depositor key in the ` +\n `finalized range. Either the redeem has not finalized yet (the scan stops about two ` +\n `epochs behind the tip), or the vault was redeemed for a vault keeper alone ` +\n `(redeemForAVK emits no depositor log), or the node did not serve every block scanned.`,\n { cause: error },\n );\n }),\n readers.protocolParamsReader.getOffchainParamsByVersion(\n protocol.offchainParamsVersion,\n ),\n ]);\n // The derivation `getTimelockPeginByVersion` runs on the params it would\n // otherwise fetch a second time.\n const timelockPegin = deriveTimelockPegin(offchainParams.timelockAssert);\n const record = findRegistrationRecord(records, vaultId, basic.createdAt);\n assertRegisteredPayoutScriptBounds(record);\n\n const recordPrePeginTxHash = registrationPrePeginTxHash(record);\n if (\n recordPrePeginTxHash.toLowerCase() !== protocol.prePeginTxHash.toLowerCase()\n ) {\n throw new Error(\n `Registration log of vault ${vaultId} records prePeginTxHash ${recordPrePeginTxHash} ` +\n `but the vault record says ${protocol.prePeginTxHash}; refusing an inconsistent node.`,\n );\n }\n if (record.vaultCoreVersion !== protocol.vaultCoreVersion) {\n throw new Error(\n `Registration log of vault ${vaultId} records vault core version ${record.vaultCoreVersion} ` +\n `but the vault record says ${protocol.vaultCoreVersion}; refusing an inconsistent node.`,\n );\n }\n if (record.amount !== basic.amount) {\n throw new Error(\n `Registration log of vault ${vaultId} records amount ${record.amount} sats ` +\n `but the vault record says ${basic.amount}; refusing an inconsistent node.`,\n );\n }\n // `selectSiblingRegistrations` filters siblings on the log's depositor\n // (`deposit-terms/claimSiblings.ts:48`), so it must be the vault's.\n if (record.depositor.toLowerCase() !== basic.depositor.toLowerCase()) {\n throw new Error(\n `Registration log of vault ${vaultId} records depositor ${record.depositor} ` +\n `but the vault record says ${basic.depositor}; refusing an inconsistent node.`,\n );\n }\n if (\n record.vaultProvider.toLowerCase() !== basic.vaultProvider.toLowerCase()\n ) {\n throw new Error(\n `Registration log of vault ${vaultId} records vaultProvider ${record.vaultProvider} ` +\n `but the vault record says ${basic.vaultProvider}; refusing an inconsistent node.`,\n );\n }\n if (claimableEvent.vaultCoreVersion !== protocol.vaultCoreVersion) {\n throw new Error(\n `Redemption log of vault ${vaultId} records vault core version ${claimableEvent.vaultCoreVersion} ` +\n `but the vault record says ${protocol.vaultCoreVersion}; refusing an inconsistent node.`,\n );\n }\n if (claimableEvent.proverCircuitVersion !== record.proverCircuitVersion) {\n throw new Error(\n `Redemption log of vault ${vaultId} records proverCircuitVersion ` +\n `${claimableEvent.proverCircuitVersion} but the registration log says ` +\n `${record.proverCircuitVersion}; refusing an inconsistent node.`,\n );\n }\n // The registry stamps the circuit from the params version it just filed\n // (`PeginLogic.sol:379-380` @ c559f5c2), so the fetched struct is the anchor.\n if (record.proverCircuitVersion !== offchainParams.proverCircuitVersion) {\n throw new Error(\n `Registration log of vault ${vaultId} records proverCircuitVersion ${record.proverCircuitVersion} ` +\n `but the stamped offchain params say ${offchainParams.proverCircuitVersion}; refusing an inconsistent node.`,\n );\n }\n // Both logs repeat the PegIn txid, and the vault record is the reference.\n // Same txid derivation the registration submitted\n // (pegin-registration-client.ts).\n const peginTxHash = calculateBtcTxHash(protocol.depositorSignedPeginTx);\n for (const [source, logged] of [\n [\"Registration\", record.peginTxHash],\n [\"Redemption\", claimableEvent.peginTxHash],\n ] as const) {\n if (logged.toLowerCase() !== peginTxHash.toLowerCase()) {\n throw new Error(\n `${source} log of vault ${vaultId} records peginTxHash ${logged} but the vault's ` +\n `depositor-signed PegIn hashes to ${peginTxHash}; refusing an inconsistent node.`,\n );\n }\n }\n // The registry derives the id it filed the vault under from exactly these\n // two values (`PeginLogic.sol:336-337` @ c559f5c2), so re-deriving it ties\n // the record's own PegIn to the vault asked for.\n const derivedVaultId = normalizeVaultId(\n derivePeginVaultId(peginTxHash, basic.depositor),\n );\n if (derivedVaultId !== normalizeVaultId(vaultId)) {\n throw new Error(\n `Depositor-signed PegIn of vault ${vaultId} derives vault id ${derivedVaultId} with ` +\n `depositor ${basic.depositor}; refusing an inconsistent node.`,\n );\n }\n // Both logs stamp these versions, so both are held against the vault record.\n for (const axis of [\n \"offchainParamsVersion\",\n \"universalChallengersVersion\",\n \"appVaultKeepersVersion\",\n ] as const) {\n if (record[axis] !== protocol[axis]) {\n throw new Error(\n `Registration log of vault ${vaultId} records ${axis} ${record[axis]} ` +\n `but the vault record says ${protocol[axis]}; refusing an inconsistent node.`,\n );\n }\n if (claimableEvent[axis] !== protocol[axis]) {\n throw new Error(\n `Redemption log of vault ${vaultId} records ${axis} ${claimableEvent[axis]} ` +\n `but the vault record says ${protocol[axis]}; refusing an inconsistent node.`,\n );\n }\n }\n\n // The Payout's input 0 spends this output (btc-vault `payout.rs:103-112`\n // @ ac4954e7), so its value anchors the claim-time fee band. Read from the\n // signed transaction itself, then cross-checked below against the vault\n // record's amount — which the registration log was already compared with.\n const peginTx = Transaction.fromHex(\n stripHexPrefix(protocol.depositorSignedPeginTx),\n );\n // btc-vault gives the PegIn exactly one input, spending the Pre-PegIn's\n // HTLC output (`transactions/pegin.rs:521-529` @ ac4954e7), so the record's\n // Pre-PegIn and the transaction it serves cannot describe different spends.\n if (peginTx.ins.length !== 1) {\n throw new Error(\n `Depositor-signed PegIn of vault ${vaultId} has ${peginTx.ins.length} inputs; it spends ` +\n `Pre-PegIn ${protocol.prePeginTxHash} output ${protocol.htlcVout} with exactly one; ` +\n `refusing an inconsistent node.`,\n );\n }\n const spentTxid = displayTxid(peginTx.ins[0].hash);\n const spentVout = peginTx.ins[0].index;\n if (\n spentTxid !== stripHexPrefix(protocol.prePeginTxHash).toLowerCase() ||\n spentVout !== protocol.htlcVout\n ) {\n throw new Error(\n `Depositor-signed PegIn of vault ${vaultId} spends ${spentTxid}:${spentVout} but the vault ` +\n `record says Pre-PegIn ${protocol.prePeginTxHash} output ${protocol.htlcVout}; ` +\n `refusing an inconsistent node.`,\n );\n }\n\n const peginVaultOutput = peginTx.outs[PEGIN_VAULT_OUTPUT_INDEX];\n if (peginVaultOutput === undefined) {\n throw new Error(\n `Depositor-signed PegIn of vault ${vaultId} has no output ` +\n `${PEGIN_VAULT_OUTPUT_INDEX}; the Payout cannot spend the Vault UTXO.`,\n );\n }\n // The registry records the vault's amount as that same output's value\n // (`PeginLogic.sol:266-267` @ c559f5c2), so the two cannot differ.\n if (BigInt(peginVaultOutput.value) !== basic.amount) {\n throw new Error(\n `Depositor-signed PegIn of vault ${vaultId} pays ${peginVaultOutput.value} sats to output ` +\n `${PEGIN_VAULT_OUTPUT_INDEX} but the vault record says ${basic.amount}; refusing an inconsistent node.`,\n );\n }\n\n return {\n context: {\n vaultId,\n depositorEthAddress: basic.depositor,\n depositorBtcPubkey,\n registeredPayoutScriptPubKey: stripHexPrefix(\n record.depositorPayoutScriptPubKey,\n ),\n vaultProviderBtcPubkey: participantKeys.vaultProvider.operationBtcPubkey,\n vaultKeeperBtcPubkeys: [\n ...participantKeys.vaultKeeperOperationKeysSorted,\n ],\n universalChallengerBtcPubkeys: [\n ...participantKeys.universalChallengerOperationKeysSorted,\n ],\n txGraphVersion: DELEGATED_CLAIM_TX_GRAPH_VERSION,\n proverCircuitVersion: offchainParams.proverCircuitVersion,\n vaultCoreVersion: protocol.vaultCoreVersion,\n claimableEventBlockNumber: claimableEvent.blockNumber,\n peginVaultOutputValueSats: peginVaultOutput.value,\n protocolFeeRate: offchainParams.feeRate,\n councilSize: offchainParams.securityCouncilKeys.length,\n timelockPegin,\n timelockAssert: Number(offchainParams.timelockAssert),\n },\n vault,\n registrationRecord: record,\n registrationRecords: records,\n participantKeys,\n offchainParams,\n depositorBtcPubKeyBytes32: basic.depositorBtcPubKey,\n depositorWotsPkHash: protocol.depositorWotsPkHash,\n prePeginTxHash: protocol.prePeginTxHash,\n peginTxHash,\n vaultProvider: basic.vaultProvider,\n htlcVout: protocol.htlcVout,\n };\n}\n","/**\n * Ethereum confirmation-depth gate for a peg-in registration.\n *\n * Between `submitPeginRequestBatch` landing on Ethereum and the Pre-PegIn\n * BTC transaction being broadcast there is a reorg window. If the block\n * carrying the registration is orphaned *after* the BTC broadcast, the vault\n * record is gone from the chain while the depositor's BTC is already locked\n * in the HTLC — the deposit is stuck, recoverable only via the HTLC refund\n * leaf after `T_refund` (~3 days). This module closes that window by proving\n * the registration is buried before any BTC is committed.\n *\n * Depth is measured from `BTCVaultBasicInfo.createdAt`, which is the Ethereum\n * **block number** the registration was mined at (see\n * {@link isActivationDeadlinePassedOnChain}, which mirrors the contract's own\n * `block.number > createdAt + pegInActivationTimeout` check). Reading it needs\n * no transaction hash, so the same primitive serves the inline deposit flow\n * and the cross-device resume flow, and every poll re-reads live contract\n * state — an orphaned registration reads back as `depositor === zeroAddress`.\n *\n * Deliberately NOT built on viem's `waitForTransactionReceipt({confirmations})`.\n * That helper fetches the receipt once and thereafter only does block-number\n * arithmetic against the cached copy; it never re-checks that the receipt's\n * block is still canonical, so it resolves happily for a transaction that has\n * been reorged out. It buys a delay, not a finality guarantee.\n *\n * @module services/deposit\n */\n\nimport { type Hex, zeroAddress } from \"viem\";\n\nimport type { VaultBasicInfo, VaultRegistryReader } from \"../../clients/eth/types\";\n\n/**\n * Ethereum block confirmations required before the Pre-PegIn BTC transaction\n * may be broadcast.\n *\n * 8 exceeds the deepest reorg ever observed on Ethereum (7, pre-merge, caused\n * by a client bug), and costs ~1.6 min at 12s slots. Deliberately not the\n * `safe` block tag: a full epoch (~12.8 min) was rejected as too slow for the\n * benefit. This is a liveness guard against an orphaned registration, not a\n * theft mitigation — every Pre-PegIn HTLC spend path requires the depositor's\n * own BTC key regardless.\n */\nexport const PEGIN_ETH_CONFIRMATIONS = 8;\n\n/** Poll cadence — half an Ethereum slot, so a new block is never missed by long. */\nconst REGISTRATION_DEPTH_POLL_INTERVAL_MS = 6_000;\n\n/**\n * Overall budget for reaching depth. Nominally ~96s (8 × 12s); the rest\n * absorbs missed slots and RPC lag. Generous on purpose: timing out discards\n * a valid registration that only needed a few more seconds, and the failure is\n * recoverable through the resume flow, so over-waiting is cheaper than\n * under-waiting.\n */\nconst REGISTRATION_DEPTH_TIMEOUT_MS = 10 * 60_000;\n\n/**\n * Consecutive absent reads tolerated before concluding the registration is\n * genuinely not on-chain. At the 6s cadence this is ~60s — five Ethereum\n * blocks — which is what absorbs a load-balanced pool member still serving\n * pre-block state moments after the receipt. Such a node answers HTTP 200\n * with a validly encoded zero struct, so no transport-level retry can see it;\n * only re-reading later can. Expressed in polls rather than milliseconds so\n * the tolerance scales with the cadence.\n */\nconst REGISTRATION_ABSENT_GRACE_POLLS = 10;\n\n/**\n * The vault is not registered on-chain, and stayed that way past the grace\n * window — long enough that a lagging backend has been ruled out. Retrying\n * will not help.\n */\nexport class PeginRegistrationMissingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PeginRegistrationMissingError\";\n }\n}\n\n/** The registration did not reach the required depth within the budget. */\nexport class PeginRegistrationNotFinalError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PeginRegistrationNotFinalError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field, as the other deposit-service\n// errors in this directory do.\nexport function isPeginRegistrationMissingError(\n err: unknown,\n): err is PeginRegistrationMissingError {\n return (\n err instanceof PeginRegistrationMissingError ||\n (err instanceof Error && err.name === \"PeginRegistrationMissingError\")\n );\n}\n\nexport function isPeginRegistrationNotFinalError(\n err: unknown,\n): err is PeginRegistrationNotFinalError {\n return (\n err instanceof PeginRegistrationNotFinalError ||\n (err instanceof Error && err.name === \"PeginRegistrationNotFinalError\")\n );\n}\n\nexport interface RegistrationDepthParams {\n /** Current chain tip block number. */\n currentBlock: bigint;\n /** Block number the registration was mined at (`VaultBasicInfo.createdAt`). */\n createdAtBlock: bigint;\n}\n\n/**\n * Confirmations accrued by a registration: the mining block counts as the\n * first confirmation, so `tip === createdAt` is 1.\n *\n * Clamped at 0. A reorg between the tip read and the vault read can surface a\n * `createdAt` above the tip already in hand; a negative depth must never\n * propagate into a comparison.\n */\nexport function computeRegistrationConfirmations(\n params: RegistrationDepthParams,\n): number {\n const { currentBlock, createdAtBlock } = params;\n const depth = currentBlock - createdAtBlock + 1n;\n return depth < 0n ? 0 : Number(depth);\n}\n\nexport interface RegistrationDepthProgress {\n /** Shallowest depth across every vault being waited on. */\n confirmations: number;\n required: number;\n}\n\nexport interface WaitForPeginRegistrationDepthParams {\n vaultRegistryReader: VaultRegistryReader;\n /**\n * Chain-tip reader. A thunk rather than a `PublicClient` so this module has\n * no viem-client dependency and stays testable with two plain fakes — the\n * same shape `verifyRegisteredVaultVersions` uses for its reader.\n */\n getBlockNumber: () => Promise<bigint>;\n /** Vaults registered by the same transaction; the shallowest one gates. */\n vaultIds: readonly Hex[];\n required?: number;\n pollIntervalMs?: number;\n timeoutMs?: number;\n signal?: AbortSignal;\n onProgress?: (progress: RegistrationDepthProgress) => void;\n}\n\nexport interface PeginRegistrationDepthResult {\n confirmations: number;\n /**\n * The final observation for the shallowest vault. Callers that gated on\n * `status` before the wait should re-assert it against this — the wait can\n * span minutes, and a vault can leave PENDING in that time.\n */\n basicInfo: VaultBasicInfo;\n}\n\n/**\n * Poll until every vault's registration is at least `required` blocks deep.\n *\n * A read that comes back empty is treated as \"not visible yet\", not as\n * \"absent\": both callers reach this having already proven the registration\n * exists, so an empty read means a lagging RPC backend or a reorg, and both\n * resolve on their own.\n *\n * @throws {PeginRegistrationMissingError} if no vault has ever been observed and the grace window is spent.\n * @throws {PeginRegistrationNotFinalError} on timeout.\n * @throws if aborted.\n */\nexport async function waitForPeginRegistrationDepth(\n params: WaitForPeginRegistrationDepthParams,\n): Promise<PeginRegistrationDepthResult> {\n const {\n vaultRegistryReader,\n getBlockNumber,\n vaultIds,\n required = PEGIN_ETH_CONFIRMATIONS,\n pollIntervalMs = REGISTRATION_DEPTH_POLL_INTERVAL_MS,\n timeoutMs = REGISTRATION_DEPTH_TIMEOUT_MS,\n signal,\n onProgress,\n } = params;\n\n if (vaultIds.length === 0) {\n throw new Error(\n \"waitForPeginRegistrationDepth requires at least one vault ID\",\n );\n }\n\n const startTime = Date.now();\n // Once any poll has seen the registration, a later disappearance is a reorg,\n // and re-inclusion is the expected outcome — that case polls to the full\n // budget. Before the first sighting, absence is far more likely to be a\n // lagging backend than a missing registration, so it gets a grace window.\n let hasObservedRegistration = false;\n let absentPolls = 0;\n let lastError: unknown;\n\n while (true) {\n if (signal?.aborted) {\n throw new Error(\n `Aborted while waiting for peg-in registration depth (${vaultIds.length} vault(s))`,\n );\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n // Deliberately avoids the word \"broadcast\". Callers classify this by\n // type, but any surface that stringifies it would otherwise land in the\n // message-matching \"broadcast failed\" bucket and tell the user the exact\n // opposite of what happened.\n throw new PeginRegistrationNotFinalError(\n `Peg-in registration did not reach ${required} Ethereum confirmations within ${timeoutMs}ms. ` +\n `The registration transaction was submitted but is not yet final, so the Pre-PegIn ` +\n `Bitcoin transaction was never sent and no funds are at risk. ` +\n `Resume this deposit from the dashboard once the network settles.` +\n (lastError instanceof Error ? ` Last read error: ${lastError.message}` : \"\"),\n );\n }\n\n try {\n // Tip FIRST, vault state SECOND. Both read `latest`, but they are two\n // round-trips: reading the tip first means it can only be at or behind\n // the state the vault read observes, so the computed depth is an\n // under-estimate. Reversing the order could over-count by a block and\n // release the broadcast at 7 confirmations.\n //\n // That ordering only guarantees the under-count when both land on the\n // same backend. Against a load-balanced pool the tip can come from an\n // ahead node and the vault from a behind one — the same root cause as\n // the empty-read handling below, and why this is a best-effort skew\n // guard rather than a proof.\n const currentBlock = await getBlockNumber();\n const infos = await Promise.all(\n vaultIds.map((vaultId) => vaultRegistryReader.getVaultBasicInfo(vaultId)),\n );\n\n // A zero record is what an orphaned (or never-included) registration\n // reads back as. `createdAt === 0n` is checked alongside the zero\n // depositor because a partially-decoded record must never be treated as\n // block 0 — that would compute as infinitely deep and pass the gate.\n const missingIndex = infos.findIndex(\n (info) => info.depositor === zeroAddress || info.createdAt === 0n,\n );\n\n if (missingIndex !== -1) {\n absentPolls += 1;\n\n // Never seen it, and the grace window is spent: a lagging backend has\n // been ruled out, so the registration really is not there. Only ever\n // reachable for a caller that supplied a vault ID which was never\n // registered — both in-repo callers arrive here having already proven\n // the vault visible (a receipt inline, a throwing `getVaultFromChain`\n // on resume).\n if (\n !hasObservedRegistration &&\n absentPolls > REGISTRATION_ABSENT_GRACE_POLLS\n ) {\n throw new PeginRegistrationMissingError(\n `Vault ${vaultIds[missingIndex]} is still not visible on-chain after ` +\n `${absentPolls} reads — the registration does not exist.`,\n );\n }\n\n // Otherwise keep polling. Before the first sighting this is almost\n // always an RPC backend that has not caught up to the registration\n // block; after one, it is the reorg this gate exists for, where\n // re-inclusion within a block or two is the expected outcome. Both\n // resolve themselves, and both would be made worse by aborting a\n // deposit that is about to be fine.\n console.warn(\n `Peg-in registration for vault ${vaultIds[missingIndex]} is not visible in chain state ` +\n `at block ${currentBlock} — a lagging RPC backend or an Ethereum reorg. Still polling.`,\n );\n onProgress?.({ confirmations: 0, required });\n } else {\n hasObservedRegistration = true;\n absentPolls = 0;\n lastError = undefined;\n\n // The shallowest vault gates the batch: they share one registration\n // transaction, so in practice the depths are equal, but taking the\n // minimum is the fail-safe reading if they ever diverge.\n let shallowest = infos[0];\n let shallowestDepth = computeRegistrationConfirmations({\n currentBlock,\n createdAtBlock: infos[0].createdAt,\n });\n for (const info of infos.slice(1)) {\n const depth = computeRegistrationConfirmations({\n currentBlock,\n createdAtBlock: info.createdAt,\n });\n if (depth < shallowestDepth) {\n shallowestDepth = depth;\n shallowest = info;\n }\n }\n\n onProgress?.({ confirmations: shallowestDepth, required });\n\n if (shallowestDepth >= required) {\n return { confirmations: shallowestDepth, basicInfo: shallowest };\n }\n }\n } catch (error) {\n if (isPeginRegistrationMissingError(error)) {\n throw error;\n }\n // Transient RPC failure. Retry on the next tick; the outer timeout owns\n // the overall budget, so one blip must not consume it.\n console.warn(\n `Registration-depth read failed (retrying in ${pollIntervalMs}ms): ` +\n (error instanceof Error ? error.message : String(error)),\n );\n lastError = error;\n }\n\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(\n new Error(\n `Aborted while waiting for peg-in registration depth (${vaultIds.length} vault(s))`,\n ),\n );\n };\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, pollIntervalMs);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","/**\n * Deterministic fingerprint over the canonical transaction set of a\n * depositor-as-claimer graph.\n *\n * `btc-vault/docs/specifications/pegin.md` §5.9 makes this the depositor's binding\n * between the two moments where it gives up leverage:\n *\n * - **Presign** — before signing the VP's PSBTs, fingerprint the transaction\n * set the VP just supplied and persist it.\n * - **Activation** — before revealing the HTLC secret, fingerprint the graph\n * the VP returns and require an exact match.\n *\n * Without this, a VP can serve one graph to sign and a different one to\n * activate against; every check in `assertBundleBoundToVault` compares the\n * bundle only against public values it also controls.\n *\n * The two moments carry the same transactions in different encodings — presign\n * hands back raw consensus `tx_hex`, activation a serde-serialized\n * `bitcoin::Transaction`. Both sides decode strictly and re-serialize through\n * `bitcoinjs-lib`, so each hashed part is exactly one canonical transaction.\n * That matters because the layout below has no length prefixes: it matches\n * `canonical_tx_set_fingerprint` in `btc-vault`\n * (`crates/depositor-cli/src/recovery_graph.rs`), which relies on the same\n * strict decode. Hashing undecoded bytes would let a VP shift bytes between\n * parts and reach the same digest with a different graph.\n *\n * Note this commits to the transaction set only. `gc_wots_keys`, the verifying\n * key, and the typed connector metadata are deliberately outside it — the spec\n * binds those at activation by other means.\n */\n\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { bytesToHex, concatBytes, hexToBytes } from \"@noble/hashes/utils.js\";\nimport { Transaction } from \"bitcoinjs-lib\";\nimport { Buffer } from \"buffer\";\n\nimport { PEGIN_DEPOSITOR_CLAIM_VOUT } from \"../../primitives/psbt/depositorClaim\";\nimport {\n canonicalizeBtcPubkey,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\n\n/** Lowercase hex, whole bytes, no prefix: the only form serde emits. */\nconst SERDE_HEX_RE = /^(?:[0-9a-f]{2})*$/;\n/** An x-only pubkey or a SHA-256 digest in serde hex. */\nconst BYTES32_SERDE_HEX_RE = /^[0-9a-f]{64}$/;\n/** `bitcoin::OutPoint` in serde: display-order txid, `:`, decimal vout. */\nconst OUTPOINT_RE = /^([0-9a-f]{64}):(0|[1-9][0-9]*)$/;\n\nconst INT32_MIN = -0x8000_0000;\nconst INT32_MAX = 0x7fff_ffff;\nconst UINT32_MAX = 0xffff_ffff;\n\n/** A depositor Claim is always version 2 (`check_depositor_claim_shape`). */\nconst DEPOSITOR_CLAIM_TX_VERSION = 2;\n/** A depositor Claim pays the assert connector and a CPFP anchor, nothing else. */\nconst DEPOSITOR_CLAIM_OUTPUT_COUNT = 2;\n/** Every Assert spends the Claim's connector output. */\nconst CLAIM_ASSERT_VOUT = 0;\n\n/** One challenger's contribution to the fingerprint. */\nexport interface ChallengerFingerprintPart {\n /** Challenger x-only pubkey, lowercase hex, no prefix. */\n pubkey: string;\n /** The challenger's NoPayout transaction, consensus-serialized. */\n nopayoutTx: Uint8Array;\n /** Per-GC output label hashes, lowercase hex, in the order the graph lists them. */\n outputLabelHashes: string[];\n}\n\n/** The four transactions every depositor-as-claimer graph carries. */\nexport interface CanonicalTxSet {\n peginTx: Uint8Array;\n claimTx: Uint8Array;\n assertTx: Uint8Array;\n payoutTx: Uint8Array;\n challengers: ChallengerFingerprintPart[];\n}\n\nexport class GraphFingerprintError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"GraphFingerprintError\";\n }\n}\n\nfunction field(node: Record<string, unknown>, name: string, path: string) {\n const value = node[name];\n if (value === undefined) {\n throw new GraphFingerprintError(`Graph tx ${path} is missing \"${name}\"`);\n }\n return value;\n}\n\nfunction asArray(value: unknown, path: string): unknown[] {\n if (!Array.isArray(value)) {\n throw new GraphFingerprintError(`Graph tx ${path} is not an array`);\n }\n return value;\n}\n\nfunction asRecord(value: unknown, path: string): Record<string, unknown> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new GraphFingerprintError(`Graph tx ${path} is not an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction asInt(value: unknown, path: string, min: number, max: number): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < min ||\n value > max\n ) {\n throw new GraphFingerprintError(\n `Graph tx ${path} is not an integer in [${min}, ${max}]`,\n );\n }\n return value;\n}\n\nfunction asSerdeHex(value: unknown, path: string): Buffer {\n if (typeof value !== \"string\" || !SERDE_HEX_RE.test(value)) {\n throw new GraphFingerprintError(\n `Graph tx ${path} is not lowercase hex without a prefix`,\n );\n }\n return Buffer.from(value, \"hex\");\n}\n\nfunction asSerdeBytes32(value: unknown, path: string): string {\n if (typeof value !== \"string\" || !BYTES32_SERDE_HEX_RE.test(value)) {\n throw new GraphFingerprintError(\n `Graph ${path} is not 32 bytes of lowercase hex`,\n );\n }\n return value;\n}\n\n/**\n * Re-encode one serde-serialized `bitcoin::Transaction` from a `tx_graph_json`\n * node into consensus bytes.\n *\n * Every field is required and range-checked: a value that serde would reject\n * or read differently must not reach the digest, or the depositor could match\n * a graph that the recovery tooling cannot load. `previous_output` arrives in\n * display byte order, so the txid is reversed back to wire order.\n */\nexport function serializeGraphTx(txNode: unknown, path: string): Uint8Array {\n const node = asRecord(txNode, path);\n const tx = new Transaction();\n tx.version = asInt(\n field(node, \"version\", path),\n `${path}.version`,\n INT32_MIN,\n INT32_MAX,\n );\n tx.locktime = asInt(\n field(node, \"lock_time\", path),\n `${path}.lock_time`,\n 0,\n UINT32_MAX,\n );\n\n asArray(field(node, \"input\", path), `${path}.input`).forEach((input, i) => {\n const at = `${path}.input[${i}]`;\n const inputNode = asRecord(input, at);\n const outpoint = field(inputNode, \"previous_output\", at);\n const match = typeof outpoint === \"string\" && OUTPOINT_RE.exec(outpoint);\n if (!match) {\n throw new GraphFingerprintError(\n `Graph tx ${at}.previous_output is not \"<txid>:<vout>\"`,\n );\n }\n const vout = asInt(Number(match[2]), `${at}.previous_output vout`, 0, UINT32_MAX);\n tx.addInput(\n Buffer.from(match[1], \"hex\").reverse(),\n vout,\n asInt(field(inputNode, \"sequence\", at), `${at}.sequence`, 0, UINT32_MAX),\n asSerdeHex(field(inputNode, \"script_sig\", at), `${at}.script_sig`),\n );\n tx.ins[i].witness = asArray(\n field(inputNode, \"witness\", at),\n `${at}.witness`,\n ).map((item, j) => asSerdeHex(item, `${at}.witness[${j}]`));\n });\n\n asArray(field(node, \"output\", path), `${path}.output`).forEach((output, i) => {\n const at = `${path}.output[${i}]`;\n const outputNode = asRecord(output, at);\n tx.addOutput(\n asSerdeHex(field(outputNode, \"script_pubkey\", at), `${at}.script_pubkey`),\n asInt(field(outputNode, \"value\", at), `${at}.value`, 0, Number.MAX_SAFE_INTEGER),\n );\n });\n\n return new Uint8Array(tx.toBuffer());\n}\n\n/**\n * Decode one presign `tx_hex` strictly and return its canonical bytes.\n *\n * Rejects trailing bytes and any encoding that does not round-trip, so the\n * part hashed below is exactly one transaction.\n */\nfunction decodePresignTx(txHex: string, path: string): Transaction {\n const hex = stripHexPrefix(txHex).toLowerCase();\n let tx: Transaction;\n try {\n tx = Transaction.fromHex(hex);\n } catch (err) {\n throw new GraphFingerprintError(\n `Presign ${path} is not one transaction: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (tx.toHex() !== hex) {\n throw new GraphFingerprintError(\n `Presign ${path} is not a canonical transaction encoding`,\n );\n }\n return tx;\n}\n\ninterface PresignClaimAssertTransactions {\n peginTx: Transaction;\n claimTx: Transaction;\n assertTx: Transaction;\n}\n\nexport interface AssertPresignClaimAssertLinkageParams {\n /** Authoritative PegIn transaction built and signed by the depositor. */\n peginTxHex: string;\n /** VP-supplied Claim transaction. */\n claimTxHex: string;\n /** VP-supplied Assert transaction. */\n assertTxHex: string;\n /** Response path included in actionable validation errors. */\n path: string;\n}\n\nfunction readInputOutpoint(\n tx: Transaction,\n inputIndex: number,\n): { txid: string; vout: number } | undefined {\n const input = tx.ins[inputIndex];\n if (!input) {\n return undefined;\n }\n return {\n txid: Buffer.from(input.hash).reverse().toString(\"hex\"),\n vout: input.index,\n };\n}\n\nfunction assertClaimHasAssertOutput(claimTx: Transaction, path: string): void {\n if (!claimTx.outs[CLAIM_ASSERT_VOUT]) {\n throw new GraphFingerprintError(\n `Presign ${path}.claim_tx must have output ${CLAIM_ASSERT_VOUT}`,\n );\n }\n}\n\nfunction assertClaimSpendsPegin(\n claimTx: Transaction,\n peginTxid: string,\n path: string,\n): void {\n if (claimTx.ins.length !== 1) {\n throw new GraphFingerprintError(\n `Presign ${path}.claim_tx must have exactly one input, got ${claimTx.ins.length}`,\n );\n }\n\n assertClaimHasAssertOutput(claimTx, path);\n\n const spent = readInputOutpoint(claimTx, 0);\n if (spent?.txid !== peginTxid || spent.vout !== PEGIN_DEPOSITOR_CLAIM_VOUT) {\n const actual = spent ? `${spent.txid}:${spent.vout}` : \"no input\";\n throw new GraphFingerprintError(\n `Presign ${path}.claim_tx must spend ${peginTxid}:${PEGIN_DEPOSITOR_CLAIM_VOUT}, got ${actual}`,\n );\n }\n}\n\nfunction assertAssertSpendsClaim(\n assertTx: Transaction,\n claimTxid: string,\n path: string,\n): void {\n const spent = readInputOutpoint(assertTx, 0);\n if (spent?.txid !== claimTxid || spent.vout !== CLAIM_ASSERT_VOUT) {\n const actual = spent ? `${spent.txid}:${spent.vout}` : \"no input\";\n throw new GraphFingerprintError(\n `Presign ${path}.assert_tx input 0 must spend ${claimTxid}:${CLAIM_ASSERT_VOUT}, got ${actual}`,\n );\n }\n}\n\nfunction decodePresignClaimAssertTransactions(\n args: AssertPresignClaimAssertLinkageParams,\n): PresignClaimAssertTransactions {\n const peginTx = decodePresignTx(args.peginTxHex, `${args.path}.pegin_tx`);\n const claimTx = decodePresignTx(args.claimTxHex, `${args.path}.claim_tx`);\n const assertTx = decodePresignTx(args.assertTxHex, `${args.path}.assert_tx`);\n\n return { peginTx, claimTx, assertTx };\n}\n\nexport interface AssertPresignAssertSpendsClaimParams {\n /** VP-supplied Claim transaction. */\n claimTxHex: string;\n /** VP-supplied Assert transaction. */\n assertTxHex: string;\n /** Response path included in actionable validation errors. */\n path: string;\n}\n\n/**\n * Bind a VP/VK claimer's Assert to its own Claim: Assert input 0 must spend\n * Claim output 0.\n *\n * A claimer funds its Claim from its own wallet (`ClaimTx::from_transaction`\n * in `btc-vault` tolerates that freedom), so the Claim's inputs are not\n * checked here. Only the depositor-as-claimer Claim spends PegIn output 1;\n * {@link assertPresignClaimAssertLinkage} checks that one.\n */\nexport function assertPresignAssertSpendsClaim(\n args: AssertPresignAssertSpendsClaimParams,\n): void {\n const claimTx = decodePresignTx(args.claimTxHex, `${args.path}.claim_tx`);\n const assertTx = decodePresignTx(args.assertTxHex, `${args.path}.assert_tx`);\n assertClaimHasAssertOutput(claimTx, args.path);\n assertAssertSpendsClaim(assertTx, claimTx.getId(), args.path);\n}\n\n/**\n * Bind the depositor-as-claimer Claim/Assert pair to the depositor's\n * authoritative PegIn.\n *\n * The Claim must have exactly one input spending PegIn output 1, and Assert\n * input 0 must spend Claim output 0. Call this before any wallet signing\n * prompt: otherwise the VP can obtain signatures for an unfundable graph.\n * Do not use it for VP/VK claimer entries: their Claims are wallet-funded\n * (see {@link assertPresignAssertSpendsClaim}).\n */\nexport function assertPresignClaimAssertLinkage(\n args: AssertPresignClaimAssertLinkageParams,\n): void {\n const { peginTx, claimTx, assertTx } =\n decodePresignClaimAssertTransactions(args);\n assertClaimSpendsPegin(claimTx, peginTx.getId(), args.path);\n assertAssertSpendsClaim(assertTx, claimTx.getId(), args.path);\n}\n\n/**\n * Mirror `check_depositor_claim_shape` in `btc-vault`\n * (`crates/vault/src/transactions/claim.rs`): one input spending the\n * depositor-claim output of the depositor's own PegIn, an empty witness,\n * version 2, and exactly two outputs.\n *\n * The reference also rebuilds the Claim outputs from canonical params\n * (`ClaimTx::from_transaction`). That needs a WASM binding the SDK does not\n * have yet, so output contents are not checked here.\n */\nfunction assertDepositorClaimShape(claim: Transaction, peginTxid: string): void {\n const [input, ...extra] = claim.ins;\n if (!input || extra.length > 0) {\n throw new GraphFingerprintError(\n `Presign claim_tx must have exactly one input, got ${claim.ins.length}`,\n );\n }\n const spentTxid = Buffer.from(input.hash).reverse().toString(\"hex\");\n if (spentTxid !== peginTxid || input.index !== PEGIN_DEPOSITOR_CLAIM_VOUT) {\n throw new GraphFingerprintError(\n `Presign claim_tx must spend ${peginTxid}:${PEGIN_DEPOSITOR_CLAIM_VOUT}, ` +\n `got ${spentTxid}:${input.index}`,\n );\n }\n if (claim.version !== DEPOSITOR_CLAIM_TX_VERSION) {\n throw new GraphFingerprintError(\n `Presign claim_tx version must be ${DEPOSITOR_CLAIM_TX_VERSION}, got ${claim.version}`,\n );\n }\n if (input.witness.length > 0) {\n throw new GraphFingerprintError(\n `Presign claim_tx input must have an empty witness, got ${input.witness.length} item(s)`,\n );\n }\n if (claim.outs.length !== DEPOSITOR_CLAIM_OUTPUT_COUNT) {\n throw new GraphFingerprintError(\n `Presign claim_tx must have exactly ${DEPOSITOR_CLAIM_OUTPUT_COUNT} outputs, got ${claim.outs.length}`,\n );\n }\n}\n\n/**\n * Hash the canonical transaction set.\n *\n * Challengers are sorted by pubkey so the digest does not depend on map or\n * response ordering, which neither side controls consistently. Two entries\n * for one key are rejected: with the order between them undefined, the digest\n * would follow whichever order the VP chose.\n */\nexport function canonicalTxSetFingerprint(set: CanonicalTxSet): string {\n const sorted = [...set.challengers].sort((a, b) =>\n a.pubkey < b.pubkey ? -1 : a.pubkey > b.pubkey ? 1 : 0,\n );\n const parts = [set.peginTx, set.claimTx, set.assertTx, set.payoutTx];\n sorted.forEach((challenger, i) => {\n const pubkey = asSerdeBytes32(challenger.pubkey, \"challenger pubkey\");\n if (i > 0 && sorted[i - 1].pubkey === pubkey) {\n throw new GraphFingerprintError(`Graph lists challenger ${pubkey} twice`);\n }\n parts.push(hexToBytes(pubkey), challenger.nopayoutTx);\n for (const hash of challenger.outputLabelHashes) {\n parts.push(hexToBytes(asSerdeBytes32(hash, \"output_label_hashes entry\")));\n }\n });\n\n return bytesToHex(sha256(concatBytes(...parts)));\n}\n\n/**\n * Validate the transaction set a VP supplied at presign and fingerprint it.\n *\n * `peginTxHex` comes from the depositor's own signing context, not the\n * response — the presign payload carries no PegIn transaction, and taking it\n * from the VP would let the VP choose both sides of the comparison. It must\n * hash to `peginTxid`, and the Claim must have the depositor-claim shape,\n * as in `validate_presign_response` in `btc-vault`.\n */\nexport function fingerprintPresignTxSet(args: {\n peginTxid: string;\n peginTxHex: string;\n claimTxHex: string;\n assertTxHex: string;\n payoutTxHex: string;\n challengers: {\n challenger_pubkey: string;\n nopayout_tx: { tx_hex: string };\n output_label_hashes: string[];\n }[];\n}): string {\n const peginTxid = stripHexPrefix(args.peginTxid).toLowerCase();\n const { peginTx, claimTx, assertTx } =\n decodePresignClaimAssertTransactions({\n peginTxHex: args.peginTxHex,\n claimTxHex: args.claimTxHex,\n assertTxHex: args.assertTxHex,\n path: \"depositor_graph\",\n });\n if (peginTx.getId() !== peginTxid) {\n throw new GraphFingerprintError(\n `Presign pegin_tx hashes to ${peginTx.getId()}, expected ${peginTxid}`,\n );\n }\n assertDepositorClaimShape(claimTx, peginTxid);\n assertAssertSpendsClaim(assertTx, claimTx.getId(), \"depositor_graph\");\n\n const bytes = (tx: Transaction) => new Uint8Array(tx.toBuffer());\n return canonicalTxSetFingerprint({\n peginTx: bytes(peginTx),\n claimTx: bytes(claimTx),\n assertTx: bytes(assertTx),\n payoutTx: bytes(decodePresignTx(args.payoutTxHex, \"payout_tx\")),\n challengers: args.challengers.map((c) => ({\n pubkey: canonicalizeBtcPubkey(c.challenger_pubkey),\n nopayoutTx: bytes(decodePresignTx(c.nopayout_tx.tx_hex, \"nopayout_tx\")),\n outputLabelHashes: c.output_label_hashes.map((hash) =>\n stripHexPrefix(hash).toLowerCase(),\n ),\n })),\n });\n}\n\n/**\n * Fingerprint the graph a VP returned at activation, parsed from\n * `tx_graph_json`.\n */\nexport function fingerprintReturnedGraph(\n graph: Record<string, unknown>,\n): string {\n const tx = (key: string) =>\n serializeGraphTx(asRecord(field(graph, key, \"graph\"), key).tx, key);\n\n const subgraphs = asRecord(\n field(graph, \"challenger_subgraphs\", \"graph\"),\n \"challenger_subgraphs\",\n );\n\n return canonicalTxSetFingerprint({\n peginTx: tx(\"pegin_tx\"),\n claimTx: tx(\"claim_tx\"),\n assertTx: tx(\"assert_tx\"),\n payoutTx: tx(\"payout_tx\"),\n challengers: Object.entries(subgraphs).map(([pubkey, value]) => {\n const at = `challenger_subgraphs[${pubkey}]`;\n const sub = asRecord(value, at);\n return {\n pubkey,\n nopayoutTx: serializeGraphTx(\n asRecord(field(sub, \"nopayout_tx\", at), `${at}.nopayout_tx`).tx,\n `${at}.nopayout_tx`,\n ),\n outputLabelHashes: asArray(\n field(sub, \"output_label_hashes\", at),\n `${at}.output_label_hashes`,\n ).map((h, i) =>\n asSerdeBytes32(h, `${at}.output_label_hashes[${i}]`),\n ),\n };\n }),\n });\n}\n\n/**\n * The challenger roster a returned graph declares in `challenger_pubkeys`,\n * as `local ∪ universal`. A key in both sets is rejected: the roles are\n * disjoint, and a repeat would hide a missing key from a size comparison.\n */\nfunction declaredChallengers(graph: Record<string, unknown>): Set<string> {\n const pubkeys = asRecord(\n field(graph, \"challenger_pubkeys\", \"graph\"),\n \"challenger_pubkeys\",\n );\n const declared = new Set<string>();\n for (const role of [\"local\", \"universal\"] as const) {\n asArray(\n field(pubkeys, role, \"challenger_pubkeys\"),\n `challenger_pubkeys.${role}`,\n ).forEach((key, i) => {\n const pubkey = asSerdeBytes32(key, `challenger_pubkeys.${role}[${i}]`);\n if (declared.has(pubkey)) {\n throw new GraphFingerprintError(\n `Graph declares challenger ${pubkey} more than once`,\n );\n }\n declared.add(pubkey);\n });\n }\n return declared;\n}\n\n/**\n * Check (a) of `pegin.md` §5.9: throw unless the graph a VP returned at\n * activation is the one the depositor fingerprinted at presign.\n *\n * Call this before revealing the HTLC secret, with the value that\n * `runDepositorPresignFlow` passed to `recordGraphFingerprint`. The\n * fingerprint covers the challengers in `challenger_subgraphs`, but the graph\n * also declares its roster in `challenger_pubkeys`. This also requires the\n * two to name the same keys, or a graph could match the fingerprint while its\n * declared roster adds or drops a challenger.\n *\n * @param graph Parsed `tx_graph_json` from the artifact bundle.\n * @param expectedFingerprint The fingerprint recorded at presign.\n * @throws GraphFingerprintError when the graph is malformed, does not\n * reproduce the fingerprint, or declares a different roster.\n */\nexport function assertReturnedGraphMatchesFingerprint(\n graph: Record<string, unknown>,\n expectedFingerprint: string,\n): void {\n const actual = fingerprintReturnedGraph(graph);\n if (actual !== expectedFingerprint) {\n throw new GraphFingerprintError(\n `Graph fingerprint ${actual} does not match the presign fingerprint ${expectedFingerprint}`,\n );\n }\n\n const fingerprinted = Object.keys(\n asRecord(graph.challenger_subgraphs, \"challenger_subgraphs\"),\n );\n const declared = declaredChallengers(graph);\n if (\n fingerprinted.length !== declared.size ||\n fingerprinted.some((key) => !declared.has(key))\n ) {\n throw new GraphFingerprintError(\n `Graph challenger_subgraphs keys ${fingerprinted.join(\", \")} do not match ` +\n `challenger_pubkeys ${[...declared].join(\", \")}`,\n );\n }\n}\n","/**\n * Depositor Graph Signing Service\n *\n * Signs the depositor's own graph transactions (Payout, NoPayout per challenger)\n * for the depositor-as-claimer flow.\n *\n * Both PSBTs are constructed locally from authoritative on-chain connector\n * parameters and the VP-advertised transaction hexes (which are themselves\n * cross-checked against on-chain or protocol-defined sinks). Building PSBTs\n * locally is essential: every field that enters the Taproot sighash\n * (witnessUtxo, tapLeafScript, controlBlock, tapInternalKey) must come from\n * trusted sources, otherwise a malicious VP could substitute metadata that\n * makes the depositor's signature valid for a different spend.\n *\n * Transaction counts: 1 Payout + N NoPayout = 1 + N total PSBTs.\n *\n * @see btc-vault docs/pegin.md - \"Automatic Graph Creation & Presigning\"\n * @see btc-vault crates/vault/src/transactions/nopayout.rs - NoPayout structure\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\n\nimport type {\n BitcoinWallet,\n SignPsbtOptions,\n} from \"../../../../shared/wallets/interfaces\";\nimport type {\n DepositorAsClaimerPresignatures,\n DepositorGraphTransactions,\n DepositorPreSigsPerChallenger,\n PresignDataPerChallenger,\n} from \"../../clients/vault-provider/types\";\nimport { signPsbtsWithFallback } from \"../../managers/pegin/signPsbtsWithFallback\";\nimport { deriveLocalChallengers } from \"../../primitives/challengers\";\nimport {\n assertPsbtUnsignedTxMatches,\n type AssertPsbtUnsignedTxMatchesParams,\n} from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport { assertChallengeAssertIsCanonical } from \"../../primitives/psbt/challengeAssert\";\nimport {\n ASSERT_MARKER_FIRST_VAULT_CORE_VERSION,\n ASSERT_NON_CHALLENGER_OUTPUT_COUNT,\n ASSERT_PAYOUT_OUTPUT_INDEX,\n CHALLENGE_ASSERT_CONNECTORS_PER_CHALLENGER,\n CHALLENGE_ASSERT_OUTPUT_CONNECTOR_INDEX,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n} from \"../../primitives/psbt/constants\";\nimport {\n assertCanonicalNoPayoutShape,\n assertNoPayoutOutputMatchesChallenger,\n buildNoPayoutPsbt,\n} from \"../../primitives/psbt/noPayout\";\nimport {\n buildPayoutPsbt,\n extractPayoutSignature,\n} from \"../../primitives/psbt/payout\";\nimport { assertScriptPathSchnorrSignature } from \"../../primitives/psbt/verifyScriptPathSchnorrSignature\";\nimport {\n getSortedXOnlyPubkeys,\n stripHexPrefix,\n uint8ArrayToHex,\n validateWalletPubkey,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\nimport { getChallengeAssertOutputScriptPubKey } from \"../../wasm\";\nimport { assertPresignClaimAssertLinkage } from \"./graphFingerprint\";\n\n/**\n * commissionBps placeholder for the depositor-as-claimer path — `buildPayoutPsbt`\n * only consults it under the VP-claimer role, so any in-range value is inert.\n */\nconst DEPOSITOR_PATH_UNUSED_COMMISSION_BPS = 1;\n\n/** Tracks which indices in the flat PSBT array belong to which challenger */\ninterface ChallengerEntry {\n challengerPubkey: string;\n noPayoutIdx: number;\n}\n\n/** Result of the collect phase - flat PSBT array with index mapping */\ninterface CollectedDepositorGraphPsbts {\n psbtHexes: string[];\n signOptions: SignPsbtOptions[];\n challengerEntries: ChallengerEntry[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Reject VP-supplied `challenger_presign_data` whose pubkey set does not\n * exactly equal `localChallengers ∪ universalChallengers`, and return that\n * set in the protocol's challenger order.\n *\n * The daemon's `challenger_presign_data` contains one entry per challenger\n * in `local ∪ universal`; for the depositor-as-claimer flow this is\n * `VKs ∪ UCs`. Its array order is not meaningful (the daemon iterates a map).\n * The order that matters is btc-vault `Challengers::all_sorted()`\n * (`crates/vault/src/lib.rs:596-601` @ b534ff9e): one sort over local and\n * universal together by x-only key bytes, which is lowercase-hex order. A\n * challenger's position in it fixes which Assert outputs its ChallengeAsserts\n * spend.\n *\n * Threat model: a malicious or buggy VP could omit, duplicate, or inject\n * unrelated entries. Missing entries → depositor activates with incomplete\n * recovery material (omitted challenger later becomes unenforceable).\n * Duplicates or extras → wallet signs PSBTs for challengers the protocol\n * doesn't recognize, handing the VP signatures it shouldn't have.\n *\n * @returns Every challenger key (lowercase x-only hex) in `all_sorted()` order\n */\nfunction assertChallengerSetMatchesExpected(\n challengerPresignData: PresignDataPerChallenger[],\n localChallengers: string[],\n universalChallengerBtcPubkeys: string[],\n): string[] {\n const universal = universalChallengerBtcPubkeys.map((k) =>\n stripHexPrefix(k).toLowerCase(),\n );\n // Protocol guarantee: local and universal sets are disjoint. Reject\n // overlap so the depositor doesn't sign for an ambiguous challenger role.\n const overlap = localChallengers.filter((k) => universal.includes(k));\n if (overlap.length > 0) {\n throw new Error(\n `Cannot validate challenger set: vault keepers and universal challengers overlap (${overlap.join(\", \")})`,\n );\n }\n const expected = [...localChallengers, ...universal];\n\n const suppliedList = challengerPresignData.map((c) =>\n stripHexPrefix(c.challenger_pubkey).toLowerCase(),\n );\n const suppliedSet = new Set(suppliedList);\n if (suppliedSet.size !== suppliedList.length) {\n throw new Error(\n \"Depositor graph contains duplicate challenger entries in challenger_presign_data\",\n );\n }\n const expectedSet = new Set(expected);\n const missing = expected.filter((c) => !suppliedSet.has(c));\n const extra = suppliedList.filter((c) => !expectedSet.has(c));\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `Depositor graph challenger set does not match expected (local ∪ universal)` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n );\n }\n return getSortedXOnlyPubkeys(expected);\n}\n\n/**\n * Require the Assert to carry exactly one ConnectorX and one ConnectorY per\n * challenger, around output 0 and the CPFP anchor (plus the RFC-008 marker\n * from vault core version 2). The ChallengeAssert vouts are derived from the\n * challenger count, so an Assert built for a different count would shift\n * which connector each ChallengeAssert spends.\n */\nfunction assertAssertChallengerOutputCount(\n assertTx: Transaction,\n challengerCount: number,\n vaultCoreVersion: number,\n): void {\n const markerOutputs =\n vaultCoreVersion >= ASSERT_MARKER_FIRST_VAULT_CORE_VERSION ? 1 : 0;\n const expected =\n ASSERT_NON_CHALLENGER_OUTPUT_COUNT +\n CHALLENGE_ASSERT_CONNECTORS_PER_CHALLENGER * challengerCount +\n markerOutputs;\n if (assertTx.outs.length !== expected) {\n throw new Error(\n `Assert must have ${expected} outputs for ${challengerCount} challengers ` +\n `(vault core version ${vaultCoreVersion}), got ${assertTx.outs.length}`,\n );\n }\n}\n\n// ============================================================================\n// NoPayout checks\n// ============================================================================\n\n/** One challenger's NoPayout, checked against its canonical parents. */\nexport interface CheckedNoPayout {\n /** The challenger's entry in the VP response */\n challenger: PresignDataPerChallenger;\n /** The challenger's x-only key (hex, no 0x prefix) */\n challengerPubkey: string;\n /** Assert:0, ChallengeAssertX:0 and ChallengeAssertY:0, in NoPayout input order */\n prevouts: Array<{ script_pubkey: string; value: number }>;\n}\n\n/** The NoPayout side of a depositor graph, checked and ready to build. */\nexport interface CheckedDepositorGraphNoPayouts {\n /** The depositor-as-claimer's local challengers, derived from the context */\n localChallengers: string[];\n /** One entry per challenger, in the VP's order */\n noPayouts: CheckedNoPayout[];\n}\n\n/**\n * Check everything on the NoPayout side of the depositor graph that needs no\n * wallet: the challenger set equals `local ∪ universal`, the Assert carries\n * one ConnectorX and one ConnectorY per challenger, and for every challenger\n * the ChallengeAssertX/Y are the canonical transactions for this Assert, the\n * NoPayout spends exactly them with the canonical sequences, and it pays the\n * challenger's BIP-86 key.\n *\n * `runDepositorPresignFlow` calls this before the deposit-terms approval and\n * every payout signing prompt. `signDepositorGraph` calls it again because it\n * is also a public entry point.\n *\n * @param depositorGraph - The depositor graph from the VP response\n * @param ctx - Authoritative inputs the graph is checked against\n * @returns The checked per-challenger data the NoPayout PSBTs are built from\n * @throws If any check fails\n */\nexport async function assertDepositorGraphNoPayoutsCanonical(\n depositorGraph: DepositorGraphTransactions,\n ctx: DepositorGraphSigningContext,\n): Promise<CheckedDepositorGraphNoPayouts> {\n const localChallengers = deriveLocalChallengers({\n claimerBtcPubkey: ctx.depositorBtcPubkey,\n depositorBtcPubkey: ctx.depositorBtcPubkey,\n vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n });\n const sortedChallengers = assertChallengerSetMatchesExpected(\n depositorGraph.challenger_presign_data,\n localChallengers,\n ctx.universalChallengerBtcPubkeys,\n );\n\n const claimerPubkey = stripHexPrefix(ctx.depositorBtcPubkey);\n const assertTx = Transaction.fromHex(\n stripHexPrefix(depositorGraph.assert_tx.tx_hex),\n );\n assertAssertChallengerOutputCount(\n assertTx,\n sortedChallengers.length,\n ctx.vaultCoreVersion,\n );\n\n const noPayouts: CheckedNoPayout[] = [];\n for (const challenger of depositorGraph.challenger_presign_data) {\n const challengerPubkey = stripHexPrefix(challenger.challenger_pubkey);\n // The set check above guarantees membership.\n const challengerIndex = sortedChallengers.indexOf(\n challengerPubkey.toLowerCase(),\n );\n const prevouts = await assertNoPayoutParentsCanonical({\n challenger,\n challengerPubkey,\n challengerIndex,\n challengerCount: sortedChallengers.length,\n claimerPubkey,\n assertTx,\n ctx,\n });\n noPayouts.push({ challenger, challengerPubkey, prevouts });\n }\n\n return { localChallengers, noPayouts };\n}\n\ninterface AssertNoPayoutParentsCanonicalParams {\n challenger: PresignDataPerChallenger;\n challengerPubkey: string;\n /** Position of the challenger in `Challengers::all_sorted()` order */\n challengerIndex: number;\n /** Number of local plus universal challengers */\n challengerCount: number;\n claimerPubkey: string;\n assertTx: Transaction;\n ctx: DepositorGraphSigningContext;\n}\n\n/**\n * Check one challenger's NoPayout and its two ChallengeAssert parents against\n * the authoritative Assert, and return the NoPayout's prevouts taken from\n * those canonical parents.\n *\n * NoPayout transaction layout (per\n * btc-vault crates/vault/src/transactions/nopayout.rs):\n * - 3 inputs (fixed order):\n * - Input 0: Assert tx output 0 (depositor signs - NoPayout path)\n * - Input 1: ChallengeAssertX tx output 0 (with timelock)\n * - Input 2: ChallengeAssertY tx output 0 (with timelock)\n * - 1 output: BIP-86 P2TR to the challenger\n */\nasync function assertNoPayoutParentsCanonical(\n params: AssertNoPayoutParentsCanonicalParams,\n): Promise<CheckedNoPayout[\"prevouts\"]> {\n const {\n challenger,\n challengerPubkey,\n challengerIndex,\n challengerCount,\n claimerPubkey,\n assertTx,\n ctx,\n } = params;\n\n // Pin the output sink before doing any sighash-relevant work.\n assertNoPayoutOutputMatchesChallenger(\n challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n ctx.network,\n );\n\n // Parse the NoPayout tx and the two ChallengeAssert parents.\n const noPayoutTx = Transaction.fromHex(\n stripHexPrefix(challenger.nopayout_tx.tx_hex),\n );\n const challengeAssertXTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_x_tx.tx_hex),\n );\n const challengeAssertYTx = Transaction.fromHex(\n stripHexPrefix(challenger.challenge_assert_y_tx.tx_hex),\n );\n\n // The depositor's signature commits to both parents' txids, and that\n // commitment is the only constraint on them: the protocol takes no claimer\n // signature on ChallengeAssert. So each parent must be exactly the\n // transaction btc-vault builds from this Assert, paying the timelocked\n // connector — otherwise a colluding challenger could spend Assert:0 through\n // NoPayout without a dispute. The label hashes are the VP's, and are bound\n // to this vault only by the presign fingerprint checked at activation. The\n // response validator accepts either hex case and the fingerprint lowercases\n // them, so they are lowercased here too: the same bytes, in the only case\n // the connector accepts.\n const outputConnectorScriptPubKey =\n await getChallengeAssertOutputScriptPubKey({\n txGraphVersion: ctx.vaultCoreVersion,\n claimer: claimerPubkey,\n challenger: challengerPubkey,\n timelockChallengeAssert: ctx.timelockChallengeAssert,\n outputLabelHashes: challenger.output_label_hashes.map((hash) =>\n hash.toLowerCase(),\n ),\n network: ctx.network,\n });\n for (const [half, challengeAssertTx] of [\n [\"X\", challengeAssertXTx],\n [\"Y\", challengeAssertYTx],\n ] as const) {\n assertChallengeAssertIsCanonical({\n challengeAssertTx,\n assertTx,\n half,\n challengerIndex,\n challengerCount,\n challengerPubkey,\n outputConnectorScriptPubKey,\n });\n }\n\n const parentOutputs = [\n assertTx.outs[ASSERT_PAYOUT_OUTPUT_INDEX],\n challengeAssertXTx.outs[CHALLENGE_ASSERT_OUTPUT_CONNECTOR_INDEX],\n challengeAssertYTx.outs[CHALLENGE_ASSERT_OUTPUT_CONNECTOR_INDEX],\n ] as const;\n assertCanonicalNoPayoutShape({\n noPayoutTx,\n assertTxid: assertTx.getId(),\n challengeAssertXTxid: challengeAssertXTx.getId(),\n challengeAssertYTxid: challengeAssertYTx.getId(),\n timelockChallengeAssert: ctx.timelockChallengeAssert,\n prevoutValues: [\n parentOutputs[0].value,\n parentOutputs[1].value,\n parentOutputs[2].value,\n ],\n });\n\n return parentOutputs.map((out) => ({\n script_pubkey: uint8ArrayToHex(new Uint8Array(out.script)),\n value: out.value,\n }));\n}\n\n// ============================================================================\n// Collect phase\n// ============================================================================\n\n/**\n * Build the depositor's payout PSBT and per-challenger NoPayout PSBTs locally\n * from authoritative connector params.\n *\n * Layout of returned arrays: [Payout, NoPayout_0, NoPayout_1, ...]\n */\nasync function collectDepositorGraphPsbts(\n depositorGraph: DepositorGraphTransactions,\n walletPublicKey: string,\n ctx: DepositorGraphSigningContext,\n): Promise<CollectedDepositorGraphPsbts> {\n const psbtHexes: string[] = [];\n const signOptions: SignPsbtOptions[] = [];\n const challengerEntries: ChallengerEntry[] = [];\n\n // 1. Fail-fast on a malformed VP response BEFORE doing any PSBT-build\n // work that would be wasted if the challenger set or a NoPayout's\n // parents are wrong.\n const { localChallengers, noPayouts } =\n await assertDepositorGraphNoPayoutsCanonical(depositorGraph, ctx);\n\n // 2. Build the payout PSBT locally — every sighash-relevant field is\n // derived from trusted on-chain connector params, not from the VP.\n // buildPayoutPsbt also runs the per-role output validation.\n const builtPayout = await buildPayoutPsbt({\n vaultCoreVersion: ctx.vaultCoreVersion,\n vkClaimerPayoutScriptPubKeys: ctx.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: ctx.vpCommissionScriptPubKey,\n payoutTxHex: depositorGraph.payout_tx.tx_hex,\n peginTxHex: ctx.peginTxHex,\n assertTxHex: depositorGraph.assert_tx.tx_hex,\n timelockAssert: ctx.timelockAssert,\n depositorBtcPubkey: ctx.depositorBtcPubkey,\n vaultProviderBtcPubkey: ctx.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: ctx.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: ctx.universalChallengerBtcPubkeys,\n timelockPegin: ctx.timelockPegin,\n network: ctx.network,\n claimerBtcPubkey: ctx.depositorBtcPubkey,\n registeredPayoutScriptPubKey: ctx.registeredPayoutScriptPubKey,\n commissionBps: DEPOSITOR_PATH_UNUSED_COMMISSION_BPS,\n protocolFeeRate: ctx.protocolFeeRate,\n councilMembers: ctx.councilMembers,\n councilQuorum: ctx.councilQuorum,\n });\n psbtHexes.push(builtPayout.psbtHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n // 3. Per-challenger: build the NoPayout PSBT locally too, over the\n // canonical parents checked in step 1.\n const claimerPubkey = stripHexPrefix(ctx.depositorBtcPubkey);\n for (const { challenger, challengerPubkey, prevouts } of noPayouts) {\n const noPayoutIdx = psbtHexes.length;\n const noPayoutHex = await buildNoPayoutPsbt({\n noPayoutTxHex: challenger.nopayout_tx.tx_hex,\n challengerPubkey,\n prevouts,\n connectorParams: {\n txGraphVersion: ctx.vaultCoreVersion,\n claimer: claimerPubkey,\n localChallengers,\n universalChallengers: ctx.universalChallengerBtcPubkeys,\n timelockAssert: ctx.timelockAssert,\n councilMembers: ctx.councilMembers,\n councilQuorum: ctx.councilQuorum,\n },\n });\n psbtHexes.push(noPayoutHex);\n signOptions.push(\n createTaprootScriptPathSignOptions(\n walletPublicKey,\n DEPOSITOR_SIGNED_INPUT_COUNT,\n ),\n );\n\n challengerEntries.push({\n challengerPubkey,\n noPayoutIdx,\n });\n }\n\n return { psbtHexes, signOptions, challengerEntries };\n}\n\n// ============================================================================\n// Extract phase\n// ============================================================================\n\n/** A pair of a locally-built PSBT and the wallet-returned PSBT for it. */\ntype PsbtPair = AssertPsbtUnsignedTxMatchesParams;\n\n/**\n * Extract all signatures from signed PSBTs and assemble into presignatures.\n * Each pair is asserted to encode the same unsigned tx before its signature\n * is extracted — defends against a wallet that returns a signature for a\n * substituted transaction.\n */\nfunction extractDepositorGraphSignatures(\n psbtPairs: PsbtPair[],\n challengerEntries: ChallengerEntry[],\n depositorPubkey: string,\n): DepositorAsClaimerPresignatures {\n // Positional invariant: psbtPairs[0] is the payout PSBT; per-challenger\n // nopayouts live at indices recorded in `challengerEntries[].noPayoutIdx`.\n // Set up by `collectDepositorGraphPsbts` (payout pushed first, then each\n // nopayout). A future refactor that reorders the array would silently\n // extract the wrong signature for the wrong slot — Critical Path #3.\n // Payout and every NoPayout PSBT are signed on input 0 (depositor script-path).\n const DEPOSITOR_SIGNED_INPUT_INDEX = 0;\n\n assertPsbtUnsignedTxMatches(psbtPairs[0]);\n const payoutSignature = extractPayoutSignature(\n psbtPairs[0].returnedPsbtHex,\n depositorPubkey,\n );\n // Critical Path #7: verify the wallet's signature against a sighash recomputed\n // from the PSBT we built (psbtPairs[0].requestedPsbtHex), not the returned one.\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtPairs[0].requestedPsbtHex,\n signatureHex: payoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: DEPOSITOR_SIGNED_INPUT_INDEX,\n });\n\n const perChallenger: Record<string, DepositorPreSigsPerChallenger> = {};\n for (const entry of challengerEntries) {\n assertPsbtUnsignedTxMatches(psbtPairs[entry.noPayoutIdx]);\n const nopayoutSignature = extractPayoutSignature(\n psbtPairs[entry.noPayoutIdx].returnedPsbtHex,\n depositorPubkey,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtPairs[entry.noPayoutIdx].requestedPsbtHex,\n signatureHex: nopayoutSignature,\n signerXOnlyPubkeyHex: depositorPubkey,\n inputIndex: DEPOSITOR_SIGNED_INPUT_INDEX,\n });\n perChallenger[entry.challengerPubkey] = {\n nopayout_signature: nopayoutSignature,\n };\n }\n\n return {\n payout_signatures: {\n payout_signature: payoutSignature,\n },\n per_challenger: perChallenger,\n };\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Authoritative inputs required to construct the depositor's Payout AND every\n * per-challenger NoPayout PSBT locally. Every field here must come from\n * trusted on-chain sources, not from the vault provider response. They feed\n * directly into the Taproot sighash.\n */\nexport interface DepositorGraphSigningContext {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion` from `BTCVaultRegistry`.\n * Selects which graph's connector scripts every PSBT is rebuilt with.\n */\n vaultCoreVersion: number;\n /** Raw pegin BTC transaction hex (provides the depositor's signed prevout) */\n peginTxHex: string;\n /** Depositor's BTC public key (x-only, 64-char hex, no 0x prefix) */\n depositorBtcPubkey: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Pegin CSV timelock from the locked offchain params version (blocks) */\n timelockPegin: number;\n /**\n * Tx-graph fee rate (sat/vB) from the locked offchain params version —\n * bounds the depositor-claimer payout's implicit fee (payout fee band).\n */\n protocolFeeRate: bigint;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Sourced from the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n */\n timelockAssert: number;\n /**\n * ChallengeAssert CSV timelock from the locked offchain params version\n * (blocks). Sourced from the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockChallengeAssert`.\n * Each NoPayout's ChallengeAssert inputs must carry it as their sequence,\n * and it is part of the connector their ChallengeAssert parents pay to.\n */\n timelockChallengeAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix). Sourced from\n * the on-chain ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).securityCouncilKeys`.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold. Sourced from the on-chain ProtocolParams\n * contract via `ViemProtocolParamsReader.getOffchainParamsByVersion(...).councilQuorum`.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /**\n * On-chain registered depositor payout scriptPubKey (hex, with or without\n * 0x prefix). Used to assert the VP-advertised payout transaction pays to\n * the depositor's registered address before the wallet produces a signature.\n */\n registeredPayoutScriptPubKey: string;\n /**\n * RFC-006 operator payout destinations. Forwarded to `buildPayoutPsbt` for\n * shape completeness only: this graph is signed under the\n * `depositor-as-claimer` role, whose payout has two outputs and reads\n * neither the keeper map nor the VP commission destination.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /** See {@link vkClaimerPayoutScriptPubKeys} — unused for this role. */\n vpCommissionScriptPubKey: string;\n}\n\nexport interface SignDepositorGraphParams {\n /** The depositor graph from VP response */\n depositorGraph: DepositorGraphTransactions;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** Authoritative inputs used to rebuild every PSBT locally */\n signingContext: DepositorGraphSigningContext;\n}\n\n/**\n * Sign all depositor graph transactions and assemble into presignatures.\n *\n * Flow:\n * 1. Build payout + per-challenger nopayout PSBTs locally\n * 2. Batch sign via wallet.signPsbts() if available, else sequential signPsbt()\n * 3. Extract Schnorr signatures from each signed PSBT\n * 4. Assemble into DepositorAsClaimerPresignatures\n */\nexport async function signDepositorGraph(\n params: SignDepositorGraphParams,\n): Promise<DepositorAsClaimerPresignatures> {\n const { depositorGraph, btcWallet, signingContext } = params;\n\n // Validate the complete funding chain before even reading the wallet key.\n // The orchestrating flow performs the same check while fingerprinting, but\n // this service is also a public entry point and must be safe on its own.\n assertPresignClaimAssertLinkage({\n peginTxHex: signingContext.peginTxHex,\n claimTxHex: depositorGraph.claim_tx.tx_hex,\n assertTxHex: depositorGraph.assert_tx.tx_hex,\n path: \"depositor_graph\",\n });\n\n const walletPublicKey = await btcWallet.getPublicKeyHex();\n // Fail fast if the connected wallet doesn't match the on-chain registered\n // depositor key — otherwise extractPayoutSignature later fails after\n // multiple wallet popups with an opaque \"no signature found\" error.\n const { depositorPubkey } = validateWalletPubkey(\n walletPublicKey,\n stripHexPrefix(signingContext.depositorBtcPubkey),\n );\n\n // 1. Build all PSBTs locally\n const { psbtHexes, signOptions, challengerEntries } =\n await collectDepositorGraphPsbts(\n depositorGraph,\n walletPublicKey,\n signingContext,\n );\n\n // 2. Sign all PSBTs (batch when supported, sequential fallback for mobile)\n // signPsbtsWithFallback guarantees one signed PSBT per input (or throws), so\n // no separate arity check is needed here.\n const signedPsbtHexes = await signPsbtsWithFallback(\n btcWallet,\n psbtHexes,\n signOptions,\n );\n\n // 3. Pair requested with signed and extract signatures\n const psbtPairs: PsbtPair[] = psbtHexes.map((requestedPsbtHex, i) => ({\n requestedPsbtHex,\n returnedPsbtHex: signedPsbtHexes[i],\n }));\n return extractDepositorGraphSignatures(\n psbtPairs,\n challengerEntries,\n depositorPubkey,\n );\n}\n","/**\n * Poll `getPeginStatusByVaultId` until the VP reaches one of the target statuses.\n *\n * Pure polling utility with no framework dependencies (no localStorage, no React).\n * Handles \"PegIn not found\" as transient (VP hasn't ingested yet).\n */\n\nimport { vaultIdKey } from \"../../clients/vault-provider/batchAttribution\";\nimport { JsonRpcError } from \"../../clients/vault-provider/json-rpc-client\";\nimport {\n DaemonStatus,\n RpcErrorCode,\n VP_TERMINAL_FAILURE_STATUSES,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader } from \"./interfaces\";\n\n/** Default polling interval (10 seconds). */\nconst DEFAULT_POLL_INTERVAL_MS = 10_000;\n\n/** Characters of a vault id shown in error text: `0x` plus 8 hex chars. */\nconst VAULT_ID_LOG_PREFIX_LEN = 10;\n\nexport interface WaitForPeginStatusParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** On-chain vault id (hex, `0x` prefix optional) */\n vaultId: string;\n /**\n * BTC pegin transaction ID (unprefixed hex, 64 chars) of the same vault.\n * The VP builds the response `vault_id` from the request, so that field\n * alone cannot show which row answered. `pegin_txid` is a DB lookup on the\n * server, so a mismatch shows a status for a different peg-in. It cannot\n * tell apart vaults that share one peg-in txid. It is also the identifier\n * the presign and WOTS writes are addressed by.\n */\n peginTxid: string;\n /** Set of acceptable statuses — polling stops when the VP reports one of these */\n targetStatuses: ReadonlySet<DaemonStatus>;\n /** Maximum time to wait in milliseconds */\n timeoutMs: number;\n /** Polling interval in milliseconds (default: 10s) */\n pollIntervalMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Poll `getPeginStatusByVaultId` until the VP reaches one of the target statuses.\n *\n * @returns The DaemonStatus that matched one of the targets, OR\n * `DaemonStatus.ACTIVATED` if the VP raced past the requested target into the\n * happy-path terminal (success-via-overshoot — the goal is satisfied).\n * @throws Error on timeout, abort, non-transient RPC error, or any terminal status (`Expired` + `VP_TERMINAL_FAILURE_STATUSES`) not in `targetStatuses`.\n */\nexport async function waitForPeginStatus(\n params: WaitForPeginStatusParams,\n): Promise<DaemonStatus> {\n const {\n statusReader,\n vaultId,\n peginTxid,\n targetStatuses,\n timeoutMs,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n signal,\n } = params;\n\n const startTime = Date.now();\n\n while (true) {\n if (signal?.aborted) {\n throw new Error(\n `Polling aborted for vault ${vaultId.slice(0, VAULT_ID_LOG_PREFIX_LEN)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n throw new Error(\n `Polling timeout after ${timeoutMs}ms for vault ${vaultId.slice(0, VAULT_ID_LOG_PREFIX_LEN)}… (target: ${[...targetStatuses].join(\", \")})`,\n );\n }\n\n try {\n const response = await statusReader.getPeginStatusByVaultId(\n { vault_id: vaultId },\n signal,\n );\n\n // Reject responses echoing a different vault id.\n if (vaultIdKey(response.vault_id) !== vaultIdKey(vaultId)) {\n throw new Error(\n `getPeginStatusByVaultId returned status for vault ${response.vault_id.slice(0, VAULT_ID_LOG_PREFIX_LEN)}…, requested ${vaultId.slice(0, VAULT_ID_LOG_PREFIX_LEN)}…`,\n );\n }\n\n // Reject a response whose server-side pegin txid names another peg-in.\n // The vault id check above catches a mangled response; this catches a\n // status for a different peg-in.\n if (response.pegin_txid.toLowerCase() !== peginTxid.toLowerCase()) {\n throw new Error(\n `getPeginStatusByVaultId returned status for pegin ${response.pegin_txid.slice(0, 8)}…, requested ${peginTxid.slice(0, 8)}…`,\n );\n }\n\n const status = response.status as DaemonStatus;\n if (targetStatuses.has(status)) {\n return status;\n }\n // Happy-path overshoot: VP raced past the requested target to ACTIVATED.\n // The caller's goal (reach some earlier state) is satisfied — return\n // success rather than time out waiting for a state the VP already left.\n if (status === DaemonStatus.ACTIVATED) {\n return status;\n }\n // EXPIRED is included — depositor has no path forward once VP marks the pegin Expired.\n if (\n status === DaemonStatus.EXPIRED ||\n VP_TERMINAL_FAILURE_STATUSES.has(status)\n ) {\n throw new Error(\n `Vault ${vaultId.slice(0, VAULT_ID_LOG_PREFIX_LEN)}… reached terminal status \"${status}\" while waiting for ${[...targetStatuses].join(\", \")}`,\n );\n }\n } catch (error) {\n // \"PegIn not found\" is transient — VP hasn't ingested the pegin yet.\n const isNotFound =\n error instanceof JsonRpcError &&\n error.code === RpcErrorCode.PEGIN_NOT_FOUND;\n if (!isNotFound) {\n throw error;\n }\n }\n\n // Wait before next poll, with abort support\n await new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(\n new Error(\n `Polling aborted for vault ${vaultId.slice(0, VAULT_ID_LOG_PREFIX_LEN)}… (target: ${[...targetStatuses].join(\", \")})`,\n ),\n );\n };\n const timeoutId = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, pollIntervalMs);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n}\n","/**\n * Payout Signing Orchestration\n *\n * Polls VP for `PendingDepositorSignatures`, fetches presign transactions,\n * signs payouts via PayoutManager, signs the depositor graph, and submits\n * all signatures back to the VP.\n *\n * This is the main deposit protocol step between registration and activation.\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\n\nimport type { BitcoinWallet } from \"../../../../shared/wallets/interfaces\";\nimport type {\n ClaimerSignatures,\n ClaimerTransactions,\n} from \"../../clients/vault-provider/types\";\nimport { DaemonStatus } from \"../../clients/vault-provider/types\";\nimport {\n supportsDepositApproval,\n type DepositTerms,\n} from \"../../deposit-terms\";\nimport { PayoutManager } from \"../../managers/PayoutManager\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport {\n assertPresignAssertSpendsClaim,\n fingerprintPresignTxSet,\n} from \"./graphFingerprint\";\nimport type { PeginStatusReader, PresignClient } from \"./interfaces\";\nimport {\n assertDepositorGraphNoPayoutsCanonical,\n signDepositorGraph,\n type DepositorGraphSigningContext,\n} from \"./signDepositorGraph\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context required for signing payout transactions.\n * Caller builds this from on-chain data (contract queries, GraphQL, config).\n */\nexport interface PayoutSigningContext {\n /**\n * Vault core (tx-graph) version the vault was registered under — the\n * vault's stamped on-chain `vaultCoreVersion` from `BTCVaultRegistry`.\n * Selects which graph's connector scripts every payout/nopayout PSBT is\n * rebuilt with.\n */\n vaultCoreVersion: number;\n /** Raw pegin BTC transaction hex (for PSBT construction) */\n peginTxHex: string;\n /** Vault provider's BTC public key (x-only hex, no prefix) */\n vaultProviderBtcPubkey: string;\n /** Sorted vault keeper BTC public keys (x-only hex, no prefix) */\n vaultKeeperBtcPubkeys: string[];\n /** Sorted universal challenger BTC public keys (x-only hex, no prefix) */\n universalChallengerBtcPubkeys: string[];\n /** Depositor's BTC public key (x-only hex, no prefix) */\n depositorBtcPubkey: string;\n /** Pegin timelock from the locked offchain params version */\n timelockPegin: number;\n /**\n * Assert CSV timelock from the locked offchain params version (blocks).\n * Source: ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockAssert`.\n * Required for the depositor-graph NoPayout local rebuild.\n */\n timelockAssert: number;\n /**\n * ChallengeAssert CSV timelock from the locked offchain params version\n * (blocks). Source: ProtocolParams contract via\n * `ViemProtocolParamsReader.getOffchainParamsByVersion(...).timelockChallengeAssert`.\n * Required to bind each NoPayout's ChallengeAssert parents before signing.\n */\n timelockChallengeAssert: number;\n /**\n * Security council member x-only public keys (hex, no prefix).\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).securityCouncilKeys`.\n * Required to rebuild every Assert:0 leaf (payout and NoPayout) locally.\n */\n councilMembers: string[];\n /**\n * M-of-N council quorum threshold.\n * Source: ProtocolParams contract via\n * `getOffchainParamsByVersion(...).councilQuorum`.\n * Required to rebuild every Assert:0 leaf (payout and NoPayout) locally.\n */\n councilQuorum: number;\n /** BTC network (Mainnet, Testnet, etc.) */\n network: Network;\n /** On-chain registered depositor payout scriptPubKey (hex) */\n registeredPayoutScriptPubKey: string;\n /** VP commission (bps) from `BTCVaultRegistry`; caps the VP-claimer payout commission output. */\n commissionBps: number;\n /**\n * Tx-graph fee rate (sat/vB) from the locked offchain params version —\n * `getOffchainParamsByVersion(...).feeRate`, the rate the VP built the\n * graph with. Bounds every payout's implicit fee (payout fee band).\n */\n protocolFeeRate: bigint;\n\n /**\n * RFC-006 resolved keeper payout destinations at the vault's frozen\n * `appKeeperKeyEpoch`, keyed by lowercased x-only operation pubkey.\n */\n vkClaimerPayoutScriptPubKeys: Readonly<Record<string, string>>;\n /**\n * RFC-006 resolved VP commission destination at the vault's frozen\n * `vpKeyEpoch`.\n */\n vpCommissionScriptPubKey: string;\n}\n\nexport interface RunDepositorPresignFlowParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the presign transaction flow interface */\n presignClient: PresignClient;\n /** Bitcoin wallet for signing */\n btcWallet: BitcoinWallet;\n /** On-chain vault id (hex, `0x` prefix optional) — addresses status polling */\n vaultId: string;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) — used by the presign RPCs */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Signing context built from on-chain data */\n signingContext: PayoutSigningContext;\n /**\n * Required for approval-capable wallets. Fresh flows pass\n * PreparePeginResult.depositTerms; resume flows rebuild them from\n * on-chain state (the vault app's rebuildDepositTerms).\n */\n depositTerms?: DepositTerms;\n /** Maximum polling timeout in milliseconds (default: 20 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Optional progress callback (completed claimers, total claimers) */\n onProgress?: (completed: number, total: number) => void;\n /**\n * Persist the fingerprint of the transaction set about to be signed.\n *\n * `pegin.md` §5.9 requires activation to refuse a bundle whose graph does\n * not reproduce this value. It is called after every check and signature\n * has passed and before the signatures are submitted, and awaited: when it\n * throws, the flow stops and no signature reaches the VP. A run that fails a\n * check, is declined, or resumes past payout signing does not call it, so an\n * earlier record stays in place.\n */\n recordGraphFingerprint: (fingerprint: string) => void | Promise<void>;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Maximum polling timeout (20 minutes) — VP may take 15-20 min to prepare. */\nconst MAX_POLLING_TIMEOUT_MS = 20 * 60 * 1000;\n\n/** Statuses after payout signatures are submitted — if VP is already here, skip. */\nconst POST_PAYOUT_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_ACKS,\n DaemonStatus.PENDING_ACTIVATION,\n DaemonStatus.ACTIVATED_PENDING_BROADCAST,\n DaemonStatus.ACTIVATED,\n]);\n\nconst TARGET_STATUS: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_SIGNATURES,\n ...POST_PAYOUT_STATUSES,\n]);\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\ninterface PreparedTransaction {\n claimerPubkeyXOnly: string;\n payoutTxHex: string;\n assertTxHex: string;\n}\n\nfunction prepareTransactionsForSigning(\n claimerTransactions: ClaimerTransactions[],\n): PreparedTransaction[] {\n return claimerTransactions.map((tx) => ({\n claimerPubkeyXOnly: processPublicKeyToXOnly(tx.claimer_pubkey),\n payoutTxHex: tx.payout_tx.tx_hex,\n assertTxHex: tx.assert_tx.tx_hex,\n }));\n}\n\n/**\n * Canonical x-only lowercase form, used for all claimer pubkey set-equality\n * comparisons in this module. `processPublicKeyToXOnly` already strips any\n * `0x` prefix; the lowercase here removes case-sensitivity (the VP-response\n * schema validator accepts uppercase hex, and `processPublicKeyToXOnly`\n * preserves the case of already-x-only 64-char input).\n */\nfunction normalizeClaimerPubkey(pubkey: string): string {\n return processPublicKeyToXOnly(pubkey).toLowerCase();\n}\n\n/**\n * Assert the approved terms describe the graph we will actually sign. An\n * RFC-006 key rotation bumps only a key epoch, so no version comparison can\n * catch it; `verifyRegisteredParticipantKeys` is the app-side pin, and this\n * keeps the seam self-contained for external providers (#2109).\n *\n * @throws If the terms and the signing context disagree\n */\nfunction assertDepositTermsMatchSigningContext(\n terms: DepositTerms,\n context: PayoutSigningContext,\n): void {\n const refuse = (field: string, a: unknown, b: unknown): never => {\n throw new Error(\n `Deposit terms ${field} (${String(a)}) does not match the vault's ` +\n `version-locked signing context (${String(b)}); refusing to sign ` +\n `payouts against terms that describe a different graph.`,\n );\n };\n\n // Every scalar the two types share: each one shapes the graph, and the\n // timelocks are what payout.ts pins the input sequences to.\n const scalars = [\n [\"protocolFeeRate\", terms.protocolFeeRate, context.protocolFeeRate],\n [\"vaultCoreVersion\", terms.vaultCoreVersion, context.vaultCoreVersion],\n [\"timelockPegin\", terms.timelockPegin, context.timelockPegin],\n [\"timelockAssert\", terms.timelockAssert, context.timelockAssert],\n ] as const;\n for (const [field, fromTerms, fromContext] of scalars) {\n if (fromTerms !== fromContext) {\n refuse(field, fromTerms, fromContext);\n }\n }\n\n // Set, not sequence: btc-vault sorts every roster and rejects duplicates\n // (crates/vault/src/lib.rs:249, :339), so a permutation is not drift.\n const canonical = (keys: readonly string[]) =>\n keys.map(normalizeClaimerPubkey).sort();\n const sameSet = (a: readonly string[], b: readonly string[]) => {\n const x = canonical(a);\n const y = canonical(b);\n return x.length === y.length && x.every((k, i) => k === y[i]);\n };\n\n if (!sameSet(terms.vaultKeeperBtcPubkeys, context.vaultKeeperBtcPubkeys)) {\n refuse(\n \"vaultKeeperBtcPubkeys\",\n terms.vaultKeeperBtcPubkeys.join(\",\"),\n context.vaultKeeperBtcPubkeys.join(\",\"),\n );\n }\n if (\n !sameSet(\n terms.universalChallengerBtcPubkeys,\n context.universalChallengerBtcPubkeys,\n )\n ) {\n refuse(\n \"universalChallengerBtcPubkeys\",\n terms.universalChallengerBtcPubkeys.join(\",\"),\n context.universalChallengerBtcPubkeys.join(\",\"),\n );\n }\n\n // Membership, not equality: `DepositTermsVaultGroup` carries a per-vault VP\n // key, so a batch may legitimately span providers. What matters is that the\n // vault this flow signs for was covered by what the depositor approved.\n const contextVp = normalizeClaimerPubkey(context.vaultProviderBtcPubkey);\n const approvedVps = terms.vaults.map((v) =>\n normalizeClaimerPubkey(v.vaultProviderBtcPubkey),\n );\n if (!approvedVps.includes(contextVp)) {\n refuse(\n \"vaults[].vaultProviderBtcPubkey\",\n approvedVps.join(\",\") || \"<no vaults>\",\n context.vaultProviderBtcPubkey,\n );\n }\n}\n\n/**\n * Reject VP-supplied `response.txs` whose non-depositor claimer set does not\n * exactly equal `{vaultProviderBtcPubkey} ∪ vaultKeeperBtcPubkeys`.\n *\n * The expected set is derived from on-chain context (sourced by the caller\n * from the registry/contract reads that populate PayoutSigningContext). A\n * malicious or buggy VP could otherwise omit registered vault keepers from\n * the response; the depositor would sign only the supplied subset and submit\n * a partial presignature map. If the VP later disappears, the omitted\n * keepers cannot exercise their payout recovery branch and BTC can lock.\n *\n * The depositor's own claimer entry (if present in `response.txs`) is\n * filtered out before diffing — its Payout PSBT is built locally and signed\n * separately via signDepositorGraph, so its presence in `response.txs` is\n * permitted but not required. Duplicate detection runs on the full supplied\n * list *before* the depositor filter, so a response containing\n * `[VP, VK, depositor, depositor]` is rejected as malformed.\n */\nfunction assertNonDepositorClaimerSetMatches(\n suppliedTxs: ClaimerTransactions[],\n expectedVpPubkey: string,\n expectedVkPubkeys: string[],\n depositorPubkeyXOnly: string,\n): void {\n const depositor = normalizeClaimerPubkey(depositorPubkeyXOnly);\n const expectedList = [\n normalizeClaimerPubkey(expectedVpPubkey),\n ...expectedVkPubkeys.map(normalizeClaimerPubkey),\n ];\n const expected = new Set(expectedList);\n if (expected.size !== expectedList.length) {\n throw new Error(\n \"Cannot validate claimer set: signing context contains duplicate vault provider or vault keeper key\",\n );\n }\n if (expected.has(depositor)) {\n throw new Error(\n \"Cannot validate claimer set: depositor key overlaps with vault provider or vault keeper set\",\n );\n }\n\n const suppliedAll = suppliedTxs.map((tx) =>\n normalizeClaimerPubkey(tx.claimer_pubkey),\n );\n if (new Set(suppliedAll).size !== suppliedAll.length) {\n throw new Error(\"Presign response contains duplicate claimer entries\");\n }\n\n const suppliedNonDepositor = suppliedAll.filter((k) => k !== depositor);\n const suppliedSet = new Set(suppliedNonDepositor);\n const missing = expectedList.filter((c) => !suppliedSet.has(c));\n const extra = suppliedNonDepositor.filter((c) => !expected.has(c));\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `Presign response claimer set does not match expected (vault provider ∪ vault keepers)` +\n (missing.length > 0 ? ` (missing: ${missing.join(\", \")})` : \"\") +\n (extra.length > 0 ? ` (unexpected: ${extra.join(\", \")})` : \"\"),\n );\n }\n}\n\n/**\n * Build the `SignPayoutParams` for a single claimer. Role/script resolution\n * happens inside `buildPayoutPsbt`; here we only forward the claimer pubkey\n * and the per-vault context fields.\n */\nfunction buildPayoutSigningInput(\n tx: PreparedTransaction,\n context: PayoutSigningContext,\n) {\n return {\n vaultCoreVersion: context.vaultCoreVersion,\n payoutTxHex: tx.payoutTxHex,\n peginTxHex: context.peginTxHex,\n assertTxHex: tx.assertTxHex,\n vaultProviderBtcPubkey: context.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: context.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: context.universalChallengerBtcPubkeys,\n depositorBtcPubkey: context.depositorBtcPubkey,\n timelockPegin: context.timelockPegin,\n timelockAssert: context.timelockAssert,\n registeredPayoutScriptPubKey: context.registeredPayoutScriptPubKey,\n claimerBtcPubkey: tx.claimerPubkeyXOnly,\n commissionBps: context.commissionBps,\n protocolFeeRate: context.protocolFeeRate,\n councilMembers: context.councilMembers,\n councilQuorum: context.councilQuorum,\n vkClaimerPayoutScriptPubKeys: context.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: context.vpCommissionScriptPubKey,\n };\n}\n\n/**\n * Sign all payout transactions using PayoutManager.\n * Uses batch signing when wallet supports it, sequential otherwise.\n */\nasync function signPayoutTransactions(\n btcWallet: BitcoinWallet,\n context: PayoutSigningContext,\n transactions: PreparedTransaction[],\n onProgress?: (completed: number, total: number) => void,\n): Promise<Record<string, ClaimerSignatures>> {\n const payoutManager = new PayoutManager({\n network: context.network,\n btcWallet,\n });\n\n const totalClaimers = transactions.length;\n onProgress?.(0, totalClaimers);\n\n let payoutSignatures: string[];\n\n if (payoutManager.supportsBatchSigning()) {\n const results = await payoutManager.signPayoutTransactionsBatch(\n transactions.map((tx) => buildPayoutSigningInput(tx, context)),\n );\n payoutSignatures = results.map((r) => r.payoutSignature);\n } else {\n payoutSignatures = [];\n for (let i = 0; i < transactions.length; i++) {\n onProgress?.(i, totalClaimers);\n const result = await payoutManager.signPayoutTransaction(\n buildPayoutSigningInput(transactions[i], context),\n );\n payoutSignatures.push(result.signature);\n }\n }\n\n const signatures: Record<string, ClaimerSignatures> = {};\n for (let i = 0; i < transactions.length; i++) {\n signatures[transactions[i].claimerPubkeyXOnly] = {\n payout_signature: payoutSignatures[i],\n };\n }\n\n onProgress?.(totalClaimers, totalClaimers);\n return signatures;\n}\n\n// ============================================================================\n// Main entry point\n// ============================================================================\n\n/**\n * Poll for payout transactions, sign them, sign the depositor graph,\n * and submit all signatures to the vault provider.\n *\n * This is the main deposit protocol step between registration and activation.\n *\n * @throws Error on timeout, abort, signing failure, or RPC error\n */\nexport async function runDepositorPresignFlow(\n params: RunDepositorPresignFlowParams,\n): Promise<void> {\n const {\n statusReader,\n presignClient,\n btcWallet,\n vaultId,\n peginTxid,\n depositorPk,\n signingContext,\n depositTerms,\n timeoutMs = MAX_POLLING_TIMEOUT_MS,\n signal,\n onProgress,\n recordGraphFingerprint,\n } = params;\n\n // Phase 1: Poll until VP is ready for depositor signatures (or already past)\n const status = await waitForPeginStatus({\n statusReader,\n vaultId,\n peginTxid,\n targetStatuses: TARGET_STATUS,\n timeoutMs,\n signal,\n });\n\n // Resume-safe: if VP already moved past payout signing, nothing to do\n if (POST_PAYOUT_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n // Approval-capable wallets must have terms available before the request,\n // but the approval ceremony itself waits until the VP response has passed\n // every fail-closed validation below.\n if (depositTerms !== undefined) {\n assertDepositTermsMatchSigningContext(depositTerms, signingContext);\n }\n\n const depositApprovalWallet = supportsDepositApproval(btcWallet)\n ? btcWallet\n : undefined;\n let approvedDepositTerms: DepositTerms | undefined;\n if (depositApprovalWallet) {\n if (!depositTerms) {\n throw new Error(\n \"runDepositorPresignFlow: this wallet requires approved deposit terms but none were \" +\n \"provided. Fresh deposits must pass PreparePeginResult.depositTerms; resume flows \" +\n \"must rebuild them from on-chain state (the vault app's rebuildDepositTerms).\",\n );\n }\n approvedDepositTerms = depositTerms;\n }\n\n // Phase 2: Fetch presign transactions\n const response = await presignClient.requestDepositorPresignTransactions(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n },\n signal,\n );\n\n signal?.throwIfAborted();\n\n // Every claimer gets its own Claim/Assert chain. Bind each Assert to its\n // Claim before any payout signing prompt: buildPayoutPsbt uses Assert:0 as\n // a prevout but cannot prove that Assert spends this Claim without the\n // Claim transaction itself. A VP/VK Claim is funded from the claimer's own\n // wallet, so only the depositor graph's Claim is pinned to PegIn:1 (in the\n // fingerprint below).\n response.txs.forEach((tx, index) => {\n assertPresignAssertSpendsClaim({\n claimTxHex: tx.claim_tx.tx_hex,\n assertTxHex: tx.assert_tx.tx_hex,\n path: `txs[${index}]`,\n });\n });\n\n // Fingerprint the set now, so a malformed one fails before any wallet\n // prompt. It is stored only after every check and signature below has\n // passed (see Phase 5). The PegIn comes from our own signing context rather\n // than the response, so the VP does not get to pick both sides of the\n // activation comparison.\n const graphFingerprint = fingerprintPresignTxSet({\n peginTxid,\n peginTxHex: signingContext.peginTxHex,\n claimTxHex: response.depositor_graph.claim_tx.tx_hex,\n assertTxHex: response.depositor_graph.assert_tx.tx_hex,\n payoutTxHex: response.depositor_graph.payout_tx.tx_hex,\n challengers: response.depositor_graph.challenger_presign_data,\n });\n\n // Bind every NoPayout to its canonical ChallengeAssert parents now, before\n // the deposit-terms approval and every payout prompt below. The depositor's\n // NoPayout signature is the only thing that binds those parents, so a\n // forged one must fail before the depositor approves anything.\n // signDepositorGraph repeats the check because it is also a public entry.\n const depositorGraphSigningContext: DepositorGraphSigningContext = {\n vaultCoreVersion: signingContext.vaultCoreVersion,\n peginTxHex: signingContext.peginTxHex,\n depositorBtcPubkey: depositorPk,\n vaultProviderBtcPubkey: signingContext.vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys: signingContext.vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys: signingContext.universalChallengerBtcPubkeys,\n timelockPegin: signingContext.timelockPegin,\n timelockAssert: signingContext.timelockAssert,\n timelockChallengeAssert: signingContext.timelockChallengeAssert,\n councilMembers: signingContext.councilMembers,\n councilQuorum: signingContext.councilQuorum,\n network: signingContext.network,\n registeredPayoutScriptPubKey: signingContext.registeredPayoutScriptPubKey,\n protocolFeeRate: signingContext.protocolFeeRate,\n vkClaimerPayoutScriptPubKeys: signingContext.vkClaimerPayoutScriptPubKeys,\n vpCommissionScriptPubKey: signingContext.vpCommissionScriptPubKey,\n };\n await assertDepositorGraphNoPayoutsCanonical(\n response.depositor_graph,\n depositorGraphSigningContext,\n );\n\n signal?.throwIfAborted();\n\n // Phase 3: Validate and prepare VP/VK claimer payout transactions.\n // Fail-fast: assert the supplied non-depositor claimer set exactly equals\n // the on-chain-derived {VP} ∪ {VKs} before any wallet prompts run. The\n // depositor's own entry is permitted but not required (its payout is\n // signed separately via signDepositorGraph in Phase 4).\n const depositorPkNormalized = normalizeClaimerPubkey(depositorPk);\n assertNonDepositorClaimerSetMatches(\n response.txs,\n signingContext.vaultProviderBtcPubkey,\n signingContext.vaultKeeperBtcPubkeys,\n depositorPk,\n );\n // Filter out the depositor's own claimer entry — its payout is signed\n // separately via signDepositorGraph (Phase 4) using VP-provided PSBTs.\n // Including it here would cause a redundant wallet signing prompt whose\n // result is discarded when the depositor graph signature overwrites it.\n // Compare on the normalized form so an uppercase-hex depositor entry in\n // the VP response is still filtered out consistently with the assertion.\n const nonDepositorTxs = response.txs.filter(\n (tx) => normalizeClaimerPubkey(tx.claimer_pubkey) !== depositorPkNormalized,\n );\n const preparedTransactions = prepareTransactionsForSigning(nonDepositorTxs);\n\n // Approval-capable wallets approve only after the full VP response is\n // validated, and still before any transaction-signing call they authorize.\n if (depositApprovalWallet && approvedDepositTerms) {\n // #2110 T4: providers exposing the validate-only pre-check fail an\n // envelope violation here, before the approval ceremony starts.\n if (typeof depositApprovalWallet.validateDepositTerms === \"function\") {\n await depositApprovalWallet.validateDepositTerms(approvedDepositTerms);\n }\n // The provider validates its own device envelope inside\n // approveDepositTerms (DepositTermsApprover contract, #2109).\n await depositApprovalWallet.approveDepositTerms(approvedDepositTerms);\n }\n\n // The approval prompt can stay open for a long time. A cancel during it\n // must not lead into the signing prompts below.\n signal?.throwIfAborted();\n\n const claimerSignatures = await signPayoutTransactions(\n btcWallet,\n signingContext,\n preparedTransactions,\n onProgress,\n );\n\n signal?.throwIfAborted();\n\n // Phase 4: Sign depositor-as-claimer graph. Both Payout and per-challenger\n // NoPayout PSBTs are rebuilt locally inside signDepositorGraph from these\n // authoritative connector params and the on-chain protocol parameters.\n const depositorClaimerPresignatures = await signDepositorGraph({\n depositorGraph: response.depositor_graph,\n btcWallet,\n signingContext: depositorGraphSigningContext,\n });\n\n signal?.throwIfAborted();\n\n // Record what was just signed, before the signatures leave the device.\n // Not earlier: a set that fails a check or a declined prompt must not\n // replace the record of a set this depositor already signed and submitted.\n // Not later: a failed write stops the flow here, so the VP never holds\n // signatures the depositor has no fingerprint for.\n await recordGraphFingerprint(graphFingerprint);\n\n signal?.throwIfAborted();\n\n // Phase 5: Submit all signatures to VP\n // Include depositor's own payout signature in the signatures map\n const allSignatures = { ...claimerSignatures };\n allSignatures[stripHexPrefix(depositorPk)] =\n depositorClaimerPresignatures.payout_signatures;\n\n await presignClient.submitDepositorPresignatures(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n signatures: allSignatures,\n depositor_claimer_presignatures: depositorClaimerPresignatures,\n },\n signal,\n );\n}\n","/**\n * Submit pre-derived WOTS public keys to the vault provider.\n *\n * Polls `getPeginStatusByVaultId` until the VP reaches `PendingDepositorWotsPK`,\n * then submits the keys. If the VP has already moved past WOTS step\n * (e.g., resume flow), submission is skipped.\n *\n * The caller is responsible for deriving WOTS keys externally using\n * `expandWotsSeed` + `deriveWotsBlocksFromSeed` from the SDK's\n * `tbv/core/vault-secrets` and `tbv/core/wots` modules respectively.\n */\n\nimport {\n DaemonStatus,\n POST_WOTS_STATUSES,\n type WotsBlockPublicKey,\n} from \"../../clients/vault-provider/types\";\nimport type { PeginStatusReader, WotsKeySubmitter } from \"./interfaces\";\nimport { waitForPeginStatus } from \"./waitForPeginStatus\";\n\n/** Maximum time to wait for VP to reach PendingDepositorWotsPK (5 min). */\nconst STATUS_POLL_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** All statuses we accept — either ready for submission or already past it. */\nconst TARGET_STATUSES: ReadonlySet<DaemonStatus> = new Set([\n DaemonStatus.PENDING_DEPOSITOR_WOTS_PK,\n ...POST_WOTS_STATUSES,\n]);\n\nexport interface SubmitWotsPublicKeyParams {\n /** VP client implementing the status reader interface */\n statusReader: PeginStatusReader;\n /** VP client implementing the WOTS key submission interface */\n wotsSubmitter: WotsKeySubmitter;\n /** On-chain vault id (hex, `0x` prefix optional) — addresses status polling */\n vaultId: string;\n /** BTC pegin transaction ID (unprefixed hex, 64 chars) — used by the write RPC */\n peginTxid: string;\n /** Depositor's x-only BTC public key (unprefixed hex, 64 chars) */\n depositorPk: string;\n /** Pre-derived WOTS block public keys (one per assert block) */\n wotsPublicKeys: WotsBlockPublicKey[];\n /** Maximum time to wait for VP to be ready (default: 5 min) */\n timeoutMs?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Submit WOTS public keys to the vault provider.\n *\n * @throws Error on timeout, abort, or RPC error\n */\nexport async function submitWotsPublicKey(\n params: SubmitWotsPublicKeyParams,\n): Promise<void> {\n const {\n statusReader,\n wotsSubmitter,\n vaultId,\n peginTxid,\n depositorPk,\n wotsPublicKeys,\n timeoutMs = STATUS_POLL_TIMEOUT_MS,\n signal,\n } = params;\n\n signal?.throwIfAborted();\n\n // Wait until VP has ingested the pegin and is ready for the WOTS key.\n const status = await waitForPeginStatus({\n statusReader,\n vaultId,\n peginTxid,\n targetStatuses: TARGET_STATUSES,\n timeoutMs,\n signal,\n });\n\n // Key was already submitted in a previous session (e.g. resume flow)\n if (POST_WOTS_STATUSES.has(status)) {\n return;\n }\n\n signal?.throwIfAborted();\n\n await wotsSubmitter.submitDepositorWotsKey(\n {\n pegin_txid: peginTxid,\n depositor_pk: depositorPk,\n wots_public_keys: wotsPublicKeys,\n },\n signal,\n );\n}\n","/**\n * RFC-006 indexer-hint acceptance policy — the single definition.\n *\n * Every path that builds against a vault takes participant keys from chain and\n * treats the indexer's copy as an untrusted *hint*. The hint never supplies key\n * material; its job is to catch a wrong vault provider address, a wrong\n * application entry point, or a stale roster version.\n *\n * Under RFC-006 an operator's BTC key is an append-only history of operation\n * keys, so there are two values the indexer may legitimately be serving at any\n * moment: the **registration** key (it has not caught up to a rotation) or the\n * **current operation** key (it has). Accepting only the first hard-fails every\n * depositor of a rotated provider the day the indexer catches up — an\n * indexer-side deploy, with nothing to deploy on our side and no user\n * workaround. Hence: accept either.\n *\n * For an operator that never rotated the two candidates are identical, so this\n * is exactly the old strict check until someone rotates.\n *\n * This module exists because the policy had already been written three times —\n * deposit, payout, and (missing) refund — and drifted each time. Callers differ\n * in shape and cannot share one entry point:\n *\n * - the deposit path compares three roles as whole sets and resolves operation\n * keys **eagerly**, because it builds the Bitcoin lock with them;\n * - payout and refund compare one scalar key and must read the operation key\n * **lazily**, because on the happy path it is a second RPC for nothing.\n *\n * So what is shared is the predicate — {@link isHintAccepted} and the two\n * matchers — plus {@link assertVaultProviderHintAccepted} for the two scalar\n * callers. Anything that needs a different acceptance rule does not belong\n * here; see `vpAuthPinnedPubkey`, which is deliberately operation-key-only\n * because accepting either would hollow out the pin.\n *\n * @module services/participants/indexerKeyHint\n */\n\nimport type { Address } from \"viem\";\n\nimport { canonicalizeBtcPubkey } from \"../../primitives/utils/bitcoin\";\n\n/**\n * Which of the two legitimate on-chain candidates a role's hint matched.\n *\n * Both true means the role never rotated, so the hint constrains nothing.\n * Both false means the hint is not explainable by any state the chain is in.\n */\nexport interface HintMatch {\n registration: boolean;\n operation: boolean;\n}\n\n/**\n * The accept-either policy itself.\n *\n * Kept as a named function rather than inlined at each call site so that\n * changing the policy is a one-line change in one file, and so a reader can\n * find every path governed by it.\n */\nexport function isHintAccepted(match: HintMatch): boolean {\n return match.registration || match.operation;\n}\n\n/** Match a single hinted key against both candidates. */\nexport function matchKeyHint(\n hint: string,\n registrationKey: string,\n operationKey: string,\n): HintMatch {\n const canonicalHint = canonicalizeBtcPubkey(hint);\n return {\n registration: canonicalHint === canonicalizeBtcPubkey(registrationKey),\n operation: canonicalHint === canonicalizeBtcPubkey(operationKey),\n };\n}\n\n/**\n * Match a hinted key *set* against both candidate sets.\n *\n * Compared as whole sets, never as per-element membership of the union: a\n * roster holding one registration key and one operation key is an indexer that\n * is halfway through applying a rotation, and union membership would wave that\n * through. Order is normalized, so this is set equality and not list equality.\n */\nexport function matchKeySetHint(\n hints: readonly string[],\n registrationKeys: readonly string[],\n operationKeys: readonly string[],\n): HintMatch {\n const canonicalHints = sortedCanonical(hints);\n return {\n registration: setsEqual(canonicalHints, sortedCanonical(registrationKeys)),\n operation: setsEqual(canonicalHints, sortedCanonical(operationKeys)),\n };\n}\n\nfunction sortedCanonical(keys: readonly string[]): string[] {\n return keys.map(canonicalizeBtcPubkey).sort();\n}\n\nfunction setsEqual(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((key, i) => key === b[i]);\n}\n\nexport interface AssertVaultProviderHintAcceptedParams {\n /** Vault provider's admin address, named in the error. */\n vaultProviderEthAddress: Address;\n /** The untrusted hint. Absent means there is nothing to cross-check. */\n hintBtcPubkey?: string;\n /** The vault provider's registration key, already read from chain. */\n registrationBtcPubkey: string;\n /**\n * Reads the vault provider's *current* operation key.\n *\n * Invoked only when the hint fails against the registration key, so a\n * provider that never rotated — and an indexer that has not caught up — cost\n * no extra RPC. Callers must not pre-read this.\n */\n readCurrentOperationBtcPubkey: () => Promise<string>;\n /**\n * Sentence appended to the error naming what was aborted, e.g.\n * `\"Aborting refund.\"`. The shared half of the message says which keys\n * failed to match; this says which operation the user just lost.\n */\n context?: string;\n}\n\n/**\n * Assert an indexer-hinted vault provider key is one the chain can explain.\n *\n * Resolves silently when there is no hint, or when the hint matches either\n * candidate. Throws otherwise — the caller's key material is unaffected either\n * way, since resolution is chain-only.\n */\nexport async function assertVaultProviderHintAccepted(\n params: AssertVaultProviderHintAcceptedParams,\n): Promise<void> {\n const {\n vaultProviderEthAddress,\n hintBtcPubkey,\n registrationBtcPubkey,\n readCurrentOperationBtcPubkey,\n context,\n } = params;\n\n if (!hintBtcPubkey) return;\n\n const canonicalHint = canonicalizeBtcPubkey(hintBtcPubkey);\n if (canonicalHint === canonicalizeBtcPubkey(registrationBtcPubkey)) return;\n\n const canonicalOperation = canonicalizeBtcPubkey(\n await readCurrentOperationBtcPubkey(),\n );\n if (canonicalHint === canonicalOperation) return;\n\n throw new Error(\n `Vault provider BTC pubkey mismatch for ${vaultProviderEthAddress}: ` +\n `indexer hint matches neither the registration key nor the current ` +\n `operation key on-chain.${context ? ` ${context}` : \"\"}`,\n );\n}\n","import { isAddress, isAddressEqual } from \"viem\";\nimport type { Address, Hex } from \"viem\";\n\nimport type {\n OperationKeyReader,\n UniversalChallengerReader,\n VaultKeeperReader,\n VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { canonicalizeBtcPubkey } from \"../../primitives/utils/bitcoin\";\nimport {\n type HintMatch,\n isHintAccepted,\n matchKeyHint,\n matchKeySetHint,\n} from \"../participants/indexerKeyHint\";\nimport { resolveCurrentParticipantKeys } from \"../participants/resolveParticipantKeys\";\nimport type { ParticipantKeySet } from \"../participants/types\";\n\nexport interface ValidateOnChainParticipantKeysParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultKeeperReader: VaultKeeperReader;\n universalChallengerReader: UniversalChallengerReader;\n vaultProviderEthAddress: Address;\n /**\n * The application entry point the caller believes the provider serves — a\n * *hint*, checked against the registry, in the same sense as\n * `expectedVaultProviderBtcPubkey` below.\n *\n * The registry's own `getVaultProviderApplication(vp)` is authoritative: it\n * is what the peg-in submit path resolves internally, and it selects the\n * keeper roster, the roster version and the keeper key epoch a deposit is\n * bonded to. This value comes from the dApp's configuration instead. They\n * agree today, so the check is normally a no-op — but if they ever diverge,\n * building against the configured one would bond the vault to an application\n * the caller never chose, and nothing downstream would say so.\n */\n applicationEntryPoint: Address;\n expectedVaultProviderBtcPubkey: string;\n expectedVaultKeeperBtcPubkeys: string[];\n expectedUniversalChallengerBtcPubkeys: string[];\n /**\n * RFC-006. Participant keys are resolved to their *current operation* keys,\n * and those are what the returned key fields carry.\n */\n operationKeyReader: OperationKeyReader;\n /**\n * Optional observer for the case where the indexer hint matched the\n * operation keys rather than the registration keys — i.e. the indexer is\n * ahead of us, not wrong. Called at most once.\n */\n onIndexerServingOperationKeys?: (message: string) => void;\n /**\n * Optional observer for the case where the indexer is serving a half-applied\n * view — one role explainable only by the registration keys, another only by\n * the operation keys. That blocks every deposit for the provider until the\n * indexer converges, and \"Refresh and try again\" cannot help, so the block\n * needs to be visible rather than showing up only as user reports. Called\n * immediately before the throw.\n */\n onIndexerHintsInconsistent?: (message: string) => void;\n /**\n * Block to resolve every read against, so the roster versions, the roster\n * members and their operation keys all describe one chain state.\n *\n * This function reads in four dependent rounds — the application entry point\n * and the challenger axis, then the keeper version and epoch keyed on that\n * entry point, then the roster members at those versions, then those members'\n * operation keys — because each round needs the previous round's output.\n * Left unpinned, each round lands on\n * whatever `latest` happens to be, and a rotation between rounds yields a key\n * set that no single block ever held. The Bitcoin lock built from it would\n * commit to that mixture, and no counterparty would agree with it.\n *\n * Required rather than optional, and deliberately so. This function exists\n * only for the fresh-deposit path, where an unpinned read is never correct —\n * an optional pin would be a silent fallback to `latest`-per-round on a path\n * that builds a Bitcoin lock. Callers must pass the same block to\n * `ProtocolParamsReader.getPegInConfiguration`; the reader interfaces keep\n * their pin optional because their other callers resolve against a vault's\n * already-frozen epochs, where pinning is meaningless.\n */\n blockNumber: bigint;\n}\n\nexport interface ValidatedOnChainParticipantKeys {\n /** The VP key to build with: its current operation key. */\n vaultProviderBtcPubkeyXOnly: string;\n vaultKeeperBtcPubkeysSorted: string[];\n universalChallengerBtcPubkeysSorted: string[];\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n /**\n * The two operation-key epochs the peg-in config fingerprint commits to, as\n * `bigint` (`uint64` on-chain).\n *\n * They live here rather than beside the protocol params because they label\n * the very keys this function resolves: `appKeeperKeyEpoch` is the epoch the\n * keeper operation keys above were read at, and `ucKeyEpoch` the same for the\n * challengers. Reading them anywhere else would create a second place for the\n * epoch and the keys it names to come from different blocks.\n */\n appKeeperKeyEpoch: bigint;\n ucKeyEpoch: bigint;\n /**\n * The registration / roster keys, sorted. These are what indexer hints are\n * compared against first, and they stay available for diagnostics after\n * resolution.\n */\n registrationKeys: {\n vaultProvider: string;\n vaultKeepers: string[];\n universalChallengers: string[];\n };\n /**\n * The full resolution, including the admin↔key pairing. Feeds the\n * post-registration read-after-mine verification.\n */\n participantKeys: ParticipantKeySet;\n}\n\n/**\n * The application entry point the caller passed cannot be used, either because\n * the registry says this vault provider serves a different one, or because the\n * value is not a well-formed address so no comparison is possible.\n *\n * Both are a deployment or configuration fault — not chain drift, and not\n * anything a depositor can act on. One class covers both because the caller's\n * recovery is identical and the distinction only matters in a bug report, which\n * the message carries.\n *\n * It is typed only so the consuming app can recognise it and show its own\n * generic copy: the messages name addresses and the protocol values at stake,\n * which belongs in that bug report rather than in front of a depositor. An\n * untyped `Error` would be rendered verbatim as the callout body by the\n * mapper's last-resort bucket.\n */\nexport class ApplicationEntryPointMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ApplicationEntryPointMismatchError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate copies, test\n// mocks). Fall back to the name field, as the sibling drift guards do.\nexport function isApplicationEntryPointMismatchError(\n err: unknown,\n): err is ApplicationEntryPointMismatchError {\n return (\n err instanceof ApplicationEntryPointMismatchError ||\n (err instanceof Error && err.name === \"ApplicationEntryPointMismatchError\")\n );\n}\n\nconst sortedSet = (keys: string[]) => keys.map(canonicalizeBtcPubkey).sort();\n\nexport async function validateOnChainParticipantKeys(\n params: ValidateOnChainParticipantKeysParams,\n): Promise<ValidatedOnChainParticipantKeys> {\n const {\n vaultRegistryReader,\n vaultKeeperReader,\n universalChallengerReader,\n vaultProviderEthAddress,\n applicationEntryPoint,\n expectedVaultProviderBtcPubkey,\n expectedVaultKeeperBtcPubkeys,\n expectedUniversalChallengerBtcPubkeys,\n operationKeyReader,\n onIndexerServingOperationKeys,\n onIndexerHintsInconsistent,\n blockNumber,\n } = params;\n\n // Round 1: everything that needs no other read's output. The application\n // entry point is among them because the keeper reads below key on it, and it\n // must come from the registry rather than from the caller's configuration.\n const [\n registryApplicationEntryPoint,\n onChainVpKey,\n expectedUniversalChallengersVersion,\n ucKeyEpoch,\n ] = await Promise.all([\n vaultRegistryReader.getVaultProviderApplication(\n vaultProviderEthAddress,\n blockNumber,\n ),\n vaultRegistryReader.getVaultProviderGenesisBtcPubKey(\n vaultProviderEthAddress,\n blockNumber,\n ),\n universalChallengerReader.getLatestUniversalChallengersVersion(blockNumber),\n universalChallengerReader.getCurrentUcKeyEpoch(blockNumber),\n ]);\n\n // Fail closed before any roster is read. A divergence here is a deployment or\n // configuration fault rather than something the depositor can act on, so it\n // carries a TYPED error: the app maps that to its own generic copy and keeps\n // the message below for the bug report. An untyped `Error` would be rendered\n // verbatim as the depositor-facing callout body by the mapper's last resort.\n //\n // The shape check runs first because callers reach this with a plain `string`\n // cast to `Address` — nothing validates that cast — and `isAddressEqual`\n // *parses* both sides, so a malformed value would throw viem's error instead\n // of the one written for this situation. `isAddressEqual` rather than `===`\n // because the two values arrive from different sources and need not share\n // EIP-55 casing.\n if (!isAddress(applicationEntryPoint, { strict: false })) {\n throw new ApplicationEntryPointMismatchError(\n `The application entry point this deposit was prepared for is not a ` +\n `valid address: ${applicationEntryPoint}`,\n );\n }\n if (!isAddressEqual(registryApplicationEntryPoint, applicationEntryPoint)) {\n throw new ApplicationEntryPointMismatchError(\n `Vault provider ${vaultProviderEthAddress} is registered to application ` +\n `${registryApplicationEntryPoint}, but this deposit was prepared for ` +\n `${applicationEntryPoint}. Refusing to build: the vault keeper roster, ` +\n `its version, and the keeper key epoch would all resolve against a ` +\n `different application than the one selected.`,\n );\n }\n\n // Round 2: keyed on the registry-resolved entry point. The keeper roster\n // version and the keeper key epoch are read together so the epoch labels the\n // roster it was read beside.\n const [expectedAppVaultKeepersVersion, appKeeperKeyEpoch] = await Promise.all(\n [\n vaultKeeperReader.getCurrentVaultKeepersVersion(\n registryApplicationEntryPoint,\n blockNumber,\n ),\n vaultKeeperReader.getCurrentAppKeeperKeyEpoch(\n registryApplicationEntryPoint,\n blockNumber,\n ),\n ],\n );\n\n const [onChainKeepers, onChainChallengers] = await Promise.all([\n vaultKeeperReader.getVaultKeepersByVersion(\n registryApplicationEntryPoint,\n expectedAppVaultKeepersVersion,\n blockNumber,\n ),\n universalChallengerReader.getUniversalChallengersByVersion(\n expectedUniversalChallengersVersion,\n blockNumber,\n ),\n ]);\n\n const registrationKeys = {\n vaultProvider: canonicalizeBtcPubkey(onChainVpKey),\n vaultKeepers: sortedSet(onChainKeepers.map((p) => p.btcPubKey)),\n universalChallengers: sortedSet(onChainChallengers.map((p) => p.btcPubKey)),\n };\n\n // Resolve the current operation keys. This is what we build the Bitcoin lock\n // with; the registration keys above stay only as the primary comparison\n // target for indexer hints.\n //\n // Read unconditionally, which is why this path cannot use\n // `assertVaultProviderHintAccepted` — that helper reads the operation key\n // lazily, for callers that only need it to explain a hint mismatch.\n const participantKeys: ParticipantKeySet =\n await resolveCurrentParticipantKeys({\n operationKeyReader,\n query: {\n vaultProviderEthAddress,\n vaultProviderGenesisBtcPubkey: `0x${onChainVpKey}` as Hex,\n applicationEntryPoint: registryApplicationEntryPoint,\n vaultKeepers: onChainKeepers,\n universalChallengers: onChainChallengers,\n },\n blockNumber,\n });\n\n const operationKeys = {\n vaultProvider: participantKeys.vaultProvider.operationBtcPubkey as string,\n vaultKeepers: [...participantKeys.vaultKeeperOperationKeysSorted],\n universalChallengers: [\n ...participantKeys.universalChallengerOperationKeysSorted,\n ],\n };\n\n // --- Indexer hint cross-check -----------------------------------------\n //\n // The hint never influences the resolved key — resolution is chain-only.\n // Its job is to catch a wrong VP address, a wrong application entry point,\n // or a stale roster version, and it still does that.\n //\n // Once rotation is possible the indexer may legitimately serve either the\n // registration keys (it has not caught up) or the operation keys (it has),\n // so each role is accepted against both candidates — see `indexerKeyHint`,\n // which owns that policy and the whole-set matching rule. The cross-role\n // check below closes the same hole one level up.\n const vpMatch: HintMatch = matchKeyHint(\n expectedVaultProviderBtcPubkey,\n registrationKeys.vaultProvider,\n operationKeys.vaultProvider,\n );\n const keeperMatch: HintMatch = matchKeySetHint(\n expectedVaultKeeperBtcPubkeys,\n registrationKeys.vaultKeepers,\n operationKeys.vaultKeepers,\n );\n const challengerMatch: HintMatch = matchKeySetHint(\n expectedUniversalChallengerBtcPubkeys,\n registrationKeys.universalChallengers,\n operationKeys.universalChallengers,\n );\n\n if (!isHintAccepted(vpMatch)) {\n throw new Error(\n `Vault provider BTC pubkey indexer hint does not match BTCVaultRegistry for ${vaultProviderEthAddress}. Refresh and try again.`,\n );\n }\n if (!isHintAccepted(keeperMatch)) {\n throw new Error(\n `Vault keeper BTC pubkeys (v${expectedAppVaultKeepersVersion}) indexer set does not match ApplicationRegistry on-chain set. Refresh and try again.`,\n );\n }\n if (!isHintAccepted(challengerMatch)) {\n throw new Error(\n `Universal challenger BTC pubkeys (v${expectedUniversalChallengersVersion}) indexer set does not match ProtocolParams on-chain set. Refresh and try again.`,\n );\n }\n\n // Cross-role consistency. A role whose two candidates are identical (nobody\n // in it rotated) matches both and constrains nothing. But if one role can\n // *only* be explained by the registration keys while another can *only* be\n // explained by the operation keys, the indexer is serving a half-applied\n // view — accepting that would mean building with a key set no single\n // snapshot of the indexer ever held.\n const roles = [vpMatch, keeperMatch, challengerMatch];\n const pinsRegistration = roles.some((m) => m.registration && !m.operation);\n const pinsOperation = roles.some((m) => m.operation && !m.registration);\n\n if (pinsRegistration && pinsOperation) {\n const message =\n `Indexer participant hints are internally inconsistent for vault provider ` +\n `${vaultProviderEthAddress}: some roles match the registration keys while ` +\n `others match the rotated operation keys.`;\n // Report before throwing. This state blocks every deposit for the provider\n // and clears only when the indexer converges across all three registries —\n // nothing the user does resolves it, so it has to be observable to us.\n onIndexerHintsInconsistent?.(message);\n throw new Error(`${message} Refresh and try again.`);\n }\n\n if (pinsOperation) {\n onIndexerServingOperationKeys?.(\n `Indexer is serving rotated operation keys for vault provider ${vaultProviderEthAddress}`,\n );\n }\n\n return {\n vaultProviderBtcPubkeyXOnly: operationKeys.vaultProvider,\n vaultKeeperBtcPubkeysSorted: operationKeys.vaultKeepers,\n universalChallengerBtcPubkeysSorted: operationKeys.universalChallengers,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n appKeeperKeyEpoch,\n ucKeyEpoch,\n registrationKeys,\n participantKeys,\n };\n}\n","/**\n * Pure validation functions for deposit operations.\n *\n * All validations return a consistent {@link ValidationResult} format or throw\n * on critical failures (e.g. missing protocol participants).\n *\n * Business rules (single-provider limit, max vault count) and form-flow\n * checks (wallet connected) belong in the consumer layer.\n *\n * @module tbv/core/services/deposit/validation\n */\n\nimport {\n formatSatoshisToBtc,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n warnings?: string[];\n}\n\n/**\n * Parameters for checking if a deposit form is valid.\n */\nexport interface DepositFormValidityParams {\n /** Deposit amount in satoshis */\n amountSats: bigint;\n /** Minimum deposit from protocol params */\n minDeposit: bigint;\n /** Maximum deposit from protocol params (optional) */\n maxDeposit?: bigint;\n /** User's available BTC balance in satoshis */\n btcBalance: bigint;\n /** Estimated transaction fee in satoshis */\n estimatedFeeSats?: bigint;\n /** Depositor claim value in satoshis (required output for challenge transactions) */\n depositorClaimValue?: bigint;\n}\n\nexport interface RemainingCapacityParams {\n /** Requested deposit amount in satoshis */\n amount: bigint;\n /**\n * Effective remaining capacity in satoshis (min of protocol-total and\n * per-address remaining). `null` means no cap applies.\n */\n effectiveRemaining: bigint | null;\n}\n\n/** Narrow structural type for UTXO — avoids importing vault-specific types. */\ninterface UtxoLike {\n txid: string;\n vout: number;\n value: number;\n}\n\n/**\n * Parameters for validating multi-vault deposit flow inputs.\n *\n * Callers must resolve any async loading states before calling — the SDK\n * validates resolved data, not React hook state.\n *\n * Form-flow checks (wallet connected, provider selected) are the caller's\n * responsibility and are NOT performed here.\n */\nexport interface MultiVaultDepositFlowInputs {\n vaultAmounts: bigint[];\n confirmedUTXOs: UtxoLike[];\n vaultProviderBtcPubkey: string;\n vaultKeeperBtcPubkeys: string[];\n universalChallengerBtcPubkeys: string[];\n /** Protocol minimum deposit per vault (satoshis) */\n minDeposit: bigint;\n /** Protocol maximum deposit per vault (satoshis) */\n maxDeposit?: bigint;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction isValidXOnlyHex(hex: string): boolean {\n return /^[0-9a-fA-F]{64}$/.test(hex);\n}\n\n// ---------------------------------------------------------------------------\n// Validation functions\n// ---------------------------------------------------------------------------\n\n/**\n * Check if deposit amount is within valid range and affordable.\n *\n * Returns false when fees/claim value are not yet known (still loading),\n * and includes them in the balance check once available.\n */\nexport function isDepositAmountValid(\n params: DepositFormValidityParams,\n): boolean {\n const {\n amountSats,\n minDeposit,\n maxDeposit,\n btcBalance,\n estimatedFeeSats,\n depositorClaimValue,\n } = params;\n\n if (amountSats <= 0n) return false;\n if (amountSats < minDeposit) return false;\n if (maxDeposit && maxDeposit > 0n && amountSats > maxDeposit) return false;\n\n if (estimatedFeeSats == null || depositorClaimValue == null) return false;\n\n const totalRequired = amountSats + estimatedFeeSats + depositorClaimValue;\n if (totalRequired > btcBalance) return false;\n\n return true;\n}\n\n/**\n * Validate deposit amount against minimum and maximum constraints.\n */\nexport function validateDepositAmount(\n amount: bigint,\n minDeposit: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (amount <= 0n) {\n return {\n valid: false,\n error: \"Deposit amount must be greater than zero\",\n };\n }\n\n if (amount < minDeposit) {\n return {\n valid: false,\n error: `Minimum deposit is ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n\n if (maxDeposit && maxDeposit > 0n && amount > maxDeposit) {\n return {\n valid: false,\n error: `Maximum deposit is ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that the requested deposit fits within the effective remaining cap.\n */\nexport function validateRemainingCapacity(\n params: RemainingCapacityParams,\n): ValidationResult {\n const { amount, effectiveRemaining } = params;\n if (effectiveRemaining === null) return { valid: true };\n\n if (effectiveRemaining === 0n) {\n return {\n valid: false,\n error: \"Supply cap reached — deposits temporarily paused\",\n };\n }\n\n if (amount > effectiveRemaining) {\n return {\n valid: false,\n error: `Vault size exceeds remaining capacity (${formatSatoshisToBtc(effectiveRemaining)} BTC)`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate that selected providers exist in the available set.\n *\n * Business rules (e.g. single-provider limit) are the caller's responsibility.\n */\nexport function validateProviderSelection(\n selectedProviders: string[],\n availableProviders: string[],\n): ValidationResult {\n if (!selectedProviders || selectedProviders.length === 0) {\n return {\n valid: false,\n error: \"At least one vault provider must be selected\",\n };\n }\n\n const availableProvidersLower = availableProviders.map((p) =>\n p.toLowerCase(),\n );\n const invalidProviders = selectedProviders.filter(\n (p) => !availableProvidersLower.includes(p.toLowerCase()),\n );\n\n if (invalidProviders.length > 0) {\n return {\n valid: false,\n error: \"Invalid vault provider selected\",\n };\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault amounts array for multi-vault deposits.\n * Checks count, positivity, and per-vault min/max protocol limits.\n *\n * Max vault count limits are the caller's responsibility.\n */\nexport function validateVaultAmounts(\n amounts: bigint[],\n minDeposit?: bigint,\n maxDeposit?: bigint,\n): ValidationResult {\n if (!amounts || amounts.length === 0) {\n return {\n valid: false,\n error: \"At least one vault amount required\",\n };\n }\n\n for (let i = 0; i < amounts.length; i++) {\n const amount = amounts[i];\n if (amount <= 0n) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount must be positive`,\n };\n }\n if (minDeposit && amount < minDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC is below minimum deposit ${formatSatoshisToBtc(minDeposit)} BTC`,\n };\n }\n if (maxDeposit && amount > maxDeposit) {\n return {\n valid: false,\n error: `Vault ${i + 1} amount ${formatSatoshisToBtc(amount)} BTC exceeds maximum deposit ${formatSatoshisToBtc(maxDeposit)} BTC`,\n };\n }\n }\n\n return { valid: true };\n}\n\n/**\n * Validate vault provider BTC public key format.\n */\nexport function validateVaultProviderPubkey(pubkey: string): ValidationResult {\n const stripped = stripHexPrefix(pubkey);\n if (!isValidXOnlyHex(stripped)) {\n return {\n valid: false,\n error:\n \"Invalid pubkey format: must be 64 hex characters (32-byte x-only public key, no 0x prefix)\",\n };\n }\n return { valid: true };\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers for multi-vault validation\n// ---------------------------------------------------------------------------\n\nfunction validateVaultKeepers(vaultKeeperBtcPubkeys: string[]): void {\n if (!vaultKeeperBtcPubkeys || vaultKeeperBtcPubkeys.length === 0) {\n throw new Error(\n \"No vault keepers available. The system requires at least one vault keeper to create a deposit.\",\n );\n }\n}\n\nfunction validateUniversalChallengers(\n universalChallengerBtcPubkeys: string[],\n): void {\n if (\n !universalChallengerBtcPubkeys ||\n universalChallengerBtcPubkeys.length === 0\n ) {\n throw new Error(\n \"No universal challengers available. The system requires at least one universal challenger to create a deposit.\",\n );\n }\n}\n\nfunction validateUTXOState(confirmedUTXOs: UtxoLike[]): void {\n if (confirmedUTXOs.length === 0) {\n throw new Error(\"No spendable UTXOs available\");\n }\n}\n\n// ---------------------------------------------------------------------------\n// Multi-vault composite validation\n// ---------------------------------------------------------------------------\n\n/**\n * Validate protocol-level multi-vault deposit inputs.\n * Throws an error if any validation fails.\n *\n * Form-flow checks (wallet connections, provider selection) must be\n * performed by the caller before invoking this function.\n */\nexport function validateMultiVaultDepositInputs(\n params: MultiVaultDepositFlowInputs,\n): void {\n const {\n vaultAmounts,\n confirmedUTXOs,\n vaultProviderBtcPubkey,\n vaultKeeperBtcPubkeys,\n universalChallengerBtcPubkeys,\n minDeposit,\n maxDeposit,\n } = params;\n\n const amountsValidation = validateVaultAmounts(\n vaultAmounts,\n minDeposit,\n maxDeposit,\n );\n if (!amountsValidation.valid) {\n throw new Error(amountsValidation.error);\n }\n\n // Vault provider pubkey\n const pubkeyValidation = validateVaultProviderPubkey(vaultProviderBtcPubkey);\n if (!pubkeyValidation.valid) {\n throw new Error(pubkeyValidation.error);\n }\n\n validateVaultKeepers(vaultKeeperBtcPubkeys);\n validateUniversalChallengers(universalChallengerBtcPubkeys);\n validateUTXOState(confirmedUTXOs);\n}\n","/**\n * RFC-006 read-after-mine verification for a freshly registered vault.\n *\n * A new peg-in is built with each participant's *current* operation key, but\n * the vault does not freeze its key epochs until `submitPeginRequest` executes.\n * An operator rotating in that window would leave the registered vault bonded\n * to keys other than the ones baked into the Bitcoin lock we are about to\n * broadcast — BTC locked into a script no counterparty will presign.\n *\n * So after the registration mines and before the BTC broadcast, re-read the\n * vault's frozen epochs, re-resolve every participant against them, and assert\n * the result is byte-identical to what we built with.\n *\n * Deliberately a sibling of `verifyRegisteredVaultVersions` rather than an\n * extension of it: that function reads `getProtocolInfoBatch` through the\n * shared 13-field ABI and must stay there, while this one needs the extended\n * key-epoch ABI.\n *\n * @module services/deposit/verifyRegisteredParticipantKeys\n */\n\nimport type { Hex } from \"viem\";\n\nimport type {\n OperationKeyReader,\n VaultRegistryReader,\n} from \"../../clients/eth/types\";\nimport { resolveParticipantKeysAtEpochs } from \"../participants/resolveParticipantKeys\";\nimport type { ParticipantKeySet } from \"../participants/types\";\n\n/**\n * Participant operation keys drifted between building the Bitcoin artifacts\n * and the vault freezing its epochs.\n *\n * A *sibling* of `RegisteredVaultVersionMismatchError`, never a subclass, and\n * the distinction is load-bearing. On a version mismatch the orchestrator drops\n * the local pending-pegin record, because the on-chain `prePeginTxHash` is\n * still the authoritative copy of the transaction and a later resume can safely\n * broadcast it from the indexer.\n *\n * Key drift breaks exactly that assumption. The registered hash commits to a\n * transaction whose scripts embed the *pre-rotation* keys, while the vault\n * froze the *post-rotation* epoch — so every counterparty resolves a different\n * funding output and the deposit can never activate. Dropping the record would\n * discard `buildParticipantOperationKeys`, the only thing that lets the resume\n * path re-detect the drift; the next attempt would fall back to the indexer's\n * copy, pass the hash check, and broadcast the very transaction this refused,\n * locking BTC until the refund timelock.\n *\n * So: callers must keep the pending record when they catch this.\n */\nexport class ParticipantKeyDriftError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ParticipantKeyDriftError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field, as the version guard does.\nexport function isParticipantKeyDriftError(\n err: unknown,\n): err is ParticipantKeyDriftError {\n return (\n err instanceof ParticipantKeyDriftError ||\n (err instanceof Error && err.name === \"ParticipantKeyDriftError\")\n );\n}\n\nexport interface VerifyRegisteredParticipantKeysParams {\n vaultRegistryReader: VaultRegistryReader;\n operationKeyReader: OperationKeyReader;\n vaultIds: readonly Hex[];\n /**\n * The exact key set the BTC artifacts were built with. Its `query` supplies\n * the rosters to re-resolve against — deliberately reused rather than\n * accepted as a separate argument, so the two can never disagree and a\n * roster that moved since the build cannot be misreported as a key drift.\n */\n expected: ParticipantKeySet;\n}\n\nfunction diffKeys(\n label: string,\n expected: readonly string[],\n actual: readonly string[],\n): string | null {\n if (\n expected.length === actual.length &&\n expected.every((k, i) => k === actual[i])\n ) {\n return null;\n }\n return `${label} expected [${expected.join(\", \")}], got [${actual.join(\", \")}]`;\n}\n\nexport async function verifyRegisteredParticipantKeys(\n params: VerifyRegisteredParticipantKeysParams,\n): Promise<void> {\n const { vaultRegistryReader, operationKeyReader, vaultIds, expected } =\n params;\n const query = expected.query;\n\n if (vaultIds.length === 0) return;\n\n // Batch registration gives every sibling vault the same epochs, so these\n // reads are redundant across siblings. Kept per-vault anyway: it is one\n // multicall, and asserting each vault individually is what makes the error\n // name the vault that drifted.\n const epochsPerVault =\n await vaultRegistryReader.getVaultKeyEpochsBatch(vaultIds);\n\n const mismatches: string[] = [];\n\n for (const [i, epochs] of epochsPerVault.entries()) {\n const vaultId = vaultIds[i];\n\n let resolved: ParticipantKeySet;\n try {\n resolved = await resolveParticipantKeysAtEpochs({\n operationKeyReader,\n query,\n epochs,\n });\n } catch (error) {\n // A resolution failure here is itself a mismatch signal: the frozen\n // epochs point at a key set we cannot reconstruct, so broadcasting\n // would be worse than aborting.\n mismatches.push(\n `vault ${vaultId}: could not resolve participants at frozen epochs ` +\n `(vp=${epochs.vpKeyEpoch}, keeper=${epochs.appKeeperKeyEpoch}, ` +\n `uc=${epochs.ucKeyEpoch}): ${(error as Error).message}`,\n );\n continue;\n }\n\n if (\n resolved.vaultProvider.operationBtcPubkey !==\n expected.vaultProvider.operationBtcPubkey\n ) {\n mismatches.push(\n `vault ${vaultId}: vault provider key expected ` +\n `${expected.vaultProvider.operationBtcPubkey}, got ` +\n `${resolved.vaultProvider.operationBtcPubkey}`,\n );\n }\n\n const keeperDiff = diffKeys(\n `vault ${vaultId}: vault keeper keys`,\n expected.vaultKeeperOperationKeysSorted,\n resolved.vaultKeeperOperationKeysSorted,\n );\n if (keeperDiff) mismatches.push(keeperDiff);\n\n const challengerDiff = diffKeys(\n `vault ${vaultId}: universal challenger keys`,\n expected.universalChallengerOperationKeysSorted,\n resolved.universalChallengerOperationKeysSorted,\n );\n if (challengerDiff) mismatches.push(challengerDiff);\n }\n\n if (mismatches.length > 0) {\n throw new ParticipantKeyDriftError(\n `Aborting BTC broadcast: participant operation keys changed during registration ` +\n `(${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ` +\n `ETH vault will time out per protocol rules.`,\n );\n }\n}\n","import type { Hex } from \"viem\";\n\nimport type { VaultRegistryReader } from \"../../clients/eth/types\";\n\nexport interface VerifyRegisteredVaultVersionsParams {\n vaultRegistryReader: VaultRegistryReader;\n vaultIds: readonly Hex[];\n expectedOffchainParamsVersion: number;\n expectedAppVaultKeepersVersion: number;\n expectedUniversalChallengersVersion: number;\n /**\n * Vault core (tx-graph) version the BTC artifacts were BUILT with. The\n * contract stamps `activeVaultCoreVersion` at registration-tx execution\n * time, so a governance flip between build and registration stamps a\n * different graph than the one the depositor signed — broadcasting would\n * lock BTC into a graph no resume path can rebuild.\n */\n expectedVaultCoreVersion: number;\n}\n\n// Distinct from a transient RPC failure: the orchestrator removes pending\n// pegin entries only when a real mismatch is confirmed on-chain.\nexport class RegisteredVaultVersionMismatchError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"RegisteredVaultVersionMismatchError\";\n }\n}\n\n// `instanceof` alone fails across module boundaries (duplicate SDK copies,\n// test mocks). Fall back to the name field so the cleanup path stays reliable.\nexport function isRegisteredVaultVersionMismatchError(\n err: unknown,\n): err is RegisteredVaultVersionMismatchError {\n return (\n err instanceof RegisteredVaultVersionMismatchError ||\n (err instanceof Error && err.name === \"RegisteredVaultVersionMismatchError\")\n );\n}\n\nexport async function verifyRegisteredVaultVersions(\n params: VerifyRegisteredVaultVersionsParams,\n): Promise<void> {\n const {\n vaultRegistryReader,\n vaultIds,\n expectedOffchainParamsVersion,\n expectedAppVaultKeepersVersion,\n expectedUniversalChallengersVersion,\n expectedVaultCoreVersion,\n } = params;\n\n const infos = await vaultRegistryReader.getProtocolInfoBatch(vaultIds);\n\n const mismatches: string[] = [];\n infos.forEach((v, i) => {\n const id = vaultIds[i];\n if (v.offchainParamsVersion !== expectedOffchainParamsVersion) {\n mismatches.push(\n `vault ${id}: offchainParams expected v${expectedOffchainParamsVersion}, got v${v.offchainParamsVersion}`,\n );\n }\n if (v.appVaultKeepersVersion !== expectedAppVaultKeepersVersion) {\n mismatches.push(\n `vault ${id}: appVaultKeepers expected v${expectedAppVaultKeepersVersion}, got v${v.appVaultKeepersVersion}`,\n );\n }\n if (v.universalChallengersVersion !== expectedUniversalChallengersVersion) {\n mismatches.push(\n `vault ${id}: universalChallengers expected v${expectedUniversalChallengersVersion}, got v${v.universalChallengersVersion}`,\n );\n }\n if (v.vaultCoreVersion !== expectedVaultCoreVersion) {\n mismatches.push(\n `vault ${id}: vaultCoreVersion expected v${expectedVaultCoreVersion} (build-time active), got v${v.vaultCoreVersion}`,\n );\n }\n });\n\n if (mismatches.length > 0) {\n throw new RegisteredVaultVersionMismatchError(\n `Aborting BTC broadcast: signer-set or offchain-params versions changed during registration (${mismatches.join(\"; \")}). The Pre-PegIn was not broadcast; the registered ETH vault will time out per protocol rules.`,\n );\n }\n}\n","/**\n * Domain errors thrown by the refund service.\n *\n * @module services/refund/errors\n */\n\nimport type { Hex } from \"viem\";\n\n/**\n * Thrown when the broadcast transport rejects the refund tx because the CSV\n * timelock has not yet matured (BIP68 non-final). Callers can surface a\n * friendly \"wait until block N\" message; the original transport error is\n * available via {@link cause}.\n */\nexport class BIP68NotMatureError extends Error {\n public readonly vaultId: Hex;\n public override readonly cause: Error;\n\n constructor(vaultId: Hex, cause: Error) {\n super(`Refund not yet mature (BIP68 not final): ${cause.message}`);\n this.name = \"BIP68NotMatureError\";\n this.vaultId = vaultId;\n this.cause = cause;\n }\n}\n","/**\n * Vault refund orchestration — reclaim BTC from an expired Pre-PegIn HTLC via\n * the CSV-timelocked refund script (leaf 1). SDK owns the sequence of:\n * fetch → fee calc → PSBT build → sign → finalize → broadcast. Pre-fetched\n * data (fee rate) is passed by value; the data-flow-dependent reads\n * (`readVault`, `readPrePeginContext(vault)`) and the interactive transports\n * (`signPsbt`, `broadcastTx`) stay as injected callbacks so the caller keeps\n * its transport choice (viem, wagmi, mempool client, etc.) and error decoding.\n *\n * @module services/refund\n */\n\nimport type { Network } from \"@babylonlabs-io/babylon-tbv-rust-wasm\";\nimport { Transaction } from \"bitcoinjs-lib\";\nimport type { Address, Hex } from \"viem\";\n\nimport type { SignPsbtOptions } from \"../../../../shared/wallets/interfaces/BitcoinWallet\";\nimport { findAuthAnchorOpReturn } from \"../../managers/pegin\";\nimport { assertPsbtUnsignedTxMatches } from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport { finalizeScriptPathWithSignatures } from \"../../primitives/psbt/finalizeScriptPathWithSignatures\";\nimport { extractPayoutSignature } from \"../../primitives/psbt/payout\";\nimport { buildRefundPsbt } from \"../../primitives/psbt/refund\";\nimport { assertScriptPathSchnorrSignature } from \"../../primitives/psbt/verifyScriptPathSchnorrSignature\";\nimport { assertValidVaultCoreVersion } from \"../../primitives/vaultCoreVersion\";\nimport {\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\n\nimport { BIP68NotMatureError } from \"./errors\";\n\nconst BYTES32_HEX_RE = /^0x[0-9a-fA-F]{64}$/;\n// BTC raw-hex convention: 0x prefix optional, even number of hex chars, must\n// be non-empty. Named distinctly from the ETH-hex regex in activateVault.ts\n// (which requires a 0x prefix and allows empty \"0x\") to make the convention\n// explicit at the call site.\nconst BTC_HEX_BYTES_RE = /^(?:0x)?(?:[0-9a-fA-F]{2})+$/;\n// Pubkeys are either 32 bytes (x-only, 64 hex chars) or 33 bytes (compressed,\n// 66 hex chars). 65 hex chars is not a valid byte length — reject it here\n// rather than letting the malformed value surface as an opaque PSBT/signing\n// failure later.\nconst PUBKEY_HEX_RE = /^(?:0x)?(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{66})$/;\n// Conservative upper bound for the fixed-shape refund tx (1 P2TR script-path\n// input spending the HTLC refund leaf → 1 P2TR/P2WPKH output). Taproot\n// script-path witness: 64-byte Schnorr sig + refund script + control block.\n// This is protocol-owned knowledge; callers don't parameterise it.\nexport const REFUND_VSIZE = 160;\n\n// Hard upper bound on the per-vbyte fee rate the SDK will sign a refund at.\n// Defense-in-depth: a compromised mempool endpoint can legally return up to\n// 10_000 sat/vB (see mempoolApi.ts `MAX_FEE_RATE`), which on a 160-vbyte\n// refund would burn up to 1.6M sats in miner fees.\n//\n// Sizing: during the April 2024 halving / Runes launch `fastestFee` peaked\n// around 1,800 sat/vB, and `halfHourFee` tracked close to it during the\n// worst of the congestion (~1,000–1,500 sat/vB range — the half-hour\n// bucket converges with the fastest bucket when the queue is deep enough).\n// 2000 leaves ~1.3× margin over that historical extreme so the cap doesn't\n// gate legitimate refunds during a comparable event, while still blocking\n// the obvious malicious case (10_000) by 5×. Small-vault burn is bounded\n// separately by REFUND_MAX_FEE_FRACTION_* below, so the rate cap is free\n// to be set generously here.\nexport const REFUND_MAX_FEE_RATE_SATS_VB = 2000;\n\n// Hard upper bound on the absolute refund fee as a fraction of the vault\n// amount. Protects small vaults where even a moderate fee rate burns a\n// disproportionate share (e.g. on a 100k-sat vault, 500 sat/vB would burn\n// 80%). The fraction cap binds before the rate cap whenever the vault is\n// small. Expressed as numerator/denominator to keep arithmetic in bigint\n// and avoid float-precision drift in the comparison.\nexport const REFUND_MAX_FEE_FRACTION_NUMERATOR = 10n;\nexport const REFUND_MAX_FEE_FRACTION_DENOMINATOR = 100n;\n\n/**\n * Network fee (sats) the SDK will charge for a refund tx at the given\n * sat/vB rate. Mirrors the internal computation in\n * {@link buildAndBroadcastRefund} so callers (e.g. UI fee previews) don't\n * have to duplicate the constant.\n */\nexport function estimateRefundFeeSats(feeRateSatsVb: number): bigint {\n if (!Number.isFinite(feeRateSatsVb) || feeRateSatsVb <= 0) {\n throw new Error(\n `feeRateSatsVb must be a positive finite number, got ${feeRateSatsVb}`,\n );\n }\n return BigInt(Math.ceil(feeRateSatsVb * REFUND_VSIZE));\n}\n// Refund tx has exactly one input — the HTLC output at htlcVout from the\n// Pre-PegIn tx. Used to tell the signer how many sign entries to generate.\n// (Not the taproot leaf index; the leaf is encoded into the PSBT by the\n// WASM PSBT builder based on the refund script path.)\nconst REFUND_INPUT_COUNT = 1;\nconst BIP68_ERROR_RE = /non-BIP68-final/i;\n\nfunction assertBytes32(value: string, label: string): void {\n if (value.length !== 66) {\n throw new Error(\n `${label} must be 32 bytes (66 hex chars with 0x prefix), got length ${value.length}`,\n );\n }\n if (!BYTES32_HEX_RE.test(value)) {\n throw new Error(\n `${label} must contain only hex characters after the 0x prefix`,\n );\n }\n}\n\n/**\n * One vault's per-HTLC binding in a Pre-PegIn batch. Carries the fields\n * needed to reconstruct the WASM `WasmPrePeginTx` template byte-for-byte\n * against the funded transaction.\n */\nexport interface VaultBatchEntry {\n /** SHA-256 hashlock commitment for this vault (bytes32, 0x-prefixed). */\n hashlock: Hex;\n /**\n * Vault deposit (peg-in) amount in satoshis — the on-chain contract's\n * `amount` field. This is the peg-in amount WASM expects in `pegInAmounts`,\n * NOT the funded HTLC output value (which is `amount + depositorClaimValue +\n * minPeginFee`). WASM re-adds that reserve internally when it sizes the HTLC\n * output, so this value is passed straight through.\n */\n amount: bigint;\n /** Index of this vault's HTLC output in the funded Pre-PegIn tx. */\n htlcVout: number;\n}\n\n/**\n * Authoritative vault fields needed to build a refund. Versioning fields,\n * the hashlock, and htlcVout must come from the on-chain contract (never the\n * indexer). The amount + `unsignedPrePeginTxHex` + `depositorBtcPubkey` can\n * come from the indexer since they are not security-critical for signing\n * (the PSBT builder re-derives the HTLC script from on-chain params).\n *\n * `batch` is the full, vout-ordered HTLC vector for the Pre-PegIn (one\n * entry per sibling vault that shares this funded transaction). For a\n * single-vault deposit this is a length-1 array. For batched deposits\n * (e.g. the Aave split) the orchestrator passes every sibling through\n * so the WASM template matches the funded tx's shape.\n */\nexport interface VaultRefundData {\n /**\n * Vault core (tx-graph) version stamped on-chain at registration\n * (`BTCVaultProtocolInfo.vaultCoreVersion`). The refund template must be\n * reconstructed under the same graph version the Pre-PegIn was built with.\n */\n vaultCoreVersion: number;\n hashlock: Hex;\n htlcVout: number;\n offchainParamsVersion: number;\n appVaultKeepersVersion: number;\n universalChallengersVersion: number;\n vaultProvider: Address;\n applicationEntryPoint: Address;\n /** Vault deposit (peg-in) amount in satoshis — the on-chain `amount` field. */\n amount: bigint;\n /**\n * Funded, pre-witness Pre-PegIn transaction hex. 0x prefix optional.\n * The name mirrors the contract/indexer schema; the bytes are the\n * funded form (refund construction needs real outpoints).\n */\n unsignedPrePeginTxHex: string;\n /** Depositor's BTC public key (x-only or compressed hex; 0x prefix optional). */\n depositorBtcPubkey: string;\n /**\n * Full vout-ordered HTLC vector for the funded Pre-PegIn (one entry\n * per sibling vault, including the target vault). Must satisfy\n * `batch[i].htlcVout === i` for all i, and the target's `htlcVout` /\n * `hashlock` / `amount` must equal `batch[vault.htlcVout]`.\n */\n batch: ReadonlyArray<VaultBatchEntry>;\n}\n\n/**\n * Version-resolved protocol context that parameterises the HTLC's taproot\n * scripts. The *signer-set* fields (`vaultKeeperPubkeys`,\n * `universalChallengerPubkeys`) and the version-locked numeric protocol\n * params **must** be sourced from the on-chain contract at the version\n * pinned in {@link VaultRefundData} — this is the trust boundary.\n * `vaultProviderPubkey` today is sourced from the GraphQL indexer via\n * `fetchVaultProviderById`; the caller is responsible for any additional\n * cross-check it requires. Keeper and challenger pubkey arrays must be\n * pre-sorted the same way the Rust protocol sorts them (canonical for\n * script derivation).\n */\nexport interface RefundPrePeginContext {\n vaultProviderPubkey: string;\n vaultKeeperPubkeys: readonly string[];\n universalChallengerPubkeys: readonly string[];\n timelockRefund: number;\n feeRate: bigint;\n minPeginFeeRate: bigint;\n numLocalChallengers: number;\n councilQuorum: number;\n councilSize: number;\n network: Network;\n}\n\n/** Minimum shape required from a broadcast result. */\nexport interface BtcBroadcastResult {\n txId: string;\n}\n\nexport type BtcBroadcaster<R extends BtcBroadcastResult = BtcBroadcastResult> =\n (signedTxHex: string) => Promise<R>;\n\nexport type RefundPsbtSigner = (\n psbtHex: string,\n opts: SignPsbtOptions,\n) => Promise<string>;\n\nexport interface RefundInput<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> {\n vaultId: Hex;\n /**\n * Fetch authoritative on-chain + indexer vault data. The SDK passes no\n * arguments — the caller closes over `vaultId` (or any other context it\n * needs).\n */\n readVault: () => Promise<VaultRefundData>;\n /**\n * Fetch the version-pinned refund context (sorted pubkeys, timelock, etc.)\n * derived from the vault's locked versions.\n */\n readPrePeginContext: (\n vault: VaultRefundData,\n ) => Promise<RefundPrePeginContext>;\n /**\n * Mempool-derived sat/vB fee rate to use for the refund tx (positive\n * number). Caller fetches this before invoking — it does not depend on\n * any value the SDK computes, and folding it into the call keeps the\n * orchestration honest.\n */\n feeRate: number;\n /** BTC wallet signer; receives a PSBT hex + taproot script-path options. */\n signPsbt: RefundPsbtSigner;\n /** Broadcast callback — returns whatever shape the caller needs. */\n broadcastTx: BtcBroadcaster<R>;\n /** Checked at every async boundary. */\n signal?: AbortSignal;\n}\n\nfunction assertNonNegativeInteger(value: number, label: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer, got ${value}`);\n }\n}\n\nfunction validateVaultRefundData(v: VaultRefundData): void {\n assertBytes32(v.hashlock, \"hashlock\");\n if (!Number.isInteger(v.htlcVout) || v.htlcVout < 0) {\n throw new Error(\n `htlcVout must be a non-negative integer, got ${v.htlcVout}`,\n );\n }\n // Batch shape — one entry per sibling HTLC, vout-ordered and\n // contiguous from 0. The reconstructed WASM template uses these\n // arrays directly: any gap, duplicate, or mis-ordering against the\n // funded tx would produce an unspendable refund. The target's\n // (hashlock, amount, htlcVout) must equal the corresponding batch\n // entry so the orchestrator and the caller can't disagree about\n // which output is being refunded.\n if (!Array.isArray(v.batch) || v.batch.length === 0) {\n throw new Error(\"batch must be a non-empty array of HTLC entries\");\n }\n if (v.htlcVout >= v.batch.length) {\n throw new Error(\n `htlcVout ${v.htlcVout} is out of range for batch of size ${v.batch.length}`,\n );\n }\n for (let i = 0; i < v.batch.length; i++) {\n const entry = v.batch[i];\n assertBytes32(entry.hashlock, `batch[${i}].hashlock`);\n if (!Number.isInteger(entry.htlcVout) || entry.htlcVout !== i) {\n throw new Error(\n `batch[${i}].htlcVout must equal ${i} (contiguous vout-ordered vector), got ${entry.htlcVout}`,\n );\n }\n if (typeof entry.amount !== \"bigint\" || entry.amount <= 0n) {\n throw new Error(\n `batch[${i}].amount must be a positive bigint, got ${entry.amount}`,\n );\n }\n }\n const targetEntry = v.batch[v.htlcVout];\n if (targetEntry.hashlock.toLowerCase() !== v.hashlock.toLowerCase()) {\n throw new Error(\n `batch[${v.htlcVout}].hashlock (${targetEntry.hashlock}) does not match target hashlock (${v.hashlock})`,\n );\n }\n if (targetEntry.amount !== v.amount) {\n throw new Error(\n `batch[${v.htlcVout}].amount (${targetEntry.amount}) does not match target amount (${v.amount})`,\n );\n }\n // Version fields flow directly into on-chain script derivation via\n // `readPrePeginContext` — NaN, negative, or non-integer values would\n // silently produce wrong scripts. Guard here as defence in depth even\n // though the caller sources these from bigint on-chain reads.\n assertNonNegativeInteger(v.offchainParamsVersion, \"offchainParamsVersion\");\n assertNonNegativeInteger(v.appVaultKeepersVersion, \"appVaultKeepersVersion\");\n assertNonNegativeInteger(\n v.universalChallengersVersion,\n \"universalChallengersVersion\",\n );\n assertValidVaultCoreVersion(\n v.vaultCoreVersion,\n \"VaultRefundData.vaultCoreVersion\",\n );\n if (\n typeof v.unsignedPrePeginTxHex !== \"string\" ||\n v.unsignedPrePeginTxHex.length === 0\n ) {\n throw new Error(\"unsignedPrePeginTxHex must be a non-empty hex string\");\n }\n if (!BTC_HEX_BYTES_RE.test(v.unsignedPrePeginTxHex)) {\n throw new Error(\n \"unsignedPrePeginTxHex must be a hex byte string (optional 0x prefix, even length)\",\n );\n }\n if (!v.depositorBtcPubkey || !PUBKEY_HEX_RE.test(v.depositorBtcPubkey)) {\n throw new Error(\n \"depositorBtcPubkey must be 32 or 33 bytes of hex (optional 0x prefix)\",\n );\n }\n if (typeof v.amount !== \"bigint\" || v.amount <= 0n) {\n throw new Error(`amount must be a positive bigint, got ${v.amount}`);\n }\n}\n\nfunction validateRefundPrePeginContext(c: RefundPrePeginContext): void {\n if (!c.vaultProviderPubkey || !PUBKEY_HEX_RE.test(c.vaultProviderPubkey)) {\n throw new Error(\"vaultProviderPubkey must be 32 or 33 bytes of hex\");\n }\n if (c.vaultKeeperPubkeys.length === 0) {\n throw new Error(\"vaultKeeperPubkeys must be non-empty\");\n }\n if (c.universalChallengerPubkeys.length === 0) {\n throw new Error(\"universalChallengerPubkeys must be non-empty\");\n }\n if (!Number.isInteger(c.timelockRefund) || c.timelockRefund <= 0) {\n throw new Error(\n `timelockRefund must be a positive integer, got ${c.timelockRefund}`,\n );\n }\n if (typeof c.feeRate !== \"bigint\" || c.feeRate <= 0n) {\n throw new Error(\n `protocol feeRate must be a positive bigint, got ${c.feeRate}`,\n );\n }\n if (typeof c.minPeginFeeRate !== \"bigint\" || c.minPeginFeeRate <= 0n) {\n throw new Error(\n `minPeginFeeRate must be a positive bigint, got ${c.minPeginFeeRate}`,\n );\n }\n if (!Number.isInteger(c.numLocalChallengers) || c.numLocalChallengers < 0) {\n throw new Error(\"numLocalChallengers must be a non-negative integer\");\n }\n if (\n !Number.isInteger(c.councilQuorum) ||\n !Number.isInteger(c.councilSize) ||\n c.councilQuorum <= 0 ||\n c.councilSize <= 0 ||\n c.councilQuorum > c.councilSize\n ) {\n throw new Error(\n `councilQuorum (${c.councilQuorum}) must be in [1, councilSize=${c.councilSize}]`,\n );\n }\n}\n\n/**\n * Build, sign, and broadcast a refund transaction for an expired vault.\n *\n * Trust boundary: `readVault` must source the hashlock, htlcVout, and\n * versioning fields from the on-chain contract — an indexer-only path\n * leaves the refund flow open to signer-set substitution. The SDK does\n * not enforce this; it is the caller's responsibility.\n *\n * The broadcast transport is expected to surface Bitcoin's `non-BIP68-final`\n * policy rejection as an `Error` whose message contains that string; when\n * it does, the SDK wraps it in {@link BIP68NotMatureError}. All other\n * transport errors propagate unchanged.\n *\n * @returns whatever the injected `broadcastTx` returns (generic pass-through)\n * @throws `Error` if any validation fails\n * @throws {@link BIP68NotMatureError} if the broadcast is rejected because\n * the refund CSV timelock has not yet matured\n * @throws anything `readVault`, `readPrePeginContext`,\n * `signPsbt`, or `broadcastTx` throws\n */\nexport async function buildAndBroadcastRefund<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n>(input: RefundInput<R>): Promise<R> {\n const {\n vaultId,\n readVault,\n readPrePeginContext,\n feeRate,\n signPsbt,\n broadcastTx,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n assertBytes32(vaultId, \"vaultId\");\n\n const vault = await readVault();\n validateVaultRefundData(vault);\n signal?.throwIfAborted();\n\n const ctx = await readPrePeginContext(vault);\n validateRefundPrePeginContext(ctx);\n signal?.throwIfAborted();\n\n if (!Number.isFinite(feeRate) || feeRate <= 0) {\n throw new Error(`feeRate must be a positive number, got ${feeRate}`);\n }\n // Rate cap: fail closed before PSBT construction if the seeded value\n // exceeds the safety ceiling. A compromised mempool API (or upstream\n // proxy / BGP hijack) can otherwise drive `halfHourFee` to the API's\n // 10_000 sat/vB ceiling and burn the refund as miner fee.\n if (feeRate > REFUND_MAX_FEE_RATE_SATS_VB) {\n throw new Error(\n `feeRate ${feeRate} sat/vB exceeds refund safety cap ` +\n `${REFUND_MAX_FEE_RATE_SATS_VB} sat/vB; refusing to sign refund.`,\n );\n }\n const refundFee = BigInt(Math.ceil(feeRate * REFUND_VSIZE));\n // Fraction cap: even within the rate ceiling, refuse to sign if the\n // absolute fee would consume more than the configured percentage of the\n // vault amount. Protects small vaults from disproportionate burn.\n const maxFeeByFraction =\n (vault.amount * REFUND_MAX_FEE_FRACTION_NUMERATOR) /\n REFUND_MAX_FEE_FRACTION_DENOMINATOR;\n if (refundFee > maxFeeByFraction) {\n throw new Error(\n `Refund fee ${refundFee} sats exceeds the per-vault safety cap ` +\n `of ${maxFeeByFraction} sats ` +\n `(${REFUND_MAX_FEE_FRACTION_NUMERATOR}/${REFUND_MAX_FEE_FRACTION_DENOMINATOR} ` +\n `of vault.amount=${vault.amount}); refusing to sign refund.`,\n );\n }\n signal?.throwIfAborted();\n\n // `vault.depositorBtcPubkey` may arrive as wallet-native compressed sec1\n // (33 bytes) because the caller fetches it live from the wallet for\n // signing. WASM script derivation wants x-only (32 bytes), so normalize\n // here; the raw form is kept for the wallet sign call below.\n const xOnlyDepositorPubkey = processPublicKeyToXOnly(\n vault.depositorBtcPubkey,\n );\n\n const cleanFundedPrePeginTxHex = stripHexPrefix(vault.unsignedPrePeginTxHex);\n\n // Production peg-ins (PeginManager) commit an OP_RETURN <PUSH32\n // SHA256(authAnchor)> output at `vout = hashlocks.length`. The\n // reconstructed unfunded template carries `batch.length` HTLC outputs,\n // so the OP_RETURN — when present — must sit at exactly that vout.\n // Legacy non-auth-anchored Pre-PegIns return `undefined` from the\n // finder; the template then has no OP_RETURN either, which is a\n // matching configuration.\n const found = findAuthAnchorOpReturn(cleanFundedPrePeginTxHex);\n if (found !== undefined && found.vout !== vault.batch.length) {\n throw new Error(\n `Auth-anchor OP_RETURN at vout ${found.vout} does not match batch size ` +\n `(${vault.batch.length} HTLC outputs expect the anchor at vout ${vault.batch.length}). ` +\n `Refund refused — sibling HTLC vector is incomplete.`,\n );\n }\n const authAnchorHash = found?.hash;\n\n // Independent structural check on the funded tx: it must carry at\n // least N HTLC outputs (one per batch entry). If the anchor is\n // present we've already pinned its position above, which transitively\n // proves the tx has ≥ N+1 outputs; if the anchor is absent (legacy)\n // we still need ≥ N to spend `htlcVout = N-1`.\n let parsedFundedTx: Transaction;\n try {\n parsedFundedTx = Transaction.fromHex(cleanFundedPrePeginTxHex);\n } catch (e) {\n throw new Error(\n `Failed to parse funded Pre-PegIn transaction hex: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n if (parsedFundedTx.outs.length < vault.batch.length) {\n throw new Error(\n `Funded Pre-PegIn tx has ${parsedFundedTx.outs.length} outputs but batch ` +\n `requires at least ${vault.batch.length} HTLC outputs. ` +\n `Refund refused — funded tx shape disagrees with sibling vector.`,\n );\n }\n\n const { psbtHex } = await buildRefundPsbt({\n prePeginParams: {\n vaultCoreVersion: vault.vaultCoreVersion,\n depositorPubkey: xOnlyDepositorPubkey,\n vaultProviderPubkey: stripHexPrefix(ctx.vaultProviderPubkey),\n vaultKeeperPubkeys: ctx.vaultKeeperPubkeys.map(stripHexPrefix),\n universalChallengerPubkeys:\n ctx.universalChallengerPubkeys.map(stripHexPrefix),\n hashlocks: vault.batch.map((b) => stripHexPrefix(b.hashlock)),\n timelockRefund: ctx.timelockRefund,\n // `batch[i].amount` is the on-chain vault deposit (peg-in) amount, which\n // is exactly what WASM's `pegInAmounts` expects — it re-adds the protocol\n // reserve (`depositorClaimValue + minPeginFee`) internally when sizing the\n // HTLC output. `buildRefundPsbt`'s value cross-check then binds the result\n // to the funded tx bytes, refusing the refund if the template's HTLC value\n // disagrees with the on-chain commitment.\n pegInAmounts: vault.batch.map((b) => b.amount),\n feeRate: ctx.feeRate,\n minPeginFeeRate: ctx.minPeginFeeRate,\n numLocalChallengers: ctx.numLocalChallengers,\n councilQuorum: ctx.councilQuorum,\n councilSize: ctx.councilSize,\n network: ctx.network,\n authAnchorHash,\n },\n fundedPrePeginTxHex: cleanFundedPrePeginTxHex,\n htlcVout: vault.htlcVout,\n refundFee,\n // buildRefundPsbt's top-level `hashlock` param is documented as \"no 0x\n // prefix\" and flows into the WASM HTLC connector derivation; a prefixed\n // value would derive the wrong refund script leaf and yield an\n // unspendable PSBT. Match the `hashlocks` array handling above.\n hashlock: stripHexPrefix(vault.hashlock),\n });\n signal?.throwIfAborted();\n\n const signOptions = createTaprootScriptPathSignOptions(\n vault.depositorBtcPubkey,\n REFUND_INPUT_COUNT,\n );\n const signedPsbtHex = await signPsbt(psbtHex, signOptions);\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // Critical Path #7: verify the depositor's script-path signature against a\n // sighash recomputed from the PSBT we built before finalizing and broadcasting.\n // The refund spends a single input (the HTLC output) on input 0.\n const REFUND_SIGNED_INPUT_INDEX = 0;\n const refundSignature = extractPayoutSignature(\n signedPsbtHex,\n xOnlyDepositorPubkey,\n REFUND_SIGNED_INPUT_INDEX,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtHex,\n signatureHex: refundSignature,\n signerXOnlyPubkeyHex: xOnlyDepositorPubkey,\n inputIndex: REFUND_SIGNED_INPUT_INDEX,\n });\n\n // Finalize the PSBT we built, not the one the wallet returned: only the\n // verified signature above crosses over. See the primitive's header for what\n // finalizing the wallet's copy would let through.\n const signedTxHex = finalizeScriptPathWithSignatures({\n requestedPsbtHex: psbtHex,\n signaturesHex: [refundSignature],\n signerXOnlyPubkeyHex: xOnlyDepositorPubkey,\n });\n signal?.throwIfAborted();\n\n try {\n return await broadcastTx(signedTxHex);\n } catch (error) {\n if (error instanceof Error && BIP68_ERROR_RE.test(error.message)) {\n throw new BIP68NotMatureError(vaultId, error);\n }\n throw error;\n }\n}\n","/**\n * Domain errors thrown by the reclaim service.\n *\n * @module services/reclaim/errors\n */\n\n/**\n * Thrown when the fee would consume too much of the swept reserve — either the\n * per-vbyte rate exceeds the safety ceiling, or the absolute fee exceeds the\n * fraction cap.\n *\n * Distinct from a generic error because the caller's response differs: nothing\n * is at risk and nothing expires. The reserve simply stays where it is until\n * fee rates fall, and the UI should say so rather than presenting a failure.\n */\nexport class ReclaimUneconomicalError extends Error {\n /** Fee the reclaim would have paid, in satoshis. */\n public readonly feeSats: bigint;\n /** Sum of the reserves the reclaim would have swept, in satoshis. */\n public readonly sweptTotalSats: bigint;\n\n constructor(message: string, feeSats: bigint, sweptTotalSats: bigint) {\n super(message);\n this.name = \"ReclaimUneconomicalError\";\n this.feeSats = feeSats;\n this.sweptTotalSats = sweptTotalSats;\n }\n}\n","/**\n * Reclaim orchestration — sweep the depositor-claim reserve (PegIn vout 1)\n * back to the depositor after their vault has terminally settled.\n *\n * The SDK owns the sequence: read → vault-id bind → fee caps → PSBT build →\n * sign → verify → finalize → broadcast. Interactive transports (`signPsbt`,\n * `broadcastTx`) and the data read stay as injected callbacks, so the caller\n * keeps its transport choice and error decoding. Mirrors\n * `services/refund/buildAndBroadcastRefund`.\n *\n * > **Eligibility is the caller's job, and it is a safety control.** The claim\n * > leaf carries no timelock, so this service will sweep a reserve whose vault\n * > is still live. Doing that permanently voids the depositor's pre-signed\n * > recourse graph: their `claim_tx` hard-codes `OutPoint(peginTxid, 1)` as its\n * > only input, and every downstream Assert / Payout / NoPayout signature\n * > commits to that tx's txid. There is no re-funding path. The caller must\n * > confirm the vault's Payout has landed — PegIn vout 0 spent and deeply\n * > confirmed — before calling this.\n *\n * @module services/reclaim\n */\n\nimport type { Address, Hex } from \"viem\";\n\nimport type { SignPsbtOptions } from \"../../../../shared/wallets/interfaces/BitcoinWallet\";\nimport { assertPsbtUnsignedTxMatches } from \"../../primitives/psbt/assertPsbtUnsignedTxMatches\";\nimport { finalizeScriptPathWithSignatures } from \"../../primitives/psbt/finalizeScriptPathWithSignatures\";\nimport { extractPayoutSignature } from \"../../primitives/psbt/payout\";\nimport {\n buildReclaimPsbt,\n estimateReclaimFeeSats,\n type ReclaimReserve,\n} from \"../../primitives/psbt/reclaim\";\nimport { assertScriptPathSchnorrSignature } from \"../../primitives/psbt/verifyScriptPathSchnorrSignature\";\nimport {\n ensureHexPrefix,\n processPublicKeyToXOnly,\n stripHexPrefix,\n} from \"../../primitives/utils/bitcoin\";\nimport { createTaprootScriptPathSignOptions } from \"../../utils/signing\";\nimport { deriveVaultId } from \"../../wasm\";\nimport { calculateBtcTxHash } from \"../../utils/transaction/btcTxHash\";\n// The BTC broadcast transport types are shared with the refund service rather\n// than redeclared — `services/index.ts` re-exports both modules flat, so a\n// second declaration would collide on the barrel.\nimport type {\n BtcBroadcastResult,\n BtcBroadcaster,\n} from \"../refund/buildAndBroadcastRefund\";\n\nimport { ReclaimUneconomicalError } from \"./errors\";\n\n/**\n * Hard upper bound on the per-vbyte fee rate the SDK will sign a reclaim at.\n * Same reasoning and value as the refund's cap: a compromised mempool endpoint\n * can legally return up to 10,000 sat/vB, and 2000 leaves margin over the\n * worst historical `halfHourFee` while still blocking that by 5×.\n */\nexport const RECLAIM_MAX_FEE_RATE_SATS_VB = 2000;\n\n/**\n * Hard upper bound on the absolute fee as a fraction of the **swept total**.\n *\n * > ⚠️ The basis here is deliberately different from the refund's. There the\n * > basis is `vault.amount` and the swept amount is far larger, so a 10% cap is\n * > generous. Here the basis *is* the swept amount — roughly 33k sats — and a\n * > 10% cap would block every reclaim above about 25 sat/vB. Do not \"fix\" this\n * > into symmetry with `REFUND_MAX_FEE_FRACTION_*`; it would silently change\n * > behaviour. The matching comment lives at the UI cap site.\n */\nexport const RECLAIM_MAX_FEE_FRACTION_NUMERATOR = 25n;\nexport const RECLAIM_MAX_FEE_FRACTION_DENOMINATOR = 100n;\n\n/**\n * Fraction of the swept total above which the UI warns but still allows the\n * reclaim. Exported so the review screen derives its threshold from the same\n * constant the SDK enforces against, rather than restating it.\n */\nexport const RECLAIM_WARN_FEE_FRACTION_NUMERATOR = 10n;\n\nexport type ReclaimPsbtSigner = (\n psbtHex: string,\n opts: SignPsbtOptions,\n) => Promise<string>;\n\n/** One reserve to sweep, as the caller resolves it. */\nexport interface ReclaimVaultData {\n /**\n * The contract's own copy of the depositor-signed PegIn transaction\n * (`VaultProtocolInfo.depositorSignedPeginTx`). Must come from the chain —\n * never the indexer. Its `outs[1]` is one leg of the three-way bind.\n */\n depositorSignedPeginTxHex: string;\n /**\n * Independent chain observation of `peginTxid:1`, including the outpoint the\n * lookup was issued against — see `ReclaimReserve.observed` for why the\n * script and value alone cannot identify a reserve.\n */\n observed: {\n txid: string;\n vout: number;\n scriptPubKey: string;\n value: bigint;\n };\n /** This vault's reserve value, recomputed via `computeMinClaimValue`. */\n expectedClaimValue: bigint;\n}\n\nexport interface ReclaimInput<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n> {\n vaultIds: Hex[];\n /**\n * The depositor's Ethereum address — the second preimage of every vault id.\n * Used to re-derive each requested id from the PegIn bytes `readVaults`\n * returned, so a reserve can be tied to the vault it was asked for.\n */\n depositorEthAddress: Address;\n /**\n * The **connected wallet's live** BTC pubkey — compressed sec1 or x-only.\n * Never the indexer's `depositorBtcPubkey`: re-deriving the claim script\n * from the live key is what proves the wallet about to sign is the wallet\n * that can spend.\n */\n depositorBtcPubkey: string;\n /**\n * Resolve the reserves to sweep, in the same order as `vaultIds`. The SDK\n * passes no arguments — the caller closes over whatever context it needs.\n */\n readVaults: () => Promise<ReclaimVaultData[]>;\n /** Mempool-derived sat/vB fee rate. Caller fetches it before invoking. */\n feeRate: number;\n /** BTC wallet signer; receives a PSBT hex + taproot script-path options. */\n signPsbt: ReclaimPsbtSigner;\n /** Broadcast callback — returns whatever shape the caller needs. */\n broadcastTx: BtcBroadcaster<R>;\n /** Checked at every async boundary. */\n signal?: AbortSignal;\n}\n\n/**\n * Assert every reserve belongs to the vault it was requested for.\n *\n * The PSBT builder's binds all compare a script or a value, and both repeat\n * across every vault a depositor owns under the same protocol parameters — so\n * none of them can tell one of their vaults from another. Re-deriving the\n * vault id from the PegIn bytes closes that: it is the same\n * `keccak256(abi.encode(peginTxHash, depositor))` the registry keys on.\n *\n * This binds the reserve to the id that was asked for. It cannot detect an id\n * that was wrong to begin with — if the caller resolved the wrong vault id\n * upstream, every read follows that vault consistently and this agrees.\n */\nasync function assertReservesMatchVaultIds(\n vaults: readonly ReclaimVaultData[],\n vaultIds: readonly Hex[],\n depositorEthAddress: Address,\n): Promise<void> {\n for (let i = 0; i < vaults.length; i++) {\n const peginTxHash = calculateBtcTxHash(vaults[i].depositorSignedPeginTxHex);\n const derivedVaultId = ensureHexPrefix(\n await deriveVaultId(\n stripHexPrefix(peginTxHash),\n stripHexPrefix(depositorEthAddress),\n ),\n ).toLowerCase();\n\n if (derivedVaultId !== vaultIds[i].toLowerCase()) {\n throw new Error(\n `Reserve ${i} belongs to vault ${derivedVaultId}, not the requested ` +\n `${vaultIds[i].toLowerCase()}. Refusing to sweep a reserve whose ` +\n `vault is not the one the caller asked for — the reserve funds that ` +\n `vault's claim transaction and cannot be replaced.`,\n );\n }\n }\n}\n\n/**\n * Build, sign, and broadcast a reclaim transaction sweeping one or more\n * depositor-claim reserves back to the depositor's BIP-86 address.\n *\n * @returns whatever the injected `broadcastTx` returns (generic pass-through)\n * @throws {@link ReclaimUneconomicalError} if the fee breaches either cap\n * @throws `Error` if any validation or script/value bind fails\n * @throws anything `readVaults`, `signPsbt`, or `broadcastTx` throws\n */\nexport async function buildAndBroadcastReclaim<\n R extends BtcBroadcastResult = BtcBroadcastResult,\n>(input: ReclaimInput<R>): Promise<R> {\n const {\n vaultIds,\n depositorEthAddress,\n depositorBtcPubkey,\n readVaults,\n feeRate,\n signPsbt,\n broadcastTx,\n signal,\n } = input;\n\n signal?.throwIfAborted();\n\n if (vaultIds.length === 0) {\n throw new Error(\"Reclaim requires at least one vault id; got none.\");\n }\n if (!Number.isFinite(feeRate) || feeRate <= 0) {\n throw new Error(`feeRate must be a positive number, got ${feeRate}`);\n }\n // Rate cap first: fail closed before any PSBT construction or wallet prompt.\n if (feeRate > RECLAIM_MAX_FEE_RATE_SATS_VB) {\n throw new ReclaimUneconomicalError(\n `Fee rate ${feeRate} sat/vB exceeds the reclaim safety cap of ` +\n `${RECLAIM_MAX_FEE_RATE_SATS_VB} sat/vB; refusing to sign.`,\n 0n,\n 0n,\n );\n }\n\n const vaults = await readVaults();\n signal?.throwIfAborted();\n\n if (vaults.length !== vaultIds.length) {\n throw new Error(\n `readVaults returned ${vaults.length} reserves for ${vaultIds.length} ` +\n `vault id(s); refusing to sweep a set that does not match the request.`,\n );\n }\n\n // Cardinality alone proves nothing about *which* vaults came back, and\n // `readVaults` is documented as ordered but never checked. Derive each id\n // back from the PegIn bytes so an out-of-order, substituted or otherwise\n // mismatched set is rejected before any of it reaches a PSBT.\n await assertReservesMatchVaultIds(vaults, vaultIds, depositorEthAddress);\n signal?.throwIfAborted();\n\n // x-only for script derivation and signature verification; the raw form is\n // kept for the wallet's own sign call below.\n const xOnlyDepositorPubkey = processPublicKeyToXOnly(depositorBtcPubkey);\n\n const reserves: ReclaimReserve[] = vaults.map((vault) => ({\n depositorSignedPeginTxHex: vault.depositorSignedPeginTxHex,\n observed: vault.observed,\n expectedValue: vault.expectedClaimValue,\n }));\n\n const feeSats = estimateReclaimFeeSats(feeRate, reserves.length);\n\n // Fraction cap: bound the burn against the swept total. See the constant's\n // note on why this basis differs from the refund's.\n const sweptTotal = reserves.reduce((sum, r) => sum + r.expectedValue, 0n);\n const maxFeeByFraction =\n (sweptTotal * RECLAIM_MAX_FEE_FRACTION_NUMERATOR) /\n RECLAIM_MAX_FEE_FRACTION_DENOMINATOR;\n if (feeSats > maxFeeByFraction) {\n throw new ReclaimUneconomicalError(\n `Reclaim fee ${feeSats} sats exceeds ` +\n `${RECLAIM_MAX_FEE_FRACTION_NUMERATOR}/${RECLAIM_MAX_FEE_FRACTION_DENOMINATOR} ` +\n `of the swept total (${sweptTotal} sats, cap ${maxFeeByFraction} sats). ` +\n `The reserve is not at risk — it stays where it is until fee rates fall.`,\n feeSats,\n sweptTotal,\n );\n }\n signal?.throwIfAborted();\n\n // Builds the three-way bind: contract PegIn bytes × chain observation × JS\n // re-derivation from the live wallet key. Throws on any disagreement.\n const { psbtHex } = buildReclaimPsbt({\n depositorPubkey: xOnlyDepositorPubkey,\n inputs: reserves,\n feeSats,\n });\n signal?.throwIfAborted();\n\n const signOptions = createTaprootScriptPathSignOptions(\n depositorBtcPubkey,\n reserves.length,\n );\n const signedPsbtHex = await signPsbt(psbtHex, signOptions);\n\n assertPsbtUnsignedTxMatches({\n requestedPsbtHex: psbtHex,\n returnedPsbtHex: signedPsbtHex,\n });\n\n // CLAUDE.md critical path #8: `useTweakedSigner: false` / `autoFinalized:\n // false` means wallet success is not evidence of a valid signature. Verify\n // every input against a sighash recomputed from the PSBT we built, before\n // finalizing — not just input 0, or a batch could broadcast with one good\n // signature and the rest garbage.\n const signatures = reserves.map((_reserve, inputIndex) => {\n const signature = extractPayoutSignature(\n signedPsbtHex,\n xOnlyDepositorPubkey,\n inputIndex,\n );\n assertScriptPathSchnorrSignature({\n requestedPsbtHex: psbtHex,\n signatureHex: signature,\n signerXOnlyPubkeyHex: xOnlyDepositorPubkey,\n inputIndex,\n });\n return signature;\n });\n\n // Finalize the PSBT we built, not the one the wallet returned: only the\n // verified signatures above cross over. See the primitive's header for what\n // finalizing the wallet's copy would let through.\n const signedTxHex = finalizeScriptPathWithSignatures({\n requestedPsbtHex: psbtHex,\n signaturesHex: signatures,\n signerXOnlyPubkeyHex: xOnlyDepositorPubkey,\n });\n signal?.throwIfAborted();\n\n return broadcastTx(signedTxHex);\n}\n"],"names":["BYTES32_HEX_RE","ADDRESS_HEX_RE","ETH_HEX_BYTES_RE","assertBytes32","value","label","assertAddress","assertHexBytes","validateActivationInputs","input","normalizedSecret","ensureHexPrefix","validateSecretAgainstHashlock","activateVault","btcVaultRegistryAddress","vaultId","hashlock","activationMetadata","writeContract","signal","BTCVaultRegistryABI","activateVaultAndRedeem","claimExpiredVault","AssertBindingError","message","options","parse","psbtBase64","Psbt","cause","inputHash","psbt","inputIndex","unsignedTxHash","Transaction","assertInputSpends","expectedHash","expectedVout","target","assertDeclaresClaimOutput","assert","claim","witnessUtxo","_a","ASSERT_CLAIM_INPUT_INDEX","claimOutput","CLAIM_CONNECTOR_OUTPUT_INDEX","assertAssertBindsClaimAndPayout","params","payout","PAYOUT_ASSERT_INPUT_INDEX","ASSERT_PAYOUT_OUTPUT_INDEX","PAYOUT_PEGIN_INPUT_INDEX","CLAIM_PEGIN_INPUT_INDEX","PEGIN_VAULT_OUTPUT_INDEX","assertDeclaredPrevouts","peginValueSats","assertOutput","pins","expectedSats","source","assertInput","assertPayoutFeeAndTimelocks","vault","feeBandParams","assertPayoutFeeBandDomain","payoutClaimer","payoutDepositor","assertPsbt","DEPOSITOR_PAYOUT_INPUT_COUNT","peginSequence","assertSequence","inputValueSats","outs","NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT","outputValueSats","out","assertPayoutFeeInBand","PAYOUT_DESTINATION_OUTPUT_INDEX","ChallengerSetMismatchError","missing","unexpected","assertChallengerSetMatchesVault","local","deriveLocalChallengers","universal","normalizeKey","overlap","key","supplied","suppliedSet","expected","expectedSet","pubkey","stripHexPrefix","PayoutDestinationError","expectedScriptHex","actualScriptHex","assertPayoutPaysRegisteredScript","version","locktime","readPayoutTx","PAYOUT_TX_VERSION","PAYOUT_TX_LOCKTIME","actual","anchor","PAYOUT_ANCHOR_DUST_SATS","expectedAnchor","deriveP2trScript","actualAnchor","Buffer","PayoutInputLeafError","copyAssertConnectorLeaf","depositor","claimer","depositorTx","claimerTx","from","to","VaultIdBindingError","expectedVaultId","derivedVaultId","peginTxid","peginTxidFromClaimPsbt","claimPsbtBase64","assertSpendsDepositorClaimOutput","displayTxid","peginTxidFromClaimTx","claimTx","inputCount","vout","PEGIN_DEPOSITOR_CLAIM_VOUT","assertClaimSpendsVault","derived","normalizeVaultId","derivePeginVaultId","internalHash","normalizeVerifyingKeyHex","body","assertVerifyingKeyIsTrusted","served","trusted","DELEGATED_CLAIM_TX_GRAPH_VERSION","ArtifactsVaultMismatchError","actualVaultId","HEX_BYTES","readBabeSessions","sessions","requireRecord","challengerPubkeys","challengerPubkey","session","artifactsHex","summarizeWatchtowerArtifacts","artifactsJson","parsed","requireString","parseClaimTx","proverCircuitVersion","requireSafeInteger","claimableEventBlockNumber","assertArtifactsUsableForVault","summary","resolved","fileVerifyingKey","trustedVerifyingKey","verifyWatchtowerArtifacts","txHex","field","wronglyChallengedRequestId","gcIndex","buildDelegatedClaimSigningRequests","psbts","requests","WRONGLY_CHALLENGED_INPUT_INDEX","UNJOINED_BABE_SESSIONS_JSON","COMPRESSED_PREFIX_HEX_LEN","COMPRESSED_PUBKEY_HEX_LEN","X_ONLY_PUBKEY_HEX_LEN","xOnlyHex","publicKeyHex","hex","assertWalletMatchesDepositor","btcWallet","depositorPublicKey","btcNetwork","walletPublicKey","signerAddress","isAddressFromPublicKey","buildBoundPsbtSet","txGraphVersion","graphJson","depositorXOnly","assertPayoutScriptMatchesPopKey","wronglyChallenged","payoutDepositorRaw","buildClaimPsbt","buildAssertClaimerPsbt","buildPayoutClaimerPsbt","buildWronglyChallengedPsbts","buildPayoutDepositorPsbt","planDelegatedClaimSigning","assertSignatureForRequest","plan","request","signatureHex","assertScriptPathSchnorrSignature","psbtBase64ToHex","psbtHexToBase64","psbtHex","SigningPlanMismatchError","assertPlanMatchesRebuilt","given","rebuilt","i","assertSignaturesCoverRequests","signatures","requestIds","id","extra","quoted","ids","requireSignature","assembleWatchtowerArtifactsFromSignatures","trustedVerifyingKeyHex","wronglyChallengedSigs","_","signedClaimTxHex","finalizeClaimTx","buildWatchtowerArtifacts","supportsDelegatedClaimSigning","wallet","DelegatedClaimSigningIncompleteError","failedRequestId","KIND_DEVICE_STATE","STANDALONE_ORDER","signDelegatedClaimPlan","opts","assertRequestIdsUnique","supportsDepositApproval","ceremony","requireApprovalInputs","assertTermsMatchVault","partition","partitionByDeviceState","signWithApprovalWallet","signWithBatch","seen","intentBound","r","standalone","kind","classified","assertEveryRequestSigned","contextKey","uint8ArrayToHex","terms","norm","keys","k","vaultProvider","group","verifiedResumable","queue","position","stop","signOne","signedHex","createTaprootScriptPathSignOptionsForInput","extractTapScriptSig","releaseIntent","phase","deriveVaultRoot","next","where","resume","signedPsbtHexes","signPsbtsWithFallback","assembleWatchtowerArtifacts","deriveClaimerWotsKeypair","root","wotsSeed","expandWotsSeed","derivation","wotsKeypairFromSeed","assertMatchesOnChainHash","validateWotsKeypairAgainstGraph","onChain","normalizeHash","hash","resolveOne","role","rosterEntry","rawOperationKey","genesisBtcPubkey","assertOnChainBtcPubkey","operationBtcPubkey","assertDistinctOperationKeys","participants","participant","previousOwner","finalize","query","raw","resolvedAt","vaultKeepers","keeper","universalChallengers","challenger","p","resolveCurrentParticipantKeys","resolveParticipantKeysAtEpochs","readStampedParticipantKeys","basic","protocol","registryReader","vaultKeeperReader","universalChallengerReader","operationKeyReader","vaultProviderGenesisBtcPubkey","epochs","readDelegatedClaimVaultContext","readers","depositorBtcPubkey","OnChainBtcVaultStatus","VaultClaimableByNotFoundError","participantKeys","records","claimableEvent","offchainParams","error","isVaultClaimableByNotFoundError","timelockPegin","deriveTimelockPegin","record","findRegistrationRecord","assertRegisteredPayoutScriptBounds","recordPrePeginTxHash","registrationPrePeginTxHash","peginTxHash","calculateBtcTxHash","logged","axis","peginTx","spentTxid","spentVout","peginVaultOutput","PEGIN_ETH_CONFIRMATIONS","REGISTRATION_DEPTH_POLL_INTERVAL_MS","REGISTRATION_DEPTH_TIMEOUT_MS","REGISTRATION_ABSENT_GRACE_POLLS","PeginRegistrationMissingError","PeginRegistrationNotFinalError","isPeginRegistrationMissingError","err","isPeginRegistrationNotFinalError","computeRegistrationConfirmations","currentBlock","createdAtBlock","depth","waitForPeginRegistrationDepth","vaultRegistryReader","getBlockNumber","vaultIds","required","pollIntervalMs","timeoutMs","onProgress","startTime","hasObservedRegistration","absentPolls","lastError","infos","missingIndex","info","zeroAddress","shallowest","shallowestDepth","resolve","reject","onAbort","timeoutId","SERDE_HEX_RE","BYTES32_SERDE_HEX_RE","OUTPOINT_RE","INT32_MIN","INT32_MAX","UINT32_MAX","DEPOSITOR_CLAIM_TX_VERSION","DEPOSITOR_CLAIM_OUTPUT_COUNT","CLAIM_ASSERT_VOUT","GraphFingerprintError","node","name","path","asArray","asRecord","asInt","min","max","asSerdeHex","asSerdeBytes32","serializeGraphTx","txNode","tx","at","inputNode","outpoint","match","item","j","output","outputNode","decodePresignTx","readInputOutpoint","assertClaimHasAssertOutput","assertClaimSpendsPegin","spent","assertAssertSpendsClaim","assertTx","claimTxid","decodePresignClaimAssertTransactions","args","assertPresignAssertSpendsClaim","assertPresignClaimAssertLinkage","assertDepositorClaimShape","canonicalTxSetFingerprint","set","sorted","a","b","parts","hexToBytes","bytesToHex","sha256","concatBytes","fingerprintPresignTxSet","bytes","c","canonicalizeBtcPubkey","fingerprintReturnedGraph","graph","subgraphs","sub","h","declaredChallengers","pubkeys","declared","assertReturnedGraphMatchesFingerprint","expectedFingerprint","fingerprinted","DEPOSITOR_PATH_UNUSED_COMMISSION_BPS","assertChallengerSetMatchesExpected","challengerPresignData","localChallengers","universalChallengerBtcPubkeys","suppliedList","getSortedXOnlyPubkeys","assertAssertChallengerOutputCount","challengerCount","vaultCoreVersion","markerOutputs","ASSERT_MARKER_FIRST_VAULT_CORE_VERSION","ASSERT_NON_CHALLENGER_OUTPUT_COUNT","CHALLENGE_ASSERT_CONNECTORS_PER_CHALLENGER","assertDepositorGraphNoPayoutsCanonical","depositorGraph","ctx","sortedChallengers","claimerPubkey","noPayouts","challengerIndex","prevouts","assertNoPayoutParentsCanonical","assertNoPayoutOutputMatchesChallenger","noPayoutTx","challengeAssertXTx","challengeAssertYTx","outputConnectorScriptPubKey","getChallengeAssertOutputScriptPubKey","half","challengeAssertTx","assertChallengeAssertIsCanonical","parentOutputs","CHALLENGE_ASSERT_OUTPUT_CONNECTOR_INDEX","assertCanonicalNoPayoutShape","collectDepositorGraphPsbts","psbtHexes","signOptions","challengerEntries","builtPayout","buildPayoutPsbt","createTaprootScriptPathSignOptions","DEPOSITOR_SIGNED_INPUT_COUNT","noPayoutIdx","noPayoutHex","buildNoPayoutPsbt","extractDepositorGraphSignatures","psbtPairs","depositorPubkey","assertPsbtUnsignedTxMatches","payoutSignature","extractPayoutSignature","perChallenger","entry","nopayoutSignature","signDepositorGraph","signingContext","validateWalletPubkey","requestedPsbtHex","DEFAULT_POLL_INTERVAL_MS","VAULT_ID_LOG_PREFIX_LEN","waitForPeginStatus","statusReader","targetStatuses","response","vaultIdKey","status","DaemonStatus","VP_TERMINAL_FAILURE_STATUSES","JsonRpcError","RpcErrorCode","MAX_POLLING_TIMEOUT_MS","POST_PAYOUT_STATUSES","TARGET_STATUS","prepareTransactionsForSigning","claimerTransactions","processPublicKeyToXOnly","normalizeClaimerPubkey","assertDepositTermsMatchSigningContext","context","refuse","scalars","fromTerms","fromContext","canonical","sameSet","x","y","contextVp","approvedVps","v","assertNonDepositorClaimerSetMatches","suppliedTxs","expectedVpPubkey","expectedVkPubkeys","depositorPubkeyXOnly","expectedList","suppliedAll","suppliedNonDepositor","buildPayoutSigningInput","signPayoutTransactions","transactions","payoutManager","PayoutManager","totalClaimers","payoutSignatures","result","runDepositorPresignFlow","presignClient","depositorPk","depositTerms","recordGraphFingerprint","depositApprovalWallet","approvedDepositTerms","index","graphFingerprint","depositorGraphSigningContext","depositorPkNormalized","nonDepositorTxs","preparedTransactions","claimerSignatures","depositorClaimerPresignatures","allSignatures","STATUS_POLL_TIMEOUT_MS","TARGET_STATUSES","POST_WOTS_STATUSES","submitWotsPublicKey","wotsSubmitter","wotsPublicKeys","isHintAccepted","matchKeyHint","hint","registrationKey","operationKey","canonicalHint","matchKeySetHint","hints","registrationKeys","operationKeys","canonicalHints","sortedCanonical","setsEqual","assertVaultProviderHintAccepted","vaultProviderEthAddress","hintBtcPubkey","registrationBtcPubkey","readCurrentOperationBtcPubkey","canonicalOperation","ApplicationEntryPointMismatchError","isApplicationEntryPointMismatchError","sortedSet","validateOnChainParticipantKeys","applicationEntryPoint","expectedVaultProviderBtcPubkey","expectedVaultKeeperBtcPubkeys","expectedUniversalChallengerBtcPubkeys","onIndexerServingOperationKeys","onIndexerHintsInconsistent","blockNumber","registryApplicationEntryPoint","onChainVpKey","expectedUniversalChallengersVersion","ucKeyEpoch","isAddress","isAddressEqual","expectedAppVaultKeepersVersion","appKeeperKeyEpoch","onChainKeepers","onChainChallengers","vpMatch","keeperMatch","challengerMatch","roles","pinsRegistration","m","pinsOperation","isValidXOnlyHex","isDepositAmountValid","amountSats","minDeposit","maxDeposit","btcBalance","estimatedFeeSats","depositorClaimValue","validateDepositAmount","amount","formatSatoshisToBtc","validateRemainingCapacity","effectiveRemaining","validateProviderSelection","selectedProviders","availableProviders","availableProvidersLower","validateVaultAmounts","amounts","validateVaultProviderPubkey","stripped","validateVaultKeepers","vaultKeeperBtcPubkeys","validateUniversalChallengers","validateUTXOState","confirmedUTXOs","validateMultiVaultDepositInputs","vaultAmounts","vaultProviderBtcPubkey","amountsValidation","pubkeyValidation","ParticipantKeyDriftError","isParticipantKeyDriftError","diffKeys","verifyRegisteredParticipantKeys","epochsPerVault","mismatches","keeperDiff","challengerDiff","RegisteredVaultVersionMismatchError","isRegisteredVaultVersionMismatchError","verifyRegisteredVaultVersions","expectedOffchainParamsVersion","expectedVaultCoreVersion","BIP68NotMatureError","__publicField","BTC_HEX_BYTES_RE","PUBKEY_HEX_RE","REFUND_VSIZE","REFUND_MAX_FEE_RATE_SATS_VB","REFUND_MAX_FEE_FRACTION_NUMERATOR","REFUND_MAX_FEE_FRACTION_DENOMINATOR","estimateRefundFeeSats","feeRateSatsVb","REFUND_INPUT_COUNT","BIP68_ERROR_RE","assertNonNegativeInteger","validateVaultRefundData","targetEntry","assertValidVaultCoreVersion","validateRefundPrePeginContext","buildAndBroadcastRefund","readVault","readPrePeginContext","feeRate","signPsbt","broadcastTx","refundFee","maxFeeByFraction","xOnlyDepositorPubkey","cleanFundedPrePeginTxHex","found","findAuthAnchorOpReturn","authAnchorHash","parsedFundedTx","e","buildRefundPsbt","signedPsbtHex","REFUND_SIGNED_INPUT_INDEX","refundSignature","signedTxHex","finalizeScriptPathWithSignatures","ReclaimUneconomicalError","feeSats","sweptTotalSats","RECLAIM_MAX_FEE_RATE_SATS_VB","RECLAIM_MAX_FEE_FRACTION_NUMERATOR","RECLAIM_MAX_FEE_FRACTION_DENOMINATOR","RECLAIM_WARN_FEE_FRACTION_NUMERATOR","assertReservesMatchVaultIds","vaults","depositorEthAddress","deriveVaultId","buildAndBroadcastReclaim","readVaults","reserves","estimateReclaimFeeSats","sweptTotal","sum","buildReclaimPsbt","_reserve","signature"],"mappings":"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"}
|