@arkade-os/sdk 0.4.59 → 0.4.60

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 (88) hide show
  1. package/README.md +34 -6
  2. package/dist/adapters/expo.cjs +5 -5
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +3 -3
  6. package/dist/adapters/indexedDB.cjs +5 -5
  7. package/dist/adapters/indexedDB.js +4 -4
  8. package/dist/{ark-C9DsPSm8.d.cts → ark-D2_yNhfz.d.cts} +1009 -523
  9. package/dist/{ark-C9DsPSm8.d.ts → ark-D2_yNhfz.d.ts} +1009 -523
  10. package/dist/{asyncStorageTaskQueue-CYHXFfvn.d.cts → asyncStorageTaskQueue-CqijAAPG.d.cts} +1 -1
  11. package/dist/{asyncStorageTaskQueue-BwuWQ1Qc.d.ts → asyncStorageTaskQueue-Vsp6uk8e.d.ts} +1 -1
  12. package/dist/{chunk-PN5RD7ZI.js → chunk-2DINXHGQ.js} +1665 -1149
  13. package/dist/chunk-2DINXHGQ.js.map +1 -0
  14. package/dist/{chunk-IPFGYVF3.cjs → chunk-2XLNQHG4.cjs} +10 -10
  15. package/dist/chunk-2XLNQHG4.cjs.map +1 -0
  16. package/dist/{chunk-VSHY5TBY.js → chunk-3Q7BBUWO.js} +4 -4
  17. package/dist/{chunk-VSHY5TBY.js.map → chunk-3Q7BBUWO.js.map} +1 -1
  18. package/dist/{chunk-DSTYXT2D.cjs → chunk-4A4GRSTB.cjs} +44 -6
  19. package/dist/chunk-4A4GRSTB.cjs.map +1 -0
  20. package/dist/{chunk-H342JPPZ.js → chunk-B66RYHTB.js} +5 -5
  21. package/dist/chunk-B66RYHTB.js.map +1 -0
  22. package/dist/{chunk-YEHGCHIL.js → chunk-FSKANUV4.js} +12 -5
  23. package/dist/chunk-FSKANUV4.js.map +1 -0
  24. package/dist/{chunk-7FA36NOA.cjs → chunk-H7JCHFM6.cjs} +17 -17
  25. package/dist/{chunk-7FA36NOA.cjs.map → chunk-H7JCHFM6.cjs.map} +1 -1
  26. package/dist/{chunk-FIMOVH25.js → chunk-JBULXTCC.js} +40 -7
  27. package/dist/chunk-JBULXTCC.js.map +1 -0
  28. package/dist/{chunk-DYNB6POO.cjs → chunk-KNS5MTTI.cjs} +3820 -2915
  29. package/dist/chunk-KNS5MTTI.cjs.map +1 -0
  30. package/dist/{chunk-2XO43UHI.cjs → chunk-MZNLCMUP.cjs} +1686 -1169
  31. package/dist/chunk-MZNLCMUP.cjs.map +1 -0
  32. package/dist/{chunk-V2KENVN6.cjs → chunk-PRFL62ID.cjs} +22 -15
  33. package/dist/chunk-PRFL62ID.cjs.map +1 -0
  34. package/dist/{chunk-OVSNY3HC.js → chunk-Y54MUBT7.js} +3476 -2584
  35. package/dist/chunk-Y54MUBT7.js.map +1 -0
  36. package/dist/contracts/handlers/index.cjs +12 -8
  37. package/dist/contracts/handlers/index.d.cts +3 -3
  38. package/dist/contracts/handlers/index.d.ts +3 -3
  39. package/dist/contracts/handlers/index.js +2 -2
  40. package/dist/{delegate-D0eGaz3J.d.cts → delegate-DNEuM_nK.d.cts} +1 -1
  41. package/dist/{delegate-B_oKWm1k.d.ts → delegate-DidNTsKm.d.ts} +1 -1
  42. package/dist/{index-BmK6tEqf.d.ts → index-BP8-zniX.d.cts} +182 -26
  43. package/dist/{index-BG2AL2Ie.d.cts → index-DzQbNQUG.d.ts} +182 -26
  44. package/dist/index.cjs +302 -230
  45. package/dist/index.d.cts +286 -48
  46. package/dist/index.d.ts +286 -48
  47. package/dist/index.js +4 -4
  48. package/dist/repositories/realm/index.cjs +43 -23
  49. package/dist/repositories/realm/index.cjs.map +1 -1
  50. package/dist/repositories/realm/index.d.cts +21 -9
  51. package/dist/repositories/realm/index.d.ts +21 -9
  52. package/dist/repositories/realm/index.js +29 -9
  53. package/dist/repositories/realm/index.js.map +1 -1
  54. package/dist/repositories/sqlite/index.cjs +48 -23
  55. package/dist/repositories/sqlite/index.cjs.map +1 -1
  56. package/dist/repositories/sqlite/index.d.cts +9 -3
  57. package/dist/repositories/sqlite/index.d.ts +9 -3
  58. package/dist/repositories/sqlite/index.js +35 -10
  59. package/dist/repositories/sqlite/index.js.map +1 -1
  60. package/dist/{taskRunner-Cy4Gh8n7.d.ts → taskRunner-9gnlYEpb.d.ts} +1 -1
  61. package/dist/{taskRunner-CBvKIttE.d.cts → taskRunner-BEQNFEa4.d.cts} +1 -1
  62. package/dist/wallet/expo/background.cjs +14 -14
  63. package/dist/wallet/expo/background.d.cts +3 -3
  64. package/dist/wallet/expo/background.d.ts +3 -3
  65. package/dist/wallet/expo/background.js +6 -6
  66. package/dist/wallet/expo/index.cjs +17 -14
  67. package/dist/wallet/expo/index.cjs.map +1 -1
  68. package/dist/wallet/expo/index.d.cts +5 -4
  69. package/dist/wallet/expo/index.d.ts +5 -4
  70. package/dist/wallet/expo/index.js +8 -5
  71. package/dist/wallet/expo/index.js.map +1 -1
  72. package/dist/{wallet-EyVDhqzP.d.ts → wallet-C_MYHyOj.d.ts} +155 -17
  73. package/dist/{wallet-DlXemAzP.d.cts → wallet-ipUA1XCp.d.cts} +155 -17
  74. package/dist/worker/expo/index.cjs +9 -9
  75. package/dist/worker/expo/index.d.cts +5 -5
  76. package/dist/worker/expo/index.d.ts +5 -5
  77. package/dist/worker/expo/index.js +5 -5
  78. package/package.json +4 -2
  79. package/dist/chunk-2XO43UHI.cjs.map +0 -1
  80. package/dist/chunk-DSTYXT2D.cjs.map +0 -1
  81. package/dist/chunk-DYNB6POO.cjs.map +0 -1
  82. package/dist/chunk-FIMOVH25.js.map +0 -1
  83. package/dist/chunk-H342JPPZ.js.map +0 -1
  84. package/dist/chunk-IPFGYVF3.cjs.map +0 -1
  85. package/dist/chunk-OVSNY3HC.js.map +0 -1
  86. package/dist/chunk-PN5RD7ZI.js.map +0 -1
  87. package/dist/chunk-V2KENVN6.cjs.map +0 -1
  88. package/dist/chunk-YEHGCHIL.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkDSTYXT2D_cjs = require('./chunk-DSTYXT2D.cjs');
3
+ var chunk4A4GRSTB_cjs = require('./chunk-4A4GRSTB.cjs');
4
4
  var chunk5BLDMQED_cjs = require('./chunk-5BLDMQED.cjs');
5
5
  var base = require('@scure/base');
6
6
  var descriptorsScure = require('@bitcoinerlab/descriptors-scure');
