@arkade-os/sdk 0.3.12 → 0.4.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +483 -54
  2. package/dist/cjs/adapters/expo-db.js +35 -0
  3. package/dist/cjs/asset/assetGroup.js +141 -0
  4. package/dist/cjs/asset/assetId.js +88 -0
  5. package/dist/cjs/asset/assetInput.js +204 -0
  6. package/dist/cjs/asset/assetOutput.js +159 -0
  7. package/dist/cjs/asset/assetRef.js +82 -0
  8. package/dist/cjs/asset/index.js +24 -0
  9. package/dist/cjs/asset/metadata.js +172 -0
  10. package/dist/cjs/asset/packet.js +164 -0
  11. package/dist/cjs/asset/types.js +25 -0
  12. package/dist/cjs/asset/utils.js +105 -0
  13. package/dist/cjs/contracts/arkcontract.js +148 -0
  14. package/dist/cjs/contracts/contractManager.js +436 -0
  15. package/dist/cjs/contracts/contractWatcher.js +567 -0
  16. package/dist/cjs/contracts/handlers/default.js +85 -0
  17. package/dist/cjs/contracts/handlers/delegate.js +89 -0
  18. package/dist/cjs/contracts/handlers/helpers.js +105 -0
  19. package/dist/cjs/contracts/handlers/index.js +19 -0
  20. package/dist/cjs/contracts/handlers/registry.js +89 -0
  21. package/dist/cjs/contracts/handlers/vhtlc.js +193 -0
  22. package/dist/cjs/contracts/index.js +41 -0
  23. package/dist/cjs/contracts/types.js +2 -0
  24. package/dist/cjs/db/manager.js +97 -0
  25. package/dist/cjs/forfeit.js +12 -8
  26. package/dist/cjs/identity/index.js +1 -0
  27. package/dist/cjs/identity/seedIdentity.js +255 -0
  28. package/dist/cjs/index.js +70 -14
  29. package/dist/cjs/intent/index.js +28 -2
  30. package/dist/cjs/providers/ark.js +7 -0
  31. package/dist/cjs/providers/delegator.js +66 -0
  32. package/dist/cjs/providers/expoIndexer.js +5 -0
  33. package/dist/cjs/providers/indexer.js +68 -1
  34. package/dist/cjs/providers/onchain.js +2 -2
  35. package/dist/cjs/providers/utils.js +1 -0
  36. package/dist/cjs/repositories/contractRepository.js +0 -103
  37. package/dist/cjs/repositories/inMemory/contractRepository.js +55 -0
  38. package/dist/cjs/repositories/inMemory/walletRepository.js +80 -0
  39. package/dist/cjs/repositories/index.js +16 -0
  40. package/dist/cjs/repositories/indexedDB/contractRepository.js +187 -0
  41. package/dist/cjs/repositories/indexedDB/db.js +57 -0
  42. package/dist/cjs/repositories/indexedDB/schema.js +159 -0
  43. package/dist/cjs/repositories/indexedDB/walletRepository.js +338 -0
  44. package/dist/cjs/repositories/indexedDB/websqlAdapter.js +144 -0
  45. package/dist/cjs/repositories/migrations/contractRepositoryImpl.js +127 -0
  46. package/dist/cjs/repositories/migrations/fromStorageAdapter.js +66 -0
  47. package/dist/cjs/repositories/migrations/walletRepositoryImpl.js +180 -0
  48. package/dist/cjs/repositories/walletRepository.js +0 -169
  49. package/dist/cjs/script/base.js +54 -0
  50. package/dist/cjs/script/delegate.js +49 -0
  51. package/dist/cjs/storage/asyncStorage.js +4 -1
  52. package/dist/cjs/storage/fileSystem.js +3 -0
  53. package/dist/cjs/storage/inMemory.js +3 -0
  54. package/dist/cjs/storage/indexedDB.js +5 -1
  55. package/dist/cjs/storage/localStorage.js +3 -0
  56. package/dist/cjs/utils/arkTransaction.js +16 -0
  57. package/dist/cjs/utils/transactionHistory.js +50 -0
  58. package/dist/cjs/utils/txSizeEstimator.js +39 -14
  59. package/dist/cjs/wallet/asset-manager.js +338 -0
  60. package/dist/cjs/wallet/asset.js +117 -0
  61. package/dist/cjs/wallet/batch.js +1 -1
  62. package/dist/cjs/wallet/delegator.js +235 -0
  63. package/dist/cjs/wallet/expo/background.js +133 -0
  64. package/dist/cjs/wallet/expo/index.js +9 -0
  65. package/dist/cjs/wallet/expo/wallet.js +231 -0
  66. package/dist/cjs/wallet/onchain.js +57 -12
  67. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +568 -0
  68. package/dist/cjs/wallet/serviceWorker/wallet.js +383 -102
  69. package/dist/cjs/wallet/unroll.js +7 -2
  70. package/dist/cjs/wallet/utils.js +60 -0
  71. package/dist/cjs/wallet/validation.js +151 -0
  72. package/dist/cjs/wallet/vtxo-manager.js +1 -1
  73. package/dist/cjs/wallet/wallet.js +702 -260
  74. package/dist/cjs/worker/browser/service-worker-manager.js +82 -0
  75. package/dist/cjs/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  76. package/dist/cjs/worker/expo/asyncStorageTaskQueue.js +78 -0
  77. package/dist/cjs/worker/expo/index.js +12 -0
  78. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +61 -0
  79. package/dist/cjs/worker/expo/processors/index.js +6 -0
  80. package/dist/cjs/worker/expo/taskQueue.js +41 -0
  81. package/dist/cjs/worker/expo/taskRunner.js +57 -0
  82. package/dist/cjs/worker/messageBus.js +252 -0
  83. package/dist/esm/adapters/expo-db.js +27 -0
  84. package/dist/esm/asset/assetGroup.js +137 -0
  85. package/dist/esm/asset/assetId.js +84 -0
  86. package/dist/esm/asset/assetInput.js +199 -0
  87. package/dist/esm/asset/assetOutput.js +154 -0
  88. package/dist/esm/asset/assetRef.js +78 -0
  89. package/dist/esm/asset/index.js +8 -0
  90. package/dist/esm/asset/metadata.js +167 -0
  91. package/dist/esm/asset/packet.js +159 -0
  92. package/dist/esm/asset/types.js +22 -0
  93. package/dist/esm/asset/utils.js +99 -0
  94. package/dist/esm/contracts/arkcontract.js +141 -0
  95. package/dist/esm/contracts/contractManager.js +432 -0
  96. package/dist/esm/contracts/contractWatcher.js +563 -0
  97. package/dist/esm/contracts/handlers/default.js +82 -0
  98. package/dist/esm/contracts/handlers/delegate.js +86 -0
  99. package/dist/esm/contracts/handlers/helpers.js +66 -0
  100. package/dist/esm/contracts/handlers/index.js +12 -0
  101. package/dist/esm/contracts/handlers/registry.js +86 -0
  102. package/dist/esm/contracts/handlers/vhtlc.js +190 -0
  103. package/dist/esm/contracts/index.js +13 -0
  104. package/dist/esm/contracts/types.js +1 -0
  105. package/dist/esm/db/manager.js +92 -0
  106. package/dist/esm/forfeit.js +11 -8
  107. package/dist/esm/identity/index.js +1 -0
  108. package/dist/esm/identity/seedIdentity.js +249 -0
  109. package/dist/esm/index.js +25 -15
  110. package/dist/esm/intent/index.js +28 -2
  111. package/dist/esm/providers/ark.js +7 -0
  112. package/dist/esm/providers/delegator.js +62 -0
  113. package/dist/esm/providers/expoIndexer.js +5 -0
  114. package/dist/esm/providers/indexer.js +68 -1
  115. package/dist/esm/providers/onchain.js +2 -2
  116. package/dist/esm/providers/utils.js +1 -0
  117. package/dist/esm/repositories/contractRepository.js +1 -101
  118. package/dist/esm/repositories/inMemory/contractRepository.js +51 -0
  119. package/dist/esm/repositories/inMemory/walletRepository.js +76 -0
  120. package/dist/esm/repositories/index.js +8 -0
  121. package/dist/esm/repositories/indexedDB/contractRepository.js +183 -0
  122. package/dist/esm/repositories/indexedDB/db.js +42 -0
  123. package/dist/esm/repositories/indexedDB/schema.js +155 -0
  124. package/dist/esm/repositories/indexedDB/walletRepository.js +334 -0
  125. package/dist/esm/repositories/indexedDB/websqlAdapter.js +138 -0
  126. package/dist/esm/repositories/migrations/contractRepositoryImpl.js +121 -0
  127. package/dist/esm/repositories/migrations/fromStorageAdapter.js +58 -0
  128. package/dist/esm/repositories/migrations/walletRepositoryImpl.js +176 -0
  129. package/dist/esm/repositories/walletRepository.js +1 -167
  130. package/dist/esm/script/base.js +21 -1
  131. package/dist/esm/script/delegate.js +46 -0
  132. package/dist/esm/storage/asyncStorage.js +4 -1
  133. package/dist/esm/storage/fileSystem.js +3 -0
  134. package/dist/esm/storage/inMemory.js +3 -0
  135. package/dist/esm/storage/indexedDB.js +5 -1
  136. package/dist/esm/storage/localStorage.js +3 -0
  137. package/dist/esm/utils/arkTransaction.js +15 -0
  138. package/dist/esm/utils/transactionHistory.js +50 -0
  139. package/dist/esm/utils/txSizeEstimator.js +39 -14
  140. package/dist/esm/wallet/asset-manager.js +333 -0
  141. package/dist/esm/wallet/asset.js +111 -0
  142. package/dist/esm/wallet/batch.js +1 -1
  143. package/dist/esm/wallet/delegator.js +231 -0
  144. package/dist/esm/wallet/expo/background.js +128 -0
  145. package/dist/esm/wallet/expo/index.js +2 -0
  146. package/dist/esm/wallet/expo/wallet.js +194 -0
  147. package/dist/esm/wallet/onchain.js +57 -12
  148. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +564 -0
  149. package/dist/esm/wallet/serviceWorker/wallet.js +382 -101
  150. package/dist/esm/wallet/unroll.js +7 -2
  151. package/dist/esm/wallet/utils.js +55 -0
  152. package/dist/esm/wallet/validation.js +139 -0
  153. package/dist/esm/wallet/vtxo-manager.js +1 -1
  154. package/dist/esm/wallet/wallet.js +704 -229
  155. package/dist/esm/worker/browser/service-worker-manager.js +76 -0
  156. package/dist/esm/{wallet/serviceWorker → worker/browser}/utils.js +2 -1
  157. package/dist/esm/worker/expo/asyncStorageTaskQueue.js +74 -0
  158. package/dist/esm/worker/expo/index.js +4 -0
  159. package/dist/esm/worker/expo/processors/contractPollProcessor.js +58 -0
  160. package/dist/esm/worker/expo/processors/index.js +1 -0
  161. package/dist/esm/worker/expo/taskQueue.js +37 -0
  162. package/dist/esm/worker/expo/taskRunner.js +54 -0
  163. package/dist/esm/worker/messageBus.js +248 -0
  164. package/dist/types/adapters/expo-db.d.ts +7 -0
  165. package/dist/types/asset/assetGroup.d.ts +28 -0
  166. package/dist/types/asset/assetId.d.ts +19 -0
  167. package/dist/types/asset/assetInput.d.ts +46 -0
  168. package/dist/types/asset/assetOutput.d.ts +39 -0
  169. package/dist/types/asset/assetRef.d.ts +25 -0
  170. package/dist/types/asset/index.d.ts +8 -0
  171. package/dist/types/asset/metadata.d.ts +37 -0
  172. package/dist/types/asset/packet.d.ts +27 -0
  173. package/dist/types/asset/types.d.ts +18 -0
  174. package/dist/types/asset/utils.d.ts +21 -0
  175. package/dist/types/contracts/arkcontract.d.ts +101 -0
  176. package/dist/types/contracts/contractManager.d.ts +331 -0
  177. package/dist/types/contracts/contractWatcher.d.ts +192 -0
  178. package/dist/types/contracts/handlers/default.d.ts +19 -0
  179. package/dist/types/contracts/handlers/delegate.d.ts +21 -0
  180. package/dist/types/contracts/handlers/helpers.d.ts +18 -0
  181. package/dist/types/contracts/handlers/index.d.ts +7 -0
  182. package/dist/types/contracts/handlers/registry.d.ts +65 -0
  183. package/dist/types/contracts/handlers/vhtlc.d.ts +32 -0
  184. package/dist/types/contracts/index.d.ts +14 -0
  185. package/dist/types/contracts/types.d.ts +222 -0
  186. package/dist/types/db/manager.d.ts +22 -0
  187. package/dist/types/forfeit.d.ts +2 -1
  188. package/dist/types/identity/index.d.ts +1 -0
  189. package/dist/types/identity/seedIdentity.d.ts +128 -0
  190. package/dist/types/index.d.ts +21 -12
  191. package/dist/types/intent/index.d.ts +2 -1
  192. package/dist/types/providers/ark.d.ts +11 -2
  193. package/dist/types/providers/delegator.d.ts +29 -0
  194. package/dist/types/providers/indexer.d.ts +11 -1
  195. package/dist/types/repositories/contractRepository.d.ts +30 -19
  196. package/dist/types/repositories/inMemory/contractRepository.d.ts +17 -0
  197. package/dist/types/repositories/inMemory/walletRepository.d.ts +26 -0
  198. package/dist/types/repositories/index.d.ts +7 -0
  199. package/dist/types/repositories/indexedDB/contractRepository.d.ts +21 -0
  200. package/dist/types/repositories/indexedDB/db.d.ts +56 -0
  201. package/dist/types/repositories/indexedDB/schema.d.ts +8 -0
  202. package/dist/types/repositories/indexedDB/walletRepository.d.ts +25 -0
  203. package/dist/types/repositories/indexedDB/websqlAdapter.d.ts +49 -0
  204. package/dist/types/repositories/migrations/contractRepositoryImpl.d.ts +24 -0
  205. package/dist/types/repositories/migrations/fromStorageAdapter.d.ts +19 -0
  206. package/dist/types/repositories/migrations/walletRepositoryImpl.d.ts +27 -0
  207. package/dist/types/repositories/walletRepository.d.ts +13 -24
  208. package/dist/types/script/base.d.ts +1 -0
  209. package/dist/types/script/delegate.d.ts +36 -0
  210. package/dist/types/storage/asyncStorage.d.ts +4 -0
  211. package/dist/types/storage/fileSystem.d.ts +3 -0
  212. package/dist/types/storage/inMemory.d.ts +3 -0
  213. package/dist/types/storage/index.d.ts +3 -0
  214. package/dist/types/storage/indexedDB.d.ts +3 -0
  215. package/dist/types/storage/localStorage.d.ts +3 -0
  216. package/dist/types/utils/arkTransaction.d.ts +6 -0
  217. package/dist/types/utils/txSizeEstimator.d.ts +12 -2
  218. package/dist/types/wallet/asset-manager.d.ts +78 -0
  219. package/dist/types/wallet/asset.d.ts +21 -0
  220. package/dist/types/wallet/batch.d.ts +1 -1
  221. package/dist/types/wallet/delegator.d.ts +24 -0
  222. package/dist/types/wallet/expo/background.d.ts +66 -0
  223. package/dist/types/wallet/expo/index.d.ts +4 -0
  224. package/dist/types/wallet/expo/wallet.d.ts +97 -0
  225. package/dist/types/wallet/index.d.ts +75 -2
  226. package/dist/types/wallet/onchain.d.ts +22 -1
  227. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +366 -0
  228. package/dist/types/wallet/serviceWorker/wallet.d.ts +20 -11
  229. package/dist/types/wallet/utils.d.ts +13 -1
  230. package/dist/types/wallet/validation.d.ts +24 -0
  231. package/dist/types/wallet/wallet.d.ts +111 -17
  232. package/dist/types/worker/browser/service-worker-manager.d.ts +21 -0
  233. package/dist/types/{wallet/serviceWorker → worker/browser}/utils.d.ts +2 -1
  234. package/dist/types/worker/expo/asyncStorageTaskQueue.d.ts +46 -0
  235. package/dist/types/worker/expo/index.d.ts +7 -0
  236. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +14 -0
  237. package/dist/types/worker/expo/processors/index.d.ts +1 -0
  238. package/dist/types/worker/expo/taskQueue.d.ts +50 -0
  239. package/dist/types/worker/expo/taskRunner.d.ts +42 -0
  240. package/dist/types/worker/messageBus.d.ts +109 -0
  241. package/package.json +71 -17
  242. package/dist/cjs/wallet/serviceWorker/request.js +0 -78
  243. package/dist/cjs/wallet/serviceWorker/response.js +0 -222
  244. package/dist/cjs/wallet/serviceWorker/worker.js +0 -655
  245. package/dist/esm/wallet/serviceWorker/request.js +0 -75
  246. package/dist/esm/wallet/serviceWorker/response.js +0 -219
  247. package/dist/esm/wallet/serviceWorker/worker.js +0 -651
  248. package/dist/types/wallet/serviceWorker/request.d.ts +0 -74
  249. package/dist/types/wallet/serviceWorker/response.d.ts +0 -123
  250. package/dist/types/wallet/serviceWorker/worker.d.ts +0 -53
