@arkade-os/sdk 0.4.15 → 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 (197) hide show
  1. package/README.md +102 -96
  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 +1 -1
  12. package/dist/cjs/extension/asset/assetGroup.js +92 -5
  13. package/dist/cjs/extension/asset/assetId.js +67 -3
  14. package/dist/cjs/extension/asset/assetInput.js +18 -0
  15. package/dist/cjs/extension/asset/assetOutput.js +15 -0
  16. package/dist/cjs/extension/asset/assetRef.js +66 -0
  17. package/dist/cjs/extension/asset/metadata.js +15 -0
  18. package/dist/cjs/extension/asset/packet.js +4 -1
  19. package/dist/cjs/extension/index.js +1 -1
  20. package/dist/cjs/forfeit.js +14 -0
  21. package/dist/cjs/identity/seedIdentity.js +2 -2
  22. package/dist/cjs/identity/singleKey.js +4 -0
  23. package/dist/cjs/intent/index.js +28 -12
  24. package/dist/cjs/providers/ark.js +3 -2
  25. package/dist/cjs/providers/delegator.js +20 -1
  26. package/dist/cjs/providers/expoArk.js +2 -2
  27. package/dist/cjs/providers/indexer.js +2 -2
  28. package/dist/cjs/providers/onchain.js +2 -1
  29. package/dist/cjs/repositories/realm/schemas.js +2 -2
  30. package/dist/cjs/repositories/realm/types.js +1 -1
  31. package/dist/cjs/script/address.js +37 -6
  32. package/dist/cjs/script/base.js +70 -1
  33. package/dist/cjs/script/default.js +3 -0
  34. package/dist/cjs/script/delegate.js +4 -0
  35. package/dist/cjs/script/tapscript.js +17 -2
  36. package/dist/cjs/script/vhtlc.js +35 -27
  37. package/dist/cjs/storage/fileSystem.js +1 -1
  38. package/dist/cjs/storage/inMemory.js +1 -1
  39. package/dist/cjs/storage/indexedDB.js +1 -1
  40. package/dist/cjs/storage/localStorage.js +1 -1
  41. package/dist/cjs/tree/validation.js +1 -1
  42. package/dist/cjs/utils/arkTransaction.js +5 -5
  43. package/dist/cjs/utils/bip21.js +16 -3
  44. package/dist/cjs/utils/syncCursors.js +4 -4
  45. package/dist/cjs/utils/transaction.js +1 -1
  46. package/dist/cjs/utils/transactionHistory.js +11 -11
  47. package/dist/cjs/utils/unknownFields.js +3 -3
  48. package/dist/cjs/wallet/asset-manager.js +4 -4
  49. package/dist/cjs/wallet/batch.js +5 -5
  50. package/dist/cjs/wallet/delegator.js +9 -8
  51. package/dist/cjs/wallet/expo/background.js +3 -3
  52. package/dist/cjs/wallet/expo/wallet.js +7 -7
  53. package/dist/cjs/wallet/index.js +43 -0
  54. package/dist/cjs/wallet/onchain.js +43 -5
  55. package/dist/cjs/wallet/ramps.js +44 -14
  56. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +22 -22
  57. package/dist/cjs/wallet/serviceWorker/wallet.js +28 -24
  58. package/dist/cjs/wallet/unroll.js +12 -8
  59. package/dist/cjs/wallet/utils.js +1 -1
  60. package/dist/cjs/wallet/vtxo-manager.js +122 -82
  61. package/dist/cjs/wallet/wallet.js +125 -67
  62. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +1 -1
  63. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +2 -2
  64. package/dist/cjs/worker/expo/taskRunner.js +3 -3
  65. package/dist/cjs/worker/messageBus.js +3 -0
  66. package/dist/esm/arkfee/estimator.js +1 -1
  67. package/dist/esm/arkfee/types.js +2 -1
  68. package/dist/esm/arknote/index.js +43 -4
  69. package/dist/esm/bip322/index.js +1 -1
  70. package/dist/esm/contracts/arkcontract.js +1 -1
  71. package/dist/esm/contracts/contractManager.js +40 -24
  72. package/dist/esm/contracts/contractWatcher.js +29 -22
  73. package/dist/esm/contracts/handlers/default.js +1 -1
  74. package/dist/esm/contracts/handlers/delegate.js +1 -1
  75. package/dist/esm/contracts/handlers/helpers.js +1 -1
  76. package/dist/esm/extension/asset/assetGroup.js +92 -5
  77. package/dist/esm/extension/asset/assetId.js +67 -3
  78. package/dist/esm/extension/asset/assetInput.js +18 -0
  79. package/dist/esm/extension/asset/assetOutput.js +15 -0
  80. package/dist/esm/extension/asset/assetRef.js +66 -0
  81. package/dist/esm/extension/asset/metadata.js +15 -0
  82. package/dist/esm/extension/asset/packet.js +4 -1
  83. package/dist/esm/extension/index.js +1 -1
  84. package/dist/esm/forfeit.js +14 -0
  85. package/dist/esm/identity/seedIdentity.js +2 -2
  86. package/dist/esm/identity/singleKey.js +4 -0
  87. package/dist/esm/index.js +1 -1
  88. package/dist/esm/intent/index.js +28 -12
  89. package/dist/esm/providers/ark.js +3 -2
  90. package/dist/esm/providers/delegator.js +20 -1
  91. package/dist/esm/providers/expoArk.js +2 -2
  92. package/dist/esm/providers/indexer.js +2 -2
  93. package/dist/esm/providers/onchain.js +2 -1
  94. package/dist/esm/repositories/realm/schemas.js +2 -2
  95. package/dist/esm/repositories/realm/types.js +1 -1
  96. package/dist/esm/script/address.js +37 -6
  97. package/dist/esm/script/base.js +70 -1
  98. package/dist/esm/script/default.js +3 -0
  99. package/dist/esm/script/delegate.js +4 -0
  100. package/dist/esm/script/tapscript.js +17 -2
  101. package/dist/esm/script/vhtlc.js +35 -27
  102. package/dist/esm/storage/fileSystem.js +1 -1
  103. package/dist/esm/storage/inMemory.js +1 -1
  104. package/dist/esm/storage/indexedDB.js +1 -1
  105. package/dist/esm/storage/localStorage.js +1 -1
  106. package/dist/esm/tree/validation.js +1 -1
  107. package/dist/esm/utils/arkTransaction.js +5 -5
  108. package/dist/esm/utils/bip21.js +16 -3
  109. package/dist/esm/utils/syncCursors.js +4 -4
  110. package/dist/esm/utils/transaction.js +1 -1
  111. package/dist/esm/utils/transactionHistory.js +11 -11
  112. package/dist/esm/utils/unknownFields.js +3 -3
  113. package/dist/esm/wallet/asset-manager.js +4 -4
  114. package/dist/esm/wallet/batch.js +5 -5
  115. package/dist/esm/wallet/delegator.js +9 -8
  116. package/dist/esm/wallet/expo/background.js +3 -3
  117. package/dist/esm/wallet/expo/wallet.js +7 -7
  118. package/dist/esm/wallet/index.js +43 -0
  119. package/dist/esm/wallet/onchain.js +43 -5
  120. package/dist/esm/wallet/ramps.js +44 -14
  121. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +22 -22
  122. package/dist/esm/wallet/serviceWorker/wallet.js +28 -24
  123. package/dist/esm/wallet/unroll.js +12 -8
  124. package/dist/esm/wallet/utils.js +1 -1
  125. package/dist/esm/wallet/vtxo-manager.js +121 -81
  126. package/dist/esm/wallet/wallet.js +125 -67
  127. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +1 -1
  128. package/dist/esm/worker/expo/processors/contractPollProcessor.js +2 -2
  129. package/dist/esm/worker/expo/taskRunner.js +3 -3
  130. package/dist/esm/worker/messageBus.js +3 -0
  131. package/dist/types/arkfee/estimator.d.ts +1 -1
  132. package/dist/types/arkfee/types.d.ts +2 -1
  133. package/dist/types/arknote/index.d.ts +44 -4
  134. package/dist/types/bip322/index.d.ts +1 -1
  135. package/dist/types/contracts/arkcontract.d.ts +1 -1
  136. package/dist/types/contracts/contractManager.d.ts +40 -63
  137. package/dist/types/contracts/contractWatcher.d.ts +39 -18
  138. package/dist/types/contracts/handlers/default.d.ts +1 -1
  139. package/dist/types/contracts/handlers/delegate.d.ts +1 -1
  140. package/dist/types/contracts/handlers/helpers.d.ts +1 -1
  141. package/dist/types/contracts/types.d.ts +36 -26
  142. package/dist/types/extension/asset/assetGroup.d.ts +92 -1
  143. package/dist/types/extension/asset/assetId.d.ts +67 -3
  144. package/dist/types/extension/asset/assetInput.d.ts +18 -0
  145. package/dist/types/extension/asset/assetOutput.d.ts +15 -0
  146. package/dist/types/extension/asset/assetRef.d.ts +66 -0
  147. package/dist/types/extension/asset/metadata.d.ts +15 -0
  148. package/dist/types/extension/asset/packet.d.ts +4 -1
  149. package/dist/types/extension/index.d.ts +1 -1
  150. package/dist/types/forfeit.d.ts +14 -0
  151. package/dist/types/identity/index.d.ts +16 -0
  152. package/dist/types/identity/seedIdentity.d.ts +8 -6
  153. package/dist/types/identity/singleKey.d.ts +4 -0
  154. package/dist/types/intent/index.d.ts +19 -6
  155. package/dist/types/providers/ark.d.ts +40 -2
  156. package/dist/types/providers/delegator.d.ts +54 -1
  157. package/dist/types/providers/expoArk.d.ts +2 -2
  158. package/dist/types/providers/indexer.d.ts +105 -2
  159. package/dist/types/providers/onchain.d.ts +62 -1
  160. package/dist/types/repositories/realm/schemas.d.ts +2 -2
  161. package/dist/types/repositories/realm/types.d.ts +2 -2
  162. package/dist/types/repositories/walletRepository.d.ts +16 -0
  163. package/dist/types/script/address.d.ts +35 -2
  164. package/dist/types/script/base.d.ts +66 -1
  165. package/dist/types/script/default.d.ts +3 -0
  166. package/dist/types/script/delegate.d.ts +4 -0
  167. package/dist/types/script/tapscript.d.ts +17 -2
  168. package/dist/types/script/vhtlc.d.ts +35 -27
  169. package/dist/types/storage/fileSystem.d.ts +1 -1
  170. package/dist/types/storage/inMemory.d.ts +1 -1
  171. package/dist/types/storage/index.d.ts +1 -1
  172. package/dist/types/storage/indexedDB.d.ts +1 -1
  173. package/dist/types/storage/localStorage.d.ts +1 -1
  174. package/dist/types/utils/arkTransaction.d.ts +3 -3
  175. package/dist/types/utils/bip21.d.ts +17 -0
  176. package/dist/types/utils/syncCursors.d.ts +4 -4
  177. package/dist/types/utils/transaction.d.ts +1 -1
  178. package/dist/types/utils/transactionHistory.d.ts +3 -3
  179. package/dist/types/utils/unknownFields.d.ts +5 -5
  180. package/dist/types/wallet/asset-manager.d.ts +3 -3
  181. package/dist/types/wallet/batch.d.ts +27 -7
  182. package/dist/types/wallet/delegator.d.ts +10 -0
  183. package/dist/types/wallet/expo/background.d.ts +4 -4
  184. package/dist/types/wallet/expo/wallet.d.ts +10 -10
  185. package/dist/types/wallet/index.d.ts +457 -25
  186. package/dist/types/wallet/onchain.d.ts +42 -4
  187. package/dist/types/wallet/ramps.d.ts +40 -10
  188. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +4 -4
  189. package/dist/types/wallet/serviceWorker/wallet.d.ts +71 -33
  190. package/dist/types/wallet/unroll.d.ts +8 -6
  191. package/dist/types/wallet/vtxo-manager.d.ts +146 -93
  192. package/dist/types/wallet/wallet.d.ts +91 -33
  193. package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +1 -1
  194. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +1 -1
  195. package/dist/types/worker/expo/taskRunner.d.ts +6 -6
  196. package/dist/types/worker/messageBus.d.ts +5 -3
  197. package/package.json +1 -1
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * FeeAmount is a wrapper around a number that represents a fee amount in satoshis floating point.
3
3
  * @param value - The fee amount in floating point.