@@ -76,14 +76,14 @@ var ContractHandlerRegistry = class {
76
76
  var contractHandlers = new ContractHandlerRegistry();
77
77
  exports.DefaultVtxo = void 0;
78
78
  ((DefaultVtxo2) => {
79
- class Script3 extends chunkDSTYXT2D_cjs.VtxoScript {
79
+ class Script3 extends chunk4A4GRSTB_cjs.VtxoScript {
80
80
  /** Create the default virtual output script with one forfeit path and one exit path. */
81
81
  constructor(options) {
82
82
  const { pubKey, serverPubKey, csvTimelock } = options;
83
- const forfeitScript = chunkDSTYXT2D_cjs.MultisigTapscript.encode({
83
+ const forfeitScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
84
84
  pubkeys: [pubKey, serverPubKey]
85
85
  }).script;
86
- const exitScript = chunkDSTYXT2D_cjs.CSVMultisigTapscript.encode({
86
+ const exitScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
87
87
  timelock: csvTimelock,
88
88
  pubkeys: [pubKey]
89
89
  }).script;
@@ -263,21 +263,49 @@ function extractPubKeyBytes(value) {
263
263
  return base.hex.decode(extractPubKey(normalizeToDescriptor(value)));
264
264
  }
265
265
  function deserializeCsvTimelock(value) {
266
- return value !== void 0 && value !== "" ? chunkDSTYXT2D_cjs.sequenceToTimelock(Number(value)) : exports.DefaultVtxo.Script.DEFAULT_TIMELOCK;
266
+ return value !== void 0 && value !== "" ? chunk4A4GRSTB_cjs.sequenceToTimelock(Number(value)) : exports.DefaultVtxo.Script.DEFAULT_TIMELOCK;
267
267
  }
268
268
  var CLTV_HEIGHT_THRESHOLD = 500000000n;
269
269
  function isCltvSatisfied(context, locktime) {
270
+ return cltvMaturity(context, locktime) === "satisfied";
271
+ }
272
+ function cltvMaturity(context, locktime) {
270
273
  if (locktime < CLTV_HEIGHT_THRESHOLD) {
271
- if (context.blockHeight === void 0) return false;
272
- return BigInt(context.blockHeight) >= locktime;
274
+ if (context.blockHeight === void 0) return "unknown";
275
+ return BigInt(context.blockHeight) >= locktime ? "satisfied" : "pending";
276
+ }
277
+ return nowSeconds(context) >= locktime ? "satisfied" : "pending";
278
+ }
279
+ function nowSeconds(context) {
280
+ return BigInt(Math.floor(context.chainTime ?? context.currentTime / 1e3));
281
+ }
282
+ function isHeightLocktime(locktime) {
283
+ return locktime < CLTV_HEIGHT_THRESHOLD;
284
+ }
285
+ function assertVhtlcSpendableNow(contract, context) {
286
+ if (!context.collaborative) return;
287
+ if (resolveRole(contract, context) !== "sender") return;
288
+ const raw = contract.params?.refundLocktime;
289
+ if (raw === void 0) return;
290
+ let refundLocktime;
291
+ try {
292
+ refundLocktime = BigInt(raw);
293
+ } catch {
294
+ return;
273
295
  }
274
- const currentTimeSec = BigInt(Math.floor(context.currentTime / 1e3));
275
- return currentTimeSec >= locktime;
296
+ if (cltvMaturity(context, refundLocktime) !== "pending") return;
297
+ const [matures, now] = isHeightLocktime(refundLocktime) ? [`block ${refundLocktime}`, `${context.blockHeight}`] : [
298
+ `${new Date(Number(refundLocktime) * 1e3).toISOString()}`,
299
+ new Date(context.currentTime).toISOString()
300
+ ];
301
+ throw new Error(
302
+ `vhtlc ${contract.script} cannot be spent yet: its refund path opens at ${matures}, now ${now}. Until then the lockup is the receiver's to claim, and the server rejects a spend of it. Retry after ${matures}, or wait for the receiver to claim it with the preimage.`
303
+ );
276
304
  }
277
305
  function isCsvSpendable(context, sequence) {
278
306
  if (sequence === void 0) return true;
279
307
  if (!context.vtxo) return false;
280
- const timelock = chunkDSTYXT2D_cjs.sequenceToTimelock(sequence);
308
+ const timelock = chunk4A4GRSTB_cjs.sequenceToTimelock(sequence);
281
309
  if (timelock.type === "blocks") {
282
310
  if (context.blockHeight === void 0 || context.vtxo.status.block_height === void 0) {
283
311
  return false;
@@ -287,7 +315,7 @@ function isCsvSpendable(context, sequence) {
287
315
  if (timelock.type === "seconds") {
288
316
  const blockTime = context.vtxo.status.block_time;
289
317
  if (blockTime === void 0) return false;
290
- return context.currentTime / 1e3 - blockTime >= Number(timelock.value);
318
+ return Number(nowSeconds(context)) - blockTime >= Number(timelock.value);
291
319
  }
292
320
  return false;
293
321
  }
@@ -335,21 +363,21 @@ function forfeitExitSpendablePaths(script, contract, context) {
335
363
  async function detectUsedScripts(indexerProvider, scriptHexes) {
336
364
  const used = /* @__PURE__ */ new Set();
337
365
  const unique = [...new Set(scriptHexes)];
338
- for (let offset = 0; offset < unique.length; offset += chunkDSTYXT2D_cjs.SCRIPT_QUERY_CHUNK_SIZE) {
339
- const scripts = unique.slice(offset, offset + chunkDSTYXT2D_cjs.SCRIPT_QUERY_CHUNK_SIZE);
366
+ for (let offset = 0; offset < unique.length; offset += chunk4A4GRSTB_cjs.SCRIPT_QUERY_CHUNK_SIZE) {
367
+ const scripts = unique.slice(offset, offset + chunk4A4GRSTB_cjs.SCRIPT_QUERY_CHUNK_SIZE);
340
368
  const remaining = new Set(scripts);
341
369
  let pageIndex = 0;
342
370
  let hasMore = true;
343
371
  while (hasMore && remaining.size > 0) {
344
- const { vtxos, page } = await chunkDSTYXT2D_cjs.getNormalizedVtxos(indexerProvider, {
372
+ const { vtxos, page } = await chunk4A4GRSTB_cjs.getNormalizedVtxos(indexerProvider, {
345
373
  scripts,
346
374
  pageIndex,
347
- pageSize: chunkDSTYXT2D_cjs.DEFAULT_PAGE_SIZE
375
+ pageSize: chunk4A4GRSTB_cjs.DEFAULT_PAGE_SIZE
348
376
  });
349
377
  for (const vtxo of vtxos) {
350
378
  if (remaining.delete(vtxo.script)) used.add(vtxo.script);
351
379
  }
352
- hasMore = page ? vtxos.length === chunkDSTYXT2D_cjs.DEFAULT_PAGE_SIZE : false;
380
+ hasMore = page ? vtxos.length === chunk4A4GRSTB_cjs.DEFAULT_PAGE_SIZE : false;
353
381
  pageIndex++;
354
382
  }
355
383
  }
@@ -410,7 +438,7 @@ var DefaultContractHandler = {
410
438
  return {
411
439
  pubKey: base.hex.encode(params.pubKey),
412
440
  serverPubKey: base.hex.encode(params.serverPubKey),
413
- csvTimelock: chunkDSTYXT2D_cjs.timelockToSequence(params.csvTimelock).toString()
441
+ csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(params.csvTimelock).toString()
414
442
  };
415
443
  },
416
444
  deserializeParams(params) {
@@ -429,6 +457,8 @@ var DefaultContractHandler = {
429
457
  getSpendablePaths(script, contract, context) {
430
458
  return forfeitExitSpendablePaths(script, contract, context);
431
459
  },
460
+ /** The wallet's own money. */
461
+ isGenericallySpendable: () => true,
432
462
  discoverAt: discoverAtViaRange(discoverDefaultRange),
433
463
  discoverRange: discoverDefaultRange,
434
464
  candidatesAt: defaultCandidatesAt
@@ -443,7 +473,7 @@ function defaultCandidatesAt(_index, descriptor, deps) {
443
473
  params: {
444
474
  pubKey: base.hex.encode(c.pubKey),
445
475
  serverPubKey: base.hex.encode(c.serverPubKey),
446
- csvTimelock: chunkDSTYXT2D_cjs.timelockToSequence(c.csvTimelock).toString()
476
+ csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(c.csvTimelock).toString()
447
477
  },
448
478
  script: c.scriptHex,
449
479
  address: c.script.address(deps.network.hrp, c.serverPubKey).encode()
@@ -459,12 +489,12 @@ function discoverDefaultRange(entries, deps) {
459
489
  }
460
490
  exports.DelegateVtxo = void 0;
461
491
  ((DelegateVtxo2) => {
462
- class Script3 extends chunkDSTYXT2D_cjs.VtxoScript {
492
+ class Script3 extends chunk4A4GRSTB_cjs.VtxoScript {
463
493
  /** Create a delegated virtual output script with forfeit, exit, and delegate paths. */
464
494
  constructor(options) {
465
495
  const defaultVtxo = new exports.DefaultVtxo.Script(options);
466
496
  const { delegatePubKey, pubKey, serverPubKey } = options;
467
- const delegateScript = chunkDSTYXT2D_cjs.MultisigTapscript.encode({
497
+ const delegateScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
468
498
  pubkeys: [pubKey, delegatePubKey, serverPubKey]
469
499
  }).script;
470
500
  super([...defaultVtxo.scripts, delegateScript]);
@@ -503,7 +533,7 @@ var DelegateContractHandler = {
503
533
  pubKey: base.hex.encode(params.pubKey),
504
534
  serverPubKey: base.hex.encode(params.serverPubKey),
505
535
  delegatePubKey: base.hex.encode(params.delegatePubKey),
506
- csvTimelock: chunkDSTYXT2D_cjs.timelockToSequence(params.csvTimelock).toString()
536
+ csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(params.csvTimelock).toString()
507
537
  };
508
538
  },
509
539
  deserializeParams(params) {
@@ -528,6 +558,8 @@ var DelegateContractHandler = {
528
558
  getSpendablePaths(script, contract, context) {
529
559
  return forfeitExitSpendablePaths(script, contract, context);
530
560
  },
561
+ /** The wallet's own money, delegated signing. */
562
+ isGenericallySpendable: () => true,
531
563
  discoverAt: discoverAtViaRange(discoverDelegateRange),
532
564
  discoverRange: discoverDelegateRange,
533
565
  candidatesAt: delegateCandidatesAt
@@ -545,7 +577,7 @@ function delegateCandidatesAt(_index, descriptor, deps) {
545
577
  pubKey: base.hex.encode(c.pubKey),
546
578
  serverPubKey: base.hex.encode(c.serverPubKey),
547
579
  delegatePubKey: base.hex.encode(delegatePubKey),
548
- csvTimelock: chunkDSTYXT2D_cjs.timelockToSequence(c.csvTimelock).toString()
580
+ csvTimelock: chunk4A4GRSTB_cjs.timelockToSequence(c.csvTimelock).toString()
549
581
  },
550
582
  script: c.scriptHex,
551
583
  address: c.script.address(deps.network.hrp, c.serverPubKey).encode()
@@ -562,1225 +594,1696 @@ function discoverDelegateRange(entries, deps) {
562
594
  (c, index, descriptor) => ({ ...c, ...rotatedReceiveMetadata(index, descriptor) })
563
595
  );
564
596
  }
565
- exports.VHTLC = void 0;
566
- ((VHTLC2) => {
567
- class Script3 extends chunkDSTYXT2D_cjs.VtxoScript {
568
- /** Create a VHTLC script from the supplied participant keys, hash, and timelocks. */
569
- constructor(options) {
570
- validateOptions(options);
571
- const {
572
- sender,
573
- receiver,
574
- server,
575
- preimageHash,
576
- refundLocktime,
577
- unilateralClaimDelay,
578
- unilateralRefundDelay,
579
- unilateralRefundWithoutReceiverDelay
580
- } = options;
581
- const conditionScript = preimageConditionScript(preimageHash);
582
- const claimScript = chunkDSTYXT2D_cjs.ConditionMultisigTapscript.encode({
583
- conditionScript,
584
- pubkeys: [receiver, server]
585
- }).script;
586
- const refundScript = chunkDSTYXT2D_cjs.MultisigTapscript.encode({
587
- pubkeys: [sender, receiver, server]
588
- }).script;
589
- const refundWithoutReceiverScript = chunkDSTYXT2D_cjs.CLTVMultisigTapscript.encode({
590
- absoluteTimelock: refundLocktime,
591
- pubkeys: [sender, server]
592
- }).script;
593
- const unilateralClaimScript = chunkDSTYXT2D_cjs.ConditionCSVMultisigTapscript.encode({
594
- conditionScript,
595
- timelock: unilateralClaimDelay,
596
- pubkeys: [receiver]
597
- }).script;
598
- const unilateralRefundScript = chunkDSTYXT2D_cjs.CSVMultisigTapscript.encode({
599
- timelock: unilateralRefundDelay,
600
- pubkeys: [sender, receiver]
601
- }).script;
602
- const unilateralRefundWithoutReceiverScript = chunkDSTYXT2D_cjs.CSVMultisigTapscript.encode({
603
- timelock: unilateralRefundWithoutReceiverDelay,
604
- pubkeys: [sender]
605
- }).script;
606
- super([
607
- claimScript,
608
- refundScript,
609
- refundWithoutReceiverScript,
610
- unilateralClaimScript,
611
- unilateralRefundScript,
612
- unilateralRefundWithoutReceiverScript
613
- ]);
614
- this.options = options;
615
- this.claimScript = base.hex.encode(claimScript);
616
- this.refundScript = base.hex.encode(refundScript);
617
- this.refundWithoutReceiverScript = base.hex.encode(refundWithoutReceiverScript);
618
- this.unilateralClaimScript = base.hex.encode(unilateralClaimScript);
619
- this.unilateralRefundScript = base.hex.encode(unilateralRefundScript);
620
- this.unilateralRefundWithoutReceiverScript = base.hex.encode(
621
- unilateralRefundWithoutReceiverScript
622
- );
623
- }
624
- options;
625
- claimScript;
626
- refundScript;
627
- refundWithoutReceiverScript;
628
- unilateralClaimScript;
629
- unilateralRefundScript;
630
- unilateralRefundWithoutReceiverScript;
631
- /** Return the collaborative claim tapleaf script. */
632
- claim() {
633
- return this.findLeaf(this.claimScript);
634
- }
635
- /** Return the collaborative refund tapleaf script. */
636
- refund() {
637
- return this.findLeaf(this.refundScript);
638
- }
639
- /** Return the refund-without-receiver tapleaf script. */
640
- refundWithoutReceiver() {
641
- return this.findLeaf(this.refundWithoutReceiverScript);
597
+ function equalBytes(a, b) {
598
+ if (a.length !== b.length)
599
+ return false;
600
+ for (let i = 0; i < a.length; i++)
601
+ if (a[i] !== b[i])
602
+ return false;
603
+ return true;
604
+ }
605
+ function isBytes(a) {
606
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
607
+ }
608
+ var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
609
+ function isPlainObject(obj) {
610
+ return Object.prototype.toString.call(obj) === "[object Object]";
611
+ }
612
+ function isNum(num) {
613
+ return Number.isSafeInteger(num);
614
+ }
615
+ var Bitset = {
616
+ BITS: 32,
617
+ FULL_MASK: -1 >>> 0,
618
+ // 1<<32 will overflow
619
+ len: (len) => Math.ceil(len / 32),
620
+ create: (len) => new Uint32Array(Bitset.len(len)),
621
+ clean: (bs) => bs.fill(0),
622
+ debug: (bs) => Array.from(bs).map((i) => (i >>> 0).toString(2).padStart(32, "0")),
623
+ checkLen: (bs, len) => {
624
+ if (Bitset.len(len) === bs.length)
625
+ return;
626
+ throw new Error(`wrong length=${bs.length}. Expected: ${Bitset.len(len)}`);
627
+ },
628
+ chunkLen: (bsLen, pos, len) => {
629
+ if (pos < 0)
630
+ throw new Error(`wrong pos=${pos}`);
631
+ if (pos + len > bsLen)
632
+ throw new Error(`wrong range=${pos}/${len} of ${bsLen}`);
633
+ },
634
+ set: (bs, chunk, value, allowRewrite = true) => {
635
+ if (!allowRewrite && (bs[chunk] & value) !== 0)
636
+ return false;
637
+ bs[chunk] |= value;
638
+ return true;
639
+ },
640
+ pos: (pos, i) => ({
641
+ chunk: Math.floor((pos + i) / 32),
642
+ mask: 1 << 32 - (pos + i) % 32 - 1
643
+ }),
644
+ indices: (bs, len, invert = false) => {
645
+ Bitset.checkLen(bs, len);
646
+ const { FULL_MASK, BITS } = Bitset;
647
+ const left = BITS - len % BITS;
648
+ const lastMask = left ? FULL_MASK >>> left << left : FULL_MASK;
649
+ const res = [];
650
+ for (let i = 0; i < bs.length; i++) {
651
+ let c = bs[i];
652
+ if (invert)
653
+ c = ~c;
654
+ if (i === bs.length - 1)
655
+ c &= lastMask;
656
+ if (c === 0)
657
+ continue;
658
+ for (let j = 0; j < BITS; j++) {
659
+ const m = 1 << BITS - j - 1;
660
+ if (c & m)
661
+ res.push(i * BITS + j);
662
+ }
642
663
  }
643
- /** Return the unilateral claim tapleaf script. */
644
- unilateralClaim() {
645
- return this.findLeaf(this.unilateralClaimScript);
664
+ return res;
665
+ },
666
+ range: (arr) => {
667
+ const res = [];
668
+ let cur;
669
+ for (const i of arr) {
670
+ if (cur === void 0 || i !== cur.pos + cur.length)
671
+ res.push(cur = { pos: i, length: 1 });
672
+ else
673
+ cur.length += 1;
646
674
  }
647
- /** Return the unilateral refund tapleaf script. */
648
- unilateralRefund() {
649
- return this.findLeaf(this.unilateralRefundScript);
675
+ return res;
676
+ },
677
+ rangeDebug: (bs, len, invert = false) => `[${Bitset.range(Bitset.indices(bs, len, invert)).map((i) => `(${i.pos}/${i.length})`).join(", ")}]`,
678
+ setRange: (bs, bsLen, pos, len, allowRewrite = true) => {
679
+ Bitset.chunkLen(bsLen, pos, len);
680
+ const { FULL_MASK, BITS } = Bitset;
681
+ const first = pos % BITS ? Math.floor(pos / BITS) : void 0;
682
+ const lastPos = pos + len;
683
+ const last = lastPos % BITS ? Math.floor(lastPos / BITS) : void 0;
684
+ if (first !== void 0 && first === last)
685
+ return Bitset.set(bs, first, FULL_MASK >>> BITS - len << BITS - len - pos, allowRewrite);
686
+ if (first !== void 0) {
687
+ if (!Bitset.set(bs, first, FULL_MASK >>> pos % BITS, allowRewrite))
688
+ return false;
650
689
  }
651
- /** Return the unilateral refund-without-receiver tapleaf script. */
652
- unilateralRefundWithoutReceiver() {
653
- return this.findLeaf(this.unilateralRefundWithoutReceiverScript);
690
+ const start = first !== void 0 ? first + 1 : pos / BITS;
691
+ const end = last !== void 0 ? last : lastPos / BITS;
692
+ for (let i = start; i < end; i++)
693
+ if (!Bitset.set(bs, i, FULL_MASK, allowRewrite))
694
+ return false;
695
+ if (last !== void 0 && first !== last) {
696
+ if (!Bitset.set(bs, last, FULL_MASK << BITS - lastPos % BITS, allowRewrite))
697
+ return false;
654
698
  }
699
+ return true;
655
700
  }
656
- VHTLC2.Script = Script3;
657
- function validateOptions(options) {
658
- const {
659
- sender,
660
- receiver,
661
- server,
662
- preimageHash,
663
- refundLocktime,
664
- unilateralClaimDelay,
665
- unilateralRefundDelay,
666
- unilateralRefundWithoutReceiverDelay
667
- } = options;
668
- if (!preimageHash || preimageHash.length !== 20) {
669
- throw new Error("preimage hash must be 20 bytes");
670
- }
671
- if (!receiver || receiver.length !== 32) {
672
- throw new Error("Invalid public key length (receiver)");
673
- }
674
- if (!sender || sender.length !== 32) {
675
- throw new Error("Invalid public key length (sender)");
676
- }
677
- if (!server || server.length !== 32) {
678
- throw new Error("Invalid public key length (server)");
701
+ };
702
+ var Path = {
703
+ /**
704
+ * Internal method for handling stack of paths (debug, errors, dynamic fields via path)
705
+ * This is looks ugly (callback), but allows us to force stack cleaning by construction (.pop always after function).
706
+ * Also, this makes impossible:
707
+ * - pushing field when stack is empty
708
+ * - pushing field inside of field (real bug)
709
+ * NOTE: we don't want to do '.pop' on error!
710
+ */
711
+ pushObj: (stack, obj, objFn) => {
712
+ const last = { obj };
713
+ stack.push(last);
714
+ objFn((field, fieldFn) => {
715
+ last.field = field;
716
+ fieldFn();
717
+ last.field = void 0;
718
+ });
719
+ stack.pop();
720
+ },
721
+ path: (stack) => {
722
+ const res = [];
723
+ for (const i of stack)
724
+ if (i.field !== void 0)
725
+ res.push(i.field);
726
+ return res.join("/");
727
+ },
728
+ err: (name, stack, msg) => {
729
+ const err = new Error(`${name}(${Path.path(stack)}): ${typeof msg === "string" ? msg : msg.message}`);
730
+ if (msg instanceof Error && msg.stack)
731
+ err.stack = msg.stack;
732
+ return err;
733
+ },
734
+ resolve: (stack, path) => {
735
+ const parts = path.split("/");
736
+ const objPath = stack.map((i2) => i2.obj);
737
+ let i = 0;
738
+ for (; i < parts.length; i++) {
739
+ if (parts[i] === "..")
740
+ objPath.pop();
741
+ else
742
+ break;
679
743
  }
680
- if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
681
- throw new Error("refund locktime must be greater than 0");
744
+ let cur = objPath.pop();
745
+ for (; i < parts.length; i++) {
746
+ if (!cur || cur[parts[i]] === void 0)
747
+ return void 0;
748
+ cur = cur[parts[i]];
682
749
  }
683
- if (!unilateralClaimDelay || typeof unilateralClaimDelay.value !== "bigint" || unilateralClaimDelay.value <= 0n) {
684
- throw new Error("unilateral claim delay must greater than 0");
685
- }
686
- if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value % 512n !== 0n) {
687
- throw new Error("seconds timelock must be multiple of 512");
750
+ return cur;
751
+ }
752
+ };
753
+ var _Reader = class __Reader {
754
+ pos = 0;
755
+ data;
756
+ opts;
757
+ stack;
758
+ parent;
759
+ parentOffset;
760
+ bitBuf = 0;
761
+ bitPos = 0;
762
+ bs;
763
+ // bitset
764
+ view;
765
+ constructor(data, opts = {}, stack = [], parent = void 0, parentOffset = 0) {
766
+ this.data = data;
767
+ this.opts = opts;
768
+ this.stack = stack;
769
+ this.parent = parent;
770
+ this.parentOffset = parentOffset;
771
+ this.view = createView(data);
772
+ }
773
+ /** Internal method for pointers. */
774
+ _enablePointers() {
775
+ if (this.parent)
776
+ return this.parent._enablePointers();
777
+ if (this.bs)
778
+ return;
779
+ this.bs = Bitset.create(this.data.length);
780
+ Bitset.setRange(this.bs, this.data.length, 0, this.pos, this.opts.allowMultipleReads);
781
+ }
782
+ markBytesBS(pos, len) {
783
+ if (this.parent)
784
+ return this.parent.markBytesBS(this.parentOffset + pos, len);
785
+ if (!len)
786
+ return true;
787
+ if (!this.bs)
788
+ return true;
789
+ return Bitset.setRange(this.bs, this.data.length, pos, len, false);
790
+ }
791
+ markBytes(len) {
792
+ const pos = this.pos;
793
+ this.pos += len;
794
+ const res = this.markBytesBS(pos, len);
795
+ if (!this.opts.allowMultipleReads && !res)
796
+ throw this.err(`multiple read pos=${this.pos} len=${len}`);
797
+ return res;
798
+ }
799
+ pushObj(obj, objFn) {
800
+ return Path.pushObj(this.stack, obj, objFn);
801
+ }
802
+ readView(n, fn) {
803
+ if (!Number.isFinite(n))
804
+ throw this.err(`readView: wrong length=${n}`);
805
+ if (this.pos + n > this.data.length)
806
+ throw this.err("readView: Unexpected end of buffer");
807
+ const res = fn(this.view, this.pos);
808
+ this.markBytes(n);
809
+ return res;
810
+ }
811
+ // read bytes by absolute offset
812
+ absBytes(n) {
813
+ if (n > this.data.length)
814
+ throw new Error("Unexpected end of buffer");
815
+ return this.data.subarray(n);
816
+ }
817
+ finish() {
818
+ if (this.opts.allowUnreadBytes)
819
+ return;
820
+ if (this.bitPos) {
821
+ throw this.err(`${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
688
822
  }
689
- if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value < 512n) {
690
- throw new Error("seconds timelock must be greater or equal to 512");
823
+ if (this.bs && !this.parent) {
824
+ const notRead = Bitset.indices(this.bs, this.data.length, true);
825
+ if (notRead.length) {
826
+ const formatted = Bitset.range(notRead).map(({ pos, length }) => `(${pos}/${length})[${base.hex.encode(this.data.subarray(pos, pos + length))}]`).join(", ");
827
+ throw this.err(`unread byte ranges: ${formatted} (total=${this.data.length})`);
828
+ } else
829
+ return;
691
830
  }
692
- if (!unilateralRefundDelay || typeof unilateralRefundDelay.value !== "bigint" || unilateralRefundDelay.value <= 0n) {
693
- throw new Error("unilateral refund delay must greater than 0");
831
+ if (!this.isEnd()) {
832
+ throw this.err(`${this.leftBytes} bytes ${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
694
833
  }
695
- if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value % 512n !== 0n) {
696
- throw new Error("seconds timelock must be multiple of 512");
834
+ }
835
+ // User methods
836
+ err(msg) {
837
+ return Path.err("Reader", this.stack, msg);
838
+ }
839
+ offsetReader(n) {
840
+ if (n > this.data.length)
841
+ throw this.err("offsetReader: Unexpected end of buffer");
842
+ return new __Reader(this.absBytes(n), this.opts, this.stack, this, n);
843
+ }
844
+ bytes(n, peek = false) {
845
+ if (this.bitPos)
846
+ throw this.err("readBytes: bitPos not empty");
847
+ if (!Number.isFinite(n))
848
+ throw this.err(`readBytes: wrong length=${n}`);
849
+ if (this.pos + n > this.data.length)
850
+ throw this.err("readBytes: Unexpected end of buffer");
851
+ const slice = this.data.subarray(this.pos, this.pos + n);
852
+ if (!peek)
853
+ this.markBytes(n);
854
+ return slice;
855
+ }
856
+ byte(peek = false) {
857
+ if (this.bitPos)
858
+ throw this.err("readByte: bitPos not empty");
859
+ if (this.pos + 1 > this.data.length)
860
+ throw this.err("readBytes: Unexpected end of buffer");
861
+ const data = this.data[this.pos];
862
+ if (!peek)
863
+ this.markBytes(1);
864
+ return data;
865
+ }
866
+ get leftBytes() {
867
+ return this.data.length - this.pos;
868
+ }
869
+ get totalBytes() {
870
+ return this.data.length;
871
+ }
872
+ isEnd() {
873
+ return this.pos >= this.data.length && !this.bitPos;
874
+ }
875
+ // bits are read in BE mode (left to right): (0b1000_0000).readBits(1) == 1
876
+ bits(bits) {
877
+ if (bits > 32)
878
+ throw this.err("BitReader: cannot read more than 32 bits in single call");
879
+ let out = 0;
880
+ while (bits) {
881
+ if (!this.bitPos) {
882
+ this.bitBuf = this.byte();
883
+ this.bitPos = 8;
884
+ }
885
+ const take = Math.min(bits, this.bitPos);
886
+ this.bitPos -= take;
887
+ out = out << take | this.bitBuf >> this.bitPos & 2 ** take - 1;
888
+ this.bitBuf &= 2 ** this.bitPos - 1;
889
+ bits -= take;
697
890
  }
698
- if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value < 512n) {
699
- throw new Error("seconds timelock must be greater or equal to 512");
891
+ return out >>> 0;
892
+ }
893
+ find(needle, pos = this.pos) {
894
+ if (!isBytes(needle))
895
+ throw this.err(`find: needle is not bytes! ${needle}`);
896
+ if (this.bitPos)
897
+ throw this.err("findByte: bitPos not empty");
898
+ if (!needle.length)
899
+ throw this.err(`find: needle is empty`);
900
+ for (let idx = pos; (idx = this.data.indexOf(needle[0], idx)) !== -1; idx++) {
901
+ if (idx === -1)
902
+ return;
903
+ const leftBytes = this.data.length - idx;
904
+ if (leftBytes < needle.length)
905
+ return;
906
+ if (equalBytes(needle, this.data.subarray(idx, idx + needle.length)))
907
+ return idx;
700
908
  }
701
- if (!unilateralRefundWithoutReceiverDelay || typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" || unilateralRefundWithoutReceiverDelay.value <= 0n) {
702
- throw new Error("unilateral refund without receiver delay must greater than 0");
909
+ return;
910
+ }
911
+ };
912
+ var _Writer = class {
913
+ pos = 0;
914
+ stack;
915
+ // We could have a single buffer here and re-alloc it with
916
+ // x1.5-2 size each time it full, but it will be slower:
917
+ // basic/encode bench: 395ns -> 560ns
918
+ buffers = [];
919
+ ptrs = [];
920
+ bitBuf = 0;
921
+ bitPos = 0;
922
+ viewBuf = new Uint8Array(8);
923
+ view;
924
+ finished = false;
925
+ constructor(stack = []) {
926
+ this.stack = stack;
927
+ this.view = createView(this.viewBuf);
928
+ }
929
+ pushObj(obj, objFn) {
930
+ return Path.pushObj(this.stack, obj, objFn);
931
+ }
932
+ writeView(len, fn) {
933
+ if (this.finished)
934
+ throw this.err("buffer: finished");
935
+ if (!isNum(len) || len > 8)
936
+ throw new Error(`wrong writeView length=${len}`);
937
+ fn(this.view);
938
+ this.bytes(this.viewBuf.slice(0, len));
939
+ this.viewBuf.fill(0);
940
+ }
941
+ // User methods
942
+ err(msg) {
943
+ if (this.finished)
944
+ throw this.err("buffer: finished");
945
+ return Path.err("Reader", this.stack, msg);
946
+ }
947
+ bytes(b) {
948
+ if (this.finished)
949
+ throw this.err("buffer: finished");
950
+ if (this.bitPos)
951
+ throw this.err("writeBytes: ends with non-empty bit buffer");
952
+ this.buffers.push(b);
953
+ this.pos += b.length;
954
+ }
955
+ byte(b) {
956
+ if (this.finished)
957
+ throw this.err("buffer: finished");
958
+ if (this.bitPos)
959
+ throw this.err("writeByte: ends with non-empty bit buffer");
960
+ this.buffers.push(new Uint8Array([b]));
961
+ this.pos++;
962
+ }
963
+ finish(clean = true) {
964
+ if (this.finished)
965
+ throw this.err("buffer: finished");
966
+ if (this.bitPos)
967
+ throw this.err("buffer: ends with non-empty bit buffer");
968
+ const buffers = this.buffers.concat(this.ptrs.map((i) => i.buffer));
969
+ const sum = buffers.map((b) => b.length).reduce((a, b) => a + b, 0);
970
+ const buf = new Uint8Array(sum);
971
+ for (let i = 0, pad = 0; i < buffers.length; i++) {
972
+ const a = buffers[i];
973
+ buf.set(a, pad);
974
+ pad += a.length;
703
975
  }
704
- if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
705
- throw new Error("seconds timelock must be multiple of 512");
976
+ for (let pos = this.pos, i = 0; i < this.ptrs.length; i++) {
977
+ const ptr = this.ptrs[i];
978
+ buf.set(ptr.ptr.encode(pos), ptr.pos);
979
+ pos += ptr.buffer.length;
706
980
  }
707
- if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value < 512n) {
708
- throw new Error("seconds timelock must be greater or equal to 512");
981
+ if (clean) {
982
+ this.buffers = [];
983
+ for (const p of this.ptrs)
984
+ p.buffer.fill(0);
985
+ this.ptrs = [];
986
+ this.finished = true;
987
+ this.bitBuf = 0;
709
988
  }
989
+ return buf;
710
990
  }
711
- })(exports.VHTLC || (exports.VHTLC = {}));
712
- function preimageConditionScript(preimageHash) {
713
- return btcSigner.Script.encode(["HASH160", preimageHash, "EQUAL"]);
714
- }
715
-
716
- // src/contracts/handlers/vhtlc.ts
717
- var VHTLCContractHandler = {
718
- type: "vhtlc",
719
- createScript(params) {
720
- const typed = this.deserializeParams(params);
721
- return new exports.VHTLC.Script(typed);
722
- },
723
- serializeParams(params) {
724
- return {
725
- sender: base.hex.encode(params.sender),
726
- receiver: base.hex.encode(params.receiver),
727
- server: base.hex.encode(params.server),
728
- hash: base.hex.encode(params.preimageHash),
729
- refundLocktime: params.refundLocktime.toString(),
730
- claimDelay: chunkDSTYXT2D_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
731
- refundDelay: chunkDSTYXT2D_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
732
- refundNoReceiverDelay: chunkDSTYXT2D_cjs.timelockToSequence(
733
- params.unilateralRefundWithoutReceiverDelay
734
- ).toString()
735
- };
736
- },
737
- deserializeParams(params) {
738
- return {
739
- sender: base.hex.decode(params.sender),
740
- receiver: base.hex.decode(params.receiver),
741
- server: base.hex.decode(params.server),
742
- preimageHash: base.hex.decode(params.hash),
743
- refundLocktime: BigInt(params.refundLocktime),
744
- unilateralClaimDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(Number(params.claimDelay)),
745
- unilateralRefundDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(Number(params.refundDelay)),
746
- unilateralRefundWithoutReceiverDelay: chunkDSTYXT2D_cjs.sequenceToTimelock(
747
- Number(params.refundNoReceiverDelay)
748
- )
749
- };
750
- },
751
- /**
752
- * Select spending path based on context.
753
- *
754
- * Role is determined from `context.role` or by matching
755
- * `context.walletDescriptor` (preferred) / `context.walletPubKey`
756
- * against sender/receiver in contract params.
757
- */
758
- selectPath(script, contract, context) {
759
- const role = resolveRole(contract, context);
760
- const preimage = contract.params?.preimage;
761
- const refundLocktime = BigInt(contract.params.refundLocktime);
762
- if (!role) {
763
- return null;
764
- }
765
- if (context.collaborative) {
766
- if (role === "receiver" && preimage) {
767
- return {
768
- leaf: script.claim(),
769
- extraWitness: [base.hex.decode(preimage)]
770
- };
771
- }
772
- if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
773
- return {
774
- leaf: script.refundWithoutReceiver()
775
- };
776
- }
777
- return null;
778
- }
779
- if (role === "receiver" && preimage) {
780
- const sequence = Number(contract.params.claimDelay);
781
- if (!isCsvSpendable(context, sequence)) return null;
782
- return {
783
- leaf: script.unilateralClaim(),
784
- extraWitness: [base.hex.decode(preimage)],
785
- sequence
786
- };
787
- }
788
- if (role === "sender") {
789
- const sequence = Number(contract.params.refundNoReceiverDelay);
790
- if (!isCsvSpendable(context, sequence)) return null;
791
- return {
792
- leaf: script.unilateralRefundWithoutReceiver(),
793
- sequence
794
- };
795
- }
796
- return null;
797
- },
798
- /**
799
- * Get all possible spending paths (no timelock checks).
800
- *
801
- * Role is determined from `context.role` or by matching
802
- * `context.walletDescriptor` (preferred) / `context.walletPubKey`
803
- * against sender/receiver in contract params.
804
- */
805
- getAllSpendingPaths(script, contract, context) {
806
- const role = resolveRole(contract, context);
807
- const paths = [];
808
- if (!role) {
809
- return paths;
810
- }
811
- const preimage = contract.params?.preimage;
812
- if (context.collaborative) {
813
- if (role === "receiver" && preimage) {
814
- paths.push({
815
- leaf: script.claim(),
816
- extraWitness: [base.hex.decode(preimage)]
817
- });
818
- }
819
- if (role === "sender") {
820
- paths.push({
821
- leaf: script.refundWithoutReceiver()
822
- });
823
- }
824
- } else {
825
- if (role === "receiver" && preimage) {
826
- const sequence = Number(contract.params.claimDelay);
827
- paths.push({
828
- leaf: script.unilateralClaim(),
829
- extraWitness: [base.hex.decode(preimage)],
830
- sequence
831
- });
832
- }
833
- if (role === "sender") {
834
- const sequence = Number(contract.params.refundNoReceiverDelay);
835
- paths.push({
836
- leaf: script.unilateralRefundWithoutReceiver(),
837
- sequence
838
- });
991
+ bits(value, bits) {
992
+ if (bits > 32)
993
+ throw this.err("writeBits: cannot write more than 32 bits in single call");
994
+ if (value >= 2 ** bits)
995
+ throw this.err(`writeBits: value (${value}) >= 2**bits (${bits})`);
996
+ while (bits) {
997
+ const take = Math.min(bits, 8 - this.bitPos);
998
+ this.bitBuf = this.bitBuf << take | value >> bits - take;
999
+ this.bitPos += take;
1000
+ bits -= take;
1001
+ value &= 2 ** bits - 1;
1002
+ if (this.bitPos === 8) {
1003
+ this.bitPos = 0;
1004
+ this.buffers.push(new Uint8Array([this.bitBuf]));
1005
+ this.pos++;
839
1006
  }
840
1007
  }
841
- return paths;
842
- },
843
- getSpendablePaths(script, contract, context) {
844
- const role = resolveRole(contract, context);
845
- const paths = [];
846
- if (!role) {
847
- return paths;
1008
+ }
1009
+ };
1010
+ function _wrap(inner) {
1011
+ return {
1012
+ // NOTE: we cannot export validate here, since it is likely mistake.
1013
+ encodeStream: inner.encodeStream,
1014
+ decodeStream: inner.decodeStream,
1015
+ size: inner.size,
1016
+ encode: (value) => {
1017
+ const w = new _Writer();
1018
+ inner.encodeStream(w, value);
1019
+ return w.finish();
1020
+ },
1021
+ decode: (data, opts = {}) => {
1022
+ const r = new _Reader(data, opts);
1023
+ const res = inner.decodeStream(r);
1024
+ r.finish();
1025
+ return res;
848
1026
  }
849
- const preimage = contract.params?.preimage;
850
- const refundLocktime = BigInt(contract.params.refundLocktime);
851
- if (context.collaborative) {
852
- if (role === "receiver" && preimage) {
853
- paths.push({
854
- leaf: script.claim(),
855
- extraWitness: [base.hex.decode(preimage)]
856
- });
1027
+ };
1028
+ }
1029
+ function validate(inner, fn) {
1030
+ if (!isCoder(inner))
1031
+ throw new Error(`validate: invalid inner value ${inner}`);
1032
+ if (typeof fn !== "function")
1033
+ throw new Error("validate: fn should be function");
1034
+ return _wrap({
1035
+ size: inner.size,
1036
+ encodeStream: (w, value) => {
1037
+ let res;
1038
+ try {
1039
+ res = fn(value);
1040
+ } catch (e) {
1041
+ throw w.err(e);
857
1042
  }
858
- if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
859
- paths.push({
860
- leaf: script.refundWithoutReceiver()
861
- });
1043
+ inner.encodeStream(w, res);
1044
+ },
1045
+ decodeStream: (r) => {
1046
+ const res = inner.decodeStream(r);
1047
+ try {
1048
+ return fn(res);
1049
+ } catch (e) {
1050
+ throw r.err(e);
862
1051
  }
863
- return paths;
864
1052
  }
865
- if (role === "receiver" && preimage) {
866
- const sequence = Number(contract.params.claimDelay);
867
- if (isCsvSpendable(context, sequence)) {
868
- paths.push({
869
- leaf: script.unilateralClaim(),
870
- extraWitness: [base.hex.decode(preimage)],
871
- sequence
872
- });
873
- }
1053
+ });
1054
+ }
1055
+ var wrap = (inner) => {
1056
+ const res = _wrap(inner);
1057
+ return inner.validate ? validate(res, inner.validate) : res;
1058
+ };
1059
+ var isBaseCoder = (elm) => isPlainObject(elm) && typeof elm.decode === "function" && typeof elm.encode === "function";
1060
+ function isCoder(elm) {
1061
+ return isPlainObject(elm) && isBaseCoder(elm) && typeof elm.encodeStream === "function" && typeof elm.decodeStream === "function" && (elm.size === void 0 || isNum(elm.size));
1062
+ }
1063
+ var view = (len, opts) => wrap({
1064
+ size: len,
1065
+ encodeStream: (w, value) => w.writeView(len, (view2) => opts.write(view2, value)),
1066
+ decodeStream: (r) => r.readView(len, opts.read),
1067
+ validate: (value) => {
1068
+ if (typeof value !== "number")
1069
+ throw new Error(`viewCoder: expected number, got ${typeof value}`);
1070
+ if (opts.validate)
1071
+ opts.validate(value);
1072
+ return value;
1073
+ }
1074
+ });
1075
+ var intView = (len, signed, opts) => {
1076
+ const bits = len * 8;
1077
+ const maxVal = 2 ** bits;
1078
+ const validateUnsigned = (value) => {
1079
+ if (!isNum(value))
1080
+ throw new Error(`uintView: value is not safe integer: ${value}`);
1081
+ if (0 > value || value >= maxVal) {
1082
+ throw new Error(`uintView: value out of bounds. Expected 0 <= ${value} < ${maxVal}`);
874
1083
  }
875
- if (role === "sender") {
876
- const sequence = Number(contract.params.refundNoReceiverDelay);
877
- if (isCsvSpendable(context, sequence)) {
878
- paths.push({
879
- leaf: script.unilateralRefundWithoutReceiver(),
880
- sequence
881
- });
882
- }
1084
+ };
1085
+ return view(len, {
1086
+ write: opts.write,
1087
+ read: opts.read,
1088
+ validate: validateUnsigned
1089
+ });
1090
+ };
1091
+ var U32LE = /* @__PURE__ */ intView(4, false, {
1092
+ read: (view2, pos) => view2.getUint32(pos, true),
1093
+ write: (view2, value) => view2.setUint32(0, value, true)
1094
+ });
1095
+ var U16LE = /* @__PURE__ */ intView(2, false, {
1096
+ read: (view2, pos) => view2.getUint16(pos, true),
1097
+ write: (view2, value) => view2.setUint16(0, value, true)
1098
+ });
1099
+ var U8 = /* @__PURE__ */ intView(1, false, {
1100
+ read: (view2, pos) => view2.getUint8(pos),
1101
+ write: (view2, value) => view2.setUint8(0, value)
1102
+ });
1103
+ var ARKADE_OP = {
1104
+ // Merkle Branch Verification (0xb3 — repurposed NOP4 slot)
1105
+ MERKLEBRANCHVERIFY: 179,
1106
+ // Digest (0xc3)
1107
+ DIGEST: 195,
1108
+ // SHA256 Streaming (0xc4-0xc6)
1109
+ SHA256INITIALIZE: 196,
1110
+ SHA256UPDATE: 197,
1111
+ SHA256FINALIZE: 198,
1112
+ // Input Introspection (0xc7-0xcb)
1113
+ INSPECTINPUTOUTPOINT: 199,
1114
+ INSPECTINPUTARKADESCRIPTHASH: 200,
1115
+ INSPECTINPUTVALUE: 201,
1116
+ INSPECTINPUTSCRIPTPUBKEY: 202,
1117
+ INSPECTINPUTSEQUENCE: 203,
1118
+ // Signatures (0xcc-0xcd)
1119
+ CHECKSIGFROMSTACK: 204,
1120
+ PUSHCURRENTINPUTINDEX: 205,
1121
+ // Input Arkade Witness Introspection (0xce)
1122
+ INSPECTINPUTARKADEWITNESSHASH: 206,
1123
+ // Output Introspection (0xcf, 0xd1)
1124
+ INSPECTOUTPUTVALUE: 207,
1125
+ INSPECTOUTPUTSCRIPTPUBKEY: 209,
1126
+ // Transaction Introspection (0xd2-0xd6)
1127
+ INSPECTVERSION: 210,
1128
+ INSPECTLOCKTIME: 211,
1129
+ INSPECTNUMINPUTS: 212,
1130
+ INSPECTNUMOUTPUTS: 213,
1131
+ TXWEIGHT: 214,
1132
+ // Byte/Number Conversion & Arithmetic (0xd7-0xda)
1133
+ NUM2BIN: 215,
1134
+ BIN2NUM: 216,
1135
+ REVERSEBYTES: 217,
1136
+ MODEXP: 218,
1137
+ // 0xdb-0xdf are unassigned (OP_UNKNOWN219-223)
1138
+ // EC Operations (0xe0-0xe2)
1139
+ ECADD: 224,
1140
+ ECMUL: 225,
1141
+ ECPAIRING: 226,
1142
+ // EC Operations (0xe3-0xe4)
1143
+ ECMULSCALARVERIFY: 227,
1144
+ TWEAKVERIFY: 228,
1145
+ // Asset Groups (0xe5-0xf2)
1146
+ INSPECTNUMASSETGROUPS: 229,
1147
+ INSPECTASSETGROUPASSETID: 230,
1148
+ INSPECTASSETGROUPCTRL: 231,
1149
+ FINDASSETGROUPBYASSETID: 232,
1150
+ INSPECTASSETGROUPMETADATAHASH: 233,
1151
+ INSPECTASSETGROUPNUM: 234,
1152
+ INSPECTASSETGROUP: 235,
1153
+ INSPECTASSETGROUPSUM: 236,
1154
+ INSPECTOUTASSETCOUNT: 237,
1155
+ INSPECTOUTASSETAT: 238,
1156
+ INSPECTOUTASSETLOOKUP: 239,
1157
+ INSPECTINASSETCOUNT: 240,
1158
+ INSPECTINASSETAT: 241,
1159
+ INSPECTINASSETLOOKUP: 242,
1160
+ // Transaction ID (0xf3)
1161
+ TXID: 243,
1162
+ // Packet Introspection (0xf4-0xf5) — added in emulator v0.0.1
1163
+ INSPECTPACKET: 244,
1164
+ INSPECTINPUTPACKET: 245,
1165
+ // Signature Hash (0xf6)
1166
+ SIGHASH: 246
1167
+ };
1168
+ var ARKADE_OPCODES = Object.values(ARKADE_OP);
1169
+ var ARKADE_OPCODE_NAMES = Object.fromEntries(
1170
+ Object.entries(ARKADE_OP).map(([name, value]) => [value, name])
1171
+ );
1172
+ var ARKADE_OPCODE_VALUES = Object.fromEntries(
1173
+ Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [name, Number(value)])
1174
+ );
1175
+ function buildBitcoinOpcodeNames() {
1176
+ const names = {};
1177
+ for (const [key, value] of Object.entries(btcSigner.OP)) {
1178
+ if (typeof value === "number") {
1179
+ const name = key.startsWith("OP_") ? key : `OP_${key}`;
1180
+ names[value] = name;
883
1181
  }
884
- return paths;
885
1182
  }
886
- };
887
- var BoardingContractHandler = {
888
- type: "boarding",
889
- createScript(params) {
890
- return DefaultContractHandler.createScript(params);
891
- },
892
- serializeParams(params) {
893
- return DefaultContractHandler.serializeParams(params);
894
- },
895
- deserializeParams(params) {
896
- return DefaultContractHandler.deserializeParams(params);
897
- },
898
- selectPath(script, contract, context) {
899
- return DefaultContractHandler.selectPath(script, contract, context);
900
- },
901
- getAllSpendingPaths(script, contract, context) {
902
- return DefaultContractHandler.getAllSpendingPaths(script, contract, context);
903
- },
904
- getSpendablePaths(script, contract, context) {
905
- return DefaultContractHandler.getSpendablePaths(script, contract, context);
906
- },
907
- /**
908
- * Probe the on-chain UTXO set for a boarding output at this HD index.
909
- *
910
- * Boarding's source of truth is the **current** on-chain coin set
911
- * (`OnchainProvider.getCoins`), not the Ark indexer: a boarded (spent)
912
- * boarding output becomes an L2 VTXO at the receive index, so the
913
- * indexer probe already keeps the gap window open for it; only an
914
- * *unspent* boarding output needs the on-chain probe (see plan §2).
915
- *
916
- * No-ops (returns `[]`) when boarding discovery is not plumbed — i.e.
917
- * `deps.boardingTimelock` or `deps.onchainNetwork` is absent — so the
918
- * scanner unit harness (which sets neither) is unaffected.
919
- *
920
- * Always emits `type: "boarding"`, even in the degenerate equal-delay case
921
- * where the boarding script is byte-identical to a `default` script at this
922
- * index (`boardingExitDelay === unilateralExitDelay`). The collision is no
923
- * longer pre-judged in discovery; it is tolerated FIRST-WINS at the
924
- * persistence layer ({@link ContractManager.upsertContract}). `deps.csvTimelocks`
925
- * is intentionally not read here.
926
- */
927
- async discoverAt(index, descriptor, deps) {
928
- if (!deps.boardingTimelock || !deps.onchainNetwork) return [];
929
- const pubKey = deriveDescriptorLeafPubKey(descriptor);
930
- const script = new exports.DefaultVtxo.Script({
931
- pubKey,
932
- serverPubKey: deps.serverPubKey,
933
- csvTimelock: deps.boardingTimelock
934
- });
935
- const onchainAddress = script.onchainAddress(deps.onchainNetwork);
936
- const coins = await deps.onchainProvider.getCoins(onchainAddress);
937
- if (coins.length === 0) return [];
938
- const scriptHex = base.hex.encode(script.pkScript);
939
- const boardingSeq = chunkDSTYXT2D_cjs.timelockToSequence(deps.boardingTimelock);
940
- return [
941
- {
942
- type: "boarding",
943
- params: {
944
- pubKey: base.hex.encode(pubKey),
945
- serverPubKey: base.hex.encode(deps.serverPubKey),
946
- csvTimelock: boardingSeq.toString()
947
- },
948
- script: scriptHex,
949
- // The persisted row's `address` is the *Ark* address (not the
950
- // on-chain P2TR), matching the row registered at init so the
951
- // ContractWatcher keeps monitoring the same L2 script and the
952
- // VTXO repository bucket lines up (plan §6-I.4). The P2TR is
953
- // recomputed from params only for the `getCoins` probe.
954
- address: script.address(deps.network.hrp, deps.serverPubKey).encode(),
955
- // Tag rotated rows (index > 0) so boot resolution can find the
956
- // newest boarding address and descriptor-aware signing can
957
- // recover the per-index key (plan §6-II/§6-III.3). The index-0
958
- // baseline stays untagged, matching `default`/`delegate`.
959
- ...rotatedReceiveMetadata(index, descriptor)
960
- }
961
- ];
1183
+ names[0] = "OP_0";
1184
+ return names;
1185
+ }
1186
+ function buildBitcoinOpcodeValues() {
1187
+ const values = {};
1188
+ for (const [key, value] of Object.entries(btcSigner.OP)) {
1189
+ if (typeof value === "number") {
1190
+ const name = key.startsWith("OP_") ? key : `OP_${key}`;
1191
+ values[name] = value;
1192
+ values[key] = value;
1193
+ }
962
1194
  }
1195
+ return values;
1196
+ }
1197
+ var BITCOIN_OPCODE_NAMES = buildBitcoinOpcodeNames();
1198
+ var BITCOIN_OPCODE_VALUES = buildBitcoinOpcodeValues();
1199
+ var OPCODE_NAMES = {
1200
+ ...BITCOIN_OPCODE_NAMES,
1201
+ // Add Arkade opcodes with OP_ prefix
1202
+ ...Object.fromEntries(
1203
+ Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [Number(value), `OP_${name}`])
1204
+ )
963
1205
  };
964
- function equalBytes(a, b) {
965
- if (a.length !== b.length)
966
- return false;
967
- for (let i = 0; i < a.length; i++)
968
- if (a[i] !== b[i])
969
- return false;
970
- return true;
1206
+ var OPCODE_VALUES = {
1207
+ ...BITCOIN_OPCODE_VALUES,
1208
+ // Add Arkade opcodes with and without OP_ prefix
1209
+ ...ARKADE_OPCODE_VALUES,
1210
+ ...Object.fromEntries(
1211
+ Object.entries(ARKADE_OPCODE_VALUES).map(([name, value]) => [`OP_${name}`, value])
1212
+ )
1213
+ };
1214
+ function getOpcodeName(value) {
1215
+ if (value >= 1 && value <= 75) {
1216
+ return `OP_DATA_${value}`;
1217
+ }
1218
+ return OPCODE_NAMES[value];
971
1219
  }
972
- function isBytes(a) {
973
- return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
1220
+ function getOpcodeValue(name) {
1221
+ const dataMatch = name.match(/^OP_DATA_(\d+)$/);
1222
+ if (dataMatch) {
1223
+ const n = parseInt(dataMatch[1], 10);
1224
+ if (n >= 1 && n <= 75) {
1225
+ return n;
1226
+ }
1227
+ return void 0;
1228
+ }
1229
+ return OPCODE_VALUES[name];
974
1230
  }
975
- var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
976
- function isPlainObject(obj) {
977
- return Object.prototype.toString.call(obj) === "[object Object]";
1231
+
1232
+ // src/arkade/bignum.ts
1233
+ var bignum_exports = {};
1234
+ chunk5BLDMQED_cjs.__export(bignum_exports, {
1235
+ BIGNUM_MAX_BYTES: () => BIGNUM_MAX_BYTES,
1236
+ decode: () => decode,
1237
+ encode: () => encode
1238
+ });
1239
+ var BIGNUM_MAX_BYTES = 520;
1240
+ var codec = btcSigner.ScriptNum(
1241
+ BIGNUM_MAX_BYTES,
1242
+ /* forceMinimal */
1243
+ true
1244
+ );
1245
+ function encode(value) {
1246
+ const result = codec.encode(value);
1247
+ if (result.length > BIGNUM_MAX_BYTES) {
1248
+ throw new Error(`BigNum value exceeds 520 bytes (encoded to ${result.length} bytes)`);
1249
+ }
1250
+ return result;
978
1251
  }
979
- function isNum(num) {
980
- return Number.isSafeInteger(num);
1252
+ function decode(value) {
1253
+ return codec.decode(value);
981
1254
  }
982
- var Bitset = {
983
- BITS: 32,
984
- FULL_MASK: -1 >>> 0,
985
- // 1<<32 will overflow
986
- len: (len) => Math.ceil(len / 32),
987
- create: (len) => new Uint32Array(Bitset.len(len)),
988
- clean: (bs) => bs.fill(0),
989
- debug: (bs) => Array.from(bs).map((i) => (i >>> 0).toString(2).padStart(32, "0")),
990
- checkLen: (bs, len) => {
991
- if (Bitset.len(len) === bs.length)
992
- return;
993
- throw new Error(`wrong length=${bs.length}. Expected: ${Bitset.len(len)}`);
994
- },
995
- chunkLen: (bsLen, pos, len) => {
996
- if (pos < 0)
997
- throw new Error(`wrong pos=${pos}`);
998
- if (pos + len > bsLen)
999
- throw new Error(`wrong range=${pos}/${len} of ${bsLen}`);
1000
- },
1001
- set: (bs, chunk, value, allowRewrite = true) => {
1002
- if (!allowRewrite && (bs[chunk] & value) !== 0)
1003
- return false;
1004
- bs[chunk] |= value;
1005
- return true;
1006
- },
1007
- pos: (pos, i) => ({
1008
- chunk: Math.floor((pos + i) / 32),
1009
- mask: 1 << 32 - (pos + i) % 32 - 1
1010
- }),
1011
- indices: (bs, len, invert = false) => {
1012
- Bitset.checkLen(bs, len);
1013
- const { FULL_MASK, BITS } = Bitset;
1014
- const left = BITS - len % BITS;
1015
- const lastMask = left ? FULL_MASK >>> left << left : FULL_MASK;
1016
- const res = [];
1017
- for (let i = 0; i < bs.length; i++) {
1018
- let c = bs[i];
1019
- if (invert)
1020
- c = ~c;
1021
- if (i === bs.length - 1)
1022
- c &= lastMask;
1023
- if (c === 0)
1255
+
1256
+ // src/arkade/script.ts
1257
+ var ARKADE_OPS = { ...btcSigner.OP, ...ARKADE_OP };
1258
+ var ArkadeOPNames = {};
1259
+ for (const [k, v] of Object.entries(ARKADE_OPS)) {
1260
+ if (typeof v === "number") ArkadeOPNames[v] = k;
1261
+ }
1262
+ var ArkadeScript = wrap({
1263
+ encodeStream: (w, value) => {
1264
+ for (let o of value) {
1265
+ if (typeof o === "string") {
1266
+ const v = ARKADE_OPS[o];
1267
+ if (v === void 0) throw new Error(`Unknown opcode=${o}`);
1268
+ w.byte(v);
1024
1269
  continue;
1025
- for (let j = 0; j < BITS; j++) {
1026
- const m = 1 << BITS - j - 1;
1027
- if (c & m)
1028
- res.push(i * BITS + j);
1270
+ } else if (typeof o === "number" || typeof o === "bigint") {
1271
+ if (o === 0 || o === 0n) {
1272
+ w.byte(0);
1273
+ continue;
1274
+ } else if (o >= 1 && o <= 16) {
1275
+ w.byte(btcSigner.OP.OP_1 - 1 + Number(o));
1276
+ continue;
1277
+ } else if (o === -1 || o === -1n) {
1278
+ w.byte(btcSigner.OP["1NEGATE"]);
1279
+ continue;
1280
+ }
1281
+ const big = typeof o === "number" ? BigInt(o) : o;
1282
+ o = encode(big);
1029
1283
  }
1284
+ if (!(o instanceof Uint8Array)) throw new Error(`Wrong Script OP=${o} (${typeof o})`);
1285
+ const len = o.length;
1286
+ if (len < btcSigner.OP.PUSHDATA1) w.byte(len);
1287
+ else if (len <= 255) {
1288
+ w.byte(btcSigner.OP.PUSHDATA1);
1289
+ w.byte(len);
1290
+ } else if (len <= 65535) {
1291
+ w.byte(btcSigner.OP.PUSHDATA2);
1292
+ w.bytes(U16LE.encode(len));
1293
+ } else {
1294
+ w.byte(btcSigner.OP.PUSHDATA4);
1295
+ w.bytes(U32LE.encode(len));
1296
+ }
1297
+ w.bytes(o);
1030
1298
  }
1031
- return res;
1032
- },
1033
- range: (arr) => {
1034
- const res = [];
1035
- let cur;
1036
- for (const i of arr) {
1037
- if (cur === void 0 || i !== cur.pos + cur.length)
1038
- res.push(cur = { pos: i, length: 1 });
1039
- else
1040
- cur.length += 1;
1041
- }
1042
- return res;
1043
- },
1044
- rangeDebug: (bs, len, invert = false) => `[${Bitset.range(Bitset.indices(bs, len, invert)).map((i) => `(${i.pos}/${i.length})`).join(", ")}]`,
1045
- setRange: (bs, bsLen, pos, len, allowRewrite = true) => {
1046
- Bitset.chunkLen(bsLen, pos, len);
1047
- const { FULL_MASK, BITS } = Bitset;
1048
- const first = pos % BITS ? Math.floor(pos / BITS) : void 0;
1049
- const lastPos = pos + len;
1050
- const last = lastPos % BITS ? Math.floor(lastPos / BITS) : void 0;
1051
- if (first !== void 0 && first === last)
1052
- return Bitset.set(bs, first, FULL_MASK >>> BITS - len << BITS - len - pos, allowRewrite);
1053
- if (first !== void 0) {
1054
- if (!Bitset.set(bs, first, FULL_MASK >>> pos % BITS, allowRewrite))
1055
- return false;
1056
- }
1057
- const start = first !== void 0 ? first + 1 : pos / BITS;
1058
- const end = last !== void 0 ? last : lastPos / BITS;
1059
- for (let i = start; i < end; i++)
1060
- if (!Bitset.set(bs, i, FULL_MASK, allowRewrite))
1061
- return false;
1062
- if (last !== void 0 && first !== last) {
1063
- if (!Bitset.set(bs, last, FULL_MASK << BITS - lastPos % BITS, allowRewrite))
1064
- return false;
1065
- }
1066
- return true;
1067
- }
1068
- };
1069
- var Path = {
1070
- /**
1071
- * Internal method for handling stack of paths (debug, errors, dynamic fields via path)
1072
- * This is looks ugly (callback), but allows us to force stack cleaning by construction (.pop always after function).
1073
- * Also, this makes impossible:
1074
- * - pushing field when stack is empty
1075
- * - pushing field inside of field (real bug)
1076
- * NOTE: we don't want to do '.pop' on error!
1077
- */
1078
- pushObj: (stack, obj, objFn) => {
1079
- const last = { obj };
1080
- stack.push(last);
1081
- objFn((field, fieldFn) => {
1082
- last.field = field;
1083
- fieldFn();
1084
- last.field = void 0;
1085
- });
1086
- stack.pop();
1087
- },
1088
- path: (stack) => {
1089
- const res = [];
1090
- for (const i of stack)
1091
- if (i.field !== void 0)
1092
- res.push(i.field);
1093
- return res.join("/");
1094
- },
1095
- err: (name, stack, msg) => {
1096
- const err = new Error(`${name}(${Path.path(stack)}): ${typeof msg === "string" ? msg : msg.message}`);
1097
- if (msg instanceof Error && msg.stack)
1098
- err.stack = msg.stack;
1099
- return err;
1100
1299
  },
1101
- resolve: (stack, path) => {
1102
- const parts = path.split("/");
1103
- const objPath = stack.map((i2) => i2.obj);
1104
- let i = 0;
1105
- for (; i < parts.length; i++) {
1106
- if (parts[i] === "..")
1107
- objPath.pop();
1108
- else
1109
- break;
1110
- }
1111
- let cur = objPath.pop();
1112
- for (; i < parts.length; i++) {
1113
- if (!cur || cur[parts[i]] === void 0)
1114
- return void 0;
1115
- cur = cur[parts[i]];
1300
+ decodeStream: (r) => {
1301
+ const out = [];
1302
+ while (!r.isEnd()) {
1303
+ const cur = r.byte();
1304
+ if (btcSigner.OP.OP_0 < cur && cur <= btcSigner.OP.PUSHDATA4) {
1305
+ let len;
1306
+ if (cur < btcSigner.OP.PUSHDATA1) len = cur;
1307
+ else if (cur === btcSigner.OP.PUSHDATA1) len = U8.decodeStream(r);
1308
+ else if (cur === btcSigner.OP.PUSHDATA2) len = U16LE.decodeStream(r);
1309
+ else if (cur === btcSigner.OP.PUSHDATA4) len = U32LE.decodeStream(r);
1310
+ else throw new Error("Should be not possible");
1311
+ out.push(r.bytes(len));
1312
+ } else if (cur === 0) {
1313
+ out.push(0);
1314
+ } else if (btcSigner.OP.OP_1 <= cur && cur <= btcSigner.OP.OP_16) {
1315
+ out.push(cur - (btcSigner.OP.OP_1 - 1));
1316
+ } else if (cur === btcSigner.OP["1NEGATE"]) {
1317
+ out.push(-1);
1318
+ } else {
1319
+ const op = ArkadeOPNames[cur];
1320
+ if (op === void 0) throw new Error(`Unknown opcode=${cur.toString(16)}`);
1321
+ out.push(op);
1322
+ }
1116
1323
  }
1117
- return cur;
1118
- }
1119
- };
1120
- var _Reader = class __Reader {
1121
- pos = 0;
1122
- data;
1123
- opts;
1124
- stack;
1125
- parent;
1126
- parentOffset;
1127
- bitBuf = 0;
1128
- bitPos = 0;
1129
- bs;
1130
- // bitset
1131
- view;
1132
- constructor(data, opts = {}, stack = [], parent = void 0, parentOffset = 0) {
1133
- this.data = data;
1134
- this.opts = opts;
1135
- this.stack = stack;
1136
- this.parent = parent;
1137
- this.parentOffset = parentOffset;
1138
- this.view = createView(data);
1139
- }
1140
- /** Internal method for pointers. */
1141
- _enablePointers() {
1142
- if (this.parent)
1143
- return this.parent._enablePointers();
1144
- if (this.bs)
1145
- return;
1146
- this.bs = Bitset.create(this.data.length);
1147
- Bitset.setRange(this.bs, this.data.length, 0, this.pos, this.opts.allowMultipleReads);
1148
- }
1149
- markBytesBS(pos, len) {
1150
- if (this.parent)
1151
- return this.parent.markBytesBS(this.parentOffset + pos, len);
1152
- if (!len)
1153
- return true;
1154
- if (!this.bs)
1155
- return true;
1156
- return Bitset.setRange(this.bs, this.data.length, pos, len, false);
1157
- }
1158
- markBytes(len) {
1159
- const pos = this.pos;
1160
- this.pos += len;
1161
- const res = this.markBytesBS(pos, len);
1162
- if (!this.opts.allowMultipleReads && !res)
1163
- throw this.err(`multiple read pos=${this.pos} len=${len}`);
1164
- return res;
1165
- }
1166
- pushObj(obj, objFn) {
1167
- return Path.pushObj(this.stack, obj, objFn);
1168
- }
1169
- readView(n, fn) {
1170
- if (!Number.isFinite(n))
1171
- throw this.err(`readView: wrong length=${n}`);
1172
- if (this.pos + n > this.data.length)
1173
- throw this.err("readView: Unexpected end of buffer");
1174
- const res = fn(this.view, this.pos);
1175
- this.markBytes(n);
1176
- return res;
1324
+ return out;
1177
1325
  }
1178
- // read bytes by absolute offset
1179
- absBytes(n) {
1180
- if (n > this.data.length)
1181
- throw new Error("Unexpected end of buffer");
1182
- return this.data.subarray(n);
1326
+ });
1327
+ function toASM(script) {
1328
+ const parts = [];
1329
+ for (const op of script) {
1330
+ if (typeof op === "string") {
1331
+ const name = op.startsWith("OP_") ? op : `OP_${op}`;
1332
+ parts.push(name);
1333
+ } else if (typeof op === "number" || typeof op === "bigint") {
1334
+ if (op === 0 || op === 0n) {
1335
+ parts.push("OP_0");
1336
+ } else if (op >= 1 && op <= 16) {
1337
+ parts.push(`OP_${op}`);
1338
+ } else if (op === -1 || op === -1n) {
1339
+ parts.push("OP_1NEGATE");
1340
+ } else {
1341
+ parts.push(base.hex.encode(encode(BigInt(op))));
1342
+ }
1343
+ } else {
1344
+ parts.push(base.hex.encode(op));
1345
+ }
1183
1346
  }
1184
- finish() {
1185
- if (this.opts.allowUnreadBytes)
1186
- return;
1187
- if (this.bitPos) {
1188
- throw this.err(`${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
1347
+ return parts.join(" ");
1348
+ }
1349
+ function fromASM(asm) {
1350
+ const tokens = asm.trim().split(/\s+/).filter(Boolean);
1351
+ const out = [];
1352
+ for (const token of tokens) {
1353
+ if (token === "OP_0" || token === "OP_FALSE") {
1354
+ out.push(0);
1355
+ continue;
1189
1356
  }
1190
- if (this.bs && !this.parent) {
1191
- const notRead = Bitset.indices(this.bs, this.data.length, true);
1192
- if (notRead.length) {
1193
- const formatted = Bitset.range(notRead).map(({ pos, length }) => `(${pos}/${length})[${base.hex.encode(this.data.subarray(pos, pos + length))}]`).join(", ");
1194
- throw this.err(`unread byte ranges: ${formatted} (total=${this.data.length})`);
1195
- } else
1196
- return;
1357
+ if (token === "OP_TRUE") {
1358
+ out.push(1);
1359
+ continue;
1197
1360
  }
1198
- if (!this.isEnd()) {
1199
- throw this.err(`${this.leftBytes} bytes ${this.bitPos} bits left after unpack: ${base.hex.encode(this.data.slice(this.pos))}`);
1361
+ const numMatch = token.match(/^OP_(\d+)$/);
1362
+ if (numMatch) {
1363
+ const n = parseInt(numMatch[1], 10);
1364
+ if (n >= 1 && n <= 16) {
1365
+ out.push(n);
1366
+ continue;
1367
+ }
1200
1368
  }
1369
+ let key;
1370
+ if (token.startsWith("OP_")) {
1371
+ key = token.slice(3);
1372
+ } else {
1373
+ key = token;
1374
+ }
1375
+ if (key in ARKADE_OPS) {
1376
+ out.push(key);
1377
+ continue;
1378
+ }
1379
+ if (/^[0-9a-fA-F]+$/.test(token) && token.length % 2 === 0) {
1380
+ try {
1381
+ out.push(base.hex.decode(token));
1382
+ continue;
1383
+ } catch {
1384
+ }
1385
+ }
1386
+ throw new Error(`Invalid ASM token: ${token}`);
1201
1387
  }
1202
- // User methods
1203
- err(msg) {
1204
- return Path.err("Reader", this.stack, msg);
1205
- }
1206
- offsetReader(n) {
1207
- if (n > this.data.length)
1208
- throw this.err("offsetReader: Unexpected end of buffer");
1209
- return new __Reader(this.absBytes(n), this.opts, this.stack, this, n);
1210
- }
1211
- bytes(n, peek = false) {
1212
- if (this.bitPos)
1213
- throw this.err("readBytes: bitPos not empty");
1214
- if (!Number.isFinite(n))
1215
- throw this.err(`readBytes: wrong length=${n}`);
1216
- if (this.pos + n > this.data.length)
1217
- throw this.err("readBytes: Unexpected end of buffer");
1218
- const slice = this.data.subarray(this.pos, this.pos + n);
1219
- if (!peek)
1220
- this.markBytes(n);
1221
- return slice;
1222
- }
1223
- byte(peek = false) {
1224
- if (this.bitPos)
1225
- throw this.err("readByte: bitPos not empty");
1226
- if (this.pos + 1 > this.data.length)
1227
- throw this.err("readBytes: Unexpected end of buffer");
1228
- const data = this.data[this.pos];
1229
- if (!peek)
1230
- this.markBytes(1);
1231
- return data;
1232
- }
1233
- get leftBytes() {
1234
- return this.data.length - this.pos;
1235
- }
1236
- get totalBytes() {
1237
- return this.data.length;
1238
- }
1239
- isEnd() {
1240
- return this.pos >= this.data.length && !this.bitPos;
1388
+ return out;
1389
+ }
1390
+ function asmToBytes(asm) {
1391
+ return ArkadeScript.encode(fromASM(asm));
1392
+ }
1393
+ function bytesToASM(script) {
1394
+ return toASM(ArkadeScript.decode(script));
1395
+ }
1396
+ var TAG_SCRIPT = "ArkScriptHash";
1397
+ var TAG_WITNESS = "ArkWitnessHash";
1398
+ function arkadeScriptHash(script) {
1399
+ return secp256k1_js.schnorr.utils.taggedHash(TAG_SCRIPT, script);
1400
+ }
1401
+ function arkadeWitnessHash(witness) {
1402
+ if (witness.length === 0) {
1403
+ return new Uint8Array(32);
1241
1404
  }
1242
- // bits are read in BE mode (left to right): (0b1000_0000).readBits(1) == 1
1243
- bits(bits) {
1244
- if (bits > 32)
1245
- throw this.err("BitReader: cannot read more than 32 bits in single call");
1246
- let out = 0;
1247
- while (bits) {
1248
- if (!this.bitPos) {
1249
- this.bitBuf = this.byte();
1250
- this.bitPos = 8;
1405
+ return secp256k1_js.schnorr.utils.taggedHash(TAG_WITNESS, witness);
1406
+ }
1407
+ function computeArkadeScriptPublicKey(pubKey, script) {
1408
+ const hash = arkadeScriptHash(script);
1409
+ const xOnly = pubKey.length === 33 ? pubKey.subarray(1) : pubKey;
1410
+ const point = secp256k1_js.schnorr.utils.lift_x(utils_js.bytesToNumberBE(xOnly));
1411
+ const scalar = utils_js.bytesToNumberBE(hash) % secp256k1_js.secp256k1.Point.CURVE().n;
1412
+ const tweak = secp256k1_js.secp256k1.Point.BASE.multiply(scalar);
1413
+ return secp256k1_js.schnorr.utils.pointToBytes(point.add(tweak));
1414
+ }
1415
+
1416
+ // src/script/vhtlc.ts
1417
+ exports.VHTLC = void 0;
1418
+ ((VHTLC2) => {
1419
+ class BaseScript extends chunk4A4GRSTB_cjs.VtxoScript {
1420
+ options;
1421
+ claimScript;
1422
+ refundScript;
1423
+ refundWithoutReceiverScript;
1424
+ unilateralClaimScript;
1425
+ unilateralRefundScript;
1426
+ unilateralRefundWithoutReceiverScript;
1427
+ nonInteractiveClaimScript;
1428
+ nonInteractiveClaimArkadeScript;
1429
+ nonInteractiveRefundScript;
1430
+ nonInteractiveRefundArkadeScript;
1431
+ constructor(options, preimageCondition) {
1432
+ validateOptions(options);
1433
+ const {
1434
+ sender,
1435
+ receiver,
1436
+ server,
1437
+ preimageHash,
1438
+ refundLocktime,
1439
+ unilateralClaimDelay,
1440
+ unilateralRefundDelay,
1441
+ unilateralRefundWithoutReceiverDelay
1442
+ } = options;
1443
+ const conditionScript = preimageCondition(preimageHash);
1444
+ const claimScript = chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
1445
+ conditionScript,
1446
+ pubkeys: [receiver, server]
1447
+ }).script;
1448
+ const refundScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
1449
+ pubkeys: [sender, receiver, server]
1450
+ }).script;
1451
+ const refundWithoutReceiverScript = chunk4A4GRSTB_cjs.CLTVMultisigTapscript.encode({
1452
+ absoluteTimelock: refundLocktime,
1453
+ pubkeys: [sender, server]
1454
+ }).script;
1455
+ const unilateralClaimScript = chunk4A4GRSTB_cjs.ConditionCSVMultisigTapscript.encode({
1456
+ conditionScript,
1457
+ timelock: unilateralClaimDelay,
1458
+ pubkeys: [receiver]
1459
+ }).script;
1460
+ const unilateralRefundScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
1461
+ timelock: unilateralRefundDelay,
1462
+ pubkeys: [sender, receiver]
1463
+ }).script;
1464
+ const unilateralRefundWithoutReceiverScript = chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({
1465
+ timelock: unilateralRefundWithoutReceiverDelay,
1466
+ pubkeys: [sender]
1467
+ }).script;
1468
+ const scripts = [
1469
+ claimScript,
1470
+ refundScript,
1471
+ refundWithoutReceiverScript,
1472
+ unilateralClaimScript,
1473
+ unilateralRefundScript,
1474
+ unilateralRefundWithoutReceiverScript
1475
+ ];
1476
+ let arkadeScriptNic;
1477
+ let nonInteractiveClaimScript;
1478
+ if (options.nonInteractiveClaim) {
1479
+ arkadeScriptNic = enforcePayTo(options.nonInteractiveClaim.receiverPkScript);
1480
+ nonInteractiveClaimScript = chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
1481
+ conditionScript,
1482
+ pubkeys: [
1483
+ server,
1484
+ computeArkadeScriptPublicKey(
1485
+ options.nonInteractiveClaim.emulatorPubkey,
1486
+ arkadeScriptNic
1487
+ )
1488
+ ]
1489
+ }).script;
1490
+ scripts.push(nonInteractiveClaimScript);
1491
+ }
1492
+ let arkadeScriptNir;
1493
+ let nonInteractiveRefundScript;
1494
+ if (options.nonInteractiveRefund) {
1495
+ arkadeScriptNir = enforcePayTo(options.nonInteractiveRefund.senderPkScript);
1496
+ nonInteractiveRefundScript = chunk4A4GRSTB_cjs.MultisigTapscript.encode({
1497
+ pubkeys: [
1498
+ server,
1499
+ receiver,
1500
+ computeArkadeScriptPublicKey(
1501
+ options.nonInteractiveRefund.emulatorPubkey,
1502
+ arkadeScriptNir
1503
+ )
1504
+ ]
1505
+ }).script;
1506
+ scripts.push(nonInteractiveRefundScript);
1507
+ }
1508
+ super(scripts);
1509
+ this.options = options;
1510
+ this.claimScript = base.hex.encode(claimScript);
1511
+ this.refundScript = base.hex.encode(refundScript);
1512
+ this.refundWithoutReceiverScript = base.hex.encode(refundWithoutReceiverScript);
1513
+ this.unilateralClaimScript = base.hex.encode(unilateralClaimScript);
1514
+ this.unilateralRefundScript = base.hex.encode(unilateralRefundScript);
1515
+ this.unilateralRefundWithoutReceiverScript = base.hex.encode(
1516
+ unilateralRefundWithoutReceiverScript
1517
+ );
1518
+ if (nonInteractiveClaimScript) {
1519
+ this.nonInteractiveClaimScript = base.hex.encode(nonInteractiveClaimScript);
1520
+ this.nonInteractiveClaimArkadeScript = arkadeScriptNic;
1521
+ }
1522
+ if (nonInteractiveRefundScript) {
1523
+ this.nonInteractiveRefundScript = base.hex.encode(nonInteractiveRefundScript);
1524
+ this.nonInteractiveRefundArkadeScript = arkadeScriptNir;
1251
1525
  }
1252
- const take = Math.min(bits, this.bitPos);
1253
- this.bitPos -= take;
1254
- out = out << take | this.bitBuf >> this.bitPos & 2 ** take - 1;
1255
- this.bitBuf &= 2 ** this.bitPos - 1;
1256
- bits -= take;
1257
1526
  }
1258
- return out >>> 0;
1259
- }
1260
- find(needle, pos = this.pos) {
1261
- if (!isBytes(needle))
1262
- throw this.err(`find: needle is not bytes! ${needle}`);
1263
- if (this.bitPos)
1264
- throw this.err("findByte: bitPos not empty");
1265
- if (!needle.length)
1266
- throw this.err(`find: needle is empty`);
1267
- for (let idx = pos; (idx = this.data.indexOf(needle[0], idx)) !== -1; idx++) {
1268
- if (idx === -1)
1269
- return;
1270
- const leftBytes = this.data.length - idx;
1271
- if (leftBytes < needle.length)
1272
- return;
1273
- if (equalBytes(needle, this.data.subarray(idx, idx + needle.length)))
1274
- return idx;
1527
+ /** Return the collaborative claim tapleaf script. */
1528
+ claim() {
1529
+ return this.findLeaf(this.claimScript);
1530
+ }
1531
+ /** Return the collaborative refund tapleaf script. */
1532
+ refund() {
1533
+ return this.findLeaf(this.refundScript);
1534
+ }
1535
+ /** Return the refund-without-receiver tapleaf script. */
1536
+ refundWithoutReceiver() {
1537
+ return this.findLeaf(this.refundWithoutReceiverScript);
1538
+ }
1539
+ /** Return the unilateral claim tapleaf script. */
1540
+ unilateralClaim() {
1541
+ return this.findLeaf(this.unilateralClaimScript);
1542
+ }
1543
+ /** Return the unilateral refund tapleaf script. */
1544
+ unilateralRefund() {
1545
+ return this.findLeaf(this.unilateralRefundScript);
1546
+ }
1547
+ /** Return the unilateral refund-without-receiver tapleaf script. */
1548
+ unilateralRefundWithoutReceiver() {
1549
+ return this.findLeaf(this.unilateralRefundWithoutReceiverScript);
1550
+ }
1551
+ /** Return the non-interactive claim tapleaf script as well as the ArkadeScript. */
1552
+ nonInteractiveClaim() {
1553
+ if (!this.nonInteractiveClaimScript || !this.nonInteractiveClaimArkadeScript) {
1554
+ throw new Error("VHTLC has no non-interactive claim leaf");
1555
+ }
1556
+ return [
1557
+ this.findLeaf(this.nonInteractiveClaimScript),
1558
+ this.nonInteractiveClaimArkadeScript
1559
+ ];
1560
+ }
1561
+ /** Return the non-interactive refund tapleaf script as well as the ArkadeScript. */
1562
+ nonInteractiveRefund() {
1563
+ if (!this.nonInteractiveRefundScript || !this.nonInteractiveRefundArkadeScript) {
1564
+ throw new Error("VHTLC has no non-interactive refund leaf");
1565
+ }
1566
+ return [
1567
+ this.findLeaf(this.nonInteractiveRefundScript),
1568
+ this.nonInteractiveRefundArkadeScript
1569
+ ];
1275
1570
  }
1276
- return;
1277
- }
1278
- };
1279
- var _Writer = class {
1280
- pos = 0;
1281
- stack;
1282
- // We could have a single buffer here and re-alloc it with
1283
- // x1.5-2 size each time it full, but it will be slower:
1284
- // basic/encode bench: 395ns -> 560ns
1285
- buffers = [];
1286
- ptrs = [];
1287
- bitBuf = 0;
1288
- bitPos = 0;
1289
- viewBuf = new Uint8Array(8);
1290
- view;
1291
- finished = false;
1292
- constructor(stack = []) {
1293
- this.stack = stack;
1294
- this.view = createView(this.viewBuf);
1295
- }
1296
- pushObj(obj, objFn) {
1297
- return Path.pushObj(this.stack, obj, objFn);
1298
- }
1299
- writeView(len, fn) {
1300
- if (this.finished)
1301
- throw this.err("buffer: finished");
1302
- if (!isNum(len) || len > 8)
1303
- throw new Error(`wrong writeView length=${len}`);
1304
- fn(this.view);
1305
- this.bytes(this.viewBuf.slice(0, len));
1306
- this.viewBuf.fill(0);
1307
1571
  }
1308
- // User methods
1309
- err(msg) {
1310
- if (this.finished)
1311
- throw this.err("buffer: finished");
1312
- return Path.err("Reader", this.stack, msg);
1572
+ class Script3 extends BaseScript {
1573
+ constructor(options) {
1574
+ super(options, preimageConditionScript);
1575
+ }
1313
1576
  }
1314
- bytes(b) {
1315
- if (this.finished)
1316
- throw this.err("buffer: finished");
1317
- if (this.bitPos)
1318
- throw this.err("writeBytes: ends with non-empty bit buffer");
1319
- this.buffers.push(b);
1320
- this.pos += b.length;
1577
+ VHTLC2.Script = Script3;
1578
+ class ScriptV2 extends BaseScript {
1579
+ constructor(options) {
1580
+ super(options, preimageConditionScriptV2);
1581
+ }
1321
1582
  }
1322
- byte(b) {
1323
- if (this.finished)
1324
- throw this.err("buffer: finished");
1325
- if (this.bitPos)
1326
- throw this.err("writeByte: ends with non-empty bit buffer");
1327
- this.buffers.push(new Uint8Array([b]));
1328
- this.pos++;
1583
+ VHTLC2.ScriptV2 = ScriptV2;
1584
+ function validateOptions(options) {
1585
+ const {
1586
+ sender,
1587
+ receiver,
1588
+ server,
1589
+ preimageHash,
1590
+ refundLocktime,
1591
+ unilateralClaimDelay,
1592
+ unilateralRefundDelay,
1593
+ unilateralRefundWithoutReceiverDelay
1594
+ } = options;
1595
+ if (!preimageHash || preimageHash.length !== 20) {
1596
+ throw new Error("preimage hash must be 20 bytes");
1597
+ }
1598
+ if (options.nonInteractiveClaim) {
1599
+ const { emulatorPubkey, receiverPkScript } = options.nonInteractiveClaim;
1600
+ if (!emulatorPubkey || emulatorPubkey.length !== 32 && emulatorPubkey.length !== 33) {
1601
+ throw new Error("Invalid public key length (emulator)");
1602
+ }
1603
+ if (!receiverPkScript || !isP2trPkScript(receiverPkScript)) {
1604
+ throw new Error("Invalid P2TR script");
1605
+ }
1606
+ }
1607
+ if (options.nonInteractiveRefund) {
1608
+ const { emulatorPubkey, senderPkScript } = options.nonInteractiveRefund;
1609
+ if (!emulatorPubkey || emulatorPubkey.length !== 32 && emulatorPubkey.length !== 33) {
1610
+ throw new Error("Invalid public key length (emulator)");
1611
+ }
1612
+ if (!senderPkScript || !isP2trPkScript(senderPkScript)) {
1613
+ throw new Error("Invalid P2TR script");
1614
+ }
1615
+ }
1616
+ if (!receiver || receiver.length !== 32) {
1617
+ throw new Error("Invalid public key length (receiver)");
1618
+ }
1619
+ if (!sender || sender.length !== 32) {
1620
+ throw new Error("Invalid public key length (sender)");
1621
+ }
1622
+ if (!server || server.length !== 32) {
1623
+ throw new Error("Invalid public key length (server)");
1624
+ }
1625
+ if (typeof refundLocktime !== "bigint" || refundLocktime <= 0n) {
1626
+ throw new Error("refund locktime must be greater than 0");
1627
+ }
1628
+ if (!unilateralClaimDelay || typeof unilateralClaimDelay.value !== "bigint" || unilateralClaimDelay.value <= 0n) {
1629
+ throw new Error("unilateral claim delay must greater than 0");
1630
+ }
1631
+ if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value % 512n !== 0n) {
1632
+ throw new Error("seconds timelock must be multiple of 512");
1633
+ }
1634
+ if (unilateralClaimDelay.type === "seconds" && unilateralClaimDelay.value < 512n) {
1635
+ throw new Error("seconds timelock must be greater or equal to 512");
1636
+ }
1637
+ if (!unilateralRefundDelay || typeof unilateralRefundDelay.value !== "bigint" || unilateralRefundDelay.value <= 0n) {
1638
+ throw new Error("unilateral refund delay must greater than 0");
1639
+ }
1640
+ if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value % 512n !== 0n) {
1641
+ throw new Error("seconds timelock must be multiple of 512");
1642
+ }
1643
+ if (unilateralRefundDelay.type === "seconds" && unilateralRefundDelay.value < 512n) {
1644
+ throw new Error("seconds timelock must be greater or equal to 512");
1645
+ }
1646
+ if (!unilateralRefundWithoutReceiverDelay || typeof unilateralRefundWithoutReceiverDelay.value !== "bigint" || unilateralRefundWithoutReceiverDelay.value <= 0n) {
1647
+ throw new Error("unilateral refund without receiver delay must greater than 0");
1648
+ }
1649
+ if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value % 512n !== 0n) {
1650
+ throw new Error("seconds timelock must be multiple of 512");
1651
+ }
1652
+ if (unilateralRefundWithoutReceiverDelay.type === "seconds" && unilateralRefundWithoutReceiverDelay.value < 512n) {
1653
+ throw new Error("seconds timelock must be greater or equal to 512");
1654
+ }
1329
1655
  }
1330
- finish(clean = true) {
1331
- if (this.finished)
1332
- throw this.err("buffer: finished");
1333
- if (this.bitPos)
1334
- throw this.err("buffer: ends with non-empty bit buffer");
1335
- const buffers = this.buffers.concat(this.ptrs.map((i) => i.buffer));
1336
- const sum = buffers.map((b) => b.length).reduce((a, b) => a + b, 0);
1337
- const buf = new Uint8Array(sum);
1338
- for (let i = 0, pad = 0; i < buffers.length; i++) {
1339
- const a = buffers[i];
1340
- buf.set(a, pad);
1341
- pad += a.length;
1656
+ })(exports.VHTLC || (exports.VHTLC = {}));
1657
+ function preimageConditionScript(preimageHash) {
1658
+ return btcSigner.Script.encode(["HASH160", preimageHash, "EQUAL"]);
1659
+ }
1660
+ function preimageConditionScriptV2(preimageHash) {
1661
+ return btcSigner.Script.encode(["SIZE", 32, "EQUALVERIFY", "HASH160", preimageHash, "EQUAL"]);
1662
+ }
1663
+ function isP2trPkScript(pkScript) {
1664
+ return pkScript.length === 34 && pkScript[0] === 81 && pkScript[1] === 32;
1665
+ }
1666
+ function enforcePayTo(destinationPkScript) {
1667
+ if (!isP2trPkScript(destinationPkScript)) {
1668
+ throw new Error("invalid P2TR script");
1669
+ }
1670
+ return ArkadeScript.encode([
1671
+ "PUSHCURRENTINPUTINDEX",
1672
+ "DUP",
1673
+ "INSPECTOUTPUTSCRIPTPUBKEY",
1674
+ 1,
1675
+ "EQUALVERIFY",
1676
+ destinationPkScript.subarray(2),
1677
+ "EQUALVERIFY",
1678
+ "INSPECTOUTPUTVALUE",
1679
+ "PUSHCURRENTINPUTINDEX",
1680
+ "INSPECTINPUTVALUE",
1681
+ "GREATERTHANOREQUAL"
1682
+ ]);
1683
+ }
1684
+
1685
+ // src/contracts/handlers/vhtlc.ts
1686
+ var VHTLCContractHandler = {
1687
+ type: "vhtlc",
1688
+ createScript(params) {
1689
+ const typed = this.deserializeParams(params);
1690
+ return new exports.VHTLC.Script(typed);
1691
+ },
1692
+ serializeParams(params) {
1693
+ return {
1694
+ sender: base.hex.encode(params.sender),
1695
+ receiver: base.hex.encode(params.receiver),
1696
+ server: base.hex.encode(params.server),
1697
+ hash: base.hex.encode(params.preimageHash),
1698
+ refundLocktime: params.refundLocktime.toString(),
1699
+ claimDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
1700
+ refundDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
1701
+ refundNoReceiverDelay: chunk4A4GRSTB_cjs.timelockToSequence(
1702
+ params.unilateralRefundWithoutReceiverDelay
1703
+ ).toString()
1704
+ };
1705
+ },
1706
+ deserializeParams(params) {
1707
+ return {
1708
+ sender: base.hex.decode(params.sender),
1709
+ receiver: base.hex.decode(params.receiver),
1710
+ server: base.hex.decode(params.server),
1711
+ preimageHash: base.hex.decode(params.hash),
1712
+ refundLocktime: BigInt(params.refundLocktime),
1713
+ unilateralClaimDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.claimDelay)),
1714
+ unilateralRefundDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.refundDelay)),
1715
+ unilateralRefundWithoutReceiverDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(
1716
+ Number(params.refundNoReceiverDelay)
1717
+ )
1718
+ };
1719
+ },
1720
+ /**
1721
+ * Select spending path based on context.
1722
+ *
1723
+ * Role is determined from `context.role` or by matching
1724
+ * `context.walletDescriptor` (preferred) / `context.walletPubKey`
1725
+ * against sender/receiver in contract params.
1726
+ */
1727
+ selectPath(script, contract, context) {
1728
+ const role = resolveRole(contract, context);
1729
+ const preimage = contract.params?.preimage;
1730
+ const refundLocktime = BigInt(contract.params.refundLocktime);
1731
+ if (!role) {
1732
+ return null;
1342
1733
  }
1343
- for (let pos = this.pos, i = 0; i < this.ptrs.length; i++) {
1344
- const ptr = this.ptrs[i];
1345
- buf.set(ptr.ptr.encode(pos), ptr.pos);
1346
- pos += ptr.buffer.length;
1734
+ if (context.collaborative) {
1735
+ if (role === "receiver" && preimage) {
1736
+ return {
1737
+ leaf: script.claim(),
1738
+ extraWitness: [base.hex.decode(preimage)]
1739
+ };
1740
+ }
1741
+ if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
1742
+ return {
1743
+ leaf: script.refundWithoutReceiver()
1744
+ };
1745
+ }
1746
+ return null;
1347
1747
  }
1348
- if (clean) {
1349
- this.buffers = [];
1350
- for (const p of this.ptrs)
1351
- p.buffer.fill(0);
1352
- this.ptrs = [];
1353
- this.finished = true;
1354
- this.bitBuf = 0;
1748
+ if (role === "receiver" && preimage) {
1749
+ const sequence = Number(contract.params.claimDelay);
1750
+ if (!isCsvSpendable(context, sequence)) return null;
1751
+ return {
1752
+ leaf: script.unilateralClaim(),
1753
+ extraWitness: [base.hex.decode(preimage)],
1754
+ sequence
1755
+ };
1355
1756
  }
1356
- return buf;
1357
- }
1358
- bits(value, bits) {
1359
- if (bits > 32)
1360
- throw this.err("writeBits: cannot write more than 32 bits in single call");
1361
- if (value >= 2 ** bits)
1362
- throw this.err(`writeBits: value (${value}) >= 2**bits (${bits})`);
1363
- while (bits) {
1364
- const take = Math.min(bits, 8 - this.bitPos);
1365
- this.bitBuf = this.bitBuf << take | value >> bits - take;
1366
- this.bitPos += take;
1367
- bits -= take;
1368
- value &= 2 ** bits - 1;
1369
- if (this.bitPos === 8) {
1370
- this.bitPos = 0;
1371
- this.buffers.push(new Uint8Array([this.bitBuf]));
1372
- this.pos++;
1373
- }
1757
+ if (role === "sender") {
1758
+ const sequence = Number(contract.params.refundNoReceiverDelay);
1759
+ if (!isCsvSpendable(context, sequence)) return null;
1760
+ return {
1761
+ leaf: script.unilateralRefundWithoutReceiver(),
1762
+ sequence
1763
+ };
1374
1764
  }
1375
- }
1376
- };
1377
- function _wrap(inner) {
1378
- return {
1379
- // NOTE: we cannot export validate here, since it is likely mistake.
1380
- encodeStream: inner.encodeStream,
1381
- decodeStream: inner.decodeStream,
1382
- size: inner.size,
1383
- encode: (value) => {
1384
- const w = new _Writer();
1385
- inner.encodeStream(w, value);
1386
- return w.finish();
1387
- },
1388
- decode: (data, opts = {}) => {
1389
- const r = new _Reader(data, opts);
1390
- const res = inner.decodeStream(r);
1391
- r.finish();
1392
- return res;
1765
+ return null;
1766
+ },
1767
+ /**
1768
+ * Get all possible spending paths (no timelock checks).
1769
+ *
1770
+ * Role is determined from `context.role` or by matching
1771
+ * `context.walletDescriptor` (preferred) / `context.walletPubKey`
1772
+ * against sender/receiver in contract params.
1773
+ */
1774
+ getAllSpendingPaths(script, contract, context) {
1775
+ const role = resolveRole(contract, context);
1776
+ const paths = [];
1777
+ if (!role) {
1778
+ return paths;
1393
1779
  }
1394
- };
1395
- }
1396
- function validate(inner, fn) {
1397
- if (!isCoder(inner))
1398
- throw new Error(`validate: invalid inner value ${inner}`);
1399
- if (typeof fn !== "function")
1400
- throw new Error("validate: fn should be function");
1401
- return _wrap({
1402
- size: inner.size,
1403
- encodeStream: (w, value) => {
1404
- let res;
1405
- try {
1406
- res = fn(value);
1407
- } catch (e) {
1408
- throw w.err(e);
1780
+ const preimage = contract.params?.preimage;
1781
+ if (context.collaborative) {
1782
+ if (role === "receiver" && preimage) {
1783
+ paths.push({
1784
+ leaf: script.claim(),
1785
+ extraWitness: [base.hex.decode(preimage)]
1786
+ });
1409
1787
  }
1410
- inner.encodeStream(w, res);
1411
- },
1412
- decodeStream: (r) => {
1413
- const res = inner.decodeStream(r);
1414
- try {
1415
- return fn(res);
1416
- } catch (e) {
1417
- throw r.err(e);
1788
+ if (role === "sender") {
1789
+ paths.push({
1790
+ leaf: script.refundWithoutReceiver()
1791
+ });
1792
+ }
1793
+ } else {
1794
+ if (role === "receiver" && preimage) {
1795
+ const sequence = Number(contract.params.claimDelay);
1796
+ paths.push({
1797
+ leaf: script.unilateralClaim(),
1798
+ extraWitness: [base.hex.decode(preimage)],
1799
+ sequence
1800
+ });
1801
+ }
1802
+ if (role === "sender") {
1803
+ const sequence = Number(contract.params.refundNoReceiverDelay);
1804
+ paths.push({
1805
+ leaf: script.unilateralRefundWithoutReceiver(),
1806
+ sequence
1807
+ });
1418
1808
  }
1419
1809
  }
1420
- });
1421
- }
1422
- var wrap = (inner) => {
1423
- const res = _wrap(inner);
1424
- return inner.validate ? validate(res, inner.validate) : res;
1425
- };
1426
- var isBaseCoder = (elm) => isPlainObject(elm) && typeof elm.decode === "function" && typeof elm.encode === "function";
1427
- function isCoder(elm) {
1428
- return isPlainObject(elm) && isBaseCoder(elm) && typeof elm.encodeStream === "function" && typeof elm.decodeStream === "function" && (elm.size === void 0 || isNum(elm.size));
1429
- }
1430
- var view = (len, opts) => wrap({
1431
- size: len,
1432
- encodeStream: (w, value) => w.writeView(len, (view2) => opts.write(view2, value)),
1433
- decodeStream: (r) => r.readView(len, opts.read),
1434
- validate: (value) => {
1435
- if (typeof value !== "number")
1436
- throw new Error(`viewCoder: expected number, got ${typeof value}`);
1437
- if (opts.validate)
1438
- opts.validate(value);
1439
- return value;
1440
- }
1441
- });
1442
- var intView = (len, signed, opts) => {
1443
- const bits = len * 8;
1444
- const maxVal = 2 ** bits;
1445
- const validateUnsigned = (value) => {
1446
- if (!isNum(value))
1447
- throw new Error(`uintView: value is not safe integer: ${value}`);
1448
- if (0 > value || value >= maxVal) {
1449
- throw new Error(`uintView: value out of bounds. Expected 0 <= ${value} < ${maxVal}`);
1810
+ return paths;
1811
+ },
1812
+ getSpendablePaths(script, contract, context) {
1813
+ const role = resolveRole(contract, context);
1814
+ const paths = [];
1815
+ if (!role) {
1816
+ return paths;
1450
1817
  }
1451
- };
1452
- return view(len, {
1453
- write: opts.write,
1454
- read: opts.read,
1455
- validate: validateUnsigned
1456
- });
1457
- };
1458
- var U32LE = /* @__PURE__ */ intView(4, false, {
1459
- read: (view2, pos) => view2.getUint32(pos, true),
1460
- write: (view2, value) => view2.setUint32(0, value, true)
1461
- });
1462
- var U16LE = /* @__PURE__ */ intView(2, false, {
1463
- read: (view2, pos) => view2.getUint16(pos, true),
1464
- write: (view2, value) => view2.setUint16(0, value, true)
1465
- });
1466
- var U8 = /* @__PURE__ */ intView(1, false, {
1467
- read: (view2, pos) => view2.getUint8(pos),
1468
- write: (view2, value) => view2.setUint8(0, value)
1469
- });
1470
- var ARKADE_OP = {
1471
- // Merkle Branch Verification (0xb3 — repurposed NOP4 slot)
1472
- MERKLEBRANCHVERIFY: 179,
1473
- // Digest (0xc3)
1474
- DIGEST: 195,
1475
- // SHA256 Streaming (0xc4-0xc6)
1476
- SHA256INITIALIZE: 196,
1477
- SHA256UPDATE: 197,
1478
- SHA256FINALIZE: 198,
1479
- // Input Introspection (0xc7-0xcb)
1480
- INSPECTINPUTOUTPOINT: 199,
1481
- INSPECTINPUTARKADESCRIPTHASH: 200,
1482
- INSPECTINPUTVALUE: 201,
1483
- INSPECTINPUTSCRIPTPUBKEY: 202,
1484
- INSPECTINPUTSEQUENCE: 203,
1485
- // Signatures (0xcc-0xcd)
1486
- CHECKSIGFROMSTACK: 204,
1487
- PUSHCURRENTINPUTINDEX: 205,
1488
- // Input Arkade Witness Introspection (0xce)
1489
- INSPECTINPUTARKADEWITNESSHASH: 206,
1490
- // Output Introspection (0xcf, 0xd1)
1491
- INSPECTOUTPUTVALUE: 207,
1492
- INSPECTOUTPUTSCRIPTPUBKEY: 209,
1493
- // Transaction Introspection (0xd2-0xd6)
1494
- INSPECTVERSION: 210,
1495
- INSPECTLOCKTIME: 211,
1496
- INSPECTNUMINPUTS: 212,
1497
- INSPECTNUMOUTPUTS: 213,
1498
- TXWEIGHT: 214,
1499
- // Byte/Number Conversion & Arithmetic (0xd7-0xda)
1500
- NUM2BIN: 215,
1501
- BIN2NUM: 216,
1502
- REVERSEBYTES: 217,
1503
- MODEXP: 218,
1504
- // 0xdb-0xdf are unassigned (OP_UNKNOWN219-223)
1505
- // EC Operations (0xe0-0xe2)
1506
- ECADD: 224,
1507
- ECMUL: 225,
1508
- ECPAIRING: 226,
1509
- // EC Operations (0xe3-0xe4)
1510
- ECMULSCALARVERIFY: 227,
1511
- TWEAKVERIFY: 228,
1512
- // Asset Groups (0xe5-0xf2)
1513
- INSPECTNUMASSETGROUPS: 229,
1514
- INSPECTASSETGROUPASSETID: 230,
1515
- INSPECTASSETGROUPCTRL: 231,
1516
- FINDASSETGROUPBYASSETID: 232,
1517
- INSPECTASSETGROUPMETADATAHASH: 233,
1518
- INSPECTASSETGROUPNUM: 234,
1519
- INSPECTASSETGROUP: 235,
1520
- INSPECTASSETGROUPSUM: 236,
1521
- INSPECTOUTASSETCOUNT: 237,
1522
- INSPECTOUTASSETAT: 238,
1523
- INSPECTOUTASSETLOOKUP: 239,
1524
- INSPECTINASSETCOUNT: 240,
1525
- INSPECTINASSETAT: 241,
1526
- INSPECTINASSETLOOKUP: 242,
1527
- // Transaction ID (0xf3)
1528
- TXID: 243,
1529
- // Packet Introspection (0xf4-0xf5) — added in emulator v0.0.1
1530
- INSPECTPACKET: 244,
1531
- INSPECTINPUTPACKET: 245,
1532
- // Signature Hash (0xf6)
1533
- SIGHASH: 246
1534
- };
1535
- var ARKADE_OPCODES = Object.values(ARKADE_OP);
1536
- var ARKADE_OPCODE_NAMES = Object.fromEntries(
1537
- Object.entries(ARKADE_OP).map(([name, value]) => [value, name])
1538
- );
1539
- var ARKADE_OPCODE_VALUES = Object.fromEntries(
1540
- Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [name, Number(value)])
1541
- );
1542
- function buildBitcoinOpcodeNames() {
1543
- const names = {};
1544
- for (const [key, value] of Object.entries(btcSigner.OP)) {
1545
- if (typeof value === "number") {
1546
- const name = key.startsWith("OP_") ? key : `OP_${key}`;
1547
- names[value] = name;
1818
+ const preimage = contract.params?.preimage;
1819
+ const refundLocktime = BigInt(contract.params.refundLocktime);
1820
+ if (context.collaborative) {
1821
+ if (role === "receiver" && preimage) {
1822
+ paths.push({
1823
+ leaf: script.claim(),
1824
+ extraWitness: [base.hex.decode(preimage)]
1825
+ });
1826
+ }
1827
+ if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
1828
+ paths.push({
1829
+ leaf: script.refundWithoutReceiver()
1830
+ });
1831
+ }
1832
+ return paths;
1548
1833
  }
1549
- }
1550
- names[0] = "OP_0";
1551
- return names;
1552
- }
1553
- function buildBitcoinOpcodeValues() {
1554
- const values = {};
1555
- for (const [key, value] of Object.entries(btcSigner.OP)) {
1556
- if (typeof value === "number") {
1557
- const name = key.startsWith("OP_") ? key : `OP_${key}`;
1558
- values[name] = value;
1559
- values[key] = value;
1834
+ if (role === "receiver" && preimage) {
1835
+ const sequence = Number(contract.params.claimDelay);
1836
+ if (isCsvSpendable(context, sequence)) {
1837
+ paths.push({
1838
+ leaf: script.unilateralClaim(),
1839
+ extraWitness: [base.hex.decode(preimage)],
1840
+ sequence
1841
+ });
1842
+ }
1560
1843
  }
1561
- }
1562
- return values;
1563
- }
1564
- var BITCOIN_OPCODE_NAMES = buildBitcoinOpcodeNames();
1565
- var BITCOIN_OPCODE_VALUES = buildBitcoinOpcodeValues();
1566
- var OPCODE_NAMES = {
1567
- ...BITCOIN_OPCODE_NAMES,
1568
- // Add Arkade opcodes with OP_ prefix
1569
- ...Object.fromEntries(
1570
- Object.entries(ARKADE_OPCODE_NAMES).map(([value, name]) => [Number(value), `OP_${name}`])
1571
- )
1572
- };
1573
- var OPCODE_VALUES = {
1574
- ...BITCOIN_OPCODE_VALUES,
1575
- // Add Arkade opcodes with and without OP_ prefix
1576
- ...ARKADE_OPCODE_VALUES,
1577
- ...Object.fromEntries(
1578
- Object.entries(ARKADE_OPCODE_VALUES).map(([name, value]) => [`OP_${name}`, value])
1579
- )
1580
- };
1581
- function getOpcodeName(value) {
1582
- if (value >= 1 && value <= 75) {
1583
- return `OP_DATA_${value}`;
1584
- }
1585
- return OPCODE_NAMES[value];
1586
- }
1587
- function getOpcodeValue(name) {
1588
- const dataMatch = name.match(/^OP_DATA_(\d+)$/);
1589
- if (dataMatch) {
1590
- const n = parseInt(dataMatch[1], 10);
1591
- if (n >= 1 && n <= 75) {
1592
- return n;
1844
+ if (role === "sender") {
1845
+ const sequence = Number(contract.params.refundNoReceiverDelay);
1846
+ if (isCsvSpendable(context, sequence)) {
1847
+ paths.push({
1848
+ leaf: script.unilateralRefundWithoutReceiver(),
1849
+ sequence
1850
+ });
1851
+ }
1593
1852
  }
1594
- return void 0;
1853
+ return paths;
1854
+ },
1855
+ /**
1856
+ * Refuse an explicit spend of a lockup whose refund path has not opened.
1857
+ * Shared with the v2 handler so the two cannot drift.
1858
+ * @see assertVhtlcSpendableNow for why this is the one certain case.
1859
+ */
1860
+ assertSpendableNow(_script, contract, context) {
1861
+ assertVhtlcSpendableNow(contract, context);
1862
+ },
1863
+ /**
1864
+ * Never. A live VHTLC is escrow: the counterparty's claim leaf is armed,
1865
+ * and generic selection — send, settle, renewal, offboard — would move the
1866
+ * lockup out from under a swap the counterparty is still entitled to
1867
+ * complete, or race a claim already in flight.
1868
+ *
1869
+ * Two narrow reasons, and neither is "spending this would destroy the
1870
+ * VTXO". It would not: the leaf {@link deriveTapscripts} stamps is
1871
+ * `refundWithoutReceiver` — `CLTV[sender, server]`, the sender's OWN
1872
+ * refund, which the server rejects until `refundLocktime` matures. The
1873
+ * reasons are that an escrowed lockup must not count toward the
1874
+ * `available` balance bucket (`computeOffchainBalance` credits `available`
1875
+ * only where this gate is open), and that generic RENEWAL must not
1876
+ * silently execute a refund the caller never asked for —
1877
+ * `runPeriodicSettle` selects through `getSpendableVtxos`, which this gate
1878
+ * filters, and it runs unprompted whenever a wallet is built without an
1879
+ * explicit `settlementConfig`.
1880
+ *
1881
+ * **Inseparable from {@link deriveTapscripts}.** Until that method existed
1882
+ * a `vhtlc` row's VTXOs could not be annotated at all, so they never
1883
+ * reached this gate and the answer here was unobservable — the previous
1884
+ * `true` and the missing derivation were safe only by cancelling out.
1885
+ * Deriving the leaf without closing the gate would hand generic renewal an
1886
+ * escrow it had never been offered before. (The earlier `true` was
1887
+ * justified as preserving selection of in-flight swaps; no such selection
1888
+ * was ever reachable, for that same annotation reason.)
1889
+ */
1890
+ isGenericallySpendable: () => false,
1891
+ /**
1892
+ * The annotation leaf stamped onto every VTXO locked to this contract.
1893
+ *
1894
+ * **Without this the contract is unusable, not merely unoptimized.**
1895
+ * `deriveContractTapscripts` falls back to `script.forfeit()` for any
1896
+ * handler that does not implement {@link TapscriptDeriving}, and no VHTLC
1897
+ * script version has a `forfeit()` — `VtxoScript` does not define one and
1898
+ * `VHTLC.BaseScript` does not add one. The fallback therefore threw
1899
+ * `legacy.forfeit is not a function`, `annotatableIn` caught it, and
1900
+ * `fetchContractVtxosBulk` dropped this contract's VTXOs before they were
1901
+ * persisted. The row existed and was watched while its balance stayed
1902
+ * permanently invisible, and `getSyncState()` reported `degraded` forever.
1903
+ *
1904
+ * `refundWithoutReceiver` is the leaf, for both roles the annotation
1905
+ * serves, because it is the only collaborative path this wallet can
1906
+ * actually satisfy as the sender — the same conclusion `selectPath`
1907
+ * reaches, kept in step with it deliberately. The claim leaves belong to
1908
+ * the receiver and need a preimage the sender does not have; `refund` and
1909
+ * `unilateralRefund` both need the counterparty's signature, which this
1910
+ * protocol has no message to ask for. V1 has no covenant leaves at all.
1911
+ *
1912
+ * **It carries a CLTV, and callers must respect it.** A settlement built
1913
+ * on this leaf is only valid once `refundLocktime` has matured, so a
1914
+ * recovery round that sweeps this VTXO early is rejected by the server.
1915
+ * Nothing in this handler can enforce that, because the annotation is
1916
+ * derived per contract and knows no clock.
1917
+ */
1918
+ deriveTapscripts(script) {
1919
+ const leaf = script.refundWithoutReceiver();
1920
+ return {
1921
+ forfeitTapLeafScript: leaf,
1922
+ intentTapLeafScript: leaf,
1923
+ tapTree: script.encode()
1924
+ };
1595
1925
  }
1596
- return OPCODE_VALUES[name];
1597
- }
1598
-
1599
- // src/arkade/bignum.ts
1600
- var bignum_exports = {};
1601
- chunk5BLDMQED_cjs.__export(bignum_exports, {
1602
- BIGNUM_MAX_BYTES: () => BIGNUM_MAX_BYTES,
1603
- decode: () => decode,
1604
- encode: () => encode
1605
- });
1606
- var BIGNUM_MAX_BYTES = 520;
1607
- var codec = btcSigner.ScriptNum(
1608
- BIGNUM_MAX_BYTES,
1609
- /* forceMinimal */
1610
- true
1611
- );
1612
- function encode(value) {
1613
- const result = codec.encode(value);
1614
- if (result.length > BIGNUM_MAX_BYTES) {
1615
- throw new Error(`BigNum value exceeds 520 bytes (encoded to ${result.length} bytes)`);
1926
+ };
1927
+ function decodeCovenantLeaf(params, destinationKey, emulatorKey, label) {
1928
+ const destination = params[destinationKey];
1929
+ const emulator = params[emulatorKey];
1930
+ if (!destination && !emulator) return void 0;
1931
+ if (!destination || !emulator) {
1932
+ throw new Error(`${label} needs both '${destinationKey}' and '${emulatorKey}', or neither`);
1616
1933
  }
1617
- return result;
1618
- }
1619
- function decode(value) {
1620
- return codec.decode(value);
1934
+ return { destination: base.hex.decode(destination), emulatorPubkey: base.hex.decode(emulator) };
1621
1935
  }
1622
-
1623
- // src/arkade/script.ts
1624
- var ARKADE_OPS = { ...btcSigner.OP, ...ARKADE_OP };
1625
- var ArkadeOPNames = {};
1626
- for (const [k, v] of Object.entries(ARKADE_OPS)) {
1627
- if (typeof v === "number") ArkadeOPNames[v] = k;
1628
- }
1629
- var ArkadeScript = wrap({
1630
- encodeStream: (w, value) => {
1631
- for (let o of value) {
1632
- if (typeof o === "string") {
1633
- const v = ARKADE_OPS[o];
1634
- if (v === void 0) throw new Error(`Unknown opcode=${o}`);
1635
- w.byte(v);
1636
- continue;
1637
- } else if (typeof o === "number" || typeof o === "bigint") {
1638
- if (o === 0 || o === 0n) {
1639
- w.byte(0);
1640
- continue;
1641
- } else if (o >= 1 && o <= 16) {
1642
- w.byte(btcSigner.OP.OP_1 - 1 + Number(o));
1643
- continue;
1644
- } else if (o === -1 || o === -1n) {
1645
- w.byte(btcSigner.OP["1NEGATE"]);
1646
- continue;
1647
- }
1648
- const big = typeof o === "number" ? BigInt(o) : o;
1649
- o = encode(big);
1936
+ var VHTLCV2ContractHandler = {
1937
+ type: "vhtlc-v2",
1938
+ createScript(params) {
1939
+ const typed = this.deserializeParams(params);
1940
+ return new exports.VHTLC.ScriptV2(typed);
1941
+ },
1942
+ serializeParams(params) {
1943
+ return {
1944
+ sender: base.hex.encode(params.sender),
1945
+ receiver: base.hex.encode(params.receiver),
1946
+ server: base.hex.encode(params.server),
1947
+ hash: base.hex.encode(params.preimageHash),
1948
+ refundLocktime: params.refundLocktime.toString(),
1949
+ claimDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralClaimDelay).toString(),
1950
+ refundDelay: chunk4A4GRSTB_cjs.timelockToSequence(params.unilateralRefundDelay).toString(),
1951
+ refundNoReceiverDelay: chunk4A4GRSTB_cjs.timelockToSequence(
1952
+ params.unilateralRefundWithoutReceiverDelay
1953
+ ).toString(),
1954
+ // Spread rather than written as `undefined`: a `Record<string,
1955
+ // string>` round-tripped through a repository must not gain keys
1956
+ // whose value is the string "undefined".
1957
+ ...params.nonInteractiveClaim && {
1958
+ nonInteractiveClaimReceiverPkScript: base.hex.encode(
1959
+ params.nonInteractiveClaim.receiverPkScript
1960
+ ),
1961
+ nonInteractiveClaimEmulatorPubkey: base.hex.encode(
1962
+ params.nonInteractiveClaim.emulatorPubkey
1963
+ )
1964
+ },
1965
+ ...params.nonInteractiveRefund && {
1966
+ nonInteractiveRefundSenderPkScript: base.hex.encode(
1967
+ params.nonInteractiveRefund.senderPkScript
1968
+ ),
1969
+ nonInteractiveRefundEmulatorPubkey: base.hex.encode(
1970
+ params.nonInteractiveRefund.emulatorPubkey
1971
+ )
1650
1972
  }
1651
- if (!(o instanceof Uint8Array)) throw new Error(`Wrong Script OP=${o} (${typeof o})`);
1652
- const len = o.length;
1653
- if (len < btcSigner.OP.PUSHDATA1) w.byte(len);
1654
- else if (len <= 255) {
1655
- w.byte(btcSigner.OP.PUSHDATA1);
1656
- w.byte(len);
1657
- } else if (len <= 65535) {
1658
- w.byte(btcSigner.OP.PUSHDATA2);
1659
- w.bytes(U16LE.encode(len));
1660
- } else {
1661
- w.byte(btcSigner.OP.PUSHDATA4);
1662
- w.bytes(U32LE.encode(len));
1973
+ };
1974
+ },
1975
+ deserializeParams(params) {
1976
+ const claim = decodeCovenantLeaf(
1977
+ params,
1978
+ "nonInteractiveClaimReceiverPkScript",
1979
+ "nonInteractiveClaimEmulatorPubkey",
1980
+ "nonInteractiveClaim"
1981
+ );
1982
+ const refund = decodeCovenantLeaf(
1983
+ params,
1984
+ "nonInteractiveRefundSenderPkScript",
1985
+ "nonInteractiveRefundEmulatorPubkey",
1986
+ "nonInteractiveRefund"
1987
+ );
1988
+ return {
1989
+ sender: base.hex.decode(params.sender),
1990
+ receiver: base.hex.decode(params.receiver),
1991
+ server: base.hex.decode(params.server),
1992
+ preimageHash: base.hex.decode(params.hash),
1993
+ refundLocktime: BigInt(params.refundLocktime),
1994
+ unilateralClaimDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.claimDelay)),
1995
+ unilateralRefundDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(Number(params.refundDelay)),
1996
+ unilateralRefundWithoutReceiverDelay: chunk4A4GRSTB_cjs.sequenceToTimelock(
1997
+ Number(params.refundNoReceiverDelay)
1998
+ ),
1999
+ ...claim && {
2000
+ nonInteractiveClaim: {
2001
+ receiverPkScript: claim.destination,
2002
+ emulatorPubkey: claim.emulatorPubkey
2003
+ }
2004
+ },
2005
+ ...refund && {
2006
+ nonInteractiveRefund: {
2007
+ senderPkScript: refund.destination,
2008
+ emulatorPubkey: refund.emulatorPubkey
2009
+ }
1663
2010
  }
1664
- w.bytes(o);
1665
- }
2011
+ };
1666
2012
  },
1667
- decodeStream: (r) => {
1668
- const out = [];
1669
- while (!r.isEnd()) {
1670
- const cur = r.byte();
1671
- if (btcSigner.OP.OP_0 < cur && cur <= btcSigner.OP.PUSHDATA4) {
1672
- let len;
1673
- if (cur < btcSigner.OP.PUSHDATA1) len = cur;
1674
- else if (cur === btcSigner.OP.PUSHDATA1) len = U8.decodeStream(r);
1675
- else if (cur === btcSigner.OP.PUSHDATA2) len = U16LE.decodeStream(r);
1676
- else if (cur === btcSigner.OP.PUSHDATA4) len = U32LE.decodeStream(r);
1677
- else throw new Error("Should be not possible");
1678
- out.push(r.bytes(len));
1679
- } else if (cur === 0) {
1680
- out.push(0);
1681
- } else if (btcSigner.OP.OP_1 <= cur && cur <= btcSigner.OP.OP_16) {
1682
- out.push(cur - (btcSigner.OP.OP_1 - 1));
1683
- } else if (cur === btcSigner.OP["1NEGATE"]) {
1684
- out.push(-1);
1685
- } else {
1686
- const op = ArkadeOPNames[cur];
1687
- if (op === void 0) throw new Error(`Unknown opcode=${cur.toString(16)}`);
1688
- out.push(op);
2013
+ /**
2014
+ * Select spending path based on context.
2015
+ *
2016
+ * Role is determined from `context.role` or by matching
2017
+ * `context.walletDescriptor` (preferred) / `context.walletPubKey`
2018
+ * against sender/receiver in contract params.
2019
+ */
2020
+ selectPath(script, contract, context) {
2021
+ const role = resolveRole(contract, context);
2022
+ const preimage = contract.params?.preimage;
2023
+ const refundLocktime = BigInt(contract.params.refundLocktime);
2024
+ if (!role) {
2025
+ return null;
2026
+ }
2027
+ if (context.collaborative) {
2028
+ if (role === "receiver" && preimage) {
2029
+ return {
2030
+ leaf: script.claim(),
2031
+ extraWitness: [base.hex.decode(preimage)]
2032
+ };
1689
2033
  }
2034
+ if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
2035
+ return {
2036
+ leaf: script.refundWithoutReceiver()
2037
+ };
2038
+ }
2039
+ return null;
2040
+ }
2041
+ if (role === "receiver" && preimage) {
2042
+ const sequence = Number(contract.params.claimDelay);
2043
+ if (!isCsvSpendable(context, sequence)) return null;
2044
+ return {
2045
+ leaf: script.unilateralClaim(),
2046
+ extraWitness: [base.hex.decode(preimage)],
2047
+ sequence
2048
+ };
2049
+ }
2050
+ if (role === "sender") {
2051
+ const sequence = Number(contract.params.refundNoReceiverDelay);
2052
+ if (!isCsvSpendable(context, sequence)) return null;
2053
+ return {
2054
+ leaf: script.unilateralRefundWithoutReceiver(),
2055
+ sequence
2056
+ };
2057
+ }
2058
+ return null;
2059
+ },
2060
+ /**
2061
+ * Get all possible spending paths (no timelock checks).
2062
+ *
2063
+ * Role is determined from `context.role` or by matching
2064
+ * `context.walletDescriptor` (preferred) / `context.walletPubKey`
2065
+ * against sender/receiver in contract params.
2066
+ */
2067
+ getAllSpendingPaths(script, contract, context) {
2068
+ const role = resolveRole(contract, context);
2069
+ const paths = [];
2070
+ if (!role) {
2071
+ return paths;
1690
2072
  }
1691
- return out;
1692
- }
1693
- });
1694
- function toASM(script) {
1695
- const parts = [];
1696
- for (const op of script) {
1697
- if (typeof op === "string") {
1698
- const name = op.startsWith("OP_") ? op : `OP_${op}`;
1699
- parts.push(name);
1700
- } else if (typeof op === "number" || typeof op === "bigint") {
1701
- if (op === 0 || op === 0n) {
1702
- parts.push("OP_0");
1703
- } else if (op >= 1 && op <= 16) {
1704
- parts.push(`OP_${op}`);
1705
- } else if (op === -1 || op === -1n) {
1706
- parts.push("OP_1NEGATE");
1707
- } else {
1708
- parts.push(base.hex.encode(encode(BigInt(op))));
2073
+ const preimage = contract.params?.preimage;
2074
+ if (context.collaborative) {
2075
+ if (role === "receiver" && preimage) {
2076
+ paths.push({
2077
+ leaf: script.claim(),
2078
+ extraWitness: [base.hex.decode(preimage)]
2079
+ });
2080
+ }
2081
+ if (role === "sender") {
2082
+ paths.push({
2083
+ leaf: script.refundWithoutReceiver()
2084
+ });
1709
2085
  }
1710
2086
  } else {
1711
- parts.push(base.hex.encode(op));
1712
- }
1713
- }
1714
- return parts.join(" ");
1715
- }
1716
- function fromASM(asm) {
1717
- const tokens = asm.trim().split(/\s+/).filter(Boolean);
1718
- const out = [];
1719
- for (const token of tokens) {
1720
- if (token === "OP_0" || token === "OP_FALSE") {
1721
- out.push(0);
1722
- continue;
2087
+ if (role === "receiver" && preimage) {
2088
+ const sequence = Number(contract.params.claimDelay);
2089
+ paths.push({
2090
+ leaf: script.unilateralClaim(),
2091
+ extraWitness: [base.hex.decode(preimage)],
2092
+ sequence
2093
+ });
2094
+ }
2095
+ if (role === "sender") {
2096
+ const sequence = Number(contract.params.refundNoReceiverDelay);
2097
+ paths.push({
2098
+ leaf: script.unilateralRefundWithoutReceiver(),
2099
+ sequence
2100
+ });
2101
+ }
1723
2102
  }
1724
- if (token === "OP_TRUE") {
1725
- out.push(1);
1726
- continue;
2103
+ return paths;
2104
+ },
2105
+ getSpendablePaths(script, contract, context) {
2106
+ const role = resolveRole(contract, context);
2107
+ const paths = [];
2108
+ if (!role) {
2109
+ return paths;
1727
2110
  }
1728
- const numMatch = token.match(/^OP_(\d+)$/);
1729
- if (numMatch) {
1730
- const n = parseInt(numMatch[1], 10);
1731
- if (n >= 1 && n <= 16) {
1732
- out.push(n);
1733
- continue;
2111
+ const preimage = contract.params?.preimage;
2112
+ const refundLocktime = BigInt(contract.params.refundLocktime);
2113
+ if (context.collaborative) {
2114
+ if (role === "receiver" && preimage) {
2115
+ paths.push({
2116
+ leaf: script.claim(),
2117
+ extraWitness: [base.hex.decode(preimage)]
2118
+ });
1734
2119
  }
2120
+ if (role === "sender" && isCltvSatisfied(context, refundLocktime)) {
2121
+ paths.push({
2122
+ leaf: script.refundWithoutReceiver()
2123
+ });
2124
+ }
2125
+ return paths;
1735
2126
  }
1736
- let key;
1737
- if (token.startsWith("OP_")) {
1738
- key = token.slice(3);
1739
- } else {
1740
- key = token;
1741
- }
1742
- if (key in ARKADE_OPS) {
1743
- out.push(key);
1744
- continue;
2127
+ if (role === "receiver" && preimage) {
2128
+ const sequence = Number(contract.params.claimDelay);
2129
+ if (isCsvSpendable(context, sequence)) {
2130
+ paths.push({
2131
+ leaf: script.unilateralClaim(),
2132
+ extraWitness: [base.hex.decode(preimage)],
2133
+ sequence
2134
+ });
2135
+ }
1745
2136
  }
1746
- if (/^[0-9a-fA-F]+$/.test(token) && token.length % 2 === 0) {
1747
- try {
1748
- out.push(base.hex.decode(token));
1749
- continue;
1750
- } catch {
2137
+ if (role === "sender") {
2138
+ const sequence = Number(contract.params.refundNoReceiverDelay);
2139
+ if (isCsvSpendable(context, sequence)) {
2140
+ paths.push({
2141
+ leaf: script.unilateralRefundWithoutReceiver(),
2142
+ sequence
2143
+ });
1751
2144
  }
1752
2145
  }
1753
- throw new Error(`Invalid ASM token: ${token}`);
2146
+ return paths;
2147
+ },
2148
+ /**
2149
+ * Refuse an explicit spend of a lockup whose refund path has not opened.
2150
+ * @see assertVhtlcSpendableNow for why this is the one certain case.
2151
+ */
2152
+ assertSpendableNow(_script, contract, context) {
2153
+ assertVhtlcSpendableNow(contract, context);
2154
+ },
2155
+ /**
2156
+ * Never. A live VHTLC is escrow: the counterparty's claim leaf is armed,
2157
+ * and generic selection — send, settle, RENEWAL, offboard — would move the
2158
+ * lockup out from under a swap the counterparty is still entitled to
2159
+ * complete, or race a claim already in flight. The `vhtlc` handler now
2160
+ * answers the same, for the same reason.
2161
+ *
2162
+ * Recovery is unaffected: the gate guards only GENERIC selection, and every
2163
+ * way out of a lockup names its outpoints explicitly — `settle({ inputs })`,
2164
+ * or `@arkade-os/swap`'s `pushRefundWithoutReceiver`, which builds the
2165
+ * refund itself from `findLockupVtxos`' outpoints.
2166
+ */
2167
+ isGenericallySpendable: () => false,
2168
+ /**
2169
+ * The annotation leaf stamped onto every VTXO locked to this contract.
2170
+ *
2171
+ * **Without this the contract is unusable, not merely unoptimized.**
2172
+ * `deriveContractTapscripts` falls back to `script.forfeit()` for any
2173
+ * handler that does not implement {@link TapscriptDeriving}, and no VHTLC
2174
+ * script version has a `forfeit()` — `VtxoScript` does not define one and
2175
+ * `VHTLC.BaseScript` does not add one. The fallback therefore throws
2176
+ * `legacy.forfeit is not a function`, `annotatableIn` catches it, and
2177
+ * `fetchContractVtxosBulk` drops this contract's VTXOs before they are
2178
+ * persisted. The row would exist and be watched while its balance stayed
2179
+ * permanently invisible, and `getSyncState()` would report `degraded`
2180
+ * forever. (The `vhtlc` handler had the same gap and now closes it the same
2181
+ * way; nothing registers V1 rows through `ContractManager` in this repo, so
2182
+ * it was never hit here.)
2183
+ *
2184
+ * `refundWithoutReceiver` is the leaf, for both roles the annotation
2185
+ * serves, because it is the only collaborative path this wallet can
2186
+ * actually satisfy as the sender — the same conclusion `selectPath`
2187
+ * reaches, kept in step with it deliberately. The claim leaves belong to
2188
+ * the receiver and need a preimage the sender does not have; `refund` and
2189
+ * `unilateralRefund` need the counterparty; the covenant leaves are the
2190
+ * emulator's.
2191
+ *
2192
+ * **It carries a CLTV, and callers must respect it.** A settlement built on
2193
+ * this leaf is only valid once `refundLocktime` has matured, so a recovery
2194
+ * round that sweeps this VTXO early is rejected by the server. That is the
2195
+ * same constraint `packages/boltz-swap` encodes as "pre-CLTV recoverable →
2196
+ * skipped"; nothing in this handler can enforce it, because the annotation
2197
+ * is derived per contract and knows no clock.
2198
+ */
2199
+ deriveTapscripts(script) {
2200
+ const leaf = script.refundWithoutReceiver();
2201
+ return {
2202
+ forfeitTapLeafScript: leaf,
2203
+ intentTapLeafScript: leaf,
2204
+ tapTree: script.encode()
2205
+ };
1754
2206
  }
1755
- return out;
1756
- }
1757
- function asmToBytes(asm) {
1758
- return ArkadeScript.encode(fromASM(asm));
1759
- }
1760
- function bytesToASM(script) {
1761
- return toASM(ArkadeScript.decode(script));
1762
- }
1763
- var TAG_SCRIPT = "ArkScriptHash";
1764
- var TAG_WITNESS = "ArkWitnessHash";
1765
- function arkadeScriptHash(script) {
1766
- return secp256k1_js.schnorr.utils.taggedHash(TAG_SCRIPT, script);
1767
- }
1768
- function arkadeWitnessHash(witness) {
1769
- if (witness.length === 0) {
1770
- return new Uint8Array(32);
2207
+ };
2208
+ var BoardingContractHandler = {
2209
+ type: "boarding",
2210
+ createScript(params) {
2211
+ return DefaultContractHandler.createScript(params);
2212
+ },
2213
+ serializeParams(params) {
2214
+ return DefaultContractHandler.serializeParams(params);
2215
+ },
2216
+ deserializeParams(params) {
2217
+ return DefaultContractHandler.deserializeParams(params);
2218
+ },
2219
+ selectPath(script, contract, context) {
2220
+ return DefaultContractHandler.selectPath(script, contract, context);
2221
+ },
2222
+ getAllSpendingPaths(script, contract, context) {
2223
+ return DefaultContractHandler.getAllSpendingPaths(script, contract, context);
2224
+ },
2225
+ getSpendablePaths(script, contract, context) {
2226
+ return DefaultContractHandler.getSpendablePaths(script, contract, context);
2227
+ },
2228
+ /** Preserves today's settle/board behaviour. */
2229
+ isGenericallySpendable: () => true,
2230
+ /**
2231
+ * Probe the on-chain UTXO set for a boarding output at this HD index.
2232
+ *
2233
+ * Boarding's source of truth is the **current** on-chain coin set
2234
+ * (`OnchainProvider.getCoins`), not the Ark indexer: a boarded (spent)
2235
+ * boarding output becomes an L2 VTXO at the receive index, so the
2236
+ * indexer probe already keeps the gap window open for it; only an
2237
+ * *unspent* boarding output needs the on-chain probe (see plan §2).
2238
+ *
2239
+ * No-ops (returns `[]`) when boarding discovery is not plumbed — i.e.
2240
+ * `deps.boardingTimelock` or `deps.onchainNetwork` is absent — so the
2241
+ * scanner unit harness (which sets neither) is unaffected.
2242
+ *
2243
+ * Always emits `type: "boarding"`, even in the degenerate equal-delay case
2244
+ * where the boarding script is byte-identical to a `default` script at this
2245
+ * index (`boardingExitDelay === unilateralExitDelay`). The collision is no
2246
+ * longer pre-judged in discovery; it is tolerated FIRST-WINS at the
2247
+ * persistence layer ({@link ContractManager.upsertContract}). `deps.csvTimelocks`
2248
+ * is intentionally not read here.
2249
+ */
2250
+ async discoverAt(index, descriptor, deps) {
2251
+ if (!deps.boardingTimelock || !deps.onchainNetwork) return [];
2252
+ const pubKey = deriveDescriptorLeafPubKey(descriptor);
2253
+ const script = new exports.DefaultVtxo.Script({
2254
+ pubKey,
2255
+ serverPubKey: deps.serverPubKey,
2256
+ csvTimelock: deps.boardingTimelock
2257
+ });
2258
+ const onchainAddress = script.onchainAddress(deps.onchainNetwork);
2259
+ const coins = await deps.onchainProvider.getCoins(onchainAddress);
2260
+ if (coins.length === 0) return [];
2261
+ const scriptHex = base.hex.encode(script.pkScript);
2262
+ const boardingSeq = chunk4A4GRSTB_cjs.timelockToSequence(deps.boardingTimelock);
2263
+ return [
2264
+ {
2265
+ type: "boarding",
2266
+ params: {
2267
+ pubKey: base.hex.encode(pubKey),
2268
+ serverPubKey: base.hex.encode(deps.serverPubKey),
2269
+ csvTimelock: boardingSeq.toString()
2270
+ },
2271
+ script: scriptHex,
2272
+ // The persisted row's `address` is the *Ark* address (not the
2273
+ // on-chain P2TR), matching the row registered at init so the
2274
+ // ContractWatcher keeps monitoring the same L2 script and the
2275
+ // VTXO repository bucket lines up (plan §6-I.4). The P2TR is
2276
+ // recomputed from params only for the `getCoins` probe.
2277
+ address: script.address(deps.network.hrp, deps.serverPubKey).encode(),
2278
+ // Tag rotated rows (index > 0) so boot resolution can find the
2279
+ // newest boarding address and descriptor-aware signing can
2280
+ // recover the per-index key (plan §6-II/§6-III.3). The index-0
2281
+ // baseline stays untagged, matching `default`/`delegate`.
2282
+ ...rotatedReceiveMetadata(index, descriptor)
2283
+ }
2284
+ ];
1771
2285
  }
1772
- return secp256k1_js.schnorr.utils.taggedHash(TAG_WITNESS, witness);
1773
- }
1774
- function computeArkadeScriptPublicKey(pubKey, script) {
1775
- const hash = arkadeScriptHash(script);
1776
- const xOnly = pubKey.length === 33 ? pubKey.subarray(1) : pubKey;
1777
- const point = secp256k1_js.schnorr.utils.lift_x(utils_js.bytesToNumberBE(xOnly));
1778
- const scalar = utils_js.bytesToNumberBE(hash) % secp256k1_js.secp256k1.Point.CURVE().n;
1779
- const tweak = secp256k1_js.secp256k1.Point.BASE.multiply(scalar);
1780
- return secp256k1_js.schnorr.utils.pointToBytes(point.add(tweak));
1781
- }
1782
-
1783
- // src/arkade/program.ts
2286
+ };
1784
2287
  var MinimalScriptNum = btcSigner.ScriptNum(void 0, true);
1785
2288
  var SUPPORTED_PROGRAM_VERSION = 0;
1786
2289
  function inputName(ref) {
@@ -1917,27 +2420,27 @@ function encodeTapscriptSegment(seg, pubkeys, args) {
1917
2420
  if (seg.csv) {
1918
2421
  const timelock = { type: seg.csv.type, value: resolveTimelockValue(seg.csv.value, args) };
1919
2422
  if (seg.asm) {
1920
- return chunkDSTYXT2D_cjs.ConditionCSVMultisigTapscript.encode({
2423
+ return chunk4A4GRSTB_cjs.ConditionCSVMultisigTapscript.encode({
1921
2424
  conditionScript: resolveAsm(seg.asm, args),
1922
2425
  timelock,
1923
2426
  pubkeys
1924
2427
  });
1925
2428
  }
1926
- return chunkDSTYXT2D_cjs.CSVMultisigTapscript.encode({ timelock, pubkeys });
2429
+ return chunk4A4GRSTB_cjs.CSVMultisigTapscript.encode({ timelock, pubkeys });
1927
2430
  }
1928
2431
  if (seg.cltv !== void 0) {
1929
- return chunkDSTYXT2D_cjs.CLTVMultisigTapscript.encode({
2432
+ return chunk4A4GRSTB_cjs.CLTVMultisigTapscript.encode({
1930
2433
  absoluteTimelock: resolveTimelockValue(seg.cltv, args),
1931
2434
  pubkeys
1932
2435
  });
1933
2436
  }
1934
2437
  if (seg.asm) {
1935
- return chunkDSTYXT2D_cjs.ConditionMultisigTapscript.encode({
2438
+ return chunk4A4GRSTB_cjs.ConditionMultisigTapscript.encode({
1936
2439
  conditionScript: resolveAsm(seg.asm, args),
1937
2440
  pubkeys
1938
2441
  });
1939
2442
  }
1940
- return chunkDSTYXT2D_cjs.MultisigTapscript.encode({ pubkeys });
2443
+ return chunk4A4GRSTB_cjs.MultisigTapscript.encode({ pubkeys });
1941
2444
  }
1942
2445
  function compileFunctions(program, args, keys) {
1943
2446
  if (program.version !== SUPPORTED_PROGRAM_VERSION) {
@@ -1967,7 +2470,7 @@ function compileFunctions(program, args, keys) {
1967
2470
  return { name: names[i], def, leafScript, arkadeScript, signerKeys: pubkeys };
1968
2471
  });
1969
2472
  }
1970
- var ArkadeProgramScript = class extends chunkDSTYXT2D_cjs.VtxoScript {
2473
+ var ArkadeProgramScript = class extends chunk4A4GRSTB_cjs.VtxoScript {
1971
2474
  constructor(program, args, keys) {
1972
2475
  const partial = compileFunctions(program, args, keys);
1973
2476
  super(partial.map((p) => p.leafScript));
@@ -2180,7 +2683,7 @@ function pathsFor(script, context, opts) {
2180
2683
  continue;
2181
2684
  }
2182
2685
  if (requiresServer) continue;
2183
- const sequence = csv ? Number(chunkDSTYXT2D_cjs.timelockToSequence(csv)) : void 0;
2686
+ const sequence = csv ? Number(chunk4A4GRSTB_cjs.timelockToSequence(csv)) : void 0;
2184
2687
  if (opts.checkTimelocks && csv && !isCsvSpendable(context, sequence)) continue;
2185
2688
  if (opts.checkTimelocks && cltv !== void 0 && !isCltvSatisfied(context, cltv)) {
2186
2689
  continue;
@@ -2219,6 +2722,18 @@ var ArkadeContractHandler = {
2219
2722
  getSpendablePaths(script, _contract, context) {
2220
2723
  return pathsFor(script, context, { checkTimelocks: true });
2221
2724
  },
2725
+ /**
2726
+ * Opt-in, never opt-out. A program artifact cannot say whether its funds are
2727
+ * escrowed — an escrow's collaborative cancel leaf is indistinguishable from
2728
+ * an ordinary spend — so the answer is read from the row the registering code
2729
+ * wrote. Anything but an explicit `true` (absent, malformed, or a row written
2730
+ * before this gate existed) is not generically spendable: `createContract` is
2731
+ * first-writer-wins, so a fail-open marker would let one unmarked
2732
+ * registration permanently open the gate for every contract sharing the script.
2733
+ */
2734
+ isGenericallySpendable(contract) {
2735
+ return contract.metadata?.genericallySpendable === true;
2736
+ },
2222
2737
  /**
2223
2738
  * Annotation tapscripts for VTXOs locked to this contract: prefer the
2224
2739
  * collaborative tapscript path (the multisig the Arkade Service co-signs,
@@ -2246,6 +2761,7 @@ var ArkadeContractHandler = {
2246
2761
  contractHandlers.register(DefaultContractHandler);
2247
2762
  contractHandlers.register(DelegateContractHandler);
2248
2763
  contractHandlers.register(VHTLCContractHandler);
2764
+ contractHandlers.register(VHTLCV2ContractHandler);
2249
2765
  contractHandlers.register(BoardingContractHandler);
2250
2766
  contractHandlers.register(ArkadeContractHandler);
2251
2767
 
@@ -2264,6 +2780,7 @@ exports.OPCODE_NAMES = OPCODE_NAMES;
2264
2780
  exports.OPCODE_VALUES = OPCODE_VALUES;
2265
2781
  exports.SUPPORTED_PROGRAM_VERSION = SUPPORTED_PROGRAM_VERSION;
2266
2782
  exports.VHTLCContractHandler = VHTLCContractHandler;
2783
+ exports.VHTLCV2ContractHandler = VHTLCV2ContractHandler;
2267
2784
  exports.WALLET_RECEIVE_SOURCE = WALLET_RECEIVE_SOURCE;
2268
2785
  exports.arkadeScriptHash = arkadeScriptHash;
2269
2786
  exports.arkadeWitnessHash = arkadeWitnessHash;
@@ -2291,5 +2808,5 @@ exports.stringifyArtifact = stringifyArtifact;
2291
2808
  exports.toASM = toASM;
2292
2809
  exports.validateProgram = validateProgram;
2293
2810
  exports.witnessRefToBytes = witnessRefToBytes;
2294
- //# sourceMappingURL=chunk-2XO43UHI.cjs.map
2295
- //# sourceMappingURL=chunk-2XO43UHI.cjs.map
2811
+ //# sourceMappingURL=chunk-MZNLCMUP.cjs.map
2812
+ //# sourceMappingURL=chunk-MZNLCMUP.cjs.map