@@ -0,0 +1,432 @@
1
+ import { hex } from "@scure/base";
2
+ import { ContractWatcher } from './contractWatcher.js';
3
+ import { contractHandlers } from './handlers/index.js';
4
+ import { extendVtxoFromContract } from '../wallet/utils.js';
5
+ /**
6
+ * Central manager for contract lifecycle and operations.
7
+ *
8
+ * The ContractManager orchestrates:
9
+ * - Contract registration and persistence
10
+ * - Multi-contract watching via ContractWatcher
11
+ * - VTXO queries across contracts
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const manager = new ContractManager({
16
+ * indexerProvider: wallet.indexerProvider,
17
+ * contractRepository: wallet.contractRepository,
18
+ * getDefaultAddress: () => wallet.getAddress(),
19
+ * });
20
+ *
21
+ * // Initialize (loads persisted contracts)
22
+ * await manager.initialize();
23
+ *
24
+ * // Create a new VHTLC contract
25
+ * const contract = await manager.createContract({
26
+ * label: "Lightning Receive",
27
+ * type: "vhtlc",
28
+ * params: { sender: "ab12...", receiver: "cd34...", ... },
29
+ * script: "5120...",
30
+ * address: "tark1...",
31
+ * });
32
+ *
33
+ * // Start watching for events
34
+ * const stop = await manager.startWatching((event) => {
35
+ * console.log(`${event.type} on ${event.contractScript}`);
36
+ * });
37
+ *
38
+ * // Get balance across all contracts
39
+ * const balances = await manager.getAllBalances();
40
+ * ```
41
+ */
42
+ export class ContractManager {
43
+ constructor(config) {
44
+ this.initialized = false;
45
+ this.eventCallbacks = new Set();
46
+ this.config = config;
47
+ // Create watcher with wallet repository for VTXO caching
48
+ this.watcher = new ContractWatcher({
49
+ indexerProvider: config.indexerProvider,
50
+ walletRepository: config.walletRepository,
51
+ ...config.watcherConfig,
52
+ });
53
+ }
54
+ /**
55
+ * Static factory method for creating a new ContractManager.
56
+ * Initialize the manager by loading persisted contracts and starting to watch.
57
+ *
58
+ * After initialization, the manager automatically watches all active contracts
59
+ * and contracts with VTXOs. Use `onContractEvent()` to register event callbacks.
60
+ *
61
+ * @param config ContractManagerConfig
62
+ */
63
+ static async create(config) {
64
+ const cm = new ContractManager(config);
65
+ await cm.initialize();
66
+ return cm;
67
+ }
68
+ async initialize() {
69
+ if (this.initialized) {
70
+ return;
71
+ }
72
+ // Load persisted contracts
73
+ const contracts = await this.config.contractRepository.getContracts();
74
+ // fetch latest VTXOs for all contracts, ensure cache is up to date
75
+ // TODO: what if the user has 1k contracts?
76
+ await this.getVtxosForContracts(contracts);
77
+ // add all contracts to the watcher
78
+ const now = Date.now();
79
+ for (const contract of contracts) {
80
+ // Check for expired contracts and mark as inactive
81
+ if (contract.state === "active" &&
82
+ contract.expiresAt &&
83
+ contract.expiresAt <= now) {
84
+ contract.state = "inactive";
85
+ await this.config.contractRepository.saveContract(contract);
86
+ }
87
+ // Add to watcher
88
+ await this.watcher.addContract(contract);
89
+ }
90
+ this.initialized = true;
91
+ // Start watching automatically
92
+ this.stopWatcherFn = await this.watcher.startWatching((event) => {
93
+ this.handleContractEvent(event);
94
+ });
95
+ }
96
+ /**
97
+ * Create and register a new contract.
98
+ *
99
+ * @param params - Contract parameters
100
+ * @returns The created contract
101
+ */
102
+ async createContract(params) {
103
+ // Validate that a handler exists for this contract type
104
+ const handler = contractHandlers.get(params.type);
105
+ if (!handler) {
106
+ throw new Error(`No handler registered for contract type '${params.type}'`);
107
+ }
108
+ // Validate params by attempting to create the script
109
+ // This catches invalid/missing params early
110
+ try {
111
+ const script = handler.createScript(params.params);
112
+ const derivedScript = hex.encode(script.pkScript);
113
+ // Verify the derived script matches the provided script
114
+ if (derivedScript !== params.script) {
115
+ throw new Error(`Script mismatch: provided script does not match script derived from params. ` +
116
+ `Expected ${derivedScript}, got ${params.script}`);
117
+ }
118
+ }
119
+ catch (error) {
120
+ if (error instanceof Error && error.message.includes("mismatch")) {
121
+ throw error;
122
+ }
123
+ throw new Error(`Invalid params for contract type '${params.type}': ${error instanceof Error ? error.message : String(error)}`);
124
+ }
125
+ // Check if contract already exists and verify it's the same type to avoid silent mismatches
126
+ const [existing] = await this.getContracts({ script: params.script });
127
+ if (existing) {
128
+ if (existing.type === params.type)
129
+ return existing;
130
+ throw new Error(`Contract with script ${params.script} already exists with with type ${existing.type}.`);
131
+ }
132
+ const contract = {
133
+ ...params,
134
+ createdAt: Date.now(),
135
+ state: params.state || "active",
136
+ };
137
+ // Persist
138
+ await this.config.contractRepository.saveContract(contract);
139
+ // ensure we have the latest VTXOs for this contract
140
+ await this.getVtxosForContracts([contract]);
141
+ // Add to watcher
142
+ await this.watcher.addContract(contract);
143
+ return contract;
144
+ }
145
+ /**
146
+ * Get contracts with optional filters.
147
+ *
148
+ * @param filter - Optional filter criteria
149
+ * @returns Filtered contracts TODO: filter spent/unspent
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * // Get all VHTLC contracts
154
+ * const vhtlcs = await manager.getContracts({ type: 'vhtlc' });
155
+ *
156
+ * // Get all active contracts
157
+ * const active = await manager.getContracts({ state: 'active' });
158
+ * ```
159
+ */
160
+ async getContracts(filter) {
161
+ const dbFilter = this.buildContractsDbFilter(filter ?? {});
162
+ return await this.config.contractRepository.getContracts(dbFilter);
163
+ }
164
+ async getContractsWithVtxos(filter) {
165
+ const contracts = await this.getContracts(filter);
166
+ const vtxos = await this.getVtxosForContracts(contracts);
167
+ return contracts.map((contract) => ({
168
+ contract,
169
+ vtxos: vtxos.get(contract.script) ?? [],
170
+ }));
171
+ }
172
+ buildContractsDbFilter(filter) {
173
+ return {
174
+ script: filter.script,
175
+ state: filter.state,
176
+ type: filter.type,
177
+ };
178
+ }
179
+ /**
180
+ * Update a contract.
181
+ * Nested fields like `params` and `metadata` are replaced with the provided values.
182
+ * If you need to preserve existing fields, merge them manually.
183
+ *
184
+ * @param script - Contract script
185
+ * @param updates - Fields to update
186
+ */
187
+ async updateContract(script, updates) {
188
+ const contracts = await this.config.contractRepository.getContracts({
189
+ script,
190
+ });
191
+ const existing = contracts[0];
192
+ if (!existing) {
193
+ throw new Error(`Contract ${script} not found`);
194
+ }
195
+ const updated = {
196
+ ...existing,
197
+ ...updates,
198
+ };
199
+ await this.config.contractRepository.saveContract(updated);
200
+ await this.watcher.updateContract(updated);
201
+ return updated;
202
+ }
203
+ /**
204
+ * Update a contract's params.
205
+ * This method preserves existing params by merging the provided values.
206
+ *
207
+ * @param script - Contract script
208
+ * @param updates - The new values to merge with existing params
209
+ */
210
+ async updateContractParams(script, updates) {
211
+ const contracts = await this.config.contractRepository.getContracts({
212
+ script,
213
+ });
214
+ const existing = contracts[0];
215
+ if (!existing) {
216
+ throw new Error(`Contract ${script} not found`);
217
+ }
218
+ const updated = {
219
+ ...existing,
220
+ params: { ...existing.params, ...updates },
221
+ };
222
+ await this.config.contractRepository.saveContract(updated);
223
+ await this.watcher.updateContract(updated);
224
+ return updated;
225
+ }
226
+ /**
227
+ * Set a contract's state.
228
+ */
229
+ async setContractState(script, state) {
230
+ await this.updateContract(script, { state });
231
+ }
232
+ /**
233
+ * Delete a contract.
234
+ *
235
+ * @param script - Contract script
236
+ */
237
+ async deleteContract(script) {
238
+ await this.config.contractRepository.deleteContract(script);
239
+ await this.watcher.removeContract(script);
240
+ }
241
+ /**
242
+ * Get currently spendable paths for a contract.
243
+ *
244
+ * @param contractScript - The contract script
245
+ * @param options - Options for getting spendable paths
246
+ */
247
+ async getSpendablePaths(options) {
248
+ const { contractScript, collaborative = true, walletPubKey, vtxo, } = options;
249
+ const [contract] = await this.getContracts({ script: contractScript });
250
+ if (!contract)
251
+ return [];
252
+ const handler = contractHandlers.get(contract.type);
253
+ if (!handler)
254
+ return [];
255
+ const script = handler.createScript(contract.params);
256
+ const context = {
257
+ collaborative,
258
+ currentTime: Date.now(),
259
+ walletPubKey,
260
+ vtxo,
261
+ };
262
+ return handler.getSpendablePaths(script, contract, context);
263
+ }
264
+ async getAllSpendingPaths(options) {
265
+ const { contractScript, collaborative = true, walletPubKey } = options;
266
+ const [contract] = await this.getContracts({ script: contractScript });
267
+ if (!contract)
268
+ return [];
269
+ const handler = contractHandlers.get(contract.type);
270
+ if (!handler)
271
+ return [];
272
+ const script = handler.createScript(contract.params);
273
+ const context = {
274
+ collaborative,
275
+ currentTime: Date.now(),
276
+ walletPubKey,
277
+ };
278
+ return handler.getAllSpendingPaths(script, contract, context);
279
+ }
280
+ /**
281
+ * Register a callback for contract events.
282
+ *
283
+ * The manager automatically watches after `initialize()`. This method
284
+ * allows registering callbacks to receive events.
285
+ *
286
+ * @param callback - Event callback
287
+ * @returns Unsubscribe function to remove this callback
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * const unsubscribe = manager.onContractEvent((event) => {
292
+ * console.log(`${event.type} on ${event.contractScript}`);
293
+ * });
294
+ *
295
+ * // Later: stop receiving events
296
+ * unsubscribe();
297
+ * ```
298
+ */
299
+ onContractEvent(callback) {
300
+ this.eventCallbacks.add(callback);
301
+ return () => {
302
+ this.eventCallbacks.delete(callback);
303
+ };
304
+ }
305
+ /**
306
+ * Check if currently watching.
307
+ */
308
+ async isWatching() {
309
+ return this.watcher.isCurrentlyWatching();
310
+ }
311
+ /**
312
+ * Emit an event to all registered callbacks.
313
+ */
314
+ emitEvent(event) {
315
+ for (const callback of this.eventCallbacks) {
316
+ try {
317
+ callback(event);
318
+ }
319
+ catch (error) {
320
+ console.error("Error in contract event callback:", error);
321
+ }
322
+ }
323
+ }
324
+ /**
325
+ * Handle events from the watcher.
326
+ */
327
+ async handleContractEvent(event) {
328
+ switch (event.type) {
329
+ // Every time there is a VTXO event for a contract, refresh all its VTXOs
330
+ case "vtxo_received":
331
+ case "vtxo_spent":
332
+ await this.fetchContractVxosFromIndexer([event.contract], true);
333
+ break;
334
+ case "connection_reset":
335
+ // Refetch all VTXOs for all active contracts
336
+ const activeWatchedContracts = this.watcher.getActiveContracts();
337
+ await this.fetchContractVxosFromIndexer(activeWatchedContracts, false);
338
+ break;
339
+ case "contract_expired":
340
+ // just update DB
341
+ await this.config.contractRepository.saveContract(event.contract);
342
+ }
343
+ // Forward to all callbacks
344
+ this.emitEvent(event);
345
+ }
346
+ async getVtxosForContracts(contracts) {
347
+ if (contracts.length === 0) {
348
+ return new Map();
349
+ }
350
+ return await this.fetchContractVxosFromIndexer(contracts, false);
351
+ }
352
+ async fetchContractVxosFromIndexer(contracts, includeSpent) {
353
+ const fetched = await this.fetchContractVtxosBulk(contracts, includeSpent);
354
+ const result = new Map();
355
+ for (const [contractScript, vtxos] of fetched) {
356
+ result.set(contractScript, vtxos);
357
+ const contract = contracts.find((c) => c.script === contractScript);
358
+ if (contract) {
359
+ await this.config.walletRepository.saveVtxos(contract.address, vtxos);
360
+ }
361
+ }
362
+ return result;
363
+ }
364
+ async fetchContractVtxosBulk(contracts, includeSpent) {
365
+ const result = new Map();
366
+ await Promise.all(contracts.map(async (contract) => {
367
+ const vtxos = await this.fetchContractVtxosPaginated(contract, includeSpent);
368
+ result.set(contract.script, vtxos);
369
+ }));
370
+ return result;
371
+ }
372
+ async fetchContractVtxosPaginated(contract, includeSpent) {
373
+ const pageSize = 100;
374
+ const allVtxos = [];
375
+ let pageIndex = 0;
376
+ let hasMore = true;
377
+ const opts = includeSpent ? {} : { spendableOnly: true };
378
+ while (hasMore) {
379
+ const { vtxos, page } = await this.config.indexerProvider.getVtxos({
380
+ scripts: [contract.script],
381
+ ...opts,
382
+ pageIndex,
383
+ pageSize,
384
+ });
385
+ for (const vtxo of vtxos) {
386
+ allVtxos.push({
387
+ ...extendVtxoFromContract(vtxo, contract),
388
+ contractScript: contract.script,
389
+ });
390
+ }
391
+ hasMore = page ? vtxos.length === pageSize : false;
392
+ pageIndex++;
393
+ }
394
+ return allVtxos;
395
+ }
396
+ /**
397
+ * Dispose of the ContractManager and release all resources.
398
+ *
399
+ * Stops the watcher, clears callbacks, and marks
400
+ * the manager as uninitialized.
401
+ *
402
+ * Implements the disposable pattern for cleanup.
403
+ */
404
+ dispose() {
405
+ // Stop watching
406
+ this.stopWatcherFn?.();
407
+ this.stopWatcherFn = undefined;
408
+ // Clear callbacks
409
+ this.eventCallbacks.clear();
410
+ // Mark as uninitialized
411
+ this.initialized = false;
412
+ }
413
+ /**
414
+ * Symbol.dispose implementation for using with `using` keyword.
415
+ * @example
416
+ * ```typescript
417
+ * {
418
+ * using manager = await wallet.getContractManager();
419
+ * // ... use manager
420
+ * } // automatically disposed
421
+ * ```
422
+ */
423
+ [Symbol.dispose]() {
424
+ // Stop watching
425
+ this.stopWatcherFn?.();
426
+ this.stopWatcherFn = undefined;
427
+ // Clear callbacks
428
+ this.eventCallbacks.clear();
429
+ // Mark as uninitialized
430
+ this.initialized = false;
431
+ }
432
+ }