@arkade-os/sdk 0.4.14 → 0.4.16

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 (203) hide show
  1. package/README.md +287 -215
  2. package/dist/cjs/arkfee/estimator.js +1 -1
  3. package/dist/cjs/arkfee/types.js +2 -1
  4. package/dist/cjs/arknote/index.js +43 -4
  5. package/dist/cjs/bip322/index.js +1 -1
  6. package/dist/cjs/contracts/arkcontract.js +1 -1
  7. package/dist/cjs/contracts/contractManager.js +40 -24
  8. package/dist/cjs/contracts/contractWatcher.js +29 -22
  9. package/dist/cjs/contracts/handlers/default.js +1 -1
  10. package/dist/cjs/contracts/handlers/delegate.js +1 -1
  11. package/dist/cjs/contracts/handlers/helpers.js +25 -1
  12. package/dist/cjs/contracts/handlers/vhtlc.js +2 -4
  13. package/dist/cjs/extension/asset/assetGroup.js +92 -5
  14. package/dist/cjs/extension/asset/assetId.js +67 -3
  15. package/dist/cjs/extension/asset/assetInput.js +18 -0
  16. package/dist/cjs/extension/asset/assetOutput.js +15 -0
  17. package/dist/cjs/extension/asset/assetRef.js +66 -0
  18. package/dist/cjs/extension/asset/metadata.js +15 -0
  19. package/dist/cjs/extension/asset/packet.js +4 -1
  20. package/dist/cjs/extension/index.js +1 -1
  21. package/dist/cjs/forfeit.js +14 -0
  22. package/dist/cjs/identity/index.js +6 -0
  23. package/dist/cjs/identity/seedIdentity.js +5 -5
  24. package/dist/cjs/identity/singleKey.js +4 -0
  25. package/dist/cjs/index.js +5 -3
  26. package/dist/cjs/intent/index.js +28 -12
  27. package/dist/cjs/providers/ark.js +3 -2
  28. package/dist/cjs/providers/delegator.js +20 -1
  29. package/dist/cjs/providers/expoArk.js +2 -2
  30. package/dist/cjs/providers/indexer.js +2 -2
  31. package/dist/cjs/providers/onchain.js +2 -1
  32. package/dist/cjs/repositories/realm/schemas.js +2 -2
  33. package/dist/cjs/repositories/realm/types.js +1 -1
  34. package/dist/cjs/script/address.js +37 -6
  35. package/dist/cjs/script/base.js +70 -1
  36. package/dist/cjs/script/default.js +3 -0
  37. package/dist/cjs/script/delegate.js +4 -0
  38. package/dist/cjs/script/tapscript.js +25 -4
  39. package/dist/cjs/script/vhtlc.js +35 -27
  40. package/dist/cjs/storage/fileSystem.js +1 -1
  41. package/dist/cjs/storage/inMemory.js +1 -1
  42. package/dist/cjs/storage/indexedDB.js +1 -1
  43. package/dist/cjs/storage/localStorage.js +1 -1
  44. package/dist/cjs/tree/validation.js +1 -1
  45. package/dist/cjs/utils/arkTransaction.js +5 -5
  46. package/dist/cjs/utils/bip21.js +16 -3
  47. package/dist/cjs/utils/syncCursors.js +4 -4
  48. package/dist/cjs/utils/transaction.js +1 -1
  49. package/dist/cjs/utils/transactionHistory.js +11 -11
  50. package/dist/cjs/utils/unknownFields.js +3 -3
  51. package/dist/cjs/wallet/asset-manager.js +4 -4
  52. package/dist/cjs/wallet/batch.js +5 -5
  53. package/dist/cjs/wallet/delegator.js +9 -8
  54. package/dist/cjs/wallet/expo/background.js +3 -3
  55. package/dist/cjs/wallet/expo/wallet.js +7 -7
  56. package/dist/cjs/wallet/index.js +43 -0
  57. package/dist/cjs/wallet/onchain.js +43 -5
  58. package/dist/cjs/wallet/ramps.js +44 -14
  59. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +22 -22
  60. package/dist/cjs/wallet/serviceWorker/wallet.js +28 -24
  61. package/dist/cjs/wallet/unroll.js +12 -8
  62. package/dist/cjs/wallet/utils.js +1 -1
  63. package/dist/cjs/wallet/vtxo-manager.js +123 -82
  64. package/dist/cjs/wallet/wallet.js +231 -98
  65. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +1 -1
  66. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +2 -2
  67. package/dist/cjs/worker/expo/taskRunner.js +3 -3
  68. package/dist/cjs/worker/messageBus.js +3 -0
  69. package/dist/esm/arkfee/estimator.js +1 -1
  70. package/dist/esm/arkfee/types.js +2 -1
  71. package/dist/esm/arknote/index.js +43 -4
  72. package/dist/esm/bip322/index.js +1 -1
  73. package/dist/esm/contracts/arkcontract.js +1 -1
  74. package/dist/esm/contracts/contractManager.js +40 -24
  75. package/dist/esm/contracts/contractWatcher.js +29 -22
  76. package/dist/esm/contracts/handlers/default.js +1 -1
  77. package/dist/esm/contracts/handlers/delegate.js +1 -1
  78. package/dist/esm/contracts/handlers/helpers.js +24 -1
  79. package/dist/esm/contracts/handlers/vhtlc.js +3 -5
  80. package/dist/esm/extension/asset/assetGroup.js +92 -5
  81. package/dist/esm/extension/asset/assetId.js +67 -3
  82. package/dist/esm/extension/asset/assetInput.js +18 -0
  83. package/dist/esm/extension/asset/assetOutput.js +15 -0
  84. package/dist/esm/extension/asset/assetRef.js +66 -0
  85. package/dist/esm/extension/asset/metadata.js +15 -0
  86. package/dist/esm/extension/asset/packet.js +4 -1
  87. package/dist/esm/extension/index.js +1 -1
  88. package/dist/esm/forfeit.js +14 -0
  89. package/dist/esm/identity/index.js +5 -0
  90. package/dist/esm/identity/seedIdentity.js +5 -5
  91. package/dist/esm/identity/singleKey.js +4 -0
  92. package/dist/esm/index.js +3 -2
  93. package/dist/esm/intent/index.js +28 -12
  94. package/dist/esm/providers/ark.js +3 -2
  95. package/dist/esm/providers/delegator.js +20 -1
  96. package/dist/esm/providers/expoArk.js +2 -2
  97. package/dist/esm/providers/indexer.js +2 -2
  98. package/dist/esm/providers/onchain.js +2 -1
  99. package/dist/esm/repositories/realm/schemas.js +2 -2
  100. package/dist/esm/repositories/realm/types.js +1 -1
  101. package/dist/esm/script/address.js +37 -6
  102. package/dist/esm/script/base.js +70 -1
  103. package/dist/esm/script/default.js +3 -0
  104. package/dist/esm/script/delegate.js +4 -0
  105. package/dist/esm/script/tapscript.js +25 -4
  106. package/dist/esm/script/vhtlc.js +35 -27
  107. package/dist/esm/storage/fileSystem.js +1 -1
  108. package/dist/esm/storage/inMemory.js +1 -1
  109. package/dist/esm/storage/indexedDB.js +1 -1
  110. package/dist/esm/storage/localStorage.js +1 -1
  111. package/dist/esm/tree/validation.js +1 -1
  112. package/dist/esm/utils/arkTransaction.js +5 -5
  113. package/dist/esm/utils/bip21.js +16 -3
  114. package/dist/esm/utils/syncCursors.js +4 -4
  115. package/dist/esm/utils/transaction.js +1 -1
  116. package/dist/esm/utils/transactionHistory.js +11 -11
  117. package/dist/esm/utils/unknownFields.js +3 -3
  118. package/dist/esm/wallet/asset-manager.js +4 -4
  119. package/dist/esm/wallet/batch.js +5 -5
  120. package/dist/esm/wallet/delegator.js +9 -8
  121. package/dist/esm/wallet/expo/background.js +3 -3
  122. package/dist/esm/wallet/expo/wallet.js +7 -7
  123. package/dist/esm/wallet/index.js +43 -0
  124. package/dist/esm/wallet/onchain.js +43 -5
  125. package/dist/esm/wallet/ramps.js +44 -14
  126. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +22 -22
  127. package/dist/esm/wallet/serviceWorker/wallet.js +28 -24
  128. package/dist/esm/wallet/unroll.js +12 -8
  129. package/dist/esm/wallet/utils.js +1 -1
  130. package/dist/esm/wallet/vtxo-manager.js +122 -81
  131. package/dist/esm/wallet/wallet.js +232 -99
  132. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +1 -1
  133. package/dist/esm/worker/expo/processors/contractPollProcessor.js +2 -2
  134. package/dist/esm/worker/expo/taskRunner.js +3 -3
  135. package/dist/esm/worker/messageBus.js +3 -0
  136. package/dist/types/arkfee/estimator.d.ts +1 -1
  137. package/dist/types/arkfee/types.d.ts +2 -1
  138. package/dist/types/arknote/index.d.ts +44 -4
  139. package/dist/types/bip322/index.d.ts +1 -1
  140. package/dist/types/contracts/arkcontract.d.ts +1 -1
  141. package/dist/types/contracts/contractManager.d.ts +40 -63
  142. package/dist/types/contracts/contractWatcher.d.ts +39 -18
  143. package/dist/types/contracts/handlers/default.d.ts +1 -1
  144. package/dist/types/contracts/handlers/delegate.d.ts +1 -1
  145. package/dist/types/contracts/handlers/helpers.d.ts +11 -1
  146. package/dist/types/contracts/types.d.ts +36 -26
  147. package/dist/types/extension/asset/assetGroup.d.ts +92 -1
  148. package/dist/types/extension/asset/assetId.d.ts +67 -3
  149. package/dist/types/extension/asset/assetInput.d.ts +18 -0
  150. package/dist/types/extension/asset/assetOutput.d.ts +15 -0
  151. package/dist/types/extension/asset/assetRef.d.ts +66 -0
  152. package/dist/types/extension/asset/metadata.d.ts +15 -0
  153. package/dist/types/extension/asset/packet.d.ts +4 -1
  154. package/dist/types/extension/index.d.ts +1 -1
  155. package/dist/types/forfeit.d.ts +14 -0
  156. package/dist/types/identity/index.d.ts +36 -0
  157. package/dist/types/identity/seedIdentity.d.ts +10 -8
  158. package/dist/types/identity/singleKey.d.ts +4 -0
  159. package/dist/types/index.d.ts +3 -3
  160. package/dist/types/intent/index.d.ts +19 -6
  161. package/dist/types/providers/ark.d.ts +40 -2
  162. package/dist/types/providers/delegator.d.ts +54 -1
  163. package/dist/types/providers/expoArk.d.ts +2 -2
  164. package/dist/types/providers/indexer.d.ts +105 -2
  165. package/dist/types/providers/onchain.d.ts +62 -1
  166. package/dist/types/repositories/realm/schemas.d.ts +2 -2
  167. package/dist/types/repositories/realm/types.d.ts +2 -2
  168. package/dist/types/repositories/walletRepository.d.ts +16 -0
  169. package/dist/types/script/address.d.ts +35 -2
  170. package/dist/types/script/base.d.ts +66 -1
  171. package/dist/types/script/default.d.ts +3 -0
  172. package/dist/types/script/delegate.d.ts +4 -0
  173. package/dist/types/script/tapscript.d.ts +17 -2
  174. package/dist/types/script/vhtlc.d.ts +35 -27
  175. package/dist/types/storage/fileSystem.d.ts +1 -1
  176. package/dist/types/storage/inMemory.d.ts +1 -1
  177. package/dist/types/storage/index.d.ts +1 -1
  178. package/dist/types/storage/indexedDB.d.ts +1 -1
  179. package/dist/types/storage/localStorage.d.ts +1 -1
  180. package/dist/types/utils/arkTransaction.d.ts +3 -3
  181. package/dist/types/utils/bip21.d.ts +17 -0
  182. package/dist/types/utils/syncCursors.d.ts +4 -4
  183. package/dist/types/utils/transaction.d.ts +1 -1
  184. package/dist/types/utils/transactionHistory.d.ts +3 -3
  185. package/dist/types/utils/unknownFields.d.ts +5 -5
  186. package/dist/types/wallet/asset-manager.d.ts +3 -3
  187. package/dist/types/wallet/batch.d.ts +27 -7
  188. package/dist/types/wallet/delegator.d.ts +10 -0
  189. package/dist/types/wallet/expo/background.d.ts +4 -4
  190. package/dist/types/wallet/expo/wallet.d.ts +10 -10
  191. package/dist/types/wallet/index.d.ts +457 -25
  192. package/dist/types/wallet/onchain.d.ts +42 -4
  193. package/dist/types/wallet/ramps.d.ts +40 -10
  194. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +4 -4
  195. package/dist/types/wallet/serviceWorker/wallet.d.ts +71 -33
  196. package/dist/types/wallet/unroll.d.ts +8 -6
  197. package/dist/types/wallet/vtxo-manager.d.ts +146 -93
  198. package/dist/types/wallet/wallet.d.ts +91 -33
  199. package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +1 -1
  200. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +1 -1
  201. package/dist/types/worker/expo/taskRunner.d.ts +6 -6
  202. package/dist/types/worker/messageBus.d.ts +5 -3
  203. package/package.json +18 -10
