@babylonlabs-io/ts-sdk 5.0.0 → 5.1.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.
Files changed (79) hide show
  1. package/dist/{PayoutManager-BXzFIZK6.cjs → PayoutManager-B5nR0x1t.cjs} +2 -2
  2. package/dist/{PayoutManager-BXzFIZK6.cjs.map → PayoutManager-B5nR0x1t.cjs.map} +1 -1
  3. package/dist/{PayoutManager-Cjkozmnn.js → PayoutManager-BHeiLQ1j.js} +2 -2
  4. package/dist/{PayoutManager-Cjkozmnn.js.map → PayoutManager-BHeiLQ1j.js.map} +1 -1
  5. package/dist/{PeginManager-BipOU_e4.cjs → PeginManager-6ghglb4r.cjs} +2 -2
  6. package/dist/{PeginManager-BipOU_e4.cjs.map → PeginManager-6ghglb4r.cjs.map} +1 -1
  7. package/dist/{PeginManager-Cd5pHNTO.js → PeginManager-CXNqbgA4.js} +5 -5
  8. package/dist/{PeginManager-Cd5pHNTO.js.map → PeginManager-CXNqbgA4.js.map} +1 -1
  9. package/dist/{buildAndBroadcastReclaim-DU8iOtbn.js → buildAndBroadcastReclaim-Acz637Vf.js} +634 -576
  10. package/dist/{buildAndBroadcastReclaim-DU8iOtbn.js.map → buildAndBroadcastReclaim-Acz637Vf.js.map} +1 -1
  11. package/dist/{buildAndBroadcastReclaim-B00roO6R.cjs → buildAndBroadcastReclaim-Cf05JFP-.cjs} +2 -2
  12. package/dist/{buildAndBroadcastReclaim-B00roO6R.cjs.map → buildAndBroadcastReclaim-Cf05JFP-.cjs.map} +1 -1
  13. package/dist/{challengeAssert-eUMgmpMk.cjs → challengeAssert-CuX_8Twt.cjs} +2 -2
  14. package/dist/{challengeAssert-eUMgmpMk.cjs.map → challengeAssert-CuX_8Twt.cjs.map} +1 -1
  15. package/dist/{challengeAssert-pJP7MKhG.js → challengeAssert-D_lRvsO0.js} +2 -2
  16. package/dist/{challengeAssert-pJP7MKhG.js.map → challengeAssert-D_lRvsO0.js.map} +1 -1
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.js +90 -87
  19. package/dist/{pegin-registration-client-ZI0k10_3.js → pegin-registration-client-BlrXyZpF.js} +67 -70
  20. package/dist/pegin-registration-client-BlrXyZpF.js.map +1 -0
  21. package/dist/pegin-registration-client-DOb-Iznp.cjs +2 -0
  22. package/dist/pegin-registration-client-DOb-Iznp.cjs.map +1 -0
  23. package/dist/peginState-B4t_PDgj.cjs +2 -0
  24. package/dist/peginState-B4t_PDgj.cjs.map +1 -0
  25. package/dist/{peginState-CIibG9S_.js → peginState-CXdbSit3.js} +7 -7
  26. package/dist/peginState-CXdbSit3.js.map +1 -0
  27. package/dist/{reservation-BZQABu4g.js → reservation-BAj5n-H_.js} +2 -2
  28. package/dist/{reservation-BZQABu4g.js.map → reservation-BAj5n-H_.js.map} +1 -1
  29. package/dist/{reservation-CA5knoQ0.cjs → reservation-D9ftyiwP.cjs} +2 -2
  30. package/dist/{reservation-CA5knoQ0.cjs.map → reservation-D9ftyiwP.cjs.map} +1 -1
  31. package/dist/tbv/core/clients/eth/index.cjs +1 -1
  32. package/dist/tbv/core/clients/eth/index.js +1 -1
  33. package/dist/tbv/core/clients/eth/types.d.ts +1 -1
  34. package/dist/tbv/core/clients/eth/vault-registry-reader.d.ts.map +1 -1
  35. package/dist/tbv/core/clients/index.cjs +1 -1
  36. package/dist/tbv/core/clients/index.js +1 -1
  37. package/dist/tbv/core/index.cjs +1 -1
  38. package/dist/tbv/core/index.js +92 -89
  39. package/dist/tbv/core/managers/index.cjs +1 -1
  40. package/dist/tbv/core/managers/index.js +2 -2
  41. package/dist/tbv/core/primitives/index.cjs +1 -1
  42. package/dist/tbv/core/primitives/index.js +3 -3
  43. package/dist/tbv/core/primitives/psbt/standardPayoutScript.d.ts.map +1 -1
  44. package/dist/tbv/core/services/deposit/index.d.ts +1 -1
  45. package/dist/tbv/core/services/deposit/index.d.ts.map +1 -1
  46. package/dist/tbv/core/services/deposit/peginState.d.ts +9 -2
  47. package/dist/tbv/core/services/deposit/peginState.d.ts.map +1 -1
  48. package/dist/tbv/core/services/deposit/verifyRegisteredParticipantKeys.d.ts +27 -1
  49. package/dist/tbv/core/services/deposit/verifyRegisteredParticipantKeys.d.ts.map +1 -1
  50. package/dist/tbv/core/services/index.cjs +1 -1
  51. package/dist/tbv/core/services/index.js +85 -82
  52. package/dist/tbv/core/utils/index.cjs +1 -1
  53. package/dist/tbv/core/utils/index.js +1 -1
  54. package/dist/tbv/index.cjs +1 -1
  55. package/dist/tbv/index.js +92 -89
  56. package/dist/{toRefundInputs-CR7vtHMa.cjs → toRefundInputs-BzyN0zvj.cjs} +2 -2
  57. package/dist/{toRefundInputs-CR7vtHMa.cjs.map → toRefundInputs-BzyN0zvj.cjs.map} +1 -1
  58. package/dist/{toRefundInputs-BrJrmOvY.js → toRefundInputs-DwXpKQxN.js} +4 -4
  59. package/dist/{toRefundInputs-BrJrmOvY.js.map → toRefundInputs-DwXpKQxN.js.map} +1 -1
  60. package/dist/types-BnZIbDHb.cjs.map +1 -1
  61. package/dist/types-K_aM5GZO.js.map +1 -1
  62. package/dist/{verifyKeyPathSchnorrSignature-DcxCojp0.cjs → verifyKeyPathSchnorrSignature-BVrAmVL3.cjs} +2 -2
  63. package/dist/{verifyKeyPathSchnorrSignature-DcxCojp0.cjs.map → verifyKeyPathSchnorrSignature-BVrAmVL3.cjs.map} +1 -1
  64. package/dist/{verifyKeyPathSchnorrSignature-DPXOFOol.js → verifyKeyPathSchnorrSignature-CIE3jlVt.js} +2 -2
  65. package/dist/{verifyKeyPathSchnorrSignature-DPXOFOol.js.map → verifyKeyPathSchnorrSignature-CIE3jlVt.js.map} +1 -1
  66. package/dist/verifyScriptPathSchnorrSignature-BHskbJJx.cjs +2 -0
  67. package/dist/verifyScriptPathSchnorrSignature-BHskbJJx.cjs.map +1 -0
  68. package/dist/{verifyScriptPathSchnorrSignature-D-fdi-sG.js → verifyScriptPathSchnorrSignature-DLNGgm4j.js} +181 -173
  69. package/dist/verifyScriptPathSchnorrSignature-DLNGgm4j.js.map +1 -0
  70. package/package.json +3 -3
  71. package/dist/pegin-registration-client-DLlaZbsZ.cjs +0 -2
  72. package/dist/pegin-registration-client-DLlaZbsZ.cjs.map +0 -1
  73. package/dist/pegin-registration-client-ZI0k10_3.js.map +0 -1
  74. package/dist/peginState-CIibG9S_.js.map +0 -1
  75. package/dist/peginState-IyE22wWh.cjs +0 -2
  76. package/dist/peginState-IyE22wWh.cjs.map +0 -1
  77. package/dist/verifyScriptPathSchnorrSignature-CgDa9Rcg.cjs +0 -2
  78. package/dist/verifyScriptPathSchnorrSignature-CgDa9Rcg.cjs.map +0 -1
  79. package/dist/verifyScriptPathSchnorrSignature-D-fdi-sG.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"pegin-registration-client-DLlaZbsZ.cjs","sources":["../src/tbv/core/deposit-terms/commissionCeiling.ts","../src/tbv/core/clients/eth/vault-registry-reader.ts","../src/tbv/core/clients/eth/pegin-registration-client.ts"],"sourcesContent":["/**\n * The depositor's commission ceiling (`maxAcceptableCommissionBps`) policy:\n * quoted VP commission + drift headroom, capped below the contract's\n * exclusive bound. Shared by `PeginManager` (the approved terms) and the\n * registration calldata so both carry the same ceiling; a resume rebuild reads\n * that submitted value back from the `PegInSubmitted` log instead.\n *\n * @module deposit-terms/commissionCeiling\n */\n\n/*\n * Commission-check map (each guards a DIFFERENT boundary — do not consolidate):\n * 1. assertVpCommissionInProtocolRange (vault app, vaultPayoutSignatureService)\n * — chain-read trust boundary; mirrors VPKeyRegistryLogic.sol bounds.\n * 2. capMaxAcceptableCommissionBps (here) — depositor quote → ceiling policy;\n * mirrors PeginLogic.sol's strict > check via the +25bps headroom.\n * 3. buildDepositTerms range check — public-API precondition on the projection\n * that mints the device-enforced commissionFee.\n * 4. payout.ts commission cap — the VP-built tx's output value vs bps at the\n * signing site (CLAUDE.md Critical Path #3).\n * 5. envelope.ts dust/cross-field gates (ledger-vault-signer) — firmware-only\n * constants, pre-device-I/O (Critical Path #7).\n */\n\nimport { MAX_VP_COMMISSION_BPS_EXCLUSIVE } from \"../primitives/psbt/constants\";\n\n/**\n * Headroom (in basis points) added to the current VP commission to compute\n * `maxAcceptableCommissionBps` at submit time. Lets the VP raise its\n * commission by up to this amount between read and submit without forcing\n * a re-quote. Capped by {@link MAX_ACCEPTABLE_COMMISSION_BPS_CAP}.\n *\n * Contract check is strict `>` (PeginLogic.sol `VaultProviderCommissionExceeded`\n * revert), so +25 allows up\n * to +25 bps of drift.\n */\nexport const COMMISSION_BPS_HEADROOM = 25;\n\n/**\n * Hard ceiling for `maxAcceptableCommissionBps`. The contract enforces\n * `commissionBps < 10000`, so any value at/above that is unreachable;\n * `9999` is the maximum useful cap.\n */\nexport const MAX_ACCEPTABLE_COMMISSION_BPS_CAP = 9999;\n\n/**\n * The commission ceiling submitted as registration calldata and mirrored\n * into `DepositTerms.commissionFee`: quoted + drift headroom, capped.\n * Single source for both consumers — feed it the SAME quoted bps at prepare\n * and register time so device-accept stays coextensive with contract-accept.\n */\nexport function capMaxAcceptableCommissionBps(bps: number): number {\n // Validate the raw quote before headroom shifts its domain — a negative\n // quote must throw here, not become a small \"legal\" ceiling.\n // Reject an out-of-range quote outright — clamping it to 9999 would turn a\n // bad read into a 99.99% ceiling, the exact failure the cap exists to stop.\n if (\n !Number.isInteger(bps) ||\n bps < 0 ||\n bps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n ) {\n throw new Error(\n `Quoted commissionBps must be an integer in ` +\n `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${bps}`,\n );\n }\n return Math.min(\n bps + COMMISSION_BPS_HEADROOM,\n MAX_ACCEPTABLE_COMMISSION_BPS_CAP,\n );\n}\n","/**\n * Concrete BTCVaultRegistry reader using viem's readContract.\n *\n * This is an optional utility — callers can use their own implementation\n * of the VaultRegistryReader interface.\n */\n\nimport type { Abi, Address, Hex, PublicClient } from \"viem\";\nimport { getAbiItem } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { BTCVaultRegistryKeyEpochsABI } from \"../../contracts/abis/BTCVaultRegistryKeyEpochs.abi\";\nimport { VaultClaimableByNotFoundError } from \"./claimable-event-error\";\nimport { assertOnChainBtcPubkey } from \"./onChainBtcPubkey\";\nimport { assertValidOffchainParamsVersion } from \"./protocol-params-validation\";\nimport { RegistrationLogsUnavailableError } from \"./registration-logs-error\";\nimport { findRegistrationRecord } from \"./registration-records\";\nimport type {\n KeyEpochs,\n OnChainBtcPubkey,\n PeginRegistrationRecord,\n VaultBasicInfo,\n VaultClaimableByEvent,\n VaultData,\n VaultProtocolInfo,\n VaultRegistryReader,\n} from \"./types\";\n\n/**\n * Inclusive upper bound the BTCVaultRegistry contract enforces on a vault\n * provider's commission (the contract check is `< 10000`).\n */\nconst MAX_VP_COMMISSION_BPS = 9999;\n\n/** Raw `getBtcVaultBasicInfo` tuple as decoded by viem. */\ntype RawVaultBasicInfo = {\n depositor: Address;\n depositorBtcPubKey: Hex;\n amount: bigint;\n vaultProvider: Address;\n status: number;\n applicationEntryPoint: Address;\n createdAt: bigint;\n};\n\n/**\n * Raw `getBtcVaultProtocolInfo` tuple as decoded by viem.\n *\n * Mirrors the ABI's components. The trailing key epochs are read through the\n * separate key-epochs ABI, so this prefix stops at `vaultCoreVersion`.\n */\ntype RawVaultProtocolInfo = {\n depositorSignedPeginTx: Hex;\n universalChallengersVersion: number;\n appVaultKeepersVersion: number;\n offchainParamsVersion: number;\n verifiedAt: bigint;\n depositorWotsPkHash: Hex;\n hashlock: Hex;\n htlcVout: number;\n depositorPopSignature: Hex;\n prePeginTxHash: Hex;\n vaultProviderCommissionBps: number;\n claimExpiredUntil: bigint;\n vaultCoreVersion: number;\n};\n\nfunction mapVaultBasicInfo(result: RawVaultBasicInfo): VaultBasicInfo {\n return {\n depositor: result.depositor,\n depositorBtcPubKey: result.depositorBtcPubKey,\n amount: result.amount,\n vaultProvider: result.vaultProvider,\n status: result.status,\n applicationEntryPoint: result.applicationEntryPoint,\n createdAt: result.createdAt,\n };\n}\n\nfunction mapVaultProtocolInfo(result: RawVaultProtocolInfo): VaultProtocolInfo {\n const offchainParamsVersion = Number(result.offchainParamsVersion);\n assertValidOffchainParamsVersion(offchainParamsVersion);\n return {\n depositorSignedPeginTx: result.depositorSignedPeginTx,\n universalChallengersVersion: result.universalChallengersVersion,\n appVaultKeepersVersion: result.appVaultKeepersVersion,\n offchainParamsVersion,\n verifiedAt: result.verifiedAt,\n depositorWotsPkHash: result.depositorWotsPkHash,\n hashlock: result.hashlock,\n htlcVout: result.htlcVout,\n depositorPopSignature: result.depositorPopSignature,\n prePeginTxHash: result.prePeginTxHash,\n vaultProviderCommissionBps: result.vaultProviderCommissionBps,\n claimExpiredUntil: result.claimExpiredUntil,\n vaultCoreVersion: result.vaultCoreVersion,\n };\n}\n\n/**\n * Exclusive upper bound for a `uint64` epoch.\n *\n * viem does not range-check a decoded `uint64`, so a misaligned decode can\n * hand back a value far larger than the field can hold. Rejecting those is\n * cheap defence in depth against a mis-decode of the extended ABI.\n *\n * The state this is load-bearing for is narrower than \"a registry that predates\n * RFC-006\". Against a *fully* pre-RFC-006 registry the path fails closed on its\n * own: `resolveParticipantKeysAtEpochs` goes on to call\n * `getOperationBtcKeyAtEpochOrGenesis` on ApplicationRegistry and\n * ProtocolParams, which do not exist there, so the multicall reverts and these\n * epochs are never used. The reachable gap is a registry that *has* the\n * operation-key getters but whose `BTCVaultProtocolInfo` struct is not\n * extended — there the tail-data epochs resolve with no error at all, and this\n * range check is the only thing looking at them.\n *\n * Even for that case it is only a backstop, and a weak one: a misaligned decode\n * can land on a small, plausible-looking integer this range accepts. Correctness\n * rests on only ever pointing at an RFC-006 registry, which is a deployment\n * precondition rather than something this call can establish — see\n * https://github.com/babylonlabs-io/babylon-toolkit/issues/2192 for where that\n * check is owned, and `BTCVaultRegistryKeyEpochs.abi.ts` for the decode hazard.\n */\nconst UINT64_EXCLUSIVE_UPPER_BOUND = 1n << 64n;\n\n/**\n * The epoch a vault provider's registration key is bonded at.\n *\n * A provider's operation-key history is append-only and every appended version\n * is stamped at epoch 1 or later, so epoch 0 always resolves to the key set at\n * registration. This is what lets `getOperationBtcKeyAtEpoch` stand in for the\n * removed `getVaultProviderBTCKey` getter.\n */\nconst VP_GENESIS_KEY_EPOCH = 0n;\n\nconst PEGIN_SUBMITTED_EVENT = getAbiItem({\n abi: BTCVaultRegistryABI,\n name: \"PegInSubmitted\",\n});\n\nconst VAULT_CLAIMABLE_BY_EVENT = getAbiItem({\n abi: BTCVaultRegistryABI,\n name: \"VaultClaimableBy\",\n});\n\n/**\n * Block span of one `VaultClaimableBy` query: btc-vault's own scan chunk\n * (`EVENT_QUERY_CHUNK_BLOCKS = 7200`, `crates/eth-client/src/client.rs:793`\n * @ ac4954e7), about a day of Ethereum blocks, so a recent redeem costs one\n * request. Not every provider serves a range this wide — an Alchemy free key\n * caps `eth_getLogs` at 10 blocks\n * (https://www.alchemy.com/docs/reference/eth-getlogs) — and a provider that\n * answered a wide range short instead of erroring would make this scan report\n * not-found, so confirming the RPC honours 7,200-block ranges is a\n * prerequisite of a claim run.\n */\nconst VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS = 7_200n;\n\nfunction maxBigint(a: bigint, b: bigint): bigint {\n return a > b ? a : b;\n}\n\n/** A `PegInSubmitted` log's decoded args, as viem's strict `getLogs` hands them back. */\ninterface PegInSubmittedArgs {\n vaultId: Hex;\n peginTxHash: Hex;\n depositor: Address;\n vaultProvider: Address;\n amount: bigint;\n vaultCoreVersion: number;\n universalChallengersVersion: number;\n appVaultKeepersVersion: number;\n proverCircuitVersion: number;\n offchainParamsVersion: number;\n depositorPayoutBtcAddress: Hex;\n depositorWotsPkHash: Hex;\n hashlock: Hex;\n htlcVout: number;\n unsignedPrePeginTx: Hex;\n maxAcceptableCommissionBps: number;\n}\n\nfunction mapRegistrationRecord(\n args: PegInSubmittedArgs,\n blockNumber: bigint,\n): PeginRegistrationRecord {\n // A plain decode of every registration log in the block, strangers'\n // included: nothing here parses a transaction or bound-checks a script, so\n // one malformed registration elsewhere in the block cannot fail the\n // caller's read. The strict per-record checks live in\n // registration-records.ts. A duplicate vault id is not covered: a vault\n // registers once, so two logs for one id are an inconsistent node and fail\n // the read closed (see the caller).\n return {\n vaultId: args.vaultId.toLowerCase() as Hex,\n depositor: args.depositor,\n vaultProvider: args.vaultProvider,\n amount: args.amount,\n vaultCoreVersion: args.vaultCoreVersion,\n universalChallengersVersion: args.universalChallengersVersion,\n appVaultKeepersVersion: args.appVaultKeepersVersion,\n proverCircuitVersion: args.proverCircuitVersion,\n offchainParamsVersion: args.offchainParamsVersion,\n peginTxHash: args.peginTxHash.toLowerCase() as Hex,\n depositorPayoutScriptPubKey:\n args.depositorPayoutBtcAddress.toLowerCase() as Hex,\n unsignedPrePeginTx: args.unsignedPrePeginTx,\n maxAcceptableCommissionBps: args.maxAcceptableCommissionBps,\n blockNumber,\n };\n}\n\nfunction assertEpochInRange(\n value: bigint,\n field: string,\n vaultId: Hex,\n): bigint {\n if (value < 0n || value >= UINT64_EXCLUSIVE_UPPER_BOUND) {\n throw new Error(\n `getBtcVaultProtocolInfo returned ${field}=${value} for vault ${vaultId}, ` +\n `outside the uint64 range — the registry may predate RFC-006`,\n );\n }\n return value;\n}\n\nfunction mapKeyEpochs(result: KeyEpochs, vaultId: Hex): KeyEpochs {\n return {\n vpKeyEpoch: assertEpochInRange(result.vpKeyEpoch, \"vpKeyEpoch\", vaultId),\n appKeeperKeyEpoch: assertEpochInRange(\n result.appKeeperKeyEpoch,\n \"appKeeperKeyEpoch\",\n vaultId,\n ),\n ucKeyEpoch: assertEpochInRange(result.ucKeyEpoch, \"ucKeyEpoch\", vaultId),\n };\n}\n\n/**\n * Concrete vault registry reader using viem.\n *\n * Usage:\n * ```ts\n * const reader = new ViemVaultRegistryReader(publicClient, registryAddress);\n * const data = await reader.getVaultData(vaultId);\n * ```\n */\nexport class ViemVaultRegistryReader implements VaultRegistryReader {\n constructor(\n private publicClient: PublicClient,\n private contractAddress: Address,\n ) {}\n\n /**\n * Read the VP's **genesis** (registration) x-only BTC pubkey — the key bonded\n * at version 0, which never moves when the operator rotates.\n *\n * Resolved as \"the operation key at epoch 0\" rather than through the\n * dedicated `getVaultProviderBTCKey` getter, which\n * https://github.com/babylonlabs-io/vault-contracts-aave-v4/pull/539 removes.\n * Epoch 0 predates any rotation — appended versions are stamped at epoch 1 or\n * later — so it resolves to the registration key, and the contracts team has\n * confirmed that is a property we can rely on rather than an implementation\n * detail. The devnet comparison behind that claim — both getters returning the\n * identical key for a provider that *has* rotated, while\n * `getCurrentOperationBtcKey` differed — is recorded in\n * https://github.com/babylonlabs-io/babylon-toolkit/issues/2188.\n *\n * This makes the read RFC-006-only, where the removed getter also existed on a\n * legacy registry. That costs nothing: every caller of this method already\n * resolves participant keys through `OperationKeyReader`, so all of them\n * require an RFC-006 registry regardless.\n *\n * Validates length, hex form, and secp256k1 curve membership before minting\n * the brand. Returns 64-char lowercase hex without the `0x` prefix.\n */\n async getVaultProviderGenesisBtcPubKey(\n vpAddress: Address,\n blockNumber?: bigint,\n ): Promise<OnChainBtcPubkey> {\n return this.getVaultProviderOperationBtcKeyAtEpoch(\n vpAddress,\n VP_GENESIS_KEY_EPOCH,\n blockNumber,\n );\n }\n\n /**\n * Read the VP operation key selected by `epoch`.\n *\n * This is public rather than hidden inside `OperationKeyReader` because\n * subject-specific VP authentication needs exactly one participant: the\n * gRPC bootstrap is signed by the key frozen into the vault, while the\n * JSON-RPC bootstrap is signed by the provider's live key.\n */\n async getVaultProviderOperationBtcKeyAtEpoch(\n vpAddress: Address,\n epoch: bigint,\n blockNumber?: bigint,\n ): Promise<OnChainBtcPubkey> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getOperationBtcKeyAtEpoch\",\n args: [vpAddress, epoch],\n blockNumber,\n })) as Hex;\n return assertOnChainBtcPubkey(\n result,\n `getOperationBtcKeyAtEpoch (vp=${vpAddress}, epoch=${epoch})`,\n );\n }\n\n /**\n * Read the application entry point a vault provider is registered for.\n *\n * This is the registry's own `vaultProviders[vp].applicationEntryPoint`, and\n * it is the value the peg-in submit path resolves internally — it selects\n * which application's vault-keeper roster, roster version and keeper key\n * epoch a deposit is bonded to. The dApp separately carries an entry point\n * from its own configuration; the two agree today, but they are different\n * sources of truth, so the build path reads this one and asserts the\n * configured value matches it rather than trusting either alone.\n */\n async getVaultProviderApplication(\n vpAddress: Address,\n blockNumber?: bigint,\n ): Promise<Address> {\n return (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getVaultProviderApplication\",\n args: [vpAddress],\n blockNumber,\n })) as Address;\n }\n\n /**\n * Read a vault provider's *current* RFC-006 operation BTC key.\n *\n * Falls back on-chain to the registration key when the provider has never\n * rotated, so this returns the same value as\n * `getVaultProviderGenesisBtcPubKey` until the first rotation.\n *\n * This is the key the VP's server uses for JSON-RPC-subject authentication.\n * The gRPC-subject bootstrap is instead bound to the existing vault's frozen\n * epoch and resolves through `getVaultProviderOperationBtcKeyAtEpoch`.\n */\n async getCurrentVaultProviderOperationBtcKey(\n vpAddress: Address,\n ): Promise<OnChainBtcPubkey> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getCurrentOperationBtcKey\",\n args: [vpAddress],\n })) as Hex;\n return assertOnChainBtcPubkey(\n result,\n `getCurrentOperationBtcKey (vp=${vpAddress})`,\n );\n }\n\n /**\n * Read a vault's frozen RFC-006 operation-key epochs.\n *\n * Reads `getBtcVaultProtocolInfo` through the **extended** ABI, which is only\n * valid against an RFC-006 registry: against one whose `BTCVaultProtocolInfo`\n * struct is not extended this call does not fail for a populated vault, it\n * silently returns three words of tail data as epochs. Nothing here can detect\n * that, so the guarantee is a deployment one — every network this ships to has\n * the RFC-006 getters, and mainnet is a fresh RFC-006 deploy.\n *\n * A registry missing the operation-key getters entirely is the *safer* of the\n * two cases: `resolveParticipantKeysAtEpochs` reverts downstream and these\n * epochs never reach key resolution. See {@link UINT64_EXCLUSIVE_UPPER_BOUND}\n * for which state the range check actually guards, and\n * `BTCVaultRegistryKeyEpochs.abi.ts` for the decode hazard.\n */\n async getVaultKeyEpochs(vaultId: Hex): Promise<KeyEpochs> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryKeyEpochsABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n })) as unknown as KeyEpochs;\n\n return mapKeyEpochs(result, vaultId);\n }\n\n async getVaultKeyEpochsBatch(vaultIds: readonly Hex[]): Promise<KeyEpochs[]> {\n if (vaultIds.length === 0) return [];\n\n const results = await this.publicClient.multicall({\n contracts: vaultIds.map((vaultId) => ({\n address: this.contractAddress,\n abi: BTCVaultRegistryKeyEpochsABI as Abi,\n functionName: \"getBtcVaultProtocolInfo\" as const,\n args: [vaultId] as const,\n })),\n allowFailure: false,\n });\n\n return results.map((info, i) =>\n mapKeyEpochs(info as unknown as KeyEpochs, vaultIds[i]),\n );\n }\n\n async getVaultBasicInfo(vaultId: Hex): Promise<VaultBasicInfo> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n })) as RawVaultBasicInfo;\n\n return mapVaultBasicInfo(result);\n }\n\n async getVaultProtocolInfo(vaultId: Hex): Promise<VaultProtocolInfo> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n })) as RawVaultProtocolInfo;\n\n return mapVaultProtocolInfo(result);\n }\n\n async getProtocolInfoBatch(\n vaultIds: readonly Hex[],\n ): Promise<VaultProtocolInfo[]> {\n if (vaultIds.length === 0) return [];\n\n const results = await this.publicClient.multicall({\n contracts: vaultIds.map((vaultId) => ({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI as Abi,\n functionName: \"getBtcVaultProtocolInfo\" as const,\n args: [vaultId] as const,\n })),\n allowFailure: false,\n });\n\n return results.map((info, i) => {\n const result = info as unknown as RawVaultProtocolInfo;\n if (\n !result.depositorSignedPeginTx ||\n result.depositorSignedPeginTx === \"0x\"\n ) {\n // An empty record is not proof the vault is absent: a lagging RPC\n // node returns HTTP 200 with a zero struct, so no retry below this\n // layer can see it. Single-shot is only safe because the finality\n // gate (`waitForPeginRegistrationDepth`) runs first on the deposit\n // path — move this ahead of it and the read-after-write race returns.\n // The vault app matches this message to render \"still confirming\".\n throw new Error(\n `Vault ${vaultIds[i]} not found on-chain or has no pegin transaction`,\n );\n }\n return mapVaultProtocolInfo(result);\n });\n }\n\n /**\n * Read the protocol pegin fee (in wei) for a given vault provider.\n * Mirrors the `getPegInFee(address)` view on BTCVaultRegistry.\n */\n async getPegInFee(vaultProvider: Address): Promise<bigint> {\n return (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getPegInFee\",\n args: [vaultProvider],\n })) as bigint;\n }\n\n /**\n * Read a vault provider's current commission in basis points from\n * BTCVaultRegistry. The contract enforces `commissionBps < 10000`, so the\n * legitimate range is `[0, 9999]`; anything outside indicates a wrong\n * contract address or ABI drift and is surfaced as an error rather than\n * trusted.\n */\n async getVaultProviderCommission(vaultProvider: Address): Promise<number> {\n // viem infers `number` from the `uint16` return in the `as const` ABI.\n const bps = await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getVaultProviderCommission\",\n args: [vaultProvider],\n });\n\n if (!Number.isInteger(bps) || bps < 0 || bps > MAX_VP_COMMISSION_BPS) {\n throw new Error(\n `getVaultProviderCommission returned ${bps} bps for ${vaultProvider}, ` +\n `outside the protocol range [0, ${MAX_VP_COMMISSION_BPS}]`,\n );\n }\n\n return bps;\n }\n\n async getVaultData(vaultId: Hex): Promise<VaultData> {\n // One round-trip for both structs (hard-fail): they feed signing/refund/\n // broadcast rebinds, so reading them in a single multicall also pins both\n // to the same block — no basic/protocol skew across two `eth_call`s.\n const [basicRaw, protocolRaw] = await this.publicClient.multicall({\n contracts: [\n {\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n },\n {\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n },\n ],\n allowFailure: false,\n });\n\n const basic = mapVaultBasicInfo(basicRaw);\n const protocol = mapVaultProtocolInfo(protocolRaw);\n\n if (\n !protocol.depositorSignedPeginTx ||\n protocol.depositorSignedPeginTx === \"0x\"\n ) {\n throw new Error(\n `Vault ${vaultId} not found on-chain or has no pegin transaction`,\n );\n }\n\n return { basic, protocol };\n }\n\n /** @inheritdoc */\n async getRegistrationRecordsAtBlock(\n createdAt: bigint,\n ): Promise<PeginRegistrationRecord[]> {\n // Deliberately no `vaultId` topic filter: the block holds the target's\n // registration, so only an unfiltered answer can tell a node that served\n // nothing (empty) from one that lacks the target (findRegistrationRecord),\n // and one query serves every vault a caller reads from the block. strict:\n // a log whose data does not decode against the ABI is dropped (viem\n // parseEventLogs) instead of surfacing as `args: {}`.\n const logs = await this.publicClient.getLogs({\n address: this.contractAddress,\n event: PEGIN_SUBMITTED_EVENT,\n fromBlock: createdAt,\n toBlock: createdAt,\n strict: true,\n });\n if (logs.length === 0) {\n throw new RegistrationLogsUnavailableError(createdAt);\n }\n\n const records = new Map<string, PeginRegistrationRecord>();\n for (const log of logs) {\n const record = mapRegistrationRecord(log.args, log.blockNumber);\n // Lenient decoding covers a malformed log, not a duplicate: a vault\n // registers once, so two logs for any id mean the node is inconsistent\n // and every record from this block is suspect — fail closed by design.\n if (records.has(record.vaultId)) {\n throw new Error(\n `Expected one PegInSubmitted log for vault ${record.vaultId} at block ${createdAt}, ` +\n `found more than one`,\n );\n }\n records.set(record.vaultId, record);\n }\n return [...records.values()];\n }\n\n /**\n * Read the depositor's commission ceiling (`maxAcceptableCommissionBps`)\n * for vaults registered in the same block, from their `PegInSubmitted`\n * logs. Returned in `vaultIds` order.\n *\n * The contract bound-checks the ceiling and discards it (PeginLogic.sol,\n * `VaultProviderCommissionExceeded`), so the registration log is its only\n * on-chain source. Vault ids are matched case-insensitively.\n *\n * @throws As {@link getRegistrationRecordsAtBlock}, plus the same typed\n * transient error when a vault has no registration log in its own\n * `createdAt` block.\n */\n async getMaxAcceptableCommissionBpsBatch(\n vaultIds: readonly Hex[],\n createdAt: bigint,\n ): Promise<number[]> {\n if (vaultIds.length === 0) return [];\n\n const records = await this.getRegistrationRecordsAtBlock(createdAt);\n return vaultIds.map(\n (vaultId) =>\n findRegistrationRecord(records, vaultId, createdAt)\n .maxAcceptableCommissionBps,\n );\n }\n\n /** @inheritdoc */\n async getVaultClaimableBy(\n vaultId: Hex,\n claimerPk: Hex,\n createdAt: bigint,\n ): Promise<VaultClaimableByEvent> {\n const claimer = assertOnChainBtcPubkey(\n claimerPk,\n `getVaultClaimableBy claimer (vault=${vaultId})`,\n );\n const claimerTopic = `0x${claimer}` as Hex;\n // viem re-filters the decoded logs client-side against `args`, comparing a\n // bytes32 with `===` against lower-case hex (parseEventLogs.ts:140,172-188).\n const vaultIdTopic = vaultId.toLowerCase() as Hex;\n // Finalized, not latest: the block found here is what the prover proves\n // against, and an unfinalized redeem can reorg away (btc-vault binds the\n // same scan to its finality level, eth-client client.rs:5989-6040).\n const { number: finalized } = await this.publicClient.getBlock({\n blockTag: \"finalized\",\n });\n if (finalized < createdAt) {\n throw new Error(\n `Finalized block ${finalized} is below vault ${vaultId}'s registration block ${createdAt}; ` +\n `the registration is not finalized yet, or the node is behind it`,\n );\n }\n\n // Newest first: the redeem usually trails the tip by far less than it\n // trails the registration. The contract emits every VaultClaimableBy of a\n // vault in one transaction, so the first non-empty chunk is the answer.\n let toBlock = finalized;\n for (;;) {\n const fromBlock = maxBigint(\n createdAt,\n toBlock - VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS + 1n,\n );\n const logs = await this.publicClient.getLogs({\n address: this.contractAddress,\n event: VAULT_CLAIMABLE_BY_EVENT,\n args: { vaultId: vaultIdTopic, claimerPK: claimerTopic },\n fromBlock,\n toBlock,\n strict: true,\n });\n // A redeem emits every VaultClaimableBy of a vault in ONE transaction\n // (RedeemLogic.sol), and `redeemForDepositor` emits the VP's key and\n // then the depositor's — so when those two keys are equal this filter\n // legitimately matches twice. Identical authorizations from one\n // transaction are the same fact; logs spanning transactions are not.\n if (logs.length > 1) {\n const transactions = new Set(logs.map((log) => log.transactionHash));\n if (transactions.size > 1) {\n throw new Error(\n `Found ${logs.length} VaultClaimableBy logs for vault ${vaultId} and claimer ` +\n `${claimerTopic} across ${transactions.size} transactions in blocks ` +\n `${fromBlock}..${toBlock}; a vault is redeemed once, so the node's answer ` +\n `is inconsistent`,\n );\n }\n }\n if (logs.length >= 1) {\n const log = logs[0];\n return {\n blockNumber: log.blockNumber,\n claimerPk: assertOnChainBtcPubkey(\n log.args.claimerPK,\n `VaultClaimableBy.claimerPK (vault=${vaultId})`,\n ),\n peginTxHash: log.args.peginTxHash,\n vaultCoreVersion: log.args.vaultCoreVersion,\n proverCircuitVersion: log.args.proverCircuitVersion,\n offchainParamsVersion: log.args.offchainParamsVersion,\n universalChallengersVersion: log.args.universalChallengersVersion,\n appVaultKeepersVersion: log.args.appVaultKeepersVersion,\n };\n }\n if (fromBlock === createdAt) {\n throw new VaultClaimableByNotFoundError(\n vaultId,\n claimerTopic,\n createdAt,\n finalized,\n );\n }\n toBlock = fromBlock - 1n;\n }\n }\n}\n","import {\n encodeFunctionData,\n isAddressEqual,\n zeroAddress,\n type Address,\n type Chain,\n type Hex,\n type PublicClient,\n type WalletClient,\n} from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { handleContractError } from \"../../contracts/errors\";\nimport {\n capMaxAcceptableCommissionBps,\n COMMISSION_BPS_HEADROOM,\n} from \"../../deposit-terms/commissionCeiling\";\nimport { waitForTransactionReceiptSmartAware } from \"../../utils/eth\";\nimport { assertOnChainBtcPubkey } from \"./onChainBtcPubkey\";\nimport { assertPayoutScriptMatchesPopKey } from \"./payout-script\";\nimport { calculateBtcTxHash, derivePeginVaultId } from \"./pegin-transaction\";\nimport { ViemVaultRegistryReader } from \"./vault-registry-reader\";\n\n/** Referral code sent with peg-in registration. Zero means no referral. */\nexport const NO_REFERRAL_CODE = 0;\nconst UINT8_MAX = 255;\n// Match the prior vault-service timeout. A dropped transaction must not wait forever.\nconst RECEIPT_TIMEOUT_MS = 120_000;\n\nfunction ensureHex(value: string, label: string): Hex {\n const prefixed = value.startsWith(\"0x\")\n ? value\n : value.startsWith(\"0X\")\n ? `0x${value.slice(2)}`\n : `0x${value}`;\n if (!/^0x(?:[0-9a-fA-F]{2})+$/.test(prefixed)) {\n throw new Error(`${label} must be non-empty, even-length hex`);\n }\n return prefixed as Hex;\n}\n\nfunction assertBytes32(value: Hex, label: string): void {\n if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {\n throw new Error(`${label} must be a 32-byte hex value`);\n }\n}\n\n/** All-zero `bytes32` — the shape an unresolved fingerprint takes. */\nconst ZERO_BYTES32 =\n \"0x0000000000000000000000000000000000000000000000000000000000000000\";\n\n/**\n * The single validation point for `expectedFingerprint`, on both the singular\n * and batch paths.\n *\n * Zero is rejected separately from the shape check because it is a *well-formed*\n * `bytes32` that the registry can never accept — the live fingerprint is a\n * keccak256 over a domain-separated preimage — so it can only ever mean the\n * caller had no value and filled the slot. Catching it here turns a defaulted\n * field into a named error instead of an opaque on-chain revert with two\n * hashes and no hint.\n */\nfunction assertFingerprint(value: Hex): void {\n assertBytes32(value, \"expectedFingerprint\");\n if (value.toLowerCase() === ZERO_BYTES32) {\n throw new Error(\n \"expectedFingerprint must not be zero: the registry cannot resolve a \" +\n \"zero fingerprint, so this submission would always revert\",\n );\n }\n}\n\nfunction assertHtlcVout(value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > UINT8_MAX) {\n throw new Error(`htlcVout must be a uint8, got ${value}`);\n }\n}\n\n/** A PoP prepared by a Bitcoin wallet, reusable by the ETH submit client. */\nexport interface PopSignature {\n btcPopSignature: Hex;\n depositorEthAddress: Address;\n /** x-only secp256k1 public key, 64 hex chars without `0x`. */\n depositorBtcPubkey: string;\n}\n\nexport interface ViemPeginRegistrationClientConfig {\n ethWallet: WalletClient;\n ethChain: Chain;\n publicClient: PublicClient;\n btcVaultRegistry: Address;\n receiptTimeoutMs?: number;\n /**\n * Refuse to register without a `quotedCommissionBps`. Set it to `true` when\n * the depositor approved deposit terms on a device: approval froze the\n * commission ceiling from the quote, so the chain-current fallback could\n * admit a commission the device would later refuse to pay out.\n *\n * Required, with no default: an omitted flag would silently pick the unsafe\n * direction for the integrator least able to probe the wallet capability.\n */\n requireQuotedCommissionBps: boolean;\n}\n\nexport interface RegisterPeginOnChainParams {\n unsignedPrePeginTx: string;\n depositorSignedPeginTx: string;\n vaultProvider: Address;\n hashlock: Hex;\n depositorWotsPkHash: Hex;\n popSignature: PopSignature;\n /** Raw x-only, compressed, or uncompressed BTC pubkey from the PoP wallet. */\n depositorBtcPubkeyRaw: string;\n htlcVout: number;\n /**\n * Bitcoin output script bytes, not an address. The client verifies that the\n * script pays the proof-of-possession key before it submits the transaction.\n */\n depositorPayoutScriptPubKey: string;\n quotedCommissionBps?: number;\n /**\n * `keccak256(abi.encode(...))` over the protocol state the Pre-PegIn was\n * built against — see the SDK's `pegin-fingerprint` module.\n *\n * The registry recomputes it live at inclusion and reverts with\n * `PeginFingerprintChanged` on any difference, so it must be computed from\n * the same block-pinned snapshot that shaped the Bitcoin scripts. Required,\n * with no default: a fingerprint the caller did not resolve is not a\n * fingerprint, and the registry has no way to tell the two apart.\n */\n expectedFingerprint: Hex;\n}\n\nexport interface PeginRegistrationResult {\n ethTxHash: Hex;\n vaultId: Hex;\n peginTxHash: Hex;\n}\n\nexport interface BatchPeginRegistrationItem {\n depositorSignedPeginTx: string;\n hashlock: Hex;\n htlcVout: number;\n /**\n * Bitcoin output script bytes, not an address. The client verifies that the\n * script pays the proof-of-possession key before it submits the transaction.\n */\n depositorPayoutScriptPubKey: string;\n depositorWotsPkHash: Hex;\n}\n\nexport interface RegisterPeginBatchOnChainParams {\n vaultProvider: Address;\n unsignedPrePeginTx: string;\n requests: BatchPeginRegistrationItem[];\n popSignature: PopSignature;\n /** Raw x-only, compressed, or uncompressed BTC pubkey from the PoP wallet. */\n depositorBtcPubkeyRaw: string;\n quotedCommissionBps?: number;\n /**\n * See {@link RegisterPeginOnChainParams.expectedFingerprint}. One value for\n * the whole batch: the fingerprint takes no per-request input and a batch\n * fixes one vault provider, so every entry would resolve the same value.\n */\n expectedFingerprint: Hex;\n}\n\nexport interface BatchPeginRegistrationResultItem {\n vaultId: Hex;\n peginTxHash: Hex;\n}\n\nexport interface PeginBatchRegistrationResult {\n ethTxHash: Hex;\n vaults: BatchPeginRegistrationResultItem[];\n}\n\n/**\n * Ethereum-only client for the on-chain half of peg-in registration.\n *\n * It consumes Bitcoin artifacts that were prepared earlier, but owns no BTC\n * wallet and imports neither Bitcoin libraries nor the Rust/WASM package.\n */\nexport class ViemPeginRegistrationClient {\n constructor(private readonly config: ViemPeginRegistrationClientConfig) {}\n\n async registerPeginOnChain(\n params: RegisterPeginOnChainParams,\n ): Promise<PeginRegistrationResult> {\n const request = {\n ...params,\n popSignature: { ...params.popSignature },\n };\n this.assertQuotedCommissionPresentWhenRequired(request.quotedCommissionBps);\n // Second, matching the batch path, so the two public entry points validate\n // in the same order and neither can drift behind a chain call.\n assertFingerprint(request.expectedFingerprint);\n const depositor = this.assertPopMatchesEthAccount(request.popSignature);\n const depositorBtcPubkey = this.normalizeBtcPubkey(request.popSignature);\n const btcPopSignature = ensureHex(\n request.popSignature.btcPopSignature,\n \"btcPopSignature\",\n );\n const normalized = this.normalizeRequest(\n request,\n depositor,\n depositorBtcPubkey,\n );\n\n await this.assertVaultDoesNotExist(\n normalized.vaultId,\n normalized.peginTxHash,\n );\n const peginFee = await this.readPeginFee(request.vaultProvider);\n const maxCommission = await this.resolveMaxAcceptableCommissionBps(\n request.vaultProvider,\n request.quotedCommissionBps,\n );\n const callData = encodeFunctionData({\n abi: BTCVaultRegistryABI,\n functionName: \"submitPeginRequest\",\n args: [\n depositor,\n depositorBtcPubkey,\n btcPopSignature,\n normalized.unsignedPrePeginTx,\n normalized.depositorSignedPeginTx,\n request.vaultProvider,\n maxCommission,\n request.hashlock,\n request.htlcVout,\n normalized.payoutScript,\n request.depositorWotsPkHash,\n // Appended last on the singular overload; the batch takes it at\n // position 4 instead. Order is the selector, so do not \"tidy\" either.\n request.expectedFingerprint,\n ],\n });\n\n const ethTxHash = await this.sendAndWait(\n callData,\n peginFee,\n false,\n depositor,\n );\n return {\n ethTxHash,\n vaultId: normalized.vaultId,\n peginTxHash: normalized.peginTxHash,\n };\n }\n\n async registerPeginBatchOnChain(\n params: RegisterPeginBatchOnChainParams,\n ): Promise<PeginBatchRegistrationResult> {\n const batch = {\n ...params,\n popSignature: { ...params.popSignature },\n requests: params.requests.map((request) => ({ ...request })),\n };\n if (batch.requests.length === 0) {\n throw new Error(\"Batch pegin requires at least one request\");\n }\n this.assertQuotedCommissionPresentWhenRequired(batch.quotedCommissionBps);\n assertFingerprint(batch.expectedFingerprint);\n const depositor = this.assertPopMatchesEthAccount(batch.popSignature);\n const unsignedPrePeginTx = ensureHex(\n batch.unsignedPrePeginTx,\n \"unsignedPrePeginTx\",\n );\n const depositorBtcPubkey = this.normalizeBtcPubkey(batch.popSignature);\n const btcPopSignature = ensureHex(\n batch.popSignature.btcPopSignature,\n \"btcPopSignature\",\n );\n\n const vaults: BatchPeginRegistrationResultItem[] = [];\n const requests = [];\n const seenVaultIds = new Set<string>();\n for (const request of batch.requests) {\n const normalized = this.normalizeRequest(\n {\n ...request,\n unsignedPrePeginTx: batch.unsignedPrePeginTx,\n vaultProvider: batch.vaultProvider,\n depositorBtcPubkeyRaw: batch.depositorBtcPubkeyRaw,\n },\n depositor,\n depositorBtcPubkey,\n );\n if (seenVaultIds.has(normalized.vaultId)) {\n throw new Error(`Duplicate vault in batch: ${normalized.vaultId}`);\n }\n seenVaultIds.add(normalized.vaultId);\n vaults.push({\n vaultId: normalized.vaultId,\n peginTxHash: normalized.peginTxHash,\n });\n requests.push({\n depositorBtcPubKey: depositorBtcPubkey,\n btcPopSignature,\n unsignedPrePeginTx,\n depositorSignedPeginTx: normalized.depositorSignedPeginTx,\n hashlock: request.hashlock,\n htlcVout: request.htlcVout,\n referralCode: NO_REFERRAL_CODE,\n depositorPayoutBtcAddress: normalized.payoutScript,\n depositorWotsPkHash: request.depositorWotsPkHash,\n });\n }\n for (const vault of vaults) {\n await this.assertVaultDoesNotExist(vault.vaultId, vault.peginTxHash);\n }\n\n const peginFee = await this.readPeginFee(batch.vaultProvider);\n const maxCommission = await this.resolveMaxAcceptableCommissionBps(\n batch.vaultProvider,\n batch.quotedCommissionBps,\n );\n const callData = encodeFunctionData({\n abi: BTCVaultRegistryABI,\n functionName: \"submitPeginRequestBatch\",\n // `expectedFingerprint` sits BEFORE `requests`, not appended. Reordering\n // these five changes the selector and the call stops existing.\n args: [\n depositor,\n batch.vaultProvider,\n maxCommission,\n batch.expectedFingerprint,\n requests,\n ],\n });\n const ethTxHash = await this.sendAndWait(\n callData,\n peginFee * BigInt(batch.requests.length),\n true,\n depositor,\n );\n return { ethTxHash, vaults };\n }\n\n private assertQuotedCommissionPresentWhenRequired(\n quotedCommissionBps?: number,\n ): void {\n if (\n quotedCommissionBps === undefined &&\n this.config.requireQuotedCommissionBps\n ) {\n throw new Error(\n \"quotedCommissionBps is required when the wallet approved deposit \" +\n \"terms: the registration ceiling must anchor to the approved quote.\",\n );\n }\n }\n\n private assertPopMatchesEthAccount(pop: PopSignature): Address {\n return this.assertEthAccount(pop.depositorEthAddress);\n }\n\n private assertEthAccount(expectedDepositor: Address): Address {\n const account = this.config.ethWallet.account;\n if (!account) throw new Error(\"Ethereum wallet account not found\");\n if (!isAddressEqual(expectedDepositor, account.address)) {\n throw new Error(\n `Proof of possession was signed for ${expectedDepositor} ` +\n `but the Ethereum wallet is currently connected to ${account.address}. ` +\n `Reconnect the original account or sign a new proof of possession.`,\n );\n }\n return account.address;\n }\n\n private normalizeBtcPubkey(pop: PopSignature): Hex {\n return `0x${assertOnChainBtcPubkey(\n ensureHex(pop.depositorBtcPubkey, \"Proof of possession BTC pubkey\"),\n \"Proof of possession BTC pubkey\",\n )}`;\n }\n\n // `expectedFingerprint` is omitted rather than accepted and ignored: nothing\n // here normalizes it, and requiring it would force the batch loop to thread a\n // value through a per-request helper that has no use for it. It is validated\n // once per call, at the top of each public method.\n private normalizeRequest(\n params: Omit<\n RegisterPeginOnChainParams,\n \"popSignature\" | \"expectedFingerprint\"\n >,\n depositor: Address,\n depositorBtcPubkey: Hex,\n ) {\n assertBytes32(params.hashlock, \"hashlock\");\n assertBytes32(params.depositorWotsPkHash, \"depositorWotsPkHash\");\n assertHtlcVout(params.htlcVout);\n const unsignedPrePeginTx = ensureHex(\n params.unsignedPrePeginTx,\n \"unsignedPrePeginTx\",\n );\n const depositorSignedPeginTx = ensureHex(\n params.depositorSignedPeginTx,\n \"depositorSignedPeginTx\",\n );\n const peginTxHash = calculateBtcTxHash(depositorSignedPeginTx);\n return {\n unsignedPrePeginTx,\n depositorSignedPeginTx,\n payoutScript: assertPayoutScriptMatchesPopKey(\n params.depositorPayoutScriptPubKey,\n depositorBtcPubkey,\n params.depositorBtcPubkeyRaw,\n ),\n peginTxHash,\n vaultId: ensureHex(derivePeginVaultId(peginTxHash, depositor), \"vaultId\"),\n };\n }\n\n private async assertVaultDoesNotExist(\n vaultId: Hex,\n peginTxHash: Hex,\n ): Promise<void> {\n const result = (await this.config.publicClient.readContract({\n address: this.config.btcVaultRegistry,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n })) as { depositor: Address };\n if (result.depositor !== zeroAddress) {\n throw new Error(\n `Vault already exists (ID: ${vaultId}, peginTxHash: ${peginTxHash}). ` +\n `Vault IDs are derived from the pegin transaction hash and depositor address. ` +\n `To create a new vault, use different UTXOs or a different amount to generate a unique transaction.`,\n );\n }\n }\n\n private async readPeginFee(vaultProvider: Address): Promise<bigint> {\n try {\n return (await this.config.publicClient.readContract({\n address: this.config.btcVaultRegistry,\n abi: BTCVaultRegistryABI,\n functionName: \"getPegInFee\",\n args: [vaultProvider],\n })) as bigint;\n } catch (error) {\n throw new Error(\n \"Failed to query pegin fee from the contract. \" +\n \"Please check your network connection and contract address.\",\n { cause: error },\n );\n }\n }\n\n private async resolveMaxAcceptableCommissionBps(\n vaultProvider: Address,\n quotedCommissionBps?: number,\n ): Promise<number> {\n let currentBps: number;\n try {\n currentBps = await new ViemVaultRegistryReader(\n this.config.publicClient,\n this.config.btcVaultRegistry,\n ).getVaultProviderCommission(vaultProvider);\n } catch (error) {\n throw new Error(\n \"Failed to query vault provider commission from the contract. \" +\n \"Please check your network connection and contract address.\",\n { cause: error },\n );\n }\n if (quotedCommissionBps !== undefined) {\n if (currentBps > quotedCommissionBps + COMMISSION_BPS_HEADROOM) {\n throw new Error(\n `Vault provider commission changed since quote: quoted ${quotedCommissionBps} bps, ` +\n `chain currently reports ${currentBps} bps (allowed drift ${COMMISSION_BPS_HEADROOM} bps). ` +\n `Please refresh and try again.`,\n );\n }\n return capMaxAcceptableCommissionBps(quotedCommissionBps);\n }\n return capMaxAcceptableCommissionBps(currentBps);\n }\n\n private async sendAndWait(\n data: Hex,\n value: bigint,\n batch: boolean,\n depositor: Address,\n ): Promise<Hex> {\n let account = this.assertEthAccount(depositor);\n let gas: bigint;\n try {\n gas = await this.config.publicClient.estimateGas({\n to: this.config.btcVaultRegistry,\n data,\n value,\n account,\n });\n } catch (error) {\n handleContractError(error);\n }\n\n account = this.assertEthAccount(depositor);\n let submittedHash: Hex;\n try {\n submittedHash = await this.config.ethWallet.sendTransaction({\n to: this.config.btcVaultRegistry,\n data,\n value,\n gas,\n account,\n chain: this.config.ethChain,\n });\n } catch (error) {\n handleContractError(error);\n }\n const receipt = await waitForTransactionReceiptSmartAware({\n publicClient: this.config.publicClient,\n walletAddress: depositor,\n hash: submittedHash,\n timeout: this.config.receiptTimeoutMs ?? RECEIPT_TIMEOUT_MS,\n });\n if (receipt.status === \"reverted\") {\n handleContractError(\n new Error(\n `${batch ? \"Batch transaction\" : \"Transaction\"} reverted. ` +\n `Hash: ${receipt.transactionHash}. Check a block explorer for details.`,\n ),\n );\n }\n return receipt.transactionHash;\n }\n}\n"],"names":["COMMISSION_BPS_HEADROOM","MAX_ACCEPTABLE_COMMISSION_BPS_CAP","capMaxAcceptableCommissionBps","bps","MAX_VP_COMMISSION_BPS_EXCLUSIVE","MAX_VP_COMMISSION_BPS","mapVaultBasicInfo","result","mapVaultProtocolInfo","offchainParamsVersion","assertValidOffchainParamsVersion","UINT64_EXCLUSIVE_UPPER_BOUND","VP_GENESIS_KEY_EPOCH","PEGIN_SUBMITTED_EVENT","getAbiItem","BTCVaultRegistryABI","VAULT_CLAIMABLE_BY_EVENT","VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS","maxBigint","a","b","mapRegistrationRecord","args","blockNumber","assertEpochInRange","value","field","vaultId","mapKeyEpochs","ViemVaultRegistryReader","publicClient","contractAddress","vpAddress","epoch","assertOnChainBtcPubkey","BTCVaultRegistryKeyEpochsABI","vaultIds","info","i","vaultProvider","basicRaw","protocolRaw","basic","protocol","createdAt","logs","RegistrationLogsUnavailableError","records","log","record","findRegistrationRecord","claimerPk","claimerTopic","vaultIdTopic","finalized","toBlock","fromBlock","transactions","VaultClaimableByNotFoundError","NO_REFERRAL_CODE","UINT8_MAX","RECEIPT_TIMEOUT_MS","ensureHex","label","prefixed","assertBytes32","ZERO_BYTES32","assertFingerprint","assertHtlcVout","ViemPeginRegistrationClient","config","params","request","depositor","depositorBtcPubkey","btcPopSignature","normalized","peginFee","maxCommission","callData","encodeFunctionData","batch","unsignedPrePeginTx","vaults","requests","seenVaultIds","vault","quotedCommissionBps","pop","expectedDepositor","account","isAddressEqual","depositorSignedPeginTx","peginTxHash","calculateBtcTxHash","assertPayoutScriptMatchesPopKey","derivePeginVaultId","zeroAddress","error","currentBps","data","gas","handleContractError","submittedHash","receipt","waitForTransactionReceiptSmartAware"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pegin-registration-client-ZI0k10_3.js","sources":["../src/tbv/core/deposit-terms/commissionCeiling.ts","../src/tbv/core/clients/eth/vault-registry-reader.ts","../src/tbv/core/clients/eth/pegin-registration-client.ts"],"sourcesContent":["/**\n * The depositor's commission ceiling (`maxAcceptableCommissionBps`) policy:\n * quoted VP commission + drift headroom, capped below the contract's\n * exclusive bound. Shared by `PeginManager` (the approved terms) and the\n * registration calldata so both carry the same ceiling; a resume rebuild reads\n * that submitted value back from the `PegInSubmitted` log instead.\n *\n * @module deposit-terms/commissionCeiling\n */\n\n/*\n * Commission-check map (each guards a DIFFERENT boundary — do not consolidate):\n * 1. assertVpCommissionInProtocolRange (vault app, vaultPayoutSignatureService)\n * — chain-read trust boundary; mirrors VPKeyRegistryLogic.sol bounds.\n * 2. capMaxAcceptableCommissionBps (here) — depositor quote → ceiling policy;\n * mirrors PeginLogic.sol's strict > check via the +25bps headroom.\n * 3. buildDepositTerms range check — public-API precondition on the projection\n * that mints the device-enforced commissionFee.\n * 4. payout.ts commission cap — the VP-built tx's output value vs bps at the\n * signing site (CLAUDE.md Critical Path #3).\n * 5. envelope.ts dust/cross-field gates (ledger-vault-signer) — firmware-only\n * constants, pre-device-I/O (Critical Path #7).\n */\n\nimport { MAX_VP_COMMISSION_BPS_EXCLUSIVE } from \"../primitives/psbt/constants\";\n\n/**\n * Headroom (in basis points) added to the current VP commission to compute\n * `maxAcceptableCommissionBps` at submit time. Lets the VP raise its\n * commission by up to this amount between read and submit without forcing\n * a re-quote. Capped by {@link MAX_ACCEPTABLE_COMMISSION_BPS_CAP}.\n *\n * Contract check is strict `>` (PeginLogic.sol `VaultProviderCommissionExceeded`\n * revert), so +25 allows up\n * to +25 bps of drift.\n */\nexport const COMMISSION_BPS_HEADROOM = 25;\n\n/**\n * Hard ceiling for `maxAcceptableCommissionBps`. The contract enforces\n * `commissionBps < 10000`, so any value at/above that is unreachable;\n * `9999` is the maximum useful cap.\n */\nexport const MAX_ACCEPTABLE_COMMISSION_BPS_CAP = 9999;\n\n/**\n * The commission ceiling submitted as registration calldata and mirrored\n * into `DepositTerms.commissionFee`: quoted + drift headroom, capped.\n * Single source for both consumers — feed it the SAME quoted bps at prepare\n * and register time so device-accept stays coextensive with contract-accept.\n */\nexport function capMaxAcceptableCommissionBps(bps: number): number {\n // Validate the raw quote before headroom shifts its domain — a negative\n // quote must throw here, not become a small \"legal\" ceiling.\n // Reject an out-of-range quote outright — clamping it to 9999 would turn a\n // bad read into a 99.99% ceiling, the exact failure the cap exists to stop.\n if (\n !Number.isInteger(bps) ||\n bps < 0 ||\n bps >= MAX_VP_COMMISSION_BPS_EXCLUSIVE\n ) {\n throw new Error(\n `Quoted commissionBps must be an integer in ` +\n `[0, ${MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${bps}`,\n );\n }\n return Math.min(\n bps + COMMISSION_BPS_HEADROOM,\n MAX_ACCEPTABLE_COMMISSION_BPS_CAP,\n );\n}\n","/**\n * Concrete BTCVaultRegistry reader using viem's readContract.\n *\n * This is an optional utility — callers can use their own implementation\n * of the VaultRegistryReader interface.\n */\n\nimport type { Abi, Address, Hex, PublicClient } from \"viem\";\nimport { getAbiItem } from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { BTCVaultRegistryKeyEpochsABI } from \"../../contracts/abis/BTCVaultRegistryKeyEpochs.abi\";\nimport { VaultClaimableByNotFoundError } from \"./claimable-event-error\";\nimport { assertOnChainBtcPubkey } from \"./onChainBtcPubkey\";\nimport { assertValidOffchainParamsVersion } from \"./protocol-params-validation\";\nimport { RegistrationLogsUnavailableError } from \"./registration-logs-error\";\nimport { findRegistrationRecord } from \"./registration-records\";\nimport type {\n KeyEpochs,\n OnChainBtcPubkey,\n PeginRegistrationRecord,\n VaultBasicInfo,\n VaultClaimableByEvent,\n VaultData,\n VaultProtocolInfo,\n VaultRegistryReader,\n} from \"./types\";\n\n/**\n * Inclusive upper bound the BTCVaultRegistry contract enforces on a vault\n * provider's commission (the contract check is `< 10000`).\n */\nconst MAX_VP_COMMISSION_BPS = 9999;\n\n/** Raw `getBtcVaultBasicInfo` tuple as decoded by viem. */\ntype RawVaultBasicInfo = {\n depositor: Address;\n depositorBtcPubKey: Hex;\n amount: bigint;\n vaultProvider: Address;\n status: number;\n applicationEntryPoint: Address;\n createdAt: bigint;\n};\n\n/**\n * Raw `getBtcVaultProtocolInfo` tuple as decoded by viem.\n *\n * Mirrors the ABI's components. The trailing key epochs are read through the\n * separate key-epochs ABI, so this prefix stops at `vaultCoreVersion`.\n */\ntype RawVaultProtocolInfo = {\n depositorSignedPeginTx: Hex;\n universalChallengersVersion: number;\n appVaultKeepersVersion: number;\n offchainParamsVersion: number;\n verifiedAt: bigint;\n depositorWotsPkHash: Hex;\n hashlock: Hex;\n htlcVout: number;\n depositorPopSignature: Hex;\n prePeginTxHash: Hex;\n vaultProviderCommissionBps: number;\n claimExpiredUntil: bigint;\n vaultCoreVersion: number;\n};\n\nfunction mapVaultBasicInfo(result: RawVaultBasicInfo): VaultBasicInfo {\n return {\n depositor: result.depositor,\n depositorBtcPubKey: result.depositorBtcPubKey,\n amount: result.amount,\n vaultProvider: result.vaultProvider,\n status: result.status,\n applicationEntryPoint: result.applicationEntryPoint,\n createdAt: result.createdAt,\n };\n}\n\nfunction mapVaultProtocolInfo(result: RawVaultProtocolInfo): VaultProtocolInfo {\n const offchainParamsVersion = Number(result.offchainParamsVersion);\n assertValidOffchainParamsVersion(offchainParamsVersion);\n return {\n depositorSignedPeginTx: result.depositorSignedPeginTx,\n universalChallengersVersion: result.universalChallengersVersion,\n appVaultKeepersVersion: result.appVaultKeepersVersion,\n offchainParamsVersion,\n verifiedAt: result.verifiedAt,\n depositorWotsPkHash: result.depositorWotsPkHash,\n hashlock: result.hashlock,\n htlcVout: result.htlcVout,\n depositorPopSignature: result.depositorPopSignature,\n prePeginTxHash: result.prePeginTxHash,\n vaultProviderCommissionBps: result.vaultProviderCommissionBps,\n claimExpiredUntil: result.claimExpiredUntil,\n vaultCoreVersion: result.vaultCoreVersion,\n };\n}\n\n/**\n * Exclusive upper bound for a `uint64` epoch.\n *\n * viem does not range-check a decoded `uint64`, so a misaligned decode can\n * hand back a value far larger than the field can hold. Rejecting those is\n * cheap defence in depth against a mis-decode of the extended ABI.\n *\n * The state this is load-bearing for is narrower than \"a registry that predates\n * RFC-006\". Against a *fully* pre-RFC-006 registry the path fails closed on its\n * own: `resolveParticipantKeysAtEpochs` goes on to call\n * `getOperationBtcKeyAtEpochOrGenesis` on ApplicationRegistry and\n * ProtocolParams, which do not exist there, so the multicall reverts and these\n * epochs are never used. The reachable gap is a registry that *has* the\n * operation-key getters but whose `BTCVaultProtocolInfo` struct is not\n * extended — there the tail-data epochs resolve with no error at all, and this\n * range check is the only thing looking at them.\n *\n * Even for that case it is only a backstop, and a weak one: a misaligned decode\n * can land on a small, plausible-looking integer this range accepts. Correctness\n * rests on only ever pointing at an RFC-006 registry, which is a deployment\n * precondition rather than something this call can establish — see\n * https://github.com/babylonlabs-io/babylon-toolkit/issues/2192 for where that\n * check is owned, and `BTCVaultRegistryKeyEpochs.abi.ts` for the decode hazard.\n */\nconst UINT64_EXCLUSIVE_UPPER_BOUND = 1n << 64n;\n\n/**\n * The epoch a vault provider's registration key is bonded at.\n *\n * A provider's operation-key history is append-only and every appended version\n * is stamped at epoch 1 or later, so epoch 0 always resolves to the key set at\n * registration. This is what lets `getOperationBtcKeyAtEpoch` stand in for the\n * removed `getVaultProviderBTCKey` getter.\n */\nconst VP_GENESIS_KEY_EPOCH = 0n;\n\nconst PEGIN_SUBMITTED_EVENT = getAbiItem({\n abi: BTCVaultRegistryABI,\n name: \"PegInSubmitted\",\n});\n\nconst VAULT_CLAIMABLE_BY_EVENT = getAbiItem({\n abi: BTCVaultRegistryABI,\n name: \"VaultClaimableBy\",\n});\n\n/**\n * Block span of one `VaultClaimableBy` query: btc-vault's own scan chunk\n * (`EVENT_QUERY_CHUNK_BLOCKS = 7200`, `crates/eth-client/src/client.rs:793`\n * @ ac4954e7), about a day of Ethereum blocks, so a recent redeem costs one\n * request. Not every provider serves a range this wide — an Alchemy free key\n * caps `eth_getLogs` at 10 blocks\n * (https://www.alchemy.com/docs/reference/eth-getlogs) — and a provider that\n * answered a wide range short instead of erroring would make this scan report\n * not-found, so confirming the RPC honours 7,200-block ranges is a\n * prerequisite of a claim run.\n */\nconst VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS = 7_200n;\n\nfunction maxBigint(a: bigint, b: bigint): bigint {\n return a > b ? a : b;\n}\n\n/** A `PegInSubmitted` log's decoded args, as viem's strict `getLogs` hands them back. */\ninterface PegInSubmittedArgs {\n vaultId: Hex;\n peginTxHash: Hex;\n depositor: Address;\n vaultProvider: Address;\n amount: bigint;\n vaultCoreVersion: number;\n universalChallengersVersion: number;\n appVaultKeepersVersion: number;\n proverCircuitVersion: number;\n offchainParamsVersion: number;\n depositorPayoutBtcAddress: Hex;\n depositorWotsPkHash: Hex;\n hashlock: Hex;\n htlcVout: number;\n unsignedPrePeginTx: Hex;\n maxAcceptableCommissionBps: number;\n}\n\nfunction mapRegistrationRecord(\n args: PegInSubmittedArgs,\n blockNumber: bigint,\n): PeginRegistrationRecord {\n // A plain decode of every registration log in the block, strangers'\n // included: nothing here parses a transaction or bound-checks a script, so\n // one malformed registration elsewhere in the block cannot fail the\n // caller's read. The strict per-record checks live in\n // registration-records.ts. A duplicate vault id is not covered: a vault\n // registers once, so two logs for one id are an inconsistent node and fail\n // the read closed (see the caller).\n return {\n vaultId: args.vaultId.toLowerCase() as Hex,\n depositor: args.depositor,\n vaultProvider: args.vaultProvider,\n amount: args.amount,\n vaultCoreVersion: args.vaultCoreVersion,\n universalChallengersVersion: args.universalChallengersVersion,\n appVaultKeepersVersion: args.appVaultKeepersVersion,\n proverCircuitVersion: args.proverCircuitVersion,\n offchainParamsVersion: args.offchainParamsVersion,\n peginTxHash: args.peginTxHash.toLowerCase() as Hex,\n depositorPayoutScriptPubKey:\n args.depositorPayoutBtcAddress.toLowerCase() as Hex,\n unsignedPrePeginTx: args.unsignedPrePeginTx,\n maxAcceptableCommissionBps: args.maxAcceptableCommissionBps,\n blockNumber,\n };\n}\n\nfunction assertEpochInRange(\n value: bigint,\n field: string,\n vaultId: Hex,\n): bigint {\n if (value < 0n || value >= UINT64_EXCLUSIVE_UPPER_BOUND) {\n throw new Error(\n `getBtcVaultProtocolInfo returned ${field}=${value} for vault ${vaultId}, ` +\n `outside the uint64 range — the registry may predate RFC-006`,\n );\n }\n return value;\n}\n\nfunction mapKeyEpochs(result: KeyEpochs, vaultId: Hex): KeyEpochs {\n return {\n vpKeyEpoch: assertEpochInRange(result.vpKeyEpoch, \"vpKeyEpoch\", vaultId),\n appKeeperKeyEpoch: assertEpochInRange(\n result.appKeeperKeyEpoch,\n \"appKeeperKeyEpoch\",\n vaultId,\n ),\n ucKeyEpoch: assertEpochInRange(result.ucKeyEpoch, \"ucKeyEpoch\", vaultId),\n };\n}\n\n/**\n * Concrete vault registry reader using viem.\n *\n * Usage:\n * ```ts\n * const reader = new ViemVaultRegistryReader(publicClient, registryAddress);\n * const data = await reader.getVaultData(vaultId);\n * ```\n */\nexport class ViemVaultRegistryReader implements VaultRegistryReader {\n constructor(\n private publicClient: PublicClient,\n private contractAddress: Address,\n ) {}\n\n /**\n * Read the VP's **genesis** (registration) x-only BTC pubkey — the key bonded\n * at version 0, which never moves when the operator rotates.\n *\n * Resolved as \"the operation key at epoch 0\" rather than through the\n * dedicated `getVaultProviderBTCKey` getter, which\n * https://github.com/babylonlabs-io/vault-contracts-aave-v4/pull/539 removes.\n * Epoch 0 predates any rotation — appended versions are stamped at epoch 1 or\n * later — so it resolves to the registration key, and the contracts team has\n * confirmed that is a property we can rely on rather than an implementation\n * detail. The devnet comparison behind that claim — both getters returning the\n * identical key for a provider that *has* rotated, while\n * `getCurrentOperationBtcKey` differed — is recorded in\n * https://github.com/babylonlabs-io/babylon-toolkit/issues/2188.\n *\n * This makes the read RFC-006-only, where the removed getter also existed on a\n * legacy registry. That costs nothing: every caller of this method already\n * resolves participant keys through `OperationKeyReader`, so all of them\n * require an RFC-006 registry regardless.\n *\n * Validates length, hex form, and secp256k1 curve membership before minting\n * the brand. Returns 64-char lowercase hex without the `0x` prefix.\n */\n async getVaultProviderGenesisBtcPubKey(\n vpAddress: Address,\n blockNumber?: bigint,\n ): Promise<OnChainBtcPubkey> {\n return this.getVaultProviderOperationBtcKeyAtEpoch(\n vpAddress,\n VP_GENESIS_KEY_EPOCH,\n blockNumber,\n );\n }\n\n /**\n * Read the VP operation key selected by `epoch`.\n *\n * This is public rather than hidden inside `OperationKeyReader` because\n * subject-specific VP authentication needs exactly one participant: the\n * gRPC bootstrap is signed by the key frozen into the vault, while the\n * JSON-RPC bootstrap is signed by the provider's live key.\n */\n async getVaultProviderOperationBtcKeyAtEpoch(\n vpAddress: Address,\n epoch: bigint,\n blockNumber?: bigint,\n ): Promise<OnChainBtcPubkey> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getOperationBtcKeyAtEpoch\",\n args: [vpAddress, epoch],\n blockNumber,\n })) as Hex;\n return assertOnChainBtcPubkey(\n result,\n `getOperationBtcKeyAtEpoch (vp=${vpAddress}, epoch=${epoch})`,\n );\n }\n\n /**\n * Read the application entry point a vault provider is registered for.\n *\n * This is the registry's own `vaultProviders[vp].applicationEntryPoint`, and\n * it is the value the peg-in submit path resolves internally — it selects\n * which application's vault-keeper roster, roster version and keeper key\n * epoch a deposit is bonded to. The dApp separately carries an entry point\n * from its own configuration; the two agree today, but they are different\n * sources of truth, so the build path reads this one and asserts the\n * configured value matches it rather than trusting either alone.\n */\n async getVaultProviderApplication(\n vpAddress: Address,\n blockNumber?: bigint,\n ): Promise<Address> {\n return (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getVaultProviderApplication\",\n args: [vpAddress],\n blockNumber,\n })) as Address;\n }\n\n /**\n * Read a vault provider's *current* RFC-006 operation BTC key.\n *\n * Falls back on-chain to the registration key when the provider has never\n * rotated, so this returns the same value as\n * `getVaultProviderGenesisBtcPubKey` until the first rotation.\n *\n * This is the key the VP's server uses for JSON-RPC-subject authentication.\n * The gRPC-subject bootstrap is instead bound to the existing vault's frozen\n * epoch and resolves through `getVaultProviderOperationBtcKeyAtEpoch`.\n */\n async getCurrentVaultProviderOperationBtcKey(\n vpAddress: Address,\n ): Promise<OnChainBtcPubkey> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getCurrentOperationBtcKey\",\n args: [vpAddress],\n })) as Hex;\n return assertOnChainBtcPubkey(\n result,\n `getCurrentOperationBtcKey (vp=${vpAddress})`,\n );\n }\n\n /**\n * Read a vault's frozen RFC-006 operation-key epochs.\n *\n * Reads `getBtcVaultProtocolInfo` through the **extended** ABI, which is only\n * valid against an RFC-006 registry: against one whose `BTCVaultProtocolInfo`\n * struct is not extended this call does not fail for a populated vault, it\n * silently returns three words of tail data as epochs. Nothing here can detect\n * that, so the guarantee is a deployment one — every network this ships to has\n * the RFC-006 getters, and mainnet is a fresh RFC-006 deploy.\n *\n * A registry missing the operation-key getters entirely is the *safer* of the\n * two cases: `resolveParticipantKeysAtEpochs` reverts downstream and these\n * epochs never reach key resolution. See {@link UINT64_EXCLUSIVE_UPPER_BOUND}\n * for which state the range check actually guards, and\n * `BTCVaultRegistryKeyEpochs.abi.ts` for the decode hazard.\n */\n async getVaultKeyEpochs(vaultId: Hex): Promise<KeyEpochs> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryKeyEpochsABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n })) as unknown as KeyEpochs;\n\n return mapKeyEpochs(result, vaultId);\n }\n\n async getVaultKeyEpochsBatch(vaultIds: readonly Hex[]): Promise<KeyEpochs[]> {\n if (vaultIds.length === 0) return [];\n\n const results = await this.publicClient.multicall({\n contracts: vaultIds.map((vaultId) => ({\n address: this.contractAddress,\n abi: BTCVaultRegistryKeyEpochsABI as Abi,\n functionName: \"getBtcVaultProtocolInfo\" as const,\n args: [vaultId] as const,\n })),\n allowFailure: false,\n });\n\n return results.map((info, i) =>\n mapKeyEpochs(info as unknown as KeyEpochs, vaultIds[i]),\n );\n }\n\n async getVaultBasicInfo(vaultId: Hex): Promise<VaultBasicInfo> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n })) as RawVaultBasicInfo;\n\n return mapVaultBasicInfo(result);\n }\n\n async getVaultProtocolInfo(vaultId: Hex): Promise<VaultProtocolInfo> {\n const result = (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n })) as RawVaultProtocolInfo;\n\n return mapVaultProtocolInfo(result);\n }\n\n async getProtocolInfoBatch(\n vaultIds: readonly Hex[],\n ): Promise<VaultProtocolInfo[]> {\n if (vaultIds.length === 0) return [];\n\n const results = await this.publicClient.multicall({\n contracts: vaultIds.map((vaultId) => ({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI as Abi,\n functionName: \"getBtcVaultProtocolInfo\" as const,\n args: [vaultId] as const,\n })),\n allowFailure: false,\n });\n\n return results.map((info, i) => {\n const result = info as unknown as RawVaultProtocolInfo;\n if (\n !result.depositorSignedPeginTx ||\n result.depositorSignedPeginTx === \"0x\"\n ) {\n // An empty record is not proof the vault is absent: a lagging RPC\n // node returns HTTP 200 with a zero struct, so no retry below this\n // layer can see it. Single-shot is only safe because the finality\n // gate (`waitForPeginRegistrationDepth`) runs first on the deposit\n // path — move this ahead of it and the read-after-write race returns.\n // The vault app matches this message to render \"still confirming\".\n throw new Error(\n `Vault ${vaultIds[i]} not found on-chain or has no pegin transaction`,\n );\n }\n return mapVaultProtocolInfo(result);\n });\n }\n\n /**\n * Read the protocol pegin fee (in wei) for a given vault provider.\n * Mirrors the `getPegInFee(address)` view on BTCVaultRegistry.\n */\n async getPegInFee(vaultProvider: Address): Promise<bigint> {\n return (await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getPegInFee\",\n args: [vaultProvider],\n })) as bigint;\n }\n\n /**\n * Read a vault provider's current commission in basis points from\n * BTCVaultRegistry. The contract enforces `commissionBps < 10000`, so the\n * legitimate range is `[0, 9999]`; anything outside indicates a wrong\n * contract address or ABI drift and is surfaced as an error rather than\n * trusted.\n */\n async getVaultProviderCommission(vaultProvider: Address): Promise<number> {\n // viem infers `number` from the `uint16` return in the `as const` ABI.\n const bps = await this.publicClient.readContract({\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getVaultProviderCommission\",\n args: [vaultProvider],\n });\n\n if (!Number.isInteger(bps) || bps < 0 || bps > MAX_VP_COMMISSION_BPS) {\n throw new Error(\n `getVaultProviderCommission returned ${bps} bps for ${vaultProvider}, ` +\n `outside the protocol range [0, ${MAX_VP_COMMISSION_BPS}]`,\n );\n }\n\n return bps;\n }\n\n async getVaultData(vaultId: Hex): Promise<VaultData> {\n // One round-trip for both structs (hard-fail): they feed signing/refund/\n // broadcast rebinds, so reading them in a single multicall also pins both\n // to the same block — no basic/protocol skew across two `eth_call`s.\n const [basicRaw, protocolRaw] = await this.publicClient.multicall({\n contracts: [\n {\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n },\n {\n address: this.contractAddress,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultProtocolInfo\",\n args: [vaultId],\n },\n ],\n allowFailure: false,\n });\n\n const basic = mapVaultBasicInfo(basicRaw);\n const protocol = mapVaultProtocolInfo(protocolRaw);\n\n if (\n !protocol.depositorSignedPeginTx ||\n protocol.depositorSignedPeginTx === \"0x\"\n ) {\n throw new Error(\n `Vault ${vaultId} not found on-chain or has no pegin transaction`,\n );\n }\n\n return { basic, protocol };\n }\n\n /** @inheritdoc */\n async getRegistrationRecordsAtBlock(\n createdAt: bigint,\n ): Promise<PeginRegistrationRecord[]> {\n // Deliberately no `vaultId` topic filter: the block holds the target's\n // registration, so only an unfiltered answer can tell a node that served\n // nothing (empty) from one that lacks the target (findRegistrationRecord),\n // and one query serves every vault a caller reads from the block. strict:\n // a log whose data does not decode against the ABI is dropped (viem\n // parseEventLogs) instead of surfacing as `args: {}`.\n const logs = await this.publicClient.getLogs({\n address: this.contractAddress,\n event: PEGIN_SUBMITTED_EVENT,\n fromBlock: createdAt,\n toBlock: createdAt,\n strict: true,\n });\n if (logs.length === 0) {\n throw new RegistrationLogsUnavailableError(createdAt);\n }\n\n const records = new Map<string, PeginRegistrationRecord>();\n for (const log of logs) {\n const record = mapRegistrationRecord(log.args, log.blockNumber);\n // Lenient decoding covers a malformed log, not a duplicate: a vault\n // registers once, so two logs for any id mean the node is inconsistent\n // and every record from this block is suspect — fail closed by design.\n if (records.has(record.vaultId)) {\n throw new Error(\n `Expected one PegInSubmitted log for vault ${record.vaultId} at block ${createdAt}, ` +\n `found more than one`,\n );\n }\n records.set(record.vaultId, record);\n }\n return [...records.values()];\n }\n\n /**\n * Read the depositor's commission ceiling (`maxAcceptableCommissionBps`)\n * for vaults registered in the same block, from their `PegInSubmitted`\n * logs. Returned in `vaultIds` order.\n *\n * The contract bound-checks the ceiling and discards it (PeginLogic.sol,\n * `VaultProviderCommissionExceeded`), so the registration log is its only\n * on-chain source. Vault ids are matched case-insensitively.\n *\n * @throws As {@link getRegistrationRecordsAtBlock}, plus the same typed\n * transient error when a vault has no registration log in its own\n * `createdAt` block.\n */\n async getMaxAcceptableCommissionBpsBatch(\n vaultIds: readonly Hex[],\n createdAt: bigint,\n ): Promise<number[]> {\n if (vaultIds.length === 0) return [];\n\n const records = await this.getRegistrationRecordsAtBlock(createdAt);\n return vaultIds.map(\n (vaultId) =>\n findRegistrationRecord(records, vaultId, createdAt)\n .maxAcceptableCommissionBps,\n );\n }\n\n /** @inheritdoc */\n async getVaultClaimableBy(\n vaultId: Hex,\n claimerPk: Hex,\n createdAt: bigint,\n ): Promise<VaultClaimableByEvent> {\n const claimer = assertOnChainBtcPubkey(\n claimerPk,\n `getVaultClaimableBy claimer (vault=${vaultId})`,\n );\n const claimerTopic = `0x${claimer}` as Hex;\n // viem re-filters the decoded logs client-side against `args`, comparing a\n // bytes32 with `===` against lower-case hex (parseEventLogs.ts:140,172-188).\n const vaultIdTopic = vaultId.toLowerCase() as Hex;\n // Finalized, not latest: the block found here is what the prover proves\n // against, and an unfinalized redeem can reorg away (btc-vault binds the\n // same scan to its finality level, eth-client client.rs:5989-6040).\n const { number: finalized } = await this.publicClient.getBlock({\n blockTag: \"finalized\",\n });\n if (finalized < createdAt) {\n throw new Error(\n `Finalized block ${finalized} is below vault ${vaultId}'s registration block ${createdAt}; ` +\n `the registration is not finalized yet, or the node is behind it`,\n );\n }\n\n // Newest first: the redeem usually trails the tip by far less than it\n // trails the registration. The contract emits every VaultClaimableBy of a\n // vault in one transaction, so the first non-empty chunk is the answer.\n let toBlock = finalized;\n for (;;) {\n const fromBlock = maxBigint(\n createdAt,\n toBlock - VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS + 1n,\n );\n const logs = await this.publicClient.getLogs({\n address: this.contractAddress,\n event: VAULT_CLAIMABLE_BY_EVENT,\n args: { vaultId: vaultIdTopic, claimerPK: claimerTopic },\n fromBlock,\n toBlock,\n strict: true,\n });\n // A redeem emits every VaultClaimableBy of a vault in ONE transaction\n // (RedeemLogic.sol), and `redeemForDepositor` emits the VP's key and\n // then the depositor's — so when those two keys are equal this filter\n // legitimately matches twice. Identical authorizations from one\n // transaction are the same fact; logs spanning transactions are not.\n if (logs.length > 1) {\n const transactions = new Set(logs.map((log) => log.transactionHash));\n if (transactions.size > 1) {\n throw new Error(\n `Found ${logs.length} VaultClaimableBy logs for vault ${vaultId} and claimer ` +\n `${claimerTopic} across ${transactions.size} transactions in blocks ` +\n `${fromBlock}..${toBlock}; a vault is redeemed once, so the node's answer ` +\n `is inconsistent`,\n );\n }\n }\n if (logs.length >= 1) {\n const log = logs[0];\n return {\n blockNumber: log.blockNumber,\n claimerPk: assertOnChainBtcPubkey(\n log.args.claimerPK,\n `VaultClaimableBy.claimerPK (vault=${vaultId})`,\n ),\n peginTxHash: log.args.peginTxHash,\n vaultCoreVersion: log.args.vaultCoreVersion,\n proverCircuitVersion: log.args.proverCircuitVersion,\n offchainParamsVersion: log.args.offchainParamsVersion,\n universalChallengersVersion: log.args.universalChallengersVersion,\n appVaultKeepersVersion: log.args.appVaultKeepersVersion,\n };\n }\n if (fromBlock === createdAt) {\n throw new VaultClaimableByNotFoundError(\n vaultId,\n claimerTopic,\n createdAt,\n finalized,\n );\n }\n toBlock = fromBlock - 1n;\n }\n }\n}\n","import {\n encodeFunctionData,\n isAddressEqual,\n zeroAddress,\n type Address,\n type Chain,\n type Hex,\n type PublicClient,\n type WalletClient,\n} from \"viem\";\n\nimport { BTCVaultRegistryABI } from \"../../contracts/abis/BTCVaultRegistry.abi\";\nimport { handleContractError } from \"../../contracts/errors\";\nimport {\n capMaxAcceptableCommissionBps,\n COMMISSION_BPS_HEADROOM,\n} from \"../../deposit-terms/commissionCeiling\";\nimport { waitForTransactionReceiptSmartAware } from \"../../utils/eth\";\nimport { assertOnChainBtcPubkey } from \"./onChainBtcPubkey\";\nimport { assertPayoutScriptMatchesPopKey } from \"./payout-script\";\nimport { calculateBtcTxHash, derivePeginVaultId } from \"./pegin-transaction\";\nimport { ViemVaultRegistryReader } from \"./vault-registry-reader\";\n\n/** Referral code sent with peg-in registration. Zero means no referral. */\nexport const NO_REFERRAL_CODE = 0;\nconst UINT8_MAX = 255;\n// Match the prior vault-service timeout. A dropped transaction must not wait forever.\nconst RECEIPT_TIMEOUT_MS = 120_000;\n\nfunction ensureHex(value: string, label: string): Hex {\n const prefixed = value.startsWith(\"0x\")\n ? value\n : value.startsWith(\"0X\")\n ? `0x${value.slice(2)}`\n : `0x${value}`;\n if (!/^0x(?:[0-9a-fA-F]{2})+$/.test(prefixed)) {\n throw new Error(`${label} must be non-empty, even-length hex`);\n }\n return prefixed as Hex;\n}\n\nfunction assertBytes32(value: Hex, label: string): void {\n if (!/^0x[0-9a-fA-F]{64}$/.test(value)) {\n throw new Error(`${label} must be a 32-byte hex value`);\n }\n}\n\n/** All-zero `bytes32` — the shape an unresolved fingerprint takes. */\nconst ZERO_BYTES32 =\n \"0x0000000000000000000000000000000000000000000000000000000000000000\";\n\n/**\n * The single validation point for `expectedFingerprint`, on both the singular\n * and batch paths.\n *\n * Zero is rejected separately from the shape check because it is a *well-formed*\n * `bytes32` that the registry can never accept — the live fingerprint is a\n * keccak256 over a domain-separated preimage — so it can only ever mean the\n * caller had no value and filled the slot. Catching it here turns a defaulted\n * field into a named error instead of an opaque on-chain revert with two\n * hashes and no hint.\n */\nfunction assertFingerprint(value: Hex): void {\n assertBytes32(value, \"expectedFingerprint\");\n if (value.toLowerCase() === ZERO_BYTES32) {\n throw new Error(\n \"expectedFingerprint must not be zero: the registry cannot resolve a \" +\n \"zero fingerprint, so this submission would always revert\",\n );\n }\n}\n\nfunction assertHtlcVout(value: number): void {\n if (!Number.isInteger(value) || value < 0 || value > UINT8_MAX) {\n throw new Error(`htlcVout must be a uint8, got ${value}`);\n }\n}\n\n/** A PoP prepared by a Bitcoin wallet, reusable by the ETH submit client. */\nexport interface PopSignature {\n btcPopSignature: Hex;\n depositorEthAddress: Address;\n /** x-only secp256k1 public key, 64 hex chars without `0x`. */\n depositorBtcPubkey: string;\n}\n\nexport interface ViemPeginRegistrationClientConfig {\n ethWallet: WalletClient;\n ethChain: Chain;\n publicClient: PublicClient;\n btcVaultRegistry: Address;\n receiptTimeoutMs?: number;\n /**\n * Refuse to register without a `quotedCommissionBps`. Set it to `true` when\n * the depositor approved deposit terms on a device: approval froze the\n * commission ceiling from the quote, so the chain-current fallback could\n * admit a commission the device would later refuse to pay out.\n *\n * Required, with no default: an omitted flag would silently pick the unsafe\n * direction for the integrator least able to probe the wallet capability.\n */\n requireQuotedCommissionBps: boolean;\n}\n\nexport interface RegisterPeginOnChainParams {\n unsignedPrePeginTx: string;\n depositorSignedPeginTx: string;\n vaultProvider: Address;\n hashlock: Hex;\n depositorWotsPkHash: Hex;\n popSignature: PopSignature;\n /** Raw x-only, compressed, or uncompressed BTC pubkey from the PoP wallet. */\n depositorBtcPubkeyRaw: string;\n htlcVout: number;\n /**\n * Bitcoin output script bytes, not an address. The client verifies that the\n * script pays the proof-of-possession key before it submits the transaction.\n */\n depositorPayoutScriptPubKey: string;\n quotedCommissionBps?: number;\n /**\n * `keccak256(abi.encode(...))` over the protocol state the Pre-PegIn was\n * built against — see the SDK's `pegin-fingerprint` module.\n *\n * The registry recomputes it live at inclusion and reverts with\n * `PeginFingerprintChanged` on any difference, so it must be computed from\n * the same block-pinned snapshot that shaped the Bitcoin scripts. Required,\n * with no default: a fingerprint the caller did not resolve is not a\n * fingerprint, and the registry has no way to tell the two apart.\n */\n expectedFingerprint: Hex;\n}\n\nexport interface PeginRegistrationResult {\n ethTxHash: Hex;\n vaultId: Hex;\n peginTxHash: Hex;\n}\n\nexport interface BatchPeginRegistrationItem {\n depositorSignedPeginTx: string;\n hashlock: Hex;\n htlcVout: number;\n /**\n * Bitcoin output script bytes, not an address. The client verifies that the\n * script pays the proof-of-possession key before it submits the transaction.\n */\n depositorPayoutScriptPubKey: string;\n depositorWotsPkHash: Hex;\n}\n\nexport interface RegisterPeginBatchOnChainParams {\n vaultProvider: Address;\n unsignedPrePeginTx: string;\n requests: BatchPeginRegistrationItem[];\n popSignature: PopSignature;\n /** Raw x-only, compressed, or uncompressed BTC pubkey from the PoP wallet. */\n depositorBtcPubkeyRaw: string;\n quotedCommissionBps?: number;\n /**\n * See {@link RegisterPeginOnChainParams.expectedFingerprint}. One value for\n * the whole batch: the fingerprint takes no per-request input and a batch\n * fixes one vault provider, so every entry would resolve the same value.\n */\n expectedFingerprint: Hex;\n}\n\nexport interface BatchPeginRegistrationResultItem {\n vaultId: Hex;\n peginTxHash: Hex;\n}\n\nexport interface PeginBatchRegistrationResult {\n ethTxHash: Hex;\n vaults: BatchPeginRegistrationResultItem[];\n}\n\n/**\n * Ethereum-only client for the on-chain half of peg-in registration.\n *\n * It consumes Bitcoin artifacts that were prepared earlier, but owns no BTC\n * wallet and imports neither Bitcoin libraries nor the Rust/WASM package.\n */\nexport class ViemPeginRegistrationClient {\n constructor(private readonly config: ViemPeginRegistrationClientConfig) {}\n\n async registerPeginOnChain(\n params: RegisterPeginOnChainParams,\n ): Promise<PeginRegistrationResult> {\n const request = {\n ...params,\n popSignature: { ...params.popSignature },\n };\n this.assertQuotedCommissionPresentWhenRequired(request.quotedCommissionBps);\n // Second, matching the batch path, so the two public entry points validate\n // in the same order and neither can drift behind a chain call.\n assertFingerprint(request.expectedFingerprint);\n const depositor = this.assertPopMatchesEthAccount(request.popSignature);\n const depositorBtcPubkey = this.normalizeBtcPubkey(request.popSignature);\n const btcPopSignature = ensureHex(\n request.popSignature.btcPopSignature,\n \"btcPopSignature\",\n );\n const normalized = this.normalizeRequest(\n request,\n depositor,\n depositorBtcPubkey,\n );\n\n await this.assertVaultDoesNotExist(\n normalized.vaultId,\n normalized.peginTxHash,\n );\n const peginFee = await this.readPeginFee(request.vaultProvider);\n const maxCommission = await this.resolveMaxAcceptableCommissionBps(\n request.vaultProvider,\n request.quotedCommissionBps,\n );\n const callData = encodeFunctionData({\n abi: BTCVaultRegistryABI,\n functionName: \"submitPeginRequest\",\n args: [\n depositor,\n depositorBtcPubkey,\n btcPopSignature,\n normalized.unsignedPrePeginTx,\n normalized.depositorSignedPeginTx,\n request.vaultProvider,\n maxCommission,\n request.hashlock,\n request.htlcVout,\n normalized.payoutScript,\n request.depositorWotsPkHash,\n // Appended last on the singular overload; the batch takes it at\n // position 4 instead. Order is the selector, so do not \"tidy\" either.\n request.expectedFingerprint,\n ],\n });\n\n const ethTxHash = await this.sendAndWait(\n callData,\n peginFee,\n false,\n depositor,\n );\n return {\n ethTxHash,\n vaultId: normalized.vaultId,\n peginTxHash: normalized.peginTxHash,\n };\n }\n\n async registerPeginBatchOnChain(\n params: RegisterPeginBatchOnChainParams,\n ): Promise<PeginBatchRegistrationResult> {\n const batch = {\n ...params,\n popSignature: { ...params.popSignature },\n requests: params.requests.map((request) => ({ ...request })),\n };\n if (batch.requests.length === 0) {\n throw new Error(\"Batch pegin requires at least one request\");\n }\n this.assertQuotedCommissionPresentWhenRequired(batch.quotedCommissionBps);\n assertFingerprint(batch.expectedFingerprint);\n const depositor = this.assertPopMatchesEthAccount(batch.popSignature);\n const unsignedPrePeginTx = ensureHex(\n batch.unsignedPrePeginTx,\n \"unsignedPrePeginTx\",\n );\n const depositorBtcPubkey = this.normalizeBtcPubkey(batch.popSignature);\n const btcPopSignature = ensureHex(\n batch.popSignature.btcPopSignature,\n \"btcPopSignature\",\n );\n\n const vaults: BatchPeginRegistrationResultItem[] = [];\n const requests = [];\n const seenVaultIds = new Set<string>();\n for (const request of batch.requests) {\n const normalized = this.normalizeRequest(\n {\n ...request,\n unsignedPrePeginTx: batch.unsignedPrePeginTx,\n vaultProvider: batch.vaultProvider,\n depositorBtcPubkeyRaw: batch.depositorBtcPubkeyRaw,\n },\n depositor,\n depositorBtcPubkey,\n );\n if (seenVaultIds.has(normalized.vaultId)) {\n throw new Error(`Duplicate vault in batch: ${normalized.vaultId}`);\n }\n seenVaultIds.add(normalized.vaultId);\n vaults.push({\n vaultId: normalized.vaultId,\n peginTxHash: normalized.peginTxHash,\n });\n requests.push({\n depositorBtcPubKey: depositorBtcPubkey,\n btcPopSignature,\n unsignedPrePeginTx,\n depositorSignedPeginTx: normalized.depositorSignedPeginTx,\n hashlock: request.hashlock,\n htlcVout: request.htlcVout,\n referralCode: NO_REFERRAL_CODE,\n depositorPayoutBtcAddress: normalized.payoutScript,\n depositorWotsPkHash: request.depositorWotsPkHash,\n });\n }\n for (const vault of vaults) {\n await this.assertVaultDoesNotExist(vault.vaultId, vault.peginTxHash);\n }\n\n const peginFee = await this.readPeginFee(batch.vaultProvider);\n const maxCommission = await this.resolveMaxAcceptableCommissionBps(\n batch.vaultProvider,\n batch.quotedCommissionBps,\n );\n const callData = encodeFunctionData({\n abi: BTCVaultRegistryABI,\n functionName: \"submitPeginRequestBatch\",\n // `expectedFingerprint` sits BEFORE `requests`, not appended. Reordering\n // these five changes the selector and the call stops existing.\n args: [\n depositor,\n batch.vaultProvider,\n maxCommission,\n batch.expectedFingerprint,\n requests,\n ],\n });\n const ethTxHash = await this.sendAndWait(\n callData,\n peginFee * BigInt(batch.requests.length),\n true,\n depositor,\n );\n return { ethTxHash, vaults };\n }\n\n private assertQuotedCommissionPresentWhenRequired(\n quotedCommissionBps?: number,\n ): void {\n if (\n quotedCommissionBps === undefined &&\n this.config.requireQuotedCommissionBps\n ) {\n throw new Error(\n \"quotedCommissionBps is required when the wallet approved deposit \" +\n \"terms: the registration ceiling must anchor to the approved quote.\",\n );\n }\n }\n\n private assertPopMatchesEthAccount(pop: PopSignature): Address {\n return this.assertEthAccount(pop.depositorEthAddress);\n }\n\n private assertEthAccount(expectedDepositor: Address): Address {\n const account = this.config.ethWallet.account;\n if (!account) throw new Error(\"Ethereum wallet account not found\");\n if (!isAddressEqual(expectedDepositor, account.address)) {\n throw new Error(\n `Proof of possession was signed for ${expectedDepositor} ` +\n `but the Ethereum wallet is currently connected to ${account.address}. ` +\n `Reconnect the original account or sign a new proof of possession.`,\n );\n }\n return account.address;\n }\n\n private normalizeBtcPubkey(pop: PopSignature): Hex {\n return `0x${assertOnChainBtcPubkey(\n ensureHex(pop.depositorBtcPubkey, \"Proof of possession BTC pubkey\"),\n \"Proof of possession BTC pubkey\",\n )}`;\n }\n\n // `expectedFingerprint` is omitted rather than accepted and ignored: nothing\n // here normalizes it, and requiring it would force the batch loop to thread a\n // value through a per-request helper that has no use for it. It is validated\n // once per call, at the top of each public method.\n private normalizeRequest(\n params: Omit<\n RegisterPeginOnChainParams,\n \"popSignature\" | \"expectedFingerprint\"\n >,\n depositor: Address,\n depositorBtcPubkey: Hex,\n ) {\n assertBytes32(params.hashlock, \"hashlock\");\n assertBytes32(params.depositorWotsPkHash, \"depositorWotsPkHash\");\n assertHtlcVout(params.htlcVout);\n const unsignedPrePeginTx = ensureHex(\n params.unsignedPrePeginTx,\n \"unsignedPrePeginTx\",\n );\n const depositorSignedPeginTx = ensureHex(\n params.depositorSignedPeginTx,\n \"depositorSignedPeginTx\",\n );\n const peginTxHash = calculateBtcTxHash(depositorSignedPeginTx);\n return {\n unsignedPrePeginTx,\n depositorSignedPeginTx,\n payoutScript: assertPayoutScriptMatchesPopKey(\n params.depositorPayoutScriptPubKey,\n depositorBtcPubkey,\n params.depositorBtcPubkeyRaw,\n ),\n peginTxHash,\n vaultId: ensureHex(derivePeginVaultId(peginTxHash, depositor), \"vaultId\"),\n };\n }\n\n private async assertVaultDoesNotExist(\n vaultId: Hex,\n peginTxHash: Hex,\n ): Promise<void> {\n const result = (await this.config.publicClient.readContract({\n address: this.config.btcVaultRegistry,\n abi: BTCVaultRegistryABI,\n functionName: \"getBtcVaultBasicInfo\",\n args: [vaultId],\n })) as { depositor: Address };\n if (result.depositor !== zeroAddress) {\n throw new Error(\n `Vault already exists (ID: ${vaultId}, peginTxHash: ${peginTxHash}). ` +\n `Vault IDs are derived from the pegin transaction hash and depositor address. ` +\n `To create a new vault, use different UTXOs or a different amount to generate a unique transaction.`,\n );\n }\n }\n\n private async readPeginFee(vaultProvider: Address): Promise<bigint> {\n try {\n return (await this.config.publicClient.readContract({\n address: this.config.btcVaultRegistry,\n abi: BTCVaultRegistryABI,\n functionName: \"getPegInFee\",\n args: [vaultProvider],\n })) as bigint;\n } catch (error) {\n throw new Error(\n \"Failed to query pegin fee from the contract. \" +\n \"Please check your network connection and contract address.\",\n { cause: error },\n );\n }\n }\n\n private async resolveMaxAcceptableCommissionBps(\n vaultProvider: Address,\n quotedCommissionBps?: number,\n ): Promise<number> {\n let currentBps: number;\n try {\n currentBps = await new ViemVaultRegistryReader(\n this.config.publicClient,\n this.config.btcVaultRegistry,\n ).getVaultProviderCommission(vaultProvider);\n } catch (error) {\n throw new Error(\n \"Failed to query vault provider commission from the contract. \" +\n \"Please check your network connection and contract address.\",\n { cause: error },\n );\n }\n if (quotedCommissionBps !== undefined) {\n if (currentBps > quotedCommissionBps + COMMISSION_BPS_HEADROOM) {\n throw new Error(\n `Vault provider commission changed since quote: quoted ${quotedCommissionBps} bps, ` +\n `chain currently reports ${currentBps} bps (allowed drift ${COMMISSION_BPS_HEADROOM} bps). ` +\n `Please refresh and try again.`,\n );\n }\n return capMaxAcceptableCommissionBps(quotedCommissionBps);\n }\n return capMaxAcceptableCommissionBps(currentBps);\n }\n\n private async sendAndWait(\n data: Hex,\n value: bigint,\n batch: boolean,\n depositor: Address,\n ): Promise<Hex> {\n let account = this.assertEthAccount(depositor);\n let gas: bigint;\n try {\n gas = await this.config.publicClient.estimateGas({\n to: this.config.btcVaultRegistry,\n data,\n value,\n account,\n });\n } catch (error) {\n handleContractError(error);\n }\n\n account = this.assertEthAccount(depositor);\n let submittedHash: Hex;\n try {\n submittedHash = await this.config.ethWallet.sendTransaction({\n to: this.config.btcVaultRegistry,\n data,\n value,\n gas,\n account,\n chain: this.config.ethChain,\n });\n } catch (error) {\n handleContractError(error);\n }\n const receipt = await waitForTransactionReceiptSmartAware({\n publicClient: this.config.publicClient,\n walletAddress: depositor,\n hash: submittedHash,\n timeout: this.config.receiptTimeoutMs ?? RECEIPT_TIMEOUT_MS,\n });\n if (receipt.status === \"reverted\") {\n handleContractError(\n new Error(\n `${batch ? \"Batch transaction\" : \"Transaction\"} reverted. ` +\n `Hash: ${receipt.transactionHash}. Check a block explorer for details.`,\n ),\n );\n }\n return receipt.transactionHash;\n }\n}\n"],"names":["COMMISSION_BPS_HEADROOM","MAX_ACCEPTABLE_COMMISSION_BPS_CAP","capMaxAcceptableCommissionBps","bps","MAX_VP_COMMISSION_BPS_EXCLUSIVE","MAX_VP_COMMISSION_BPS","mapVaultBasicInfo","result","mapVaultProtocolInfo","offchainParamsVersion","assertValidOffchainParamsVersion","UINT64_EXCLUSIVE_UPPER_BOUND","VP_GENESIS_KEY_EPOCH","PEGIN_SUBMITTED_EVENT","getAbiItem","BTCVaultRegistryABI","VAULT_CLAIMABLE_BY_EVENT","VAULT_CLAIMABLE_BY_QUERY_CHUNK_BLOCKS","maxBigint","a","b","mapRegistrationRecord","args","blockNumber","assertEpochInRange","value","field","vaultId","mapKeyEpochs","ViemVaultRegistryReader","publicClient","contractAddress","vpAddress","epoch","assertOnChainBtcPubkey","BTCVaultRegistryKeyEpochsABI","vaultIds","info","i","vaultProvider","basicRaw","protocolRaw","basic","protocol","createdAt","logs","RegistrationLogsUnavailableError","records","log","record","findRegistrationRecord","claimerPk","claimerTopic","vaultIdTopic","finalized","toBlock","fromBlock","transactions","VaultClaimableByNotFoundError","NO_REFERRAL_CODE","UINT8_MAX","RECEIPT_TIMEOUT_MS","ensureHex","label","prefixed","assertBytes32","ZERO_BYTES32","assertFingerprint","assertHtlcVout","ViemPeginRegistrationClient","config","params","request","depositor","depositorBtcPubkey","btcPopSignature","normalized","peginFee","maxCommission","callData","encodeFunctionData","batch","unsignedPrePeginTx","vaults","requests","seenVaultIds","vault","quotedCommissionBps","pop","expectedDepositor","account","isAddressEqual","depositorSignedPeginTx","peginTxHash","calculateBtcTxHash","assertPayoutScriptMatchesPopKey","derivePeginVaultId","zeroAddress","error","currentBps","data","gas","handleContractError","submittedHash","receipt","waitForTransactionReceiptSmartAware"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"peginState-CIibG9S_.js","sources":["../src/tbv/core/services/deposit/peginState.ts"],"sourcesContent":["// ============================================================================\n// State Definitions\n// ============================================================================\n\n/**\n * Vault status — combines on-chain contract status (0-4) with indexer-derived\n * statuses (5-7). The contract enum (BTCVaultRegistry.sol BTCVaultStatus) only\n * has: Pending(0), Verified(1), Active(2), Redeemed(3), Expired(4).\n * The indexer maps these and adds extra statuses for UI display.\n *\n * IMPORTANT: With the new contract architecture:\n * - Core vault status (BTCVaultRegistry) does NOT change when used by applications\n * - Vaults remain at ACTIVE status even when used in DeFi positions\n * - Application usage status is tracked separately by each integration controller\n */\nexport enum ContractStatus {\n /** Status 0: Request submitted, waiting for ACKs */\n PENDING = 0,\n /** Status 1: All ACKs collected, ready for secret activation */\n VERIFIED = 1,\n /** Status 2: HTLC secret revealed, vault is active and usable (stays here even when used by apps) */\n ACTIVE = 2,\n /** Status 3: Vault has been redeemed, BTC is claimable */\n REDEEMED = 3,\n /** Status 4 (indexer-only): Vault was liquidated (collateral seized due to unpaid debt) */\n LIQUIDATED = 4,\n /** Status 5 (indexer-only): Vault is invalid — BTC UTXOs were spent in a different transaction */\n INVALID = 5,\n /** Status 6 (indexer-only): Depositor has withdrawn their BTC (redemption complete) */\n DEPOSITOR_WITHDRAWN = 6,\n /** Status 7 (indexer-only): Vault expired due to AckTimeout or ActivationTimeout */\n EXPIRED = 7,\n}\n\n/** Reason why a vault expired */\nexport type ExpirationReason =\n | \"ack_timeout\"\n | \"proof_timeout\"\n | \"activation_timeout\";\n\n// ============================================================================\n// Protocol State Model\n// ============================================================================\n\n/**\n * Available actions user can take\n */\nexport enum PeginAction {\n /** Submit WOTS key (re-derives via wallet `deriveContextHash`) */\n SUBMIT_WOTS_KEY = \"SUBMIT_WOTS_KEY\",\n /** Sign payout transactions */\n SIGN_PAYOUT_TRANSACTIONS = \"SIGN_PAYOUT_TRANSACTIONS\",\n /** Sign and broadcast peg-in transaction to Bitcoin */\n SIGN_AND_BROADCAST_TO_BITCOIN = \"SIGN_AND_BROADCAST_TO_BITCOIN\",\n /** Reveal HTLC secret on Ethereum to activate vault */\n ACTIVATE_VAULT = \"ACTIVATE_VAULT\",\n /**\n * Escape hatch: reveal the HTLC secret and immediately redeem the vault for\n * the depositor (`activateVaultWithSecretAndRedeem`), skipping application\n * activation. Recovery path when the peg-in was swept on Bitcoin but the\n * vault could not be activated (application paused / activation revert).\n */\n ACTIVATE_AND_REDEEM = \"ACTIVATE_AND_REDEEM\",\n /** Sign and broadcast HTLC refund transaction for an expired vault */\n REFUND_HTLC = \"REFUND_HTLC\",\n /**\n * Reveal the HTLC secret for a vault that expired after reaching Verified\n * (`claimExpiredVault`), so the BTC the PegIn swept into the vault is\n * claimed out to the keys recorded on chain. The only exit once the PegIn\n * has spent the HTLC; the contract accepts it until the vault's\n * `claimExpiredUntil` block.\n */\n CLAIM_EXPIRED_VAULT = \"CLAIM_EXPIRED_VAULT\",\n}\n\n/**\n * Protocol-level peg-in state (framework-agnostic)\n */\nexport interface PeginProtocolState {\n /** Smart contract status (source of truth for on-chain state) */\n contractStatus: ContractStatus;\n /** Available user actions (empty array when no action is available) */\n availableActions: PeginAction[];\n}\n\n/**\n * Options for getPeginProtocolState function.\n *\n * All fields represent protocol-level state from the vault provider or\n * on-chain contracts. Client-side tracking (localStorage, polling state)\n * is NOT included — consumers handle that in their own layer.\n */\nexport interface GetPeginProtocolStateOptions {\n /** Whether claim/payout transactions are ready from VP */\n transactionsReady?: boolean;\n /** Whether the vault provider is waiting for the depositor's WOTS public key */\n needsWotsKey?: boolean;\n /** Whether the vault provider hasn't ingested this peg-in yet */\n pendingIngestion?: boolean;\n /** Whether the depositor can refund the HTLC (Pre-PegIn tx available) */\n canRefund?: boolean;\n /** Whether the vault provider reported a terminal failure */\n hasProviderTerminalFailure?: boolean;\n /**\n * VERIFIED only: the Pre-PegIn HTLC outpoint has been spent on Bitcoin BY\n * THE PEGIN TRANSACTION while the vault is still Verified on Ethereum. The\n * secret was revealed (e.g. in the calldata of a reverted activation) and\n * the peg-in swept without the vault activating, so the normal activation\n * no longer returns value to the depositor and the CSV refund can never\n * broadcast. The remaining recovery is the activate-and-redeem escape\n * hatch.\n *\n * The caller MUST prove the spender by comparing the outspend's\n * `spendingTxid` against the vault's PegIn txid before setting this. A\n * bare \"spent\" observation is not sufficient: the spend may be the\n * depositor's own CSV refund, and offering the secret-revealing hatch\n * against a refund burns the secret for a vault whose funds already\n * returned.\n */\n htlcSpentByPeginTx?: boolean;\n /**\n * EXPIRED only: the vault expired after reaching Verified, the PegIn\n * transaction has spent the Pre-PegIn HTLC, and the claim window has not\n * elapsed. The CSV refund is gone, so the claim is the remaining exit.\n *\n * The same spender proof as `htlcSpentByPeginTx` applies: offering the claim\n * while the HTLC is unspent reveals the secret and lets anyone broadcast the\n * PegIn ahead of the depositor's refund. When set, it takes precedence over\n * `canRefund`, which a proven PegIn spend makes impossible.\n */\n canClaimExpired?: boolean;\n}\n\n// ============================================================================\n// State Machine Logic\n// ============================================================================\n\n/**\n * Determine the current protocol state and available actions based on contract\n * status and vault provider state. Framework-agnostic: returns only\n * protocol-level data with no display labels, messages, or UI concerns.\n *\n * Client-side tracking overrides (e.g. suppressing actions after the user\n * has already acted but on-chain state hasn't caught up) are the caller's\n * responsibility.\n *\n * @param contractStatus - On-chain contract status (source of truth)\n * @param options - Vault provider state\n * @returns Protocol state with available actions\n */\nexport function getPeginProtocolState(\n contractStatus: ContractStatus,\n options: GetPeginProtocolStateOptions = {},\n): PeginProtocolState {\n const {\n transactionsReady,\n needsWotsKey,\n pendingIngestion,\n canRefund,\n hasProviderTerminalFailure,\n htlcSpentByPeginTx,\n canClaimExpired,\n } = options;\n\n if (contractStatus === ContractStatus.PENDING) {\n if (hasProviderTerminalFailure) {\n return { contractStatus, availableActions: [] };\n }\n\n if (needsWotsKey) {\n return {\n contractStatus,\n availableActions: [PeginAction.SUBMIT_WOTS_KEY],\n };\n }\n\n if (pendingIngestion === true && !transactionsReady) {\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_AND_BROADCAST_TO_BITCOIN],\n };\n }\n\n if (pendingIngestion === undefined && !transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n if (!transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_PAYOUT_TRANSACTIONS],\n };\n }\n\n if (contractStatus === ContractStatus.VERIFIED) {\n // A spent HTLC while still Verified means the peg-in was swept without\n // activation: activating normally would hand the collateral to an\n // application flow that already failed once, and refunding is impossible\n // (the outpoint is gone). Surface only the escape hatch.\n if (htlcSpentByPeginTx) {\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_AND_REDEEM],\n };\n }\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_VAULT],\n };\n }\n\n if (contractStatus === ContractStatus.ACTIVE) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.REDEEMED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.LIQUIDATED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.EXPIRED) {\n if (canClaimExpired) {\n return {\n contractStatus,\n availableActions: [PeginAction.CLAIM_EXPIRED_VAULT],\n };\n }\n return {\n contractStatus,\n availableActions: canRefund ? [PeginAction.REFUND_HTLC] : [],\n };\n }\n\n if (contractStatus === ContractStatus.INVALID) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.DEPOSITOR_WITHDRAWN) {\n return { contractStatus, availableActions: [] };\n }\n\n return { contractStatus, availableActions: [] };\n}\n\n/**\n * Check if a specific action is available in the current state\n */\nexport function canPerformAction(\n state: PeginProtocolState,\n action: PeginAction,\n): boolean {\n return state.availableActions.includes(action);\n}\n\n// ============================================================================\n// Activation deadline\n// ============================================================================\n\n/**\n * Whether a vault's on-chain activation window has closed. Mirrors the\n * BTCVaultRegistry check that reverts `ActivationDeadlineExpired`:\n * `block.number > createdAt + pegInActivationTimeout` — strict `>`, so a\n * boundary-equal block is NOT expired. All values are Ethereum block numbers.\n */\nexport function isActivationDeadlinePassedOnChain(params: {\n currentBlock: bigint;\n createdAtBlock: bigint;\n pegInActivationTimeout: bigint;\n}): boolean {\n const { currentBlock, createdAtBlock, pegInActivationTimeout } = params;\n return currentBlock > createdAtBlock + pegInActivationTimeout;\n}\n\n/**\n * Blocks still available for an activation transaction to be mined; `0` once\n * the window has closed.\n *\n * `isActivationDeadlinePassedOnChain` answers whether the window is already\n * shut, which is the right question for a badge but the wrong one for a\n * secret-bearing call. An activation submitted in the last block before the\n * deadline reverts if it lands one block late, and by then `s` is public\n * calldata: the vault expires with `ActivationTimeout`, and the vault\n * provider, which holds the rest of the HTLC signature set, can broadcast the\n * PegIn with that secret. The caller therefore needs the remaining margin,\n * not a boolean.\n *\n * The contract accepts a transaction mined at `block.number <= createdAt +\n * timeout`. `currentBlock` is the chain head, which is already mined, so a new\n * transaction lands at `currentBlock + 1` at the earliest: at\n * `currentBlock === createdAt + timeout` no usable block remains.\n */\nexport function activationDeadlineBlocksRemaining(params: {\n currentBlock: bigint;\n createdAtBlock: bigint;\n pegInActivationTimeout: bigint;\n}): number {\n const { currentBlock, createdAtBlock, pegInActivationTimeout } = params;\n const lastUsableBlock = createdAtBlock + pegInActivationTimeout;\n if (currentBlock >= lastUsableBlock) return 0;\n return Number(lastUsableBlock - currentBlock);\n}\n"],"names":["ContractStatus","PeginAction","getPeginProtocolState","contractStatus","options","transactionsReady","needsWotsKey","pendingIngestion","canRefund","hasProviderTerminalFailure","htlcSpentByPeginTx","canClaimExpired","canPerformAction","state","action","isActivationDeadlinePassedOnChain","params","currentBlock","createdAtBlock","pegInActivationTimeout","activationDeadlineBlocksRemaining","lastUsableBlock"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use strict";var l=(i=>(i[i.PENDING=0]="PENDING",i[i.VERIFIED=1]="VERIFIED",i[i.ACTIVE=2]="ACTIVE",i[i.REDEEMED=3]="REDEEMED",i[i.LIQUIDATED=4]="LIQUIDATED",i[i.INVALID=5]="INVALID",i[i.DEPOSITOR_WITHDRAWN=6]="DEPOSITOR_WITHDRAWN",i[i.EXPIRED=7]="EXPIRED",i))(l||{}),E=(i=>(i.SUBMIT_WOTS_KEY="SUBMIT_WOTS_KEY",i.SIGN_PAYOUT_TRANSACTIONS="SIGN_PAYOUT_TRANSACTIONS",i.SIGN_AND_BROADCAST_TO_BITCOIN="SIGN_AND_BROADCAST_TO_BITCOIN",i.ACTIVATE_VAULT="ACTIVATE_VAULT",i.ACTIVATE_AND_REDEEM="ACTIVATE_AND_REDEEM",i.REFUND_HTLC="REFUND_HTLC",i.CLAIM_EXPIRED_VAULT="CLAIM_EXPIRED_VAULT",i))(E||{});function r(i,e={}){const{transactionsReady:A,needsWotsKey:T,pendingIngestion:I,canRefund:n,hasProviderTerminalFailure:_,htlcSpentByPeginTx:D,canClaimExpired:N}=e;return i===0?_?{contractStatus:i,availableActions:[]}:T?{contractStatus:i,availableActions:["SUBMIT_WOTS_KEY"]}:I===!0&&!A?{contractStatus:i,availableActions:["SIGN_AND_BROADCAST_TO_BITCOIN"]}:I===void 0&&!A?{contractStatus:i,availableActions:[]}:A?{contractStatus:i,availableActions:["SIGN_PAYOUT_TRANSACTIONS"]}:{contractStatus:i,availableActions:[]}:i===1?D?{contractStatus:i,availableActions:["ACTIVATE_AND_REDEEM"]}:{contractStatus:i,availableActions:["ACTIVATE_VAULT"]}:i===2?{contractStatus:i,availableActions:[]}:i===3?{contractStatus:i,availableActions:[]}:i===4?{contractStatus:i,availableActions:[]}:i===7?N?{contractStatus:i,availableActions:["CLAIM_EXPIRED_VAULT"]}:{contractStatus:i,availableActions:n?["REFUND_HTLC"]:[]}:i===5?{contractStatus:i,availableActions:[]}:i===6?{contractStatus:i,availableActions:[]}:{contractStatus:i,availableActions:[]}}function R(i,e){return i.availableActions.includes(e)}function v(i){const{currentBlock:e,createdAtBlock:A,pegInActivationTimeout:T}=i;return e>A+T}function O(i){const{currentBlock:e,createdAtBlock:A,pegInActivationTimeout:T}=i,I=A+T;return e>=I?0:Number(I-e)}exports.ContractStatus=l;exports.PeginAction=E;exports.activationDeadlineBlocksRemaining=O;exports.canPerformAction=R;exports.getPeginProtocolState=r;exports.isActivationDeadlinePassedOnChain=v;
2
- //# sourceMappingURL=peginState-IyE22wWh.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"peginState-IyE22wWh.cjs","sources":["../src/tbv/core/services/deposit/peginState.ts"],"sourcesContent":["// ============================================================================\n// State Definitions\n// ============================================================================\n\n/**\n * Vault status — combines on-chain contract status (0-4) with indexer-derived\n * statuses (5-7). The contract enum (BTCVaultRegistry.sol BTCVaultStatus) only\n * has: Pending(0), Verified(1), Active(2), Redeemed(3), Expired(4).\n * The indexer maps these and adds extra statuses for UI display.\n *\n * IMPORTANT: With the new contract architecture:\n * - Core vault status (BTCVaultRegistry) does NOT change when used by applications\n * - Vaults remain at ACTIVE status even when used in DeFi positions\n * - Application usage status is tracked separately by each integration controller\n */\nexport enum ContractStatus {\n /** Status 0: Request submitted, waiting for ACKs */\n PENDING = 0,\n /** Status 1: All ACKs collected, ready for secret activation */\n VERIFIED = 1,\n /** Status 2: HTLC secret revealed, vault is active and usable (stays here even when used by apps) */\n ACTIVE = 2,\n /** Status 3: Vault has been redeemed, BTC is claimable */\n REDEEMED = 3,\n /** Status 4 (indexer-only): Vault was liquidated (collateral seized due to unpaid debt) */\n LIQUIDATED = 4,\n /** Status 5 (indexer-only): Vault is invalid — BTC UTXOs were spent in a different transaction */\n INVALID = 5,\n /** Status 6 (indexer-only): Depositor has withdrawn their BTC (redemption complete) */\n DEPOSITOR_WITHDRAWN = 6,\n /** Status 7 (indexer-only): Vault expired due to AckTimeout or ActivationTimeout */\n EXPIRED = 7,\n}\n\n/** Reason why a vault expired */\nexport type ExpirationReason =\n | \"ack_timeout\"\n | \"proof_timeout\"\n | \"activation_timeout\";\n\n// ============================================================================\n// Protocol State Model\n// ============================================================================\n\n/**\n * Available actions user can take\n */\nexport enum PeginAction {\n /** Submit WOTS key (re-derives via wallet `deriveContextHash`) */\n SUBMIT_WOTS_KEY = \"SUBMIT_WOTS_KEY\",\n /** Sign payout transactions */\n SIGN_PAYOUT_TRANSACTIONS = \"SIGN_PAYOUT_TRANSACTIONS\",\n /** Sign and broadcast peg-in transaction to Bitcoin */\n SIGN_AND_BROADCAST_TO_BITCOIN = \"SIGN_AND_BROADCAST_TO_BITCOIN\",\n /** Reveal HTLC secret on Ethereum to activate vault */\n ACTIVATE_VAULT = \"ACTIVATE_VAULT\",\n /**\n * Escape hatch: reveal the HTLC secret and immediately redeem the vault for\n * the depositor (`activateVaultWithSecretAndRedeem`), skipping application\n * activation. Recovery path when the peg-in was swept on Bitcoin but the\n * vault could not be activated (application paused / activation revert).\n */\n ACTIVATE_AND_REDEEM = \"ACTIVATE_AND_REDEEM\",\n /** Sign and broadcast HTLC refund transaction for an expired vault */\n REFUND_HTLC = \"REFUND_HTLC\",\n /**\n * Reveal the HTLC secret for a vault that expired after reaching Verified\n * (`claimExpiredVault`), so the BTC the PegIn swept into the vault is\n * claimed out to the keys recorded on chain. The only exit once the PegIn\n * has spent the HTLC; the contract accepts it until the vault's\n * `claimExpiredUntil` block.\n */\n CLAIM_EXPIRED_VAULT = \"CLAIM_EXPIRED_VAULT\",\n}\n\n/**\n * Protocol-level peg-in state (framework-agnostic)\n */\nexport interface PeginProtocolState {\n /** Smart contract status (source of truth for on-chain state) */\n contractStatus: ContractStatus;\n /** Available user actions (empty array when no action is available) */\n availableActions: PeginAction[];\n}\n\n/**\n * Options for getPeginProtocolState function.\n *\n * All fields represent protocol-level state from the vault provider or\n * on-chain contracts. Client-side tracking (localStorage, polling state)\n * is NOT included — consumers handle that in their own layer.\n */\nexport interface GetPeginProtocolStateOptions {\n /** Whether claim/payout transactions are ready from VP */\n transactionsReady?: boolean;\n /** Whether the vault provider is waiting for the depositor's WOTS public key */\n needsWotsKey?: boolean;\n /** Whether the vault provider hasn't ingested this peg-in yet */\n pendingIngestion?: boolean;\n /** Whether the depositor can refund the HTLC (Pre-PegIn tx available) */\n canRefund?: boolean;\n /** Whether the vault provider reported a terminal failure */\n hasProviderTerminalFailure?: boolean;\n /**\n * VERIFIED only: the Pre-PegIn HTLC outpoint has been spent on Bitcoin BY\n * THE PEGIN TRANSACTION while the vault is still Verified on Ethereum. The\n * secret was revealed (e.g. in the calldata of a reverted activation) and\n * the peg-in swept without the vault activating, so the normal activation\n * no longer returns value to the depositor and the CSV refund can never\n * broadcast. The remaining recovery is the activate-and-redeem escape\n * hatch.\n *\n * The caller MUST prove the spender by comparing the outspend's\n * `spendingTxid` against the vault's PegIn txid before setting this. A\n * bare \"spent\" observation is not sufficient: the spend may be the\n * depositor's own CSV refund, and offering the secret-revealing hatch\n * against a refund burns the secret for a vault whose funds already\n * returned.\n */\n htlcSpentByPeginTx?: boolean;\n /**\n * EXPIRED only: the vault expired after reaching Verified, the PegIn\n * transaction has spent the Pre-PegIn HTLC, and the claim window has not\n * elapsed. The CSV refund is gone, so the claim is the remaining exit.\n *\n * The same spender proof as `htlcSpentByPeginTx` applies: offering the claim\n * while the HTLC is unspent reveals the secret and lets anyone broadcast the\n * PegIn ahead of the depositor's refund. When set, it takes precedence over\n * `canRefund`, which a proven PegIn spend makes impossible.\n */\n canClaimExpired?: boolean;\n}\n\n// ============================================================================\n// State Machine Logic\n// ============================================================================\n\n/**\n * Determine the current protocol state and available actions based on contract\n * status and vault provider state. Framework-agnostic: returns only\n * protocol-level data with no display labels, messages, or UI concerns.\n *\n * Client-side tracking overrides (e.g. suppressing actions after the user\n * has already acted but on-chain state hasn't caught up) are the caller's\n * responsibility.\n *\n * @param contractStatus - On-chain contract status (source of truth)\n * @param options - Vault provider state\n * @returns Protocol state with available actions\n */\nexport function getPeginProtocolState(\n contractStatus: ContractStatus,\n options: GetPeginProtocolStateOptions = {},\n): PeginProtocolState {\n const {\n transactionsReady,\n needsWotsKey,\n pendingIngestion,\n canRefund,\n hasProviderTerminalFailure,\n htlcSpentByPeginTx,\n canClaimExpired,\n } = options;\n\n if (contractStatus === ContractStatus.PENDING) {\n if (hasProviderTerminalFailure) {\n return { contractStatus, availableActions: [] };\n }\n\n if (needsWotsKey) {\n return {\n contractStatus,\n availableActions: [PeginAction.SUBMIT_WOTS_KEY],\n };\n }\n\n if (pendingIngestion === true && !transactionsReady) {\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_AND_BROADCAST_TO_BITCOIN],\n };\n }\n\n if (pendingIngestion === undefined && !transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n if (!transactionsReady) {\n return { contractStatus, availableActions: [] };\n }\n\n return {\n contractStatus,\n availableActions: [PeginAction.SIGN_PAYOUT_TRANSACTIONS],\n };\n }\n\n if (contractStatus === ContractStatus.VERIFIED) {\n // A spent HTLC while still Verified means the peg-in was swept without\n // activation: activating normally would hand the collateral to an\n // application flow that already failed once, and refunding is impossible\n // (the outpoint is gone). Surface only the escape hatch.\n if (htlcSpentByPeginTx) {\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_AND_REDEEM],\n };\n }\n return {\n contractStatus,\n availableActions: [PeginAction.ACTIVATE_VAULT],\n };\n }\n\n if (contractStatus === ContractStatus.ACTIVE) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.REDEEMED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.LIQUIDATED) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.EXPIRED) {\n if (canClaimExpired) {\n return {\n contractStatus,\n availableActions: [PeginAction.CLAIM_EXPIRED_VAULT],\n };\n }\n return {\n contractStatus,\n availableActions: canRefund ? [PeginAction.REFUND_HTLC] : [],\n };\n }\n\n if (contractStatus === ContractStatus.INVALID) {\n return { contractStatus, availableActions: [] };\n }\n\n if (contractStatus === ContractStatus.DEPOSITOR_WITHDRAWN) {\n return { contractStatus, availableActions: [] };\n }\n\n return { contractStatus, availableActions: [] };\n}\n\n/**\n * Check if a specific action is available in the current state\n */\nexport function canPerformAction(\n state: PeginProtocolState,\n action: PeginAction,\n): boolean {\n return state.availableActions.includes(action);\n}\n\n// ============================================================================\n// Activation deadline\n// ============================================================================\n\n/**\n * Whether a vault's on-chain activation window has closed. Mirrors the\n * BTCVaultRegistry check that reverts `ActivationDeadlineExpired`:\n * `block.number > createdAt + pegInActivationTimeout` — strict `>`, so a\n * boundary-equal block is NOT expired. All values are Ethereum block numbers.\n */\nexport function isActivationDeadlinePassedOnChain(params: {\n currentBlock: bigint;\n createdAtBlock: bigint;\n pegInActivationTimeout: bigint;\n}): boolean {\n const { currentBlock, createdAtBlock, pegInActivationTimeout } = params;\n return currentBlock > createdAtBlock + pegInActivationTimeout;\n}\n\n/**\n * Blocks still available for an activation transaction to be mined; `0` once\n * the window has closed.\n *\n * `isActivationDeadlinePassedOnChain` answers whether the window is already\n * shut, which is the right question for a badge but the wrong one for a\n * secret-bearing call. An activation submitted in the last block before the\n * deadline reverts if it lands one block late, and by then `s` is public\n * calldata: the vault expires with `ActivationTimeout`, and the vault\n * provider, which holds the rest of the HTLC signature set, can broadcast the\n * PegIn with that secret. The caller therefore needs the remaining margin,\n * not a boolean.\n *\n * The contract accepts a transaction mined at `block.number <= createdAt +\n * timeout`. `currentBlock` is the chain head, which is already mined, so a new\n * transaction lands at `currentBlock + 1` at the earliest: at\n * `currentBlock === createdAt + timeout` no usable block remains.\n */\nexport function activationDeadlineBlocksRemaining(params: {\n currentBlock: bigint;\n createdAtBlock: bigint;\n pegInActivationTimeout: bigint;\n}): number {\n const { currentBlock, createdAtBlock, pegInActivationTimeout } = params;\n const lastUsableBlock = createdAtBlock + pegInActivationTimeout;\n if (currentBlock >= lastUsableBlock) return 0;\n return Number(lastUsableBlock - currentBlock);\n}\n"],"names":["ContractStatus","PeginAction","getPeginProtocolState","contractStatus","options","transactionsReady","needsWotsKey","pendingIngestion","canRefund","hasProviderTerminalFailure","htlcSpentByPeginTx","canClaimExpired","canPerformAction","state","action","isActivationDeadlinePassedOnChain","params","currentBlock","createdAtBlock","pegInActivationTimeout","activationDeadlineBlocksRemaining","lastUsableBlock"],"mappings":"aAeO,IAAKA,GAAAA,IAEVA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAEAA,EAAAA,EAAA,SAAW,CAAA,EAAX,WAEAA,EAAAA,EAAA,OAAS,CAAA,EAAT,SAEAA,EAAAA,EAAA,SAAW,CAAA,EAAX,WAEAA,EAAAA,EAAA,WAAa,CAAA,EAAb,aAEAA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAEAA,EAAAA,EAAA,oBAAsB,CAAA,EAAtB,sBAEAA,EAAAA,EAAA,QAAU,CAAA,EAAV,UAhBUA,IAAAA,GAAA,CAAA,CAAA,EAgCAC,GAAAA,IAEVA,EAAA,gBAAkB,kBAElBA,EAAA,yBAA2B,2BAE3BA,EAAA,8BAAgC,gCAEhCA,EAAA,eAAiB,iBAOjBA,EAAA,oBAAsB,sBAEtBA,EAAA,YAAc,cAQdA,EAAA,oBAAsB,sBAzBZA,IAAAA,GAAA,CAAA,CAAA,EAuGL,SAASC,EACdC,EACAC,EAAwC,GACpB,CACpB,KAAM,CACJ,kBAAAC,EACA,aAAAC,EACA,iBAAAC,EACA,UAAAC,EACA,2BAAAC,EACA,mBAAAC,EACA,gBAAAC,CAAA,EACEP,EAEJ,OAAID,IAAmB,EACjBM,EACK,CAAE,eAAAN,EAAgB,iBAAkB,EAAC,EAG1CG,EACK,CACL,eAAAH,EACA,iBAAkB,CAAC,iBAAA,CAA2B,EAI9CI,IAAqB,IAAQ,CAACF,EACzB,CACL,eAAAF,EACA,iBAAkB,CAAC,+BAAA,CAAyC,EAI5DI,IAAqB,QAAa,CAACF,EAC9B,CAAE,eAAAF,EAAgB,iBAAkB,EAAC,EAGzCE,EAIE,CACL,eAAAF,EACA,iBAAkB,CAAC,0BAAA,CAAoC,EALhD,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAS5CA,IAAmB,EAKjBO,EACK,CACL,eAAAP,EACA,iBAAkB,CAAC,qBAAA,CAA+B,EAG/C,CACL,eAAAA,EACA,iBAAkB,CAAC,gBAAA,CAA0B,EAI7CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACjBQ,EACK,CACL,eAAAR,EACA,iBAAkB,CAAC,qBAAA,CAA+B,EAG/C,CACL,eAAAA,EACA,iBAAkBK,EAAY,CAAC,eAA2B,CAAA,CAAC,EAI3DL,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAG1CA,IAAmB,EACd,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,EAGvC,CAAE,eAAAA,EAAgB,iBAAkB,EAAC,CAC9C,CAKO,SAASS,EACdC,EACAC,EACS,CACT,OAAOD,EAAM,iBAAiB,SAASC,CAAM,CAC/C,CAYO,SAASC,EAAkCC,EAItC,CACV,KAAM,CAAE,aAAAC,EAAc,eAAAC,EAAgB,uBAAAC,CAAA,EAA2BH,EACjE,OAAOC,EAAeC,EAAiBC,CACzC,CAoBO,SAASC,EAAkCJ,EAIvC,CACT,KAAM,CAAE,aAAAC,EAAc,eAAAC,EAAgB,uBAAAC,CAAA,EAA2BH,EAC3DK,EAAkBH,EAAiBC,EACzC,OAAIF,GAAgBI,EAAwB,EACrC,OAAOA,EAAkBJ,CAAY,CAC9C"}
@@ -1,2 +0,0 @@
1
- "use strict";const P=require("bitcoinjs-lib"),w=require("./tbv/core/wasm/index.cjs"),k=require("./fundPeginTransaction-54-Rwb2k.cjs"),c=require("./bitcoin-D-O2GjbC.cjs"),Pe=require("./secp256k1-DDHQ1_za.cjs"),l=require("buffer"),p=require("./constants-BCg6O0PX.cjs"),pe=require("@bitcoin-js/tiny-secp256k1-asmjs"),U=require("./validation-C6XTfZak.cjs");function he(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const ee=he(pe);function m(e){return c.processPublicKeyToXOnly(e).toLowerCase()}function fe(e,t,r){const o=new Set;o.add(m(e));for(const n of t)o.add(m(n));return o.delete(m(r)),o.size}function te(e){const t=m(e.claimerBtcPubkey),r=m(e.depositorBtcPubkey),o=e.vaultKeeperBtcPubkeys.map(m);if(t===r){const s=o.filter(u=>u!==r);if(s.length===0)throw new Error("Cannot derive localChallengers: vault keeper set is empty (or contains only the depositor)");if(new Set(s).size!==s.length)throw new Error("Cannot derive localChallengers: duplicate vaultKeeper key — signing context is misconfigured");return s}const i=[...new Set([m(e.vaultProviderBtcPubkey),...o])].sort().filter(s=>s!==t);if(i.length===0)throw new Error(`Cannot derive localChallengers: no vault provider or vault keeper remains after excluding claimer ${t}`);return i}const de=32,ge=65535,Ee=2;function I(e,t){const r=c.stripHexPrefix(e).toLowerCase();if(!/^[0-9a-f]{64}$/.test(r))throw new Error(`${t} must be a 32-byte x-only public key.`);try{Pe.secp256k1.Point.fromBytes(l.Buffer.concat([l.Buffer.from([Ee]),l.Buffer.from(r,"hex")]))}catch{throw new Error(`${t} is not a secp256k1 x-coordinate.`)}return r}function O(e,t){if(e.length===0)throw new Error(`${t} must not be empty.`);const r=e.map((o,n)=>I(o,`${t}[${n}]`)).sort();if(new Set(r).size!==r.length)throw new Error(`${t} must not contain duplicate keys.`);return r}function R(e,t){const r=[];return e.forEach((o,n)=>{r.push(l.Buffer.from(o,"hex"),n===0?P.opcodes.OP_CHECKSIG:P.opcodes.OP_CHECKSIGADD)}),r.push(P.script.number.encode(e.length),t?P.opcodes.OP_NUMEQUALVERIFY:P.opcodes.OP_NUMEQUAL),r}function re(e,t){var y;c.assertEccInitialized();const r=I(e.depositorPubkey,"depositorPubkey"),o=I(e.vaultProviderPubkey,"vaultProviderPubkey"),n=O(e.vaultKeeperPubkeys,"vaultKeeperPubkeys"),i=O(e.universalChallengerPubkeys,"universalChallengerPubkeys"),s=c.stripHexPrefix(t).toLowerCase();if(!/^[0-9a-f]{64}$/.test(s))throw new Error("hashlock must be 32 bytes.");if(!Number.isInteger(e.timelockRefund)||e.timelockRefund<1||e.timelockRefund>ge)throw new Error("timelockRefund must be an integer from 1 to 65535.");const u=P.script.compile([P.opcodes.OP_SIZE,P.script.number.encode(de),P.opcodes.OP_EQUALVERIFY,P.opcodes.OP_SHA256,l.Buffer.from(s,"hex"),P.opcodes.OP_EQUALVERIFY,l.Buffer.from(r,"hex"),P.opcodes.OP_CHECKSIGVERIFY,l.Buffer.from(o,"hex"),P.opcodes.OP_CHECKSIGVERIFY,...R(n,!0),...R(i,!1)]),a=P.script.compile([l.Buffer.from(r,"hex"),P.opcodes.OP_CHECKSIGVERIFY,P.script.number.encode(e.timelockRefund),P.opcodes.OP_CHECKSEQUENCEVERIFY]),g=[{output:u,version:c.TAPSCRIPT_LEAF_VERSION},{output:a,version:c.TAPSCRIPT_LEAF_VERSION}],{hash:h,output:f,witness:d}=P.payments.p2tr({internalPubkey:l.Buffer.from(w.tapInternalPubkey),scriptTree:g,redeem:{output:u,redeemVersion:c.TAPSCRIPT_LEAF_VERSION}}),E=d==null?void 0:d[d.length-1],v=P.payments.p2tr({internalPubkey:l.Buffer.from(w.tapInternalPubkey),scriptTree:g,redeem:{output:a,redeemVersion:c.TAPSCRIPT_LEAF_VERSION}}),S=(y=v.witness)==null?void 0:y[v.witness.length-1];if(!h||!f||!E||!S)throw new Error("Failed to derive the expected Pre-PegIn HTLC output.");return{hashlockScript:u,hashlockControlBlock:E,refundScript:a,refundControlBlock:S,scriptPubKey:f,tapMerkleRoot:h}}function ye(e,t){c.assertEccInitialized();const r=I(e.depositorPubkey,"depositorPubkey"),o=I(e.vaultProviderPubkey,"vaultProviderPubkey"),n=O(e.vaultKeeperPubkeys,"vaultKeeperPubkeys"),i=O(e.universalChallengerPubkeys,"universalChallengerPubkeys"),s=P.script.compile([l.Buffer.from(r,"hex"),P.opcodes.OP_CHECKSIGVERIFY,l.Buffer.from(o,"hex"),P.opcodes.OP_CHECKSIGVERIFY,...R(n,!0),...R(i,!0),P.script.number.encode(t),P.opcodes.OP_CHECKSEQUENCEVERIFY]),{output:u}=P.payments.p2tr({internalPubkey:l.Buffer.from(w.tapInternalPubkey),scriptTree:{output:s,version:c.TAPSCRIPT_LEAF_VERSION}});if(!u)throw new Error("Failed to derive the expected PegIn payout output.");return u}async function we(e,t){const r=t.pegInAmounts.length;if(t.hashlocks.length!==r)throw new Error(`Pre-PegIn has ${t.hashlocks.length} hashlock(s), expected ${r} (one per requested deposit).`);if(e.htlcValues.length!==r)throw new Error(`WASM Pre-PegIn returned ${e.htlcValues.length} HTLC value(s), expected ${r} (one per requested deposit).`);if(e.peginAmounts.length!==r||e.htlcScriptPubKeys.length!==r||e.htlcAddresses.length!==r)throw new Error(`WASM Pre-PegIn returned mismatched array lengths (htlcValues=${e.htlcValues.length}, peginAmounts=${e.peginAmounts.length}, htlcScriptPubKeys=${e.htlcScriptPubKeys.length}, htlcAddresses=${e.htlcAddresses.length}); expected ${r} each.`);if(e.depositorClaimValue<=0n)throw new Error(`WASM Pre-PegIn returned non-positive depositorClaimValue ${e.depositorClaimValue}; expected > 0.`);const o=await w.computeMinClaimValue(t.vaultCoreVersion,t.numLocalChallengers,t.universalChallengerPubkeys.length,t.councilQuorum,t.councilSize,t.feeRate);if(e.depositorClaimValue!==o)throw new Error(`WASM Pre-PegIn depositorClaimValue ${e.depositorClaimValue} does not match the independently computed minimum claim value ${o} (vaultCoreVersion=${t.vaultCoreVersion}, numLocalChallengers=${t.numLocalChallengers}, numUniversalChallengers=${t.universalChallengerPubkeys.length}, councilQuorum=${t.councilQuorum}, councilSize=${t.councilSize}, feeRate=${t.feeRate}).`);const{value:n}=await w.peginP2aAnchorOutput(t.vaultCoreVersion),i=await w.computeMinPeginFee(t.vaultCoreVersion,t.vaultKeeperPubkeys.length,t.universalChallengerPubkeys.length,t.minPeginFeeRate),s=i+n,u=t.minPeginFeeRate*k.MAX_REASONABLE_PEGIN_VBYTES;for(let a=0;a<r;a++){const g=t.pegInAmounts[a],h=e.peginAmounts[a],f=e.htlcValues[a];if(h!==g)throw new Error(`WASM Pre-PegIn peginAmount[${a}] ${h} does not match the requested amount ${g}; refusing to build a tx whose recorded amount differs from the depositor's request.`);if(h<=0n)throw new Error(`WASM Pre-PegIn peginAmount[${a}] is non-positive (${h}); expected > 0.`);if(f<=0n)throw new Error(`WASM Pre-PegIn htlcValue[${a}] is non-positive (${f}); expected > 0.`);const d=f-h-e.depositorClaimValue;if(d<=0n)throw new Error(`WASM Pre-PegIn htlcValue[${a}] ${f} does not strictly cover peginAmount ${h} + depositorClaimValue ${e.depositorClaimValue} + a PegIn reserve (implied reserve ${d}).`);if(d>u)throw new Error(`WASM Pre-PegIn implied reserve for HTLC[${a}] (${d} sat) exceeds the plausibility cap ${u} sat (minPeginFeeRate=${t.minPeginFeeRate} × ${k.MAX_REASONABLE_PEGIN_VBYTES} vbytes); htlcValue ${f} appears grossly inflated.`);if(d!==s)throw new Error(`WASM Pre-PegIn htlcValue[${a}] ${f} implies a PegIn fee+anchor reserve of ${d} sat, expected exactly ${s} sat (minPeginFee ${i} + p2aAnchor ${n} for vaultCoreVersion ${t.vaultCoreVersion}, vaultKeepers=${t.vaultKeeperPubkeys.length}, universalChallengers=${t.universalChallengerPubkeys.length}, minPeginFeeRate=${t.minPeginFeeRate}).`)}return i}class C extends Error{constructor(t){super(t),this.name="HtlcOutputMismatchError"}}function oe(e,t,r){if(e.length<t.length)throw new C(`Encoded Pre-PegIn tx has ${e.length} output(s), fewer than the ${t.length} HTLC output(s) the cross-check validated.`);for(let o=0;o<t.length;o++){const n=BigInt(e[o].value);if(n!==t[o])throw new C(`Encoded Pre-PegIn HTLC output[${o}] value ${n} does not match the cross-checked htlcValue ${t[o]}; the funded/signed tx would not pay the validated amount.`);const i=e[o].script.toString("hex").toLowerCase(),s=r[o].toLowerCase();if(i!==s)throw new C(`Encoded Pre-PegIn HTLC output[${o}] scriptPubKey ${i} does not match the cross-checked htlcScriptPubKey ${s}.`)}}function Se(e,t,r,o,n,i,s){if(i!==p.PRE_PEGIN_TX_VERSION)throw new Error(`WASM Pre-PegIn transaction version ${i}; expected ${p.PRE_PEGIN_TX_VERSION}.`);if(s!==p.PRE_PEGIN_TX_LOCKTIME)throw new Error(`WASM Pre-PegIn transaction locktime ${s}; expected ${p.PRE_PEGIN_TX_LOCKTIME}.`);if(t.length<p.PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT)throw new Error(`WASM Pre-PegIn output layout has ${t.length} HTLC output(s); expected at least ${p.PRE_PEGIN_MIN_HTLC_OUTPUT_COUNT}.`);const u=t.length,a=u+ +(n!==void 0),g=k.peginOutputCount(t.length,n!==void 0);if(e.length!==g)throw new Error(`WASM Pre-PegIn output layout has ${e.length} output(s); expected exactly ${g}.`);oe(e,t,r);for(let E=0;E<t.length;E++){const v=re(o,o.hashlocks[E]).scriptPubKey;if(!e[E].script.equals(v))throw new C(`Pre-PegIn HTLC output[${E}] does not match the independently derived scriptPubKey.`)}if(n!==void 0){const E=e[u];if(E.value!==p.PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS)throw new Error(`WASM Pre-PegIn auth output[${u}] value ${E.value} sat; expected exactly ${p.PRE_PEGIN_AUTH_OUTPUT_VALUE_SATS} sat.`);const v=E.script.toString("hex").toLowerCase(),S=p.PRE_PEGIN_AUTH_SCRIPT_PREFIX+n.toLowerCase();if(v!==S)throw new Error(`WASM Pre-PegIn auth output[${u}] scriptPubKey ${v}; expected ${S}.`)}const h=e[a];if(h.value!==p.PRE_PEGIN_CPFP_VALUE_SATS)throw new Error(`WASM Pre-PegIn CPFP output[${a}] value ${h.value} sat; expected exactly ${p.PRE_PEGIN_CPFP_VALUE_SATS} sat.`);const f=h.script.toString("hex").toLowerCase(),d=c.stripHexPrefix(c.deriveBip86ScriptPubKeyHex(o.depositorPubkey)).toLowerCase();if(f!==d)throw new Error(`WASM Pre-PegIn CPFP output[${a}] scriptPubKey ${f}; expected depositor BIP-86 script ${d}.`)}const ne=1;function ie(e){const t=c.stripHexPrefix(e);if(!/^[0-9a-fA-F]{64}$/.test(t))throw new Error("Invalid depositor pubkey for the depositor-claim script: must be an x-only key of 64 hex characters (32 bytes).");const r=P.script.compile([l.Buffer.from(c.hexToUint8Array(t)),P.opcodes.OP_CHECKSIG]),o=l.Buffer.from(w.tapInternalPubkey),n={output:r,redeemVersion:c.TAPSCRIPT_LEAF_VERSION},{output:i,witness:s}=P.payments.p2tr({internalPubkey:o,scriptTree:{output:r},redeem:n});if(!i)throw new Error("Failed to derive the depositor-claim P2TR scriptPubKey for PegIn output validation");const u=s==null?void 0:s[s.length-1];if(!u)throw new Error("Failed to derive the depositor-claim tapleaf control block; refusing to build an unspendable input");return{scriptPubKey:i,leafScript:r,controlBlock:u,internalKey:o}}function se(e){return ie(e).scriptPubKey}const K=64,ve=/^[0-9a-fA-F]+$/,M=65535;async function xe(e){const t=N(e.authAnchorHash),r=await w.createPrePeginTransaction({txGraphVersion:e.vaultCoreVersion,depositorPubkey:e.depositorPubkey,vaultProviderPubkey:e.vaultProviderPubkey,vaultKeeperPubkeys:e.vaultKeeperPubkeys,universalChallengerPubkeys:e.universalChallengerPubkeys,hashlocks:[...e.hashlocks],timelockRefund:e.timelockRefund,pegInAmounts:[...e.pegInAmounts],feeRate:e.feeRate,minPeginFeeRate:e.minPeginFeeRate,numLocalChallengers:e.numLocalChallengers,councilQuorum:e.councilQuorum,councilSize:e.councilSize,network:e.network,authAnchorHash:t}),o=await we(r,e),n=k.parseUnfundedWasmTransaction(r.txHex);Se(n.outputs,r.htlcValues,r.htlcScriptPubKeys,e,t,n.version,n.locktime);const i=n.outputs.reduce((u,a)=>u+BigInt(a.value),0n),s=t!==void 0?r.htlcValues.length:null;return{psbtHex:r.txHex,totalOutputValue:i,htlcValues:r.htlcValues,htlcScriptPubKeys:r.htlcScriptPubKeys,htlcAddresses:r.htlcAddresses,peginAmounts:r.peginAmounts,depositorClaimValue:r.depositorClaimValue,authAnchorVout:s,minPeginFee:o}}function N(e){if(e===void 0)return;const t=e.startsWith("0x")||e.startsWith("0X")?e.slice(2):e;if(t.length!==K||!ve.test(t))throw new Error(`authAnchorHash must be 32-byte hex (${K} chars, no 0x prefix); got length ${t.length}`);return t.toLowerCase()}async function _e(e){if(!Number.isInteger(e.timelockPegin)||e.timelockPegin<1||e.timelockPegin>M)throw new Error(`PegIn timelock ${e.timelockPegin} must be a whole number of blocks from 1 to ${M}.`);const t=await w.buildPeginTxFromPrePegin({txGraphVersion:e.prePeginParams.vaultCoreVersion,depositorPubkey:e.prePeginParams.depositorPubkey,vaultProviderPubkey:e.prePeginParams.vaultProviderPubkey,vaultKeeperPubkeys:e.prePeginParams.vaultKeeperPubkeys,universalChallengerPubkeys:e.prePeginParams.universalChallengerPubkeys,hashlocks:[...e.prePeginParams.hashlocks],timelockRefund:e.prePeginParams.timelockRefund,pegInAmounts:[...e.prePeginParams.pegInAmounts],feeRate:e.prePeginParams.feeRate,minPeginFeeRate:e.prePeginParams.minPeginFeeRate,numLocalChallengers:e.prePeginParams.numLocalChallengers,councilQuorum:e.prePeginParams.councilQuorum,councilSize:e.prePeginParams.councilSize,network:e.prePeginParams.network,authAnchorHash:N(e.prePeginParams.authAnchorHash)},e.timelockPegin,e.fundedPrePeginTxHex,e.htlcVout);return await $e(t,e),{txHex:t.txHex,txid:t.txid,vaultScriptPubKey:t.vaultScriptPubKey,vaultValue:t.vaultValue}}async function $e(e,t){const r=t.prePeginParams.vaultCoreVersion;if(r!==p.PEGIN_VAULT_CORE_VERSION)throw new Error(`Unsupported vaultCoreVersion ${r} for PegIn tx; only ${p.PEGIN_VAULT_CORE_VERSION} has a PegIn shape.`);await w.validatePeginP2aAnchor(r,e.txHex);const o=P.Transaction.fromHex(c.stripHexPrefix(e.txHex));if(o.version!==p.PEGIN_TX_VERSION)throw new Error(`PegIn tx version ${o.version} does not match vaultCoreVersion ${r}; expected ${p.PEGIN_TX_VERSION}.`);if(o.locktime!==p.PEGIN_TX_LOCKTIME)throw new Error(`PegIn tx locktime ${o.locktime} does not match the canonical locktime ${p.PEGIN_TX_LOCKTIME}.`);if(o.ins.length!==1)throw new Error(`PegIn tx has ${o.ins.length} input(s), expected exactly 1 (the Pre-PegIn HTLC outpoint).`);const n=P.Transaction.fromHex(c.stripHexPrefix(t.fundedPrePeginTxHex)).getId(),i=c.uint8ArrayToHex(new Uint8Array(o.ins[0].hash).slice().reverse());if(i!==n)throw new Error(`PegIn input spends txid ${i}, expected the funded Pre-PegIn ${n}.`);if(o.ins[0].index!==t.htlcVout)throw new Error(`PegIn input spends Pre-PegIn output ${o.ins[0].index}, expected the requested HTLC vout ${t.htlcVout}.`);if(o.ins[0].sequence!==p.PEGIN_INPUT_SEQUENCE)throw new Error(`PegIn input sequence ${o.ins[0].sequence} does not match the canonical sequence ${p.PEGIN_INPUT_SEQUENCE}.`);if(o.ins[0].script.length!==0)throw new Error("PegIn input scriptSig must be empty.");if(o.ins[0].witness.length!==0)throw new Error("PegIn input witness must be empty before signing.");if(o.outs.length!==p.PEGIN_OUTPUT_COUNT)throw new Error(`PegIn tx has ${o.outs.length} output(s), expected exactly ${p.PEGIN_OUTPUT_COUNT} for vaultCoreVersion ${r} (vault + depositor claim + P2A anchor).`);const s=t.prePeginParams.pegInAmounts[t.htlcVout];if(e.vaultValue!==s)throw new Error(`PegIn vault output value ${e.vaultValue} does not match the requested peg-in amount ${s} for HTLC ${t.htlcVout}; refusing to sign a vault that locks a different amount than requested.`);const u=o.outs[0];if(BigInt(u.value)!==e.vaultValue)throw new Error(`Encoded PegIn vault output value ${u.value} does not match the WASM-reported vaultValue ${e.vaultValue}.`);const a=ye(t.prePeginParams,t.timelockPegin).toString("hex");if(c.stripHexPrefix(e.vaultScriptPubKey).toLowerCase()!==a)throw new Error(`WASM-reported PegIn vaultScriptPubKey ${e.vaultScriptPubKey} does not match the independently derived payout scriptPubKey ${a}.`);const g=u.script.toString("hex");if(g!==a)throw new Error(`Encoded PegIn vault output scriptPubKey ${g} does not match the independently derived payout scriptPubKey ${a}.`);const h=o.getId();if(h!==c.stripHexPrefix(e.txid).toLowerCase())throw new Error(`Encoded PegIn txid ${h} does not match the WASM-reported txid ${e.txid}.`);const f=await w.computeMinClaimValue(r,t.prePeginParams.numLocalChallengers,t.prePeginParams.universalChallengerPubkeys.length,t.prePeginParams.councilQuorum,t.prePeginParams.councilSize,t.prePeginParams.feeRate),d=o.outs[ne];if(BigInt(d.value)!==f)throw new Error(`Encoded PegIn depositor-claim output value ${d.value} does not match the independently computed claim value ${f} for vaultCoreVersion ${r}.`);const E=se(t.prePeginParams.depositorPubkey);if(!d.script.equals(E))throw new Error(`Encoded PegIn depositor-claim output scriptPubKey ${d.script.toString("hex")} does not pay to the depositor's claim script (expected ${E.toString("hex")}).`)}async function ue(e){const t=await w.createPayoutConnector({txGraphVersion:e.vaultCoreVersion,depositor:e.depositor,vaultProvider:e.vaultProvider,vaultKeepers:e.vaultKeepers,universalChallengers:e.universalChallengers,timelockPegin:e.timelockPegin},e.network);return{payoutScript:t.payoutScript,taprootScriptHash:t.taprootScriptHash,scriptPubKey:t.scriptPubKey,address:t.address,payoutControlBlock:t.payoutControlBlock}}const F=253,me=254,Ce=65535,Ie=4294967295,be="TapLeaf",Te="TapBranch",Ae="TapTweak",T=33,A=32,q=128,ke=1,Oe=254,Re=l.Buffer.from([81,32]);function Le(e){if(e<F)return l.Buffer.from([e]);if(e<=Ce){const t=l.Buffer.alloc(2);return t.writeUInt16LE(e),l.Buffer.concat([l.Buffer.from([F]),t])}if(e<=Ie){const t=l.Buffer.alloc(4);return t.writeUInt32LE(e),l.Buffer.concat([l.Buffer.from([me]),t])}throw new Error(`Script too large to encode as CompactSize: ${e} bytes`)}function H(e,t){const r=l.Buffer.concat([l.Buffer.from([e]),Le(t.length),l.Buffer.from(t)]);return P.crypto.taggedHash(be,r)}function Ve(e){const{leafVersion:t,script:r,controlBlock:o}=e,n=o.length-T;if(n<0||n%A!==0||n/A>q)throw new Error(`Malformed Taproot control block: length ${o.length} must be ${T} + 32*m with 0 <= m <= ${q}`);const i=o[0]&Oe;if(i!==t)throw new Error(`Taproot control block leaf version 0x${i.toString(16)} does not match the tapLeafScript leaf version 0x${t.toString(16)}`);const s=l.Buffer.from(o.subarray(1,T));let u=H(t,r);for(let f=T;f<o.length;f+=A){const d=l.Buffer.from(o.subarray(f,f+A));u=P.crypto.taggedHash(Te,l.Buffer.compare(u,d)<=0?l.Buffer.concat([u,d]):l.Buffer.concat([d,u]))}const a=P.crypto.taggedHash(Ae,l.Buffer.concat([s,u])),g=ee.xOnlyPointAddTweak(s,a);if(g===null)throw new Error("Taproot control block does not yield a valid output key (tweak is off-curve)");const h=o[0]&ke;if(g.parity!==h)throw new Error(`Taproot output key parity ${g.parity} contradicts the control block's parity bit ${h}`);return l.Buffer.concat([Re,l.Buffer.from(g.xOnlyPubkey)])}const Ne=256,He=1500,X=256,G=0xffffffffn,Be=500,Ue=55,Ke=34;function ce(e){if(typeof e.protocolFeeRate!="bigint"||e.protocolFeeRate<=0n||e.protocolFeeRate>G)throw new Error(`protocolFeeRate must be in [1, ${G}] sat/vB, got ${e.protocolFeeRate}`);for(const[t,r,o,n]of[["keepers",e.numVaultKeepers,Ne,"fee-floor model limit"],["challengers",e.numUniversalChallengers,He,"protocol maximum"]])if(!Number.isInteger(r)||r<1||r>o)throw new Error(`Participant count for ${t} (${r}) is outside the supported range [1, ${o}]${r>o?` (${n})`:""}.`);if(!Number.isInteger(e.councilSize)||e.councilSize<1||e.councilSize>X)throw new Error(`councilSize must be an integer in [1, ${X}], got ${e.councilSize}`)}async function ae(e,t){const{implicitFeeSats:r,out0Len:o,out1Len:n}=t,i=e.numVaultKeepers+e.numUniversalChallengers,s=BigInt(r),u=Math.max(0,o-Ke),a=Be+Ue*i+u,g=e.protocolFeeRate*BigInt(a);if(s>g)throw new Error(`Payout implicit fee ${r} sats exceeds the safety cap of ${g} sats (${e.protocolFeeRate} sat/vB x ${a} vB for ${i} participants); refusing to sign payout.`);const h=await w.computePayoutFeeFloor(e.vaultCoreVersion,e.numVaultKeepers,e.numUniversalChallengers,e.numVaultKeepers,e.councilSize,o,n,e.protocolFeeRate);if(s<h)throw new Error(`Payout implicit fee ${r} sats is below the floor of ${h} sats (the smallest fee any known vault-provider build produces for this shape); refusing to sign payout.`)}const Me=22,Fe=34,qe=34,Xe=25,Ge=23;function We(e){return e.length===Me*2&&e.startsWith("0014")}function Ye(e){return e.length===Fe*2&&e.startsWith("0020")}function ze(e){return e.length===qe*2&&e.startsWith("5120")}function De(e){return e.length===Xe*2&&e.startsWith("76a914")&&e.endsWith("88ac")}function Qe(e){return e.length===Ge*2&&e.startsWith("a914")&&e.endsWith("87")}function W(e,t){const r=c.stripHexPrefix(e).toLowerCase();if(r.length===0)throw new Error(`${t} is empty`);if(!/^[0-9a-f]+$/.test(r)||r.length%2!==0)throw new Error(`${t} is not valid hex`);if(r.startsWith("6a"))throw new Error(`${t} is an OP_RETURN output, which is provably unspendable`);if(!ze(r)&&!We(r)&&!Ye(r)&&!De(r)&&!Qe(r))throw new Error(`${t} is not a standard scriptPubKey (expected P2TR, P2WPKH, P2WSH, P2PKH, or P2SH; got ${r.length/2} bytes: ${r})`)}const Y=3;async function Ze(e){const t={vaultCoreVersion:e.vaultCoreVersion,numVaultKeepers:e.vaultKeeperBtcPubkeys.length,numUniversalChallengers:e.universalChallengerBtcPubkeys.length,councilSize:e.councilMembers.length,protocolFeeRate:e.protocolFeeRate};ce(t);const r=P.Transaction.fromHex(c.stripHexPrefix(e.payoutTxHex)),o=P.Transaction.fromHex(c.stripHexPrefix(e.peginTxHex)),n=P.Transaction.fromHex(c.stripHexPrefix(e.assertTxHex));if(r.ins.length!==p.DEPOSITOR_PAYOUT_INPUT_COUNT)throw new Error(`Payout transaction must have exactly ${p.DEPOSITOR_PAYOUT_INPUT_COUNT} inputs, got ${r.ins.length}`);if(r.version!==p.PAYOUT_TX_VERSION)throw new Error(`Payout transaction version ${r.version} must be ${p.PAYOUT_TX_VERSION}; refusing to sign payout.`);if(r.locktime!==p.PAYOUT_TX_LOCKTIME)throw new Error(`Payout transaction locktime ${r.locktime} must be ${p.PAYOUT_TX_LOCKTIME}; refusing to sign payout.`);const i=z(r.ins[0],0,o,"PegIn",p.PEGIN_VAULT_OUTPUT_INDEX);if(r.ins[0].sequence!==e.timelockPegin)throw new Error(`Payout input 0 sequence ${r.ins[0].sequence} must equal the PegIn CSV timelock ${e.timelockPegin}; refusing to sign payout.`);const s=z(r.ins[1],1,n,"Assert",p.ASSERT_PAYOUT_OUTPUT_INDEX);if(r.ins[1].sequence!==e.timelockAssert)throw new Error(`Payout input 1 sequence ${r.ins[1].sequence} must equal the Assert CSV timelock ${e.timelockAssert}; refusing to sign payout.`);const{out0Len:u,out1Len:a}=et(r,i.value,e),g=i.value+s.value;let h=0;for(const v of r.outs)h+=v.value;if(h>g)throw new Error(`Payout outputs (${h} sats) exceed inputs (${g} sats); invalid transaction.`);const f=g-h;await ae(t,{implicitFeeSats:f,out0Len:u,out1Len:a});const d=await ue({vaultCoreVersion:e.vaultCoreVersion,depositor:e.depositorBtcPubkey,vaultProvider:e.vaultProviderBtcPubkey,vaultKeepers:e.vaultKeeperBtcPubkeys,universalChallengers:e.universalChallengerBtcPubkeys,timelockPegin:e.timelockPegin,network:e.network}),E=await je(e,s);return{psbtHex:Je(r,i,s,d,E)}}async function je(e,t){const{payoutScript:r,payoutControlBlock:o}=await w.getAssertPayoutScriptInfo({txGraphVersion:e.vaultCoreVersion,claimer:c.stripHexPrefix(e.claimerBtcPubkey).toLowerCase(),localChallengers:te({claimerBtcPubkey:e.claimerBtcPubkey,depositorBtcPubkey:e.depositorBtcPubkey,vaultProviderBtcPubkey:e.vaultProviderBtcPubkey,vaultKeeperBtcPubkeys:e.vaultKeeperBtcPubkeys}),universalChallengers:e.universalChallengerBtcPubkeys.map(u=>c.stripHexPrefix(u).toLowerCase()),timelockAssert:e.timelockAssert,councilMembers:e.councilMembers.map(u=>c.stripHexPrefix(u).toLowerCase()),councilQuorum:e.councilQuorum}),n=c.hexToUint8Array(r),i=c.hexToUint8Array(o),s=Ve({leafVersion:c.TAPSCRIPT_LEAF_VERSION,script:n,controlBlock:i});if(!t.script.equals(s))throw new Error(`Rebuilt Assert:0 payout leaf does not spend the Assert output being referenced: leaf binds to ${s.toString("hex")}, Assert:${p.ASSERT_PAYOUT_OUTPUT_INDEX} pays ${t.script.toString("hex")}; refusing to sign payout.`);return{script:n,controlBlock:i}}function z(e,t,r,o,n){const i=c.uint8ArrayToHex(new Uint8Array(e.hash).slice().reverse()),s=r.getId();if(i!==s||e.index!==n)throw new Error(`Input ${t} must spend ${o}:${n}. Expected ${s}:${n}, got ${i}:${e.index}`);const u=r.outs[e.index];if(!u)throw new Error(`Previous output not found for input ${t} (txid: ${i}, index: ${e.index})`);return u}function Je(e,t,r,o,n){const i=new P.Psbt;i.setVersion(e.version),i.setLocktime(e.locktime);const[s,u]=e.ins;i.addInput({hash:s.hash,index:s.index,sequence:s.sequence,witnessUtxo:{script:t.script,value:t.value},tapLeafScript:[{leafVersion:c.TAPSCRIPT_LEAF_VERSION,script:l.Buffer.from(c.hexToUint8Array(o.payoutScript)),controlBlock:l.Buffer.from(c.hexToUint8Array(o.payoutControlBlock))}],tapInternalKey:l.Buffer.from(w.tapInternalPubkey)}),i.addInput({hash:u.hash,index:u.index,sequence:u.sequence,witnessUtxo:{script:r.script,value:r.value},tapLeafScript:[{leafVersion:c.TAPSCRIPT_LEAF_VERSION,script:l.Buffer.from(n.script),controlBlock:l.Buffer.from(n.controlBlock)}],tapInternalKey:l.Buffer.from(w.tapInternalPubkey)});for(const a of e.outs)i.addOutput({script:a.script,value:a.value});return i.toHex()}function D(e,t){const r=c.stripHexPrefix(e).toLowerCase(),o=c.stripHexPrefix(c.deriveBip86ScriptPubKeyHex(t)).toLowerCase();return r===o?[r]:[r,o]}function Q(e,t){return t.some(r=>e.equals(l.Buffer.from(r,"hex")))}function et(e,t,r){const{claimerBtcPubkey:o,vaultProviderBtcPubkey:n,depositorBtcPubkey:i,vaultKeeperBtcPubkeys:s,registeredPayoutScriptPubKey:u,commissionBps:a,vkClaimerPayoutScriptPubKeys:g,vpCommissionScriptPubKey:h}=r;if(!c.isValidHex(u))throw new Error("Invalid registeredPayoutScriptPubKey: not valid hex");const f=c.stripHexPrefix(o).toLowerCase(),d=c.stripHexPrefix(n).toLowerCase(),E=c.stripHexPrefix(i).toLowerCase(),v=s.map($=>c.stripHexPrefix($).toLowerCase());let S,y,_;if(f===d)S="vp-claimer",y=p.VP_CLAIMER_PAYOUT_OUTPUT_COUNT,_=[c.stripHexPrefix(u)];else if(f===E)S="depositor-as-claimer",y=p.NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT,_=[c.stripHexPrefix(u)];else if(v.includes(f)){S="vk-claimer",y=p.NON_VP_CLAIMER_PAYOUT_OUTPUT_COUNT;const $=g[f];if($===void 0)throw new Error(`No registered payout script resolved for vault keeper claimer ${f}`);W($,`Vault keeper payout script for claimer ${f}`),_=D($,f)}else throw new Error(`Unknown claimer pubkey ${f}: not VP, depositor, or a registered vault keeper`);if(e.outs.length!==y)throw new Error(`Payout transaction has ${e.outs.length} output(s), expected exactly ${y} for role ${S}.`);if(!Q(e.outs[0].script,_))throw new Error(`Payout transaction output 0 does not pay the expected scriptPubKey for role ${S}. Accepted: ${_.join(" or ")}; got ${e.outs[0].script.toString("hex")}`);const L=y-1;if(e.outs[L].value!==p.PAYOUT_ANCHOR_DUST_SATS)throw new Error(`Payout CPFP anchor (out ${L}) value ${e.outs[L].value} sats must equal ${p.PAYOUT_ANCHOR_DUST_SATS} sats`);if(S==="vp-claimer"){W(h,"Vault provider commission payout script");const $=D(h,d);if(!Q(e.outs[1].script,$))throw new Error(`Payout transaction output 1 does not pay the vault provider's commission scriptPubKey. Accepted: ${$.join(" or ")}; got ${e.outs[1].script.toString("hex")}`);if(!Number.isInteger(a)||a<0||a>=p.MAX_VP_COMMISSION_BPS_EXCLUSIVE)throw new Error(`commissionBps must be an integer in [0, ${p.MAX_VP_COMMISSION_BPS_EXCLUSIVE}), got ${a}`);const B=Math.floor(t*a/p.BPS_DENOMINATOR);if(e.outs[1].value>B)throw new Error(`Payout VP commission (out 1) value ${e.outs[1].value} sats exceeds cap ${B} sats (${a} bps of peginValue=${t})`)}const b=e.outs[0].script.length;if(b===0||b>p.MAX_PAYOUT_SCRIPT_LEN)throw new Error(`Payout receiver scriptPubKey length ${b} is outside the contract's registration cap [1, ${p.MAX_PAYOUT_SCRIPT_LEN}]; refusing to sign payout.`);const le=S==="vp-claimer"?e.outs[1].script.length:void 0;return{out0Len:b,out1Len:le}}function tt(e,t,r=0){const o=P.Psbt.fromHex(e);if(r>=o.data.inputs.length)throw new Error(`Input index ${r} out of range (${o.data.inputs.length} inputs)`);const n=o.data.inputs[r];if(n.tapScriptSig&&n.tapScriptSig.length>0){const i=c.hexToUint8Array(t);for(const s of n.tapScriptSig)if(s.pubkey.equals(l.Buffer.from(i)))return Z(s.signature,r);throw new Error(`No signature found for depositor pubkey: ${t} at input ${r}`)}if(n.finalScriptWitness&&n.finalScriptWitness.length>0){const i=rt(n.finalScriptWitness);if(i.length!==Y)throw new Error(`Unexpected finalized witness stack size at input ${r}: expected ${Y} items (signature, script, controlBlock), got ${i.length}`);return Z(i[0],r)}throw new Error(`No tapScriptSig or finalScriptWitness found in signed PSBT at input ${r}`)}function Z(e,t){if(e.length===64)return c.uint8ArrayToHex(new Uint8Array(e));throw e.length===65?new Error(`Unexpected sighash byte 0x${e[64].toString(16).padStart(2,"0")} at input ${t}. Expected implicit SIGHASH_DEFAULT as a 64-byte signature.`):new Error(`Unexpected signature length at input ${t}: ${e.length}`)}function rt(e){const t=[];let r=0;const o=s=>{if(r+s>e.length)throw new Error(`Malformed witness data: need ${s} byte(s) at offset ${r}, only ${e.length-r} remaining`)},n=()=>{o(1);const s=e[r++];if(s<253)return s;if(s===253){o(2);const u=(e[r]|e[r+1]<<8)>>>0;return r+=2,u}if(s===254){o(4);const u=(e[r]|e[r+1]<<8|e[r+2]<<16|e[r+3]<<24)>>>0;return r+=4,u}throw new Error(`Malformed witness data: 8-byte varint (0xff) not supported at offset ${r-1}`)},i=n();for(let s=0;s<i;s++){const u=n();o(u),t.push(l.Buffer.from(e.subarray(r,r+u))),r+=u}if(r!==e.length)throw new Error(`Malformed witness data: ${e.length-r} trailing byte(s) after parsing ${i} item(s)`);return t}class x extends Error{constructor(t){super(`Wallet returned a PSBT for a different transaction: ${t}`),this.name="PsbtSubstitutionError"}}function j(e,t){try{return P.Psbt.fromHex(t)}catch(r){const o=r instanceof Error?r.message:String(r);throw new Error(`Failed to parse ${e} PSBT: ${o}`)}}const ot=8;function V(e){return`${e.toString("hex").slice(0,ot)}…`}function J(e){const t=l.Buffer.from(e).reverse();return V(t)}function nt(e){const t=j("requested",e.requestedPsbtHex),r=j("returned",e.returnedPsbtHex);if(t.version!==r.version)throw new x(`tx version differs (requested=${t.version}, returned=${r.version})`);if(t.locktime!==r.locktime)throw new x(`tx locktime differs (requested=${t.locktime}, returned=${r.locktime})`);if(t.txInputs.length!==r.txInputs.length)throw new x(`input count differs (requested=${t.txInputs.length}, returned=${r.txInputs.length})`);if(t.txOutputs.length!==r.txOutputs.length)throw new x(`output count differs (requested=${t.txOutputs.length}, returned=${r.txOutputs.length})`);for(let o=0;o<t.txInputs.length;o++){const n=t.txInputs[o],i=r.txInputs[o];if(!n.hash.equals(i.hash))throw new x(`input ${o} prevout txid differs (requested=${J(n.hash)}, returned=${J(i.hash)})`);if(n.index!==i.index)throw new x(`input ${o} prevout vout differs (requested=${n.index}, returned=${i.index})`);if(n.sequence!==i.sequence)throw new x(`input ${o} sequence differs (requested=${n.sequence}, returned=${i.sequence})`)}for(let o=0;o<t.txOutputs.length;o++){const n=t.txOutputs[o],i=r.txOutputs[o];if(!n.script.equals(i.script))throw new x(`output ${o} scriptPubKey differs (requested=${V(n.script)}, returned=${V(i.script)})`);if(n.value!==i.value)throw new x(`output ${o} value differs (requested=${n.value}, returned=${i.value})`)}}function it(e){const{requestedPsbtHex:t,signatureHex:r,signerXOnlyPubkeyHex:o,inputIndex:n}=e,i=c.stripHexPrefix(r);if(i.length!==c.SCHNORR_SIG_HEX_LEN)throw new Error(`Schnorr signature for input ${n} must be ${c.SCHNORR_SIG_HEX_LEN} hex chars (64 bytes), got ${i.length}.`);const s=c.stripHexPrefix(o);if(s.length!==U.X_ONLY_PUBKEY_HEX_LEN)throw new Error(`Signer x-only pubkey for input ${n} must be ${U.X_ONLY_PUBKEY_HEX_LEN} hex chars (32 bytes), got ${s.length}.`);const u=P.Psbt.fromHex(t);if(n<0||n>=u.data.inputs.length)throw new Error(`Input index ${n} out of range (${u.data.inputs.length} inputs).`);const a=[],g=[];for(let y=0;y<u.data.inputs.length;y++){const _=u.data.inputs[y].witnessUtxo;if(!_)throw new Error(`Cannot verify signature: input ${y} of the requested PSBT has no witnessUtxo (required to recompute the Taproot sighash).`);a.push(_.script),g.push(_.value)}const h=u.data.inputs[n].tapLeafScript;if(!h||h.length!==1)throw new Error(`Cannot verify signature: input ${n} of the requested PSBT must have exactly one tapLeafScript, got ${(h==null?void 0:h.length)??0}.`);const f=h[0];if(f.leafVersion!==c.TAPSCRIPT_LEAF_VERSION)throw new Error(`Cannot verify signature: input ${n} tapLeafScript has leaf version 0x${f.leafVersion.toString(16)}, expected 0x${c.TAPSCRIPT_LEAF_VERSION.toString(16)}.`);const d=H(f.leafVersion,f.script),E=new P.Transaction;E.version=u.version,E.locktime=u.locktime;for(const y of u.txInputs)E.addInput(y.hash,y.index,y.sequence);for(const y of u.txOutputs)E.addOutput(y.script,y.value);const v=E.hashForWitnessV1(n,a,g,P.Transaction.SIGHASH_DEFAULT,d);if(!ee.verifySchnorr(v,c.hexToUint8Array(s),c.hexToUint8Array(i)))throw new Error(`Schnorr signature for input ${n} (signer ${s}) does not verify against the expected Taproot script-path sighash. The wallet may have signed with the tweaked key, signed a different transaction, or returned an invalid signature.`)}exports.HtlcOutputMismatchError=C;exports.PEGIN_DEPOSITOR_CLAIM_VOUT=ne;exports.PsbtSubstitutionError=x;exports.assertEncodedHtlcOutputsMatch=oe;exports.assertPayoutFeeBandDomain=ce;exports.assertPayoutFeeInBand=ae;exports.assertPsbtUnsignedTxMatches=nt;exports.assertScriptPathSchnorrSignature=it;exports.buildPayoutPsbt=Ze;exports.buildPeginTxFromFundedPrePegin=_e;exports.buildPrePeginPsbt=xe;exports.computeNumLocalChallengers=fe;exports.computeTapLeafHash=H;exports.createPayoutScript=ue;exports.deriveDepositorClaimDescriptor=ie;exports.deriveDepositorClaimScriptPubKey=se;exports.deriveExpectedPrePeginHtlc=re;exports.deriveLocalChallengers=te;exports.extractPayoutSignature=tt;exports.normalizeAuthAnchorHash=N;
2
- //# sourceMappingURL=verifyScriptPathSchnorrSignature-CgDa9Rcg.cjs.map