4
- * @method satoshis - Returns the fee amount in satoshis as a integer.
5
4
  * @example
6
5
  * const fee = new FeeAmount(1.23456789);
7
6
  * console.log(fee.value); // 1.23456789
@@ -11,9 +10,11 @@ export class FeeAmount {
11
10
  constructor(value) {
12
11
  this.value = value;
13
12
  }
13
+ /** Returns the fee amount rounded up to whole satoshis. */
14
14
  get satoshis() {
15
15
  return this.value ? Math.ceil(this.value) : 0;
16
16
  }
17
+ /** Add two fee amounts together. */
17
18
  add(other) {
18
19
  return new FeeAmount(this.value + other.value);
19
20
  }
@@ -3,10 +3,12 @@ import { sha256 } from "@scure/btc-signer/utils.js";
3
3
  import { Script } from "@scure/btc-signer";
4
4
  import { VtxoScript } from '../script/base.js';
5
5
  /**
6
- * ArkNotes are special virtual coins in the Ark protocol that can be created
7
- * and spent without requiring any transactions. The server mints them, and they
8
- * are encoded as base58 strings with a human-readable prefix. It contains a
9
- * preimage and value.
6
+ * ArkNotes are special virtual outputs in the Arkade protocol that
7
+ * can be created and spent without requiring any transactions.
8
+ * The server mints them, and they are encoded as base58 strings
9
+ * with a human-readable prefix, a preimage and a value.
10
+ *
11
+ * @see VtxoScript
10
12
  *
11
13
  * @example
12
14
  * ```typescript
@@ -21,6 +23,13 @@ import { VtxoScript } from '../script/base.js';
21
23
  * ```
22
24
  */
23
25
  export class ArkNote {
26
+ /**
27
+ * Create an ArkNote from a preimage and value.
28
+ *
29
+ * @param preimage - 32-byte preimage revealed to spend the note
30
+ * @param value - Note value in satoshis
31
+ * @param HRP - Optional human-readable prefix for string encoding
32
+ */
24
33
  constructor(preimage, value, HRP = ArkNote.DefaultHRP) {
25
34
  this.preimage = preimage;
26
35
  this.value = value;
@@ -37,12 +46,27 @@ export class ArkNote {
37
46
  this.status = { confirmed: true };
38
47
  this.extraWitness = [this.preimage];
39
48
  }
49
+ /**
50
+ * Encode the note as raw bytes.
51
+ *
52
+ * @returns Serialized note bytes
53
+ * @see decode
54
+ */
40
55
  encode() {
41
56
  const result = new Uint8Array(ArkNote.Length);
42
57
  result.set(this.preimage, 0);
43
58
  writeUInt32BE(result, this.value, this.preimage.length);
44
59
  return result;
45
60
  }
61
+ /**
62
+ * Decode a note from raw bytes.
63
+ *
64
+ * @param data - Serialized note bytes
65
+ * @param hrp - Human-readable prefix expected for future string encoding
66
+ * @returns Decoded ArkNote
67
+ * @throws Error if the payload length is invalid
68
+ * @see encode
69
+ */
46
70
  static decode(data, hrp = ArkNote.DefaultHRP) {
47
71
  if (data.length !== ArkNote.Length) {
48
72
  throw new Error(`invalid data length: expected ${ArkNote.Length} bytes, got ${data.length}`);
@@ -51,6 +75,15 @@ export class ArkNote {
51
75
  const value = readUInt32BE(data, ArkNote.PreimageLength);
52
76
  return new ArkNote(preimage, value, hrp);
53
77
  }
78
+ /**
79
+ * Decode a note from its base58 string form.
80
+ *
81
+ * @param noteStr - Base58-encoded note string
82
+ * @param hrp - Human-readable prefix expected on the note string
83
+ * @returns Decoded ArkNote
84
+ * @throws Error if the prefix or base58 payload is invalid
85
+ * @see toString
86
+ */
54
87
  static fromString(noteStr, hrp = ArkNote.DefaultHRP) {
55
88
  noteStr = noteStr.trim();
56
89
  if (!noteStr.startsWith(hrp)) {
@@ -63,6 +96,12 @@ export class ArkNote {
63
96
  }
64
97
  return ArkNote.decode(decoded, hrp);
65
98
  }
99
+ /**
100
+ * Encode the note to its human-readable base58 string form.
101
+ *
102
+ * @returns Base58-encoded note string
103
+ * @see fromString
104
+ */
66
105
  toString() {
67
106
  return this.HRP + base58.encode(this.encode());
68
107
  }
@@ -14,7 +14,7 @@ const TAG_BIP322 = "BIP0322-signed-message";
14
14
  *
15
15
  * Reuses the same toSpend/toSign transaction construction as Intent proofs,
16
16
  * but with the standard BIP-322 tagged hash ("BIP0322-signed-message")
17
- * instead of the Ark-specific tag.
17
+ * instead of the Arkade-specific tag.
18
18
  *
19
19
  * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
20
20
  *
@@ -10,7 +10,7 @@ const ARKCONTRACT_PREFIX = "arkcontract";
10
10
  * Format: arkcontract={type}&{key1}={value1}&{key2}={value2}...
11
11
  *
12
12
  * This format is compatible with NArk and allows contracts to be
13
- * shared/imported across different Ark implementations.
13
+ * shared/imported across different Arkade SDKs.
14
14
  *
15
15
  * @example
16
16
  * ```typescript
@@ -7,38 +7,50 @@ const DEFAULT_PAGE_SIZE = 500;
7
7
  /**
8
8
  * Central manager for contract lifecycle and operations.
9
9
  *
10
- * The ContractManager orchestrates:
11
- * - Contract registration and persistence
12
- * - Multi-contract watching via ContractWatcher
13
- * - VTXO queries across contracts
10
+ * Responsibilities:
11
+ * - Create and persist contracts
12
+ * - Query stored contracts (optionally with their virtual outputs)
13
+ * - Provide spendable path selection for a contract
14
+ * - Emit contract-related events (virtual output received/spent/expired, connection reset)
15
+ *
16
+ * Notes:
17
+ * - Implementations typically start watching automatically during initialization
18
+ * (so `onContractEvent()` is just for subscribing).
14
19
  *
15
20
  * @example
16
21
  * ```typescript
17
- * const manager = new ContractManager({
22
+ * const manager = await ContractManager.create({
18
23
  * indexerProvider: wallet.indexerProvider,
19
24
  * contractRepository: wallet.contractRepository,
25
+ * walletRepository: wallet.walletRepository,
20
26
  * getDefaultAddress: () => wallet.getAddress(),
21
27
  * });
22
28
  *
23
- * // Initialize (loads persisted contracts)
24
- * await manager.initialize();
25
- *
26
29
  * // Create a new VHTLC contract
27
30
  * const contract = await manager.createContract({
28
31
  * label: "Lightning Receive",
29
32
  * type: "vhtlc",
30
- * params: { sender: "ab12...", receiver: "cd34...", ... },
33
+ * params: { sender: "ark1q...", receiver: "ark1q...", ... },
31
34
  * script: "5120...",
32
- * address: "tark1...",
35
+ * address: "ark1q...",
33
36
  * });
34
37
  *
35
38
  * // Start watching for events
36
- * const stop = await manager.startWatching((event) => {
39
+ * const unsubscribe = manager.onContractEvent((event) => {
37
40
  * console.log(`${event.type} on ${event.contractScript}`);
38
41
  * });
39
42
  *
43
+ * // Query contracts together with their current virtual outputs
44
+ * const contractsWithVtxos = await manager.getContractsWithVtxos();
45
+ *
40
46
  * // Get balance across all contracts
41
- * const balances = await manager.getAllBalances();
47
+ * const balances = contractsWithVtxos.flatMap(({vtxos}) => vtxos).reduce((acc, vtxo) => acc + vtxo.value, 0)
48
+ *
49
+ * // Later: unsubscribe from events
50
+ * unsubscribe();
51
+ *
52
+ * // Clean up
53
+ * manager.dispose();
42
54
  * ```
43
55
  */
44
56
  export class ContractManager {
@@ -46,7 +58,7 @@ export class ContractManager {
46
58
  this.initialized = false;
47
59
  this.eventCallbacks = new Set();
48
60
  this.config = config;
49
- // Create watcher with wallet repository for VTXO caching
61
+ // Create watcher with wallet repository for virtual output caching
50
62
  this.watcher = new ContractWatcher({
51
63
  indexerProvider: config.indexerProvider,
52
64
  walletRepository: config.walletRepository,
@@ -58,7 +70,7 @@ export class ContractManager {
58
70
  * Initialize the manager by loading persisted contracts and starting to watch.
59
71
  *
60
72
  * After initialization, the manager automatically watches all active contracts
61
- * and contracts with VTXOs. Use `onContractEvent()` to register event callbacks.
73
+ * and contracts with virtual outputs. Use `onContractEvent()` to register event callbacks.
62
74
  *
63
75
  * @param config ContractManagerConfig
64
76
  */
@@ -73,10 +85,10 @@ export class ContractManager {
73
85
  }
74
86
  // Load persisted contracts
75
87
  const contracts = await this.config.contractRepository.getContracts();
76
- // Delta-sync: fetch only VTXOs that changed since the last cursor,
88
+ // Delta-sync: fetch only virtual outputs that changed since the last cursor,
77
89
  // falling back to a full bootstrap for scripts seen for the first time.
78
90
  await this.deltaSyncContracts(contracts);
79
- // Reconcile the pending frontier: fetch all not-yet-finalized VTXOs
91
+ // Reconcile the pending frontier: fetch all not-yet-finalized virtual outputs
80
92
  // to catch any that the delta window may have missed.
81
93
  if (contracts.length > 0) {
82
94
  await this.reconcilePendingFrontier(contracts);
@@ -145,7 +157,7 @@ export class ContractManager {
145
157
  };
146
158
  // Persist
147
159
  await this.config.contractRepository.saveContract(contract);
148
- // fetch all VTXOs (including spent/swept) for this contract
160
+ // fetch all virtual outputs (including spent/swept) for this contract
149
161
  const requestStartedAt = Date.now();
150
162
  await this.fetchContractVxosFromIndexer([contract], true);
151
163
  // Advance the sync cursor so that the watcher's vtxo_received
@@ -258,7 +270,6 @@ export class ContractManager {
258
270
  /**
259
271
  * Get currently spendable paths for a contract.
260
272
  *
261
- * @param contractScript - The contract script
262
273
  * @param options - Options for getting spendable paths
263
274
  */
264
275
  async getSpendablePaths(options) {
@@ -278,6 +289,11 @@ export class ContractManager {
278
289
  };
279
290
  return handler.getSpendablePaths(script, contract, context);
280
291
  }
292
+ /**
293
+ * Get every currently valid spending path for a contract.
294
+ *
295
+ * @param options - Options for getting spending paths
296
+ */
281
297
  async getAllSpendingPaths(options) {
282
298
  const { contractScript, collaborative = true, walletPubKey } = options;
283
299
  const [contract] = await this.getContracts({ script: contractScript });
@@ -320,7 +336,7 @@ export class ContractManager {
320
336
  };
321
337
  }
322
338
  /**
323
- * Force a VTXO refresh from the indexer.
339
+ * Force refresh virtual outputs from the indexer.
324
340
  *
325
341
  * Without options, clears all sync cursors and re-fetches every contract.
326
342
  * With options, narrows the refresh to specific scripts and/or a time window.
@@ -382,7 +398,7 @@ export class ContractManager {
382
398
  */
383
399
  async handleContractEvent(event) {
384
400
  switch (event.type) {
385
- // Delta-sync only the changed VTXOs for this contract.
401
+ // Delta-sync only the changed virtual outputs for this contract.
386
402
  case "vtxo_received":
387
403
  case "vtxo_spent":
388
404
  await this.deltaSyncContracts([event.contract]);
@@ -407,7 +423,7 @@ export class ContractManager {
407
423
  return await this.fetchContractVxosFromIndexer(contracts, false, pageSize);
408
424
  }
409
425
  /**
410
- * Incrementally sync VTXOs for the given contracts.
426
+ * Incrementally sync virtual outputs for the given contracts.
411
427
  * Uses per-script cursors to fetch only what changed since the last sync.
412
428
  * Scripts without a cursor are bootstrapped with a full fetch.
413
429
  */
@@ -459,8 +475,8 @@ export class ContractManager {
459
475
  return result;
460
476
  }
461
477
  /**
462
- * Fetch all pending (not-yet-finalized) VTXOs and upsert them into the
463
- * repository. This catches VTXOs whose state changed outside the delta
478
+ * Fetch all pending (unfinalized) virtual outputs and upsert them into the
479
+ * repository. This catches virtual outputs whose state changed outside the delta
464
480
  * window (e.g. a spend that hasn't settled yet).
465
481
  */
466
482
  async reconcilePendingFrontier(contracts) {
@@ -542,7 +558,7 @@ export class ContractManager {
542
558
  pageSize,
543
559
  });
544
560
  for (const vtxo of vtxos) {
545
- // Match the VTXO back to its contract via the script field
561
+ // Match the virtual output back to its contract via the script field
546
562
  // populated by the indexer.
547
563
  if (!vtxo.script)
548
564
  continue;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Watches multiple contracts for VTXO changes with resilient connection handling.
2
+ * Watches multiple contracts for virtual output state changes with resilient connection handling.
3
3
  *
4
4
  * Features:
5
5
  * - Automatic reconnection with exponential backoff
@@ -29,6 +29,12 @@
29
29
  * ```
30
30
  */
31
31
  export class ContractWatcher {
32
+ /**
33
+ * Create a contract watcher with the given providers and polling settings.
34
+ *
35
+ * @param config - Contract watcher configuration
36
+ * @see ContractWatcherConfig
37
+ */
32
38
  constructor(config) {
33
39
  this.contracts = new Map();
34
40
  this.isWatching = false;
@@ -45,9 +51,10 @@ export class ContractWatcher {
45
51
  /**
46
52
  * Add a contract to be watched.
47
53
  *
48
- * Active contracts are immediately subscribed. All contracts are polled
49
- * to discover any existing VTXOs (which may cause them to be watched
50
- * even if inactive).
54
+ * Active contracts are immediately subscribed.
55
+ *
56
+ * All contracts are polled to discover any existing virtual outputs
57
+ * (which may cause them to be watched even if inactive).
51
58
  */
52
59
  async addContract(contract) {
53
60
  const state = {
@@ -55,11 +62,11 @@ export class ContractWatcher {
55
62
  lastKnownVtxos: new Map(),
56
63
  };
57
64
  this.contracts.set(contract.script, state);
58
- // If we're already watching, poll to discover VTXOs and update subscription
65
+ // If we're already watching, poll to discover virtual outputs and update subscription
59
66
  if (this.isWatching) {
60
- // Poll first to discover VTXOs (may affect whether we watch this contract)
67
+ // Poll first to discover virtual outputs (may affect whether we watch this contract).
61
68
  await this.pollContracts([contract.script]);
62
- // Update subscription based on active state and VTXOs
69
+ // Update subscription based on active state and virtual outputs.
63
70
  await this.tryUpdateSubscription();
64
71
  }
65
72
  }
@@ -105,10 +112,10 @@ export class ContractWatcher {
105
112
  *
106
113
  * Returns scripts for:
107
114
  * - All active contracts
108
- * - All contracts with known VTXOs (regardless of state)
115
+ * - All contracts with known virtual outputs (regardless of state)
109
116
  *
110
117
  * This ensures we continue monitoring contracts even after they're
111
- * deactivated, as long as they have unspent VTXOs.
118
+ * deactivated, as long as they have unspent virtual outputs.
112
119
  */
113
120
  getScriptsToWatch() {
114
121
  const scripts = new Set();
@@ -118,7 +125,7 @@ export class ContractWatcher {
118
125
  scripts.add(state.contract.script);
119
126
  continue;
120
127
  }
121
- // Also watch inactive/expired contracts that have VTXOs
128
+ // Also watch inactive/expired contracts that have virtual outputs.
122
129
  if (state.lastKnownVtxos.size > 0) {
123
130
  scripts.add(state.contract.script);
124
131
  }
@@ -126,8 +133,8 @@ export class ContractWatcher {
126
133
  return Array.from(scripts);
127
134
  }
128
135
  /**
129
- * Get VTXOs for contracts, grouped by contract script.
130
- * Uses Repository.
136
+ * Get virtual outputs for contracts, grouped by contract script.
137
+ * @see WalletRepository for `repo`
131
138
  */
132
139
  async getContractVtxos(options) {
133
140
  const { contractScripts, includeSpent } = options;
@@ -160,7 +167,7 @@ export class ContractWatcher {
160
167
  return new Map(results.flat(1));
161
168
  }
162
169
  /**
163
- * Start watching for VTXO events across all active contracts.
170
+ * Start watching for virtual output events across all active contracts.
164
171
  */
165
172
  async startWatching(callback) {
166
173
  if (this.isWatching) {
@@ -339,7 +346,7 @@ export class ContractWatcher {
339
346
  return;
340
347
  const now = Date.now();
341
348
  try {
342
- // Load all the VTXOs for these contracts, from DB
349
+ // Load all the virtual outputs for these contracts, from DB
343
350
  const vtxosMap = await this.getContractVtxos({
344
351
  contractScripts,
345
352
  includeSpent: false, // only spendable ones!
@@ -350,7 +357,7 @@ export class ContractWatcher {
350
357
  continue;
351
358
  const currentVtxos = vtxosMap.get(contractScript) || [];
352
359
  const currentKeys = new Set(currentVtxos.map((v) => `${v.txid}:${v.vout}`));
353
- // Find new VTXOs and add them to the contract's state
360
+ // Find new virtual outputs and add them to the contract's state
354
361
  const newVtxos = [];
355
362
  for (const vtxo of currentVtxos) {
356
363
  const key = `${vtxo.txid}:${vtxo.vout}`;
@@ -359,7 +366,7 @@ export class ContractWatcher {
359
366
  state.lastKnownVtxos.set(key, vtxo);
360
367
  }
361
368
  }
362
- // Find spent VTXOs and remove them from the contract's state
369
+ // Find spent virtual outputs and remove them from the contract's state
363
370
  const spentVtxos = [];
364
371
  for (const [key, vtxo] of state.lastKnownVtxos) {
365
372
  if (!currentKeys.has(key)) {
@@ -394,7 +401,7 @@ export class ContractWatcher {
394
401
  /**
395
402
  * Update the subscription with scripts that should be watched.
396
403
  *
397
- * Watches both active contracts and contracts with VTXOs.
404
+ * Watches both active contracts and contracts with virtual outputs.
398
405
  */
399
406
  async updateSubscription() {
400
407
  const scriptsToWatch = this.getScriptsToWatch();
@@ -471,11 +478,11 @@ export class ContractWatcher {
471
478
  }
472
479
  }
473
480
  /**
474
- * Process VTXOs from subscription and route to correct contracts.
481
+ * Process virtual outputs from subscription and route to correct contracts.
475
482
  * Uses the scripts from the subscription response to determine contract ownership.
476
483
  */
477
484
  processSubscriptionVtxos(vtxos, scripts, eventType, timestamp) {
478
- // If we have exactly one script, all VTXOs belong to that contract
485
+ // If we have exactly one script, all virtual outputs belong to that contract
479
486
  // Otherwise, we can't reliably determine ownership without script in VirtualCoin
480
487
  if (scripts.length === 1) {
481
488
  const contractScript = scripts[0];
@@ -497,8 +504,8 @@ export class ContractWatcher {
497
504
  }
498
505
  return;
499
506
  }
500
- // Multiple scripts - assign VTXOs to all matching contracts
501
- // This is a limitation: we can't know which VTXO belongs to which script
507
+ // Multiple scripts - assign virtual outputs to all matching contracts
508
+ // This is a limitation: we can't know which virtual output belongs to which script
502
509
  // In practice, subscription events usually come with a single script context
503
510
  for (const script of scripts) {
504
511
  const contractScript = script;
@@ -520,7 +527,7 @@ export class ContractWatcher {
520
527
  }
521
528
  }
522
529
  /**
523
- * Emit a VTXO event for a contract.
530
+ * Emit a virtual output event for a contract.
524
531
  */
525
532
  emitVtxoEvent(contractScript, vtxos, eventType, timestamp) {
526
533
  if (!this.eventCallback)
@@ -2,7 +2,7 @@ import { hex } from "@scure/base";
2
2
  import { DefaultVtxo } from '../../script/default.js';
3
3
  import { isCsvSpendable, sequenceToTimelock, timelockToSequence, } from './helpers.js';
4
4
  /**
5
- * Handler for default wallet VTXOs.
5
+ * Handler for default wallet virtual outputs.
6
6
  *
7
7
  * Default contracts use the standard forfeit + exit tapscript:
8
8
  * - forfeit: (Alice + Server) multisig for collaborative spending
@@ -3,7 +3,7 @@ import { DelegateVtxo } from '../../script/delegate.js';
3
3
  import { DefaultVtxo } from '../../script/default.js';
4
4
  import { isCsvSpendable, sequenceToTimelock, timelockToSequence, } from './helpers.js';
5
5
  /**
6
- * Handler for delegate wallet VTXOs.
6
+ * Handler for delegate wallet virtual outputs.
7
7
  *
8
8
  * Delegate contracts extend the default tapscript with an additional delegate path:
9
9
  * - forfeit: (Alice + Server) multisig for collaborative spending
@@ -63,7 +63,7 @@ export function isCltvSatisfied(context, locktime) {
63
63
  return currentTimeSec >= locktime;
64
64
  }
65
65
  /**
66
- * Check if a CSV timelock is currently satisfied for the given context/VTXO.
66
+ * Check if a CSV timelock is currently satisfied for the given context/virtual output.
67
67
  */
68
68
  export function isCsvSpendable(context, sequence) {
69
69
  if (sequence === undefined)
@@ -7,9 +7,25 @@ import { AssetOutputs } from './assetOutput.js';
7
7
  import { MetadataList } from './metadata.js';
8
8
  import { BufferReader, BufferWriter } from './utils.js';
9
9
  /**
10
- * An asset group contains inputs/outputs and all data related to a given asset id.
10
+ * An asset group contains inputs, outputs, and all data related to a given asset id.
11
+ *
12
+ * @see Packet
13
+ * @see AssetId
14
+ * @see AssetRef
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const group = AssetGroup.create(
19
+ * null, // asset ID: null for new issuance
20
+ * null, // control asset ID: null when reissuance not needed
21
+ * [], // asset inputs: empty for new issuance
22
+ * [AssetOutput.create(0, 1000)], // asset outputs: 1000 units at vout index 0
23
+ * [] // metadata: can be empty
24
+ * )
25
+ * ```
11
26
  */
12
27
  export class AssetGroup {
28
+ /** @see create */
13
29
  constructor(assetId, controlAsset, inputs, outputs, metadata) {
14
30
  this.assetId = assetId;
15
31
  this.controlAsset = controlAsset;
@@ -17,12 +33,31 @@ export class AssetGroup {
17
33
  this.outputs = outputs;
18
34
  this.metadataList = new MetadataList(metadata);
19
35
  }
36
+ /**
37
+ * Create and validate an asset group.
38
+ *
39
+ * @param assetId - Asset id for this group, or `null` for fresh issuance
40
+ * @param controlAsset - Optional control asset reference for (re) issuance
41
+ * @param inputs - Asset inputs in the group
42
+ * @param outputs - Asset outputs in the group
43
+ * @param metadata - Metadata entries associated with the group
44
+ * @returns A validated asset group
45
+ * @throws Error if the group fails validation
46
+ * @see validate
47
+ */
20
48
  static create(assetId, controlAsset, inputs, outputs, metadata) {
21
49
  const ag = new AssetGroup(assetId, controlAsset, inputs, outputs, metadata);
22
50
  ag.validate();
23
51
  return ag;
24
52
  }
25
- // from hex encoded
53
+ /**
54
+ * Decode an asset group from its hex string form.
55
+ *
56
+ * @param s - Hex-encoded asset group
57
+ * @returns Decoded asset group
58
+ * @throws Error if the string is not valid hex or does not encode a valid asset group
59
+ * @see toString
60
+ */
26
61
  static fromString(s) {
27
62
  let buf;
28
63
  try {
@@ -33,6 +68,13 @@ export class AssetGroup {
33
68
  }
34
69
  return AssetGroup.fromBytes(buf);
35
70
  }
71
+ /**
72
+ * Decode an asset group from its serialized bytes.
73
+ *
74
+ * @param buf - Serialized asset group bytes
75
+ * @returns Decoded asset group
76
+ * @throws Error if the buffer is empty or malformed
77
+ */
36
78
  static fromBytes(buf) {
37
79
  if (!buf || buf.length === 0) {
38
80
  throw new Error("missing asset group");
@@ -40,12 +82,21 @@ export class AssetGroup {
40
82
  const reader = new BufferReader(buf);
41
83
  return AssetGroup.fromReader(reader);
42
84
  }
43
- // an issuance is a group with null assetId
85
+ /**
86
+ * Return true when the group represents an issuance.
87
+ *
88
+ * @returns `true` when the group has no asset id
89
+ */
44
90
  isIssuance() {
45
91
  return this.assetId === null;
46
92
  }
47
- // a reissuance is a group that is not an issuance
48
- // but where the sum of the outputs is greater than the sum of the inputs
93
+ /**
94
+ * Return true when the group represents a reissuance.
95
+ *
96
+ * @returns `true` when the group has an asset id and outputs exceed local inputs
97
+ * @remarks
98
+ * Only local inputs contribute to the comparison; intent-backed inputs contribute `0` here.
99
+ */
49
100
  isReissuance() {
50
101
  const sumReducer = (s, { amount }) => s + amount;
51
102
  const sumOutputs = this.outputs.reduce(sumReducer, 0n);
@@ -56,12 +107,23 @@ export class AssetGroup {
56
107
  .reduce(sumReducer, 0n);
57
108
  return !this.isIssuance() && sumInputs < sumOutputs;
58
109
  }
110
+ /**
111
+ * Serialize the asset group to raw bytes.
112
+ *
113
+ * @returns Serialized asset group bytes
114
+ * @see fromBytes
115
+ */
59
116
  serialize() {
60
117
  this.validate();
61
118
  const writer = new BufferWriter();
62
119
  this.serializeTo(writer);
63
120
  return writer.toBytes();
64
121
  }
122
+ /**
123
+ * Validate the asset group and its child structures.
124
+ *
125
+ * @throws Error if the group is empty or violates issuance invariants
126
+ */
65
127
  validate() {
66
128
  if (this.inputs.length === 0 && this.outputs.length === 0) {
67
129
  throw new Error("empty asset group");
@@ -77,13 +139,33 @@ export class AssetGroup {
77
139
  }
78
140
  }
79
141
  }
142
+ /**
143
+ * Convert the group into its batch-leaf representation for the given intent txid.
144
+ *
145
+ * @param intentTxid - Intent transaction id used to build the leaf input reference
146
+ * @returns Batch-leaf asset group
147
+ * @see AssetInput.createIntent
148
+ */
80
149
  toBatchLeafAssetGroup(intentTxid) {
81
150
  const leafInput = AssetInput.createIntent(hex.encode(intentTxid), 0, 0);
82
151
  return new AssetGroup(this.assetId, this.controlAsset, [leafInput], this.outputs, this.metadataList.items);
83
152
  }
153
+ /**
154
+ * Encode the asset group to a hex string.
155
+ *
156
+ * @returns Hex-encoded asset group
157
+ * @see fromString
158
+ */
84
159
  toString() {
85
160
  return hex.encode(this.serialize());
86
161
  }
162
+ /**
163
+ * Decode an asset group from a binary reader.
164
+ *
165
+ * @param reader - Reader positioned at an asset group
166
+ * @returns Decoded asset group
167
+ * @throws Error if the encoded group is malformed
168
+ */
87
169
  static fromReader(reader) {
88
170
  const presence = reader.readByte();
89
171
  let assetId = null;
@@ -104,6 +186,11 @@ export class AssetGroup {
104
186
  ag.validate();
105
187
  return ag;
106
188
  }
189
+ /**
190
+ * Serialize the asset group into an existing binary writer.
191
+ *
192
+ * @param writer - Writer to append the asset group to
193
+ */
107
194
  serializeTo(writer) {
108
195
  let presence = 0;
109
196
  if (this.assetId !== null) {