@@ -4,7 +4,7 @@ exports.Estimator = void 0;
4
4
  const celenv_js_1 = require("./celenv.js");
5
5
  const types_js_1 = require("./types.js");
6
6
  /**
7
- * Estimator evaluates CEL expressions to calculate fees for Ark intents
7
+ * Estimator evaluates CEL expressions to calculate fees for Arkade intents
8
8
  */
9
9
  class Estimator {
10
10
  /**
@@ -4,7 +4,6 @@ exports.FeeAmount = void 0;
4
4
  /**
5
5
  * FeeAmount is a wrapper around a number that represents a fee amount in satoshis floating point.
6
6
  * @param value - The fee amount in floating point.
7
- * @method satoshis - Returns the fee amount in satoshis as a integer.
8
7
  * @example
9
8
  * const fee = new FeeAmount(1.23456789);
10
9
  * console.log(fee.value); // 1.23456789
@@ -14,9 +13,11 @@ class FeeAmount {
14
13
  constructor(value) {
15
14
  this.value = value;
16
15
  }
16
+ /** Returns the fee amount rounded up to whole satoshis. */
17
17
  get satoshis() {
18
18
  return this.value ? Math.ceil(this.value) : 0;
19
19
  }
20
+ /** Add two fee amounts together. */
20
21
  add(other) {
21
22
  return new FeeAmount(this.value + other.value);
22
23
  }
@@ -6,10 +6,12 @@ const utils_js_1 = require("@scure/btc-signer/utils.js");
6
6
  const btc_signer_1 = require("@scure/btc-signer");
7
7
  const base_2 = require("../script/base");
8
8
  /**
9
- * ArkNotes are special virtual coins in the Ark protocol that can be created
10
- * and spent without requiring any transactions. The server mints them, and they
11
- * are encoded as base58 strings with a human-readable prefix. It contains a
12
- * preimage and value.
9
+ * ArkNotes are special virtual outputs in the Arkade protocol that
10
+ * can be created and spent without requiring any transactions.
11
+ * The server mints them, and they are encoded as base58 strings
12
+ * with a human-readable prefix, a preimage and a value.
13
+ *
14
+ * @see VtxoScript
13
15
  *
14
16
  * @example
15
17
  * ```typescript
@@ -24,6 +26,13 @@ const base_2 = require("../script/base");
24
26
  * ```
25
27
  */
26
28
  class ArkNote {
29
+ /**
30
+ * Create an ArkNote from a preimage and value.
31
+ *
32
+ * @param preimage - 32-byte preimage revealed to spend the note
33
+ * @param value - Note value in satoshis
34
+ * @param HRP - Optional human-readable prefix for string encoding
35
+ */
27
36
  constructor(preimage, value, HRP = ArkNote.DefaultHRP) {
28
37
  this.preimage = preimage;
29
38
  this.value = value;
@@ -40,12 +49,27 @@ class ArkNote {
40
49
  this.status = { confirmed: true };
41
50
  this.extraWitness = [this.preimage];
42
51
  }
52
+ /**
53
+ * Encode the note as raw bytes.
54
+ *
55
+ * @returns Serialized note bytes
56
+ * @see decode
57
+ */
43
58
  encode() {
44
59
  const result = new Uint8Array(ArkNote.Length);
45
60
  result.set(this.preimage, 0);
46
61
  writeUInt32BE(result, this.value, this.preimage.length);
47
62
  return result;
48
63
  }
64
+ /**
65
+ * Decode a note from raw bytes.
66
+ *
67
+ * @param data - Serialized note bytes
68
+ * @param hrp - Human-readable prefix expected for future string encoding
69
+ * @returns Decoded ArkNote
70
+ * @throws Error if the payload length is invalid
71
+ * @see encode
72
+ */
49
73
  static decode(data, hrp = ArkNote.DefaultHRP) {
50
74
  if (data.length !== ArkNote.Length) {
51
75
  throw new Error(`invalid data length: expected ${ArkNote.Length} bytes, got ${data.length}`);
@@ -54,6 +78,15 @@ class ArkNote {
54
78
  const value = readUInt32BE(data, ArkNote.PreimageLength);
55
79
  return new ArkNote(preimage, value, hrp);
56
80
  }
81
+ /**
82
+ * Decode a note from its base58 string form.
83
+ *
84
+ * @param noteStr - Base58-encoded note string
85
+ * @param hrp - Human-readable prefix expected on the note string
86
+ * @returns Decoded ArkNote
87
+ * @throws Error if the prefix or base58 payload is invalid
88
+ * @see toString
89
+ */
57
90
  static fromString(noteStr, hrp = ArkNote.DefaultHRP) {
58
91
  noteStr = noteStr.trim();
59
92
  if (!noteStr.startsWith(hrp)) {
@@ -66,6 +99,12 @@ class ArkNote {
66
99
  }
67
100
  return ArkNote.decode(decoded, hrp);
68
101
  }
102
+ /**
103
+ * Encode the note to its human-readable base58 string form.
104
+ *
105
+ * @returns Base58-encoded note string
106
+ * @see fromString
107
+ */
69
108
  toString() {
70
109
  return this.HRP + base_1.base58.encode(this.encode());
71
110
  }
@@ -17,7 +17,7 @@ const TAG_BIP322 = "BIP0322-signed-message";
17
17
  *
18
18
  * Reuses the same toSpend/toSign transaction construction as Intent proofs,
19
19
  * but with the standard BIP-322 tagged hash ("BIP0322-signed-message")
20
- * instead of the Ark-specific tag.
20
+ * instead of the Arkade-specific tag.
21
21
  *
22
22
  * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
23
23
  *
@@ -17,7 +17,7 @@ const ARKCONTRACT_PREFIX = "arkcontract";
17
17
  * Format: arkcontract={type}&{key1}={value1}&{key2}={value2}...
18
18
  *
19
19
  * This format is compatible with NArk and allows contracts to be
20
- * shared/imported across different Ark implementations.
20
+ * shared/imported across different Arkade SDKs.
21
21
  *
22
22
  * @example
23
23
  * ```typescript
@@ -10,38 +10,50 @@ const DEFAULT_PAGE_SIZE = 500;
10
10
  /**
11
11
  * Central manager for contract lifecycle and operations.
12
12
  *
13
- * The ContractManager orchestrates:
14
- * - Contract registration and persistence
15
- * - Multi-contract watching via ContractWatcher
16
- * - VTXO queries across contracts
13
+ * Responsibilities:
14
+ * - Create and persist contracts
15
+ * - Query stored contracts (optionally with their virtual outputs)
16
+ * - Provide spendable path selection for a contract
17
+ * - Emit contract-related events (virtual output received/spent/expired, connection reset)
18
+ *
19
+ * Notes:
20
+ * - Implementations typically start watching automatically during initialization
21
+ * (so `onContractEvent()` is just for subscribing).
17
22
  *
18
23
  * @example
19
24
  * ```typescript
20
- * const manager = new ContractManager({
25
+ * const manager = await ContractManager.create({
21
26
  * indexerProvider: wallet.indexerProvider,
22
27
  * contractRepository: wallet.contractRepository,
28
+ * walletRepository: wallet.walletRepository,
23
29
  * getDefaultAddress: () => wallet.getAddress(),
24
30
  * });
25
31
  *
26
- * // Initialize (loads persisted contracts)
27
- * await manager.initialize();
28
- *
29
32
  * // Create a new VHTLC contract
30
33
  * const contract = await manager.createContract({
31
34
  * label: "Lightning Receive",
32
35
  * type: "vhtlc",
33
- * params: { sender: "ab12...", receiver: "cd34...", ... },
36
+ * params: { sender: "ark1q...", receiver: "ark1q...", ... },
34
37
  * script: "5120...",
35
- * address: "tark1...",
38
+ * address: "ark1q...",
36
39
  * });
37
40
  *
38
41
  * // Start watching for events
39
- * const stop = await manager.startWatching((event) => {
42
+ * const unsubscribe = manager.onContractEvent((event) => {
40
43
  * console.log(`${event.type} on ${event.contractScript}`);
41
44
  * });
42
45
  *
46
+ * // Query contracts together with their current virtual outputs
47
+ * const contractsWithVtxos = await manager.getContractsWithVtxos();
48
+ *
43
49
  * // Get balance across all contracts
44
- * const balances = await manager.getAllBalances();
50
+ * const balances = contractsWithVtxos.flatMap(({vtxos}) => vtxos).reduce((acc, vtxo) => acc + vtxo.value, 0)
51
+ *
52
+ * // Later: unsubscribe from events
53
+ * unsubscribe();
54
+ *
55
+ * // Clean up
56
+ * manager.dispose();
45
57
  * ```
46
58
  */
47
59
  class ContractManager {
@@ -49,7 +61,7 @@ class ContractManager {
49
61
  this.initialized = false;
50
62
  this.eventCallbacks = new Set();
51
63
  this.config = config;
52
- // Create watcher with wallet repository for VTXO caching
64
+ // Create watcher with wallet repository for virtual output caching
53
65
  this.watcher = new contractWatcher_1.ContractWatcher({
54
66
  indexerProvider: config.indexerProvider,
55
67
  walletRepository: config.walletRepository,
@@ -61,7 +73,7 @@ class ContractManager {
61
73
  * Initialize the manager by loading persisted contracts and starting to watch.
62
74
  *
63
75
  * After initialization, the manager automatically watches all active contracts
64
- * and contracts with VTXOs. Use `onContractEvent()` to register event callbacks.
76
+ * and contracts with virtual outputs. Use `onContractEvent()` to register event callbacks.
65
77
  *
66
78
  * @param config ContractManagerConfig
67
79
  */
@@ -76,10 +88,10 @@ class ContractManager {
76
88
  }
77
89
  // Load persisted contracts
78
90
  const contracts = await this.config.contractRepository.getContracts();
79
- // Delta-sync: fetch only VTXOs that changed since the last cursor,
91
+ // Delta-sync: fetch only virtual outputs that changed since the last cursor,
80
92
  // falling back to a full bootstrap for scripts seen for the first time.
81
93
  await this.deltaSyncContracts(contracts);
82
- // Reconcile the pending frontier: fetch all not-yet-finalized VTXOs
94
+ // Reconcile the pending frontier: fetch all not-yet-finalized virtual outputs
83
95
  // to catch any that the delta window may have missed.
84
96
  if (contracts.length > 0) {
85
97
  await this.reconcilePendingFrontier(contracts);
@@ -148,7 +160,7 @@ class ContractManager {
148
160
  };
149
161
  // Persist
150
162
  await this.config.contractRepository.saveContract(contract);
151
- // fetch all VTXOs (including spent/swept) for this contract
163
+ // fetch all virtual outputs (including spent/swept) for this contract
152
164
  const requestStartedAt = Date.now();
153
165
  await this.fetchContractVxosFromIndexer([contract], true);
154
166
  // Advance the sync cursor so that the watcher's vtxo_received
@@ -261,7 +273,6 @@ class ContractManager {
261
273
  /**
262
274
  * Get currently spendable paths for a contract.
263
275
  *
264
- * @param contractScript - The contract script
265
276
  * @param options - Options for getting spendable paths
266
277
  */
267
278
  async getSpendablePaths(options) {
@@ -281,6 +292,11 @@ class ContractManager {
281
292
  };
282
293
  return handler.getSpendablePaths(script, contract, context);
283
294
  }
295
+ /**
296
+ * Get every currently valid spending path for a contract.
297
+ *
298
+ * @param options - Options for getting spending paths
299
+ */
284
300
  async getAllSpendingPaths(options) {
285
301
  const { contractScript, collaborative = true, walletPubKey } = options;
286
302
  const [contract] = await this.getContracts({ script: contractScript });
@@ -323,7 +339,7 @@ class ContractManager {
323
339
  };
324
340
  }
325
341
  /**
326
- * Force a VTXO refresh from the indexer.
342
+ * Force refresh virtual outputs from the indexer.
327
343
  *
328
344
  * Without options, clears all sync cursors and re-fetches every contract.
329
345
  * With options, narrows the refresh to specific scripts and/or a time window.
@@ -385,7 +401,7 @@ class ContractManager {
385
401
  */
386
402
  async handleContractEvent(event) {
387
403
  switch (event.type) {
388
- // Delta-sync only the changed VTXOs for this contract.
404
+ // Delta-sync only the changed virtual outputs for this contract.
389
405
  case "vtxo_received":
390
406
  case "vtxo_spent":
391
407
  await this.deltaSyncContracts([event.contract]);
@@ -410,7 +426,7 @@ class ContractManager {
410
426
  return await this.fetchContractVxosFromIndexer(contracts, false, pageSize);
411
427
  }
412
428
  /**
413
- * Incrementally sync VTXOs for the given contracts.
429
+ * Incrementally sync virtual outputs for the given contracts.
414
430
  * Uses per-script cursors to fetch only what changed since the last sync.
415
431
  * Scripts without a cursor are bootstrapped with a full fetch.
416
432
  */
@@ -462,8 +478,8 @@ class ContractManager {
462
478
  return result;
463
479
  }
464
480
  /**
465
- * Fetch all pending (not-yet-finalized) VTXOs and upsert them into the
466
- * repository. This catches VTXOs whose state changed outside the delta
481
+ * Fetch all pending (unfinalized) virtual outputs and upsert them into the
482
+ * repository. This catches virtual outputs whose state changed outside the delta
467
483
  * window (e.g. a spend that hasn't settled yet).
468
484
  */
469
485
  async reconcilePendingFrontier(contracts) {
@@ -545,7 +561,7 @@ class ContractManager {
545
561
  pageSize,
546
562
  });
547
563
  for (const vtxo of vtxos) {
548
- // Match the VTXO back to its contract via the script field
564
+ // Match the virtual output back to its contract via the script field
549
565
  // populated by the indexer.
550
566
  if (!vtxo.script)
551
567
  continue;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContractWatcher = void 0;
4
4
  /**
5
- * Watches multiple contracts for VTXO changes with resilient connection handling.
5
+ * Watches multiple contracts for virtual output state changes with resilient connection handling.
6
6
  *
7
7
  * Features:
8
8
  * - Automatic reconnection with exponential backoff
@@ -32,6 +32,12 @@ exports.ContractWatcher = void 0;
32
32
  * ```
33
33
  */
34
34
  class ContractWatcher {
35
+ /**
36
+ * Create a contract watcher with the given providers and polling settings.
37
+ *
38
+ * @param config - Contract watcher configuration
39
+ * @see ContractWatcherConfig
40
+ */
35
41
  constructor(config) {
36
42
  this.contracts = new Map();
37
43
  this.isWatching = false;
@@ -48,9 +54,10 @@ class ContractWatcher {
48
54
  /**
49
55
  * Add a contract to be watched.
50
56
  *
51
- * Active contracts are immediately subscribed. All contracts are polled
52
- * to discover any existing VTXOs (which may cause them to be watched
53
- * even if inactive).
57
+ * Active contracts are immediately subscribed.
58
+ *
59
+ * All contracts are polled to discover any existing virtual outputs
60
+ * (which may cause them to be watched even if inactive).
54
61
  */
55
62
  async addContract(contract) {
56
63
  const state = {
@@ -58,11 +65,11 @@ class ContractWatcher {
58
65
  lastKnownVtxos: new Map(),
59
66
  };
60
67
  this.contracts.set(contract.script, state);
61
- // If we're already watching, poll to discover VTXOs and update subscription
68
+ // If we're already watching, poll to discover virtual outputs and update subscription
62
69
  if (this.isWatching) {
63
- // Poll first to discover VTXOs (may affect whether we watch this contract)
70
+ // Poll first to discover virtual outputs (may affect whether we watch this contract).
64
71
  await this.pollContracts([contract.script]);
65
- // Update subscription based on active state and VTXOs
72
+ // Update subscription based on active state and virtual outputs.
66
73
  await this.tryUpdateSubscription();
67
74
  }
68
75
  }
@@ -108,10 +115,10 @@ class ContractWatcher {
108
115
  *
109
116
  * Returns scripts for:
110
117
  * - All active contracts
111
- * - All contracts with known VTXOs (regardless of state)
118
+ * - All contracts with known virtual outputs (regardless of state)
112
119
  *
113
120
  * This ensures we continue monitoring contracts even after they're
114
- * deactivated, as long as they have unspent VTXOs.
121
+ * deactivated, as long as they have unspent virtual outputs.
115
122
  */
116
123
  getScriptsToWatch() {
117
124
  const scripts = new Set();
@@ -121,7 +128,7 @@ class ContractWatcher {
121
128
  scripts.add(state.contract.script);
122
129
  continue;
123
130
  }
124
- // Also watch inactive/expired contracts that have VTXOs
131
+ // Also watch inactive/expired contracts that have virtual outputs.
125
132
  if (state.lastKnownVtxos.size > 0) {
126
133
  scripts.add(state.contract.script);
127
134
  }
@@ -129,8 +136,8 @@ class ContractWatcher {
129
136
  return Array.from(scripts);
130
137
  }
131
138
  /**
132
- * Get VTXOs for contracts, grouped by contract script.
133
- * Uses Repository.
139
+ * Get virtual outputs for contracts, grouped by contract script.
140
+ * @see WalletRepository for `repo`
134
141
  */
135
142
  async getContractVtxos(options) {
136
143
  const { contractScripts, includeSpent } = options;
@@ -163,7 +170,7 @@ class ContractWatcher {
163
170
  return new Map(results.flat(1));
164
171
  }
165
172
  /**
166
- * Start watching for VTXO events across all active contracts.
173
+ * Start watching for virtual output events across all active contracts.
167
174
  */
168
175
  async startWatching(callback) {
169
176
  if (this.isWatching) {
@@ -342,7 +349,7 @@ class ContractWatcher {
342
349
  return;
343
350
  const now = Date.now();
344
351
  try {
345
- // Load all the VTXOs for these contracts, from DB
352
+ // Load all the virtual outputs for these contracts, from DB
346
353
  const vtxosMap = await this.getContractVtxos({
347
354
  contractScripts,
348
355
  includeSpent: false, // only spendable ones!
@@ -353,7 +360,7 @@ class ContractWatcher {
353
360
  continue;
354
361
  const currentVtxos = vtxosMap.get(contractScript) || [];
355
362
  const currentKeys = new Set(currentVtxos.map((v) => `${v.txid}:${v.vout}`));
356
- // Find new VTXOs and add them to the contract's state
363
+ // Find new virtual outputs and add them to the contract's state
357
364
  const newVtxos = [];
358
365
  for (const vtxo of currentVtxos) {
359
366
  const key = `${vtxo.txid}:${vtxo.vout}`;
@@ -362,7 +369,7 @@ class ContractWatcher {
362
369
  state.lastKnownVtxos.set(key, vtxo);
363
370
  }
364
371
  }
365
- // Find spent VTXOs and remove them from the contract's state
372
+ // Find spent virtual outputs and remove them from the contract's state
366
373
  const spentVtxos = [];
367
374
  for (const [key, vtxo] of state.lastKnownVtxos) {
368
375
  if (!currentKeys.has(key)) {
@@ -397,7 +404,7 @@ class ContractWatcher {
397
404
  /**
398
405
  * Update the subscription with scripts that should be watched.
399
406
  *
400
- * Watches both active contracts and contracts with VTXOs.
407
+ * Watches both active contracts and contracts with virtual outputs.
401
408
  */
402
409
  async updateSubscription() {
403
410
  const scriptsToWatch = this.getScriptsToWatch();
@@ -474,11 +481,11 @@ class ContractWatcher {
474
481
  }
475
482
  }
476
483
  /**
477
- * Process VTXOs from subscription and route to correct contracts.
484
+ * Process virtual outputs from subscription and route to correct contracts.
478
485
  * Uses the scripts from the subscription response to determine contract ownership.
479
486
  */
480
487
  processSubscriptionVtxos(vtxos, scripts, eventType, timestamp) {
481
- // If we have exactly one script, all VTXOs belong to that contract
488
+ // If we have exactly one script, all virtual outputs belong to that contract
482
489
  // Otherwise, we can't reliably determine ownership without script in VirtualCoin
483
490
  if (scripts.length === 1) {
484
491
  const contractScript = scripts[0];
@@ -500,8 +507,8 @@ class ContractWatcher {
500
507
  }
501
508
  return;
502
509
  }
503
- // Multiple scripts - assign VTXOs to all matching contracts
504
- // This is a limitation: we can't know which VTXO belongs to which script
510
+ // Multiple scripts - assign virtual outputs to all matching contracts
511
+ // This is a limitation: we can't know which virtual output belongs to which script
505
512
  // In practice, subscription events usually come with a single script context
506
513
  for (const script of scripts) {
507
514
  const contractScript = script;
@@ -523,7 +530,7 @@ class ContractWatcher {
523
530
  }
524
531
  }
525
532
  /**
526
- * Emit a VTXO event for a contract.
533
+ * Emit a virtual output event for a contract.
527
534
  */
528
535
  emitVtxoEvent(contractScript, vtxos, eventType, timestamp) {
529
536
  if (!this.eventCallback)
@@ -5,7 +5,7 @@ const base_1 = require("@scure/base");
5
5
  const default_1 = require("../../script/default");
6
6
  const helpers_1 = require("./helpers");
7
7
  /**
8
- * Handler for default wallet VTXOs.
8
+ * Handler for default wallet virtual outputs.
9
9
  *
10
10
  * Default contracts use the standard forfeit + exit tapscript:
11
11
  * - forfeit: (Alice + Server) multisig for collaborative spending
@@ -6,7 +6,7 @@ const delegate_1 = require("../../script/delegate");
6
6
  const default_1 = require("../../script/default");
7
7
  const helpers_1 = require("./helpers");
8
8
  /**
9
- * Handler for delegate wallet VTXOs.
9
+ * Handler for delegate wallet virtual outputs.
10
10
  *
11
11
  * Delegate contracts extend the default tapscript with an additional delegate path:
12
12
  * - forfeit: (Alice + Server) multisig for collaborative spending
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.timelockToSequence = timelockToSequence;
37
37
  exports.sequenceToTimelock = sequenceToTimelock;
38
38
  exports.resolveRole = resolveRole;
39
+ exports.isCltvSatisfied = isCltvSatisfied;
39
40
  exports.isCsvSpendable = isCsvSpendable;
40
41
  const bip68 = __importStar(require("bip68"));
41
42
  /**
@@ -79,7 +80,30 @@ function resolveRole(contract, context) {
79
80
  return undefined;
80
81
  }
81
82
  /**
82
- * Check if a CSV timelock is currently satisfied for the given context/VTXO.
83
+ * BIP65 threshold: locktime values below this are interpreted as block heights,
84
+ * values at or above are interpreted as Unix timestamps (seconds).
85
+ */
86
+ const CLTV_HEIGHT_THRESHOLD = 500000000n;
87
+ /**
88
+ * Check if an absolute (CLTV) locktime is currently satisfied.
89
+ *
90
+ * Following the BIP65 convention:
91
+ * - locktime < 500_000_000 → interpreted as a block height; compared against `context.blockHeight`
92
+ * - locktime >= 500_000_000 → interpreted as a Unix timestamp (seconds); compared against `context.currentTime`
93
+ *
94
+ * Returns false if the relevant context field is missing.
95
+ */
96
+ function isCltvSatisfied(context, locktime) {
97
+ if (locktime < CLTV_HEIGHT_THRESHOLD) {
98
+ if (context.blockHeight === undefined)
99
+ return false;
100
+ return BigInt(context.blockHeight) >= locktime;
101
+ }
102
+ const currentTimeSec = BigInt(Math.floor(context.currentTime / 1000));
103
+ return currentTimeSec >= locktime;
104
+ }
105
+ /**
106
+ * Check if a CSV timelock is currently satisfied for the given context/virtual output.
83
107
  */
84
108
  function isCsvSpendable(context, sequence) {
85
109
  if (sequence === undefined)
@@ -59,7 +59,6 @@ exports.VHTLCContractHandler = {
59
59
  const role = (0, helpers_1.resolveRole)(contract, context);
60
60
  const preimage = contract.params?.preimage;
61
61
  const refundLocktime = BigInt(contract.params.refundLocktime);
62
- const currentTimeSec = Math.floor(context.currentTime / 1000);
63
62
  if (!role) {
64
63
  return null;
65
64
  }
@@ -70,7 +69,7 @@ exports.VHTLCContractHandler = {
70
69
  extraWitness: [base_1.hex.decode(preimage)],
71
70
  };
72
71
  }
73
- if (role === "sender" && BigInt(currentTimeSec) >= refundLocktime) {
72
+ if (role === "sender" && (0, helpers_1.isCltvSatisfied)(context, refundLocktime)) {
74
73
  return {
75
74
  leaf: script.refundWithoutReceiver(),
76
75
  };
@@ -154,7 +153,6 @@ exports.VHTLCContractHandler = {
154
153
  }
155
154
  const preimage = contract.params?.preimage;
156
155
  const refundLocktime = BigInt(contract.params.refundLocktime);
157
- const currentTimeSec = Math.floor(context.currentTime / 1000);
158
156
  if (context.collaborative) {
159
157
  if (role === "receiver" && preimage) {
160
158
  paths.push({
@@ -162,7 +160,7 @@ exports.VHTLCContractHandler = {
162
160
  extraWitness: [base_1.hex.decode(preimage)],
163
161
  });
164
162
  }
165
- if (role === "sender" && BigInt(currentTimeSec) >= refundLocktime) {
163
+ if (role === "sender" && (0, helpers_1.isCltvSatisfied)(context, refundLocktime)) {
166
164
  paths.push({
167
165
  leaf: script.refundWithoutReceiver(),
168
166
  });